You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2017/11/21 12:10:33 UTC

[39/47] ignite git commit: IGNITE-6337 .NET: Thin client: SQL queries

IGNITE-6337 .NET: Thin client: SQL queries

This closes #2832


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

Branch: refs/heads/ignite-zk
Commit: 0bd712ddb3df9408b80bf0da6d19480ef737b7bc
Parents: 25048f8
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Fri Nov 17 17:16:38 2017 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Fri Nov 17 17:16:38 2017 +0300

----------------------------------------------------------------------
 .../odbc/jdbc/JdbcQueryExecuteRequest.java      |   2 +-
 .../processors/odbc/jdbc/JdbcStatementType.java |  13 +
 .../platform/cache/PlatformCache.java           |   2 +-
 .../platform/client/ClientMessageParser.java    |  34 ++-
 .../cache/ClientCacheEntryQueryCursor.java      |  46 ++++
 .../cache/ClientCacheFieldsQueryCursor.java     |  53 ++++
 .../client/cache/ClientCacheQueryCursor.java    | 141 ++++++++++
 .../cache/ClientCacheQueryNextPageRequest.java  |  49 ++++
 .../cache/ClientCacheQueryNextPageResponse.java |  50 ++++
 .../client/cache/ClientCacheQueryResponse.java  |  52 ++++
 .../client/cache/ClientCacheRequest.java        |  30 ++-
 .../cache/ClientCacheScanQueryCursor.java       | 135 ----------
 .../ClientCacheScanQueryNextPageRequest.java    |  49 ----
 .../ClientCacheScanQueryNextPageResponse.java   |  50 ----
 .../cache/ClientCacheScanQueryRequest.java      |   5 +-
 .../cache/ClientCacheScanQueryResponse.java     |  52 ----
 .../cache/ClientCacheSqlFieldsQueryRequest.java | 126 +++++++++
 .../ClientCacheSqlFieldsQueryResponse.java      |  75 ++++++
 .../cache/ClientCacheSqlQueryRequest.java       |  79 ++++++
 .../Apache.Ignite.Core.Tests.csproj             |   1 +
 .../Client/Cache/CacheTestNoMeta.cs             |  11 +-
 .../Client/Cache/Person.cs                      |  29 ++
 .../Client/Cache/SqlQueryTest.cs                | 268 +++++++++++++++++++
 .../Client/ClientTestBase.cs                    |  11 +-
 .../Apache.Ignite.Core.csproj                   |   4 +
 .../Cache/Query/IFieldsQueryCursor.cs           |  34 +++
 .../Client/Cache/ICacheClient.cs                |  14 +
 .../Impl/Cache/Query/FieldsQueryCursor.cs       |  29 +-
 .../Impl/Cache/Query/PlatformQueryQursorBase.cs |   8 +-
 .../Impl/Cache/Query/QueryCursor.cs             |  16 +-
 .../Impl/Cache/Query/QueryCursorBase.cs         |  22 +-
 .../Impl/Client/Cache/CacheClient.cs            |  80 +++++-
 .../Cache/Query/ClientFieldsQueryCursor.cs      |  79 ++++++
 .../Client/Cache/Query/ClientQueryCursor.cs     |  62 +----
 .../Client/Cache/Query/ClientQueryCursorBase.cs |  89 ++++++
 .../Impl/Client/Cache/Query/StatementType.cs    |  42 +++
 .../Apache.Ignite.Core/Impl/Client/ClientOp.cs  |   6 +-
 37 files changed, 1428 insertions(+), 420 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
index 1c6262e..3e54fc8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
@@ -156,7 +156,7 @@ public class JdbcQueryExecuteRequest extends JdbcRequest {
 
         try {
             if (reader.available() > 0)
-                stmtType = JdbcStatementType.values()[reader.readByte()];
+                stmtType = JdbcStatementType.fromOrdinal(reader.readByte());
             else
                 stmtType = JdbcStatementType.ANY_STATEMENT_TYPE;
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcStatementType.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcStatementType.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcStatementType.java
index aec2d12..ebe303f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcStatementType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcStatementType.java
@@ -29,4 +29,17 @@ public enum JdbcStatementType {
 
     /** DML / DDL statement type. */
     UPDATE_STMT_TYPE;
+
+    /** Enumerated values. */
+    private static final JdbcStatementType[] VALS = values();
+
+    /**
+     * Efficiently gets enumerated value from its ordinal.
+     *
+     * @param ord Ordinal value.
+     * @return Enumerated value or {@code null} if ordinal out of range.
+     */
+    public static JdbcStatementType fromOrdinal(int ord) {
+        return ord >= 0 && ord < VALS.length ? VALS[ord] : null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
index 0e227f5..bbdd6d2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
@@ -930,7 +930,7 @@ public class PlatformCache extends PlatformAbstractTarget {
      * @param reader Reader.
      * @return Arguments.
      */
-    @Nullable private Object[] readQueryArgs(BinaryRawReaderEx reader) {
+    @Nullable public static Object[] readQueryArgs(BinaryRawReaderEx reader) {
         int cnt = reader.readInt();
 
         if (cnt > 0) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
index 4ad6a90..626b7ff 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
@@ -54,14 +54,16 @@ import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheGe
 import org.apache.ignite.internal.processors.platform.client.cache.ClientCachePutAllRequest;
 import org.apache.ignite.internal.processors.platform.client.cache.ClientCachePutIfAbsentRequest;
 import org.apache.ignite.internal.processors.platform.client.cache.ClientCachePutRequest;
-import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheRemoveIfEqualsRequest;
+import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheQueryNextPageRequest;
 import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheRemoveAllRequest;
-import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheRemoveKeysRequest;
+import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheRemoveIfEqualsRequest;
 import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheRemoveKeyRequest;
+import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheRemoveKeysRequest;
 import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheReplaceIfEqualsRequest;
 import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheReplaceRequest;
-import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheScanQueryNextPageRequest;
 import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheScanQueryRequest;
+import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheSqlFieldsQueryRequest;
+import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheSqlQueryRequest;
 
 /**
  * Thin client message parser.
@@ -172,6 +174,18 @@ public class ClientMessageParser implements ClientListenerMessageParser {
     /** */
     private static final short OP_CACHE_GET_OR_CREATE_WITH_CONFIGURATION = 35;
 
+    /** */
+    private static final short OP_QUERY_SQL = 36;
+
+    /** */
+    private static final short OP_QUERY_SQL_CURSOR_GET_PAGE = 37;
+
+    /** */
+    private static final short OP_QUERY_SQL_FIELDS = 38;
+
+    /** */
+    private static final short OP_QUERY_SQL_FIELDS_CURSOR_GET_PAGE = 39;
+
     /** Marshaller. */
     private final GridBinaryMarshaller marsh;
 
@@ -229,7 +243,7 @@ public class ClientMessageParser implements ClientListenerMessageParser {
                 return new ClientCacheScanQueryRequest(reader);
 
             case OP_QUERY_SCAN_CURSOR_GET_PAGE:
-                return new ClientCacheScanQueryNextPageRequest(reader);
+                return new ClientCacheQueryNextPageRequest(reader);
 
             case OP_RESOURCE_CLOSE:
                 return new ClientResourceCloseRequest(reader);
@@ -311,6 +325,18 @@ public class ClientMessageParser implements ClientListenerMessageParser {
 
             case OP_CACHE_GET_OR_CREATE_WITH_CONFIGURATION:
                 return new ClientCacheGetOrCreateWithConfigurationRequest(reader);
+
+            case OP_QUERY_SQL:
+                return new ClientCacheSqlQueryRequest(reader);
+
+            case OP_QUERY_SQL_CURSOR_GET_PAGE:
+                return new ClientCacheQueryNextPageRequest(reader);
+
+            case OP_QUERY_SQL_FIELDS:
+                return new ClientCacheSqlFieldsQueryRequest(reader);
+
+            case OP_QUERY_SQL_FIELDS_CURSOR_GET_PAGE:
+                return new ClientCacheQueryNextPageRequest(reader);
         }
 
         return new ClientRawRequest(reader.readLong(), ClientStatus.INVALID_OP_CODE,

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheEntryQueryCursor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheEntryQueryCursor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheEntryQueryCursor.java
new file mode 100644
index 0000000..5269342
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheEntryQueryCursor.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.client.cache;
+
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+
+import javax.cache.Cache;
+
+/**
+ * Query cursor holder.
+  */
+class ClientCacheEntryQueryCursor extends ClientCacheQueryCursor<Cache.Entry> {
+    /**
+     * Ctor.
+     *
+     * @param cursor   Cursor.
+     * @param pageSize Page size.
+     * @param ctx      Context.
+     */
+    ClientCacheEntryQueryCursor(QueryCursor<Cache.Entry> cursor, int pageSize, ClientConnectionContext ctx) {
+        super(cursor, pageSize, ctx);
+    }
+
+    /** {@inheritDoc} */
+    @Override void writeEntry(BinaryRawWriterEx writer, Cache.Entry e) {
+        writer.writeObjectDetached(e.getKey());
+        writer.writeObjectDetached(e.getValue());
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheFieldsQueryCursor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheFieldsQueryCursor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheFieldsQueryCursor.java
new file mode 100644
index 0000000..98b747b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheFieldsQueryCursor.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.client.cache;
+
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+
+import java.util.List;
+
+/**
+ * Query cursor holder.
+  */
+class ClientCacheFieldsQueryCursor extends ClientCacheQueryCursor<List> {
+    /** Column count. */
+    private final int columnCount;
+
+    /**
+     * Ctor.
+     *
+     * @param cursor   Cursor.
+     * @param pageSize Page size.
+     * @param ctx      Context.
+     */
+    ClientCacheFieldsQueryCursor(FieldsQueryCursor<List> cursor, int pageSize, ClientConnectionContext ctx) {
+        super(cursor, pageSize, ctx);
+
+        columnCount = cursor.getColumnsCount();
+    }
+
+    /** {@inheritDoc} */
+    @Override void writeEntry(BinaryRawWriterEx writer, List e) {
+        assert e.size() == columnCount;
+
+        for (Object o : e)
+            writer.writeObjectDetached(o);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryCursor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryCursor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryCursor.java
new file mode 100644
index 0000000..080ab74
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryCursor.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.client.cache;
+
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.client.ClientCloseableResource;
+import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Base query cursor holder.
+  */
+abstract class ClientCacheQueryCursor<T> implements ClientCloseableResource {
+    /** Cursor. */
+    private final QueryCursor<T> cursor;
+
+    /** Page size. */
+    private final int pageSize;
+
+    /** Context. */
+    private final ClientConnectionContext ctx;
+
+    /** Id. */
+    private long id;
+
+    /** Iterator. */
+    private Iterator<T> iterator;
+
+    /** Close guard. */
+    private final AtomicBoolean closeGuard = new AtomicBoolean();
+
+    /**
+     * Ctor.
+     *  @param cursor Cursor.
+     * @param pageSize Page size.
+     * @param ctx Context.
+     */
+    ClientCacheQueryCursor(QueryCursor<T> cursor, int pageSize, ClientConnectionContext ctx) {
+        assert cursor != null;
+        assert pageSize > 0;
+        assert ctx != null;
+
+        this.cursor = cursor;
+        this.pageSize = pageSize;
+        this.ctx = ctx;
+    }
+
+    /**
+     * Writes next page to the writer.
+     *
+     * @param writer Writer.
+     */
+    void writePage(BinaryRawWriterEx writer) {
+        Iterator<T> iter = iterator();
+
+        int cntPos = writer.reserveInt();
+        int cnt = 0;
+
+        while (cnt < pageSize && iter.hasNext()) {
+            T e = iter.next();
+
+            writeEntry(writer, e);
+
+            cnt++;
+        }
+
+        writer.writeInt(cntPos, cnt);
+
+        writer.writeBoolean(iter.hasNext());
+
+        if (!iter.hasNext())
+            ctx.resources().release(id);
+    }
+
+    /**
+     * Writes cursor entry.
+     *
+     * @param writer Writer.
+     * @param e Entry.
+     */
+    abstract void writeEntry(BinaryRawWriterEx writer, T e);
+
+    /**
+     * Closes the cursor.
+     */
+    @Override public void close() {
+        if (closeGuard.compareAndSet(false, true)) {
+            cursor.close();
+
+            ctx.decrementCursors();
+        }
+    }
+
+    /**
+     * Sets the cursor id.
+     *
+     * @param id Id.
+     */
+    public void id(long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets the cursor id.
+     *
+     * @return Id.
+     */
+    public long id() {
+        return id;
+    }
+
+    /**
+     * Gets the iterator.
+     *
+     * @return Iterator.
+     */
+    private Iterator<T> iterator() {
+        if (iterator == null)
+            iterator = cursor.iterator();
+
+        return iterator;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryNextPageRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryNextPageRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryNextPageRequest.java
new file mode 100644
index 0000000..a6aa799
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryNextPageRequest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.client.cache;
+
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+import org.apache.ignite.internal.processors.platform.client.ClientRequest;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+
+/**
+ * Query cursor next page request.
+ */
+public class ClientCacheQueryNextPageRequest extends ClientRequest {
+    /** Cursor id. */
+    private final long cursorId;
+
+    /**
+     * Ctor.
+     *
+     * @param reader Reader.
+     */
+    public ClientCacheQueryNextPageRequest(BinaryRawReader reader) {
+        super(reader);
+
+        cursorId = reader.readLong();
+    }
+
+    /** {@inheritDoc} */
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
+        ClientCacheQueryCursor cur = ctx.resources().get(cursorId);
+
+        return new ClientCacheQueryNextPageResponse(requestId(), cur);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryNextPageResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryNextPageResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryNextPageResponse.java
new file mode 100644
index 0000000..af81ac5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryNextPageResponse.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.client.cache;
+
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+
+/**
+ * Query cursor next page response.
+ */
+class ClientCacheQueryNextPageResponse extends ClientResponse {
+    /** Cursor. */
+    private final ClientCacheQueryCursor cursor;
+
+    /**
+     * Ctor.
+     *
+     * @param requestId Request id.
+     * @param cursor Cursor.
+     */
+    ClientCacheQueryNextPageResponse(long requestId, ClientCacheQueryCursor cursor) {
+        super(requestId);
+
+        assert cursor != null;
+
+        this.cursor = cursor;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void encode(BinaryRawWriterEx writer) {
+        super.encode(writer);
+
+        cursor.writePage(writer);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryResponse.java
new file mode 100644
index 0000000..ab76387
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheQueryResponse.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.client.cache;
+
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+
+/**
+ * Scan query response.
+ */
+class ClientCacheQueryResponse extends ClientResponse {
+    /** Cursor. */
+    private final ClientCacheQueryCursor cursor;
+
+    /**
+     * Ctor.
+     *
+     * @param requestId Request id.
+     * @param cursor Cursor.
+     */
+    ClientCacheQueryResponse(long requestId, ClientCacheQueryCursor cursor) {
+        super(requestId);
+
+        assert cursor != null;
+
+        this.cursor = cursor;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void encode(BinaryRawWriterEx writer) {
+        super.encode(writer);
+
+        writer.writeLong(cursor.id());
+
+        cursor.writePage(writer);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java
index b290a5b..44416be 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java
@@ -77,14 +77,34 @@ class ClientCacheRequest extends ClientRequest {
      * @return Cache.
      */
     protected IgniteCache rawCache(ClientConnectionContext ctx) {
-        DynamicCacheDescriptor cacheDesc = ctx.kernalContext().cache().cacheDescriptor(cacheId);
-
-        if (cacheDesc == null)
-            throw new IgniteClientException(ClientStatus.CACHE_DOES_NOT_EXIST, "Cache does not exist [cacheId= " +
-                cacheId + "]", null);
+        DynamicCacheDescriptor cacheDesc = cacheDescriptor(ctx);
 
         String cacheName = cacheDesc.cacheName();
 
         return ctx.kernalContext().grid().cache(cacheName);
     }
+
+    /**
+     * Gets the cache descriptor.
+     *
+     * @return Cache descriptor.
+     */
+    protected DynamicCacheDescriptor cacheDescriptor(ClientConnectionContext ctx) {
+        DynamicCacheDescriptor desc = ctx.kernalContext().cache().cacheDescriptor(cacheId);
+
+        if (desc == null)
+            throw new IgniteClientException(ClientStatus.CACHE_DOES_NOT_EXIST, "Cache does not exist [cacheId= " +
+                    cacheId + "]", null);
+
+        return desc;
+    }
+
+    /**
+     * Gets the cache id.
+     *
+     * @return Cache id.
+     */
+    protected int cacheId() {
+        return cacheId;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryCursor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryCursor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryCursor.java
deleted file mode 100644
index 9d3d158..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryCursor.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.processors.platform.client.cache;
-
-import org.apache.ignite.internal.binary.BinaryRawWriterEx;
-import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
-import org.apache.ignite.internal.processors.platform.client.ClientCloseableResource;
-import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
-
-import javax.cache.Cache;
-import java.util.Iterator;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Query cursor holder.
-  */
-class ClientCacheScanQueryCursor implements ClientCloseableResource {
-    /** Cursor. */
-    private final QueryCursorEx<Cache.Entry> cursor;
-
-    /** Page size. */
-    private final int pageSize;
-
-    /** Context. */
-    private final ClientConnectionContext ctx;
-
-    /** Id. */
-    private long id;
-
-    /** Iterator. */
-    private Iterator<Cache.Entry> iterator;
-
-    /** Close guard. */
-    private final AtomicBoolean closeGuard = new AtomicBoolean();
-
-    /**
-     * Ctor.
-     *  @param cursor Cursor.
-     * @param pageSize Page size.
-     * @param ctx Context.
-     */
-    ClientCacheScanQueryCursor(QueryCursorEx<Cache.Entry> cursor, int pageSize, ClientConnectionContext ctx) {
-        assert cursor != null;
-        assert pageSize > 0;
-        assert ctx != null;
-
-        this.cursor = cursor;
-        this.pageSize = pageSize;
-        this.ctx = ctx;
-    }
-
-    /**
-     * Writes next page to the writer.
-     *
-     * @param writer Writer.
-     */
-    void writePage(BinaryRawWriterEx writer) {
-        Iterator<Cache.Entry> iter = iterator();
-
-        int cntPos = writer.reserveInt();
-        int cnt = 0;
-
-        while (cnt < pageSize && iter.hasNext()) {
-            Cache.Entry e = iter.next();
-
-            writer.writeObjectDetached(e.getKey());
-            writer.writeObjectDetached(e.getValue());
-
-            cnt++;
-        }
-
-        writer.writeInt(cntPos, cnt);
-
-        writer.writeBoolean(iter.hasNext());
-
-        if (!iter.hasNext())
-            ctx.resources().release(id);
-    }
-
-    /**
-     * Closes the cursor.
-     */
-    @Override public void close() {
-        if (closeGuard.compareAndSet(false, true)) {
-            cursor.close();
-
-            ctx.decrementCursors();
-        }
-    }
-
-    /**
-     * Sets the cursor id.
-     *
-     * @param id Id.
-     */
-    public void id(long id) {
-        this.id = id;
-    }
-
-    /**
-     * Gets the cursor id.
-     *
-     * @return Id.
-     */
-    public long id() {
-        return id;
-    }
-
-    /**
-     * Gets the iterator.
-     *
-     * @return Iterator.
-     */
-    private Iterator<Cache.Entry> iterator() {
-        if (iterator == null)
-            iterator = cursor.iterator();
-
-        return iterator;
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageRequest.java
deleted file mode 100644
index a9620d2..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageRequest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.processors.platform.client.cache;
-
-import org.apache.ignite.binary.BinaryRawReader;
-import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
-import org.apache.ignite.internal.processors.platform.client.ClientRequest;
-import org.apache.ignite.internal.processors.platform.client.ClientResponse;
-
-/**
- * Query cursor next page request.
- */
-public class ClientCacheScanQueryNextPageRequest extends ClientRequest {
-    /** Cursor id. */
-    private final long cursorId;
-
-    /**
-     * Ctor.
-     *
-     * @param reader Reader.
-     */
-    public ClientCacheScanQueryNextPageRequest(BinaryRawReader reader) {
-        super(reader);
-
-        cursorId = reader.readLong();
-    }
-
-    /** {@inheritDoc} */
-    @Override public ClientResponse process(ClientConnectionContext ctx) {
-        ClientCacheScanQueryCursor cur = ctx.resources().get(cursorId);
-
-        return new ClientCacheScanQueryNextPageResponse(requestId(), cur);
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageResponse.java
deleted file mode 100644
index e4ffe6d..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageResponse.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.processors.platform.client.cache;
-
-import org.apache.ignite.internal.binary.BinaryRawWriterEx;
-import org.apache.ignite.internal.processors.platform.client.ClientResponse;
-
-/**
- * Query cursor next page response.
- */
-class ClientCacheScanQueryNextPageResponse extends ClientResponse {
-    /** Cursor. */
-    private final ClientCacheScanQueryCursor cursor;
-
-    /**
-     * Ctor.
-     *
-     * @param requestId Request id.
-     * @param cursor Cursor.
-     */
-    ClientCacheScanQueryNextPageResponse(long requestId, ClientCacheScanQueryCursor cursor) {
-        super(requestId);
-
-        assert cursor != null;
-
-        this.cursor = cursor;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void encode(BinaryRawWriterEx writer) {
-        super.encode(writer);
-
-        cursor.writePage(writer);
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryRequest.java
index 7c163e3..26ab236 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryRequest.java
@@ -23,7 +23,6 @@ import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.cache.query.QueryCursor;
 import org.apache.ignite.cache.query.ScanQuery;
 import org.apache.ignite.internal.binary.BinaryRawReaderEx;
-import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
 import org.apache.ignite.internal.processors.platform.PlatformContext;
 import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
 import org.apache.ignite.internal.processors.platform.client.ClientResponse;
@@ -94,13 +93,13 @@ public class ClientCacheScanQueryRequest extends ClientCacheRequest {
         try {
             QueryCursor cur = cache.query(qry);
 
-            ClientCacheScanQueryCursor cliCur = new ClientCacheScanQueryCursor((QueryCursorEx)cur, pageSize, ctx);
+            ClientCacheEntryQueryCursor cliCur = new ClientCacheEntryQueryCursor(cur, pageSize, ctx);
 
             long cursorId = ctx.resources().put(cliCur);
 
             cliCur.id(cursorId);
 
-            return new ClientCacheScanQueryResponse(requestId(), cliCur);
+            return new ClientCacheQueryResponse(requestId(), cliCur);
         }
         catch (Exception e) {
             ctx.decrementCursors();

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryResponse.java
deleted file mode 100644
index 0623804..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryResponse.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.processors.platform.client.cache;
-
-import org.apache.ignite.internal.binary.BinaryRawWriterEx;
-import org.apache.ignite.internal.processors.platform.client.ClientResponse;
-
-/**
- * Scan query response.
- */
-class ClientCacheScanQueryResponse extends ClientResponse {
-    /** Cursor. */
-    private final ClientCacheScanQueryCursor cursor;
-
-    /**
-     * Ctor.
-     *
-     * @param requestId Request id.
-     * @param cursor Cursor.
-     */
-    ClientCacheScanQueryResponse(long requestId, ClientCacheScanQueryCursor cursor) {
-        super(requestId);
-
-        assert cursor != null;
-
-        this.cursor = cursor;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void encode(BinaryRawWriterEx writer) {
-        super.encode(writer);
-
-        writer.writeLong(cursor.id());
-
-        cursor.writePage(writer);
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryRequest.java
new file mode 100644
index 0000000..ca3595d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryRequest.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.client.cache;
+
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.internal.binary.BinaryRawReaderEx;
+import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
+import org.apache.ignite.internal.processors.cache.query.SqlFieldsQueryEx;
+import org.apache.ignite.internal.processors.odbc.jdbc.JdbcStatementType;
+import org.apache.ignite.internal.processors.platform.cache.PlatformCache;
+import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Sql query request.
+ */
+@SuppressWarnings("unchecked")
+public class ClientCacheSqlFieldsQueryRequest extends ClientCacheRequest {
+    /** Query. */
+    private final SqlFieldsQuery qry;
+
+    /** Include field names flag. */
+    private final boolean includeFieldNames;
+
+    /**
+     * Ctor.
+     *
+     * @param reader Reader.
+     */
+    public ClientCacheSqlFieldsQueryRequest(BinaryRawReaderEx reader) {
+        super(reader);
+
+        // Same request format as in JdbcQueryExecuteRequest.
+        String schema = reader.readString();
+        int pageSize = reader.readInt();
+        reader.readInt();  // maxRows
+        String sql = reader.readString();
+        Object[] args = PlatformCache.readQueryArgs(reader);
+        JdbcStatementType stmtType = JdbcStatementType.fromOrdinal(reader.readByte());
+        boolean distributedJoins = reader.readBoolean();
+        boolean loc = reader.readBoolean();
+        boolean replicatedOnly = reader.readBoolean();
+        boolean enforceJoinOrder = reader.readBoolean();
+        boolean collocated = reader.readBoolean();
+        boolean lazy = reader.readBoolean();
+        int timeout = (int) reader.readLong();
+        includeFieldNames = reader.readBoolean();
+
+        SqlFieldsQuery qry = stmtType == JdbcStatementType.ANY_STATEMENT_TYPE
+                ? new SqlFieldsQuery(sql)
+                : new SqlFieldsQueryEx(sql,stmtType == JdbcStatementType.SELECT_STATEMENT_TYPE);
+
+        qry.setSchema(schema)
+                .setPageSize(pageSize)
+                .setArgs(args)
+                .setDistributedJoins(distributedJoins)
+                .setLocal(loc)
+                .setReplicatedOnly(replicatedOnly)
+                .setEnforceJoinOrder(enforceJoinOrder)
+                .setCollocated(collocated)
+                .setLazy(lazy)
+                .setTimeout(timeout, TimeUnit.MILLISECONDS);
+
+        this.qry = qry;
+    }
+
+    /** {@inheritDoc} */
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
+        ctx.incrementCursors();
+
+        try {
+            // If cacheId is provided, we must check the cache for existence.
+            if (cacheId() != 0) {
+                DynamicCacheDescriptor desc = cacheDescriptor(ctx);
+
+                if (qry.getSchema() == null) {
+                    String schema = QueryUtils.normalizeSchemaName(desc.cacheName(),
+                            desc.cacheConfiguration().getSqlSchema());
+
+                    qry.setSchema(schema);
+                }
+            }
+
+            List<FieldsQueryCursor<List<?>>> curs = ctx.kernalContext().query()
+                    .querySqlFieldsNoCache(qry, true, true);
+
+            assert curs.size() == 1;
+
+            FieldsQueryCursor cur = curs.get(0);
+
+            ClientCacheFieldsQueryCursor cliCur = new ClientCacheFieldsQueryCursor(
+                    cur, qry.getPageSize(), ctx);
+
+            long cursorId = ctx.resources().put(cliCur);
+
+            cliCur.id(cursorId);
+
+            return new ClientCacheSqlFieldsQueryResponse(requestId(), cliCur, cur, includeFieldNames);
+        }
+        catch (Exception e) {
+            ctx.decrementCursors();
+
+            throw e;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryResponse.java
new file mode 100644
index 0000000..1ff2ea5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryResponse.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.client.cache;
+
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+
+import java.util.List;
+
+/**
+ * Scan query response.
+ */
+class ClientCacheSqlFieldsQueryResponse extends ClientResponse {
+    /** Cursor. */
+    private final ClientCacheQueryCursor cursor;
+
+    /** Fields cursor. */
+    private final FieldsQueryCursor<List> fieldsCursor;
+
+    /** Include field names flag. */
+    private final boolean includeFieldNames;
+
+    /**
+     * Ctor.
+     * @param requestId Request id.
+     * @param cursor Client cursor.
+     * @param fieldsCursor Fields cursor.
+     * @param includeFieldNames Whether to include field names.
+     */
+    ClientCacheSqlFieldsQueryResponse(long requestId, ClientCacheQueryCursor cursor,
+                                      FieldsQueryCursor<List> fieldsCursor, boolean includeFieldNames) {
+        super(requestId);
+
+        assert cursor != null;
+        assert fieldsCursor != null;
+
+        this.cursor = cursor;
+        this.fieldsCursor = fieldsCursor;
+        this.includeFieldNames = includeFieldNames;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void encode(BinaryRawWriterEx writer) {
+        super.encode(writer);
+
+        writer.writeLong(cursor.id());
+
+        int cnt = fieldsCursor.getColumnsCount();
+        writer.writeInt(cnt);
+
+        if (includeFieldNames) {
+            for (int i = 0; i < cnt; i++) {
+                writer.writeString(fieldsCursor.getFieldName(i));
+            }
+        }
+
+        cursor.writePage(writer);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlQueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlQueryRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlQueryRequest.java
new file mode 100644
index 0000000..8c21be1
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlQueryRequest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.platform.client.cache;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.SqlQuery;
+import org.apache.ignite.internal.binary.BinaryRawReaderEx;
+import org.apache.ignite.internal.processors.platform.cache.PlatformCache;
+import org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Sql query request.
+ */
+@SuppressWarnings("unchecked")
+public class ClientCacheSqlQueryRequest extends ClientCacheRequest {
+    /** Query. */
+    private final SqlQuery qry;
+
+    /**
+     * Ctor.
+     *
+     * @param reader Reader.
+     */
+    public ClientCacheSqlQueryRequest(BinaryRawReaderEx reader) {
+        super(reader);
+
+        qry = new SqlQuery(reader.readString(), reader.readString())
+                .setArgs(PlatformCache.readQueryArgs(reader))
+                .setDistributedJoins(reader.readBoolean())
+                .setLocal(reader.readBoolean())
+                .setReplicatedOnly(reader.readBoolean())
+                .setPageSize(reader.readInt())
+                .setTimeout((int) reader.readLong(), TimeUnit.MILLISECONDS);
+    }
+
+    /** {@inheritDoc} */
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
+        IgniteCache cache = cache(ctx);
+
+        ctx.incrementCursors();
+
+        try {
+            QueryCursor cur = cache.query(qry);
+
+            ClientCacheEntryQueryCursor cliCur = new ClientCacheEntryQueryCursor(
+                    cur, qry.getPageSize(), ctx);
+
+            long cursorId = ctx.resources().put(cliCur);
+
+            cliCur.id(cursorId);
+
+            return new ClientCacheQueryResponse(requestId(), cliCur);
+        }
+        catch (Exception e) {
+            ctx.decrementCursors();
+
+            throw e;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index 2e34ba2..2d5a54b 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
@@ -112,6 +112,7 @@
     <Compile Include="Client\Cache\CreateCacheTest.cs" />
     <Compile Include="Client\Cache\ScanQueryTest.cs" />
     <Compile Include="Client\Cache\Person.cs" />
+    <Compile Include="Client\Cache\SqlQueryTest.cs" />
     <Compile Include="Client\ClientTestBase.cs" />
     <Compile Include="Client\RawSocketTest.cs" />
     <Compile Include="Client\ClientConnectionTest.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
index 782e3cc..a2ca65d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
@@ -52,16 +52,7 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
             using (var client = Ignition.StartClient(cfg))
             {
                 var serverCache = Ignition.GetIgnite().GetOrCreateCache<int?, Person>(
-                    new CacheConfiguration("person", new QueryEntity
-                    {
-                        KeyType = typeof(int),
-                        ValueType = typeof(Person),
-                        Fields = new[]
-                        {
-                            new QueryField("id", typeof(int)),
-                            new QueryField("name", typeof(string))
-                        }
-                    }));
+                    new CacheConfiguration("person", typeof(Person)));
 
                 var clientCache = client.GetCache<int?, Person>(serverCache.Name);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/Person.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/Person.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/Person.cs
index a6bc9d7..327e707 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/Person.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/Person.cs
@@ -17,22 +17,51 @@
 
 namespace Apache.Ignite.Core.Tests.Client.Cache
 {
+    using System;
+    using Apache.Ignite.Core.Cache.Configuration;
+
     /// <summary>
     /// Test person.
     /// </summary>
     public class Person
     {
         /// <summary>
+        /// Initializes a new instance of the <see cref="Person"/> class.
+        /// </summary>
+        public Person()
+        {
+            DateTime = DateTime.UtcNow;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Person"/> class.
+        /// </summary>
+        public Person(int id)
+        {
+            Id = id;
+            Name = "Person " + id;
+            DateTime = DateTime.UtcNow.AddDays(id);
+        }
+
+        /// <summary>
         /// Gets or sets the identifier.
         /// </summary>
+        [QuerySqlField(IsIndexed = true)]
         public int Id { get; set; }
 
         /// <summary>
         /// Gets or sets the name.
         /// </summary>
+        [QuerySqlField]
         public string Name { get; set; }
 
         /// <summary>
+        /// Gets or sets the date time.
+        /// </summary>
+        [QuerySqlField]
+        public DateTime DateTime { get; set; }
+
+        /// <summary>
         /// Gets or sets the parent.
         /// </summary>
         public Person Parent { get;set; }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/SqlQueryTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/SqlQueryTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/SqlQueryTest.cs
new file mode 100644
index 0000000..720a71b
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/SqlQueryTest.cs
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Tests.Client.Cache
+{
+    using System;
+    using System.Linq;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Cache.Query;
+    using Apache.Ignite.Core.Client;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests SQL queries via thin client.
+    /// </summary>
+    public class SqlQueryTest : ClientTestBase
+    {
+        /// <summary>
+        /// Cache item count.
+        /// </summary>
+        private const int Count = 10;
+
+        /// <summary>
+        /// Second cache name.
+        /// </summary>
+        private const string CacheName2 = CacheName + "2";
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ScanQueryTest"/> class.
+        /// </summary>
+        public SqlQueryTest() : base(2)
+        {
+            // No-op.
+        }
+
+        /// <summary>
+        /// Sets up the test.
+        /// </summary>
+        public override void TestSetUp()
+        {
+            InitCache(CacheName);
+            InitCache(CacheName2);
+        }
+
+        /// <summary>
+        /// Tests the SQL query.
+        /// </summary>
+        [Test]
+        public void TestSqlQuery()
+        {
+            var cache = GetClientCache<Person>();
+
+            // All items.
+            var qry = new SqlQuery(typeof(Person), "where 1 = 1");
+            Assert.AreEqual(Count, cache.Query(qry).Count());
+
+            // All items local.
+            qry.Local = true;
+            Assert.Greater(Count, cache.Query(qry).Count());
+
+            // Filter.
+            qry = new SqlQuery(typeof(Person), "where Name like '%7'");
+            Assert.AreEqual(7, cache.Query(qry).Single().Key);
+
+            // Args.
+            qry = new SqlQuery(typeof(Person), "where Id = ?", 3);
+            Assert.AreEqual(3, cache.Query(qry).Single().Value.Id);
+
+            // DateTime.
+            qry = new SqlQuery(typeof(Person), "where DateTime > ?", DateTime.UtcNow.AddDays(Count - 1));
+            Assert.AreEqual(Count, cache.Query(qry).Single().Key);
+
+            // Invalid args.
+            qry.Sql = null;
+            Assert.Throws<ArgumentNullException>(() => cache.Query(qry));
+
+            qry.Sql = "abc";
+            qry.QueryType = null;
+            Assert.Throws<ArgumentNullException>(() => cache.Query(qry));
+        }
+
+        /// <summary>
+        /// Tests the SQL query with distributed joins.
+        /// </summary>
+        [Test]
+        public void TestSqlQueryDistributedJoins()
+        {
+            var cache = GetClientCache<Person>();
+
+            // Non-distributed join returns incomplete results.
+            var qry = new SqlQuery(typeof(Person),
+                string.Format("from \"{0}\".Person, \"{1}\".Person as p2 where Person.Id = 11 - p2.Id",
+                    CacheName, CacheName2));
+            
+            Assert.Greater(Count, cache.Query(qry).Count());
+
+            // Distributed join fixes the problem.
+            qry.EnableDistributedJoins = true;
+            Assert.AreEqual(Count, cache.Query(qry).Count());
+        }
+
+        /// <summary>
+        /// Tests the fields query.
+        /// </summary>
+        [Test]
+        public void TestFieldsQuery()
+        {
+            var cache = GetClientCache<Person>();
+
+            // All items.
+            var qry = new SqlFieldsQuery("select Id from Person");
+            var cursor = cache.Query(qry);
+            CollectionAssert.AreEquivalent(Enumerable.Range(1, Count), cursor.Select(x => (int) x[0]));
+            Assert.AreEqual("ID", cursor.FieldNames.Single());
+
+            // All items local.
+            // TODO: IGNITE-5571 - exception should be fixed.
+            qry.Local = true;
+            Assert.Throws<IgniteClientException>(() => Assert.Greater(Count, cache.Query(qry).Count()));
+
+            // Filter.
+            qry = new SqlFieldsQuery("select Name from Person where Id = ?", 1)
+            {
+                Lazy = true,
+                PageSize = 5,
+            };
+            Assert.AreEqual("Person 1", cache.Query(qry).Single().Single());
+
+            // DateTime.
+            qry = new SqlFieldsQuery("select Id, DateTime from Person where DateTime > ?", DateTime.UtcNow.AddDays(9));
+            Assert.AreEqual(cache[Count].DateTime, cache.Query(qry).Single().Last());
+
+            // Invalid args.
+            qry.Sql = null;
+            Assert.Throws<ArgumentNullException>(() => cache.Query(qry));
+        }
+
+        /// <summary>
+        /// Tests the SQL fields query with distributed joins.
+        /// </summary>
+        [Test]
+        public void TestFieldsQueryDistributedJoins()
+        {
+            var cache = GetClientCache<Person>();
+
+            // Non-distributed join returns incomplete results.
+            var qry = new SqlFieldsQuery(string.Format(
+                "select p2.Name from \"{0}\".Person, \"{1}\".Person as p2 where Person.Id = 11 - p2.Id", 
+                CacheName, CacheName2));
+
+            Assert.Greater(Count, cache.Query(qry).Count());
+
+            // Distributed join fixes the problem.
+            qry.EnableDistributedJoins = true;
+            Assert.AreEqual(Count, cache.Query(qry).Count());
+        }
+
+        /// <summary>
+        /// Tests the fields query timeout.
+        /// </summary>
+        [Test]
+        public void TestFieldsQueryTimeout()
+        {
+            var cache = GetClientCache<Person>();
+
+            cache.PutAll(Enumerable.Range(1, 30000).ToDictionary(x => x, x => new Person(x)));
+
+            var qry = new SqlFieldsQuery("select * from Person where Name like '%ers%'")
+            {
+                Timeout = TimeSpan.FromMilliseconds(1)
+            };
+
+            Assert.Throws<IgniteClientException>(() => cache.Query(qry).GetAll());
+        }
+
+        /// <summary>
+        /// Tests the fields query on a missing cache.
+        /// </summary>
+        [Test]
+        public void TestFieldsQueryMissingCache()
+        {
+            var cache = Client.GetCache<int, Person>("I do not exist");
+            var qry = new SqlFieldsQuery("select name from person")
+            {
+                Schema = CacheName
+            };
+
+            // Schema is set => we still check for cache existence.
+            var ex = Assert.Throws<IgniteClientException>(() => cache.Query(qry).GetAll());
+            Assert.AreEqual("Cache doesn't exist: I do not exist", ex.Message);
+
+            // Schema not set => also exception.
+            qry.Schema = null;
+            ex = Assert.Throws<IgniteClientException>(() => cache.Query(qry).GetAll());
+            Assert.AreEqual("Cache doesn't exist: I do not exist", ex.Message);
+        }
+
+        /// <summary>
+        /// Tests fields query with custom schema.
+        /// </summary>
+        [Test]
+        public void TestFieldsQueryCustomSchema()
+        {
+            var cache1 = Client.GetCache<int, Person>(CacheName);
+            var cache2 = Client.GetCache<int, Person>(CacheName2);
+
+            cache1.RemoveAll();
+
+            var qry = new SqlFieldsQuery("select name from person");
+
+            // Schema not set: cache name is used.
+            Assert.AreEqual(0, cache1.Query(qry).Count());
+            Assert.AreEqual(Count, cache2.Query(qry).Count());
+
+            // Schema set to first cache: no results both cases.
+            qry.Schema = cache1.Name;
+            Assert.AreEqual(0, cache1.Query(qry).Count());
+            Assert.AreEqual(0, cache2.Query(qry).Count());
+
+            // Schema set to second cache: full results both cases.
+            qry.Schema = cache2.Name;
+            Assert.AreEqual(Count, cache1.Query(qry).Count());
+            Assert.AreEqual(Count, cache2.Query(qry).Count());
+        }
+
+        /// <summary>
+        /// Tests the DML.
+        /// </summary>
+        [Test]
+        public void TestDml()
+        {
+            var cache = GetClientCache<Person>();
+
+            var qry = new SqlFieldsQuery("insert into Person (_key, id, name) values (?, ?, ?)", -10, 1, "baz");
+            var res = cache.Query(qry).GetAll();
+
+            Assert.AreEqual(1, res[0][0]);
+            Assert.AreEqual("baz", cache[-10].Name);
+        }
+
+        /// <summary>
+        /// Initializes the cache.
+        /// </summary>
+        private static void InitCache(string cacheName)
+        {
+            var cache = Ignition.GetIgnite().GetOrCreateCache<int, Person>(
+                new CacheConfiguration(cacheName, new QueryEntity(typeof(int), typeof(Person))));
+
+            cache.RemoveAll();
+
+            cache.PutAll(Enumerable.Range(1, Count).ToDictionary(x => x, x => new Person(x)));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs
index 9b7a566..e1d30b9 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs
@@ -20,6 +20,7 @@ namespace Apache.Ignite.Core.Tests.Client
     using System.Net;
     using Apache.Ignite.Core.Cache;
     using Apache.Ignite.Core.Client;
+    using Apache.Ignite.Core.Client.Cache;
     using NUnit.Framework;
 
     /// <summary>
@@ -81,7 +82,7 @@ namespace Apache.Ignite.Core.Tests.Client
         /// Sets up the test.
         /// </summary>
         [SetUp]
-        public void TestSetUp()
+        public virtual void TestSetUp()
         {
             GetCache<int>().RemoveAll();
         }
@@ -100,6 +101,14 @@ namespace Apache.Ignite.Core.Tests.Client
         }
 
         /// <summary>
+        /// Gets the client cache.
+        /// </summary>
+        protected ICacheClient<int, T> GetClientCache<T>()
+        {
+            return Client.GetCache<int, T>(CacheName);
+        }
+
+        /// <summary>
         /// Gets the client.
         /// </summary>
         protected IIgniteClient GetClient()

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
index 0076d47..21738a2 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -104,6 +104,7 @@
     <Compile Include="Cache\Configuration\MemoryPolicyConfiguration.cs" />
     <Compile Include="Cache\Configuration\PartitionLossPolicy.cs" />
     <Compile Include="Cache\IMemoryMetrics.cs" />
+    <Compile Include="Cache\Query\IFieldsQueryCursor.cs" />
     <Compile Include="Client\Cache\ICacheClient.cs" />
     <Compile Include="Client\IgniteClientConfiguration.cs" />
     <Compile Include="Client\IgniteClientException.cs" />
@@ -121,10 +122,13 @@
     <Compile Include="Impl\Binary\MultidimensionalArraySerializer.cs" />
     <Compile Include="Impl\Client\Cache\CacheFlags.cs" />
     <Compile Include="Impl\Client\Cache\ClientCacheConfigurationSerializer.cs" />
+    <Compile Include="Impl\Client\Cache\Query\ClientFieldsQueryCursor.cs" />
     <Compile Include="Impl\Client\Cache\Query\ClientQueryCursor.cs" />
     <Compile Include="Impl\Cache\Query\PlatformQueryQursorBase.cs" />
     <Compile Include="Impl\Binary\BinaryProcessorClient.cs" />
     <Compile Include="Impl\Binary\IBinaryProcessor.cs" />
+    <Compile Include="Impl\Client\Cache\Query\ClientQueryCursorBase.cs" />
+    <Compile Include="Impl\Client\Cache\Query\StatementType.cs" />
     <Compile Include="Impl\Client\ClientStatus.cs" />
     <Compile Include="Events\LocalEventListener.cs" />
     <Compile Include="Impl\DataStorageMetrics.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/IFieldsQueryCursor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/IFieldsQueryCursor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/IFieldsQueryCursor.cs
new file mode 100644
index 0000000..fbeaf8c
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/IFieldsQueryCursor.cs
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Cache.Query
+{
+    using System.Collections.Generic;
+    using System.Diagnostics.CodeAnalysis;
+
+    /// <summary>
+    /// Fields query cursor.
+    /// </summary>
+    [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
+    public interface IFieldsQueryCursor : IQueryCursor<IList<object>>
+    {
+        /// <summary>
+        /// Gets the field names.
+        /// </summary>
+        IList<string> FieldNames { get; }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs
index a3964c6..eb91b0a 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Client/Cache/ICacheClient.cs
@@ -100,6 +100,20 @@ namespace Apache.Ignite.Core.Client.Cache
         IQueryCursor<ICacheEntry<TK, TV>> Query(ScanQuery<TK, TV> scanQuery);
 
         /// <summary>
+        /// Executes an SQL query.
+        /// </summary>
+        /// <param name="sqlQuery">SQL query.</param>
+        /// <returns>Query cursor.</returns>
+        IQueryCursor<ICacheEntry<TK, TV>> Query(SqlQuery sqlQuery);
+
+        /// <summary>
+        /// Executes an SQL Fields query.
+        /// </summary>
+        /// <param name="sqlFieldsQuery">SQL query.</param>
+        /// <returns>Query cursor.</returns>
+        IFieldsQueryCursor Query(SqlFieldsQuery sqlFieldsQuery);
+
+        /// <summary>
         /// Associates the specified value with the specified key in this cache,
         /// returning an existing value if one existed.
         /// </summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
index 17dc93b..c60e010 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
@@ -18,19 +18,13 @@
 namespace Apache.Ignite.Core.Impl.Cache.Query
 {
     using System;
-    using System.Diagnostics;
-    using System.Diagnostics.CodeAnalysis;
     using Apache.Ignite.Core.Binary;
-    using Apache.Ignite.Core.Impl.Binary;
 
     /// <summary>
     /// Cursor for entry-based queries.
     /// </summary>
     internal class FieldsQueryCursor<T> : PlatformQueryQursorBase<T>
     {
-        /** */
-        private readonly Func<IBinaryRawReader, int, T> _readerFunc;
-
         /// <summary>
         /// Constructor.
         /// </summary>
@@ -39,23 +33,18 @@ namespace Apache.Ignite.Core.Impl.Cache.Query
         /// <param name="readerFunc">The reader function.</param>
         public FieldsQueryCursor(IPlatformTargetInternal target, bool keepBinary, 
             Func<IBinaryRawReader, int, T> readerFunc)
-            : base(target, keepBinary)
-        {
-            Debug.Assert(readerFunc != null);
-
-            _readerFunc = readerFunc;
-        }
+            : base(target, keepBinary, r =>
+            {
+                // Reading and skipping row size in bytes.
+                r.ReadInt();
 
-        /** <inheritdoc /> */
-        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
-        protected override T Read(BinaryReader reader)
-        {
-            // Reading and skipping row size in bytes.
-            reader.ReadInt();
+                int cnt = r.ReadInt();
 
-            int cnt = reader.ReadInt();
+                return readerFunc(r, cnt);
 
-            return _readerFunc(reader, cnt);
+            })
+        {
+            // No-op.
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/PlatformQueryQursorBase.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/PlatformQueryQursorBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/PlatformQueryQursorBase.cs
index 8a51dab..fc78392 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/PlatformQueryQursorBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/PlatformQueryQursorBase.cs
@@ -17,7 +17,9 @@
 
 namespace Apache.Ignite.Core.Impl.Cache.Query
 {
+    using System;
     using System.Collections.Generic;
+    using Apache.Ignite.Core.Impl.Binary;
 
     /// <summary>
     /// Base for platform cursors.
@@ -44,8 +46,10 @@ namespace Apache.Ignite.Core.Impl.Cache.Query
         /// </summary>
         /// <param name="target">The target.</param>
         /// <param name="keepBinary">Keep binary flag.</param>
-        protected PlatformQueryQursorBase(IPlatformTargetInternal target, bool keepBinary) 
-            : base(target.Marshaller, keepBinary)
+        /// <param name="readFunc"></param>
+        protected PlatformQueryQursorBase(IPlatformTargetInternal target, bool keepBinary, 
+            Func<BinaryReader, T> readFunc) 
+            : base(target.Marshaller, keepBinary, readFunc)
         {
             _target = target;
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursor.cs
index b967d6a..ca773fe 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursor.cs
@@ -17,9 +17,7 @@
 
 namespace Apache.Ignite.Core.Impl.Cache.Query
 {
-    using System.Diagnostics.CodeAnalysis;
     using Apache.Ignite.Core.Cache;
-    using Apache.Ignite.Core.Impl.Binary;
 
     /// <summary>
     /// Cursor for entry-based queries.
@@ -31,19 +29,11 @@ namespace Apache.Ignite.Core.Impl.Cache.Query
         /// </summary>
         /// <param name="target">Target.</param>
         /// <param name="keepBinary">Keep poratble flag.</param>
-        public QueryCursor(IPlatformTargetInternal target, bool keepBinary) : base(target, keepBinary)
+        public QueryCursor(IPlatformTargetInternal target, bool keepBinary)
+            : base(target, keepBinary,
+                r => new CacheEntry<TK, TV>(r.ReadObject<TK>(), r.ReadObject<TV>()))
         {
             // No-op.
         }
-
-        /** <inheritdoc /> */
-        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
-        protected override ICacheEntry<TK, TV> Read(BinaryReader reader)
-        {
-            TK key = reader.ReadObject<TK>();
-            TV val = reader.ReadObject<TV>();
-
-            return new CacheEntry<TK, TV>(key, val);
-        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0bd712dd/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs
index 216d7ea..c8c02ad 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/QueryCursorBase.cs
@@ -21,7 +21,6 @@ namespace Apache.Ignite.Core.Impl.Cache.Query
     using System.Collections;
     using System.Collections.Generic;
     using System.Diagnostics;
-    using System.Diagnostics.CodeAnalysis;
     using Apache.Ignite.Core.Cache.Query;
     using Apache.Ignite.Core.Impl.Binary;
     using Apache.Ignite.Core.Impl.Binary.IO;
@@ -40,6 +39,9 @@ namespace Apache.Ignite.Core.Impl.Cache.Query
         /** Marshaller. */
         private readonly Marshaller _marsh;
 
+        /** Read func. */
+        private readonly Func<BinaryReader, T> _readFunc;
+
         /** Wherther "GetAll" was called. */
         private bool _getAllCalled;
 
@@ -63,14 +65,15 @@ namespace Apache.Ignite.Core.Impl.Cache.Query
         /// </summary>
         /// <param name="marsh">Marshaller.</param>
         /// <param name="keepBinary">Keep binary flag.</param>
+        /// <param name="readFunc">The read function.</param>
         /// <param name="initialBatchStream">Optional stream with initial batch.</param>
-        [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
-            Justification = "ConvertGetBatch calls Read, which does not rely on constructor being run.")]
-        protected QueryCursorBase(Marshaller marsh, bool keepBinary, IBinaryStream initialBatchStream = null)
+        protected QueryCursorBase(Marshaller marsh, bool keepBinary, Func<BinaryReader, T> readFunc, 
+            IBinaryStream initialBatchStream = null)
         {
             Debug.Assert(marsh != null);
 
             _keepBinary = keepBinary;
+            _readFunc = readFunc;
             _marsh = marsh;
 
             if (initialBatchStream != null)
@@ -198,13 +201,6 @@ namespace Apache.Ignite.Core.Impl.Cache.Query
         protected abstract IList<T> GetAllInternal();
 
         /// <summary>
-        /// Reads entry from the reader.
-        /// </summary> 
-        /// <param name="reader">Reader.</param>
-        /// <returns>Entry.</returns>
-        protected abstract T Read(BinaryReader reader);
-
-        /// <summary>
         /// Requests next batch.
         /// </summary>
         private void RequestBatch()
@@ -233,7 +229,7 @@ namespace Apache.Ignite.Core.Impl.Cache.Query
             var res = new List<T>(size);
 
             for (var i = 0; i < size; i++)
-                res.Add(Read(reader));
+                res.Add(_readFunc(reader));
 
             return res;
         }
@@ -259,7 +255,7 @@ namespace Apache.Ignite.Core.Impl.Cache.Query
 
             for (var i = 0; i < size; i++)
             {
-                res[i] = Read(reader);
+                res[i] = _readFunc(reader);
             }
 
             _hasNext = stream.ReadBool();