You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by is...@apache.org on 2020/11/12 17:38:08 UTC

[ignite] branch master updated: IGNITE-13637: Implemented column-wise binding for result rows

This is an automated email from the ASF dual-hosted git repository.

isapego pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 2d58258  IGNITE-13637: Implemented column-wise binding for result rows
2d58258 is described below

commit 2d582586e1402fb9174f2fdad8ed4d677b3856d2
Author: Igor Sapego <is...@apache.org>
AuthorDate: Thu Nov 12 20:37:23 2020 +0300

    IGNITE-13637: Implemented column-wise binding for result rows
    
    This closes #8403
---
 modules/platforms/cpp/odbc-test/CMakeLists.txt     |   1 +
 .../cpp/odbc-test/include/odbc_test_suite.h        | 179 ++++++++++++-
 .../cpp/odbc-test/project/vs/odbc-test.vcxproj     |   3 +-
 .../odbc-test/project/vs/odbc-test.vcxproj.filters |   5 +-
 .../cpp/odbc-test/src/api_robustness_test.cpp      |   2 +-
 .../cpp/odbc-test/src/cursor_binding_test.cpp      | 288 +++++++++++++++++++++
 .../cpp/odbc-test/src/odbc_test_suite.cpp          | 214 ++++++++++++---
 .../platforms/cpp/odbc-test/src/queries_test.cpp   |   8 +-
 .../platforms/cpp/odbc-test/src/streaming_test.cpp |   4 +-
 .../cpp/odbc/include/ignite/odbc/statement.h       |  25 +-
 .../cpp/odbc/src/app/application_data_buffer.cpp   |  22 +-
 modules/platforms/cpp/odbc/src/statement.cpp       | 111 ++++++--
 12 files changed, 781 insertions(+), 81 deletions(-)

diff --git a/modules/platforms/cpp/odbc-test/CMakeLists.txt b/modules/platforms/cpp/odbc-test/CMakeLists.txt
index d9cefb6..3a08f42 100644
--- a/modules/platforms/cpp/odbc-test/CMakeLists.txt
+++ b/modules/platforms/cpp/odbc-test/CMakeLists.txt
@@ -65,6 +65,7 @@ set(SOURCES src/teamcity/teamcity_boost.cpp
         src/authentication_test.cpp
         src/sql_parsing_test.cpp
         src/streaming_test.cpp
+        src/cursor_binding_test.cpp
         src/test_server.cpp
         ../odbc/src/log.cpp
         ../odbc/src/cursor.cpp
diff --git a/modules/platforms/cpp/odbc-test/include/odbc_test_suite.h b/modules/platforms/cpp/odbc-test/include/odbc_test_suite.h
index 89133a9..1bdd3b0 100644
--- a/modules/platforms/cpp/odbc-test/include/odbc_test_suite.h
+++ b/modules/platforms/cpp/odbc-test/include/odbc_test_suite.h
@@ -25,6 +25,16 @@
 #include <sql.h>
 #include <sqlext.h>
 
+#include <boost/test/unit_test.hpp>
+
+#ifndef BOOST_TEST_CONTEXT
+#   define BOOST_TEST_CONTEXT(...)
+#endif
+
+#ifndef BOOST_TEST_INFO
+#   define BOOST_TEST_INFO(...)
+#endif
+
 #include <string>
 
 #include "ignite/ignite.h"
@@ -97,7 +107,7 @@ namespace ignite
 
             /**
              * Insert requested number of TestType values with all defaults except
-             * for the strFields, which are generated using getTestString().
+             * for the strFields, which are generated using GetTestString().
              *
              * @param recordsNum Number of records to insert.
              * @param merge Set to true to use merge instead.
@@ -131,12 +141,175 @@ namespace ignite
             void InsertNonFullBatchSelect(int recordsNum, int splitAt);
 
             /**
+             * Get test i8Field.
+             *
+             * @param idx Index.
+             * @return Corresponding i8Field value.
+             */
+            static int8_t GetTestI8Field(int64_t idx);
+
+            /**
+             * Check i8Field test value.
+             * @param idx Index.
+             * @param value Value to test.
+             */
+            static void CheckTestI8Value(int idx, int8_t value);
+
+            /**
+             * Get test i16Field.
+             *
+             * @param idx Index.
+             * @return Corresponding i16Field value.
+             */
+            static int16_t GetTestI16Field(int64_t idx);
+
+            /**
+             * Check i16Field test value.
+             * @param idx Index.
+             * @param value Value to test.
+             */
+            static void CheckTestI16Value(int idx, int16_t value);
+
+            /**
+             * Get test i32Field.
+             *
+             * @param idx Index.
+             * @return Corresponding i32Field value.
+             */
+            static int32_t GetTestI32Field(int64_t idx);
+
+            /**
+             * Check i32Field test value.
+             * @param idx Index.
+             * @param value Value to test.
+             */
+            static void CheckTestI32Value(int idx, int32_t value);
+
+            /**
              * Get test string.
              *
-             * @param ind Index.
+             * @param idx Index.
              * @return Corresponding test string.
              */
-            static std::string getTestString(int64_t ind);
+            static std::string GetTestString(int64_t idx);
+
+            /**
+             * Check strField test value.
+             * @param idx Index.
+             * @param value Value to test.
+             */
+            static void CheckTestStringValue(int idx, const std::string& value);
+
+            /**
+             * Get test floatField.
+             *
+             * @param idx Index.
+             * @return Corresponding floatField value.
+             */
+            static float GetTestFloatField(int64_t idx);
+
+            /**
+             * Check floatField test value.
+             * @param idx Index.
+             * @param value Value to test.
+             */
+            static void CheckTestFloatValue(int idx, float value);
+
+            /**
+             * Get test doubleField.
+             *
+             * @param idx Index.
+             * @return Corresponding doubleField value.
+             */
+            static double GetTestDoubleField(int64_t idx);
+
+            /**
+             * Check doubleField test value.
+             * @param idx Index.
+             * @param value Value to test.
+             */
+            static void CheckTestDoubleValue(int idx, double value);
+
+            /**
+             * Get test boolField.
+             *
+             * @param idx Index.
+             * @return Corresponding boolField value.
+             */
+            static bool GetTestBoolField(int64_t idx);
+
+            /**
+             * Check boolField test value.
+             * @param idx Index.
+             * @param value Value to test.
+             */
+            static void CheckTestBoolValue(int idx, bool value);
+
+            /**
+             * Get test dateField.
+             *
+             * @param idx Index.
+             * @param val Output value.
+             */
+            static void GetTestDateField(int64_t idx, SQL_DATE_STRUCT& val);
+
+            /**
+             * Check dateField test value.
+             *
+             * @param idx Index.
+             * @param val Value to test.
+             */
+            static void CheckTestDateValue(int idx, const SQL_DATE_STRUCT& val);
+
+            /**
+             * Get test timeField.
+             *
+             * @param idx Index.
+             * @param val Output value.
+             */
+            static void GetTestTimeField(int64_t idx, SQL_TIME_STRUCT& val);
+
+            /**
+             * Check timeField test value.
+             *
+             * @param idx Index.
+             * @param val Value to test.
+             */
+            static void CheckTestTimeValue(int idx, const SQL_TIME_STRUCT& val);
+
+            /**
+             * Get test timestampField.
+             *
+             * @param idx Index.
+             * @param val Output value.
+             */
+            static void GetTestTimestampField(int64_t idx, SQL_TIMESTAMP_STRUCT& val);
+
+            /**
+             * Check timestampField test value.
+             *
+             * @param idx Index.
+             * @param val Value to test.
+             */
+            static void CheckTestTimestampValue(int idx, const SQL_TIMESTAMP_STRUCT& val);
+
+            /**
+             * Get test i8ArrayField.
+             *
+             * @param idx Index.
+             * @param val Output value.
+             * @param valLen Value length.
+             */
+            static void GetTestI8ArrayField(int64_t idx, int8_t* val, size_t valLen);
+
+            /**
+             * Check i8ArrayField test value.
+             *
+             * @param idx Index.
+             * @param val Value to test.
+             * @param valLen Value length.
+             */
+            static void CheckTestI8ArrayValue(int idx, const int8_t* val, size_t valLen);
 
             /**
              * Check that SQL error has expected SQL state.
diff --git a/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj b/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj
index 90abdc8..cfc5ac1 100644
--- a/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj
+++ b/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj
@@ -185,6 +185,7 @@
     <ClCompile Include="..\..\src\configuration_test.cpp" />
     <ClCompile Include="..\..\src\connection_info_test.cpp" />
     <ClCompile Include="..\..\src\connection_test.cpp" />
+    <ClCompile Include="..\..\src\cursor_binding_test.cpp" />
     <ClCompile Include="..\..\src\cursor_test.cpp" />
     <ClCompile Include="..\..\src\errors_test.cpp" />
     <ClCompile Include="..\..\src\meta_queries_test.cpp" />
@@ -252,4 +253,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj.filters b/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj.filters
index 68c78cd..5d6e787 100644
--- a/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj.filters
+++ b/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj.filters
@@ -37,6 +37,9 @@
     <ClCompile Include="..\..\src\row_test.cpp">
       <Filter>Code</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\cursor_binding_test.cpp">
+      <Filter>Code</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\cursor_test.cpp">
       <Filter>Code</Filter>
     </ClCompile>
@@ -246,4 +249,4 @@
       <Filter>Configs</Filter>
     </None>
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/modules/platforms/cpp/odbc-test/src/api_robustness_test.cpp b/modules/platforms/cpp/odbc-test/src/api_robustness_test.cpp
index 9148ba9..8fcb965 100644
--- a/modules/platforms/cpp/odbc-test/src/api_robustness_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/api_robustness_test.cpp
@@ -113,7 +113,7 @@ struct ApiRobustnessTestSuiteFixture : public odbc::OdbcTestSuite
         // Operation is not supported. However, there should be no crash.
         BOOST_CHECK(ret == SQL_ERROR);
 
-        CheckSQLStatementDiagnosticError("HY106");
+        CheckSQLStatementDiagnosticError("HYC00");
     }
 
     /**
diff --git a/modules/platforms/cpp/odbc-test/src/cursor_binding_test.cpp b/modules/platforms/cpp/odbc-test/src/cursor_binding_test.cpp
new file mode 100644
index 0000000..5d09501
--- /dev/null
+++ b/modules/platforms/cpp/odbc-test/src/cursor_binding_test.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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.
+ */
+
+#ifdef _WIN32
+#   include <windows.h>
+#endif
+
+#include <sql.h>
+#include <sqlext.h>
+
+#include <cstdio>
+
+#include <vector>
+#include <string>
+
+#include <boost/test/unit_test.hpp>
+
+#include "ignite/ignite.h"
+#include "ignite/ignition.h"
+#include "ignite/impl/binary/binary_utils.h"
+
+#include "test_type.h"
+#include "test_utils.h"
+#include "odbc_test_suite.h"
+
+using namespace ignite;
+using namespace ignite::cache;
+using namespace ignite::cache::query;
+using namespace ignite::common;
+using namespace ignite_test;
+
+using namespace boost::unit_test;
+
+using ignite::impl::binary::BinaryUtils;
+
+/**
+ * Test setup fixture.
+ */
+struct CursorBindingTestSuiteFixture : public odbc::OdbcTestSuite
+{
+    static Ignite StartAdditionalNode(const char* name)
+    {
+        return StartPlatformNode("queries-test.xml", name);
+    }
+
+    /**
+     * Constructor.
+     */
+    CursorBindingTestSuiteFixture() :
+        testCache(0)
+    {
+        grid = StartAdditionalNode("NodeMain");
+
+        testCache = grid.GetCache<int64_t, TestType>("cache");
+    }
+
+    /**
+     * Destructor.
+     */
+    virtual ~CursorBindingTestSuiteFixture()
+    {
+        // No-op.
+    }
+
+    /** Node started during the test. */
+    Ignite grid;
+
+    /** Test cache instance. */
+    Cache<int64_t, TestType> testCache;
+};
+
+BOOST_FIXTURE_TEST_SUITE(CursorBindingTestSuite, CursorBindingTestSuiteFixture)
+
+
+#define CHECK_TEST_VALUES(idx, testIdx)                                                                             \
+    do {                                                                                                            \
+        BOOST_TEST_CONTEXT("Test idx: " << testIdx)                                                                 \
+        {                                                                                                           \
+            BOOST_CHECK(RowStatus[idx] == SQL_ROW_SUCCESS || RowStatus[idx] == SQL_ROW_SUCCESS_WITH_INFO);          \
+                                                                                                                    \
+            BOOST_CHECK(i8FieldsInd[idx] != SQL_NULL_DATA);                                                         \
+            BOOST_CHECK(i16FieldsInd[idx] != SQL_NULL_DATA);                                                        \
+            BOOST_CHECK(i32FieldsInd[idx] != SQL_NULL_DATA);                                                        \
+            BOOST_CHECK(strFieldsLen[idx] != SQL_NULL_DATA);                                                        \
+            BOOST_CHECK(floatFields[idx] != SQL_NULL_DATA);                                                         \
+            BOOST_CHECK(doubleFieldsInd[idx] != SQL_NULL_DATA);                                                     \
+            BOOST_CHECK(boolFieldsInd[idx] != SQL_NULL_DATA);                                                       \
+            BOOST_CHECK(dateFieldsInd[idx] != SQL_NULL_DATA);                                                       \
+            BOOST_CHECK(timeFieldsInd[idx] != SQL_NULL_DATA);                                                       \
+            BOOST_CHECK(timestampFieldsInd[idx] != SQL_NULL_DATA);                                                  \
+            BOOST_CHECK(i8ArrayFieldsLen[idx] != SQL_NULL_DATA);                                                    \
+                                                                                                                    \
+            int8_t i8Field = static_cast<int8_t>(i8Fields[idx]);                                                    \
+            int16_t i16Field = static_cast<int16_t>(i16Fields[idx]);                                                \
+            int32_t i32Field = static_cast<int32_t>(i32Fields[idx]);                                                \
+            std::string strField(reinterpret_cast<char*>(&strFields[idx][0]),                                       \
+                static_cast<size_t>(strFieldsLen[idx]));                                                            \
+            float floatField = static_cast<float>(floatFields[idx]);                                                \
+            double doubleField = static_cast<double>(doubleFields[idx]);                                            \
+            bool boolField = boolFields[idx] != 0;                                                                  \
+                                                                                                                    \
+            CheckTestI8Value(testIdx, i8Field);                                                                     \
+            CheckTestI16Value(testIdx, i16Field);                                                                   \
+            CheckTestI32Value(testIdx, i32Field);                                                                   \
+            CheckTestStringValue(testIdx, strField);                                                                \
+            CheckTestFloatValue(testIdx, floatField);                                                               \
+            CheckTestDoubleValue(testIdx, doubleField);                                                             \
+            CheckTestBoolValue(testIdx, boolField);                                                                 \
+            CheckTestDateValue(testIdx, dateFields[idx]);                                                           \
+            CheckTestTimeValue(testIdx, timeFields[idx]);                                                           \
+            CheckTestTimestampValue(testIdx, timestampFields[idx]);                                                 \
+            CheckTestI8ArrayValue(testIdx, reinterpret_cast<int8_t*>(i8ArrayFields[idx]),                           \
+                static_cast<SQLLEN>(i8ArrayFieldsLen[idx]));                                                        \
+        }                                                                                                           \
+    } while (false)
+
+BOOST_AUTO_TEST_CASE(TestCursorBindingColumnWise)
+{
+    enum { ROWS_COUNT = 15 };
+    enum { ROW_ARRAY_SIZE = 10 };
+    enum { BUFFER_SIZE = 1024 };
+
+    StartAdditionalNode("Node2");
+
+    Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache;PAGE_SIZE=8");
+
+    // Preloading data.
+
+    InsertTestBatch(0, ROWS_COUNT, ROWS_COUNT);
+
+    // Setting attributes.
+
+    SQLUSMALLINT RowStatus[ROW_ARRAY_SIZE];
+    SQLUINTEGER NumRowsFetched;
+
+    SQLRETURN ret;
+
+    ret = SQLSetStmtAttr(stmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLSetStmtAttr(stmt, SQL_ATTR_ROW_ARRAY_SIZE, reinterpret_cast<SQLPOINTER*>(ROW_ARRAY_SIZE), 0);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLSetStmtAttr(stmt, SQL_ATTR_ROW_STATUS_PTR, RowStatus, 0);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLSetStmtAttr(stmt, SQL_ATTR_ROWS_FETCHED_PTR, &NumRowsFetched, 0);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    // Binding collumns.
+
+    SQLSCHAR i8Fields[ROW_ARRAY_SIZE] = {0};
+    SQLLEN i8FieldsInd[ROW_ARRAY_SIZE];
+
+    SQLSMALLINT i16Fields[ROW_ARRAY_SIZE] = {0};
+    SQLLEN i16FieldsInd[ROW_ARRAY_SIZE];
+
+    SQLINTEGER i32Fields[ROW_ARRAY_SIZE] = {0};
+    SQLLEN i32FieldsInd[ROW_ARRAY_SIZE];
+
+    SQLCHAR strFields[ROW_ARRAY_SIZE][BUFFER_SIZE];
+    SQLLEN strFieldsLen[ROW_ARRAY_SIZE];
+
+    SQLREAL floatFields[ROW_ARRAY_SIZE];
+    SQLLEN floatFieldsInd[ROW_ARRAY_SIZE];
+
+    SQLDOUBLE doubleFields[ROW_ARRAY_SIZE];
+    SQLLEN doubleFieldsInd[ROW_ARRAY_SIZE];
+
+    SQLCHAR boolFields[ROW_ARRAY_SIZE];
+    SQLLEN boolFieldsInd[ROW_ARRAY_SIZE];
+
+    SQL_DATE_STRUCT dateFields[ROW_ARRAY_SIZE];
+    SQLLEN dateFieldsInd[ROW_ARRAY_SIZE];
+
+    SQL_TIME_STRUCT timeFields[ROW_ARRAY_SIZE];
+    SQLLEN timeFieldsInd[ROW_ARRAY_SIZE];
+
+    SQL_TIMESTAMP_STRUCT timestampFields[ROW_ARRAY_SIZE];
+    SQLLEN timestampFieldsInd[ROW_ARRAY_SIZE];
+
+    SQLCHAR i8ArrayFields[ROW_ARRAY_SIZE][BUFFER_SIZE];
+    SQLLEN i8ArrayFieldsLen[ROW_ARRAY_SIZE];
+
+    ret = SQLBindCol(stmt, 1, SQL_C_STINYINT, i8Fields, 0, i8FieldsInd);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 2, SQL_C_SSHORT, i16Fields, 0, i16FieldsInd);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 3, SQL_C_LONG, i32Fields, 0, i32FieldsInd);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 4, SQL_C_CHAR, strFields, BUFFER_SIZE, strFieldsLen);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 5, SQL_C_FLOAT, floatFields, 0, floatFieldsInd);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 6, SQL_C_DOUBLE, doubleFields, 0, doubleFieldsInd);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 7, SQL_C_BIT, boolFields, 0, boolFieldsInd);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 8, SQL_C_DATE, dateFields, 0, dateFieldsInd);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 9, SQL_C_TIME, timeFields, 0, timeFieldsInd);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 10, SQL_C_TIMESTAMP, timestampFields, 0, timestampFieldsInd);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLBindCol(stmt, 11, SQL_C_BINARY, i8ArrayFields, BUFFER_SIZE, i8ArrayFieldsLen);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    SQLCHAR sql[] = "SELECT "
+            "i8Field, i16Field, i32Field, strField, floatField, doubleField, "
+            "boolField, dateField, timeField, timestampField, i8ArrayField "
+            "FROM TestType "
+            "ORDER BY _key";
+
+    // Execute a statement to retrieve rows from the Orders table.
+    ret = SQLExecDirect(stmt, sql, SQL_NTS);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    ret = SQLFetchScroll(stmt, SQL_FETCH_NEXT, 0);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    BOOST_CHECK_EQUAL(NumRowsFetched, (SQLUINTEGER)ROW_ARRAY_SIZE);
+
+    for (int64_t i = 0; i < NumRowsFetched; i++)
+    {
+        CHECK_TEST_VALUES(i, static_cast<int>(i));
+    }
+
+    ret = SQLFetch(stmt);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+    BOOST_CHECK_EQUAL(NumRowsFetched, ROWS_COUNT - ROW_ARRAY_SIZE);
+
+    for (int64_t i = 0; i < NumRowsFetched; i++)
+    {
+        int64_t testIdx = i + ROW_ARRAY_SIZE;
+        CHECK_TEST_VALUES(i, static_cast<int>(testIdx));
+    }
+
+    for (int64_t i = NumRowsFetched; i < ROW_ARRAY_SIZE; i++)
+    {
+        BOOST_TEST_INFO("Checking row status for row: " << i);
+        BOOST_CHECK(RowStatus[i] == SQL_ROW_NOROW);
+    }
+
+    ret = SQLFetchScroll(stmt, SQL_FETCH_NEXT, 0);
+    BOOST_CHECK_EQUAL(ret, SQL_NO_DATA);
+
+    // Close the cursor.
+    ret = SQLCloseCursor(stmt);
+    ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+}
+
+BOOST_AUTO_TEST_CASE(TestCursorBindingRowWise)
+{
+    Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache;PAGE_SIZE=8");
+
+    SQLRETURN ret = SQLSetStmtAttr(stmt, SQL_ATTR_ROW_BIND_TYPE, reinterpret_cast<SQLPOINTER*>(42), 0);
+
+    BOOST_CHECK_EQUAL(ret, SQL_ERROR);
+
+    CheckSQLStatementDiagnosticError("HYC00");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/modules/platforms/cpp/odbc-test/src/odbc_test_suite.cpp b/modules/platforms/cpp/odbc-test/src/odbc_test_suite.cpp
index 4ebec9f..28affb7 100644
--- a/modules/platforms/cpp/odbc-test/src/odbc_test_suite.cpp
+++ b/modules/platforms/cpp/odbc-test/src/odbc_test_suite.cpp
@@ -95,9 +95,7 @@ namespace ignite
                 outstr, sizeof(outstr), &outstrlen, SQL_DRIVER_COMPLETE);
 
             if (!SQL_SUCCEEDED(ret))
-            {
                 BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_DBC, dbc));
-            }
 
             // Allocate a statement handle
             SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
@@ -184,15 +182,182 @@ namespace ignite
             Ignition::StopAll(true);
         }
 
-        std::string OdbcTestSuite::getTestString(int64_t ind)
+        int8_t OdbcTestSuite::GetTestI8Field(int64_t idx)
+        {
+            return static_cast<int8_t>(idx * 8);
+        }
+
+        void OdbcTestSuite::CheckTestI8Value(int idx, int8_t value)
+        {
+            BOOST_TEST_INFO("Test index: " << idx);
+            BOOST_CHECK_EQUAL(value, GetTestI8Field(idx));
+        }
+
+        int16_t OdbcTestSuite::GetTestI16Field(int64_t idx)
+        {
+            return static_cast<int16_t>(idx * 16);
+        }
+
+        void OdbcTestSuite::CheckTestI16Value(int idx, int16_t value)
+        {
+            BOOST_TEST_INFO("Test index: " << idx);
+            BOOST_CHECK_EQUAL(value, GetTestI16Field(idx));
+        }
+
+        int32_t OdbcTestSuite::GetTestI32Field(int64_t idx)
+        {
+            return static_cast<int32_t>(idx * 32);
+        }
+
+        void OdbcTestSuite::CheckTestI32Value(int idx, int32_t value)
+        {
+            BOOST_TEST_INFO("Test index: " << idx);
+            BOOST_CHECK_EQUAL(value, GetTestI32Field(idx));
+        }
+
+        std::string OdbcTestSuite::GetTestString(int64_t idx)
         {
             std::stringstream builder;
 
-            builder << "String#" << ind;
+            builder << "String#" << idx;
 
             return builder.str();
         }
 
+        void OdbcTestSuite::CheckTestStringValue(int idx, const std::string &value)
+        {
+            BOOST_TEST_INFO("Test index: " << idx);
+            BOOST_CHECK_EQUAL(value, GetTestString(idx));
+        }
+
+        float OdbcTestSuite::GetTestFloatField(int64_t idx)
+        {
+            return static_cast<float>(idx * 0.5f);
+        }
+
+        void OdbcTestSuite::CheckTestFloatValue(int idx, float value)
+        {
+            BOOST_TEST_INFO("Test index: " << idx);
+            BOOST_CHECK_EQUAL(value, GetTestFloatField(idx));
+        }
+
+        double OdbcTestSuite::GetTestDoubleField(int64_t idx)
+        {
+            return static_cast<double>(idx * 0.25f);
+        }
+
+        void OdbcTestSuite::CheckTestDoubleValue(int idx, double value)
+        {
+            BOOST_TEST_INFO("Test index: " << idx);
+            BOOST_CHECK_EQUAL(value, GetTestDoubleField(idx));
+        }
+
+        bool OdbcTestSuite::GetTestBoolField(int64_t idx)
+        {
+            return static_cast<bool>(idx % 2 == 0);
+        }
+
+        void OdbcTestSuite::CheckTestBoolValue(int idx, bool value)
+        {
+            BOOST_TEST_INFO("Test index: " << idx);
+            BOOST_CHECK_EQUAL(value, GetTestBoolField(idx));
+        }
+
+        void OdbcTestSuite::GetTestDateField(int64_t idx, SQL_DATE_STRUCT& val)
+        {
+            val.year = static_cast<SQLSMALLINT>(2017 + idx / 365);
+            val.month = static_cast<SQLUSMALLINT>(((idx / 28) % 12) + 1);
+            val.day = static_cast<SQLUSMALLINT>((idx % 28) + 1);
+        }
+
+        void OdbcTestSuite::CheckTestDateValue(int idx, const SQL_DATE_STRUCT& val)
+        {
+            BOOST_TEST_CONTEXT("Test index: " << idx)
+            {
+                SQL_DATE_STRUCT expected;
+                GetTestDateField(idx, expected);
+
+                BOOST_CHECK_EQUAL(val.year, expected.year);
+                BOOST_CHECK_EQUAL(val.month, expected.month);
+                BOOST_CHECK_EQUAL(val.day, expected.day);
+            }
+        }
+
+        void OdbcTestSuite::GetTestTimeField(int64_t idx, SQL_TIME_STRUCT& val)
+        {
+            val.hour = (idx / 3600) % 24;
+            val.minute = (idx / 60) % 60;
+            val.second = idx % 60;
+        }
+
+        void OdbcTestSuite::CheckTestTimeValue(int idx, const SQL_TIME_STRUCT& val)
+        {
+            BOOST_TEST_CONTEXT("Test index: " << idx)
+            {
+                SQL_TIME_STRUCT expected;
+                GetTestTimeField(idx, expected);
+
+                BOOST_CHECK_EQUAL(val.hour, expected.hour);
+                BOOST_CHECK_EQUAL(val.minute, expected.minute);
+                BOOST_CHECK_EQUAL(val.second, expected.second);
+            }
+        }
+
+        void OdbcTestSuite::GetTestTimestampField(int64_t idx, SQL_TIMESTAMP_STRUCT& val)
+        {
+            SQL_DATE_STRUCT date;
+            GetTestDateField(idx, date);
+
+            SQL_TIME_STRUCT time;
+            GetTestTimeField(idx, time);
+
+            val.year = date.year;
+            val.month = date.month;
+            val.day = date.day;
+            val.hour = time.hour;
+            val.minute = time.minute;
+            val.second = time.second;
+            val.fraction = static_cast<uint64_t>(std::abs(idx * 914873)) % 1000000000;
+        }
+
+        void OdbcTestSuite::CheckTestTimestampValue(int idx, const SQL_TIMESTAMP_STRUCT& val)
+        {
+            BOOST_TEST_CONTEXT("Test index: " << idx)
+            {
+                SQL_TIMESTAMP_STRUCT expected;
+                GetTestTimestampField(idx, expected);
+
+                BOOST_CHECK_EQUAL(val.year, expected.year);
+                BOOST_CHECK_EQUAL(val.month, expected.month);
+                BOOST_CHECK_EQUAL(val.day, expected.day);
+                BOOST_CHECK_EQUAL(val.hour, expected.hour);
+                BOOST_CHECK_EQUAL(val.minute, expected.minute);
+                BOOST_CHECK_EQUAL(val.second, expected.second);
+                BOOST_CHECK_EQUAL(val.fraction, expected.fraction);
+            }
+        }
+
+        void OdbcTestSuite::GetTestI8ArrayField(int64_t idx, int8_t* val, size_t valLen)
+        {
+            for (size_t j = 0; j < valLen; ++j)
+                val[j] = static_cast<int8_t>(idx * valLen + j);
+        }
+
+        void OdbcTestSuite::CheckTestI8ArrayValue(int idx, const int8_t* val, size_t valLen)
+        {
+            BOOST_TEST_CONTEXT("Test index: " << idx)
+            {
+                common::FixedSizeArray<int8_t> expected(static_cast<int32_t>(valLen));
+                GetTestI8ArrayField(idx, expected.GetData(), expected.GetSize());
+
+                for (size_t j = 0; j < valLen; ++j)
+                {
+                    BOOST_TEST_INFO("Byte index: " << j);
+                    BOOST_CHECK_EQUAL(val[j], expected[(int32_t)j]);
+                }
+            }
+        }
+
         void OdbcTestSuite::CheckSQLDiagnosticError(int16_t handleType, SQLHANDLE handle, const std::string& expectSqlState)
         {
             SQLCHAR state[ODBC_BUFFER_SIZE];
@@ -267,7 +432,7 @@ namespace ignite
             for (SQLSMALLINT i = 0; i < recordsNum; ++i)
             {
                 key = i + 1;
-                std::string val = getTestString(i);
+                std::string val = GetTestString(i);
 
                 strncpy(strField, val.c_str(), sizeof(strField));
                 strFieldLen = SQL_NTS;
@@ -342,36 +507,23 @@ namespace ignite
                 int seed = from + i;
 
                 keys[i] = seed;
-                i8Fields[i] = seed * 8;
-                i16Fields[i] = seed * 16;
-                i32Fields[i] = seed * 32;
+                i8Fields[i] = GetTestI8Field(seed);
+                i16Fields[i] = GetTestI16Field(seed);
+                i32Fields[i] = GetTestI32Field(seed);
 
-                std::string val = getTestString(seed);
+                std::string val = GetTestString(seed);
                 strncpy(strFields.GetData() + 1024 * i, val.c_str(), 1023);
                 strFieldsLen[i] = val.size();
 
-                floatFields[i] = seed * 0.5f;
-                doubleFields[i] = seed * 0.25f;
-                boolFields[i] = seed % 2 == 0;
-
-                dateFields[i].year = 2017 + seed / 365;
-                dateFields[i].month = ((seed / 28) % 12) + 1;
-                dateFields[i].day = (seed % 28) + 1;
-
-                timeFields[i].hour = (seed / 3600) % 24;
-                timeFields[i].minute = (seed / 60) % 60;
-                timeFields[i].second = seed % 60;
+                floatFields[i] = GetTestFloatField(seed);
+                doubleFields[i] = GetTestDoubleField(seed);
+                boolFields[i] = GetTestBoolField(seed);
 
-                timestampFields[i].year = dateFields[i].year;
-                timestampFields[i].month = dateFields[i].month;
-                timestampFields[i].day = dateFields[i].day;
-                timestampFields[i].hour = timeFields[i].hour;
-                timestampFields[i].minute = timeFields[i].minute;
-                timestampFields[i].second = timeFields[i].second;
-                timestampFields[i].fraction = static_cast<uint64_t>(std::abs(seed * 914873)) % 1000000000;
+                GetTestDateField(seed, dateFields[i]);
+                GetTestTimeField(seed, timeFields[i]);
+                GetTestTimestampField(seed, timestampFields[i]);
 
-                for (int j = 0; j < 42; ++j)
-                    i8ArrayFields[i * 42 + j] = seed * 42 + j;
+                GetTestI8ArrayField(seed, &i8ArrayFields[i*42], 42);
                 i8ArrayFieldsLen[i] = 42;
             }
 
@@ -553,7 +705,7 @@ namespace ignite
                 if (!SQL_SUCCEEDED(ret))
                     BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
 
-                std::string expectedStr = getTestString(selectedRecordsNum);
+                std::string expectedStr = GetTestString(selectedRecordsNum);
                 int64_t expectedKey = selectedRecordsNum;
 
                 BOOST_CHECK_EQUAL(key, expectedKey);
@@ -634,7 +786,7 @@ namespace ignite
                 if (!SQL_SUCCEEDED(ret))
                     BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
 
-                std::string expectedStr = getTestString(selectedRecordsNum);
+                std::string expectedStr = GetTestString(selectedRecordsNum);
                 int64_t expectedKey = selectedRecordsNum;
 
                 BOOST_CHECK_EQUAL(key, expectedKey);
diff --git a/modules/platforms/cpp/odbc-test/src/queries_test.cpp b/modules/platforms/cpp/odbc-test/src/queries_test.cpp
index 46fed8a..a22c336 100644
--- a/modules/platforms/cpp/odbc-test/src/queries_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/queries_test.cpp
@@ -922,7 +922,7 @@ BOOST_AUTO_TEST_CASE(TestInsertSelect)
         if (!SQL_SUCCEEDED(ret))
             BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
 
-        std::string expectedStr = getTestString(selectedRecordsNum);
+        std::string expectedStr = GetTestString(selectedRecordsNum);
         int64_t expectedKey = selectedRecordsNum + 1;
 
         BOOST_CHECK_EQUAL(key, expectedKey);
@@ -999,7 +999,7 @@ BOOST_AUTO_TEST_CASE(TestInsertUpdateSelect)
         if (expectedKey == 42)
             expectedStr = "Updated value";
         else
-            expectedStr = getTestString(selectedRecordsNum);
+            expectedStr = GetTestString(selectedRecordsNum);
 
         BOOST_CHECK_EQUAL(std::string(strField, strFieldLen), expectedStr);
 
@@ -1066,7 +1066,7 @@ BOOST_AUTO_TEST_CASE(TestInsertDeleteSelect)
             BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
 
         int64_t expectedKey = (selectedRecordsNum + 1) * 2;
-        std::string expectedStr = getTestString(expectedKey - 1);
+        std::string expectedStr = GetTestString(expectedKey - 1);
 
         BOOST_CHECK_EQUAL(key, expectedKey);
         BOOST_CHECK_EQUAL(std::string(strField, strFieldLen), expectedStr);
@@ -1127,7 +1127,7 @@ BOOST_AUTO_TEST_CASE(TestInsertMergeSelect)
         if (!SQL_SUCCEEDED(ret))
             BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
 
-        std::string expectedStr = getTestString(selectedRecordsNum);
+        std::string expectedStr = GetTestString(selectedRecordsNum);
         int64_t expectedKey = selectedRecordsNum + 1;
 
         BOOST_CHECK_EQUAL(key, expectedKey);
diff --git a/modules/platforms/cpp/odbc-test/src/streaming_test.cpp b/modules/platforms/cpp/odbc-test/src/streaming_test.cpp
index 1d74338..d863e37 100644
--- a/modules/platforms/cpp/odbc-test/src/streaming_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/streaming_test.cpp
@@ -115,7 +115,7 @@ struct StreamingTestSuiteFixture : odbc::OdbcTestSuite
         for (int32_t i = begin; i < end; ++i)
         {
             key = i;
-            std::string val = getTestString(i);
+            std::string val = GetTestString(i);
 
             strncpy(strField, val.c_str(), sizeof(strField));
             strFieldLen = SQL_NTS;
@@ -240,7 +240,7 @@ struct StreamingTestSuiteFixture : odbc::OdbcTestSuite
                 BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
 
             BOOST_CHECK_EQUAL(i, keyVal);
-            BOOST_CHECK_EQUAL(getTestString(i), std::string(strField, static_cast<size_t>(strFieldLen)));
+            BOOST_CHECK_EQUAL(GetTestString(i), std::string(strField, static_cast<size_t>(strFieldLen)));
         }
 
         // Resetting parameters.
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/statement.h b/modules/platforms/cpp/odbc/include/ignite/odbc/statement.h
index 19d29a9..37c91b4 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/statement.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/statement.h
@@ -296,28 +296,28 @@ namespace ignite
              *
              * @param ptr Rows fetched buffer pointer.
              */
-            void SetRowsFetchedPtr(size_t* ptr);
+            void SetRowsFetchedPtr(SQLINTEGER* ptr);
 
             /**
              * Get rows fetched buffer pointer.
              *
              * @return Rows fetched buffer pointer.
              */
-            size_t* GetRowsFetchedPtr();
+            SQLINTEGER* GetRowsFetchedPtr();
 
             /**
              * Set row statuses array pointer.
              *
              * @param ptr Row statuses array pointer.
              */
-            void SetRowStatusesPtr(uint16_t* ptr);
+            void SetRowStatusesPtr(SQLUSMALLINT* ptr);
 
             /**
              * Get row statuses array pointer.
              *
              * @return Row statuses array pointer.
              */
-            uint16_t* GetRowStatusesPtr();
+            SQLUSMALLINT* GetRowStatusesPtr();
 
             /**
              * Select next parameter data for which is required.
@@ -671,6 +671,13 @@ namespace ignite
             SqlResult::Type UpdateParamsMeta();
 
             /**
+             * Convert SQLRESULT to SQL_ROW_RESULT.
+             *
+             * @return Operation result.
+             */
+            uint16_t SqlResultToRowResult(SqlResult::Type value);
+
+            /**
              * Constructor.
              * Called by friend classes.
              *
@@ -687,18 +694,18 @@ namespace ignite
             /** Underlying query. */
             std::auto_ptr<query::Query> currentQuery;
 
-            /** Row bind type. */
-            SqlUlen rowBindType;
-
             /** Buffer to store number of rows fetched by the last fetch. */
-            size_t* rowsFetched;
+            SQLINTEGER* rowsFetched;
 
             /** Array to store statuses of rows fetched by the last fetch. */
-            uint16_t* rowStatuses;
+            SQLUSMALLINT* rowStatuses;
 
             /** Offset added to pointers to change binding of column data. */
             int* columnBindOffset;
 
+            /** Row array size. */
+            SqlUlen rowArraySize;
+
             /** Parameters. */
             app::ParameterSet parameters;
 
diff --git a/modules/platforms/cpp/odbc/src/app/application_data_buffer.cpp b/modules/platforms/cpp/odbc/src/app/application_data_buffer.cpp
index 2f1fbcb..35af8cf 100644
--- a/modules/platforms/cpp/odbc/src/app/application_data_buffer.cpp
+++ b/modules/platforms/cpp/odbc/src/app/application_data_buffer.cpp
@@ -1707,28 +1707,36 @@ namespace ignite
                         return buflen;
 
                     case OdbcNativeType::AI_SIGNED_SHORT:
+                        return static_cast<SqlLen>(sizeof(SQLSMALLINT));
+
                     case OdbcNativeType::AI_UNSIGNED_SHORT:
-                        return static_cast<SqlLen>(sizeof(short));
+                        return static_cast<SqlLen>(sizeof(SQLUSMALLINT));
 
                     case OdbcNativeType::AI_SIGNED_LONG:
+                        return static_cast<SqlLen>(sizeof(SQLUINTEGER));
+
                     case OdbcNativeType::AI_UNSIGNED_LONG:
-                        return static_cast<SqlLen>(sizeof(long));
+                        return static_cast<SqlLen>(sizeof(SQLINTEGER));
 
                     case OdbcNativeType::AI_FLOAT:
-                        return static_cast<SqlLen>(sizeof(float));
+                        return static_cast<SqlLen>(sizeof(SQLREAL));
 
                     case OdbcNativeType::AI_DOUBLE:
-                        return static_cast<SqlLen>(sizeof(double));
+                        return static_cast<SqlLen>(sizeof(SQLDOUBLE));
 
-                    case OdbcNativeType::AI_BIT:
                     case OdbcNativeType::AI_SIGNED_TINYINT:
+                        return static_cast<SqlLen>(sizeof(SQLSCHAR));
+
+                    case OdbcNativeType::AI_BIT:
                     case OdbcNativeType::AI_UNSIGNED_TINYINT:
-                        return static_cast<SqlLen>(sizeof(char));
+                        return static_cast<SqlLen>(sizeof(SQLCHAR));
 
                     case OdbcNativeType::AI_SIGNED_BIGINT:
-                    case OdbcNativeType::AI_UNSIGNED_BIGINT:
                         return static_cast<SqlLen>(sizeof(SQLBIGINT));
 
+                    case OdbcNativeType::AI_UNSIGNED_BIGINT:
+                        return static_cast<SqlLen>(sizeof(SQLUBIGINT));
+
                     case OdbcNativeType::AI_TDATE:
                         return static_cast<SqlLen>(sizeof(SQL_DATE_STRUCT));
 
diff --git a/modules/platforms/cpp/odbc/src/statement.cpp b/modules/platforms/cpp/odbc/src/statement.cpp
index d88d449..9253030 100644
--- a/modules/platforms/cpp/odbc/src/statement.cpp
+++ b/modules/platforms/cpp/odbc/src/statement.cpp
@@ -46,10 +46,10 @@ namespace ignite
             connection(parent),
             columnBindings(),
             currentQuery(),
-            rowBindType(SQL_BIND_BY_COLUMN),
             rowsFetched(0),
             rowStatuses(0),
             columnBindOffset(0),
+            rowArraySize(1),
             parameters(),
             timeout(0)
         {
@@ -205,7 +205,7 @@ namespace ignite
             if (!buffer && !resLen)
             {
                 AddStatusRecord(SqlState::SHY009_INVALID_USE_OF_NULL_POINTER,
-                    "ParameterValuePtr and StrLen_or_IndPtr are both null pointers.");
+                    "ParameterValuePtr and StrLen_or_IndPtr are both null pointers");
 
                 return SqlResult::AI_ERROR;
             }
@@ -234,34 +234,59 @@ namespace ignite
 
                     LOG_MSG("SQL_ATTR_ROW_ARRAY_SIZE: " << val);
 
-                    if (val != 1)
+                    if (val < 1)
                     {
-                        AddStatusRecord(SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED,
-                            "Fetching of more than one row by call is not supported.");
+                        AddStatusRecord(SqlState::SHY092_OPTION_TYPE_OUT_OF_RANGE,
+                            "Array size value can not be less than 1");
 
                         return SqlResult::AI_ERROR;
                     }
 
+                    rowArraySize = val;
+
                     break;
                 }
 
                 case SQL_ATTR_ROW_BIND_TYPE:
                 {
-                    rowBindType = reinterpret_cast<SqlUlen>(value);
+                    SqlUlen rowBindType = reinterpret_cast<SqlUlen>(value);
+
+                    if (rowBindType != SQL_BIND_BY_COLUMN)
+                    {
+                        AddStatusRecord(SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED,
+                            "Only binding by column is currently supported");
+
+                        return SqlResult::AI_ERROR;
+                    }
 
                     break;
                 }
 
                 case SQL_ATTR_ROWS_FETCHED_PTR:
                 {
-                    SetRowsFetchedPtr(reinterpret_cast<size_t*>(value));
+                    SetRowsFetchedPtr(reinterpret_cast<SQLINTEGER*>(value));
 
                     break;
                 }
 
                 case SQL_ATTR_ROW_STATUS_PTR:
                 {
-                    SetRowStatusesPtr(reinterpret_cast<uint16_t*>(value));
+                    SetRowStatusesPtr(reinterpret_cast<SQLUSMALLINT*>(value));
+
+                    break;
+                }
+
+                case SQL_ATTR_PARAM_BIND_TYPE:
+                {
+                    SqlUlen paramBindType = reinterpret_cast<SqlUlen>(value);
+
+                    if (paramBindType != SQL_PARAM_BIND_BY_COLUMN)
+                    {
+                        AddStatusRecord(SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED,
+                            "Only binding by column is currently supported");
+
+                        return SqlResult::AI_ERROR;
+                    }
 
                     break;
                 }
@@ -380,7 +405,7 @@ namespace ignite
                 {
                     SqlUlen* val = reinterpret_cast<SqlUlen*>(buf);
 
-                    *val = rowBindType;
+                    *val = SQL_BIND_BY_COLUMN;
 
                     break;
                 }
@@ -421,6 +446,15 @@ namespace ignite
                     break;
                 }
 
+                case SQL_ATTR_PARAM_BIND_TYPE:
+                {
+                    SqlUlen* val = reinterpret_cast<SqlUlen*>(buf);
+
+                    *val = SQL_PARAM_BIND_BY_COLUMN;
+
+                    break;
+                }
+
                 case SQL_ATTR_PARAM_BIND_OFFSET_PTR:
                 {
                     SQLULEN** val = reinterpret_cast<SQLULEN**>(buf);
@@ -956,7 +990,9 @@ namespace ignite
 
             if (orientation != SQL_FETCH_NEXT)
             {
-                AddStatusRecord(SqlState::SHY106_FETCH_TYPE_OUT_OF_RANGE, "The value specified for the argument FetchOrientation was not SQL_FETCH_NEXT.");
+                AddStatusRecord(SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED,
+                    "Only SQL_FETCH_NEXT FetchOrientation type is supported");
+
                 return SqlResult::AI_ERROR;
             }
 
@@ -975,8 +1011,7 @@ namespace ignite
 
             if (!currentQuery.get())
             {
-                AddStatusRecord(SqlState::S24000_INVALID_CURSOR_STATE,
-                    "Cursor is not in the open state.");
+                AddStatusRecord(SqlState::S24000_INVALID_CURSOR_STATE, "Cursor is not in the open state");
 
                 return SqlResult::AI_ERROR;
             }
@@ -987,18 +1022,32 @@ namespace ignite
                     it->second.SetByteOffset(*columnBindOffset);
             }
 
-            SqlResult::Type res = currentQuery->FetchNextRow(columnBindings);
+            SQLINTEGER fetched = 0;
+            SQLINTEGER errors = 0;
 
-            if (res == SqlResult::AI_SUCCESS)
+            for (SqlUlen i = 0; i < rowArraySize; ++i)
             {
-                if (rowsFetched)
-                    *rowsFetched = 1;
+                for (app::ColumnBindingMap::iterator it = columnBindings.begin(); it != columnBindings.end(); ++it)
+                    it->second.SetElementOffset(i);
+
+                SqlResult::Type res = currentQuery->FetchNextRow(columnBindings);
+
+                if (res == SqlResult::AI_SUCCESS || res == SqlResult::AI_SUCCESS_WITH_INFO)
+                    ++fetched;
+                else if (res != SqlResult::AI_NO_DATA)
+                    ++errors;
 
                 if (rowStatuses)
-                    rowStatuses[0] = SQL_ROW_SUCCESS;
+                    rowStatuses[i] = SqlResultToRowResult(res);
             }
 
-            return res;
+            if (rowsFetched)
+                *rowsFetched = fetched < 0 ? static_cast<SQLINTEGER>(rowArraySize) : fetched;
+
+            if (fetched > 0)
+                return errors == 0 ? SqlResult::AI_SUCCESS : SqlResult::AI_SUCCESS_WITH_INFO;
+
+            return errors == 0 ? SqlResult::AI_NO_DATA : SqlResult::AI_ERROR;
         }
 
         const meta::ColumnMetaVector* Statement::GetMeta()
@@ -1114,22 +1163,22 @@ namespace ignite
             return SqlResult::AI_SUCCESS;
         }
 
-        void Statement::SetRowsFetchedPtr(size_t* ptr)
+        void Statement::SetRowsFetchedPtr(SQLINTEGER* ptr)
         {
             rowsFetched = ptr;
         }
 
-        size_t* Statement::GetRowsFetchedPtr()
+        SQLINTEGER* Statement::GetRowsFetchedPtr()
         {
             return rowsFetched;
         }
 
-        void Statement::SetRowStatusesPtr(uint16_t* ptr)
+        void Statement::SetRowStatusesPtr(SQLUSMALLINT* ptr)
         {
             rowStatuses = ptr;
         }
 
-        uint16_t * Statement::GetRowStatusesPtr()
+        SQLUSMALLINT * Statement::GetRowStatusesPtr()
         {
             return rowStatuses;
         }
@@ -1327,6 +1376,24 @@ namespace ignite
 
             return SqlResult::AI_SUCCESS;
         }
+
+        uint16_t Statement::SqlResultToRowResult(SqlResult::Type value)
+        {
+            switch (value)
+            {
+                case SqlResult::AI_NO_DATA:
+                    return SQL_ROW_NOROW;
+
+                case SqlResult::AI_SUCCESS:
+                    return SQL_ROW_SUCCESS;
+
+                case SqlResult::AI_SUCCESS_WITH_INFO:
+                    return SQL_ROW_SUCCESS_WITH_INFO;
+
+                default:
+                    return SQL_ROW_ERROR;
+            }
+        }
     }
 }