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/12/09 15:13:23 UTC

[ignite] branch master updated: IGNITE-13801: Fix Ab Initio related ODBC issues

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 27062ee  IGNITE-13801: Fix Ab Initio related ODBC issues
27062ee is described below

commit 27062ee6b1c5d934d25b27089177b920d4c70417
Author: Igor Sapego <is...@apache.org>
AuthorDate: Wed Dec 9 18:12:16 2020 +0300

    IGNITE-13801: Fix Ab Initio related ODBC issues
    
    This closes #8528
---
 .../platforms/cpp/odbc-test/include/test_utils.h   |   6 +-
 .../cpp/odbc-test/src/attributes_test.cpp          |  20 ++
 .../cpp/odbc-test/src/authentication_test.cpp      |  91 +++++++-
 .../cpp/odbc-test/src/meta_queries_test.cpp        | 260 +++++++++++++++++++++
 modules/platforms/cpp/odbc-test/src/test_utils.cpp |   8 +-
 .../platforms/cpp/odbc-test/src/utility_test.cpp   |  64 +++++
 .../ignite/odbc/config/connection_string_parser.h  |   6 +
 .../cpp/odbc/include/ignite/odbc/dsn_config.h      |   5 +-
 .../platforms/cpp/odbc/os/win/src/system_dsn.cpp   |   4 +-
 .../odbc/src/config/connection_string_parser.cpp   |  18 +-
 modules/platforms/cpp/odbc/src/connection.cpp      |   2 +-
 modules/platforms/cpp/odbc/src/dsn_config.cpp      |  12 +-
 .../platforms/cpp/odbc/src/meta/column_meta.cpp    |   2 +-
 modules/platforms/cpp/odbc/src/odbc.cpp            |   5 +-
 modules/platforms/cpp/odbc/src/statement.cpp       |   2 +
 modules/platforms/cpp/odbc/src/utility.cpp         |  14 +-
 16 files changed, 491 insertions(+), 28 deletions(-)

diff --git a/modules/platforms/cpp/odbc-test/include/test_utils.h b/modules/platforms/cpp/odbc-test/include/test_utils.h
index 1f2aeec..6cac2af 100644
--- a/modules/platforms/cpp/odbc-test/include/test_utils.h
+++ b/modules/platforms/cpp/odbc-test/include/test_utils.h
@@ -121,18 +121,20 @@ namespace ignite_test
      *
      * @param handleType Type of the handle.
      * @param handle Handle.
+     * @param idx Error record index.
      * @return Error state.
      */
-    std::string GetOdbcErrorState(SQLSMALLINT handleType, SQLHANDLE handle);
+    std::string GetOdbcErrorState(SQLSMALLINT handleType, SQLHANDLE handle, int idx = 1);
 
     /**
      * Extract error message.
      *
      * @param handleType Type of the handle.
      * @param handle Handle.
+     * @param idx Error record index.
      * @return Error message.
      */
-    std::string GetOdbcErrorMessage(SQLSMALLINT handleType, SQLHANDLE handle);
+    std::string GetOdbcErrorMessage(SQLSMALLINT handleType, SQLHANDLE handle, int idx = 1);
 
     /**
      * @return Test config directory path.
diff --git a/modules/platforms/cpp/odbc-test/src/attributes_test.cpp b/modules/platforms/cpp/odbc-test/src/attributes_test.cpp
index 0519f61..278a066 100644
--- a/modules/platforms/cpp/odbc-test/src/attributes_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/attributes_test.cpp
@@ -243,4 +243,24 @@ BOOST_AUTO_TEST_CASE(ConnectionAttributeLoginTimeout)
     BOOST_REQUIRE_EQUAL(timeout, 42);
 }
 
+/**
+ * Check that environment returns expected version of ODBC standard.
+ *
+ * 1. Start node.
+ * 2. Establish connection using ODBC driver.
+ * 3. Get current ODBC version from env handle.
+ * 4. Check that version is of the expected value.
+ */
+BOOST_AUTO_TEST_CASE(TestSQLGetEnvAttrDriverVersion)
+{
+    Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+    SQLINTEGER version;
+    SQLRETURN ret = SQLGetEnvAttr(env, SQL_ATTR_ODBC_VERSION, &version, 0, 0);
+
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+    BOOST_CHECK_EQUAL(version, SQL_OV_ODBC3);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/modules/platforms/cpp/odbc-test/src/authentication_test.cpp b/modules/platforms/cpp/odbc-test/src/authentication_test.cpp
index 5a77370..e082413 100644
--- a/modules/platforms/cpp/odbc-test/src/authentication_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/authentication_test.cpp
@@ -85,8 +85,6 @@ struct AuthenticationTestSuiteFixture : odbc::OdbcTestSuite
      */
     static std::string MakeConnectionString(const std::string& user, const std::string& pass)
     {
-        std::string cfgDirPath = GetTestConfigDir();
-
         std::stringstream connectString;
 
         connectString <<
@@ -122,6 +120,95 @@ BOOST_AUTO_TEST_CASE(TestConnectionDefaultAuthSuccess)
     InsertTestBatch(11, 20, 9);
 }
 
+/**
+ * Check that connection with UID and PWD arguments established successfully.
+ *
+ * 1. Start test node with configured authentication.
+ * 2. Establish connection using UID and PWD arguments. Check that it established successfully.
+ * 3. Check that connection can be used successfully for SQL insert and select operations.
+ */
+BOOST_AUTO_TEST_CASE(TestConnectionLegacyAuthSuccess)
+{
+    std::stringstream comp;
+
+    comp <<
+        "DRIVER={Apache Ignite};"
+        "ADDRESS=127.0.0.1:11110;"
+        "SCHEMA=cache;"
+        "UID=" << defaultUser << ";"
+        "PWD=" << defaultPass << ";";
+
+    std::string connStr = comp.str();
+
+    Connect(connStr);
+
+    InsertTestStrings(10, false);
+    InsertTestBatch(11, 20, 9);
+}
+
+/**
+ * Check that connection with UID, USER, PWD and PASSWORD arguments established successfully.
+ *
+ * 1. Start test node with configured authentication.
+ * 2. Establish connection using UID, USER, PWD and PASSWORD arguments. Check that it established successfully.
+ * 3. Check that connection returns warning that password and user arguments duplicated.
+ * 4. Check that connection can be used successfully for SQL insert and select operations.
+ */
+BOOST_AUTO_TEST_CASE(TestConnectionBothAuthSuccess)
+{
+    std::stringstream comp;
+
+    comp <<
+        "DRIVER={Apache Ignite};"
+        "ADDRESS=127.0.0.1:11110;"
+        "SCHEMA=cache;"
+        "UID=" << defaultUser << ";"
+        "PWD=" << defaultPass << ";"
+        "USER=" << defaultUser << ";"
+        "PASSWORD=" << defaultPass << ";";
+
+    std::string connStr = comp.str();
+
+    Prepare();
+
+    // Connect string
+    std::vector<SQLCHAR> connectStr0(connStr.begin(), connStr.end());
+
+    SQLCHAR outstr[ODBC_BUFFER_SIZE];
+    SQLSMALLINT outstrlen;
+
+    // Connecting to ODBC server.
+    SQLRETURN ret = SQLDriverConnect(dbc, NULL, &connectStr0[0], static_cast<SQLSMALLINT>(connectStr0.size()),
+        outstr, sizeof(outstr), &outstrlen, SQL_DRIVER_COMPLETE);
+
+    if (!SQL_SUCCEEDED(ret))
+        BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_DBC, dbc));
+
+    BOOST_CHECK_EQUAL(ret, SQL_SUCCESS_WITH_INFO);
+
+    std::string message = GetOdbcErrorMessage(SQL_HANDLE_DBC, dbc);
+
+    BOOST_CHECK(!message.empty());
+
+    BOOST_CHECK(message.find("01S02") != std::string::npos);
+    BOOST_CHECK(message.find("Re-writing PASSWORD (have you specified it several times?") != std::string::npos);
+
+    message = GetOdbcErrorMessage(SQL_HANDLE_DBC, dbc, 2);
+
+    BOOST_CHECK(!message.empty());
+
+    BOOST_CHECK(message.find("01S02") != std::string::npos);
+    BOOST_CHECK(message.find("Re-writing USER (have you specified it several times?") != std::string::npos);
+
+    // Allocate a statement handle
+    SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
+
+    BOOST_REQUIRE(stmt != NULL);
+
+    InsertTestStrings(10, false);
+    InsertTestBatch(11, 20, 9);
+}
+
 BOOST_AUTO_TEST_CASE(TestConnectionAuthReject)
 {
     std::string state = ExpectConnectionReject(MakeConnectionString("unknown", "unknown"));
diff --git a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp
index 04f7692..05a0e46 100644
--- a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp
@@ -132,6 +132,212 @@ struct MetaQueriesTestSuiteFixture : public odbc::OdbcTestSuite
     }
 
     /**
+     * Check result set column metadata using SQLDescribeCol.
+     *
+     * @param stmt Statement.
+     * @param idx Index.
+     * @param expName Expected name.
+     * @param expDataType Expected data type.
+     * @param expSize Expected column size.
+     * @param expScale Expected column scale.
+     * @param expNullability expected nullability.
+     */
+    void CheckColumnMetaWithSQLDescribeCol(SQLHSTMT stmt, SQLUSMALLINT idx, const std::string& expName,
+        SQLSMALLINT expDataType, SQLULEN expSize, SQLSMALLINT expScale, SQLSMALLINT expNullability)
+    {
+        std::vector<SQLCHAR> name(ODBC_BUFFER_SIZE);
+        SQLSMALLINT nameLen = 0;
+        SQLSMALLINT dataType = 0;
+        SQLULEN size;
+        SQLSMALLINT scale;
+        SQLSMALLINT nullability;
+
+        SQLRETURN ret = SQLDescribeCol(stmt, idx, &name[0], (SQLSMALLINT)name.size(), &nameLen, &dataType, &size, &scale, &nullability);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        BOOST_CHECK_GE(nameLen, 0);
+        BOOST_CHECK_LE(nameLen, static_cast<SQLSMALLINT>(ODBC_BUFFER_SIZE));
+
+        std::string nameStr(name.begin(), name.begin() + nameLen);
+
+        BOOST_CHECK_EQUAL(nameStr, expName);
+        BOOST_CHECK_EQUAL(dataType, expDataType);
+        BOOST_CHECK_EQUAL(size, expSize);
+        BOOST_CHECK_EQUAL(scale, expScale);
+        BOOST_CHECK_EQUAL(nullability, expNullability);
+    }
+
+    /**
+     * @param func Function to call before tests. May be PrepareQuery or ExecQuery.
+     *
+     * 1. Start node.
+     * 2. Connect to node using ODBC.
+     * 3. Create table with decimal and char columns with specified size and scale.
+     * 4. Execute or prepare statement.
+     * 5. Check presicion and scale of every column using SQLDescribeCol.
+     */
+    template<typename F>
+    void CheckSQLDescribeColPrecisionAndScale(F func)
+    {
+        Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=PUBLIC");
+
+        SQLRETURN ret = ExecQuery(
+            "create table TestScalePrecision("
+            "   id int primary key,"
+            "   dec1 decimal(3,0),"
+            "   dec2 decimal(42,12),"
+            "   dec3 decimal,"
+            "   char1 char(3),"
+            "   char2 char(42),"
+            "   char3 char,"
+            "   vchar varchar"
+            ")");
+
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = SQLFreeStmt(stmt, SQL_CLOSE);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = ExecQuery(
+            "insert into "
+            "TestScalePrecision(id, dec1, dec2, dec3, char1, char2, char3, vchar) "
+            "values (1, 12, 160.23, -1234.56789, 'TST', 'Lorem Ipsum', 'Some test value', 'Some test varchar')");
+
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = SQLFreeStmt(stmt, SQL_CLOSE);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = (this->*func)("select id, dec1, dec2, dec3, char1, char2, char3, vchar from PUBLIC.TestScalePrecision");
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        SQLSMALLINT columnCount = 0;
+
+        ret = SQLNumResultCols(stmt, &columnCount);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        BOOST_CHECK_EQUAL(columnCount, 8);
+
+        CheckColumnMetaWithSQLDescribeCol(stmt, 1, "ID", SQL_INTEGER, 10, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLDescribeCol(stmt, 2, "DEC1", SQL_DECIMAL, 3, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLDescribeCol(stmt, 3, "DEC2", SQL_DECIMAL, 42, 12, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLDescribeCol(stmt, 4, "DEC3", SQL_DECIMAL, 65535, 32767, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLDescribeCol(stmt, 5, "CHAR1", SQL_VARCHAR, 3, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLDescribeCol(stmt, 6, "CHAR2", SQL_VARCHAR, 42, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLDescribeCol(stmt, 7, "CHAR3", SQL_VARCHAR, 2147483647, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLDescribeCol(stmt, 8, "VCHAR", SQL_VARCHAR, 2147483647, 0, SQL_NULLABLE_UNKNOWN);
+    }
+
+    /**
+     * Check result set column metadata using SQLColAttribute.
+     *
+     * @param stmt Statement.
+     * @param idx Index.
+     * @param expName Expected name.
+     * @param expDataType Expected data type.
+     * @param expSize Expected column size.
+     * @param expScale Expected column scale.
+     * @param expNullability expected nullability.
+     */
+    void CheckColumnMetaWithSQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT idx, const std::string& expName,
+        SQLLEN expDataType, SQLULEN expSize, SQLLEN expScale, SQLLEN expNullability)
+    {
+        std::vector<SQLCHAR> name(ODBC_BUFFER_SIZE);
+        SQLSMALLINT nameLen = 0;
+        SQLLEN dataType = 0;
+        SQLLEN size;
+        SQLLEN scale;
+        SQLLEN nullability;
+
+        SQLRETURN ret = SQLColAttribute(stmt, idx, SQL_DESC_NAME, &name[0], (SQLSMALLINT)name.size(), &nameLen, 0);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = SQLColAttribute(stmt, idx, SQL_DESC_TYPE, 0, 0, 0, &dataType);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = SQLColAttribute(stmt, idx, SQL_DESC_PRECISION, 0, 0, 0, &size);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = SQLColAttribute(stmt, idx, SQL_DESC_SCALE, 0, 0, 0, &scale);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = SQLColAttribute(stmt, idx, SQL_DESC_NULLABLE, 0, 0, 0, &nullability);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        BOOST_CHECK_GE(nameLen, 0);
+        BOOST_CHECK_LE(nameLen, static_cast<SQLSMALLINT>(ODBC_BUFFER_SIZE));
+
+        std::string nameStr(name.begin(), name.begin() + nameLen);
+
+        BOOST_CHECK_EQUAL(nameStr, expName);
+        BOOST_CHECK_EQUAL(dataType, expDataType);
+        BOOST_CHECK_EQUAL(size, expSize);
+        BOOST_CHECK_EQUAL(scale, expScale);
+        BOOST_CHECK_EQUAL(nullability, expNullability);
+    }
+
+    /**
+     * @param func Function to call before tests. May be PrepareQuery or ExecQuery.
+     *
+     * 1. Start node.
+     * 2. Connect to node using ODBC.
+     * 3. Create table with decimal and char columns with specified size and scale.
+     * 4. Execute or prepare statement.
+     * 5. Check presicion and scale of every column using SQLColAttribute.
+     */
+    template<typename F>
+    void CheckSQLColAttributePrecisionAndScale(F func)
+    {
+        Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=PUBLIC");
+
+        SQLRETURN ret = ExecQuery(
+            "create table TestScalePrecision("
+            "   id int primary key,"
+            "   dec1 decimal(3,0),"
+            "   dec2 decimal(42,12),"
+            "   dec3 decimal,"
+            "   char1 char(3),"
+            "   char2 char(42),"
+            "   char3 char,"
+            "   vchar varchar"
+            ")");
+
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = SQLFreeStmt(stmt, SQL_CLOSE);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = ExecQuery(
+            "insert into "
+            "TestScalePrecision(id, dec1, dec2, dec3, char1, char2, char3, vchar) "
+            "values (1, 12, 160.23, -1234.56789, 'TST', 'Lorem Ipsum', 'Some test value', 'Some test varchar')");
+
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = SQLFreeStmt(stmt, SQL_CLOSE);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        ret = (this->*func)("select id, dec1, dec2, dec3, char1, char2, char3, vchar from PUBLIC.TestScalePrecision");
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        SQLSMALLINT columnCount = 0;
+
+        ret = SQLNumResultCols(stmt, &columnCount);
+        ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+
+        BOOST_CHECK_EQUAL(columnCount, 8);
+
+        CheckColumnMetaWithSQLColAttribute(stmt, 1, "ID", SQL_INTEGER, 10, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLColAttribute(stmt, 2, "DEC1", SQL_DECIMAL, 3, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLColAttribute(stmt, 3, "DEC2", SQL_DECIMAL, 42, 12, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLColAttribute(stmt, 4, "DEC3", SQL_DECIMAL, 65535, 32767, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLColAttribute(stmt, 5, "CHAR1", SQL_VARCHAR, 3, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLColAttribute(stmt, 6, "CHAR2", SQL_VARCHAR, 42, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLColAttribute(stmt, 7, "CHAR3", SQL_VARCHAR, 2147483647, 0, SQL_NULLABLE_UNKNOWN);
+        CheckColumnMetaWithSQLColAttribute(stmt, 8, "VCHAR", SQL_VARCHAR, 2147483647, 0, SQL_NULLABLE_UNKNOWN);
+    }
+
+    /**
      * Destructor.
      */
     ~MetaQueriesTestSuiteFixture()
@@ -670,4 +876,58 @@ BOOST_AUTO_TEST_CASE(TestSQLNumResultColsAfterSQLPrepare)
     BOOST_CHECK_EQUAL(columnCount, 4);
 }
 
+/**
+ * Check that SQLDescribeCol return valid scale and precision for columns of different type after Prepare.
+ *
+ * 1. Start node.
+ * 2. Connect to node using ODBC.
+ * 3. Create table with decimal and char columns with specified size and scale.
+ * 4. Prepare statement.
+ * 5. Check presicion and scale of every column using SQLDescribeCol.
+ */
+BOOST_AUTO_TEST_CASE(TestSQLDescribeColPrecisionAndScaleAfterPrepare)
+{
+    CheckSQLDescribeColPrecisionAndScale(&OdbcTestSuite::PrepareQuery);
+}
+
+/**
+ * Check that SQLDescribeCol return valid scale and precision for columns of different type after Execute.
+ *
+ * 1. Start node.
+ * 2. Connect to node using ODBC.
+ * 3. Create table with decimal and char columns with specified size and scale.
+ * 4. Execute statement.
+ * 5. Check presicion and scale of every column using SQLDescribeCol. */
+BOOST_AUTO_TEST_CASE(TestSQLDescribeColPrecisionAndScaleAfterExec)
+{
+    CheckSQLDescribeColPrecisionAndScale(&OdbcTestSuite::ExecQuery);
+}
+
+/**
+ * Check that SQLColAttribute return valid scale and precision for columns of different type after Prepare.
+ *
+ * 1. Start node.
+ * 2. Connect to node using ODBC.
+ * 3. Create table with decimal and char columns with specified size and scale.
+ * 4. Prepare statement.
+ * 5. Check presicion and scale of every column using SQLColAttribute.
+ */
+BOOST_AUTO_TEST_CASE(TestSQLColAttributePrecisionAndScaleAfterPrepare)
+{
+    CheckSQLColAttributePrecisionAndScale(&OdbcTestSuite::PrepareQuery);
+}
+
+/**
+ * Check that SQLColAttribute return valid scale and precision for columns of different type after Execute.
+ *
+ * 1. Start node.
+ * 2. Connect to node using ODBC.
+ * 3. Create table with decimal and char columns with specified size and scale.
+ * 4. Execute statement.
+ * 5. Check presicion and scale of every column using SQLColAttribute. */
+BOOST_AUTO_TEST_CASE(TestSQLColAttributePrecisionAndScaleAfterExec)
+{
+    CheckSQLColAttributePrecisionAndScale(&OdbcTestSuite::ExecQuery);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/modules/platforms/cpp/odbc-test/src/test_utils.cpp b/modules/platforms/cpp/odbc-test/src/test_utils.cpp
index 6cdaed2..1519e73 100644
--- a/modules/platforms/cpp/odbc-test/src/test_utils.cpp
+++ b/modules/platforms/cpp/odbc-test/src/test_utils.cpp
@@ -40,7 +40,7 @@ namespace ignite_test
             std::string(reinterpret_cast<char*>(message), reallen));
     }
 
-    std::string GetOdbcErrorState(SQLSMALLINT handleType, SQLHANDLE handle)
+    std::string GetOdbcErrorState(SQLSMALLINT handleType, SQLHANDLE handle, int idx)
     {
         SQLCHAR sqlstate[7] = {};
         SQLINTEGER nativeCode;
@@ -48,12 +48,12 @@ namespace ignite_test
         SQLCHAR message[ODBC_BUFFER_SIZE];
         SQLSMALLINT reallen = 0;
 
-        SQLGetDiagRec(handleType, handle, 1, sqlstate, &nativeCode, message, ODBC_BUFFER_SIZE, &reallen);
+        SQLGetDiagRec(handleType, handle, idx, sqlstate, &nativeCode, message, ODBC_BUFFER_SIZE, &reallen);
 
         return std::string(reinterpret_cast<char*>(sqlstate));
     }
 
-    std::string GetOdbcErrorMessage(SQLSMALLINT handleType, SQLHANDLE handle)
+    std::string GetOdbcErrorMessage(SQLSMALLINT handleType, SQLHANDLE handle, int idx)
     {
         SQLCHAR sqlstate[7] = {};
         SQLINTEGER nativeCode;
@@ -61,7 +61,7 @@ namespace ignite_test
         SQLCHAR message[ODBC_BUFFER_SIZE];
         SQLSMALLINT reallen = 0;
 
-        SQLGetDiagRec(handleType, handle, 1, sqlstate, &nativeCode, message, ODBC_BUFFER_SIZE, &reallen);
+        SQLGetDiagRec(handleType, handle, idx, sqlstate, &nativeCode, message, ODBC_BUFFER_SIZE, &reallen);
 
         std::string res(reinterpret_cast<char*>(sqlstate));
 
diff --git a/modules/platforms/cpp/odbc-test/src/utility_test.cpp b/modules/platforms/cpp/odbc-test/src/utility_test.cpp
index 7fe602c..58469bd 100644
--- a/modules/platforms/cpp/odbc-test/src/utility_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/utility_test.cpp
@@ -90,4 +90,68 @@ BOOST_AUTO_TEST_CASE(TestUtilityWriteReadString)
     BOOST_REQUIRE(outStr4.empty());
 }
 
+void CheckDecimalWriteRead(const std::string& val)
+{
+    using namespace ignite::impl::binary;
+    using namespace ignite::impl::interop;
+    using namespace ignite::common;
+    using namespace ignite::utility;
+
+    InteropUnpooledMemory mem(1024);
+    InteropOutputStream outStream(&mem);
+    BinaryWriterImpl writer(&outStream, 0);
+
+    Decimal decimal(val);
+
+    WriteDecimal(writer, decimal);
+
+    outStream.Synchronize();
+
+    InteropInputStream inStream(&mem);
+    BinaryReaderImpl reader(&inStream);
+
+    Decimal out;
+    ReadDecimal(reader, out);
+
+    std::stringstream converter;
+    converter << out;
+
+    std::string res = converter.str();
+
+    BOOST_CHECK_EQUAL(res, val);
+}
+
+/**
+ * Check that Decimal writing and reading works as expected.
+ *
+ * 1. Create Decimal value.
+ * 2. Write using standard serialization algorithm.
+ * 3. Read using standard de-serialization algorithm.
+ * 4. Check that initial and read value are equal.
+ *
+ * Repeat with the following values: 0, 1, -1, 0.1, -0.1, 42, -42, 160, -160, 34729864879625196, -34729864879625196,
+ * 3472986487.9625196, -3472986487.9625196, 3472.9864879625196, -3472.9864879625196, 0.34729864879625196,
+ * -0.34729864879625196
+ */
+BOOST_AUTO_TEST_CASE(TestUtilityWriteReadDecimal)
+{
+    CheckDecimalWriteRead("0");
+    CheckDecimalWriteRead("1");
+    CheckDecimalWriteRead("-1");
+    CheckDecimalWriteRead("0.1");
+    CheckDecimalWriteRead("-0.1");
+    CheckDecimalWriteRead("42");
+    CheckDecimalWriteRead("-42");
+    CheckDecimalWriteRead("160");
+    CheckDecimalWriteRead("-160");
+    CheckDecimalWriteRead("34729864879625196");
+    CheckDecimalWriteRead("-34729864879625196");
+    CheckDecimalWriteRead("3472986487.9625196");
+    CheckDecimalWriteRead("-3472986487.9625196");
+    CheckDecimalWriteRead("3472.9864879625196");
+    CheckDecimalWriteRead("-3472.9864879625196");
+    CheckDecimalWriteRead("0.34729864879625196");
+    CheckDecimalWriteRead("-0.34729864879625196");
+}
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/config/connection_string_parser.h b/modules/platforms/cpp/odbc/include/ignite/odbc/config/connection_string_parser.h
index 1fdedf6..de02cb4 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/config/connection_string_parser.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/config/connection_string_parser.h
@@ -98,6 +98,12 @@ namespace ignite
                     /** Connection attribute keyword for password attribute. */
                     static const std::string password;
 
+                    /** Connection attribute keyword for username attribute. */
+                    static const std::string uid;
+
+                    /** Connection attribute keyword for password attribute. */
+                    static const std::string pwd;
+
                     /** Connection attribute keyword for nestedTxMode attribute. */
                     static const std::string nestedTxMode;
                 };
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/dsn_config.h b/modules/platforms/cpp/odbc/include/ignite/odbc/dsn_config.h
index dbad9b5..640046f 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/dsn_config.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/dsn_config.h
@@ -53,9 +53,10 @@ namespace ignite
          *
          * @param dsn DSN name.
          * @param config Configuration.
+         * @param diag Diagnostic collector.
          */
-        void ReadDsnConfiguration(const char* dsn, config::Configuration& config);
+        void ReadDsnConfiguration(const char* dsn, config::Configuration& config, diagnostic::DiagnosticRecordStorage *diag);
     }
 }
 
-#endif //_IGNITE_ODBC_DSN_CONFIG
\ No newline at end of file
+#endif //_IGNITE_ODBC_DSN_CONFIG
diff --git a/modules/platforms/cpp/odbc/os/win/src/system_dsn.cpp b/modules/platforms/cpp/odbc/os/win/src/system_dsn.cpp
index 733d0cd..0672911 100644
--- a/modules/platforms/cpp/odbc/os/win/src/system_dsn.cpp
+++ b/modules/platforms/cpp/odbc/os/win/src/system_dsn.cpp
@@ -189,7 +189,7 @@ BOOL INSTAPI ConfigDSN(HWND hwndParent, WORD req, LPCSTR driver, LPCSTR attribut
 
             Configuration loaded(config);
 
-            ReadDsnConfiguration(dsn.c_str(), loaded);
+            ReadDsnConfiguration(dsn.c_str(), loaded, &diag);
 
             if (!DisplayConfigureDsnWindow(hwndParent, loaded))
                 return FALSE;
@@ -218,4 +218,4 @@ BOOL INSTAPI ConfigDSN(HWND hwndParent, WORD req, LPCSTR driver, LPCSTR attribut
     }
 
     return TRUE;
-}
\ No newline at end of file
+}
diff --git a/modules/platforms/cpp/odbc/src/config/connection_string_parser.cpp b/modules/platforms/cpp/odbc/src/config/connection_string_parser.cpp
index fb779f6..a93e3b5 100644
--- a/modules/platforms/cpp/odbc/src/config/connection_string_parser.cpp
+++ b/modules/platforms/cpp/odbc/src/config/connection_string_parser.cpp
@@ -51,6 +51,8 @@ namespace ignite
             const std::string ConnectionStringParser::Key::sslCaFile              = "ssl_ca_file";
             const std::string ConnectionStringParser::Key::user                   = "user";
             const std::string ConnectionStringParser::Key::password               = "password";
+            const std::string ConnectionStringParser::Key::uid                    = "uid";
+            const std::string ConnectionStringParser::Key::pwd                    = "pwd";
             const std::string ConnectionStringParser::Key::nestedTxMode           = "nested_tx_mode";
 
             ConnectionStringParser::ConnectionStringParser(Configuration& cfg):
@@ -417,12 +419,24 @@ namespace ignite
                 {
                     cfg.SetDriver(value);
                 }
-                else if (lKey == Key::user)
+                else if (lKey == Key::user || lKey == Key::uid)
                 {
+                    if (!cfg.GetUser().empty() && diag)
+                    {
+                        diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
+                            "Re-writing USER (have you specified it several times?");
+                    }
+
                     cfg.SetUser(value);
                 }
-                else if (lKey == Key::password)
+                else if (lKey == Key::password || lKey == Key::pwd)
                 {
+                    if (!cfg.GetPassword().empty() && diag)
+                    {
+                        diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
+                            "Re-writing PASSWORD (have you specified it several times?");
+                    }
+
                     cfg.SetPassword(value);
                 }
                 else if (lKey == Key::nestedTxMode)
diff --git a/modules/platforms/cpp/odbc/src/connection.cpp b/modules/platforms/cpp/odbc/src/connection.cpp
index a5beb0c..b073580 100644
--- a/modules/platforms/cpp/odbc/src/connection.cpp
+++ b/modules/platforms/cpp/odbc/src/connection.cpp
@@ -119,7 +119,7 @@ namespace ignite
             {
                 std::string dsn = config.GetDsn();
 
-                ReadDsnConfiguration(dsn.c_str(), config);
+                ReadDsnConfiguration(dsn.c_str(), config, &GetDiagnosticRecords());
             }
 
             return InternalEstablish(config);
diff --git a/modules/platforms/cpp/odbc/src/dsn_config.cpp b/modules/platforms/cpp/odbc/src/dsn_config.cpp
index dcdb8f4..8f1a6df 100644
--- a/modules/platforms/cpp/odbc/src/dsn_config.cpp
+++ b/modules/platforms/cpp/odbc/src/dsn_config.cpp
@@ -72,10 +72,8 @@ namespace ignite
 
             std::string res(buf.GetData());
 
-            if (res == unique)
-                return val;
-
-            val.SetValue(res);
+            if (res != unique)
+                val.SetValue(res);
 
             return val;
         }
@@ -104,7 +102,7 @@ namespace ignite
             return res;
         }
 
-        void ReadDsnConfiguration(const char* dsn, Configuration& config)
+        void ReadDsnConfiguration(const char* dsn, Configuration& config, diagnostic::DiagnosticRecordStorage* diag)
         {
             SettableValue<std::string> address = ReadDsnString(dsn, ConnectionStringParser::Key::address);
 
@@ -112,7 +110,7 @@ namespace ignite
             {
                 std::vector<EndPoint> endPoints;
 
-                ParseAddress(address.GetValue(), endPoints, 0);
+                ParseAddress(address.GetValue(), endPoints, diag);
 
                 config.SetAddresses(endPoints);
             }
@@ -219,4 +217,4 @@ namespace ignite
                 config.SetNestedTxMode(NestedTxMode::FromString(nestedTxModeStr.GetValue(), config.GetNestedTxMode()));
         }
     }
-}
\ No newline at end of file
+}
diff --git a/modules/platforms/cpp/odbc/src/meta/column_meta.cpp b/modules/platforms/cpp/odbc/src/meta/column_meta.cpp
index 476f6a6..203b4ad 100644
--- a/modules/platforms/cpp/odbc/src/meta/column_meta.cpp
+++ b/modules/platforms/cpp/odbc/src/meta/column_meta.cpp
@@ -163,7 +163,7 @@ namespace ignite
                         if (scale == -1)
                             return false;
 
-                        value = common::LexicalCast<std::string>(precision);
+                        value = common::LexicalCast<std::string>(scale);
 
                         return true;
                     }
diff --git a/modules/platforms/cpp/odbc/src/odbc.cpp b/modules/platforms/cpp/odbc/src/odbc.cpp
index b9107fc..f297ec4 100644
--- a/modules/platforms/cpp/odbc/src/odbc.cpp
+++ b/modules/platforms/cpp/odbc/src/odbc.cpp
@@ -325,7 +325,7 @@ namespace ignite
 
         LOG_MSG("DSN: " << dsn);
 
-        odbc::ReadDsnConfiguration(dsn.c_str(), config);
+        odbc::ReadDsnConfiguration(dsn.c_str(), config, &connection->GetDiagnosticRecords());
 
         connection->Establish(config);
 
@@ -1144,6 +1144,7 @@ namespace ignite
         using odbc::Environment;
 
         LOG_MSG("SQLSetEnvAttr called");
+        LOG_MSG("Attribute: " << attr << ", Value: " << (size_t)value);
 
         Environment *environment = reinterpret_cast<Environment*>(env);
 
@@ -1174,7 +1175,7 @@ namespace ignite
             return SQL_INVALID_HANDLE;
 
         SqlLen outResLen;
-        ApplicationDataBuffer outBuffer(OdbcNativeType::AI_DEFAULT, valueBuf,
+        ApplicationDataBuffer outBuffer(OdbcNativeType::AI_SIGNED_LONG, valueBuf,
             static_cast<int32_t>(valueBufLen), &outResLen);
 
         environment->GetAttribute(attr, outBuffer);
diff --git a/modules/platforms/cpp/odbc/src/statement.cpp b/modules/platforms/cpp/odbc/src/statement.cpp
index 9253030..cc508e3 100644
--- a/modules/platforms/cpp/odbc/src/statement.cpp
+++ b/modules/platforms/cpp/odbc/src/statement.cpp
@@ -1096,6 +1096,8 @@ namespace ignite
         {
             const meta::ColumnMetaVector *meta = GetMeta();
 
+            LOG_MSG("Collumn ID: " << colIdx << ", Attribute ID: " << attrId);
+
             if (!meta)
                 return SqlResult::AI_ERROR;
 
diff --git a/modules/platforms/cpp/odbc/src/utility.cpp b/modules/platforms/cpp/odbc/src/utility.cpp
index 498490c..c060a0a 100644
--- a/modules/platforms/cpp/odbc/src/utility.cpp
+++ b/modules/platforms/cpp/odbc/src/utility.cpp
@@ -111,10 +111,18 @@ namespace ignite
 
             unscaled.MagnitudeToBytes(magnitude);
 
-            if (unscaled.GetSign() == -1)
-                magnitude[0] |= -0x80;
+            int8_t addBit = unscaled.GetSign() == -1 ? -0x80 : 0;
 
-            writer.WriteInt32(magnitude.GetSize());
+            if (magnitude[0] < 0)
+            {
+                writer.WriteInt32(magnitude.GetSize() + 1);
+                writer.WriteInt8(addBit);
+            }
+            else
+            {
+                writer.WriteInt32(magnitude.GetSize());
+                magnitude[0] |= addBit;
+            }
 
             impl::binary::BinaryUtils::WriteInt8Array(writer.GetStream(), magnitude.GetData(), magnitude.GetSize());
         }