diff --git a/src/odbc-test/CMakeLists.txt b/src/odbc-test/CMakeLists.txt index 9f5526a4b..23411c5f0 100644 --- a/src/odbc-test/CMakeLists.txt +++ b/src/odbc-test/CMakeLists.txt @@ -92,6 +92,9 @@ set(SOURCES ../odbc/src/diagnostic/diagnosable_adapter.cpp ../odbc/src/diagnostic/diagnostic_record_storage.cpp ../odbc/src/diagnostic/diagnostic_record.cpp + ../odbc/src/documentdb_column.cpp + ../odbc/src/documentdb_cursor.cpp + ../odbc/src/documentdb_row.cpp ../odbc/src/dsn_config.cpp ../odbc/src/environment.cpp ../odbc/src/ignite_error.cpp @@ -113,7 +116,6 @@ set(SOURCES ../odbc/src/query/foreign_keys_query.cpp ../odbc/src/query/primary_keys_query.cpp ../odbc/src/query/special_columns_query.cpp - ../odbc/src/query/streaming_query.cpp ../odbc/src/query/type_info_query.cpp ../odbc/src/query/table_metadata_query.cpp ../odbc/src/sql/sql_lexer.cpp diff --git a/src/odbc-test/include/odbc_test_suite.h b/src/odbc-test/include/odbc_test_suite.h index 7f85cce81..a53d11ebf 100644 --- a/src/odbc-test/include/odbc_test_suite.h +++ b/src/odbc-test/include/odbc_test_suite.h @@ -360,7 +360,8 @@ struct OdbcTestSuite { void CreateDsnConnectionStringForRemoteServer( std::string& connectionString, bool sshTunnel = true, const std::string& username = std::string(), - const std::string& miscOptions = std::string()) const; + const std::string& miscOptions = std::string(), + const std::string databasename = std::string()) const; /** * Creates the standard DSN connection string for use with local instance. diff --git a/src/odbc-test/input/api_robustness_test_001.json b/src/odbc-test/input/api_robustness_test_001.json index df08fa4d2..d9c474f1d 100644 --- a/src/odbc-test/input/api_robustness_test_001.json +++ b/src/odbc-test/input/api_robustness_test_001.json @@ -1,32 +1,34 @@ -{ - "_id":{ - "$oid":"62196dcc4d91892191475139" - }, - "fieldDecimal128":{ - "$numberDecimal":"Infinity" - }, - "fieldDouble":1.7976931348623157E308, - "fieldString":"新年快乐", - "fieldObjectId":{ - "$oid":"62196dcc4d9189219147513a" - }, - "fieldBoolean":true, - "fieldDate":{ - "$date":"2020-01-01T00:00:00Z" - }, - "fieldInt":2147483647, - "fieldLong":9223372036854775807, - "fieldMaxKey":{ - "$maxKey":1 - }, - "fieldMinKey":{ - "$minKey":1 - }, - "fieldNull":null, - "fieldBinary":{ - "$binary":{ - "base64":"AAEC", - "subType":"00" - } - } -} +[ + { + "_id": { + "$oid": "62196dcc4d91892191475139" + }, + "fieldDecimal128": { + "$numberDecimal": "Infinity" + }, + "fieldDouble": 1.7976931348623157E308, + "fieldString": "新年快乐", + "fieldObjectId": { + "$oid": "62196dcc4d9189219147513a" + }, + "fieldBoolean": true, + "fieldDate": { + "$date": "2020-01-01T00:00:00Z" + }, + "fieldInt": 2147483647, + "fieldLong": 9223372036854775807, + "fieldMaxKey": { + "$maxKey": 1 + }, + "fieldMinKey": { + "$minKey": 1 + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + } +] diff --git a/src/odbc-test/input/jni_test_001.json b/src/odbc-test/input/jni_test_001.json index df08fa4d2..d9c474f1d 100644 --- a/src/odbc-test/input/jni_test_001.json +++ b/src/odbc-test/input/jni_test_001.json @@ -1,32 +1,34 @@ -{ - "_id":{ - "$oid":"62196dcc4d91892191475139" - }, - "fieldDecimal128":{ - "$numberDecimal":"Infinity" - }, - "fieldDouble":1.7976931348623157E308, - "fieldString":"新年快乐", - "fieldObjectId":{ - "$oid":"62196dcc4d9189219147513a" - }, - "fieldBoolean":true, - "fieldDate":{ - "$date":"2020-01-01T00:00:00Z" - }, - "fieldInt":2147483647, - "fieldLong":9223372036854775807, - "fieldMaxKey":{ - "$maxKey":1 - }, - "fieldMinKey":{ - "$minKey":1 - }, - "fieldNull":null, - "fieldBinary":{ - "$binary":{ - "base64":"AAEC", - "subType":"00" - } - } -} +[ + { + "_id": { + "$oid": "62196dcc4d91892191475139" + }, + "fieldDecimal128": { + "$numberDecimal": "Infinity" + }, + "fieldDouble": 1.7976931348623157E308, + "fieldString": "新年快乐", + "fieldObjectId": { + "$oid": "62196dcc4d9189219147513a" + }, + "fieldBoolean": true, + "fieldDate": { + "$date": "2020-01-01T00:00:00Z" + }, + "fieldInt": 2147483647, + "fieldLong": 9223372036854775807, + "fieldMaxKey": { + "$maxKey": 1 + }, + "fieldMinKey": { + "$minKey": 1 + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + } +] diff --git a/src/odbc-test/input/meta_queries_test_001.json b/src/odbc-test/input/meta_queries_test_001.json index df08fa4d2..d9c474f1d 100644 --- a/src/odbc-test/input/meta_queries_test_001.json +++ b/src/odbc-test/input/meta_queries_test_001.json @@ -1,32 +1,34 @@ -{ - "_id":{ - "$oid":"62196dcc4d91892191475139" - }, - "fieldDecimal128":{ - "$numberDecimal":"Infinity" - }, - "fieldDouble":1.7976931348623157E308, - "fieldString":"新年快乐", - "fieldObjectId":{ - "$oid":"62196dcc4d9189219147513a" - }, - "fieldBoolean":true, - "fieldDate":{ - "$date":"2020-01-01T00:00:00Z" - }, - "fieldInt":2147483647, - "fieldLong":9223372036854775807, - "fieldMaxKey":{ - "$maxKey":1 - }, - "fieldMinKey":{ - "$minKey":1 - }, - "fieldNull":null, - "fieldBinary":{ - "$binary":{ - "base64":"AAEC", - "subType":"00" - } - } -} +[ + { + "_id": { + "$oid": "62196dcc4d91892191475139" + }, + "fieldDecimal128": { + "$numberDecimal": "Infinity" + }, + "fieldDouble": 1.7976931348623157E308, + "fieldString": "新年快乐", + "fieldObjectId": { + "$oid": "62196dcc4d9189219147513a" + }, + "fieldBoolean": true, + "fieldDate": { + "$date": "2020-01-01T00:00:00Z" + }, + "fieldInt": 2147483647, + "fieldLong": 9223372036854775807, + "fieldMaxKey": { + "$maxKey": 1 + }, + "fieldMinKey": { + "$minKey": 1 + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + } +] diff --git a/src/odbc-test/input/meta_queries_test_002.json b/src/odbc-test/input/meta_queries_test_002.json index feac1b49e..87f3f8d54 100644 --- a/src/odbc-test/input/meta_queries_test_002.json +++ b/src/odbc-test/input/meta_queries_test_002.json @@ -1,32 +1,34 @@ -{ - "_id":{ - "$oid":"8888f88886afa51ac62bed23" - }, - "fieldDecimal128":{ - "$numberDecimal":"Infinity" - }, - "fieldDouble":3.1415926E23, - "fieldString":"こんにちは", - "fieldObjectId":{ - "$oid":"888888caabcdef8888888888" - }, - "fieldBoolean":false, - "fieldDate":{ - "$date":"2022-03-08T00:00:00Z" - }, - "fieldInt":21345621, - "fieldLong":9281883193847263322, - "fieldMaxKey":{ - "$maxKey":1 - }, - "fieldMinKey":{ - "$minKey":1 - }, - "fieldNull":null, - "fieldBinary":{ - "$binary":{ - "base64":"AAEC", - "subType":"00" +[ + { + "_id": { + "$oid": "8888f88886afa51ac62bed23" + }, + "fieldDecimal128": { + "$numberDecimal": "Infinity" + }, + "fieldDouble": 3.1415926E23, + "fieldString": "こんにちは", + "fieldObjectId": { + "$oid": "888888caabcdef8888888888" + }, + "fieldBoolean": false, + "fieldDate": { + "$date": "2022-03-08T00:00:00Z" + }, + "fieldInt": 21345621, + "fieldLong": 9281883193847263322, + "fieldMaxKey": { + "$maxKey": 1 + }, + "fieldMinKey": { + "$minKey": 1 + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" } - } -} + } + } +] diff --git a/src/odbc-test/input/queries_test_001.json b/src/odbc-test/input/queries_test_001.json index df08fa4d2..cdb977aa9 100644 --- a/src/odbc-test/input/queries_test_001.json +++ b/src/odbc-test/input/queries_test_001.json @@ -1,32 +1,34 @@ -{ - "_id":{ - "$oid":"62196dcc4d91892191475139" - }, - "fieldDecimal128":{ - "$numberDecimal":"Infinity" - }, - "fieldDouble":1.7976931348623157E308, - "fieldString":"新年快乐", - "fieldObjectId":{ - "$oid":"62196dcc4d9189219147513a" - }, - "fieldBoolean":true, - "fieldDate":{ - "$date":"2020-01-01T00:00:00Z" - }, - "fieldInt":2147483647, - "fieldLong":9223372036854775807, - "fieldMaxKey":{ - "$maxKey":1 - }, - "fieldMinKey":{ - "$minKey":1 - }, - "fieldNull":null, - "fieldBinary":{ - "$binary":{ - "base64":"AAEC", - "subType":"00" - } - } -} +[ + { + "_id": { + "$oid": "62196dcc4d91892191475139" + }, + "fieldDecimal128": { + "$numberDecimal": "3.4028235e20" + }, + "fieldDouble": 1.7976931348623157E308, + "fieldString": "some Text", + "fieldObjectId": { + "$oid": "62196dcc4d9189219147513a" + }, + "fieldBoolean": true, + "fieldDate": { + "$date": "2020-01-01T00:00:00Z" + }, + "fieldInt": 2147483647, + "fieldLong": 9223372036854775807, + "fieldMaxKey": { + "$maxKey": 1 + }, + "fieldMinKey": { + "$minKey": 1 + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + } +] diff --git a/src/odbc-test/input/queries_test_002.json b/src/odbc-test/input/queries_test_002.json new file mode 100644 index 000000000..278d25ceb --- /dev/null +++ b/src/odbc-test/input/queries_test_002.json @@ -0,0 +1,71 @@ +[ + { + "_id": { + "$oid": "62196dcc4d91892191475139" + }, + "fieldDecimal128": { + "$numberDecimal": "3.4028235e20" + }, + "fieldDouble": 1.7976931348623157E308, + "fieldString": "some Text", + "fieldObjectId": { + "$oid": "62196dcc4d9189219147513a" + }, + "fieldBoolean": true, + "fieldDate": { + "$date": "2020-01-01T00:00:00Z" + }, + "fieldInt": 2147483647, + "fieldLong": 9223372036854775807, + "fieldMaxKey": { + "$maxKey": 1 + }, + "fieldMinKey": { + "$minKey": 1 + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "62196dcc4d9189219147513a" + } + }, + { + "_id": { + "$oid": "62196dcc4d9189219147513b" + }, + "fieldDecimal128": { + "$numberDecimal": "3.4028235e20" + }, + "fieldDouble": 1.7976931348623157E308, + "fieldString": "some Text", + "fieldObjectId": { + "$oid": "62196dcc4d9189219147513a" + }, + "fieldBoolean": true, + "fieldDate": { + "$date": "2020-01-01T00:00:00Z" + }, + "fieldInt": 2147483647, + "fieldLong": 9223372036854775807, + "fieldMaxKey": { + "$maxKey": 1 + }, + "fieldMinKey": { + "$minKey": 1 + }, + "fieldNull": null, + "fieldBinary": { + "$binary": { + "base64": "AAEC", + "subType": "00" + } + } + } +] diff --git a/src/odbc-test/input/queries_test_003.json b/src/odbc-test/input/queries_test_003.json new file mode 100644 index 000000000..4f15966d8 --- /dev/null +++ b/src/odbc-test/input/queries_test_003.json @@ -0,0 +1,23 @@ +[ + { + "_id": { + "$oid": "62196dcc4d91892191475139" + }, + "fieldArrayOfString": [ + "value1", + "value2", + "value3" + ], + "fieldArrayOfInt": [ + 1, + 2, + 3 + ], + "fieldDocument": { + "field1": "field1 Value", + "subDoc": { + "field2": "field2 Value" + } + } + } +] diff --git a/src/odbc-test/scripts/import_test_data.ps1 b/src/odbc-test/scripts/import_test_data.ps1 index c96dfc99f..26cf1de54 100644 --- a/src/odbc-test/scripts/import_test_data.ps1 +++ b/src/odbc-test/scripts/import_test_data.ps1 @@ -21,11 +21,11 @@ Foreach-Object { # Log what input we're about to import Write-Output ` "mongoimport -u=$($env:DOC_DB_USER_NAME) -p=... --authenticationDatabase=admin ` - -d=$($DATABASE_NAME) -c=$($COLLECTION_NAME) ` + -d=$($DATABASE_NAME) -c=$($COLLECTION_NAME) --jsonArray ` --file=""$($TEST_INPUT_FOLDER)\$($TEST_FILE_NAME)""" # Import the test input mongoimport -u="$($env:DOC_DB_USER_NAME)" -p="$($env:DOC_DB_PASSWORD)" --authenticationDatabase=admin ` - -d="$($DATABASE_NAME)" -c="$($COLLECTION_NAME)" ` + -d="$($DATABASE_NAME)" -c="$($COLLECTION_NAME)" --jsonArray ` --file="""$($TEST_INPUT_FOLDER)\$($TEST_FILE_NAME)""" if (!$?) { # If error detected stop the rest of the processing and exit diff --git a/src/odbc-test/scripts/import_test_data.sh b/src/odbc-test/scripts/import_test_data.sh index 2f839d2b9..4ca631e27 100755 --- a/src/odbc-test/scripts/import_test_data.sh +++ b/src/odbc-test/scripts/import_test_data.sh @@ -22,7 +22,7 @@ do TEST_FILE_NAME="$(basename -- ${FILENAME})" mongoimport -u="${DOC_DB_USER_NAME}" -p="${DOC_DB_PASSWORD}" --authenticationDatabase=admin \ - -d="${DATABASE_NAME}" -c="${COLLECTION_NAME}" \ + -d="${DATABASE_NAME}" -c="${COLLECTION_NAME}" --jsonArray \ --file="""${TEST_INPUT_FOLDER}/${TEST_FILE_NAME}""" if [ $? -ne 0 ] then diff --git a/src/odbc-test/src/java_test.cpp b/src/odbc-test/src/java_test.cpp index 9e7dec735..3acb6de71 100644 --- a/src/odbc-test/src/java_test.cpp +++ b/src/odbc-test/src/java_test.cpp @@ -486,7 +486,6 @@ BOOST_AUTO_TEST_CASE(TestDatabaseMetaDataGetTables) { } } -// todo note: might need to add some values to test in the future? BOOST_AUTO_TEST_CASE(TestDatabaseMetaDataGetColumns) { PrepareContext(); BOOST_REQUIRE(_ctx.Get() != nullptr); diff --git a/src/odbc-test/src/odbc_test_suite.cpp b/src/odbc-test/src/odbc_test_suite.cpp index a4d5554d1..7b85639d4 100644 --- a/src/odbc-test/src/odbc_test_suite.cpp +++ b/src/odbc-test/src/odbc_test_suite.cpp @@ -796,7 +796,8 @@ void OdbcTestSuite::InsertNonFullBatchSelect(int recordsNum, int splitAt) { void OdbcTestSuite::CreateDsnConnectionStringForRemoteServer( std::string& connectionString, bool sshTunnel, const std::string& username, - const std::string& miscOptions) const { + const std::string& miscOptions, + const std::string databasename) const { std::string user = common::GetEnv("DOC_DB_USER_NAME", "documentdb"); std::string password = common::GetEnv("DOC_DB_PASSWORD", ""); std::string host = @@ -809,6 +810,10 @@ void OdbcTestSuite::CreateDsnConnectionStringForRemoteServer( if (!username.empty()) { user = username; } + std::string database = "test"; + if (!databasename.empty()) { + database = databasename; + } std::string sshUser; std::string sshTunnelHost; @@ -822,7 +827,7 @@ void OdbcTestSuite::CreateDsnConnectionStringForRemoteServer( connectionString = "DRIVER={Amazon DocumentDB};" "HOSTNAME=" + host + ":" + port + ";" - "DATABASE=test;" + "DATABASE=" + database + ";" "USER=" + user + ";" "PASSWORD=" + password + ";" "TLS_ALLOW_INVALID_HOSTNAMES=true;"; diff --git a/src/odbc-test/src/queries_test.cpp b/src/odbc-test/src/queries_test.cpp index 0775d8e25..b79bad400 100644 --- a/src/odbc-test/src/queries_test.cpp +++ b/src/odbc-test/src/queries_test.cpp @@ -216,19 +216,424 @@ struct QueriesTestSuiteFixture : odbc::OdbcTestSuite { BOOST_FIXTURE_TEST_SUITE(QueriesTestSuite, QueriesTestSuiteFixture) -BOOST_AUTO_TEST_CASE(TestEmptyResult) { +// Re-enable to test large data set. +BOOST_AUTO_TEST_CASE(TestMoviesCast, *disabled()) { + std::string dsnConnectionString; + CreateDsnConnectionStringForRemoteServer(dsnConnectionString, true, "", "", + "movies"); + Connect(dsnConnectionString); + SQLRETURN ret; + char request[] = "SELECT * FROM \"movies_cast\""; + + ret = SQLExecDirect(stmt, reinterpret_cast< SQLCHAR* >(request), SQL_NTS); + if (!SQL_SUCCEEDED(ret)) { + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + } + + const int32_t buf_size = 1024; + SQLCHAR id[buf_size]{}; + SQLLEN id_len = 0; + SQLBIGINT index = 0; + SQLLEN index_len = 0; + SQLCHAR value[buf_size]{}; + SQLLEN value_len = 0; + + // Fetch 1st row + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + while (SQL_SUCCEEDED(ret)) { + + ret = SQLGetData(stmt, 1, SQL_C_CHAR, id, buf_size, &id_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 2, SQL_C_SBIGINT, &index, sizeof(index), + &index_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 3, SQL_C_CHAR, value, buf_size, + &value_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + BOOST_CHECK_NE(SQL_NULL_DATA, id_len); + BOOST_CHECK_NE(SQL_NULL_DATA, index_len); + + ret = SQLFetch(stmt); + } + + // Fetch 2nd row - not exist + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_NO_DATA, ret); +} + +BOOST_AUTO_TEST_CASE(TestSingleResultUsingGetData) { + std::string dsnConnectionString; + CreateDsnConnectionStringForLocalServer(dsnConnectionString); + Connect(dsnConnectionString); + SQLRETURN ret; + char request[] = "SELECT * FROM \"queries_test_001\""; + + ret = SQLExecDirect(stmt, reinterpret_cast< SQLCHAR* >(request), SQL_NTS); + if (!SQL_SUCCEEDED(ret)) { + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + } + + const int32_t buf_size = 1024; + SQLCHAR id[buf_size]{}; + SQLLEN id_len = 0; + SQLCHAR fieldDecimal128[buf_size]{}; + SQLLEN fieldDecimal128_len = 0; + double fieldDouble = 0; + SQLLEN fieldDouble_len = 0; + SQLCHAR fieldString[buf_size]{}; + SQLLEN fieldString_len = 0; + SQLCHAR fieldObjectId[buf_size]{}; + SQLLEN fieldObjectId_len = 0; + bool fieldBoolean = false; + SQLLEN fieldBoolean_len = 0; + DATE_STRUCT fieldDate{}; + SQLLEN fieldDate_len = 0; + SQLINTEGER fieldInt; + SQLLEN fieldInt_len = 0; + SQLBIGINT fieldLong; + SQLLEN fieldLong_len = 0; + SQLCHAR fieldMaxKey[buf_size]; + SQLLEN fieldMaxKey_len = 0; + SQLCHAR fieldMinKey[buf_size]; + SQLLEN fieldMinKey_len = 0; + SQLCHAR fieldNull[buf_size]; + SQLLEN fieldNull_len = 0; + SQLCHAR fieldBinary[buf_size]; + SQLLEN fieldBinary_len = 0; + + // Fetch 1st row + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + ret = SQLGetData(stmt, 1, SQL_C_CHAR, id, buf_size, &id_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 2, SQL_C_CHAR, fieldDecimal128, buf_size, + &fieldDecimal128_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 3, SQL_C_DOUBLE, &fieldDouble, sizeof(fieldDouble), + &fieldDouble_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = + SQLGetData(stmt, 4, SQL_C_CHAR, fieldString, buf_size, &fieldString_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 5, SQL_C_CHAR, fieldObjectId, buf_size, + &fieldObjectId_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 6, SQL_C_BIT, &fieldBoolean, sizeof(fieldBoolean), + &fieldBoolean_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 7, SQL_C_TYPE_DATE, &fieldDate, sizeof(fieldDate), + &fieldDate_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 8, SQL_C_SLONG, &fieldInt, sizeof(fieldInt), + &fieldInt_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 9, SQL_C_SBIGINT, &fieldLong, sizeof(fieldLong), + &fieldLong_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = + SQLGetData(stmt, 10, SQL_C_CHAR, fieldMaxKey, buf_size, &fieldMaxKey_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = + SQLGetData(stmt, 11, SQL_C_CHAR, fieldMinKey, buf_size, &fieldMinKey_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 12, SQL_C_CHAR, fieldNull, buf_size, &fieldNull_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 13, SQL_C_BINARY, fieldBinary, buf_size, + &fieldBinary_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + BOOST_CHECK_NE(SQL_NULL_DATA, id_len); + BOOST_CHECK_EQUAL("62196dcc4d91892191475139", (char*)id); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldDecimal128_len); + BOOST_CHECK_EQUAL("340282350000000000000", (char*)fieldDecimal128); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldDouble_len); + BOOST_CHECK_EQUAL(1.7976931348623157e308, fieldDouble); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldString_len); + BOOST_CHECK_EQUAL("some Text", (char*)fieldString); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldObjectId_len); + BOOST_CHECK_EQUAL("62196dcc4d9189219147513a", (char*)fieldObjectId); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldBoolean_len); + BOOST_CHECK_EQUAL(true, fieldBoolean); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldDate_len); + BOOST_CHECK_EQUAL(2020, fieldDate.year); + BOOST_CHECK_EQUAL(1, fieldDate.month); + BOOST_CHECK_EQUAL(1, fieldDate.day); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldInt_len); + BOOST_CHECK_EQUAL(2147483647, fieldInt); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldLong_len); + BOOST_CHECK_EQUAL(9223372036854775807, fieldLong); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldMaxKey_len); + BOOST_CHECK_EQUAL("MAXKEY", (char*)fieldMaxKey); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldMinKey_len); + BOOST_CHECK_EQUAL("MINKEY", (char*)fieldMinKey); + BOOST_CHECK_EQUAL(SQL_NULL_DATA, fieldNull_len); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldBinary_len); + BOOST_CHECK_EQUAL(3, fieldBinary_len); + BOOST_CHECK_EQUAL(fieldBinary[0], 0); + BOOST_CHECK_EQUAL(fieldBinary[1], 1); + BOOST_CHECK_EQUAL(fieldBinary[2], 2); + + // Fetch 2nd row - not exist + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_NO_DATA, ret); +} + +BOOST_AUTO_TEST_CASE(TestSingleResultUsingBindCol) { + std::string dsnConnectionString; + CreateDsnConnectionStringForLocalServer(dsnConnectionString); + Connect(dsnConnectionString); + SQLRETURN ret; + char request[] = "SELECT * FROM \"queries_test_001\""; + + ret = SQLExecDirect(stmt, reinterpret_cast< SQLCHAR* >(request), SQL_NTS); + if (!SQL_SUCCEEDED(ret)) { + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + } + const int32_t buf_size = 1024; + SQLCHAR id[buf_size]{}; + SQLLEN id_len = 0; + SQLCHAR fieldDecimal128[buf_size]{}; + SQLLEN fieldDecimal128_len = 0; + double fieldDouble = 0; + SQLLEN fieldDouble_len = 0; + SQLCHAR fieldString[buf_size]{}; + SQLLEN fieldString_len = 0; + SQLCHAR fieldObjectId[buf_size]{}; + SQLLEN fieldObjectId_len = 0; + bool fieldBoolean = false; + SQLLEN fieldBoolean_len = 0; + DATE_STRUCT fieldDate{}; + SQLLEN fieldDate_len = 0; + SQLINTEGER fieldInt; + SQLLEN fieldInt_len = 0; + SQLBIGINT fieldLong; + SQLLEN fieldLong_len = 0; + SQLCHAR fieldMaxKey[buf_size]; + SQLLEN fieldMaxKey_len = 0; + SQLCHAR fieldMinKey[buf_size]; + SQLLEN fieldMinKey_len = 0; + SQLCHAR fieldNull[buf_size]; + SQLLEN fieldNull_len = 0; + SQLCHAR fieldBinary[buf_size]; + SQLLEN fieldBinary_len = 0; + + ret = SQLBindCol(stmt, 1, SQL_C_CHAR, id, buf_size, &id_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 2, SQL_C_CHAR, fieldDecimal128, buf_size, &fieldDecimal128_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 3, SQL_C_DOUBLE, &fieldDouble, sizeof(fieldDouble), + &fieldDouble_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 4, SQL_C_CHAR, fieldString, buf_size, + &fieldString_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 5, SQL_C_CHAR, fieldObjectId, buf_size, + &fieldObjectId_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 6, SQL_C_BIT, &fieldBoolean, sizeof(fieldBoolean), + &fieldBoolean_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 7, SQL_C_TYPE_DATE, &fieldDate, sizeof(fieldDate), + &fieldDate_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 8, SQL_C_SLONG, &fieldInt, sizeof(fieldInt), &fieldInt_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 9, SQL_C_SBIGINT, &fieldLong, sizeof(fieldLong), + &fieldLong_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = + SQLBindCol(stmt, 10, SQL_C_CHAR, fieldMaxKey, buf_size, &fieldMaxKey_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = + SQLBindCol(stmt, 11, SQL_C_CHAR, fieldMinKey, buf_size, &fieldMinKey_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 12, SQL_C_CHAR, fieldNull, buf_size, &fieldNull_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLBindCol(stmt, 13, SQL_C_BINARY, fieldBinary, buf_size, &fieldBinary_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + // Fetch 1st row + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + BOOST_CHECK_NE(SQL_NULL_DATA, id_len); + BOOST_CHECK_EQUAL("62196dcc4d91892191475139", (char*)id); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldDecimal128_len); + BOOST_CHECK_EQUAL("340282350000000000000", (char*)fieldDecimal128); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldDouble_len); + BOOST_CHECK_EQUAL(1.7976931348623157e308, fieldDouble); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldString_len); + BOOST_CHECK_EQUAL("some Text", (char*)fieldString); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldObjectId_len); + BOOST_CHECK_EQUAL("62196dcc4d9189219147513a", (char*)fieldObjectId); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldBoolean_len); + BOOST_CHECK_EQUAL(true, fieldBoolean); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldDate_len); + BOOST_CHECK_EQUAL(2020, fieldDate.year); + BOOST_CHECK_EQUAL(1, fieldDate.month); + BOOST_CHECK_EQUAL(1, fieldDate.day); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldInt_len); + BOOST_CHECK_EQUAL(2147483647, fieldInt); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldLong_len); + BOOST_CHECK_EQUAL(9223372036854775807, fieldLong); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldMaxKey_len); + BOOST_CHECK_EQUAL("MAXKEY", (char*)fieldMaxKey); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldMinKey_len); + BOOST_CHECK_EQUAL("MINKEY", (char*)fieldMinKey); + BOOST_CHECK_EQUAL(SQL_NULL_DATA, fieldNull_len); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldBinary_len); + BOOST_CHECK_EQUAL(3, fieldBinary_len); + BOOST_CHECK_EQUAL(fieldBinary[0], 0); + BOOST_CHECK_EQUAL(fieldBinary[1], 1); + BOOST_CHECK_EQUAL(fieldBinary[2], 2); + + // Fetch 2nd row - not exist + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_NO_DATA, ret); +} + +BOOST_AUTO_TEST_CASE(TestMultiLineResultUsingGetData) { + std::string dsnConnectionString; + CreateDsnConnectionStringForLocalServer(dsnConnectionString); + Connect(dsnConnectionString); + SQLRETURN ret; + char request[] = "SELECT * FROM \"queries_test_002\" ORDER BY \"queries_test_002__id\""; + + ret = SQLExecDirect(stmt, reinterpret_cast< SQLCHAR* >(request), SQL_NTS); + if (!SQL_SUCCEEDED(ret)) { + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + } + + const int32_t buf_size = 1024; + SQLCHAR id[buf_size]{}; + SQLLEN id_len = 0; + //"\"fieldDecimal128\": \"$fieldDecimal128\", " + SQLCHAR fieldDecimal128[buf_size]{}; + SQLLEN fieldDecimal128_len = 0; + + // Fetch 1st row + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + ret = SQLGetData(stmt, 1, SQL_C_CHAR, id, buf_size, &id_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 2, SQL_C_CHAR, fieldDecimal128, buf_size, + &fieldDecimal128_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + BOOST_CHECK_NE(SQL_NULL_DATA, id_len); + BOOST_CHECK_EQUAL("62196dcc4d91892191475139", (char*)id); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldDecimal128_len); + BOOST_CHECK_EQUAL("340282350000000000000", (char*)fieldDecimal128); + + // Fetch 2nd row + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + ret = SQLGetData(stmt, 1, SQL_C_CHAR, id, buf_size, &id_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 2, SQL_C_CHAR, fieldDecimal128, buf_size, + &fieldDecimal128_len); + + BOOST_CHECK_NE(SQL_NULL_DATA, id_len); + BOOST_CHECK_EQUAL("62196dcc4d9189219147513a", (char*)id); + BOOST_CHECK_EQUAL(SQL_NULL_DATA, fieldDecimal128_len); + + // Fetch 3rd row + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + ret = SQLGetData(stmt, 1, SQL_C_CHAR, id, buf_size, &id_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 2, SQL_C_CHAR, fieldDecimal128, buf_size, + &fieldDecimal128_len); + + BOOST_CHECK_NE(SQL_NULL_DATA, id_len); + BOOST_CHECK_EQUAL("62196dcc4d9189219147513b", (char*)id); + BOOST_CHECK_NE(SQL_NULL_DATA, fieldDecimal128_len); + BOOST_CHECK_EQUAL("340282350000000000000", (char*)fieldDecimal128); + + // Fetch 4th row - not exist + ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_NO_DATA, ret); +} + +BOOST_AUTO_TEST_CASE(TestArrayStructJoinUsingGetData) { std::string dsnConnectionString; CreateDsnConnectionStringForLocalServer(dsnConnectionString); Connect(dsnConnectionString); SQLRETURN ret; - char request[] = "SELECT * FROM queries_test_001"; + char request[] = + "SELECT q3.queries_test_003__id, \n" + " \"a1\".\"value\", \n" + " \"a2\".\"value\", \n" + " \"d1\".\"field1\", \n" + " \"d2\".\"field2\" \n" + " FROM queries_test_003 AS q3 \n" + " JOIN queries_test_003_fieldArrayOfInt AS a1 \n" + " ON q3.queries_test_003__id = a1.queries_test_003__id \n" + " JOIN queries_test_003_fieldArrayOfString AS a2 \n" + " ON a1.queries_test_003__id = a2.queries_test_003__id \n" + " JOIN queries_test_003_fieldDocument AS d1 \n" + " ON a2.queries_test_003__id = d1.queries_test_003__id \n" + " JOIN queries_test_003_fieldDocument_subDoc AS d2 \n" + " ON d1.queries_test_003__id = d2.queries_test_003__id"; ret = SQLExecDirect(stmt, reinterpret_cast< SQLCHAR* >(request), SQL_NTS); if (!SQL_SUCCEEDED(ret)) { - GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt); + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); } + + const int32_t buf_size = 1024; + SQLCHAR id[buf_size]{}; + SQLLEN id_len = 0; + SQLINTEGER a1_value = 0; + SQLLEN a1_value_len = 0; + SQLCHAR a2_value[buf_size]{}; + SQLLEN a2_value_len = 0; + SQLCHAR d1_value[buf_size]{}; + SQLLEN d1_value_len = 0; + SQLCHAR d2_value[buf_size]{}; + SQLLEN d2_value_len = 0; + + // Fetch 1st row ret = SQLFetch(stmt); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + ret = SQLGetData(stmt, 1, SQL_C_CHAR, id, buf_size, &id_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 2, SQL_C_SLONG, &a1_value, sizeof(a1_value), &a1_value_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 3, SQL_C_CHAR, a2_value, buf_size, &a2_value_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 4, SQL_C_CHAR, d1_value, buf_size, &d1_value_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + ret = SQLGetData(stmt, 5, SQL_C_CHAR, d2_value, buf_size, &d2_value_len); + BOOST_CHECK_EQUAL(SQL_SUCCESS, ret); + + // Check the first row + BOOST_CHECK_NE(SQL_NULL_DATA, id_len); + BOOST_CHECK_EQUAL("62196dcc4d91892191475139", (char*)id); + BOOST_CHECK_NE(SQL_NULL_DATA, a1_value_len); + BOOST_CHECK_EQUAL(1, a1_value); + BOOST_CHECK_NE(SQL_NULL_DATA, a2_value_len); + BOOST_CHECK_EQUAL("value1", (char*)a2_value); + BOOST_CHECK_NE(SQL_NULL_DATA, d1_value_len); + BOOST_CHECK_EQUAL("field1 Value", (char*)d1_value); + BOOST_CHECK_NE(SQL_NULL_DATA, d2_value_len); + BOOST_CHECK_EQUAL("field2 Value", (char*)d2_value); + + // Count the rows + int32_t actual_rows = 0; + while (SQL_SUCCEEDED(ret)) { + actual_rows++; + ret = SQLFetch(stmt); + } BOOST_CHECK_EQUAL(SQL_NO_DATA, ret); + BOOST_CHECK_EQUAL(9, actual_rows); } BOOST_AUTO_TEST_CASE(TestTwoRowsInt8, *disabled()) { @@ -885,393 +1290,6 @@ BOOST_AUTO_TEST_CASE(TestDistributedJoins, *disabled()) { BOOST_CHECK_EQUAL(rowsNum, entriesNum); } -BOOST_AUTO_TEST_CASE(TestInsertSelect, *disabled()) { - Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - const int recordsNum = 100; - - // Inserting values. - InsertTestStrings(recordsNum); - - int64_t key = 0; - char strField[1024]; - 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 - SQLCHAR selectReq[] = "SELECT _key, strField FROM TestType ORDER BY _key"; - - ret = SQLExecDirect(stmt, selectReq, sizeof(selectReq)); - - 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 + 1; - - BOOST_CHECK_EQUAL(key, expectedKey); - - BOOST_CHECK_EQUAL(std::string(strField, strFieldLen), expectedStr); - - ++selectedRecordsNum; - } - - BOOST_CHECK_EQUAL(recordsNum, selectedRecordsNum); -} - -BOOST_AUTO_TEST_CASE(TestInsertUpdateSelect, *disabled()) { - Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - const int recordsNum = 100; - - // Inserting values. - InsertTestStrings(recordsNum); - - int64_t key = 0; - char strField[1024]; - SQLLEN strFieldLen = 0; - - SQLCHAR updateReq[] = - "UPDATE TestType SET strField = 'Updated value' WHERE _key = 42"; - - SQLRETURN ret = SQLExecDirect(stmt, updateReq, SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - ret = SQLFreeStmt(stmt, SQL_CLOSE); - - // 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 - SQLCHAR selectReq[] = "SELECT _key, strField FROM TestType ORDER BY _key"; - - ret = SQLExecDirect(stmt, selectReq, sizeof(selectReq)); - - 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)); - - int64_t expectedKey = selectedRecordsNum + 1; - std::string expectedStr; - - BOOST_CHECK_EQUAL(key, expectedKey); - - if (expectedKey == 42) - expectedStr = "Updated value"; - else - expectedStr = GetTestString(selectedRecordsNum); - - BOOST_CHECK_EQUAL(std::string(strField, strFieldLen), expectedStr); - - ++selectedRecordsNum; - } - - BOOST_CHECK_EQUAL(recordsNum, selectedRecordsNum); -} - -BOOST_AUTO_TEST_CASE(TestInsertDeleteSelect, *disabled()) { - Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - const int recordsNum = 100; - - // Inserting values. - InsertTestStrings(recordsNum); - - int64_t key = 0; - char strField[1024]; - SQLLEN strFieldLen = 0; - - SQLCHAR updateReq[] = "DELETE FROM TestType WHERE (_key % 2) = 1"; - - SQLRETURN ret = SQLExecDirect(stmt, updateReq, SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - ret = SQLFreeStmt(stmt, SQL_CLOSE); - - // 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 - SQLCHAR selectReq[] = "SELECT _key, strField FROM TestType ORDER BY _key"; - - ret = SQLExecDirect(stmt, selectReq, sizeof(selectReq)); - - 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)); - - int64_t expectedKey = (selectedRecordsNum + 1) * 2; - std::string expectedStr = GetTestString(expectedKey - 1); - - BOOST_CHECK_EQUAL(key, expectedKey); - BOOST_CHECK_EQUAL(std::string(strField, strFieldLen), expectedStr); - - ++selectedRecordsNum; - } - - BOOST_CHECK_EQUAL(recordsNum / 2, selectedRecordsNum); -} - -BOOST_AUTO_TEST_CASE(TestInsertMergeSelect, *disabled()) { - Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - const int recordsNum = 100; - - // Inserting values. - InsertTestStrings(recordsNum / 2); - - // Merging values. - InsertTestStrings(recordsNum, true); - - int64_t key = 0; - char strField[1024]; - 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 - SQLCHAR selectReq[] = "SELECT _key, strField FROM TestType ORDER BY _key"; - - ret = SQLExecDirect(stmt, selectReq, sizeof(selectReq)); - - 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 + 1; - - BOOST_CHECK_EQUAL(key, expectedKey); - - BOOST_CHECK_EQUAL(std::string(strField, strFieldLen), expectedStr); - - ++selectedRecordsNum; - } - - BOOST_CHECK_EQUAL(recordsNum, selectedRecordsNum); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect2, *disabled()) { - InsertBatchSelect(2); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect100, *disabled()) { - InsertBatchSelect(100); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect1000, *disabled()) { - InsertBatchSelect(1000); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect1023, *disabled()) { - InsertBatchSelect(1023); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect1024, *disabled()) { - InsertBatchSelect(1024); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect1025, *disabled()) { - InsertBatchSelect(1025); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect2000, *disabled()) { - InsertBatchSelect(2000); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect2047, *disabled()) { - InsertBatchSelect(2047); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect2048, *disabled()) { - InsertBatchSelect(2048); -} - -BOOST_AUTO_TEST_CASE(TestInsertBatchSelect2049, *disabled()) { - InsertBatchSelect(2049); -} - -BOOST_AUTO_TEST_CASE(TestNotFullInsertBatchSelect900, *disabled()) { - InsertNonFullBatchSelect(900, 42); -} - -BOOST_AUTO_TEST_CASE(TestNotFullInsertBatchSelect1500, *disabled()) { - InsertNonFullBatchSelect(1500, 100); -} - -BOOST_AUTO_TEST_CASE(TestNotFullInsertBatchSelect4500, *disabled()) { - InsertNonFullBatchSelect(4500, 1500); -} - -BOOST_AUTO_TEST_CASE(TestNotFullInsertBatchSelect4096, *disabled()) { - InsertNonFullBatchSelect(4096, 1024); -} - -template < size_t n, size_t k > -void CheckMeta(char columns[n][k], SQLLEN columnsLen[n]) { - std::string catalog(columns[0], columnsLen[0]); - std::string schema(columns[1], columnsLen[1]); - std::string table(columns[2], columnsLen[2]); - std::string tableType(columns[3], columnsLen[3]); - - BOOST_CHECK_EQUAL(catalog, std::string("")); - BOOST_CHECK_EQUAL(tableType, std::string("TABLE")); - BOOST_CHECK_EQUAL(columnsLen[4], SQL_NULL_DATA); - - if (schema == "\"cache\"") { - BOOST_CHECK_EQUAL(table, std::string("TESTTYPE")); - } else if (schema == "\"cache2\"") { - BOOST_CHECK_EQUAL(table, std::string("COMPLEXTYPE")); - } else { - BOOST_FAIL("Unknown schema: " + schema); - } -} - -BOOST_AUTO_TEST_CASE(TestTablesMeta, *disabled()) { - Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache2"); - - SQLRETURN ret; - - enum { COLUMNS_NUM = 5 }; - - // Five collumns: TABLE_CAT, TABLE_SCHEM, TABLE_NAME, TABLE_TYPE, REMARKS - char columns[COLUMNS_NUM][ODBC_BUFFER_SIZE]; - SQLLEN columnsLen[COLUMNS_NUM]; - - // Binding columns. - for (size_t i = 0; i < COLUMNS_NUM; ++i) { - columnsLen[i] = ODBC_BUFFER_SIZE; - - ret = SQLBindCol(stmt, static_cast< SQLSMALLINT >(i + 1), SQL_C_CHAR, - columns[i], columnsLen[i], &columnsLen[i]); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - } - - SQLCHAR catalogPattern[] = ""; - SQLCHAR schemaPattern[] = ""; - SQLCHAR tablePattern[] = ""; - SQLCHAR tableTypePattern[] = ""; - - ret = SQLTables(stmt, catalogPattern, SQL_NTS, schemaPattern, SQL_NTS, - tablePattern, SQL_NTS, tableTypePattern, SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - ret = SQLFetch(stmt); - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - CheckMeta< COLUMNS_NUM, ODBC_BUFFER_SIZE >(columns, columnsLen); - - ret = SQLFetch(stmt); - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - CheckMeta< COLUMNS_NUM, ODBC_BUFFER_SIZE >(columns, columnsLen); - - ret = SQLFetch(stmt); - BOOST_CHECK(ret == SQL_NO_DATA); -} - template < typename T > void CheckObjectData(int8_t* data, int32_t len, T const& value) { InteropUnpooledMemory mem(len); @@ -1613,274 +1631,6 @@ BOOST_AUTO_TEST_CASE(TestErrorMessage, *disabled()) { BOOST_FAIL("'" + error + "' does not match '" + pattern + "'"); } -BOOST_AUTO_TEST_CASE(TestAffectedRows, *disabled()) { - Connect( - "DRIVER={Apache " - "Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache;PAGE_SIZE=1024"); - - const int recordsNum = 100; - - // Inserting values. - InsertTestStrings(recordsNum); - - SQLCHAR updateReq[] = - "UPDATE TestType SET strField = 'Updated value' WHERE _key > 20 AND _key " - "< 40"; - - SQLRETURN ret = SQLExecDirect(stmt, updateReq, SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - SQLLEN affected = 0; - ret = SQLRowCount(stmt, &affected); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_CHECK_EQUAL(affected, 19); - - ret = SQLFreeStmt(stmt, SQL_CLOSE); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - // Just selecting everything to make sure everything is OK - SQLCHAR selectReq[] = "SELECT _key, strField FROM TestType ORDER BY _key"; - - ret = SQLExecDirect(stmt, selectReq, sizeof(selectReq)); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - affected = -1; - ret = SQLRowCount(stmt, &affected); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_CHECK_EQUAL(affected, 1024); -} - -BOOST_AUTO_TEST_CASE(TestAffectedRowsOnSelect, *disabled()) { - Connect( - "DRIVER={Apache " - "Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache;PAGE_SIZE=123"); - - const int recordsNum = 1000; - - // Inserting values. - InsertTestStrings(recordsNum); - - // Just selecting everything to make sure everything is OK - SQLCHAR selectReq[] = "SELECT _key, strField FROM TestType ORDER BY _key"; - - SQLRETURN ret = SQLExecDirect(stmt, selectReq, sizeof(selectReq)); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - for (int i = 0; i < 200; ++i) { - SQLLEN affected = -1; - ret = SQLRowCount(stmt, &affected); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_CHECK_EQUAL(affected, 123); - - ret = SQLFetch(stmt); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - } -} - -BOOST_AUTO_TEST_CASE(TestMultipleSelects, *disabled()) { - Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - const int stmtCnt = 10; - - std::stringstream stream; - for (int i = 0; i < stmtCnt; ++i) - stream << "select " << i << "; "; - - stream << '\0'; - - std::string query0 = stream.str(); - std::vector< SQLCHAR > query(query0.begin(), query0.end()); - - SQLRETURN ret = SQLExecDirect(stmt, &query[0], SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - long res = 0; - - BOOST_TEST_CHECKPOINT("Binding column"); - ret = SQLBindCol(stmt, 1, SQL_C_SLONG, &res, 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - for (long i = 0; i < stmtCnt; ++i) { - ret = SQLFetch(stmt); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_CHECK_EQUAL(res, i); - - ret = SQLFetch(stmt); - - BOOST_CHECK_EQUAL(ret, SQL_NO_DATA); - - ret = SQLMoreResults(stmt); - - if (i < stmtCnt - 1 && !SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - else if (i == stmtCnt - 1) - BOOST_CHECK_EQUAL(ret, SQL_NO_DATA); - } -} - -BOOST_AUTO_TEST_CASE(TestMultipleMixedStatements, *disabled()) { - Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - const int stmtCnt = 10; - - std::stringstream stream; - for (int i = 0; i < stmtCnt; ++i) - stream << "select " << i << "; insert into TestType(_key) values(" << i - << "); "; - - stream << '\0'; - - std::string query0 = stream.str(); - std::vector< SQLCHAR > query(query0.begin(), query0.end()); - - SQLRETURN ret = SQLExecDirect(stmt, &query[0], SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - long res = 0; - - BOOST_TEST_CHECKPOINT("Binding column"); - ret = SQLBindCol(stmt, 1, SQL_C_SLONG, &res, 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - for (long i = 0; i < stmtCnt; ++i) { - ret = SQLFetch(stmt); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_CHECK_EQUAL(res, i); - - ret = SQLFetch(stmt); - - BOOST_CHECK_EQUAL(ret, SQL_NO_DATA); - - ret = SQLMoreResults(stmt); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - SQLLEN affected = 0; - ret = SQLRowCount(stmt, &affected); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_CHECK_EQUAL(affected, 1); - - ret = SQLFetch(stmt); - - BOOST_CHECK_EQUAL(ret, SQL_NO_DATA); - - ret = SQLMoreResults(stmt); - - if (i < stmtCnt - 1 && !SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - else if (i == stmtCnt - 1) - BOOST_CHECK_EQUAL(ret, SQL_NO_DATA); - } -} - -BOOST_AUTO_TEST_CASE(TestMultipleMixedStatementsNoFetch, *disabled()) { - Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - const int stmtCnt = 10; - - std::stringstream stream; - for (int i = 0; i < stmtCnt; ++i) - stream << "select " << i << "; insert into TestType(_key) values(" << i - << "); "; - - stream << '\0'; - - std::string query0 = stream.str(); - std::vector< SQLCHAR > query(query0.begin(), query0.end()); - - SQLRETURN ret = SQLExecDirect(stmt, &query[0], SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - long res = 0; - - BOOST_TEST_CHECKPOINT("Binding column"); - ret = SQLBindCol(stmt, 1, SQL_C_SLONG, &res, 0, 0); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - for (long i = 0; i < stmtCnt; ++i) { - ret = SQLMoreResults(stmt); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - SQLLEN affected = 0; - ret = SQLRowCount(stmt, &affected); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - BOOST_CHECK_EQUAL(affected, 1); - - ret = SQLFetch(stmt); - - BOOST_CHECK_EQUAL(ret, SQL_NO_DATA); - - ret = SQLMoreResults(stmt); - - if (i < stmtCnt - 1 && !SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - else if (i == stmtCnt - 1) - BOOST_CHECK_EQUAL(ret, SQL_NO_DATA); - } -} - -BOOST_AUTO_TEST_CASE(TestCloseAfterEmptyUpdate, *disabled()) { - Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); - - SQLCHAR query[] = "update TestType set strField='test' where _key=42"; - - SQLRETURN ret = SQLExecDirect(stmt, &query[0], SQL_NTS); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); - - ret = SQLFreeStmt(stmt, SQL_CLOSE); - - if (!SQL_SUCCEEDED(ret)) - BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); -} - BOOST_AUTO_TEST_CASE(TestLoginTimeout, *disabled()) { Prepare(); diff --git a/src/odbc/CMakeLists.txt b/src/odbc/CMakeLists.txt index c9d7f384f..fc19af592 100644 --- a/src/odbc/CMakeLists.txt +++ b/src/odbc/CMakeLists.txt @@ -70,6 +70,9 @@ set(SOURCES src/app/application_data_buffer.cpp src/diagnostic/diagnosable_adapter.cpp src/diagnostic/diagnostic_record.cpp src/diagnostic/diagnostic_record_storage.cpp + src/documentdb_column.cpp + src/documentdb_cursor.cpp + src/documentdb_row.cpp src/jni/database_metadata.cpp src/jni/documentdb_connection.cpp src/jni/documentdb_connection_properties.cpp @@ -93,7 +96,6 @@ set(SOURCES src/app/application_data_buffer.cpp src/query/table_metadata_query.cpp src/query/type_info_query.cpp src/query/special_columns_query.cpp - src/query/streaming_query.cpp src/sql/sql_parser.cpp src/sql/sql_lexer.cpp src/sql/sql_set_streaming_command.cpp diff --git a/src/odbc/include/ignite/odbc/app/application_data_buffer.h b/src/odbc/include/ignite/odbc/app/application_data_buffer.h index d0df7d1a5..90c869bdc 100644 --- a/src/odbc/include/ignite/odbc/app/application_data_buffer.h +++ b/src/odbc/include/ignite/odbc/app/application_data_buffer.h @@ -122,6 +122,14 @@ class ApplicationDataBuffer { this->elementOffset = idx; } + /** + * Put in buffer value of type optional int8_t. + * + * @param value Value. + * @return Conversion result. + */ + ConversionResult::Type PutInt8(boost::optional< int8_t > value); + /** * Put in buffer value of type int8_t. * @@ -136,7 +144,7 @@ class ApplicationDataBuffer { * @param value Value. * @return Conversion result. */ - ConversionResult::Type PutOptInt16(boost::optional value); + ConversionResult::Type PutInt16(boost::optional value); /** * Put in buffer value of type int16_t. @@ -152,7 +160,7 @@ class ApplicationDataBuffer { * @param value Value. * @return Conversion result. */ - ConversionResult::Type PutOptInt32(boost::optional< int32_t > value); + ConversionResult::Type PutInt32(boost::optional< int32_t > value); /** * Put in buffer value of type int32_t. @@ -162,6 +170,14 @@ class ApplicationDataBuffer { */ ConversionResult::Type PutInt32(int32_t value); + /** + * Put in buffer value of type optional int64_t. + * + * @param value Value. + * @return Conversion result. + */ + ConversionResult::Type PutInt64(boost::optional< int64_t > value); + /** * Put in buffer value of type int64_t. * @@ -170,6 +186,14 @@ class ApplicationDataBuffer { */ ConversionResult::Type PutInt64(int64_t value); + /** + * Put in buffer value of type optional float. + * + * @param value Value. + * @return Conversion result. + */ + ConversionResult::Type PutFloat(boost::optional< float > value); + /** * Put in buffer value of type float. * @@ -178,6 +202,14 @@ class ApplicationDataBuffer { */ ConversionResult::Type PutFloat(float value); + /** + * Put in buffer value of type optional double. + * + * @param value Value. + * @return Conversion result. + */ + ConversionResult::Type PutDouble(boost::optional< double > value); + /** * Put in buffer value of type double. * @@ -192,7 +224,7 @@ class ApplicationDataBuffer { * @param optional value Value. * @return Conversion result. */ - ConversionResult::Type PutOptString( + ConversionResult::Type PutString( const boost::optional< std::string >& value); /** @@ -228,7 +260,7 @@ class ApplicationDataBuffer { * @param written Number of written characters. * @return Conversion result. */ - ConversionResult::Type PutBinaryData(void* data, size_t len, + ConversionResult::Type PutBinaryData(const void* data, size_t len, int32_t& written); /** @@ -237,6 +269,14 @@ class ApplicationDataBuffer { */ ConversionResult::Type PutNull(); + /** + * Put optional decimal value to buffer. + * + * @param value Value to put. + * @return Conversion result. + */ + ConversionResult::Type PutDecimal(const boost::optional< common::Decimal >& value); + /** * Put decimal value to buffer. * @@ -245,6 +285,14 @@ class ApplicationDataBuffer { */ ConversionResult::Type PutDecimal(const common::Decimal& value); + /** + * Put optional date to buffer. + * + * @param value Value to put. + * @return Conversion result. + */ + ConversionResult::Type PutDate(const boost::optional< Date >& value); + /** * Put date to buffer. * @@ -253,6 +301,14 @@ class ApplicationDataBuffer { */ ConversionResult::Type PutDate(const Date& value); + /** + * Put optional timestamp to buffer. + * + * @param value Value to put. + * @return Conversion result. + */ + ConversionResult::Type PutTimestamp(const boost::optional< Timestamp >& value); + /** * Put timestamp to buffer. * @@ -261,6 +317,14 @@ class ApplicationDataBuffer { */ ConversionResult::Type PutTimestamp(const Timestamp& value); + /** + * Put optional time to buffer. + * + * @param value Value to put. + * @return Conversion result. + */ + ConversionResult::Type PutTime(const boost::optional< Time >& value); + /** * Put time to buffer. * @@ -487,7 +551,7 @@ class ApplicationDataBuffer { * @param written Number of characters written. * @return Conversion result. */ - ConversionResult::Type PutRawDataToBuffer(void* data, size_t len, + ConversionResult::Type PutRawDataToBuffer(const void* data, size_t len, int32_t& written); /** diff --git a/src/odbc/include/ignite/odbc/connection.h b/src/odbc/include/ignite/odbc/connection.h index 2f15b1ed9..2afe85d6d 100644 --- a/src/odbc/include/ignite/odbc/connection.h +++ b/src/odbc/include/ignite/odbc/connection.h @@ -35,6 +35,7 @@ #include "ignite/odbc/odbc_error.h" #include "ignite/odbc/parser.h" #include "ignite/odbc/streaming/streaming_context.h" +#include "mongocxx/client.hpp" using ignite::odbc::common::concurrent::SharedPointer; using ignite::odbc::jni::DatabaseMetaData; @@ -166,15 +167,6 @@ class Connection : public diagnostic::DiagnosableAdapter { */ bool IsAutoCommit() const; - /** - * Get streaming context. - * - * @return Streaming context. - */ - streaming::StreamingContext& GetStreamingContext() { - return streamingContext; - } - /** * Create diagnostic record associated with the Connection instance. * @@ -259,6 +251,10 @@ class Connection : public diagnostic::DiagnosableAdapter { */ void SetAttribute(int attr, void* value, SQLINTEGER valueLen); + inline std::shared_ptr& GetMongoClient() { + return mongoClient_; + } + private: IGNITE_NO_COPY_ASSIGNMENT(Connection); @@ -463,33 +459,32 @@ class Connection : public diagnostic::DiagnosableAdapter { Connection(Environment* env); /** Parent. */ - Environment* env; + Environment* env_; /** Connection timeout in seconds. */ - int32_t timeout = 0; + int32_t timeout_ = 0; /** Login timeout in seconds. */ - int32_t loginTimeout = DEFAULT_CONNECT_TIMEOUT; + int32_t loginTimeout_ = DEFAULT_CONNECT_TIMEOUT; /** Autocommit flag. */ - bool autoCommit = true; + bool autoCommit_ = true; /** Configuration. */ - config::Configuration config; + config::Configuration config_; /** Connection info. */ - config::ConnectionInfo info; + config::ConnectionInfo info_; /** Java connection object */ - SharedPointer< DocumentDbConnection > _connection; + SharedPointer< DocumentDbConnection > connection_; - SharedPointer< JniContext > _jniContext; + SharedPointer< JniContext > jniContext_; - /** JVM options */ - std::vector< char* > opts; + std::shared_ptr< mongocxx::client > mongoClient_; - /** Streaming context. */ - streaming::StreamingContext streamingContext; + /** JVM options */ + std::vector< char* > opts_; }; } // namespace odbc } // namespace ignite diff --git a/src/odbc/include/ignite/odbc/documentdb_column.h b/src/odbc/include/ignite/odbc/documentdb_column.h new file mode 100644 index 000000000..9042bb6bd --- /dev/null +++ b/src/odbc/include/ignite/odbc/documentdb_column.h @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _IGNITE_ODBC_DOCUMENTDB_COLUMN +#define _IGNITE_ODBC_DOCUMENTDB_COLUMN + +#include +#include +#include +#include +#include + +using namespace ignite::odbc::impl::binary; +using ignite::odbc::jni::JdbcColumnMetadata; +using ignite::odbc::app::ApplicationDataBuffer; +using ignite::odbc::app::ConversionResult; + +namespace ignite { +namespace odbc { +/** + * Result set column. + */ +class DocumentDbColumn { + public: + /** + * Default constructor. + */ + DocumentDbColumn() = delete; + + /** + * Copy constructor. + * + * @param other Another instance. + */ + DocumentDbColumn(const DocumentDbColumn& other); + + /** + * Copy operator. + * + * @param other Another instance. + * @return This. + */ + DocumentDbColumn& operator=(const DocumentDbColumn& other) = delete; + + /** + * Destructor. + */ + ~DocumentDbColumn(); + + /** + * Constructor. + * + * @param reader Reader to be used to retrieve column data. + */ + DocumentDbColumn(bsoncxx::document::view& document, + JdbcColumnMetadata& columnMetadata, std::string& path); + + /** + * Get column size in bytes. + * + * @return Column size. + */ + int32_t GetSize() const { + return size_; + } + + /** + * Read column data and store it in application data buffer. + * + * @param reader Reader to use. + * @param dataBuf Application data buffer. + * @return Operation result. + */ + ConversionResult::Type ReadToBuffer( + ApplicationDataBuffer& dataBuf) const; + + private: + + /** Setter for int8 data type */ + ConversionResult::Type PutInt8( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for int16 data type */ + ConversionResult::Type PutInt16( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for int32 data type */ + ConversionResult::Type PutInt32( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for int64 data type */ + ConversionResult::Type PutInt64( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for float data type */ + ConversionResult::Type PutFloat( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for double data type */ + ConversionResult::Type PutDouble( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for string data type */ + ConversionResult::Type PutString( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for decimal data type */ + ConversionResult::Type PutDecimal( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for time data type */ + ConversionResult::Type PutTime( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for date data type */ + ConversionResult::Type PutDate( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for timestamp data type */ + ConversionResult::Type PutTimestamp( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + /** Setter for binary data type */ + ConversionResult::Type PutBinaryData( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const; + + /** Column type */ + int32_t type_; + + /** Column data size in bytes. */ + int32_t size_ = 0; + + bsoncxx::document::view const& document_; + + JdbcColumnMetadata& columnMetadata_; + + std::string& path_; +}; +} // namespace odbc +} // namespace ignite + +#endif //_IGNITE_ODBC_DOCUMENTDB_COLUMN diff --git a/src/odbc/include/ignite/odbc/documentdb_cursor.h b/src/odbc/include/ignite/odbc/documentdb_cursor.h new file mode 100644 index 000000000..172dec729 --- /dev/null +++ b/src/odbc/include/ignite/odbc/documentdb_cursor.h @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _IGNITE_ODBC_DOCUMENTDB_CURSOR +#define _IGNITE_ODBC_DOCUMENTDB_CURSOR + +#include + +#include +#include + +#include "ignite/odbc/common_types.h" +#include "ignite/odbc/result_page.h" +#include "ignite/odbc/documentdb_row.h" +#include "mongocxx/client.hpp" +#include "mongocxx/cursor.hpp" + +namespace ignite { +namespace odbc { +/** + * Query result cursor. + */ +class DocumentDbCursor { + public: + /** + * Constructor. + * @param queryId ID of the executed query. + */ + DocumentDbCursor(mongocxx::cursor& cursor, std::vector< JdbcColumnMetadata >& columnMetadata, std::vector< std::string >& paths); + + /** + * Destructor. + */ + ~DocumentDbCursor(); + + /** + * Move cursor to the next result row. + * + * @return False if data update required or no more data. + */ + bool Increment(); + + /** + * Check if the cursor has data. + * + * @return True if the cursor has data. + */ + bool HasData() const; + + /** + * Get current row. + * + * @return Current row. Returns zero if cursor needs data update or has no + * more data. + */ + DocumentDbRow* GetRow(); + + private: + IGNITE_NO_COPY_ASSIGNMENT(DocumentDbCursor); + + /** The resulting cursor to query/aggregate call */ + mongocxx::cursor cursor_; + + /** The iterator to beginning of cursor */ + mongocxx::cursor::iterator iterator_; + + /** The iterator to end of cursor */ + mongocxx::cursor::iterator iteratorEnd_; + + /** The column metadata */ + std::vector< JdbcColumnMetadata > columnMetadata_; + + /** The associated path in the resulting document for each column */ + std::vector< std::string > paths_; + + /** The current row */ + std::unique_ptr< DocumentDbRow > currentRow_; + + // Is this the first row of the iterator? + bool isFirstRow_ = true; +}; +} // namespace odbc +} // namespace ignite + +#endif //_IGNITE_ODBC_DOCUMENTDB_CURSOR diff --git a/src/odbc/include/ignite/odbc/documentdb_row.h b/src/odbc/include/ignite/odbc/documentdb_row.h new file mode 100644 index 000000000..a16582acf --- /dev/null +++ b/src/odbc/include/ignite/odbc/documentdb_row.h @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _IGNITE_ODBC_DOCUMENTDB_ROW +#define _IGNITE_ODBC_DOCUMENTDB_ROW + +#include + +#include + +#include "ignite/odbc/app/application_data_buffer.h" +#include "ignite/odbc/jni/jdbc_column_metadata.h" +#include "ignite/odbc/documentdb_column.h" +#include "bsoncxx/document/view.hpp" +#include "mongocxx/cursor.hpp" + +using namespace ignite::odbc::impl::interop; +using namespace ignite::odbc::impl::binary; +using ignite::odbc::jni::JdbcColumnMetadata; + +namespace ignite { +namespace odbc { +/** + * Query result row. + */ +class DocumentDbRow { + public: + /** + * Constructor. + * + * @param pageData Page data. + */ + DocumentDbRow(bsoncxx::document::view const& document, + std::vector< JdbcColumnMetadata >& columnMetadata, + std::vector< std::string >& paths); + + /** + * Destructor. + */ + ~DocumentDbRow(); + + /** + * Get row size in columns. + * + * @return number of columns. + */ + int32_t GetSize() const { + return columnMetadata_.size(); + } + + /** + * Read column data and store it in application data buffer. + * + * @param columnIdx Column index. + * @param dataBuf Application data buffer. + * @return Conversion result. + */ + app::ConversionResult::Type ReadColumnToBuffer( + uint32_t columnIdx, app::ApplicationDataBuffer& dataBuf); + + private: + IGNITE_NO_COPY_ASSIGNMENT(DocumentDbRow); + + /** + * Get columns by its index. + * + * Column indexing starts at 1. + * + * @note This operation is private because it's unsafe to use: + * It is neccessary to ensure that column is discovered prior + * to calling this method using EnsureColumnDiscovered(). + * + * @param columnIdx Column index. + * @return Reference to specified column. + */ + DocumentDbColumn& GetColumn(uint32_t columnIdx) { + return columns_[columnIdx - 1]; + } + + /** + * Ensure that column data is discovered. + * + * @param columnIdx Column index. + * @return True if the column is discovered and false if it can not + * be discovered. + */ + bool EnsureColumnDiscovered(int16_t columnIdx); + + /** Current position in row. */ + int32_t pos; + + /** Row size in columns. */ + int32_t size; + + /** Columns. */ + std::vector< DocumentDbColumn > columns_; + + /** The current document */ + bsoncxx::document::view document_; + + /** The column metadata */ + std::vector< JdbcColumnMetadata >& columnMetadata_; + + /** The matching paths in the document for the columns */ + std::vector< std::string >& paths_; +}; +} // namespace odbc +} // namespace ignite + +#endif //_IGNITE_ODBC_DOCUMENTDB_ROW diff --git a/src/odbc/include/ignite/odbc/jni/documentdb_connection_properties.h b/src/odbc/include/ignite/odbc/jni/documentdb_connection_properties.h index e83d807ea..0898504b3 100644 --- a/src/odbc/include/ignite/odbc/jni/documentdb_connection_properties.h +++ b/src/odbc/include/ignite/odbc/jni/documentdb_connection_properties.h @@ -50,14 +50,14 @@ class DocumentDbConnectionProperties { DocumentDbConnectionProperties( SharedPointer< JniContext >& jniContext, SharedPointer< GlobalJObject >& connectionProperties) - : _jniContext(jniContext), _connectionProperties(connectionProperties) { + : jniContext_(jniContext), connectionProperties_(connectionProperties) { } /** The JNI context */ - SharedPointer< JniContext > _jniContext; + SharedPointer< JniContext > jniContext_; /** The DocumentDbConnectionProperties Java object */ - SharedPointer< GlobalJObject > _connectionProperties; + SharedPointer< GlobalJObject > connectionProperties_; }; } // namespace jni } // namespace odbc diff --git a/src/odbc/include/ignite/odbc/jni/documentdb_database_metadata.h b/src/odbc/include/ignite/odbc/jni/documentdb_database_metadata.h index 07bc84f40..2e7ea54ab 100644 --- a/src/odbc/include/ignite/odbc/jni/documentdb_database_metadata.h +++ b/src/odbc/include/ignite/odbc/jni/documentdb_database_metadata.h @@ -53,14 +53,14 @@ class DocumentDbDatabaseMetadata { DocumentDbDatabaseMetadata( SharedPointer< JniContext >& jniContext, SharedPointer< GlobalJObject >& databaseMetadata) - : _jniContext(jniContext), _databaseMetadata(databaseMetadata) { + : jniContext_(jniContext), databaseMetadata_(databaseMetadata) { } /** The JNI context */ - SharedPointer< JniContext > _jniContext; + SharedPointer< JniContext > jniContext_; /** The DocumentDbDatabaseMetadata Java object */ - SharedPointer< GlobalJObject > _databaseMetadata; + SharedPointer< GlobalJObject > databaseMetadata_; }; } // namespace jni } // namespace odbc diff --git a/src/odbc/include/ignite/odbc/jni/documentdb_query_mapping_service.h b/src/odbc/include/ignite/odbc/jni/documentdb_query_mapping_service.h index 6eec17ef3..07013f6e3 100644 --- a/src/odbc/include/ignite/odbc/jni/documentdb_query_mapping_service.h +++ b/src/odbc/include/ignite/odbc/jni/documentdb_query_mapping_service.h @@ -70,7 +70,7 @@ class DocumentDbQueryMappingService { */ DocumentDbQueryMappingService(SharedPointer< JniContext >& jniContext, SharedPointer< GlobalJObject >& queryMappingService) - : _jniContext(jniContext), _queryMappingService(queryMappingService) { + : jniContext_(jniContext), queryMappingService_(queryMappingService) { } /** @@ -84,10 +84,10 @@ class DocumentDbQueryMappingService { JniErrorInfo& errInfo); /** The JNI context */ - SharedPointer< JniContext > _jniContext; + SharedPointer< JniContext > jniContext_; /** The DocumentDbQueryMappingService Java object */ - SharedPointer< GlobalJObject > _queryMappingService; + SharedPointer< GlobalJObject > queryMappingService_; }; } // namespace jni } // namespace odbc diff --git a/src/odbc/include/ignite/odbc/jni/jdbc_column_metadata.h b/src/odbc/include/ignite/odbc/jni/jdbc_column_metadata.h index 486fe8ad6..a717a524b 100644 --- a/src/odbc/include/ignite/odbc/jni/jdbc_column_metadata.h +++ b/src/odbc/include/ignite/odbc/jni/jdbc_column_metadata.h @@ -44,6 +44,9 @@ class JdbcColumnMetadata { friend class DocumentDbQueryMappingService; public: + /** Constructs a default instance */ + JdbcColumnMetadata() = default; + /** * Destructs the JdbcColumnMetadata object. */ @@ -52,148 +55,148 @@ class JdbcColumnMetadata { /** * Gets the (zero-indexed) ordinal of the column in the table. */ - int32_t GetOrdinal() { - return _ordinal; + int32_t GetOrdinal() const { + return ordinal_; } /** * Gets the indicator of whether the column is auto incremented. */ - bool IsAutoIncrement() { - return _autoIncrement; + bool IsAutoIncrement() const { + return autoIncrement_; } /** * Gets the indicator of whether the column is case sensitive. */ - bool IsCaseSensitive() { - return _caseSensitive; + bool IsCaseSensitive() const { + return caseSensitive_; } /** * Gets the indicator of whether the column is searchable. */ - bool IsSearchable() { - return _searchable; + bool IsSearchable() const { + return searchable_; } /** * Gets the indicator of whether the column is a currency. */ - bool IsCurrency() { - return _currency; + bool IsCurrency() const { + return currency_; } /** * Gets the indicator of whether the column is nullable, non-nullable or unknown-nullable. */ - int32_t GetNullable() { - return _nullable; + int32_t GetNullable() const { + return nullable_; } /** * Gets the indicator of whether the column is signed numeric value. */ - bool IsSigned() { - return _signed; + bool IsSigned() const { + return signed_; } /** * Gets the display size for the column. */ - int32_t GetColumnDisplaySize() { - return _columnDisplaySize; + int32_t GetColumnDisplaySize() const { + return columnDisplaySize_; } /** * Gets the label for the column. */ - boost::optional< std::string > GetColumnLabel() { - return _columnLabel; + boost::optional< std::string > GetColumnLabel() const { + return columnLabel_; } /** * Gets the name of the column. */ - boost::optional< std::string > GetColumnName() { - return _columnName; + boost::optional< std::string > GetColumnName() const { + return columnName_; } /** * Gets the schema name (if any) for the column. */ - boost::optional< std::string > GetSchemaName() { - return _schemaName; + boost::optional< std::string > GetSchemaName() const { + return schemaName_; } /** * Gets the precision (i.e. length) of the value. */ - int32_t GetPrecision() { - return _precision; + int32_t GetPrecision() const { + return precision_; } /** * Gets the scale of the numeric value. */ - int32_t GetScale() { - return _scale; + int32_t GetScale() const { + return scale_; } /** * Gets the name of the table the column belongs. */ - boost::optional< std::string > GetTableName() { - return _tableName; + boost::optional< std::string > GetTableName() const { + return tableName_; } /** * Gets the name of the catalog (if any) the column belongs. */ - boost::optional< std::string > GetCatalogName() { - return _catalogName; + boost::optional< std::string > GetCatalogName() const { + return catalogName_; } /** * Gets the data type of the column. See JDBC_TYPE_* */ - int32_t GetColumnType() { - return _columnType; + int32_t GetColumnType() const { + return columnType_; } /** * Gets the type name of the column. */ - boost::optional< std::string > GetColumnTypeName() { - return _columnTypeName; + boost::optional< std::string > GetColumnTypeName() const { + return columnTypeName_; } /** * Gets the indicator of whether the column is read only. */ - bool IsReadOnly() { - return _readOnly; + bool IsReadOnly() const { + return readOnly_; } /** * Gets the indicator of whether the column is writable. */ - bool IsWritable() { - return _writable; + bool IsWritable() const { + return writable_; } /** * Gets the indicator of whether the column is definitely writable. */ - bool IsDefinitelyWritable() { - return _definitelyWritable; + bool IsDefinitelyWritable() const { + return definitelyWritable_; } /** * Gets the Java class name for the column. */ - boost::optional< std::string > GetColumnClassName() { - return _columnClassName; + boost::optional< std::string > GetColumnClassName() const { + return columnClassName_; } @@ -214,51 +217,51 @@ class JdbcColumnMetadata { boost::optional< std::string > columnTypeName, bool readOnly, bool writable, bool definitelyWritable, boost::optional< std::string > columnClassName) - : _ordinal(ordinal), - _autoIncrement(autoIncrement), - _caseSensitive(caseSensitive), - _searchable(searchable), - _currency(currency), - _nullable(nullable), - _signed(isSigned), - _columnDisplaySize(columnDisplaySize), - _columnLabel(columnLabel), - _columnName(columnName), - _schemaName(schemaName), - _precision(precision), - _scale(scale), - _tableName(tableName), - _catalogName(catalogName), - _columnType(columnType), - _columnTypeName(columnTypeName), - _readOnly(readOnly), - _writable(writable), - _definitelyWritable(definitelyWritable), - _columnClassName(columnClassName) { + : ordinal_(ordinal), + autoIncrement_(autoIncrement), + caseSensitive_(caseSensitive), + searchable_(searchable), + currency_(currency), + nullable_(nullable), + signed_(isSigned), + columnDisplaySize_(columnDisplaySize), + columnLabel_(columnLabel), + columnName_(columnName), + schemaName_(schemaName), + precision_(precision), + scale_(scale), + tableName_(tableName), + catalogName_(catalogName), + columnType_(columnType), + columnTypeName_(columnTypeName), + readOnly_(readOnly), + writable_(writable), + definitelyWritable_(definitelyWritable), + columnClassName_(columnClassName) { // No-op } - int32_t _ordinal; - bool _autoIncrement; - bool _caseSensitive; - bool _searchable; - bool _currency; - int32_t _nullable; - bool _signed; - int32_t _columnDisplaySize; - boost::optional< std::string > _columnLabel; - boost::optional< std::string > _columnName; - boost::optional< std::string > _schemaName; - int32_t _precision; - int32_t _scale; - boost::optional< std::string > _tableName; - boost::optional< std::string > _catalogName; - int32_t _columnType; - boost::optional< std::string > _columnTypeName; - bool _readOnly; - bool _writable; - bool _definitelyWritable; - boost::optional< std::string > _columnClassName; + int32_t ordinal_; + bool autoIncrement_; + bool caseSensitive_; + bool searchable_; + bool currency_; + int32_t nullable_; + bool signed_; + int32_t columnDisplaySize_; + boost::optional< std::string > columnLabel_; + boost::optional< std::string > columnName_; + boost::optional< std::string > schemaName_; + int32_t precision_; + int32_t scale_; + boost::optional< std::string > tableName_; + boost::optional< std::string > catalogName_; + int32_t columnType_; + boost::optional< std::string > columnTypeName_; + bool readOnly_; + bool writable_; + bool definitelyWritable_; + boost::optional< std::string > columnClassName_; }; } // namespace jni } // namespace odbc diff --git a/src/odbc/include/ignite/odbc/query/data_query.h b/src/odbc/include/ignite/odbc/query/data_query.h index 57a024bac..7877b578b 100644 --- a/src/odbc/include/ignite/odbc/query/data_query.h +++ b/src/odbc/include/ignite/odbc/query/data_query.h @@ -19,7 +19,7 @@ #define _IGNITE_ODBC_QUERY_DATA_QUERY #include "ignite/odbc/app/parameter_set.h" -#include "ignite/odbc/cursor.h" +#include "ignite/odbc/documentdb_cursor.h" #include "ignite/odbc/query/query.h" #include "ignite/odbc/jni/documentdb_mql_query_context.h" @@ -120,19 +120,12 @@ class DataQuery : public Query { * @return SQL query string. */ const std::string& GetSql() const { - return sql; + return sql_; } private: IGNITE_NO_COPY_ASSIGNMENT(DataQuery); - /** - * Check whether all cursors are closed remotely. - * - * @return true, if all cursors closed remotely. - */ - bool IsClosedRemotely() const; - /** * Make query prepare request and use response to set internal * state. @@ -223,34 +216,25 @@ class DataQuery : public Query { SqlResult::Type InternalClose(); /** Connection associated with the statement. */ - Connection& connection; + Connection& connection_; /** SQL Query. */ - std::string sql; + std::string sql_; /** Parameter bindings. */ - const app::ParameterSet& params; + const app::ParameterSet& params_; /** Result set metadata is available */ - bool resultMetaAvailable; + bool resultMetaAvailable_ = false; /** Result set metadata. */ - meta::ColumnMetaVector resultMeta; + meta::ColumnMetaVector resultMeta_{}; /** Cursor. */ - std::unique_ptr< Cursor > cursor; - - /** Number of rows affected. */ - std::vector< int64_t > rowsAffected; - - /** Rows affected index. */ - size_t rowsAffectedIdx; - - /** Cached next result page. */ - std::shared_ptr< ResultPage > cachedNextPage; + std::unique_ptr< DocumentDbCursor > cursor_{}; /** Timeout. */ - int32_t& timeout; + int32_t& timeout_; }; } // namespace query } // namespace odbc diff --git a/src/odbc/include/ignite/odbc/query/streaming_query.h b/src/odbc/include/ignite/odbc/query/streaming_query.h deleted file mode 100644 index 255f764bc..000000000 --- a/src/odbc/include/ignite/odbc/query/streaming_query.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _IGNITE_ODBC_QUERY_STREAMING_QUERY -#define _IGNITE_ODBC_QUERY_STREAMING_QUERY - -#include "ignite/odbc/app/parameter_set.h" -#include "ignite/odbc/query/query.h" - -namespace ignite { -namespace odbc { -/** Connection forward-declaration. */ -class Connection; - -namespace query { -/** - * Streaming Query. - */ -class StreamingQuery : public Query { - public: - /** - * Constructor. - * - * @param diag Diagnostics collector. - * @param connection Associated connection. - * @param params SQL params. - */ - StreamingQuery(diagnostic::DiagnosableAdapter& diag, Connection& connection, - const app::ParameterSet& params); - - /** - * Destructor. - */ - virtual ~StreamingQuery(); - - /** - * Execute query. - * - * @return True on success. - */ - virtual SqlResult::Type Execute(); - - /** - * Get column metadata. - * - * @return Column metadata. - */ - virtual const meta::ColumnMetaVector* GetMeta(); - - /** - * Fetch next result row to application buffers. - * - * @param columnBindings Application buffers to put data to. - * @return Operation result. - */ - virtual SqlResult::Type FetchNextRow(app::ColumnBindingMap& columnBindings); - - /** - * Get data of the specified column in the result set. - * - * @param columnIdx Column index. - * @param buffer Buffer to put column data to. - * @return Operation result. - */ - virtual SqlResult::Type GetColumn(uint16_t columnIdx, - app::ApplicationDataBuffer& buffer); - - /** - * Close query. - * - * @return Result. - */ - virtual SqlResult::Type Close(); - - /** - * Check if data is available. - * - * @return True if data is available. - */ - virtual bool DataAvailable() const; - - /** - * Get number of rows affected by the statement. - * - * @return Number of rows affected by the statement. - */ - virtual int64_t AffectedRows() const; - - /** - * Move to the next result set. - * - * @return Operation result. - */ - virtual SqlResult::Type NextResultSet(); - - /** - * Get SQL query string. - * - * @return SQL query string. - */ - const std::string& GetSql() const { - return sql; - } - - /** - * Prepare query for execution in a streaming mode. - * - * @param query Query. - */ - void PrepareQuery(const std::string& query) { - sql = query; - } - - private: - IGNITE_NO_COPY_ASSIGNMENT(StreamingQuery); - - /** Connection associated with the statement. */ - Connection& connection; - - /** SQL Query. */ - std::string sql; - - /** Parameter bindings. */ - const app::ParameterSet& params; -}; -} // namespace query -} // namespace odbc -} // namespace ignite - -#endif //_IGNITE_ODBC_QUERY_STREAMING_QUERY diff --git a/src/odbc/include/ignite/odbc/statement.h b/src/odbc/include/ignite/odbc/statement.h index 6fe66bc79..fa52908a8 100644 --- a/src/odbc/include/ignite/odbc/statement.h +++ b/src/odbc/include/ignite/odbc/statement.h @@ -475,13 +475,6 @@ class Statement : public diagnostic::DiagnosableAdapter { */ SqlResult::Type InternalClose(); - /** - * Stop streaming. - * - * @return Operation result. - */ - SqlResult::Type StopStreaming(); - /** * Process internal SQL command. * @@ -490,13 +483,6 @@ class Statement : public diagnostic::DiagnosableAdapter { */ SqlResult::Type ProcessInternalCommand(const std::string& query); - /** - * Check if the streaming is active currently. - * - * @return @c true, if the streaming is active. - */ - bool IsStreamingActive() const; - /** * Prepare SQL query. * @@ -520,13 +506,6 @@ class Statement : public diagnostic::DiagnosableAdapter { */ SqlResult::Type InternalExecuteSqlQuery(); - /** - * Process internal query. - * - * @return Operation result. - */ - SqlResult::Type ProcessInternalQuery(); - /** * Fetch query result row with offset * @param orientation Fetch type diff --git a/src/odbc/src/app/application_data_buffer.cpp b/src/odbc/src/app/application_data_buffer.cpp index a8b5baca5..b05f89647 100644 --- a/src/odbc/src/app/application_data_buffer.cpp +++ b/src/odbc/src/app/application_data_buffer.cpp @@ -309,7 +309,7 @@ ConversionResult::Type ApplicationDataBuffer::PutStrToStrBuffer( } ConversionResult::Type ApplicationDataBuffer::PutRawDataToBuffer( - void* data, size_t len, int32_t& written) { + const void* data, size_t len, int32_t& written) { SqlLen iLen = static_cast< SqlLen >(len); SqlLen* resLenPtr = GetResLen(); @@ -329,11 +329,18 @@ ConversionResult::Type ApplicationDataBuffer::PutRawDataToBuffer( : ConversionResult::AI_SUCCESS; } +ConversionResult::Type ApplicationDataBuffer::PutInt8(boost::optional< int8_t > value) { + if (value) + return PutInt8(*value); + else + return PutNull(); +} + ConversionResult::Type ApplicationDataBuffer::PutInt8(int8_t value) { return PutNum(value); } -ConversionResult::Type ApplicationDataBuffer::PutOptInt16( +ConversionResult::Type ApplicationDataBuffer::PutInt16( boost::optional< int16_t > value) { if (value) return PutInt16(*value); @@ -345,7 +352,7 @@ ConversionResult::Type ApplicationDataBuffer::PutInt16(int16_t value) { return PutNum(value); } -ConversionResult::Type ApplicationDataBuffer::PutOptInt32( +ConversionResult::Type ApplicationDataBuffer::PutInt32( boost::optional< int32_t > value) { if (value) return PutInt32(*value); @@ -357,19 +364,43 @@ ConversionResult::Type ApplicationDataBuffer::PutInt32(int32_t value) { return PutNum(value); } +ConversionResult::Type ApplicationDataBuffer::PutInt64( + boost::optional< int64_t > value) { + if (value) + return PutInt64(*value); + else + return PutNull(); +} + ConversionResult::Type ApplicationDataBuffer::PutInt64(int64_t value) { return PutNum(value); } +ConversionResult::Type ApplicationDataBuffer::PutFloat( + boost::optional< float > value) { + if (value) + return PutFloat(*value); + else + return PutNull(); +} + ConversionResult::Type ApplicationDataBuffer::PutFloat(float value) { return PutNum(value); } +ConversionResult::Type ApplicationDataBuffer::PutDouble( + boost::optional< double > value) { + if (value) + return PutDouble(*value); + else + return PutNull(); +} + ConversionResult::Type ApplicationDataBuffer::PutDouble(double value) { return PutNum(value); } -ConversionResult::Type ApplicationDataBuffer::PutOptString( +ConversionResult::Type ApplicationDataBuffer::PutString( const boost::optional< std::string >& value) { if (value) return PutString(*value); @@ -490,7 +521,7 @@ ConversionResult::Type ApplicationDataBuffer::PutGuid(const Guid& value) { return ConversionResult::AI_UNSUPPORTED_CONVERSION; } -ConversionResult::Type ApplicationDataBuffer::PutBinaryData(void* data, +ConversionResult::Type ApplicationDataBuffer::PutBinaryData(const void* data, size_t len, int32_t& written) { using namespace type_traits; @@ -504,7 +535,7 @@ ConversionResult::Type ApplicationDataBuffer::PutBinaryData(void* data, case OdbcNativeType::AI_CHAR: { std::stringstream converter; - uint8_t* dataBytes = reinterpret_cast< uint8_t* >(data); + auto dataBytes = reinterpret_cast< const uint8_t* >(data); for (size_t i = 0; i < len; ++i) { converter << std::hex << std::setfill('0') << std::setw(2) @@ -517,7 +548,7 @@ ConversionResult::Type ApplicationDataBuffer::PutBinaryData(void* data, case OdbcNativeType::AI_WCHAR: { std::wstringstream converter; - uint8_t* dataBytes = reinterpret_cast< uint8_t* >(data); + auto dataBytes = reinterpret_cast< const uint8_t* >(data); for (size_t i = 0; i < len; ++i) { converter << std::hex << std::setfill(L'0') << std::setw(2) @@ -545,6 +576,14 @@ ConversionResult::Type ApplicationDataBuffer::PutNull() { return ConversionResult::AI_SUCCESS; } +ConversionResult::Type ApplicationDataBuffer::PutDecimal( + const boost::optional< common::Decimal >& value) { + if (value) + return PutDecimal(*value); + else + return PutNull(); +} + ConversionResult::Type ApplicationDataBuffer::PutDecimal( const common::Decimal& value) { using namespace type_traits; @@ -627,6 +666,14 @@ ConversionResult::Type ApplicationDataBuffer::PutDecimal( return ConversionResult::AI_UNSUPPORTED_CONVERSION; } +ConversionResult::Type ApplicationDataBuffer::PutDate( + const boost::optional< Date >& value) { + if (value) + return PutDate(*value); + else + return PutNull(); +} + ConversionResult::Type ApplicationDataBuffer::PutDate(const Date& value) { using namespace type_traits; @@ -740,6 +787,14 @@ ConversionResult::Type ApplicationDataBuffer::PutDate(const Date& value) { return ConversionResult::AI_UNSUPPORTED_CONVERSION; } +ConversionResult::Type ApplicationDataBuffer::PutTimestamp( + const boost::optional< Timestamp >& value) { + if (value) + return PutTimestamp(*value); + else + return PutNull(); +} + ConversionResult::Type ApplicationDataBuffer::PutTimestamp( const Timestamp& value) { using namespace type_traits; @@ -856,6 +911,13 @@ ConversionResult::Type ApplicationDataBuffer::PutTimestamp( return ConversionResult::AI_UNSUPPORTED_CONVERSION; } +ConversionResult::Type ApplicationDataBuffer::PutTime(const boost::optional< Time >& value) { + if (value) + return PutTime(*value); + else + return PutNull(); +} + ConversionResult::Type ApplicationDataBuffer::PutTime(const Time& value) { using namespace type_traits; diff --git a/src/odbc/src/connection.cpp b/src/odbc/src/connection.cpp index 70f93ba77..0bf53a839 100644 --- a/src/odbc/src/connection.cpp +++ b/src/odbc/src/connection.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -74,18 +75,18 @@ struct OdbcProtocolHeader { namespace ignite { namespace odbc { -Connection::Connection(Environment* env) : env(env), info(config) { +Connection::Connection(Environment* env) : env_(env), info_(config_) { // No-op } Connection::~Connection() { Close(); - _jniContext = nullptr; + jniContext_ = nullptr; Deinit(); } const config::ConnectionInfo& Connection::GetInfo() const { - return info; + return info_; } void Connection::GetInfo(config::ConnectionInfo::InfoType type, void* buf, @@ -152,16 +153,16 @@ SqlResult::Type Connection::InternalEstablish( const config::Configuration& cfg) { using ssl::SslMode; - config = cfg; + config_ = cfg; - if (_connection.IsValid()) { + if (connection_.IsValid()) { AddStatusRecord(SqlState::S08002_ALREADY_CONNECTED, "Already connected."); return SqlResult::AI_ERROR; } try { - config.Validate(); + config_.Validate(); } catch (const OdbcError& err) { AddStatusRecord(err); return SqlResult::AI_ERROR; @@ -188,11 +189,11 @@ void Connection::Release() { } void Connection::Deregister() { - env->DeregisterConnection(this); + env_->DeregisterConnection(this); } SqlResult::Type Connection::InternalRelease() { - if (!_connection.IsValid()) { + if (!connection_.IsValid()) { AddStatusRecord(SqlState::S08003_NOT_CONNECTED, "Connection is not open."); // It is important to return SUCCESS_WITH_INFO and not ERROR here, as if we @@ -207,14 +208,14 @@ SqlResult::Type Connection::InternalRelease() { } void Connection::Close() { - if (_jniContext.IsValid()) { - if (_connection.IsValid()) { + if (jniContext_.IsValid()) { + if (connection_.IsValid()) { JniErrorInfo errInfo; - _connection.Get()->Close(errInfo); + connection_.Get()->Close(errInfo); if (errInfo.code != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { // TODO: Determine if we need to error check the close. } - _connection = nullptr; + connection_ = nullptr; } } } @@ -228,13 +229,13 @@ Statement* Connection::CreateStatement() { } SharedPointer< DatabaseMetaData > Connection::GetMetaData(IgniteError& err) { - if (!_connection.IsValid()) { + if (!connection_.IsValid()) { err = IgniteError(IgniteError::IGNITE_ERR_ILLEGAL_STATE, "Must be connected."); return nullptr; } JniErrorInfo errInfo; - auto databaseMetaData = _connection.Get()->GetMetaData(errInfo); + auto databaseMetaData = connection_.Get()->GetMetaData(errInfo); if (!databaseMetaData.IsValid()) { std::string message = errInfo.errMsg; err = IgniteError(IgniteError::IGNITE_ERR_JNI_GET_DATABASE_METADATA, @@ -246,14 +247,14 @@ SharedPointer< DatabaseMetaData > Connection::GetMetaData(IgniteError& err) { SharedPointer< DocumentDbDatabaseMetadata > Connection::GetDatabaseMetadata( IgniteError& err) { - if (!_connection.IsValid()) { + if (!connection_.IsValid()) { err = IgniteError(IgniteError::IGNITE_ERR_ILLEGAL_STATE, "Must be connected."); return nullptr; } JniErrorInfo errInfo; auto documentDbDatabaseMetaData = - _connection.Get()->GetDatabaseMetadata(errInfo); + connection_.Get()->GetDatabaseMetadata(errInfo); if (!documentDbDatabaseMetaData.IsValid()) { std::string message = errInfo.errMsg; err = IgniteError(IgniteError::IGNITE_ERR_JNI_GET_DOCUMENTDB_DATABASE_METADATA, @@ -265,14 +266,14 @@ SharedPointer< DocumentDbDatabaseMetadata > Connection::GetDatabaseMetadata( SharedPointer< DocumentDbConnectionProperties > Connection::GetConnectionProperties( IgniteError& err) { - if (!_connection.IsValid()) { + if (!connection_.IsValid()) { err = IgniteError(IgniteError::IGNITE_ERR_ILLEGAL_STATE, "Must be connected."); return nullptr; } JniErrorInfo errInfo; auto connectionProperties = - _connection.Get()->GetConnectionProperties(errInfo); + connection_.Get()->GetConnectionProperties(errInfo); if (!connectionProperties.IsValid()) { std::string message = errInfo.errMsg; err = IgniteError(IgniteError::IGNITE_ERR_JNI_GET_DOCUMENTDB_CONNECTION_PROPERTIES, @@ -295,15 +296,15 @@ SqlResult::Type Connection::InternalCreateStatement(Statement*& statement) { } const std::string& Connection::GetSchema() const { - return config.GetDatabase(); + return config_.GetDatabase(); } const config::Configuration& Connection::GetConfiguration() const { - return config; + return config_; } bool Connection::IsAutoCommit() const { - return autoCommit; + return autoCommit_; } diagnostic::DiagnosticRecord Connection::CreateStatusRecord( @@ -318,15 +319,15 @@ void Connection::TransactionCommit() { } SqlResult::Type Connection::InternalTransactionCommit() { - std::string schema = config.GetDatabase(); + std::string schema = config_.GetDatabase(); app::ParameterSet empty; - QueryExecuteRequest req(schema, "COMMIT", empty, timeout, autoCommit); + QueryExecuteRequest req(schema, "COMMIT", empty, timeout_, autoCommit_); QueryExecuteResponse rsp; try { - bool sent = SyncMessage(req, rsp, timeout); + bool sent = SyncMessage(req, rsp, timeout_); if (!sent) { AddStatusRecord(SqlState::S08S01_LINK_FAILURE, @@ -352,15 +353,15 @@ void Connection::TransactionRollback() { } SqlResult::Type Connection::InternalTransactionRollback() { - std::string schema = config.GetDatabase(); + std::string schema = config_.GetDatabase(); app::ParameterSet empty; - QueryExecuteRequest req(schema, "ROLLBACK", empty, timeout, autoCommit); + QueryExecuteRequest req(schema, "ROLLBACK", empty, timeout_, autoCommit_); QueryExecuteResponse rsp; try { - bool sent = SyncMessage(req, rsp, timeout); + bool sent = SyncMessage(req, rsp, timeout_); if (!sent) { AddStatusRecord(SqlState::S08S01_LINK_FAILURE, @@ -400,7 +401,7 @@ SqlResult::Type Connection::InternalGetAttribute(int attr, void* buf, case SQL_ATTR_CONNECTION_DEAD: { SQLUINTEGER* val = reinterpret_cast< SQLUINTEGER* >(buf); - *val = _connection.Get() ? SQL_CD_FALSE : SQL_CD_TRUE; + *val = connection_.Get() ? SQL_CD_FALSE : SQL_CD_TRUE; if (valueLen) *valueLen = SQL_IS_INTEGER; @@ -411,7 +412,7 @@ SqlResult::Type Connection::InternalGetAttribute(int attr, void* buf, case SQL_ATTR_CONNECTION_TIMEOUT: { SQLUINTEGER* val = reinterpret_cast< SQLUINTEGER* >(buf); - *val = static_cast< SQLUINTEGER >(timeout); + *val = static_cast< SQLUINTEGER >(timeout_); if (valueLen) *valueLen = SQL_IS_INTEGER; @@ -422,7 +423,7 @@ SqlResult::Type Connection::InternalGetAttribute(int attr, void* buf, case SQL_ATTR_LOGIN_TIMEOUT: { SQLUINTEGER* val = reinterpret_cast< SQLUINTEGER* >(buf); - *val = static_cast< SQLUINTEGER >(loginTimeout); + *val = static_cast< SQLUINTEGER >(loginTimeout_); if (valueLen) *valueLen = SQL_IS_INTEGER; @@ -433,7 +434,7 @@ SqlResult::Type Connection::InternalGetAttribute(int attr, void* buf, case SQL_ATTR_AUTOCOMMIT: { SQLUINTEGER* val = reinterpret_cast< SQLUINTEGER* >(buf); - *val = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF; + *val = autoCommit_ ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF; if (valueLen) *valueLen = SQL_IS_INTEGER; @@ -467,7 +468,7 @@ SqlResult::Type Connection::InternalSetAttribute(int attr, void* value, } case SQL_ATTR_CONNECTION_TIMEOUT: { - timeout = RetrieveTimeout(value); + timeout_ = RetrieveTimeout(value); if (GetDiagnosticRecords().GetStatusRecordsNumber() != 0) return SqlResult::AI_SUCCESS_WITH_INFO; @@ -476,7 +477,7 @@ SqlResult::Type Connection::InternalSetAttribute(int attr, void* value, } case SQL_ATTR_LOGIN_TIMEOUT: { - loginTimeout = RetrieveTimeout(value); + loginTimeout_ = RetrieveTimeout(value); if (GetDiagnosticRecords().GetStatusRecordsNumber() != 0) return SqlResult::AI_SUCCESS_WITH_INFO; @@ -495,7 +496,7 @@ SqlResult::Type Connection::InternalSetAttribute(int attr, void* value, return SqlResult::AI_ERROR; } - autoCommit = mode == SQL_AUTOCOMMIT_ON; + autoCommit_ = mode == SQL_AUTOCOMMIT_ON; break; } @@ -512,13 +513,13 @@ SqlResult::Type Connection::InternalSetAttribute(int attr, void* value, } SqlResult::Type Connection::MakeRequestHandshake() { - HandshakeRequest req(config); + HandshakeRequest req(config_); HandshakeResponse rsp; try { // Workaround for some Linux systems that report connection on non-blocking // sockets as successful but fail to establish real connection. - bool sent = InternalSyncMessage(req, rsp, loginTimeout); + bool sent = InternalSyncMessage(req, rsp, loginTimeout_); if (!sent) { AddStatusRecord( @@ -556,7 +557,7 @@ SqlResult::Type Connection::MakeRequestHandshake() { } void Connection::EnsureConnected() { - if (_connection.IsValid()) + if (connection_.IsValid()) return; IgniteError err; @@ -572,7 +573,7 @@ void Connection::EnsureConnected() { } bool Connection::TryRestoreConnection(IgniteError& err) { - if (_connection.IsValid()) { + if (connection_.IsValid()) { return true; } @@ -587,14 +588,14 @@ bool Connection::TryRestoreConnection(IgniteError& err) { } SharedPointer< DocumentDbConnection > conn = new DocumentDbConnection(ctx); if (!conn.IsValid() - || conn.Get()->Open(config, errInfo) + || conn.Get()->Open(config_, errInfo) != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { std::string message = errInfo.errMsg; err = IgniteError(IgniteError::IGNITE_ERR_SECURE_CONNECTION_FAILURE, message.c_str()); } - _connection = conn; - bool connected = _connection.IsValid() && _connection.Get()->IsOpen() + connection_ = conn; + bool connected = connection_.IsValid() && connection_.Get()->IsOpen() && errInfo.code == JniErrorCode::IGNITE_JNI_ERR_SUCCESS; if (!connected) { @@ -617,7 +618,7 @@ bool Connection::GetInternalSSHTunnelPort(int32_t& localSSHTunnelPort, bool isSSHTunnelActive; JniErrorInfo errInfo; JniErrorCode success = - _connection.Get()->IsSshTunnelActive(isSSHTunnelActive, errInfo); + connection_.Get()->IsSshTunnelActive(isSSHTunnelActive, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { err = IgniteError(odbc::IgniteError::IGNITE_ERR_JVM_INIT, @@ -627,7 +628,7 @@ bool Connection::GetInternalSSHTunnelPort(int32_t& localSSHTunnelPort, if (isSSHTunnelActive) { JniErrorCode success = - _connection.Get()->GetSshLocalPort(localSSHTunnelPort, errInfo); + connection_.Get()->GetSshLocalPort(localSSHTunnelPort, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { err = IgniteError(odbc::IgniteError::IGNITE_ERR_JVM_INIT, errInfo.errMsg.c_str()); @@ -645,18 +646,18 @@ std::string Connection::FormatMongoCppConnectionString( // localSSHTunnelPort == 0 means that internal SSH tunnel option was not set if (localSSHTunnelPort == 0) { - host = config.GetHostname(); - port = common::LexicalCast< std::string >(config.GetPort()); + host = config_.GetHostname(); + port = common::LexicalCast< std::string >(config_.GetPort()); } std::string mongoConnectionString; mongoConnectionString = "mongodb:"; - mongoConnectionString.append("//" + config.GetUser()); - mongoConnectionString.append(":" + config.GetPassword()); + mongoConnectionString.append("//" + config_.GetUser()); + mongoConnectionString.append(":" + config_.GetPassword()); mongoConnectionString.append("@" + host); mongoConnectionString.append(":" + port); mongoConnectionString.append("/admin"); - if (config.IsTls()) { + if (config_.IsTls()) { mongoConnectionString.append("?tlsAllowInvalidHostnames=true"); } // tls configuration is handled using tls_options in connectionCPP @@ -667,7 +668,7 @@ std::string Connection::FormatMongoCppConnectionString( } SharedPointer< JniContext > Connection::GetJniContext(JniErrorInfo &errInfo) { - if (!_jniContext.IsValid()) { + if (!jniContext_.IsValid()) { // Resolve DOCUMENTDB_HOME. std::string home = jni::ResolveDocumentDbHome(); @@ -682,10 +683,10 @@ SharedPointer< JniContext > Connection::GetJniContext(JniErrorInfo &errInfo) { // Create the context SharedPointer< JniContext > ctx(JniContext::Create( - &opts[0], static_cast< int >(opts.size()), JniHandlers(), errInfo)); - _jniContext = ctx; + &opts_[0], static_cast< int >(opts_.size()), JniHandlers(), errInfo)); + jniContext_ = ctx; } - return _jniContext; + return jniContext_; } /** @@ -697,15 +698,15 @@ SharedPointer< JniContext > Connection::GetJniContext(JniErrorInfo &errInfo) { */ void Connection::SetJvmOptions(const std::string& cp) { Deinit(); - BuildJvmOptions(cp, opts); + BuildJvmOptions(cp, opts_); } /** * Deallocates all allocated data. */ void Connection::Deinit() { - std::for_each(opts.begin(), opts.end(), ReleaseChars); - opts.clear(); + std::for_each(opts_.begin(), opts_.end(), ReleaseChars); + opts_.clear(); } int32_t Connection::RetrieveTimeout(void* value) { @@ -715,7 +716,7 @@ int32_t Connection::RetrieveTimeout(void* value) { if (uTimeout > INT32_MAX) { std::stringstream ss; - ss << "Value is too big: " << uTimeout << ", changing to " << timeout + ss << "Value is too big: " << uTimeout << ", changing to " << timeout_ << "."; AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED, ss.str()); @@ -738,19 +739,19 @@ bool Connection::ConnectCPPDocumentDB(int32_t localSSHTunnelPort, FormatMongoCppConnectionString(localSSHTunnelPort); mongocxx::options::client client_options; mongocxx::options::tls tls_options; - if (config.IsTls()) { + if (config_.IsTls()) { // TO-DO Adapt to use certificates // https://bitquill.atlassian.net/browse/AD-598 tls_options.allow_invalid_certificates(true); client_options.tls_opts(tls_options); } - auto client1 = mongocxx::client{mongocxx::uri{mongoCPPConnectionString}, - client_options}; - - std::string database = config.GetDatabase(); + + mongoClient_ = std::make_shared< mongocxx::client >( + mongocxx::uri(mongoCPPConnectionString), client_options); + std::string database = config_.GetDatabase(); bsoncxx::builder::stream::document ping; ping << "ping" << 1; - auto db = client1[database]; + auto db = (*mongoClient_.get())[database]; auto result = db.run_command(ping.view()); if (result.view()["ok"].get_double() != 1) { @@ -760,10 +761,15 @@ bool Connection::ConnectCPPDocumentDB(int32_t localSSHTunnelPort, } return true; - } catch (const std::exception& xcp) { + } catch (const mongocxx::exception& xcp) { + std::stringstream message; + message << "Unable to establish connection with DocumentDB." + << " code: " << xcp.code().value() + << " messagge: " << xcp.code().message() + << " cause: " << xcp.what(); err = odbc::IgniteError( odbc::IgniteError::IGNITE_ERR_SECURE_CONNECTION_FAILURE, - "Unable to establish connection with DocumentDB."); + message.str().c_str()); return false; } } diff --git a/src/odbc/src/documentdb_column.cpp b/src/odbc/src/documentdb_column.cpp new file mode 100644 index 000000000..4ebe10203 --- /dev/null +++ b/src/odbc/src/documentdb_column.cpp @@ -0,0 +1,780 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "ignite/odbc/documentdb_column.h" +#include +#include "ignite/odbc/utility.h" +#include "bsoncxx/types.hpp" +#include "bsoncxx/json.hpp" + +namespace { +using ignite::odbc::app::ApplicationDataBuffer; +using ignite::odbc::app::ConversionResult; +using namespace ignite::odbc::impl::interop; +using namespace ignite::odbc::impl::binary; +using ignite::odbc::jni::JdbcColumnMetadata; + +bool GetObjectLength(InteropInputStream& stream, int32_t& len) { + InteropStreamPositionGuard< InteropInputStream > guard(stream); + + int8_t hdr = stream.ReadInt8(); + + switch (hdr) { + case IGNITE_TYPE_BINARY: { + // Header field + Length field + Object itself + Offset field + len = 1 + 4 + stream.ReadInt32() + 4; + + break; + } + + case IGNITE_TYPE_OBJECT: { + int8_t protoVer = stream.ReadInt8(); + + if (protoVer != IGNITE_PROTO_VER) + return false; + + // Skipping flags, typeId and hash code + len = stream.ReadInt32(stream.Position() + 2 + 4 + 4); + + break; + } + + default: + return false; + } + + return true; +} + +/** + * Read column header and restores position if the column is of + * complex type. + * @return Column type header. + */ +int8_t ReadColumnHeader(InteropInputStream& stream) { + using namespace ignite::odbc::impl::binary; + + int32_t headerPos = stream.Position(); + + int8_t hdr = stream.ReadInt8(); + + // Check if we need to restore position - to read complex types + // stream should have unread header, but for primitive types it + // should not. + switch (hdr) { + case IGNITE_TYPE_BYTE: + case IGNITE_TYPE_SHORT: + case IGNITE_TYPE_CHAR: + case IGNITE_TYPE_INT: + case IGNITE_TYPE_LONG: + case IGNITE_TYPE_FLOAT: + case IGNITE_TYPE_DOUBLE: + case IGNITE_TYPE_BOOL: + case IGNITE_HDR_NULL: + case IGNITE_TYPE_ARRAY_BYTE: { + // No-op. + break; + } + + default: { + // Restoring position. + stream.Position(headerPos); + break; + } + } + + return hdr; +} +} // namespace + +namespace ignite { +namespace odbc { + +DocumentDbColumn::DocumentDbColumn(const DocumentDbColumn& other) + : document_(other.document_), + columnMetadata_(other.columnMetadata_), + path_(other.path_), + type_(other.type_), + size_(other.size_) { + // No-op. +} + +DocumentDbColumn::~DocumentDbColumn() { + // No-op. +} + +DocumentDbColumn::DocumentDbColumn(bsoncxx::document::view& document, + JdbcColumnMetadata& columnMetadata, std::string& path) + : type_(columnMetadata.GetColumnType()), + document_(document), + columnMetadata_(columnMetadata), + path_(path) { +} + +int64_t ToValidLong(int64_t value, ConversionResult::Type& convRes, int64_t max, + int64_t min) { + int64_t intValue = value; + if (intValue > max) { + intValue = max; + convRes = ConversionResult::AI_FAILURE; + } else if (intValue < min) { + intValue = min; + convRes = ConversionResult::AI_FAILURE; + } + return intValue; +} + +int64_t ToValidLong(std::string const& value, ConversionResult::Type& convRes, + int64_t max, int64_t min) { + int64_t intValue = 0; + try { + intValue = + ToValidLong(std::stoll(value.c_str(), nullptr, 0), convRes, max, min); + } catch (std::invalid_argument const&) { + convRes = ConversionResult::AI_FAILURE; + } catch (std::out_of_range const&) { + convRes = ConversionResult::AI_FAILURE; + } + return intValue; +} + +ConversionResult::Type DocumentDbColumn::PutInt8( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< int8_t > value{}; + switch (docType) { + case bsoncxx::type::k_int32: + value = + ToValidLong(element.get_int32().value, convRes, INT8_MAX, INT8_MIN); + break; + case bsoncxx::type::k_int64: + value = + ToValidLong(element.get_int64().value, convRes, INT8_MAX, INT8_MIN); + break; + case bsoncxx::type::k_double: + if (element.get_double().value > INT8_MAX + || element.get_double().value < INT8_MIN) { + convRes = ConversionResult::AI_FAILURE; + } else { + value = element.get_double().value != 0 ? 1 : 0; + } + break; + case bsoncxx::type::k_decimal128: + value = ToValidLong(element.get_decimal128().value.to_string(), convRes, + INT8_MAX, INT8_MIN); + break; + case bsoncxx::type::k_utf8: + value = ToValidLong(element.get_utf8().value.to_string(), convRes, + INT8_MAX, INT8_MIN); + break; + case bsoncxx::type::k_bool: + value = element.get_bool().value ? 1 : 0; + break; + case bsoncxx::type::k_null: + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::Type::AI_SUCCESS) { + dataBuf.PutInt8(value); + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutInt16( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< int16_t > value{}; + switch (docType) { + case bsoncxx::type::k_int32: + value = + ToValidLong(element.get_int32().value, convRes, INT16_MAX, INT16_MIN); + break; + case bsoncxx::type::k_int64: + value = + ToValidLong(element.get_int64().value, convRes, INT16_MAX, INT16_MIN); + break; + case bsoncxx::type::k_double: + if (element.get_double().value > INT16_MAX + || element.get_double().value < INT16_MIN) { + convRes = ConversionResult::AI_FAILURE; + } else { + value = element.get_double().value; + } + break; + case bsoncxx::type::k_decimal128: + value = ToValidLong(element.get_decimal128().value.to_string(), convRes, INT16_MAX, + INT16_MIN); + break; + case bsoncxx::type::k_utf8: + value = ToValidLong(element.get_utf8().value.to_string(), convRes, + INT16_MAX, INT16_MIN); + break; + case bsoncxx::type::k_bool: + value = element.get_bool().value ? 1 : 0; + break; + case bsoncxx::type::k_date: + value = element.get_date().value.count(); + break; + case bsoncxx::type::k_timestamp: + value = element.get_timestamp().timestamp; + break; + case bsoncxx::type::k_null: + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::Type::AI_SUCCESS) { + dataBuf.PutInt16(value); + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutInt32( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< int32_t > value{}; + switch (docType) { + case bsoncxx::type::k_int32: + value = element.get_int32().value; + break; + case bsoncxx::type::k_int64: + value = + ToValidLong(element.get_int64().value, convRes, INT32_MAX, INT32_MIN); + break; + case bsoncxx::type::k_double: + if (element.get_double().value > INT32_MAX + || element.get_double().value < INT32_MIN) { + convRes = ConversionResult::AI_FAILURE; + } else { + value = element.get_double().value; + } + break; + case bsoncxx::type::k_decimal128: + value = ToValidLong(element.get_decimal128().value.to_string(), convRes, + INT32_MAX, INT32_MIN); + break; + case bsoncxx::type::k_utf8: + value = ToValidLong(element.get_utf8().value.to_string(), convRes, + INT32_MAX, INT32_MIN); + break; + case bsoncxx::type::k_bool: + value = element.get_bool().value ? 1 : 0; + break; + case bsoncxx::type::k_date: + value = element.get_date().value.count(); + break; + case bsoncxx::type::k_timestamp: + value = element.get_timestamp().timestamp; + break; + case bsoncxx::type::k_null: + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::Type::AI_SUCCESS) { + dataBuf.PutInt32(value); + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutInt64( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< int64_t > value{}; + switch (docType) { + case bsoncxx::type::k_int32: + value = element.get_int32().value; + break; + case bsoncxx::type::k_int64: + value = element.get_int64().value; + break; + case bsoncxx::type::k_double: + if (element.get_double().value > INT64_MAX + || element.get_double().value < INT64_MIN) { + convRes = ConversionResult::AI_FAILURE; + } else { + value = element.get_double().value; + } + break; + case bsoncxx::type::k_decimal128: + value = ToValidLong(element.get_decimal128().value.to_string(), convRes, + INT64_MAX, INT64_MIN); + break; + case bsoncxx::type::k_utf8: + value = ToValidLong(element.get_utf8().value.to_string(), convRes, + INT64_MAX, INT64_MIN); + break; + case bsoncxx::type::k_bool: + value = element.get_bool().value ? 1 : 0; + break; + case bsoncxx::type::k_date: + value = element.get_date().value.count(); + break; + case bsoncxx::type::k_timestamp: + value = element.get_timestamp().timestamp; + break; + case bsoncxx::type::k_null: + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::Type::AI_SUCCESS) { + dataBuf.PutInt64(value); + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutFloat( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< float > value{}; + switch (docType) { + case bsoncxx::type::k_int32: + value = element.get_int32().value; + break; + case bsoncxx::type::k_int64: + value = element.get_int64().value; + break; + case bsoncxx::type::k_double: + value = element.get_double().value; + break; + case bsoncxx::type::k_decimal128: + value = std::stof(element.get_decimal128().value.to_string()); + break; + case bsoncxx::type::k_utf8: + value = std::stof(element.get_utf8().value.to_string()); + break; + case bsoncxx::type::k_bool: + value = element.get_bool().value ? 1 : 0; + break; + case bsoncxx::type::k_date: + value = element.get_date().value.count(); + break; + case bsoncxx::type::k_timestamp: + value = element.get_timestamp().timestamp; + break; + case bsoncxx::type::k_null: + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::Type::AI_SUCCESS) { + dataBuf.PutFloat(value); + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutDouble( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< double > value{}; + switch (docType) { + case bsoncxx::type::k_int32: + value = element.get_int32().value; + break; + case bsoncxx::type::k_int64: + value = element.get_int64().value; + break; + case bsoncxx::type::k_double: + value = element.get_double().value; + break; + case bsoncxx::type::k_decimal128: + value = std::stod(element.get_decimal128().value.to_string()); + break; + case bsoncxx::type::k_utf8: + value = std::stod(element.get_utf8().value.to_string()); + break; + case bsoncxx::type::k_bool: + value = element.get_bool().value ? 1 : 0; + break; + case bsoncxx::type::k_date: + value = element.get_date().value.count(); + break; + case bsoncxx::type::k_timestamp: + value = element.get_timestamp().timestamp; + break; + case bsoncxx::type::k_null: + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::Type::AI_SUCCESS) { + dataBuf.PutDouble(value); + } + return convRes; +} + +ConversionResult::Type + DocumentDbColumn::PutString( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< std::string > value{}; + switch (docType) { + case bsoncxx::type::k_int32: + value = std::to_string(element.get_int32().value); + break; + case bsoncxx::type::k_int64: + value = std::to_string(element.get_int64().value); + break; + case bsoncxx::type::k_double: + value = std::to_string(element.get_double().value); + break; + case bsoncxx::type::k_decimal128: + value = element.get_decimal128().value.to_string(); + break; + case bsoncxx::type::k_utf8: + value = element.get_utf8().value.to_string(); + break; + case bsoncxx::type::k_binary: { + std::stringstream ss; + ss << std::hex; + int32_t array_size = element.get_binary().size; + const uint8_t* data = element.get_binary().bytes; + for (int i = 0; i < array_size; i++) { + ss << std::setw(2) << std::setfill('0') << (int)data[i]; + } + value = ss.str(); + break; + } + case bsoncxx::type::k_oid: + value = element.get_oid().value.to_string(); + break; + case bsoncxx::type::k_bool: + value = std::to_string(element.get_bool().value); + break; + case bsoncxx::type::k_date: + value = std::to_string(std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(element.get_date().value))); + break; + case bsoncxx::type::k_timestamp: + value = std::to_string(element.get_timestamp().timestamp); + break; + case bsoncxx::type::k_null: + break; + case bsoncxx::type::k_maxkey: + value = "MAXKEY"; + break; + case bsoncxx::type::k_minkey: + value = "MINKEY"; + break; + case bsoncxx::type::k_document: + // probably need to convert to string + value = bsoncxx::to_json(element.get_document().value); + break; + case bsoncxx::type::k_array: + // probably need to convert to string + value = bsoncxx::to_json(element.get_array().value); + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::Type::AI_SUCCESS) { + dataBuf.PutString(value); + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutDecimal( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< common::Decimal > value{}; + switch (docType) { + case bsoncxx::type::k_int32: + value = common::Decimal(element.get_int32().value); + break; + case bsoncxx::type::k_int64: + value = common::Decimal(element.get_int64().value); + break; + case bsoncxx::type::k_double: + value = common::Decimal(std::to_string(element.get_double().value)); + break; + case bsoncxx::type::k_decimal128: + value = common::Decimal(element.get_decimal128().value.to_string()); + break; + case bsoncxx::type::k_utf8: + value = common::Decimal(element.get_utf8().value.to_string()); + break; + case bsoncxx::type::k_bool: + value = common::Decimal(element.get_bool().value ? 1 : 0); + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::AI_SUCCESS) { + dataBuf.PutDecimal(value); + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutTime( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< Time > value{}; + switch (docType) { + case bsoncxx::type::k_int64: + break; + value = Time(element.get_int64().value); + case bsoncxx::type::k_date: + value = Time(element.get_date().value.count()); + break; + case bsoncxx::type::k_timestamp: + // TODO: Determine if this is correct to milliseconds + // https://bitquill.atlassian.net/browse/AD-679 + value = Time(element.get_timestamp().timestamp); + break; + case bsoncxx::type::k_utf8: + // TODO: Determine if we could support reading data as string + // https://bitquill.atlassian.net/browse/AD-680 + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::AI_SUCCESS) { + dataBuf.PutTime(value); + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutDate( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< Date > value{}; + switch (docType) { + case bsoncxx::type::k_int64: + break; + value = Date(element.get_int64().value); + case bsoncxx::type::k_date: + value = Date(element.get_date().value.count()); + break; + case bsoncxx::type::k_timestamp: + // TODO: Determine if this is correct to milliseconds + // https://bitquill.atlassian.net/browse/AD-679 + value = Date(element.get_timestamp().timestamp); + break; + case bsoncxx::type::k_utf8: + // TODO: Determine if we could support reading data as string + // https://bitquill.atlassian.net/browse/AD-680 + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::AI_SUCCESS) { + dataBuf.PutDate(value); + + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutTimestamp( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::Type::AI_SUCCESS; + bsoncxx::type docType = element.type(); + boost::optional< Timestamp > value{}; + switch (docType) { + case bsoncxx::type::k_int64: + value = Timestamp(element.get_int64().value); + break; + case bsoncxx::type::k_date: + value = Timestamp(element.get_date().value.count()); + break; + case bsoncxx::type::k_timestamp: + // TODO: Determine if this is correct to milliseconds + // https://bitquill.atlassian.net/browse/AD-679 + value = Timestamp(element.get_timestamp().timestamp); + break; + case bsoncxx::type::k_utf8: + // TODO: Determine if we could support reading data as string + // https://bitquill.atlassian.net/browse/AD-680 + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::AI_SUCCESS) { + dataBuf.PutTimestamp(value); + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::PutBinaryData( + ApplicationDataBuffer& dataBuf, + bsoncxx::document::element const& element) const { + ConversionResult::Type convRes = ConversionResult::AI_SUCCESS; + bsoncxx::type docType = element.type(); + const void* value = nullptr; + size_t length = 0; + switch (docType) { + case bsoncxx::type::k_utf8: + value = element.get_utf8().value.to_string().c_str(); + length = element.get_utf8().value.to_string().length(); + break; + case bsoncxx::type::k_binary: + value = element.get_binary().bytes; + length = element.get_binary().size; + break; + case bsoncxx::type::k_oid: + value = element.get_oid().value.bytes(); + length = element.get_oid().value.size(); + break; + case bsoncxx::type::k_null: + break; + default: + convRes = ConversionResult::AI_UNSUPPORTED_CONVERSION; + break; + } + if (convRes == ConversionResult::Type::AI_SUCCESS) { + if (value) { + int32_t len_written = 0; + convRes = dataBuf.PutBinaryData(value, length, len_written); + } else { + convRes = dataBuf.PutNull(); + } + } + return convRes; +} + +ConversionResult::Type DocumentDbColumn::ReadToBuffer( + ApplicationDataBuffer& dataBuf) const { + + auto element = document_[path_]; + // Invalid (or missing) element is null + if (!element) { + dataBuf.PutNull(); + return ConversionResult::AI_SUCCESS; + } + + ConversionResult::Type convRes = ConversionResult::AI_SUCCESS; + + switch (type_) { + + case JDBC_TYPE_BOOLEAN: + case JDBC_TYPE_SMALLINT: { + convRes = PutInt8(dataBuf, element); + break; + } + + case JDBC_TYPE_TINYINT: { + convRes = PutInt16(dataBuf, element); + break; + } + + case JDBC_TYPE_INTEGER: { + convRes = PutInt32(dataBuf, element); + break; + } + + case JDBC_TYPE_BIGINT: { + convRes = PutInt64(dataBuf, element); + break; + } + + case JDBC_TYPE_FLOAT: { + convRes = PutFloat(dataBuf, element); + break; + } + + case JDBC_TYPE_DOUBLE: { + convRes = PutDouble(dataBuf, element); + break; + } + + case JDBC_TYPE_VARCHAR: + case JDBC_TYPE_CHAR: + case JDBC_TYPE_NCHAR: + case JDBC_TYPE_NVARCHAR: + case JDBC_TYPE_LONGVARCHAR: + case JDBC_TYPE_LONGNVARCHAR: { + convRes = PutString(dataBuf, element); + break; + } + + case JDBC_TYPE_NULL: { + convRes = dataBuf.PutNull(); + break; + } + + case JDBC_TYPE_BINARY: + case JDBC_TYPE_VARBINARY: { + convRes = PutBinaryData(dataBuf, element); + break; + } + + case JDBC_TYPE_DECIMAL: { + convRes = PutDecimal(dataBuf, element); + break; + } + + case JDBC_TYPE_DATE: { + convRes = PutDate(dataBuf, element); + break; + } + + case JDBC_TYPE_TIMESTAMP: { + convRes = PutTimestamp(dataBuf, element); + break; + } + + case JDBC_TYPE_TIME: { + convRes = PutTime(dataBuf, element); + break; + } + + default: + return ConversionResult::AI_UNSUPPORTED_CONVERSION; + } + + return convRes; +} +} // namespace odbc +} // namespace ignite diff --git a/src/odbc/src/documentdb_cursor.cpp b/src/odbc/src/documentdb_cursor.cpp new file mode 100644 index 000000000..3effd8814 --- /dev/null +++ b/src/odbc/src/documentdb_cursor.cpp @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ignite/odbc/documentdb_cursor.h" +#include "mongocxx/cursor.hpp" + +namespace ignite { +namespace odbc { +DocumentDbCursor::DocumentDbCursor(mongocxx::cursor& cursor, + std::vector< JdbcColumnMetadata >& columnMetadata, + std::vector< std::string >& paths) + : cursor_(std::move(cursor)), + iterator_(cursor_.begin()), + iteratorEnd_(cursor_.end()), + columnMetadata_(columnMetadata), + paths_(paths) { + // No-op. +} + +DocumentDbCursor::~DocumentDbCursor() { + currentRow_.release(); +} + +bool DocumentDbCursor::Increment() { + bool hasData = HasData(); + if (hasData) { + if (!isFirstRow_) { + iterator_++; + } else { + isFirstRow_ = false; + } + } + hasData = HasData(); + if (hasData) { + currentRow_.reset(new DocumentDbRow(*iterator_, columnMetadata_, paths_)); + } else { + currentRow_.reset(); + } + return hasData; +} + +bool DocumentDbCursor::HasData() const { + return iterator_ != iteratorEnd_; +} + +DocumentDbRow* DocumentDbCursor::GetRow() { + return currentRow_.get(); +} +} // namespace odbc +} // namespace ignite diff --git a/src/odbc/src/documentdb_row.cpp b/src/odbc/src/documentdb_row.cpp new file mode 100644 index 000000000..0ce49411b --- /dev/null +++ b/src/odbc/src/documentdb_row.cpp @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ignite/odbc/jni/jdbc_column_metadata.h" +#include "ignite/odbc/documentdb_row.h" +#include "ignite/odbc/utility.h" +#include "mongocxx/cursor.hpp" + +using namespace ignite::odbc::impl::interop; +using ignite::odbc::jni::JdbcColumnMetadata; + +namespace ignite { +namespace odbc { +// ASSUMPTION: iterator is not at the end. +DocumentDbRow::DocumentDbRow(bsoncxx::document::view const& document, + std::vector< JdbcColumnMetadata >& columnMetadata, + std::vector< std::string >& paths) + : pos(0), + size(columnMetadata.size()), + columns_(), + document_(document), + columnMetadata_(columnMetadata), + paths_(paths) { +} + +DocumentDbRow::~DocumentDbRow() { + // No-op. +} + +app::ConversionResult::Type DocumentDbRow::ReadColumnToBuffer( + uint32_t columnIdx, app::ApplicationDataBuffer& dataBuf) { + if (columnIdx > GetSize() || columnIdx < 1) + return app::ConversionResult::AI_FAILURE; + + if (!EnsureColumnDiscovered(columnIdx)) + return app::ConversionResult::AI_FAILURE; + + DocumentDbColumn const& column = GetColumn(columnIdx); + + return column.ReadToBuffer(dataBuf); +} + +bool DocumentDbRow::EnsureColumnDiscovered(int16_t columnIdx) { + if (columns_.size() == size) + return true; + + int64_t index = columns_.size(); + while (columns_.size() < columnIdx) { + DocumentDbColumn newColumn(document_, columnMetadata_[index], paths_[index]); + + columns_.push_back(newColumn); + index++; + } + + return true; +} +} // namespace odbc +} // namespace ignite diff --git a/src/odbc/src/jni/documentdb_query_mapping_service.cpp b/src/odbc/src/jni/documentdb_query_mapping_service.cpp index e20c0d747..15e78095c 100644 --- a/src/odbc/src/jni/documentdb_query_mapping_service.cpp +++ b/src/odbc/src/jni/documentdb_query_mapping_service.cpp @@ -43,11 +43,11 @@ DocumentDbQueryMappingService::Create( } SharedPointer< JniContext > jniContext = - connectionProperties.Get()->_jniContext; + connectionProperties.Get()->jniContext_; SharedPointer< GlobalJObject > queryMappingService; JniErrorCode success = jniContext.Get()->DocumentDbQueryMappingServiceCtor( - connectionProperties.Get()->_connectionProperties, - databaseMetadata.Get()->_databaseMetadata, queryMappingService, errInfo); + connectionProperties.Get()->connectionProperties_, + databaseMetadata.Get()->databaseMetadata_, queryMappingService, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return nullptr; } @@ -86,141 +86,141 @@ bool DocumentDbQueryMappingService::ReadJdbcColumnMetadata( JniErrorInfo& errInfo) { JniErrorCode success; int32_t listSize; - success = _jniContext.Get()->ListSize(columnMetadata, listSize, errInfo); + success = jniContext_.Get()->ListSize(columnMetadata, listSize, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } for (int32_t index = 0; index < listSize; index++) { SharedPointer< GlobalJObject > jdbcColumnMetadata; - success = _jniContext.Get()->ListGet(columnMetadata, index, + success = jniContext_.Get()->ListGet(columnMetadata, index, jdbcColumnMetadata, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } int32_t ordinal; - success = _jniContext.Get()->JdbcColumnMetadataGetOrdinal( + success = jniContext_.Get()->JdbcColumnMetadataGetOrdinal( jdbcColumnMetadata, ordinal, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } bool autoIncrement; - success = _jniContext.Get()->JdbcColumnMetadataIsAutoIncrement( + success = jniContext_.Get()->JdbcColumnMetadataIsAutoIncrement( jdbcColumnMetadata, autoIncrement, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } bool caseSensitive; - success = _jniContext.Get()->JdbcColumnMetadataIsCaseSensitive( + success = jniContext_.Get()->JdbcColumnMetadataIsCaseSensitive( jdbcColumnMetadata, caseSensitive, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } bool searchable; - success = _jniContext.Get()->JdbcColumnMetadataIsSearchable( + success = jniContext_.Get()->JdbcColumnMetadataIsSearchable( jdbcColumnMetadata, searchable, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } bool currency; - success = _jniContext.Get()->JdbcColumnMetadataIsCurrency( + success = jniContext_.Get()->JdbcColumnMetadataIsCurrency( jdbcColumnMetadata, currency, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } int32_t nullable; - success = _jniContext.Get()->JdbcColumnMetadataGetNullable( + success = jniContext_.Get()->JdbcColumnMetadataGetNullable( jdbcColumnMetadata, nullable, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } bool isSigned; - success = _jniContext.Get()->JdbcColumnMetadataIsSigned(jdbcColumnMetadata, + success = jniContext_.Get()->JdbcColumnMetadataIsSigned(jdbcColumnMetadata, isSigned, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } int32_t columnDisplaySize; - success = _jniContext.Get()->JdbcColumnMetadataGetColumnDisplaySize( + success = jniContext_.Get()->JdbcColumnMetadataGetColumnDisplaySize( jdbcColumnMetadata, columnDisplaySize, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } boost::optional< std::string > columnLabel; - success = _jniContext.Get()->JdbcColumnMetadataGetColumnLabel( + success = jniContext_.Get()->JdbcColumnMetadataGetColumnLabel( jdbcColumnMetadata, columnLabel, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } boost::optional< std::string > columnName; - success = _jniContext.Get()->JdbcColumnMetadataGetColumnName( + success = jniContext_.Get()->JdbcColumnMetadataGetColumnName( jdbcColumnMetadata, columnName, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } boost::optional< std::string > schemaName; - success = _jniContext.Get()->JdbcColumnMetadataGetSchemaName( + success = jniContext_.Get()->JdbcColumnMetadataGetSchemaName( jdbcColumnMetadata, schemaName, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } int32_t precision; - success = _jniContext.Get()->JdbcColumnMetadataGetPrecision( + success = jniContext_.Get()->JdbcColumnMetadataGetPrecision( jdbcColumnMetadata, precision, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } int32_t scale; - success = _jniContext.Get()->JdbcColumnMetadataGetScale(jdbcColumnMetadata, + success = jniContext_.Get()->JdbcColumnMetadataGetScale(jdbcColumnMetadata, scale, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } boost::optional< std::string > tableName; - success = _jniContext.Get()->JdbcColumnMetadataGetTableName( + success = jniContext_.Get()->JdbcColumnMetadataGetTableName( jdbcColumnMetadata, tableName, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } boost::optional< std::string > catalogName; - success = _jniContext.Get()->JdbcColumnMetadataGetCatalogName( + success = jniContext_.Get()->JdbcColumnMetadataGetCatalogName( jdbcColumnMetadata, catalogName, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } int32_t columnType; - success = _jniContext.Get()->JdbcColumnMetadataGetColumnType( + success = jniContext_.Get()->JdbcColumnMetadataGetColumnType( jdbcColumnMetadata, columnType, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } boost::optional< std::string > columnTypeName; - success = _jniContext.Get()->JdbcColumnMetadataGetColumnTypeName( + success = jniContext_.Get()->JdbcColumnMetadataGetColumnTypeName( jdbcColumnMetadata, columnTypeName, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } bool readOnly; - success = _jniContext.Get()->JdbcColumnMetadataIsReadOnly( + success = jniContext_.Get()->JdbcColumnMetadataIsReadOnly( jdbcColumnMetadata, readOnly, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } bool writable; - success = _jniContext.Get()->JdbcColumnMetadataIsWritable( + success = jniContext_.Get()->JdbcColumnMetadataIsWritable( jdbcColumnMetadata, writable, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } bool definitelyWritable; - success = _jniContext.Get()->JdbcColumnMetadataIsDefinitelyWritable( + success = jniContext_.Get()->JdbcColumnMetadataIsDefinitelyWritable( jdbcColumnMetadata, definitelyWritable, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; } boost::optional< std::string > columnClassName; - success = _jniContext.Get()->JdbcColumnMetadataGetColumnClassName( + success = jniContext_.Get()->JdbcColumnMetadataGetColumnClassName( jdbcColumnMetadata, columnClassName, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return false; @@ -242,14 +242,14 @@ DocumentDbQueryMappingService::GetMqlQueryContext(const std::string& sql, int maxRowCount, JniErrorInfo& errInfo) { SharedPointer< GlobalJObject > mqlQueryContext; - JniErrorCode success = _jniContext.Get()->DocumentDbQueryMappingServiceGet( - _queryMappingService, sql, maxRowCount, mqlQueryContext, errInfo); + JniErrorCode success = jniContext_.Get()->DocumentDbQueryMappingServiceGet( + queryMappingService_, sql, maxRowCount, mqlQueryContext, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return nullptr; } std::string collectionName; - success = _jniContext.Get()->DocumentdbMqlQueryContextGetCollectionName( + success = jniContext_.Get()->DocumentdbMqlQueryContextGetCollectionName( mqlQueryContext, collectionName, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return nullptr; @@ -259,31 +259,31 @@ DocumentDbQueryMappingService::GetMqlQueryContext(const std::string& sql, new DocumentDbMqlQueryContext(collectionName); SharedPointer< GlobalJObject > aggregateOperations; - success = _jniContext.Get() + success = jniContext_.Get() ->DocumentdbMqlQueryContextGetAggregateOperationsAsStrings( mqlQueryContext, aggregateOperations, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return nullptr; } if (!ReadListOfString( - _jniContext, aggregateOperations, + jniContext_, aggregateOperations, documentDbMqlQueryContext.Get()->GetAggregateOperations())) { return nullptr; } SharedPointer< GlobalJObject > paths; - success = _jniContext.Get()->DocumentdbMqlQueryContextGetPaths( + success = jniContext_.Get()->DocumentdbMqlQueryContextGetPaths( mqlQueryContext, paths, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return nullptr; } - if (!ReadListOfString(_jniContext, paths, + if (!ReadListOfString(jniContext_, paths, documentDbMqlQueryContext.Get()->GetPaths())) { return nullptr; } SharedPointer< GlobalJObject > columnMetadata; - success = _jniContext.Get()->DocumentdbMqlQueryContextGetColumnMetadata( + success = jniContext_.Get()->DocumentdbMqlQueryContextGetColumnMetadata( mqlQueryContext, columnMetadata, errInfo); if (success != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { return nullptr; diff --git a/src/odbc/src/query/column_metadata_query.cpp b/src/odbc/src/query/column_metadata_query.cpp index 52fce40f3..ede19da59 100644 --- a/src/odbc/src/query/column_metadata_query.cpp +++ b/src/odbc/src/query/column_metadata_query.cpp @@ -236,42 +236,42 @@ SqlResult::Type ColumnMetadataQuery::GetColumn( switch (columnIdx) { case ResultColumn::TABLE_CAT: { - buffer.PutOptString(currentColumn.GetCatalogName()); + buffer.PutString(currentColumn.GetCatalogName()); break; } case ResultColumn::TABLE_SCHEM: { - buffer.PutOptString(currentColumn.GetSchemaName()); + buffer.PutString(currentColumn.GetSchemaName()); break; } case ResultColumn::TABLE_NAME: { - buffer.PutOptString(currentColumn.GetTableName()); + buffer.PutString(currentColumn.GetTableName()); break; } case ResultColumn::COLUMN_NAME: { - buffer.PutOptString(currentColumn.GetColumnName()); + buffer.PutString(currentColumn.GetColumnName()); break; } case ResultColumn::DATA_TYPE: { - buffer.PutOptInt16(type_traits::BinaryToSqlType(columnType)); + buffer.PutInt16(type_traits::BinaryToSqlType(columnType)); break; } case ResultColumn::TYPE_NAME: { - buffer.PutOptString(type_traits::BinaryTypeToSqlTypeName(columnType)); + buffer.PutString(type_traits::BinaryTypeToSqlTypeName(columnType)); break; } case ResultColumn::COLUMN_SIZE: { - buffer.PutOptInt32(type_traits::BinaryTypeColumnSize(columnType)); + buffer.PutInt32(type_traits::BinaryTypeColumnSize(columnType)); break; } case ResultColumn::BUFFER_LENGTH: { - buffer.PutOptInt32(type_traits::BinaryTypeTransferLength(columnType)); + buffer.PutInt32(type_traits::BinaryTypeTransferLength(columnType)); break; } @@ -298,22 +298,22 @@ SqlResult::Type ColumnMetadataQuery::GetColumn( } case ResultColumn::NULLABLE: { - buffer.PutOptInt32(currentColumn.GetNullability()); + buffer.PutInt32(currentColumn.GetNullability()); break; } case ResultColumn::REMARKS: { - buffer.PutOptString(currentColumn.GetRemarks()); + buffer.PutString(currentColumn.GetRemarks()); break; } case ResultColumn::COLUMN_DEF: { - buffer.PutOptString(currentColumn.GetColumnDef()); + buffer.PutString(currentColumn.GetColumnDef()); break; } case ResultColumn::SQL_DATA_TYPE: { - buffer.PutOptInt16(type_traits::BinaryToSqlType(columnType)); + buffer.PutInt16(type_traits::BinaryToSqlType(columnType)); break; } @@ -325,17 +325,17 @@ SqlResult::Type ColumnMetadataQuery::GetColumn( } case ResultColumn::CHAR_OCTET_LENGTH: { - buffer.PutOptInt32(type_traits::BinaryTypeCharOctetLength(columnType)); + buffer.PutInt32(type_traits::BinaryTypeCharOctetLength(columnType)); break; } case ResultColumn::ORDINAL_POSITION: { - buffer.PutOptInt32(currentColumn.GetOrdinalPosition()); + buffer.PutInt32(currentColumn.GetOrdinalPosition()); break; } case ResultColumn::IS_NULLABLE: { - buffer.PutOptString( + buffer.PutString( type_traits::NullabilityToIsNullable(currentColumn.GetNullability())); break; } diff --git a/src/odbc/src/query/data_query.cpp b/src/odbc/src/query/data_query.cpp index fc32c55d4..a0fff9336 100644 --- a/src/odbc/src/query/data_query.cpp +++ b/src/odbc/src/query/data_query.cpp @@ -15,7 +15,12 @@ * limitations under the License. */ -#include "ignite/odbc/query/data_query.h" +#include +#include +#include +#include +#include +#include #include #include @@ -27,8 +32,10 @@ #include "ignite/odbc/jni/documentdb_query_mapping_service.h" #include "ignite/odbc/log.h" #include "ignite/odbc/message.h" +#include "ignite/odbc/documentdb_cursor.h" #include "ignite/odbc/odbc_error.h" #include "ignite/odbc/query/batch_query.h" +#include "ignite/odbc/query/data_query.h" using ignite::odbc::jni::DocumentDbConnectionProperties; using ignite::odbc::jni::DocumentDbDatabaseMetadata; @@ -43,16 +50,10 @@ DataQuery::DataQuery(diagnostic::DiagnosableAdapter& diag, Connection& connection, const std::string& sql, const app::ParameterSet& params, int32_t& timeout) : Query(diag, QueryType::DATA), - connection(connection), - sql(sql), - params(params), - resultMetaAvailable(false), - resultMeta(), - cursor(), - rowsAffected(), - rowsAffectedIdx(0), - cachedNextPage(), - timeout(timeout) { + connection_(connection), + sql_(sql), + params_(params), + timeout_(timeout) { // No-op. } @@ -61,55 +62,38 @@ DataQuery::~DataQuery() { } SqlResult::Type DataQuery::Execute() { - if (cursor.get()) + if (cursor_.get()) InternalClose(); return MakeRequestExecute(); } const meta::ColumnMetaVector* DataQuery::GetMeta() { - if (!resultMetaAvailable) { + if (!resultMetaAvailable_) { MakeRequestResultsetMeta(); - if (!resultMetaAvailable) + if (!resultMetaAvailable_) return nullptr; } - return &resultMeta; + return &resultMeta_; } SqlResult::Type DataQuery::FetchNextRow(app::ColumnBindingMap& columnBindings) { - // TODO: AD-604 - MakeRequestExecute - // https://bitquill.atlassian.net/browse/AD-604 - return SqlResult::AI_NO_DATA; - - if (!cursor.get()) { + if (!cursor_.get()) { diag.AddStatusRecord(SqlState::SHY010_SEQUENCE_ERROR, "Query was not executed."); return SqlResult::AI_ERROR; } - if (!cursor->HasData()) + if (!cursor_->HasData()) return SqlResult::AI_NO_DATA; - cursor->Increment(); - - if (cursor->NeedDataUpdate()) { - if (cachedNextPage.get()) - cursor->UpdateData(cachedNextPage); - else { - SqlResult::Type result = MakeRequestFetch(); - - if (result != SqlResult::AI_SUCCESS) - return result; - } - } - - if (!cursor->HasData()) + if (!cursor_->Increment()) return SqlResult::AI_NO_DATA; - Row* row = cursor->GetRow(); + DocumentDbRow* row = cursor_->GetRow(); if (!row) { diag.AddStatusRecord("Unknown error."); @@ -117,7 +101,7 @@ SqlResult::Type DataQuery::FetchNextRow(app::ColumnBindingMap& columnBindings) { return SqlResult::AI_ERROR; } - for (int32_t i = 1; i < row->GetSize() + 1; ++i) { + for (uint32_t i = 1; i < row->GetSize() + 1; ++i) { app::ColumnBindingMap::iterator it = columnBindings.find(i); if (it == columnBindings.end()) @@ -137,14 +121,14 @@ SqlResult::Type DataQuery::FetchNextRow(app::ColumnBindingMap& columnBindings) { SqlResult::Type DataQuery::GetColumn(uint16_t columnIdx, app::ApplicationDataBuffer& buffer) { - if (!cursor.get()) { + if (!cursor_.get()) { diag.AddStatusRecord(SqlState::SHY010_SEQUENCE_ERROR, "Query was not executed."); return SqlResult::AI_ERROR; } - Row* row = cursor->GetRow(); + DocumentDbRow* row = cursor_->GetRow(); if (!row) { diag.AddStatusRecord(SqlState::S24000_INVALID_CURSOR_STATE, @@ -166,105 +150,101 @@ SqlResult::Type DataQuery::Close() { } SqlResult::Type DataQuery::InternalClose() { - if (!cursor.get()) + if (!cursor_.get()) return SqlResult::AI_SUCCESS; - SqlResult::Type result = SqlResult::AI_SUCCESS; - - if (!IsClosedRemotely()) - result = MakeRequestClose(); - + SqlResult::Type result = MakeRequestClose(); if (result == SqlResult::AI_SUCCESS) { - cursor.reset(); - - rowsAffectedIdx = 0; - - rowsAffected.clear(); + cursor_.reset(); } return result; } bool DataQuery::DataAvailable() const { - return cursor.get() && cursor->HasData(); + return cursor_.get() && cursor_->HasData(); } int64_t DataQuery::AffectedRows() const { - int64_t affected = - rowsAffectedIdx < rowsAffected.size() ? rowsAffected[rowsAffectedIdx] : 0; - - if (affected >= 0) - return affected; - - return connection.GetConfiguration().GetDefaultFetchSize(); + return 0; } SqlResult::Type DataQuery::NextResultSet() { - if (rowsAffectedIdx + 1 >= rowsAffected.size()) { InternalClose(); - return SqlResult::AI_NO_DATA; - } - - SqlResult::Type res = MakeRequestMoreResults(); - - if (res == SqlResult::AI_SUCCESS) - ++rowsAffectedIdx; - - return res; -} - -bool DataQuery::IsClosedRemotely() const { - for (size_t i = 0; i < rowsAffected.size(); ++i) { - if (rowsAffected[i] < 0) - return false; - } - - return true; } SqlResult::Type DataQuery::MakeRequestExecute() { - // TODO: AD-604 - MakeRequestExecute - // https://bitquill.atlassian.net/browse/AD-604 - cursor.reset(new Cursor(0L)); - rowsAffectedIdx = 0; - - IgniteError error; - SharedPointer< DocumentDbMqlQueryContext > mqlQueryContext; - SqlResult::Type sqlRes = GetMqlQueryContext(mqlQueryContext, error); - if (!mqlQueryContext.IsValid() || sqlRes != SqlResult::AI_SUCCESS) { - diag.AddStatusRecord(error.GetText()); - return SqlResult::AI_ERROR; - } - - ReadJdbcColumnMetadataVector(mqlQueryContext.Get()->GetColumnMetadata()); - - return SqlResult::AI_SUCCESS; + cursor_.reset(); + return MakeRequestFetch(); } SqlResult::Type DataQuery::MakeRequestClose() { - // TODO: AD-604 - MakeRequestExecute - // https://bitquill.atlassian.net/browse/AD-604 return SqlResult::AI_SUCCESS; } SqlResult::Type DataQuery::MakeRequestFetch() { - // TODO: AD-604 - MakeRequestExecute - // https://bitquill.atlassian.net/browse/AD-604 + try { + SharedPointer< DocumentDbMqlQueryContext > mqlQueryContext; + IgniteError error; + + SqlResult::Type result = GetMqlQueryContext(mqlQueryContext, error); + if (result != SqlResult::AI_SUCCESS) { + diag.AddStatusRecord(error.GetText()); + return result; + } - return SqlResult::AI_SUCCESS; + std::vector< std::string > const& aggregateOperations = + mqlQueryContext.Get()->GetAggregateOperations(); + std::vector< JdbcColumnMetadata >& columnMetadata = + mqlQueryContext.Get()->GetColumnMetadata(); + std::vector< std::string >& paths = mqlQueryContext.Get()->GetPaths(); + + if (!resultMetaAvailable_) { + ReadJdbcColumnMetadataVector(columnMetadata); + } + + const config::Configuration& config = connection_.GetConfiguration(); + std::string databaseName = config.GetDatabase(); + std::string collectionName = mqlQueryContext.Get()->GetCollectionName(); + + std::shared_ptr< mongocxx::client > const& mongoClient = + connection_.GetMongoClient(); + mongocxx::database database = mongoClient.get()->database(databaseName); + mongocxx::collection collection = database[collectionName]; + auto pipeline = mongocxx::pipeline{}; + for (auto const& stage : aggregateOperations) { + pipeline.append_stage(bsoncxx::from_json(stage)); + } + mongocxx::cursor cursor = collection.aggregate(pipeline); + + this->cursor_.reset(new DocumentDbCursor(cursor, columnMetadata, paths)); + + return SqlResult::AI_SUCCESS; + } catch (mongocxx::exception const& xcp) { + std::stringstream message; + message << "Unable to establish connection with DocumentDB." + << " code: " << xcp.code().value() + << " messagge: " << xcp.code().message() + << " cause: " << xcp.what(); + odbc::IgniteError error( + odbc::IgniteError::IGNITE_ERR_SECURE_CONNECTION_FAILURE, + message.str().c_str()); + diag.AddStatusRecord(error.GetText()); + return SqlResult::AI_ERROR; + } } SqlResult::Type DataQuery::GetMqlQueryContext( SharedPointer< DocumentDbMqlQueryContext >& mqlQueryContext, IgniteError& error) { SharedPointer< DocumentDbConnectionProperties > connectionProperties = - connection.GetConnectionProperties(error); + connection_.GetConnectionProperties(error); if (error.GetCode() != IgniteError::IGNITE_SUCCESS) { return SqlResult::AI_ERROR; } SharedPointer< DocumentDbDatabaseMetadata > databaseMetadata = - connection.GetDatabaseMetadata(error); + connection_.GetDatabaseMetadata(error); if (error.GetCode() != IgniteError::IGNITE_SUCCESS) { return SqlResult::AI_ERROR; } @@ -278,7 +258,7 @@ SqlResult::Type DataQuery::GetMqlQueryContext( return SqlResult::AI_ERROR; } mqlQueryContext = - queryMappingService.Get()->GetMqlQueryContext(sql, 0, errInfo); + queryMappingService.Get()->GetMqlQueryContext(sql_, 0, errInfo); if (errInfo.code != JniErrorCode::IGNITE_JNI_ERR_SUCCESS) { IgniteError::SetError(errInfo.code, errInfo.errCls.c_str(), errInfo.errMsg.c_str(), error); @@ -286,15 +266,12 @@ SqlResult::Type DataQuery::GetMqlQueryContext( } return SqlResult::AI_SUCCESS; } + SqlResult::Type DataQuery::MakeRequestMoreResults() { - // TODO: AD-604 - MakeRequestExecute - // https://bitquill.atlassian.net/browse/AD-604 return SqlResult::AI_SUCCESS; } SqlResult::Type DataQuery::MakeRequestResultsetMeta() { - // TODO: AD-604 - MakeRequestExecute - // https://bitquill.atlassian.net/browse/AD-604 IgniteError error; SharedPointer< DocumentDbMqlQueryContext > mqlQueryContext; SqlResult::Type sqlRes = GetMqlQueryContext(mqlQueryContext, error); @@ -309,7 +286,7 @@ SqlResult::Type DataQuery::MakeRequestResultsetMeta() { void DataQuery::ReadJdbcColumnMetadataVector( std::vector< JdbcColumnMetadata > jdbcVector) { using ignite::odbc::meta::ColumnMeta; - resultMeta.clear(); + resultMeta_.clear(); if (jdbcVector.empty()) { return; @@ -319,10 +296,10 @@ void DataQuery::ReadJdbcColumnMetadataVector( int32_t prevPosition = 0; for (JdbcColumnMetadata jdbcMetadata : jdbcVector) { - resultMeta.emplace_back(ColumnMeta()); - resultMeta.back().ReadJdbcMetadata(jdbcMetadata, prevPosition); + resultMeta_.emplace_back(ColumnMeta()); + resultMeta_.back().ReadJdbcMetadata(jdbcMetadata, prevPosition); } - resultMetaAvailable = true; + resultMetaAvailable_ = true; } SqlResult::Type DataQuery::ProcessConversionResult( @@ -384,11 +361,11 @@ SqlResult::Type DataQuery::ProcessConversionResult( } void DataQuery::SetResultsetMeta(const meta::ColumnMetaVector& value) { - resultMeta.assign(value.begin(), value.end()); - resultMetaAvailable = true; + resultMeta_.assign(value.begin(), value.end()); + resultMetaAvailable_ = true; - for (size_t i = 0; i < resultMeta.size(); ++i) { - meta::ColumnMeta& meta = resultMeta.at(i); + for (size_t i = 0; i < resultMeta_.size(); ++i) { + meta::ColumnMeta& meta = resultMeta_.at(i); if (meta.GetDataType()) { LOG_MSG( "\n[" << i << "] SchemaName: " diff --git a/src/odbc/src/query/streaming_query.cpp b/src/odbc/src/query/streaming_query.cpp deleted file mode 100644 index affc2dd4b..000000000 --- a/src/odbc/src/query/streaming_query.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ignite/odbc/query/streaming_query.h" - -#include "ignite/odbc/connection.h" -#include "ignite/odbc/log.h" -#include "ignite/odbc/message.h" -#include "ignite/odbc/sql/sql_set_streaming_command.h" - -namespace ignite { -namespace odbc { -namespace query { -StreamingQuery::StreamingQuery(diagnostic::DiagnosableAdapter& diag, - Connection& connection, - const app::ParameterSet& params) - : Query(diag, QueryType::STREAMING), - connection(connection), - params(params) { - // No-op. -} - -StreamingQuery::~StreamingQuery() { - // No-op. -} - -SqlResult::Type StreamingQuery::Execute() { - return connection.GetStreamingContext().Execute(sql, params); -} - -const meta::ColumnMetaVector* StreamingQuery::GetMeta() { - return 0; -} - -SqlResult::Type StreamingQuery::FetchNextRow(app::ColumnBindingMap&) { - return SqlResult::AI_NO_DATA; -} - -SqlResult::Type StreamingQuery::GetColumn(uint16_t, - app::ApplicationDataBuffer&) { - diag.AddStatusRecord(SqlState::S24000_INVALID_CURSOR_STATE, - "Column is not available."); - - return SqlResult::AI_ERROR; -} - -SqlResult::Type StreamingQuery::Close() { - return SqlResult::AI_SUCCESS; -} - -bool StreamingQuery::DataAvailable() const { - return false; -} - -int64_t StreamingQuery::AffectedRows() const { - return 0; -} - -SqlResult::Type StreamingQuery::NextResultSet() { - return SqlResult::AI_NO_DATA; -} -} // namespace query -} // namespace odbc -} // namespace ignite diff --git a/src/odbc/src/query/table_metadata_query.cpp b/src/odbc/src/query/table_metadata_query.cpp index be054deeb..73a398c35 100644 --- a/src/odbc/src/query/table_metadata_query.cpp +++ b/src/odbc/src/query/table_metadata_query.cpp @@ -168,27 +168,27 @@ SqlResult::Type TableMetadataQuery::GetColumn( switch (columnIdx) { case ResultColumn::TABLE_CAT: { - buffer.PutOptString(currentColumn.GetCatalogName()); + buffer.PutString(currentColumn.GetCatalogName()); break; } case ResultColumn::TABLE_SCHEM: { - buffer.PutOptString(currentColumn.GetSchemaName()); + buffer.PutString(currentColumn.GetSchemaName()); break; } case ResultColumn::TABLE_NAME: { - buffer.PutOptString(currentColumn.GetTableName()); + buffer.PutString(currentColumn.GetTableName()); break; } case ResultColumn::TABLE_TYPE: { - buffer.PutOptString(currentColumn.GetTableType()); + buffer.PutString(currentColumn.GetTableType()); break; } case ResultColumn::REMARKS: { - buffer.PutOptString(currentColumn.GetRemarks()); + buffer.PutString(currentColumn.GetRemarks()); break; } diff --git a/src/odbc/src/query/type_info_query.cpp b/src/odbc/src/query/type_info_query.cpp index df7cfe24a..2339407a9 100644 --- a/src/odbc/src/query/type_info_query.cpp +++ b/src/odbc/src/query/type_info_query.cpp @@ -264,20 +264,20 @@ SqlResult::Type TypeInfoQuery::GetColumn(uint16_t columnIdx, switch (columnIdx) { case ResultColumn::TYPE_NAME: { - buffer.PutOptString(type_traits::BinaryTypeToSqlTypeName(currentType)); + buffer.PutString(type_traits::BinaryTypeToSqlTypeName(currentType)); break; } case ResultColumn::DATA_TYPE: case ResultColumn::SQL_DATA_TYPE: { - buffer.PutOptInt16(type_traits::BinaryToSqlType(currentType)); + buffer.PutInt16(type_traits::BinaryToSqlType(currentType)); break; } case ResultColumn::COLUMN_SIZE: { - buffer.PutOptInt32(type_traits::BinaryTypeColumnSize(currentType)); + buffer.PutInt32(type_traits::BinaryTypeColumnSize(currentType)); break; } @@ -350,7 +350,7 @@ SqlResult::Type TypeInfoQuery::GetColumn(uint16_t columnIdx, case ResultColumn::MINIMUM_SCALE: case ResultColumn::MAXIMUM_SCALE: { - buffer.PutOptInt16(type_traits::BinaryTypeDecimalDigits(currentType)); + buffer.PutInt16(type_traits::BinaryTypeDecimalDigits(currentType)); break; } @@ -362,7 +362,7 @@ SqlResult::Type TypeInfoQuery::GetColumn(uint16_t columnIdx, } case ResultColumn::NUM_PREC_RADIX: { - buffer.PutOptInt32(type_traits::BinaryTypeNumPrecRadix(currentType)); + buffer.PutInt32(type_traits::BinaryTypeNumPrecRadix(currentType)); break; } diff --git a/src/odbc/src/statement.cpp b/src/odbc/src/statement.cpp index 2dbcd8187..5519ddaf3 100644 --- a/src/odbc/src/statement.cpp +++ b/src/odbc/src/statement.cpp @@ -31,7 +31,6 @@ #include "ignite/odbc/query/internal_query.h" #include "ignite/odbc/query/primary_keys_query.h" #include "ignite/odbc/query/special_columns_query.h" -#include "ignite/odbc/query/streaming_query.h" #include "ignite/odbc/query/table_metadata_query.h" #include "ignite/odbc/query/type_info_query.h" #include "ignite/odbc/sql/sql_parser.h" @@ -295,14 +294,6 @@ SqlResult::Type Statement::InternalSetAttribute(int attr, void* value, case SQL_ATTR_PARAMSET_SIZE: { SqlUlen size = reinterpret_cast< SqlUlen >(value); - - if (size > 1 && IsStreamingActive()) { - AddStatusRecord(SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED, - "Batching is not supported in streaming mode."); - - return SqlResult::AI_ERROR; - } - parameters.SetParamSetSize(size); break; @@ -581,10 +572,6 @@ SqlResult::Type Statement::ProcessInternalCommand(const std::string& query) { } } -bool Statement::IsStreamingActive() const { - return connection.GetStreamingContext().IsEnabled(); -} - SqlResult::Type Statement::InternalPrepareSqlQuery(const std::string& query) { if (sql_utils::IsInternalCommand(query)) return ProcessInternalCommand(query); @@ -592,19 +579,6 @@ SqlResult::Type Statement::InternalPrepareSqlQuery(const std::string& query) { // Resetting parameters types as we are changing the query. parameters.Prepare(); - if (IsStreamingActive()) { - if (!currentQuery.get()) - currentQuery.reset( - new query::StreamingQuery(*this, connection, parameters)); - - query::StreamingQuery* currentQuery0 = - static_cast< query::StreamingQuery* >(currentQuery.get()); - - currentQuery0->PrepareQuery(query); - - return SqlResult::AI_SUCCESS; - } - if (currentQuery.get()) currentQuery->Close(); @@ -638,12 +612,6 @@ SqlResult::Type Statement::InternalExecuteSqlQuery() { return SqlResult::AI_ERROR; } - if (currentQuery->GetType() == query::QueryType::INTERNAL) { - ProcessInternalQuery(); - - return SqlResult::AI_SUCCESS; - } - if (parameters.GetParamSetSize() > 1 && currentQuery->GetType() == query::QueryType::DATA) { query::DataQuery& qry = static_cast< query::DataQuery& >(*currentQuery); @@ -681,46 +649,6 @@ SqlResult::Type Statement::InternalExecuteSqlQuery() { return currentQuery->Execute(); } -SqlResult::Type Statement::ProcessInternalQuery() { - assert(currentQuery->GetType() == query::QueryType::INTERNAL); - - query::InternalQuery* qry = - static_cast< query::InternalQuery* >(currentQuery.get()); - - LOG_MSG("Processing internal query: " << qry->GetQuery()); - - assert(qry->GetCommand().GetType() == SqlCommandType::SET_STREAMING); - - SqlSetStreamingCommand& cmd = - static_cast< SqlSetStreamingCommand& >(qry->GetCommand()); - - StopStreaming(); - - if (!cmd.IsEnabled()) - return SqlResult::AI_SUCCESS; - - LOG_MSG("Sending start streaming command"); - - query::DataQuery enablingQuery(*this, connection, qry->GetQuery(), parameters, - timeout); - - SqlResult::Type res = enablingQuery.Execute(); - - if (res != SqlResult::AI_SUCCESS) - return res; - - LOG_MSG("Preparing streaming context on client"); - - connection.GetStreamingContext().Enable(cmd); - - std::unique_ptr< query::Query > newQry( - new query::StreamingQuery(*this, connection, parameters)); - - std::swap(currentQuery, newQry); - - return SqlResult::AI_SUCCESS; -} - void Statement::ExecuteGetColumnsMetaQuery(const std::string& catalog, const std::string& schema, const std::string& table, @@ -912,17 +840,6 @@ SqlResult::Type Statement::InternalClose() { return result; } -SqlResult::Type Statement::StopStreaming() { - if (!IsStreamingActive()) - return SqlResult::AI_SUCCESS; - - LOG_MSG("Stopping streaming"); - - SqlResult::Type result = connection.GetStreamingContext().Disable(); - - return result; -} - void Statement::FetchScroll(int16_t orientation, int64_t offset) { IGNITE_ODBC_API_CALL(InternalFetchScroll(orientation, offset)); }