|
| 1 | +#include "driver/test/client_utils.h" |
| 2 | +#include "driver/test/client_test_base.h" |
| 3 | +#include "driver/test/result_set_reader.hpp" |
| 4 | + |
| 5 | +#include <gtest/gtest.h> |
| 6 | +#include <gmock/gmock.h> |
| 7 | + |
| 8 | +class ErrorHandlingTest |
| 9 | + : public ClientTestBase |
| 10 | +{ |
| 11 | +public: |
| 12 | + std::string getQueryId() |
| 13 | + { |
| 14 | + char query_id_data[74] = {}; |
| 15 | + SQLINTEGER query_id_len{}; |
| 16 | + SQLGetStmtAttr(hstmt, SQL_CH_STMT_ATTR_LAST_QUERY_ID, query_id_data, std::size(query_id_data), &query_id_len); |
| 17 | + |
| 18 | +#ifdef _win_ |
| 19 | + return toUTF8(reinterpret_cast<PTChar*>(query_id_data)); |
| 20 | +#else |
| 21 | + // unixODBC usually converts all strings from the driver encoding to the application encoding, |
| 22 | + // similar to how Windows behaves. However, `SQLGetStmtAttr` appears to be an exception in unixODBC, |
| 23 | + // as it passes the result in the driver encoding unchanged. |
| 24 | + // See: https://github.com/lurcher/unixODBC/issues/22 |
| 25 | + // |
| 26 | + // To solve this issue and ensure the result reaches the application in the correct encoding, |
| 27 | + // we would need to implement both `SQLGetStmtAttr` and `SQLGetStmtAttrW` in the driver. |
| 28 | + // This would introduce a number of workarounds and non-uniform solutions. |
| 29 | + // Currently, there is only one string attribute, `SQL_CH_STMT_ATTR_LAST_QUERY_ID`, which is internal, |
| 30 | + // so this added complexity in the driver is not currently justified. |
| 31 | + // |
| 32 | + // Since we know that the Query ID is 36 bytes long, we can infer the encoding of the result |
| 33 | + // and re-encode it accordingly here in the test. |
| 34 | + if (query_id_len == 36) |
| 35 | + return toUTF8(query_id_data); |
| 36 | + else |
| 37 | + return toUTF8(reinterpret_cast<char16_t*>(query_id_data)); |
| 38 | +#endif |
| 39 | + } |
| 40 | +}; |
| 41 | + |
| 42 | +/** |
| 43 | + * Given a query with a syntax error, we receive the error description with the query id. |
| 44 | + */ |
| 45 | +TEST_F(ErrorHandlingTest, ServerExceptionInBeginning) |
| 46 | +{ |
| 47 | + static const size_t stream_size = 100000; |
| 48 | + auto query = fromUTF8<PTChar>("SELECT * FROM this.table.cannot.exist"); |
| 49 | + |
| 50 | + ODBC_CALL_ON_STMT_THROW(hstmt, SQLPrepare(hstmt, ptcharCast(query.data()), SQL_NTS)); |
| 51 | + ASSERT_EQ(SQLExecute(hstmt), SQL_ERROR); |
| 52 | + auto error_message = extract_diagnostics(hstmt, SQL_HANDLE_STMT); |
| 53 | + auto query_id = getQueryId(); |
| 54 | + |
| 55 | + auto expect_error_message = "1:[HY000][1]Error while processing query " + query_id + ": HTTP status code: 400"; |
| 56 | + ASSERT_EQ(error_message.substr(0, expect_error_message.size()), expect_error_message); |
| 57 | +} |
| 58 | + |
| 59 | +/** |
| 60 | + * Given a query with an exception in the middle of the data stream, we receive the error description with the query id. |
| 61 | + * (We do not yet parse error messages, we only report "Incomplete input stream, expected at least" at the moment) |
| 62 | + */ |
| 63 | +TEST_F(ErrorHandlingTest, ServerExceptionInMiddleOfStream) |
| 64 | +{ |
| 65 | + static const size_t stream_size = 100000; |
| 66 | + auto query = fromUTF8<PTChar>( |
| 67 | + "SELECT throwIf(number >= " |
| 68 | + + std::to_string(stream_size) + ") FROM numbers(" |
| 69 | + + std::to_string(stream_size) + " + 1)"); |
| 70 | + |
| 71 | + ODBC_CALL_ON_STMT_THROW(hstmt, SQLPrepare(hstmt, ptcharCast(query.data()), SQL_NTS)); |
| 72 | + ODBC_CALL_ON_STMT_THROW(hstmt, SQLExecute(hstmt)); |
| 73 | + |
| 74 | + std::string error_message = ""; |
| 75 | + SQLRETURN res = SQL_SUCCESS; |
| 76 | + while (res == SQL_SUCCESS) |
| 77 | + { |
| 78 | + res = SQLFetch(hstmt); |
| 79 | + if (res != SQL_SUCCESS) |
| 80 | + error_message = extract_diagnostics(hstmt, SQL_HANDLE_STMT); |
| 81 | + } |
| 82 | + |
| 83 | + auto query_id = getQueryId(); |
| 84 | + auto expect_error_message = "1:[HY000][1]Error while processing query " + query_id + ": "; |
| 85 | + ASSERT_EQ(error_message.substr(0, expect_error_message.size()), expect_error_message); |
| 86 | +} |
0 commit comments