From 9af2284c9abb99849fa06624caa4c78b220a6665 Mon Sep 17 00:00:00 2001 From: Forest Vey Date: Thu, 19 Sep 2024 16:00:00 -0700 Subject: [PATCH] Fix power bi connection (#22) * Fix bug with SQLGetData incorrect set strLen and add exception for login timeout attribute. Signed-off-by: Forest Vey * Adding test case for empty response. Signed-off-by: Forest Vey * Add SQLGetData test and update to not set SQL_NO_TOTAL for empty responses. Signed-off-by: Forest Vey * Cleaning up test comments. Signed-off-by: Forest Vey * Removing not needed empty fetch test. Signed-off-by: Forest Vey * re-work putStrToBuffer function. Signed-off-by: Forest Vey * Indentation fix. Signed-off-by: Forest Vey * Update PutStrToStrBuffer to note use SQL_NO_TOTAL. Signed-off-by: Forest Vey * Revising setting cell offset. Signed-off-by: Forest Vey * Cleaning up code for meaningless variables and conversions. Signed-off-by: Forest Vey * Fixing off by one error when setting truncated cell offset. Signed-off-by: Forest Vey * Updating wstring_convert to fix compilation error. Signed-off-by: Forest Vey * Removing wstring_convert dependency to application_data_buffer_test. Signed-off-by: Forest Vey * Moving to older version of cmake to fix windows build CI. Signed-off-by: Forest Vey * Testing ignoring warnings compilation to fix windows build. Signed-off-by: Forest Vey * Adding noerr flag for msbuild commands with aws-sdk-cpp. Signed-off-by: Forest Vey * Upgrading cmake min req version. Signed-off-by: Forest Vey * Reverting cmake version change. Signed-off-by: Forest Vey * Revising test for reslen comparison in bytes. Signed-off-by: Forest Vey * adding warnaserror flag for msbuild with aws-sdk-cpp. Signed-off-by: Forest Vey * Adding noerr for C2220 Signed-off-by: Forest Vey * Fixing noerr flag for msbuild command. Signed-off-by: Forest Vey * Testing DAWS_SDK_WARNING_ARE_ERRORS Signed-off-by: Forest Vey * re-organizing aws-sdk-cpp build script to ignore warnings. Signed-off-by: Forest Vey * Re-testing DAWS_SDK_WARNINGS_ARE_ERRORS Signed-off-by: Forest Vey * Testing deprecated-declarations Signed-off-by: Forest Vey * Removing noerr portions to msbuild config. Signed-off-by: Forest Vey * Adding MSVC specific error ignore. Signed-off-by: Forest Vey * Disabling WX for aws-sdk-cpp compilation. Signed-off-by: Forest Vey * Reverting aws-sdk-cpp fixes to use upstream instead. Signed-off-by: Forest Vey * Removing aws-sdk-cpp build script for windows. Signed-off-by: Forest Vey * Fixing invalid version updates for workflows during rebase. Signed-off-by: Forest Vey --------- Signed-off-by: Forest Vey --- src/odbc/src/app/application_data_buffer.cpp | 23 +++-- src/odbc/src/connection.cpp | 5 ++ .../src/api_robustness_test.cpp | 26 ++++++ .../src/application_data_buffer_test.cpp | 86 +++++++++++++++++++ 4 files changed, 127 insertions(+), 13 deletions(-) diff --git a/src/odbc/src/app/application_data_buffer.cpp b/src/odbc/src/app/application_data_buffer.cpp index 26e80e51..c06f2500 100644 --- a/src/odbc/src/app/application_data_buffer.cpp +++ b/src/odbc/src/app/application_data_buffer.cpp @@ -277,27 +277,25 @@ ConversionResult::Type ApplicationDataBuffer::PutStrToStrBuffer( return ConversionResult::Type::AI_SUCCESS; } + // If cellOffset is not -1, we are on a continuation SqlUlen currentCellOffset = cellOffset >= 0 ? cellOffset : 0; - // Since cellOffset is in bytes, an index needs to be calculated. - SqlUlen inCharIndex = currentCellOffset / inCharSize; - if (inCharIndex >= value.length()) { - if (resLenPtr) { - *resLenPtr = SQL_NO_TOTAL; - } + // If all data has already been read, return AI_NO_DATA + if ((currentCellOffset * outCharSize) >= bytesRequired) { return ConversionResult::Type::AI_NO_DATA; } + size_t bytesWritten = 0; bool isTruncated = false; if (inCharSize == 1) { if (outCharSize == 2 || outCharSize == 4) { bytesWritten = utility::CopyUtf8StringToSqlWcharString( - reinterpret_cast< const char* >(value.c_str() + inCharIndex), + reinterpret_cast< const char* >(value.c_str() + currentCellOffset), reinterpret_cast< SQLWCHAR* >(dataPtr), buflen, isTruncated); } else if (sizeof(OutCharT) == 1) { bytesWritten = utility::CopyUtf8StringToSqlCharString( - reinterpret_cast< const char* >(value.c_str() + inCharIndex), + reinterpret_cast< const char* >(value.c_str() + currentCellOffset), reinterpret_cast< SQLCHAR* >(dataPtr), buflen, isTruncated); } else { LOG_ERROR_MSG("Unexpected conversion from UTF8 string."); @@ -326,14 +324,13 @@ ConversionResult::Type ApplicationDataBuffer::PutStrToStrBuffer( *resLenPtr = remainingBytesRequired; } - if (cellOffset >= 0) { - size_t numCharsWritten = bytesWritten / outCharSize; - SetCellOffset(cellOffset + numCharsWritten * inCharSize); - } - if (isTruncated) { + SetCellOffset(currentCellOffset + ((buflen / outCharSize) - 1)); return ConversionResult::Type::AI_VARLEN_DATA_TRUNCATED; } else { + if (cellOffset >= 0) { + SetCellOffset(totalBytesWritten); + } return ConversionResult::Type::AI_SUCCESS; } } diff --git a/src/odbc/src/connection.cpp b/src/odbc/src/connection.cpp index abf3e866..7332e09e 100644 --- a/src/odbc/src/connection.cpp +++ b/src/odbc/src/connection.cpp @@ -476,6 +476,11 @@ SqlResult::Type Connection::InternalSetAttribute(int attr, void* value, LOG_INFO_MSG("log level is set to " << static_cast< int >(type)); break; } + + case SQL_LOGIN_TIMEOUT: { + LOG_INFO_MSG("login timeout is not implemented yet and this value is ignored."); + break; + } default: { AddStatusRecord(SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED, "Specified attribute is not supported."); diff --git a/src/tests/integration-test/src/api_robustness_test.cpp b/src/tests/integration-test/src/api_robustness_test.cpp index 898eed8a..183d99c3 100644 --- a/src/tests/integration-test/src/api_robustness_test.cpp +++ b/src/tests/integration-test/src/api_robustness_test.cpp @@ -856,6 +856,32 @@ BOOST_AUTO_TEST_CASE(TestSQLGetData) { SQLFetch(stmt); } +BOOST_AUTO_TEST_CASE(TestSQLGetDataEmpty) { + ConnectToTS(); + + std::vector< SQLWCHAR > sql = + MakeSqlBuffer("select ''"); + + SQLRETURN ret = SQLExecDirect(stmt, sql.data(), SQL_NTS); + + ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); + + ret = SQLFetch(stmt); + + ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); + + SQLWCHAR buffer[ODBC_BUFFER_SIZE]; + SQLLEN resLen = 0; + + ret = SQLGetData(stmt, 1, SQL_C_WCHAR, buffer, sizeof(buffer), &resLen); + // Empty response should set resLen to 0 + BOOST_CHECK(resLen == 0); + + ret = SQLGetData(stmt, 1, SQL_C_WCHAR, buffer, sizeof(buffer), &resLen); + // Follow up calls to SQLGetData should return SQL_NO_DATA + BOOST_CHECK_EQUAL(ret, SQL_NO_DATA); +} + BOOST_AUTO_TEST_CASE(TestSQLGetDataVarcharAsciiZeroBufferLength) { // Ensures that calling SQLGetData with a buffer length of zero // returns the required amount of data in the indicator pointer. diff --git a/src/tests/integration-test/src/application_data_buffer_test.cpp b/src/tests/integration-test/src/application_data_buffer_test.cpp index 81429f83..9d2e4da1 100644 --- a/src/tests/integration-test/src/application_data_buffer_test.cpp +++ b/src/tests/integration-test/src/application_data_buffer_test.cpp @@ -1549,6 +1549,92 @@ BOOST_AUTO_TEST_CASE(TestSetStringWithOffset) { BOOST_CHECK(buf[1].reslen == strlen("Hello with offset!")); } + +BOOST_AUTO_TEST_CASE(TestSetLongString) { + char buffer[1024]; + SqlLen reslen = 0; + + ApplicationDataBuffer appBuf(OdbcNativeType::AI_CHAR, buffer, sizeof(buffer), + &reslen); + + std::string longString("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus cursus urna in nibh congue, at semper sapien efficitur. \ + Cras eget velit at eros pharetra tempus. Sed laoreet lorem nunc, a congue orci euismod vel. Cras molestie tellus vitae nisl pretium, a tristique magna \ + tristique. In feugiat leo ac augue elementum facilisis. Pellentesque sollicitudin fringilla felis, id ornare mi lobortis a. Cras id eros vel nisi \ + condimentum elementum sed eget tellus. Pellentesque commodo augue vitae diam suscipit dictum. Quisque vulputate a nulla dignissim semper. Cras rhoncus \ + tortor sed ante maximus bibendum. Vivamus accumsan eros sem, vitae accumsan purus ornare quis. Cras eget quam at ipsum vestibulum laoreet. Nullam ut \ + massa rhoncus, elementum velit nec, dignissim eros. Suspendisse potenti. Vivamus feugiat urna arcu, ut blandit dui molestie porttitor. Suspendisse mi \ + risus, luctus vitae bibendum quis, ornare eget lacus. Maecenas rutrum sapien at interdum sagittis. Donec consectetur rutrum leo at ornare. Duis molestie \ + diam ac diam imperdiet gravida. Ut sed tempus lorem. Vestibulum mattis quam mauris, non iaculis risus faucibus nec. Nullam congue volutpat gravida. \ + Nullam vestibulum metus ultricies, consequat sem id, viverra velit. Sed eu diam eget purus rhoncus viverra ac et magna. Maecenas quis dignissim tortor, \ + eu sodales ante. Maecenas rhoncus et massa eu suscipit. Sed pulvinar, sem vel viverra semper, dui risus ornare libero, vitae lacinia leo metus pharetra \ + massa. Phasellus elementum efficitur nibh at blandit. Phasellus et auctor augue. Praesent quis facilisis orci. Orci varius natoque penatibus et magnis dis \ + parturient montes, nascetur ridiculus mus. Nullam maximus ac mauris vel semper. Integer hendrerit bibendum nulla vitae blandit. Suspendisse in eleifend \ + libero, ac semper est. Fusce non mattis lorem. Sed finibus leo egestas finibus euismod. Maecenas quis mauris vitae lacus efficitur mollis. Fusce faucibus \ + fermentum mauris, vitae vestibulum ligula aliquam in. Proin ut eu."); + + ConversionResult::Type ret = appBuf.PutString(longString); + BOOST_CHECK(!longString.substr(0, 1023).compare(buffer)); + BOOST_CHECK_EQUAL(reslen, longString.size() - 1023); + BOOST_CHECK(ConversionResult::Type::AI_VARLEN_DATA_TRUNCATED == ret); + + ret = appBuf.PutString(longString); + BOOST_CHECK_EQUAL(reslen, longString.size() - 2046); + BOOST_CHECK(!longString.substr(1023, 1023).compare(buffer)); + BOOST_CHECK(ConversionResult::Type::AI_VARLEN_DATA_TRUNCATED == ret); + + ret = appBuf.PutString(longString); + BOOST_CHECK(ConversionResult::Type::AI_SUCCESS == ret); + BOOST_CHECK(!longString.substr(2046).compare(buffer)); + BOOST_CHECK_EQUAL(reslen, 2080); + + ret = appBuf.PutString(longString); + BOOST_CHECK(ConversionResult::Type::AI_NO_DATA == ret); +} + +BOOST_AUTO_TEST_CASE(TestSetLongStringWchar) { + SQLWCHAR buffer[1024]; + SqlLen reslen = 0; + + ApplicationDataBuffer appBuf(OdbcNativeType::AI_WCHAR, buffer, sizeof(buffer), + &reslen); + + std::string longString("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus cursus urna in nibh congue, at semper sapien efficitur. \ + Cras eget velit at eros pharetra tempus. Sed laoreet lorem nunc, a congue orci euismod vel. Cras molestie tellus vitae nisl pretium, a tristique magna \ + tristique. In feugiat leo ac augue elementum facilisis. Pellentesque sollicitudin fringilla felis, id ornare mi lobortis a. Cras id eros vel nisi \ + condimentum elementum sed eget tellus. Pellentesque commodo augue vitae diam suscipit dictum. Quisque vulputate a nulla dignissim semper. Cras rhoncus \ + tortor sed ante maximus bibendum. Vivamus accumsan eros sem, vitae accumsan purus ornare quis. Cras eget quam at ipsum vestibulum laoreet. Nullam ut \ + massa rhoncus, elementum velit nec, dignissim eros. Suspendisse potenti. Vivamus feugiat urna arcu, ut blandit dui molestie porttitor. Suspendisse mi \ + risus, luctus vitae bibendum quis, ornare eget lacus. Maecenas rutrum sapien at interdum sagittis. Donec consectetur rutrum leo at ornare. Duis molestie \ + diam ac diam imperdiet gravida. Ut sed tempus lorem. Vestibulum mattis quam mauris, non iaculis risus faucibus nec. Nullam congue volutpat gravida. \ + Nullam vestibulum metus ultricies, consequat sem id, viverra velit. Sed eu diam eget purus rhoncus viverra ac et magna. Maecenas quis dignissim tortor, \ + eu sodales ante. Maecenas rhoncus et massa eu suscipit. Sed pulvinar, sem vel viverra semper, dui risus ornare libero, vitae lacinia leo metus pharetra \ + massa. Phasellus elementum efficitur nibh at blandit. Phasellus et auctor augue. Praesent quis facilisis orci. Orci varius natoque penatibus et magnis dis \ + parturient montes, nascetur ridiculus mus. Nullam maximus ac mauris vel semper. Integer hendrerit bibendum nulla vitae blandit. Suspendisse in eleifend \ + libero, ac semper est. Fusce non mattis lorem. Sed finibus leo egestas finibus euismod. Maecenas quis mauris vitae lacus efficitur mollis. Fusce faucibus \ + fermentum mauris, vitae vestibulum ligula aliquam in. Proin ut eu."); + + ConversionResult::Type ret = appBuf.PutString(longString); + + BOOST_CHECK(!longString.substr(0, 1023).compare(timestream::odbc::utility::SqlWcharToString(buffer, SQL_NTS))); + BOOST_CHECK_EQUAL(reslen, (longString.size() - 1023) * sizeof(SQLWCHAR)); + BOOST_CHECK(ConversionResult::Type::AI_VARLEN_DATA_TRUNCATED == ret); + + ret = appBuf.PutString(longString); + + BOOST_CHECK(!longString.substr(1023, 1023).compare(timestream::odbc::utility::SqlWcharToString(buffer, SQL_NTS))); + BOOST_CHECK_EQUAL(reslen, (longString.size() - 2046) * sizeof(SQLWCHAR)); + BOOST_CHECK(ConversionResult::Type::AI_VARLEN_DATA_TRUNCATED == ret); + + ret = appBuf.PutString(longString); + + BOOST_CHECK(!longString.substr(2046).compare(timestream::odbc::utility::SqlWcharToString(buffer, SQL_NTS))); + BOOST_CHECK(ConversionResult::Type::AI_SUCCESS == ret); + BOOST_CHECK_EQUAL(reslen, longString.size() * sizeof(SQLWCHAR)); + + ret = appBuf.PutString(longString); + BOOST_CHECK(ConversionResult::Type::AI_NO_DATA == ret); +} + BOOST_AUTO_TEST_CASE(TestGetDateFromString) { char buf[] = "1999-02-22"; SqlLen reslen = sizeof(buf);