diff --git a/src/odbc-test/CMakeLists.txt b/src/odbc-test/CMakeLists.txt index bde3b9d62..dcf07890e 100644 --- a/src/odbc-test/CMakeLists.txt +++ b/src/odbc-test/CMakeLists.txt @@ -51,6 +51,7 @@ set(SOURCES src/column_meta_test.cpp src/configuration_test.cpp src/connection_test.cpp + src/cursor_binding_test.cpp src/java_test.cpp src/jni_test.cpp src/log_test.cpp diff --git a/src/odbc-test/include/odbc_test_suite.h b/src/odbc-test/include/odbc_test_suite.h index 84ed74e06..2b6f1e442 100644 --- a/src/odbc-test/include/odbc_test_suite.h +++ b/src/odbc-test/include/odbc_test_suite.h @@ -201,49 +201,64 @@ struct OdbcTestSuite { void InsertNonFullBatchSelect(int recordsNum, int splitAt); /** - * Get test i8Field. + * Get test i64Field. * * @param idx Index. - * @return Corresponding i8Field value. + * @return Corresponding i64Field value. */ - static int8_t GetTestI8Field(int64_t idx); + static int64_t GetTestI64Field(int idx); /** - * Check i8Field test value. + * Check i64Field test value. * @param idx Index. * @param value Value to test. */ - static void CheckTestI8Value(int idx, int8_t value); + static void CheckTestI64Value(int idx, int64_t value); /** - * Get test i16Field. + * Get test i32Field. * * @param idx Index. - * @return Corresponding i16Field value. + * @return Corresponding i32Field value. */ - static int16_t GetTestI16Field(int64_t idx); + static int32_t GetTestI32Field(int idx); /** - * Check i16Field test value. + * Check i32Field test value. * @param idx Index. * @param value Value to test. */ - static void CheckTestI16Value(int idx, int16_t value); + static void CheckTestI32Value(int idx, int32_t value); /** - * Get test i32Field. + * Get test _id string. * * @param idx Index. - * @return Corresponding i32Field value. + * @return Corresponding test string. */ - static int32_t GetTestI32Field(int64_t idx); + static std::string GetIdString(int idx); /** - * Check i32Field test value. + * Check _id test value. * @param idx Index. * @param value Value to test. */ - static void CheckTestI32Value(int idx, int32_t value); + static void CheckTestIdValue(int idx, const std::string& value); + + /** + * Get test Decimal 128 as string. + * + * @param idx Index. + * @return Corresponding test string. + */ + static std::string GetTestDec128String(int64_t idx); + + /** + * Check fieldDecimal128 test value. + * @param idx Index. + * @param value Value to test. + */ + static void CheckTestDec128Value(int idx, const std::string& value); /** * Get test string. diff --git a/src/odbc-test/input/queries_test_006.json b/src/odbc-test/input/queries_test_006.json new file mode 100644 index 000000000..2d04df8a8 --- /dev/null +++ b/src/odbc-test/input/queries_test_006.json @@ -0,0 +1,370 @@ +[ + { + "_id": { + "$oid": "62196dcc4d91892191475130" + }, + "fieldInt": 0, + "fieldLong": 0, + "fieldDecimal128": { + "$numberDecimal": "0.0" + }, + "fieldDouble": 0.0, + "fieldString": "String#0", + "fieldBoolean": 1, + "fieldDate": { + "$date": "2017-01-01T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d91892191475131" + }, + "fieldInt": 32, + "fieldLong": 64, + "fieldDecimal128": { + "$numberDecimal": "1.0" + }, + "fieldDouble": 0.25, + "fieldString": "String#1", + "fieldBoolean": 0, + "fieldDate": { + "$date": "2017-01-02T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d91892191475132" + }, + "fieldInt": 64, + "fieldLong": 128, + "fieldDecimal128": { + "$numberDecimal": "2.0" + }, + "fieldDouble": 0.5, + "fieldString": "String#2", + "fieldBoolean": 1, + "fieldDate": { + "$date": "2017-01-03T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d91892191475133" + }, + "fieldInt": 96, + "fieldLong": 192, + "fieldDecimal128": { + "$numberDecimal": "3.0" + }, + "fieldDouble": 0.75, + "fieldString": "String#3", + "fieldBoolean": 0, + "fieldDate": { + "$date": "2017-01-04T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d91892191475134" + }, + "fieldInt": 128, + "fieldLong": 256, + "fieldDecimal128": { + "$numberDecimal": "4.0" + }, + "fieldDouble": 1.0, + "fieldString": "String#4", + "fieldBoolean": 1, + "fieldDate": { + "$date": "2017-01-05T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d91892191475135" + }, + "fieldInt": 160, + "fieldLong": 320, + "fieldDecimal128": { + "$numberDecimal": "5.0" + }, + "fieldDouble": 1.25, + "fieldString": "String#5", + "fieldBoolean": 0, + "fieldDate": { + "$date": "2017-01-06T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d91892191475136" + }, + "fieldInt": 192, + "fieldLong": 384, + "fieldDecimal128": { + "$numberDecimal": "6.0" + }, + "fieldDouble": 1.5, + "fieldString": "String#6", + "fieldBoolean": 1, + "fieldDate": { + "$date": "2017-01-07T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d91892191475137" + }, + "fieldInt": 224, + "fieldLong": 448, + "fieldDecimal128": { + "$numberDecimal": "7.0" + }, + "fieldDouble": 1.75, + "fieldString": "String#7", + "fieldBoolean": 0, + "fieldDate": { + "$date": "2017-01-08T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d91892191475138" + }, + "fieldInt": 256, + "fieldLong": 512, + "fieldDecimal128": { + "$numberDecimal": "8.0" + }, + "fieldDouble": 2.0, + "fieldString": "String#8", + "fieldBoolean": 1, + "fieldDate": { + "$date": "2017-01-09T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d91892191475139" + }, + "fieldInt": 288, + "fieldLong": 576, + "fieldDecimal128": { + "$numberDecimal": "9.0" + }, + "fieldDouble": 2.25, + "fieldString": "String#9", + "fieldBoolean": 0, + "fieldDate": { + "$date": "2017-01-10T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d9189219147513a" + }, + "fieldInt": 320, + "fieldLong": 640, + "fieldDecimal128": { + "$numberDecimal": "10.0" + }, + "fieldDouble": 2.5, + "fieldString": "String#10", + "fieldBoolean": 1, + "fieldDate": { + "$date": "2017-01-11T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d9189219147513b" + }, + "fieldInt": 352, + "fieldLong": 704, + "fieldDecimal128": { + "$numberDecimal": "11.0" + }, + "fieldDouble": 2.75, + "fieldString": "String#11", + "fieldBoolean": 0, + "fieldDate": { + "$date": "2017-01-12T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d9189219147513c" + }, + "fieldInt": 384, + "fieldLong": 768, + "fieldDecimal128": { + "$numberDecimal": "12.0" + }, + "fieldDouble": 3.0, + "fieldString": "String#12", + "fieldBoolean": 1, + "fieldDate": { + "$date": "2017-01-13T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d9189219147513d" + }, + "fieldInt": 416, + "fieldLong": 832, + "fieldDecimal128": { + "$numberDecimal": "13.0" + }, + "fieldDouble": 3.25, + "fieldString": "String#13", + "fieldBoolean": 0, + "fieldDate": { + "$date": "2017-01-14T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d9189219147513e" + }, + "fieldInt": 448, + "fieldLong": 896, + "fieldDecimal128": { + "$numberDecimal": "14.0" + }, + "fieldDouble": 3.5, + "fieldString": "String#14", + "fieldBoolean": 1, + "fieldDate": { + "$date": "2017-01-15T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d9189219147513f" + }, + "fieldInt": 480, + "fieldLong": 960, + "fieldDecimal128": { + "$numberDecimal": "15.0" + }, + "fieldDouble": 3.75, + "fieldString": "String#15", + "fieldBoolean": 0, + "fieldDate": { + "$date": "2017-01-16T00:00:00Z" + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + } +] diff --git a/src/odbc-test/src/api_robustness_test.cpp b/src/odbc-test/src/api_robustness_test.cpp index 0d2e1e59d..8f581668c 100644 --- a/src/odbc-test/src/api_robustness_test.cpp +++ b/src/odbc-test/src/api_robustness_test.cpp @@ -29,7 +29,7 @@ #include #include -#include "documentdb/odbc/impl/binary/binary_utils.h" +#include "documentdb/odbc/utility.h" #include "odbc_test_suite.h" #include "test_type.h" #include "test_utils.h" @@ -143,41 +143,32 @@ BOOST_AUTO_TEST_CASE(TestSQLSetStmtAttrRowArraySize) { Connect(dsnConnectionString); + SQLINTEGER prev_row_array_size = 1; SQLINTEGER actual_row_array_size; SQLINTEGER resLen = 0; - // check that statement array size cannot be set to values not equal to 1 + // check that statement array size cannot be set to values less than 1 // repeat test for different values - SQLULEN valList[5] = {0, 2, 3, 4, 5}; + SQLULEN valList[] = {6, 0, 2, 3, 4, 5, 1}; for (SQLULEN val : valList) { SQLRETURN ret = SQLSetStmtAttr(stmt, SQL_ATTR_ROW_ARRAY_SIZE, reinterpret_cast< SQLPOINTER >(val), sizeof(val)); - BOOST_CHECK_EQUAL(ret, SQL_ERROR); + if (val >= 1) { + BOOST_CHECK_EQUAL(ret, SQL_SUCCESS); + } else { + BOOST_CHECK_EQUAL(ret, SQL_ERROR); + CheckSQLStatementDiagnosticError("HY024"); + } ret = SQLGetStmtAttr(stmt, SQL_ATTR_ROW_ARRAY_SIZE, &actual_row_array_size, sizeof(actual_row_array_size), &resLen); - ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - BOOST_CHECK_EQUAL(actual_row_array_size, 1); + BOOST_CHECK_EQUAL(actual_row_array_size, val > 0 ? val : prev_row_array_size); + prev_row_array_size = actual_row_array_size; } - - // check that setting row array size to 1 is successful - SQLULEN val = 1; - SQLRETURN ret = - SQLSetStmtAttr(stmt, SQL_ATTR_ROW_ARRAY_SIZE, - reinterpret_cast< SQLPOINTER >(val), sizeof(val)); - - BOOST_CHECK_EQUAL(ret, SQL_SUCCESS); - - ret = SQLGetStmtAttr(stmt, SQL_ATTR_ROW_ARRAY_SIZE, &actual_row_array_size, - sizeof(actual_row_array_size), &resLen); - - ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - - BOOST_CHECK_EQUAL(actual_row_array_size, val); } #ifndef __APPLE__ @@ -581,12 +572,15 @@ BOOST_AUTO_TEST_CASE(TestSQLBindParameter) { unsupportedSql[i], 100, 100, &ind1, sizeof(ind1), &len1); + std::string expectedSqlState; #ifdef __APPLE__ + expectedSqlState = "HY105"; BOOST_REQUIRE_EQUAL(ret, SQL_INVALID_HANDLE); #else + expectedSqlState = "HYC00"; BOOST_REQUIRE_EQUAL(ret, SQL_ERROR); #endif - CheckSQLStatementDiagnosticError("HYC00"); + CheckSQLStatementDiagnosticError(expectedSqlState); } // Size is null. diff --git a/src/odbc-test/src/cursor_binding_test.cpp b/src/odbc-test/src/cursor_binding_test.cpp index 81b272bdf..d41f42096 100644 --- a/src/odbc-test/src/cursor_binding_test.cpp +++ b/src/odbc-test/src/cursor_binding_test.cpp @@ -27,113 +27,81 @@ #include #include -#include "documentdb/ignite.h" -#include "documentdb/ignition.h" -#include "documentdb/odbc/impl/binary/binary_utils.h" +#include "documentdb/odbc/utility.h" #include "odbc_test_suite.h" #include "test_type.h" #include "test_utils.h" using namespace documentdb; -using namespace documentdb::cache; -using namespace documentdb::cache::query; -using namespace documentdb::common; using namespace documentdb_test; using namespace boost::unit_test; -using documentdb::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"); - } + CursorBindingTestSuiteFixture() = default; /** * Destructor. */ - virtual ~CursorBindingTestSuiteFixture() { - // No-op. - } - - /** Node started during the test. */ - Ignite grid; - - /** Test cache instance. */ - Cache< int64_t, TestType > testCache; + virtual ~CursorBindingTestSuiteFixture() = default; }; 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])); \ - } \ +#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(idFieldsLen[idx] != SQL_NULL_DATA); \ + BOOST_CHECK(i32FieldsInd[idx] != SQL_NULL_DATA); \ + BOOST_CHECK(i64FieldsInd[idx] != SQL_NULL_DATA); \ + BOOST_CHECK(dec128FieldsLen[idx] != SQL_NULL_DATA); \ + BOOST_CHECK(doubleFieldsInd[idx] != SQL_NULL_DATA); \ + BOOST_CHECK(strFieldsLen[idx] != SQL_NULL_DATA); \ + BOOST_CHECK(boolFieldsInd[idx] != SQL_NULL_DATA); \ + BOOST_CHECK(dateFieldsInd[idx] != SQL_NULL_DATA); \ + BOOST_CHECK(nullFieldsLen[idx] == SQL_NULL_DATA); \ + BOOST_CHECK(binaryFieldsLen[idx] != SQL_NULL_DATA); \ + \ + std::string idField = utility::SqlWcharToString(&idFields[idx][0]); \ + int32_t i32Field = static_cast< int32_t >(i32Fields[idx]); \ + int32_t i64Field = static_cast< int64_t >(i64Fields[idx]); \ + std::string dec128Field = \ + utility::SqlWcharToString(&dec128Fields[idx][0]); \ + double doubleField = static_cast< double >(doubleFields[idx]); \ + std::string strField = utility::SqlWcharToString(&strFields[idx][0]); \ + bool boolField = boolFields[idx] != 0; \ + \ + CheckTestIdValue(testIdx, idField); \ + CheckTestI32Value(testIdx, i32Field); \ + CheckTestI64Value(testIdx, i64Field); \ + CheckTestDec128Value(testIdx, dec128Field); \ + CheckTestDoubleValue(testIdx, doubleField); \ + CheckTestStringValue(testIdx, strField); \ + CheckTestBoolValue(testIdx, boolField); \ + CheckTestDateValue(testIdx, dateFields[idx]); \ + CheckTestI8ArrayValue(testIdx, \ + reinterpret_cast< int8_t* >(binaryFields[idx]), \ + static_cast< SQLLEN >(binaryFieldsLen[idx])); \ + } \ } while (false) BOOST_AUTO_TEST_CASE(TestCursorBindingColumnWise) { - enum { ROWS_COUNT = 15 }; + enum { ROWS_COUNT = 16 }; 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"); + enum { BUFFER_SIZE = 64 }; - // Preloading data. - - InsertTestBatch(0, ROWS_COUNT, ROWS_COUNT); + std::string connectionStr; + CreateDsnConnectionStringForLocalServer(connectionStr); + Connect(connectionStr); // Setting attributes. @@ -157,55 +125,55 @@ BOOST_AUTO_TEST_CASE(TestCursorBindingColumnWise) { // Binding collumns. - SQLSCHAR i8Fields[ROW_ARRAY_SIZE] = {0}; - SQLLEN i8FieldsInd[ROW_ARRAY_SIZE]; - - SQLSMALLINT i16Fields[ROW_ARRAY_SIZE] = {0}; - SQLLEN i16FieldsInd[ROW_ARRAY_SIZE]; + SQLWCHAR idFields[ROW_ARRAY_SIZE][BUFFER_SIZE]; + SQLLEN idFieldsLen[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]; + SQLBIGINT i64Fields[ROW_ARRAY_SIZE] = {0}; + SQLLEN i64FieldsInd[ROW_ARRAY_SIZE]; - SQLREAL floatFields[ROW_ARRAY_SIZE]; - SQLLEN floatFieldsInd[ROW_ARRAY_SIZE]; + SQLWCHAR dec128Fields[ROW_ARRAY_SIZE][BUFFER_SIZE]; + SQLLEN dec128FieldsLen[ROW_ARRAY_SIZE]; SQLDOUBLE doubleFields[ROW_ARRAY_SIZE]; SQLLEN doubleFieldsInd[ROW_ARRAY_SIZE]; - SQLCHAR boolFields[ROW_ARRAY_SIZE]; + SQLWCHAR strFields[ROW_ARRAY_SIZE][BUFFER_SIZE]; + SQLLEN strFieldsLen[ROW_ARRAY_SIZE]; + + bool 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]; + SQLWCHAR nullFields[ROW_ARRAY_SIZE][BUFFER_SIZE]; + SQLLEN nullFieldsLen[ROW_ARRAY_SIZE]; - SQL_TIMESTAMP_STRUCT timestampFields[ROW_ARRAY_SIZE]; - SQLLEN timestampFieldsInd[ROW_ARRAY_SIZE]; + SQLSCHAR binaryFields[ROW_ARRAY_SIZE][BUFFER_SIZE]; + SQLLEN binaryFieldsLen[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); + ret = SQLBindCol(stmt, 1, SQL_C_WCHAR, idFields, + BUFFER_SIZE * sizeof(SQLWCHAR), idFieldsLen); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - ret = SQLBindCol(stmt, 2, SQL_C_SSHORT, i16Fields, 0, i16FieldsInd); + ret = SQLBindCol(stmt, 2, SQL_C_LONG, i32Fields, 0, i32FieldsInd); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - ret = SQLBindCol(stmt, 3, SQL_C_LONG, i32Fields, 0, i32FieldsInd); + ret = SQLBindCol(stmt, 3, SQL_C_SBIGINT, i64Fields, 0, i64FieldsInd); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - ret = SQLBindCol(stmt, 4, SQL_C_CHAR, strFields, BUFFER_SIZE, strFieldsLen); + ret = SQLBindCol(stmt, 4, SQL_C_WCHAR, dec128Fields, + BUFFER_SIZE * sizeof(SQLWCHAR), dec128FieldsLen); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - ret = SQLBindCol(stmt, 5, SQL_C_FLOAT, floatFields, 0, floatFieldsInd); + ret = SQLBindCol(stmt, 5, SQL_C_DOUBLE, doubleFields, 0, doubleFieldsInd); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - ret = SQLBindCol(stmt, 6, SQL_C_DOUBLE, doubleFields, 0, doubleFieldsInd); + ret = SQLBindCol(stmt, 6, SQL_C_WCHAR, strFields, + BUFFER_SIZE * sizeof(SQLWCHAR), strFieldsLen); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); ret = SQLBindCol(stmt, 7, SQL_C_BIT, boolFields, 0, boolFieldsInd); @@ -214,26 +182,23 @@ BOOST_AUTO_TEST_CASE(TestCursorBindingColumnWise) { ret = SQLBindCol(stmt, 8, SQL_C_TYPE_DATE, dateFields, 0, dateFieldsInd); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - ret = SQLBindCol(stmt, 9, SQL_C_TYPE_TIME, timeFields, 0, timeFieldsInd); - ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - - ret = SQLBindCol(stmt, 10, SQL_C_TYPE_TIMESTAMP, timestampFields, 0, - timestampFieldsInd); + ret = SQLBindCol(stmt, 9, SQL_C_TYPE_TIME, nullFields, 0, nullFieldsLen); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - ret = SQLBindCol(stmt, 11, SQL_C_BINARY, i8ArrayFields, BUFFER_SIZE, - i8ArrayFieldsLen); + ret = SQLBindCol(stmt, 10, SQL_C_BINARY, binaryFields, BUFFER_SIZE, + binaryFieldsLen); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); - SQLCHAR sql[] = + std::vector< SQLWCHAR > sql = utility::ToWCHARVector( "SELECT " - "i8Field, i16Field, i32Field, strField, floatField, doubleField, " - "boolField, dateField, timeField, timestampField, i8ArrayField " - "FROM TestType " - "ORDER BY _key"; + " queries_test_006__id, fieldInt, fieldLong, fieldDecimal128, " + " fieldDouble, fieldString, fieldBoolean, fieldDate, fieldNull, " + " fieldBinary " + " FROM queries_test_006 " + " ORDER BY queries_test_006__id"); // Execute a statement to retrieve rows from the Orders table. - ret = SQLExecDirect(stmt, sql, SQL_NTS); + ret = SQLExecDirect(stmt, sql.data(), SQL_NTS); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); ret = SQLFetchScroll(stmt, SQL_FETCH_NEXT, 0); @@ -245,7 +210,7 @@ BOOST_AUTO_TEST_CASE(TestCursorBindingColumnWise) { CHECK_TEST_VALUES(i, static_cast< int >(i)); } - ret = SQLFetch(stmt); + ret = SQLFetchScroll(stmt, SQL_FETCH_NEXT, 0); ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); BOOST_CHECK_EQUAL(NumRowsFetched, ROWS_COUNT - ROW_ARRAY_SIZE); @@ -269,9 +234,9 @@ BOOST_AUTO_TEST_CASE(TestCursorBindingColumnWise) { } BOOST_AUTO_TEST_CASE(TestCursorBindingRowWise) { - Connect( - "DRIVER={Apache " - "Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache;PAGE_SIZE=8"); + std::string connectionStr; + CreateDsnConnectionStringForLocalServer(connectionStr); + Connect(connectionStr); SQLRETURN ret = SQLSetStmtAttr(stmt, SQL_ATTR_ROW_BIND_TYPE, reinterpret_cast< SQLPOINTER* >(42), 0); diff --git a/src/odbc-test/src/odbc_test_suite.cpp b/src/odbc-test/src/odbc_test_suite.cpp index 6faf5e183..293296233 100644 --- a/src/odbc-test/src/odbc_test_suite.cpp +++ b/src/odbc-test/src/odbc_test_suite.cpp @@ -285,31 +285,49 @@ OdbcTestSuite::~OdbcTestSuite() { CleanUp(); } -int8_t OdbcTestSuite::GetTestI8Field(int64_t idx) { - return static_cast< int8_t >(idx * 8); +int64_t OdbcTestSuite::GetTestI64Field(int idx) { + return static_cast< int16_t >(idx * 64); } -void OdbcTestSuite::CheckTestI8Value(int idx, int8_t value) { +void OdbcTestSuite::CheckTestI64Value(int idx, int64_t value) { BOOST_TEST_INFO("Test index: " << idx); - BOOST_CHECK_EQUAL(value, GetTestI8Field(idx)); + BOOST_CHECK_EQUAL(value, GetTestI64Field(idx)); } -int16_t OdbcTestSuite::GetTestI16Field(int64_t idx) { - return static_cast< int16_t >(idx * 16); +int32_t OdbcTestSuite::GetTestI32Field(int idx) { + return static_cast< int32_t >(idx * 32); } -void OdbcTestSuite::CheckTestI16Value(int idx, int16_t value) { +void OdbcTestSuite::CheckTestI32Value(int idx, int32_t value) { BOOST_TEST_INFO("Test index: " << idx); - BOOST_CHECK_EQUAL(value, GetTestI16Field(idx)); + BOOST_CHECK_EQUAL(value, GetTestI32Field(idx)); } -int32_t OdbcTestSuite::GetTestI32Field(int64_t idx) { - return static_cast< int32_t >(idx * 32); +std::string OdbcTestSuite::GetIdString(int idx) { + std::stringstream builder; + + // Note: prefix used in queries_test_006.json input file. + builder << "62196dcc4d9189219147513" << std::hex << idx; + + return builder.str(); } -void OdbcTestSuite::CheckTestI32Value(int idx, int32_t value) { +void OdbcTestSuite::CheckTestIdValue(int idx, const std::string& value) { BOOST_TEST_INFO("Test index: " << idx); - BOOST_CHECK_EQUAL(value, GetTestI32Field(idx)); + BOOST_CHECK_EQUAL(value, GetIdString(idx)); +} + +std::string OdbcTestSuite::GetTestDec128String(int64_t idx) { + std::stringstream builder; + + builder << idx; + + return builder.str(); +} + +void OdbcTestSuite::CheckTestDec128Value(int idx, const std::string& value) { + BOOST_TEST_INFO("Test index: " << idx); + BOOST_CHECK_EQUAL(value, GetTestDec128String(idx)); } std::string OdbcTestSuite::GetTestString(int64_t idx) { @@ -422,19 +440,19 @@ void OdbcTestSuite::CheckTestTimestampValue(int idx, 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); + val[j] = static_cast< int8_t >(j); } void OdbcTestSuite::CheckTestI8ArrayValue(int idx, const int8_t* val, size_t valLen) { BOOST_TEST_CONTEXT("Test index: " << idx) { - odbc::common::FixedSizeArray< int8_t > expected( - static_cast< int32_t >(valLen)); - GetTestI8ArrayField(idx, expected.GetData(), expected.GetSize()); + std::vector< int8_t > expected; + expected.resize(valLen); + GetTestI8ArrayField(idx, expected.data(), expected.size()); for (size_t j = 0; j < valLen; ++j) { BOOST_TEST_INFO("Byte index: " << j); - BOOST_CHECK_EQUAL(val[j], expected[(int32_t)j]); + BOOST_CHECK_EQUAL(val[j], expected[j]); } } } @@ -450,13 +468,9 @@ void OdbcTestSuite::CheckSQLDiagnosticError( SQLRETURN ret = SQLGetDiagRec(handleType, handle, 1, state, &nativeError, message, ODBC_BUFFER_SIZE, &messageLen); - std::vector< SQLWCHAR > expectSqlStateWchar = - MakeSqlBuffer(expectedSqlStateStr); - const std::string expectedSqlState = - reinterpret_cast< char* >(&expectSqlStateWchar[0]); - const std::string sqlState = reinterpret_cast< char* >(state); + const std::string sqlState = utility::SqlWcharToString(state); BOOST_REQUIRE_EQUAL(ret, SQL_SUCCESS); - BOOST_REQUIRE_EQUAL(sqlState, expectedSqlState); + BOOST_REQUIRE_EQUAL(sqlState, expectedSqlStateStr); BOOST_REQUIRE(messageLen > 0); } @@ -550,365 +564,6 @@ void OdbcTestSuite::InsertTestStrings(int recordsNum, bool merge) { BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); } -int OdbcTestSuite::InsertTestBatch(int from, int to, int expectedToAffect, - bool merge) { - using common::FixedSizeArray; - - std::vector< SQLWCHAR > insertReq = MakeSqlBuffer( - "INSERT " - "INTO TestType(_key, i8Field, i16Field, i32Field, strField, floatField, " - "doubleField, boolField, dateField, " - "timeField, timestampField, i8ArrayField) VALUES(?, ?, ?, ?, ?, ?, ?, " - "?, ?, ?, ?, ?)"); - - std::vector< SQLWCHAR > mergeReq = MakeSqlBuffer( - "MERGE " - "INTO TestType(_key, i8Field, i16Field, i32Field, strField, floatField, " - "doubleField, boolField, dateField, " - "timeField, timestampField, i8ArrayField) VALUES(?, ?, ?, ?, ?, ?, ?, " - "?, ?, ?, ?, ?)"); - - SQLRETURN ret; - - int32_t recordsNum = to - from; - - ret = SQLPrepare(stmt, merge ? mergeReq.data() : insertReq.data(), SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - FixedSizeArray< int64_t > keys(recordsNum); - FixedSizeArray< int8_t > i8Fields(recordsNum); - FixedSizeArray< int16_t > i16Fields(recordsNum); - FixedSizeArray< int32_t > i32Fields(recordsNum); - FixedSizeArray< char > strFields(recordsNum * 1024); - FixedSizeArray< float > floatFields(recordsNum); - FixedSizeArray< double > doubleFields(recordsNum); - FixedSizeArray< bool > boolFields(recordsNum); - FixedSizeArray< SQL_DATE_STRUCT > dateFields(recordsNum); - FixedSizeArray< SQL_TIME_STRUCT > timeFields(recordsNum); - FixedSizeArray< SQL_TIMESTAMP_STRUCT > timestampFields(recordsNum); - FixedSizeArray< int8_t > i8ArrayFields(recordsNum * 42); - - FixedSizeArray< SQLLEN > strFieldsLen(recordsNum); - FixedSizeArray< SQLLEN > i8ArrayFieldsLen(recordsNum); - - BOOST_TEST_CHECKPOINT("Filling param data"); - - for (int i = 0; i < recordsNum; ++i) { - int seed = from + i; - - keys[i] = seed; - i8Fields[i] = GetTestI8Field(seed); - i16Fields[i] = GetTestI16Field(seed); - i32Fields[i] = GetTestI32Field(seed); - - std::string val = GetTestString(seed); - CopyStringToBuffer(strFields.GetData() + 1024 * i, val, 1023); - strFieldsLen[i] = val.size(); - - floatFields[i] = GetTestFloatField(seed); - doubleFields[i] = GetTestDoubleField(seed); - boolFields[i] = GetTestBoolField(seed); - - GetTestDateField(seed, dateFields[i]); - GetTestTimeField(seed, timeFields[i]); - GetTestTimestampField(seed, timestampFields[i]); - - GetTestI8ArrayField(seed, &i8ArrayFields[i * 42], 42); - i8ArrayFieldsLen[i] = 42; - } - - SQLULEN setsProcessed = 0; - - BOOST_TEST_CHECKPOINT("Setting processed pointer"); - ret = SQLSetStmtAttr(stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &setsProcessed, - SQL_IS_POINTER); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding keys"); - ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, - 0, keys.GetData(), 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding i8Fields"); - ret = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_STINYINT, SQL_TINYINT, - 0, 0, i8Fields.GetData(), 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding i16Fields"); - ret = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_SMALLINT, - 0, 0, i16Fields.GetData(), 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding i32Fields"); - ret = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, - 0, i32Fields.GetData(), 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding strFields"); - ret = - SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_WVARCHAR, 1024, - 0, strFields.GetData(), 1024, strFieldsLen.GetData()); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding floatFields"); - ret = SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, 0, 0, - floatFields.GetData(), 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding doubleFields"); - ret = SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, 0, - 0, doubleFields.GetData(), 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding boolFields"); - ret = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, 0, 0, - boolFields.GetData(), 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding dateFields"); - ret = SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, - SQL_TYPE_DATE, 0, 0, dateFields.GetData(), 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding timeFields"); - ret = SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_TYPE_TIME, - SQL_TYPE_TIME, 0, 0, timeFields.GetData(), 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding timestampFields"); - ret = SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, - SQL_TYPE_TIMESTAMP, 0, 0, timestampFields.GetData(), 0, - 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Binding i8ArrayFields"); - ret = SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, - 42, 0, i8ArrayFields.GetData(), 42, - i8ArrayFieldsLen.GetData()); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Setting paramset size"); - ret = SQLSetStmtAttr( - stmt, SQL_ATTR_PARAMSET_SIZE, - reinterpret_cast< SQLPOINTER >(static_cast< ptrdiff_t >(recordsNum)), 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Executing query"); - ret = SQLExecute(stmt); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - SQLLEN totallyAffected = 0; - - do { - SQLLEN affected = 0; - ret = SQLRowCount(stmt, &affected); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - totallyAffected += affected; - - BOOST_TEST_CHECKPOINT("Getting next result set"); - - ret = SQLMoreResults(stmt); - - if (ret != SQL_SUCCESS && ret != SQL_NO_DATA) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - } while (ret != SQL_NO_DATA); - - BOOST_CHECK_EQUAL(totallyAffected, expectedToAffect); - - BOOST_TEST_CHECKPOINT("Resetting parameters."); - ret = SQLFreeStmt(stmt, SQL_RESET_PARAMS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_TEST_CHECKPOINT("Setting paramset size"); - ret = SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, - reinterpret_cast< SQLPOINTER >(1), 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - return static_cast< int >(setsProcessed); -} - -void OdbcTestSuite::InsertBatchSelect(int recordsNum) { - Connect("DRIVER={Amazon DocumentDB};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - // Inserting values. - int inserted = InsertTestBatch(0, recordsNum, recordsNum); - - BOOST_REQUIRE_EQUAL(inserted, recordsNum); - - int64_t key = 0; - char strField[1024] = {0}; - SQLLEN strFieldLen = 0; - - // Binding columns. - SQLRETURN ret = SQLBindCol(stmt, 1, SQL_C_SLONG, &key, 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - // Binding columns. - ret = SQLBindCol(stmt, 2, SQL_C_CHAR, &strField, sizeof(strField), - &strFieldLen); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - // Just selecting everything to make sure everything is OK - std::vector< SQLWCHAR > selectReq = - MakeSqlBuffer("SELECT _key, strField FROM TestType ORDER BY _key"); - - ret = SQLExecDirect(stmt, selectReq.data(), SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - int selectedRecordsNum = 0; - - ret = SQL_SUCCESS; - - while (ret == SQL_SUCCESS) { - ret = SQLFetch(stmt); - - if (ret == SQL_NO_DATA) - break; - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - std::string expectedStr = GetTestString(selectedRecordsNum); - int64_t expectedKey = selectedRecordsNum; - - BOOST_CHECK_EQUAL(key, expectedKey); - - BOOST_CHECK_EQUAL(std::string(strField, strFieldLen), expectedStr); - - ++selectedRecordsNum; - } - - BOOST_CHECK_EQUAL(recordsNum, selectedRecordsNum); -} - -void OdbcTestSuite::InsertNonFullBatchSelect(int recordsNum, int splitAt) { - Connect("DRIVER={Amazon DocumentDB};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - std::vector< SQLUSMALLINT > statuses(recordsNum, 42); - - // Binding statuses array. - SQLRETURN ret = - SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_STATUS_PTR, &statuses[0], 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - // Inserting values. - int setsProcessed = - InsertTestBatch(splitAt, recordsNum, recordsNum - splitAt); - - BOOST_REQUIRE_EQUAL(setsProcessed, recordsNum - splitAt); - - for (int i = 0; i < recordsNum - splitAt; ++i) - BOOST_REQUIRE_EQUAL(statuses[i], SQL_PARAM_SUCCESS); - - setsProcessed = InsertTestBatch(0, recordsNum, splitAt); - - BOOST_REQUIRE_EQUAL(setsProcessed, recordsNum); - - for (int i = 0; i < splitAt; ++i) - BOOST_REQUIRE_EQUAL(statuses[i], SQL_PARAM_SUCCESS); - - for (int i = splitAt; i < recordsNum; ++i) - BOOST_REQUIRE_EQUAL(statuses[i], SQL_PARAM_ERROR); - - int64_t key = 0; - char strField[1024] = {0}; - SQLLEN strFieldLen = 0; - - // Binding columns. - ret = SQLBindCol(stmt, 1, SQL_C_SLONG, &key, 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - // Binding columns. - ret = SQLBindCol(stmt, 2, SQL_C_CHAR, &strField, sizeof(strField), - &strFieldLen); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - // Just selecting everything to make sure everything is OK - std::vector< SQLWCHAR > selectReq = - MakeSqlBuffer("SELECT _key, strField FROM TestType ORDER BY _key"); - - ret = SQLExecDirect(stmt, selectReq.data(), SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - int selectedRecordsNum = 0; - - ret = SQL_SUCCESS; - - while (ret == SQL_SUCCESS) { - ret = SQLFetch(stmt); - - if (ret == SQL_NO_DATA) - break; - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - std::string expectedStr = GetTestString(selectedRecordsNum); - int64_t expectedKey = selectedRecordsNum; - - BOOST_CHECK_EQUAL(key, expectedKey); - - BOOST_CHECK_EQUAL(std::string(strField, strFieldLen), expectedStr); - - ++selectedRecordsNum; - } - - BOOST_CHECK_EQUAL(recordsNum, selectedRecordsNum); -} - void OdbcTestSuite::CreateDsnConnectionStringForRemoteServer( std::string& connectionString, bool sshTunnel, const std::string& username, const std::string& miscOptions, const std::string databasename) const { diff --git a/src/odbc/include/documentdb/odbc/common_types.h b/src/odbc/include/documentdb/odbc/common_types.h index 830cc3a78..e77e7df5c 100644 --- a/src/odbc/include/documentdb/odbc/common_types.h +++ b/src/odbc/include/documentdb/odbc/common_types.h @@ -175,6 +175,9 @@ struct SqlState { /** Function sequence error. */ SHY010_SEQUENCE_ERROR, + /** Invalid attribute value. */ + SHY024_INVALID_ATTRIBUTE_VALUE, + /** * Invalid string or buffer length */ diff --git a/src/odbc/include/documentdb/odbc/documentdb_error.h b/src/odbc/include/documentdb/odbc/documentdb_error.h index 3037c8ac0..549d56a50 100644 --- a/src/odbc/include/documentdb/odbc/documentdb_error.h +++ b/src/odbc/include/documentdb/odbc/documentdb_error.h @@ -246,6 +246,9 @@ class DOCUMENTDB_IMPORT_EXPORT DocumentDbError : public std::exception { /** Generic transaction error. */ static const int DOCUMENTDB_ERR_TX = 2028; + /** SQL Exception (from JNI call). */ + static const int DOCUMENTDB_ERR_SQL_EXCEPTION = 2029; + /** Unknown error. */ static const int DOCUMENTDB_ERR_UNKNOWN = -1; diff --git a/src/odbc/src/diagnostic/diagnostic_record.cpp b/src/odbc/src/diagnostic/diagnostic_record.cpp index 9457836ec..ded98ecfd 100644 --- a/src/odbc/src/diagnostic/diagnostic_record.cpp +++ b/src/odbc/src/diagnostic/diagnostic_record.cpp @@ -126,6 +126,9 @@ const std::string STATE_HY009 = "HY009"; /** SQL state HY010 constant. */ const std::string STATE_HY010 = "HY010"; +/** SQL state HY024 constant. */ +const std::string STATE_HY024 = "HY024"; + /** SQL state HY090 constant. */ const std::string STATE_HY090 = "HY090"; @@ -361,6 +364,9 @@ const std::string& DiagnosticRecord::GetSqlState() const { case SqlState::SHY010_SEQUENCE_ERROR: return STATE_HY010; + case SqlState::SHY024_INVALID_ATTRIBUTE_VALUE: + return STATE_HY024; + case SqlState::SHY090_INVALID_STRING_OR_BUFFER_LENGTH: return STATE_HY090; diff --git a/src/odbc/src/documentdb_error.cpp b/src/odbc/src/documentdb_error.cpp index eda1a93bd..aaae3b15d 100644 --- a/src/odbc/src/documentdb_error.cpp +++ b/src/odbc/src/documentdb_error.cpp @@ -128,14 +128,21 @@ void DocumentDbError::SetError(const JniErrorCode jniCode, const char* jniCls, else if (jniCls0.compare("javax.cache.processor.EntryProcessorException") == 0) err = DocumentDbError(DOCUMENTDB_ERR_ENTRY_PROCESSOR, jniMsg); - else { + else if (jniCls0.compare("java.sql.SQLException") == 0) { std::stringstream stream; + stream << "SQL exception occurred [cls=" << jniCls0; + if (jniMsg) { + stream << ", msg=" << jniMsg; + } + stream << "]"; + err = + DocumentDbError(DOCUMENTDB_ERR_SQL_EXCEPTION, stream.str().c_str()); + } else { + std::stringstream stream; stream << "Java exception occurred [cls=" << jniCls0; - if (jniMsg) stream << ", msg=" << jniMsg; - stream << "]"; err = DocumentDbError(DOCUMENTDB_ERR_UNKNOWN, stream.str().c_str()); diff --git a/src/odbc/src/query/data_query.cpp b/src/odbc/src/query/data_query.cpp index 1169ef5f3..1d53a04a2 100644 --- a/src/odbc/src/query/data_query.cpp +++ b/src/odbc/src/query/data_query.cpp @@ -267,6 +267,17 @@ SqlResult::Type DataQuery::MakeRequestFetch() { SqlResult::Type result = GetMqlQueryContext(mqlQueryContext, error); if (result != SqlResult::AI_SUCCESS) { + switch (error.GetCode()) { + case DocumentDbError::DOCUMENTDB_ERR_SQL_EXCEPTION: + // Assume that query cannot be parsed or is not implemented. + diag.AddStatusRecord( + SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED, + error.GetText()); + break; + default: + diag.AddStatusRecord(error.GetText()); + break; + } diag.AddStatusRecord(error.GetText()); LOG_ERROR_MSG( diff --git a/src/odbc/src/statement.cpp b/src/odbc/src/statement.cpp index 3cbecaa19..57e2a9854 100644 --- a/src/odbc/src/statement.cpp +++ b/src/odbc/src/statement.cpp @@ -227,24 +227,16 @@ SqlResult::Type Statement::InternalSetAttribute(int attr, void* value, switch (attr) { case SQL_ATTR_ROW_ARRAY_SIZE: { SqlUlen val = reinterpret_cast< SqlUlen >(value); - LOG_DEBUG_MSG("SQL_ATTR_ROW_ARRAY_SIZE: " << val); - if (val != 1) { - AddStatusRecord( - SqlState::SIM001_FUNCTION_NOT_SUPPORTED, - "Array size value cannot be set to a value other than 1"); - + if (val < 1) { + AddStatusRecord(SqlState::SHY024_INVALID_ATTRIBUTE_VALUE, + "Array size value must be a positive integer value."); return SqlResult::AI_ERROR; - } else if (rowArraySize != 1) { - // val is 1 - rowArraySize = 1; - LOG_DEBUG_MSG( - "else if (rowArraySize != 1) branch is executed." - "This branch should not be executed as we currently do not support " - "rowArraySize to have values other than 1."); } + // Update row array size setting. + rowArraySize = val; LOG_DEBUG_MSG("rowArraySize: " << rowArraySize); break; diff --git a/src/tests/README.md b/src/tests/README.md index 748db08f0..5b394d749 100644 --- a/src/tests/README.md +++ b/src/tests/README.md @@ -5,8 +5,8 @@ Performance test framework for testing ODBC driver 1. Setup test database system under a dsn 2. Specify test plan input file 3. Specify output file -4. Run executable and pass in command line arguments: dsn-name input-file output-file -e.g. `performance.exe "documentdb-perf-test" "Performance_Test_Plan.csv" "Performance_Test_Results.csv"` +4. Run executable and pass in command line arguments: input-file output-file dsn-name [user password] +e.g. `performance.exe "Performance_Test_Plan.csv" "Performance_Test_Results.csv" "documentdb-perf-test" "documentdb" "secret"` e.g. output console: ``` %%__PARSE__SYNC__START__%% diff --git a/src/tests/performance/include/performance_test_runner.h b/src/tests/performance/include/performance_test_runner.h index be72e560d..d5f2c688e 100644 --- a/src/tests/performance/include/performance_test_runner.h +++ b/src/tests/performance/include/performance_test_runner.h @@ -58,9 +58,14 @@ const std::string kTestQuery = enum testCaseStatus { success, error, skip }; +// To test array size greater than one, change the value below. +// [Performance test can be run with a ROW ARRAY SIZE greater +// than 1.](https://bitquill.atlassian.net/browse/AD-914) +enum { ARRAY_SIZE = 1 }; + typedef struct Col { - SQLLEN data_len; - SQLWCHAR data_dat[BIND_SIZE]; + SQLLEN data_len[ARRAY_SIZE]; + SQLWCHAR data_dat[ARRAY_SIZE][BIND_SIZE]; } Col; struct CsvHeaders { @@ -120,6 +125,8 @@ class PerformanceTestRunner { std::string _input_file = kInputFile; // input test plan csv file std::string _output_file = kOutputFile; // output test results csv file std::string _csv_data = ""; + std::string _user; + std::string _password; CsvHeaders _headers; // col index of headers // _output_mode = 0 - output time for exec/bind/fetch combined @@ -206,7 +213,9 @@ class PerformanceTestRunner { // connection string to test database PerformanceTestRunner(const std::string test_plan_csv, const std::string output_file_csv, - const std::string dsn, const int output_mode = 0); + const std::string dsn, const int output_mode = 0, + const std::string user = "", + const std::string password = ""); // Destructor virtual ~PerformanceTestRunner(); diff --git a/src/tests/performance/src/performance.cpp b/src/tests/performance/src/performance.cpp index 33ceab3d4..1547a6189 100644 --- a/src/tests/performance/src/performance.cpp +++ b/src/tests/performance/src/performance.cpp @@ -52,7 +52,7 @@ int main(int argc, char* argv[]) { #endif // Initialize variables - std::string data_source_name, test_plan_file, test_results_file; + std::string data_source_name, test_plan_file, test_results_file, user, password; int output_mode = 0; // output time for exec/bind/fetch combined // Check command line arguments @@ -82,6 +82,13 @@ int main(int argc, char* argv[]) { test_plan_file = argv[1]; test_results_file = argv[2]; data_source_name = argv[3]; + } else if (argc == 6) { + // input file, output file, dsn, user and password passed in + test_plan_file = argv[1]; + test_results_file = argv[2]; + data_source_name = argv[3]; + user = argv[4]; + password = argv[5]; } else { std::cerr << "ERROR: invalid number of command line arguments\n"; } @@ -91,7 +98,7 @@ int main(int argc, char* argv[]) { try { // Initialize performance test runner performance::PerformanceTestRunner performanceTest( - test_plan_file, test_results_file, data_source_name, output_mode); + test_plan_file, test_results_file, data_source_name, output_mode, user, password); performanceTest.SetupConnection(); performanceTest.ReadPerformanceTestPlan(); diff --git a/src/tests/performance/src/performance_test_runner.cpp b/src/tests/performance/src/performance_test_runner.cpp index b1d8f488b..ec5500eb4 100644 --- a/src/tests/performance/src/performance_test_runner.cpp +++ b/src/tests/performance/src/performance_test_runner.cpp @@ -409,6 +409,42 @@ void performance::PerformanceTestRunner::RecordExecBindFetch( long long time_bind_ms; long long time_fetch_ms; std::vector< Col > cols; + SQLUSMALLINT RowStatus[ARRAY_SIZE] = {0}; + SQLUINTEGER NumRowsFetched = 0; + + ret = SQLSetStmtAttr(*hstmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0); + if (!SQL_SUCCEEDED(ret)) { + LogAnyDiagnostics(SQL_HANDLE_STMT, *hstmt, ret); + test_case.status = error; + test_case.err_msg = "SQLSetStmtAttr SQL_ATTR_ROW_BIND_TYPE failed"; + return; // continue to next test case + } + + ret = SQLSetStmtAttr(*hstmt, SQL_ATTR_ROW_ARRAY_SIZE, + reinterpret_cast< SQLPOINTER* >(ARRAY_SIZE), 0); + if (!SQL_SUCCEEDED(ret)) { + LogAnyDiagnostics(SQL_HANDLE_STMT, *hstmt, ret); + test_case.status = error; + test_case.err_msg = "SQLSetStmtAttr SQL_ATTR_ROW_ARRAY_SIZE failed"; + return; // continue to next test case + } + + ret = SQLSetStmtAttr(*hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatus, 0); + if (!SQL_SUCCEEDED(ret)) { + LogAnyDiagnostics(SQL_HANDLE_STMT, *hstmt, ret); + test_case.status = error; + test_case.err_msg = "SQLSetStmtAttr SQL_ATTR_ROW_STATUS_PTR failed"; + return; // continue to next test case + } + + ret = SQLSetStmtAttr(*hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &NumRowsFetched, 0); + if (!SQL_SUCCEEDED(ret)) { + LogAnyDiagnostics(SQL_HANDLE_STMT, *hstmt, ret); + test_case.status = error; + test_case.err_msg = "SQLSetStmtAttr SQL_ATTR_ROWS_FETCHED_PTR failed"; + return; // continue to next test case + } + // Query std::string temp_str = @@ -455,8 +491,8 @@ void performance::PerformanceTestRunner::RecordExecBindFetch( auto time_bind_start = std::chrono::steady_clock::now(); for (size_t i = 0; i < static_cast< size_t >(total_columns); i++) { ret = SQLBindCol(*hstmt, static_cast< SQLUSMALLINT >(i + 1), SQL_C_WCHAR, - static_cast< SQLPOINTER >(&cols[i].data_dat[0]), - BIND_SIZE * sizeof(SQLWCHAR), &cols[i].data_len); + static_cast< SQLPOINTER >(cols[i].data_dat), + BIND_SIZE * sizeof(SQLWCHAR), cols[i].data_len); if (ret != SQL_SUCCESS) { LogAnyDiagnostics(SQL_HANDLE_STMT, *hstmt, ret); test_case.status = error; @@ -811,7 +847,8 @@ performance::PerformanceTestRunner::PerformanceTestRunner() { performance::PerformanceTestRunner::PerformanceTestRunner( const std::string test_plan_csv, const std::string output_file_csv, - const std::string dsn, const int output_mode) { + const std::string dsn, const int output_mode, const std::string user, + const std::string password) { // check input arguments CheckFileExtension(test_plan_csv, ".csv"); CheckFileExtension(output_file_csv, ".csv"); @@ -821,6 +858,8 @@ performance::PerformanceTestRunner::PerformanceTestRunner( _input_file = test_plan_csv; _output_file = output_file_csv; _dsn = dsn; + _user = user; + _password = password; _output_mode = output_mode; } @@ -889,7 +928,10 @@ void performance::PerformanceTestRunner::SetupConnection() { SQLTCHAR out_conn_string[1024]; SQLSMALLINT out_conn_string_length; SQLRETURN ret; - test_string dsn = to_test_string("DSN=" + _dsn); + test_string driver_conn_str = to_test_string("DSN=" + _dsn); + test_string conn_str = to_test_string(_dsn); + test_string user = to_test_string(_user); + test_string password = to_test_string(_password); std::string error_msg; ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &_env); @@ -910,18 +952,27 @@ void performance::PerformanceTestRunner::SetupConnection() { throw std::runtime_error(error_msg); } - ret = SQLDriverConnect(_conn, NULL, (SQLTCHAR*)dsn.c_str(), SQL_NTS, - out_conn_string, IT_SIZEOF(out_conn_string), - &out_conn_string_length, SQL_DRIVER_COMPLETE); + std::string connApiName; + if (user.empty() || password.empty()) { + connApiName = "SQLDriverConnect"; + ret = SQLDriverConnect(_conn, NULL, (SQLTCHAR*)driver_conn_str.c_str(), SQL_NTS, + out_conn_string, IT_SIZEOF(out_conn_string), + &out_conn_string_length, SQL_DRIVER_COMPLETE); + } else { + connApiName = "SQLConnect"; + ret = SQLConnect(_conn, (SQLTCHAR*)conn_str.c_str(), SQL_NTS, + (SQLTCHAR*)user.c_str(), SQL_NTS, + (SQLTCHAR*)password.c_str(), SQL_NTS); + } if (ret == SQL_INVALID_HANDLE || ret == SQL_ERROR) { error_msg = - "SQLDriverConnect ERROR: failed to connect to DSN = " + _dsn + "."; + connApiName + " ERROR: failed to connect to DSN = " + _dsn + "."; throw std::runtime_error(error_msg); } else if (ret == SQL_NO_DATA) { - error_msg = "SQLDriverConnect ERROR: no data in DSN = " + _dsn + "."; + error_msg = connApiName + " ERROR: no data in DSN = " + _dsn + "."; throw std::runtime_error(error_msg); } else if (ret == SQL_STILL_EXECUTING) { - error_msg = "SQLDriverConnect ERROR: still trying to connect to DSN = " + error_msg = connApiName + " ERROR: still trying to connect to DSN = " + _dsn + "."; throw std::runtime_error(error_msg); }