32 |
32 |
33 SqlException::SqlException(std::wstring message) : message(message) { |
33 SqlException::SqlException(std::wstring message) : message(message) { |
34 } |
34 } |
35 |
35 |
36 SqlException::SqlException(std::wstring message, SQLRETURN resultCode, SQLSMALLINT handleType, SQLHANDLE handle, bool freeHandle) : message(message), resultCode(resultCode) { |
36 SqlException::SqlException(std::wstring message, SQLRETURN resultCode, SQLSMALLINT handleType, SQLHANDLE handle, bool freeHandle) : message(message), resultCode(resultCode) { |
37 std::wstring_convert < std::codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings |
37 std::wstring_convert < std::codecvt_utf8<wchar_t>> convertor("", L"conversion failed"); // TODO: support also other encodings |
38 SQLCHAR buffer[SQL_MAX_MESSAGE_LENGTH + 1]; |
38 SQLCHAR buffer[SQL_MAX_MESSAGE_LENGTH + 1]; |
39 SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1]; |
39 SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1]; |
40 memset(buffer, 0, sizeof (buffer)); |
40 memset(buffer, 0, sizeof (buffer)); |
41 memset(sqlstate, 0, sizeof (sqlstate)); |
41 memset(sqlstate, 0, sizeof (sqlstate)); |
42 SQLINTEGER sqlcode; |
42 SQLINTEGER sqlcode; |
43 SQLSMALLINT length; |
43 SQLSMALLINT length; |
44 for (SQLSMALLINT i = 1; SQLGetDiagRec(handleType, handle, i, sqlstate, &sqlcode, buffer, SQL_MAX_MESSAGE_LENGTH + 1, &length) == SQL_SUCCESS; i++) { |
44 for (SQLSMALLINT i = 1; SQLGetDiagRec(handleType, handle, i, sqlstate, &sqlcode, buffer, SQL_MAX_MESSAGE_LENGTH + 1, &length) == SQL_SUCCESS; i++) { |
45 for (size_t i = 0; i < sizeof (buffer); i++) if (buffer[i] > 126 || buffer[i] == '\r') buffer[i] = '?'; // keep only ASCII characters and avoid CR which breaks the output |
45 std::wstring diagMessage = convertor.from_bytes((char*) buffer); |
46 diagnostics.push_back({convertor.from_bytes((char*) sqlstate), sqlcode, convertor.from_bytes((char*) buffer)}); |
46 if (diagMessage == L"conversion failed") { |
47 // FIXME: decoding fails is error message contains unicode characters – exception is thrown: |
47 for (size_t i = 0; i < sizeof (buffer); i++) if (buffer[i] > 126 || buffer[i] == '\r') buffer[i] = '?'; // keep only ASCII characters and avoid CR which breaks the output |
48 // terminate called after throwing an instance of 'std::range_error' |
48 diagMessage = convertor.from_bytes((char*) buffer); |
49 // what(): wstring_convert::from_bytes |
49 } |
50 // Exception can be avoided by: |
50 diagnostics.push_back({convertor.from_bytes((char*) sqlstate), sqlcode, diagMessage}); |
51 // std::wstring_convert < std::codecvt_utf8<wchar_t>> convertor("", L"XXX Unable to decode error message from SQLGetDiagRec()"); |
51 // FIXME: character encoding in SQLGetDiagRec() |
52 // but actual error message is then lost. |
52 // |
53 // So as a workaround we keep only ASCII characters. |
53 // sometimes we get valid UTF-8 string |
54 // It seems that we sometimes get valid ISO-8859-2 or ISO-8859-1 encoded messages even if our platform encoding is UTF-8. |
54 // sometimes we get valid ISO-8859-1 string (which is not valid UTF-8) |
|
55 // |
|
56 // Temporary workaround: try UTF-8 decoding and if it fails, remove all non-ASCII characters and try again |
|
57 // |
|
58 // See also: DriverManager/SQLGetDiagRec.c: |
|
59 // else if ( !__get_connection( head ) -> unicode_driver |
|
60 // and DriverManager/__info.c: |
|
61 // char *asc[] = { "char", "char", "ISO8859-1", "ISO-8859-1", "8859-1", "iso8859_1", "ASCII", NULL }; |
|
62 // in unixODBC |
|
63 // |
|
64 // We get valid UTF-8 if "PostgreSQL Unicode" driver is used and connection is successful: e.g. SELECT * FROM žádná_taková_tabulka_neexistuje; |
|
65 // We get valid ISO-8859-1 if "PostgreSQL Unicode" driver is used and connection is not successful and LANG=cs_CZ.UTF-8 e.g. [unixODBC]could not connect to server: Spojení odmítnuto |
|
66 // n.b. character í is present in ISO-8859-1, but other Czech characters (čřš etc.) are not (they are in ISO-8859-2) |
|
67 // |
|
68 // SQLGetDiagRec() behavior differs from other functions like SQLGetData(). Maybe use SQLGetDiagRecW() and decode UCS-2 / UTF-16. |
55 |
69 |
56 } |
70 } |
57 if (freeHandle) OdbcCommon::freeHandle(handleType, handle); |
71 if (freeHandle) OdbcCommon::freeHandle(handleType, handle); |
58 } |
72 } |
59 |
73 |