diff --git a/driver/convert.c b/driver/convert.c index fe3aad78..adf077f6 100644 --- a/driver/convert.c +++ b/driver/convert.c @@ -3666,7 +3666,6 @@ static BOOL xstr_to_number(esodbc_stmt_st *stmt, void *data_ptr, SQLRETURN c2sql_null(esodbc_rec_st *arec, esodbc_rec_st *irec, char *dest, size_t *len) { - assert(irec->concise_type == ESODBC_SQL_NULL); if (dest) { memcpy(dest, JSON_VAL_NULL, sizeof(JSON_VAL_NULL) - /*\0*/1); } @@ -4617,6 +4616,37 @@ SQLRETURN c2sql_interval(esodbc_rec_st *arec, esodbc_rec_st *irec, # undef ASSIGN_SIGNED } +static inline SQLLEN get_octet_len(SQLLEN *octet_len_ptr, void *data_ptr, + BOOL wide) +{ + SQLLEN cnt; + + assert(data_ptr); + + if (! octet_len_ptr) { + /* "If [...] is a null pointer, the driver assumes that all input + * parameter values are non-NULL and that character and binary data is + * null-terminated." */ + cnt = wide ? wcslen((wchar_t *)data_ptr) : strlen((char *)data_ptr); + } else { + cnt = *octet_len_ptr; + switch (cnt) { + case SQL_NTSL: + cnt = wide ? wcslen((wchar_t *)data_ptr) : + strlen((char *)data_ptr); + break; + case SQL_NULL_DATA: + BUG("converting SQL_NULL_DATA"); + cnt = -1; /* UTF16/8 will fail */ + break; + default: /* get characters count from octets count */ + cnt /= wide ? sizeof(SQLWCHAR) : sizeof(SQLCHAR); + } + } + + return cnt; +} + static SQLRETURN c2sql_cstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec, SQLULEN pos, char *dest, size_t *len) { @@ -4629,7 +4659,7 @@ static SQLRETURN c2sql_cstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec, /* pointer to app's buffer */ data_ptr = deferred_address(SQL_DESC_DATA_PTR, pos, arec); - cnt = octet_len_ptr ? *octet_len_ptr : strlen((char *)data_ptr); + cnt = get_octet_len(octet_len_ptr, data_ptr, /*wide*/FALSE); if (dest) { *dest = '"'; @@ -4663,7 +4693,7 @@ static SQLRETURN c2sql_wstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec, /* pointer to app's buffer */ data_ptr = deferred_address(SQL_DESC_DATA_PTR, pos, arec); - cnt = octet_len_ptr ? *octet_len_ptr : wcslen((wchar_t *)data_ptr); + cnt = get_octet_len(octet_len_ptr, data_ptr, /*wide*/TRUE); if (dest) { *dest = '"'; @@ -4682,7 +4712,7 @@ static SQLRETURN c2sql_wstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec, octets = U16WC_TO_MBU8((wchar_t *)data_ptr, cnt, dest + !!dest, dest ? INT_MAX : 0); if ((err = WAPI_ERRNO()) != ERROR_SUCCESS) { - ERRH(stmt, "converting to multibyte string failed: %d", err); + ERRH(stmt, "converting to multibyte string failed: 0x%x", err); RET_HDIAGS(stmt, SQL_STATE_HY000); } assert(0 < octets); /* shouldn't not fail and return negative */ diff --git a/test/test_conversion_c2sql_null.cc b/test/test_conversion_c2sql_null.cc new file mode 100644 index 00000000..494c7388 --- /dev/null +++ b/test/test_conversion_c2sql_null.cc @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +#include +#include "connected_dbc.h" + +namespace test { + +class ConvertC2SQL_Null : public ::testing::Test, public ConnectedDBC { +}; + + +TEST_F(ConvertC2SQL_Null, CStr2Boolean_null) +{ + prepareStatement(); + + SQLCHAR val[] = "1"; + SQLLEN osize = SQL_NULL_DATA; + ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, + ESODBC_SQL_BOOLEAN, /*size*/0, /*decdigits*/0, val, + sizeof(val) - /*\0*/1, &osize); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); + + assertRequest("[{\"type\": \"BOOLEAN\", \"value\": null}]"); +} + +TEST_F(ConvertC2SQL_Null, WStr2Null) +{ + prepareStatement(); + + SQLWCHAR val[] = L"0X"; + ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, + ESODBC_SQL_NULL, /*size*/0, /*decdigits*/0, val, 0, NULL); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); + + assertRequest("[{\"type\": \"NULL\", \"value\": null}]"); +} + + +} // test namespace + +/* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */ diff --git a/test/test_conversion_c2sql_varchar.cc b/test/test_conversion_c2sql_varchar.cc index 1a12e8ed..180a3512 100644 --- a/test/test_conversion_c2sql_varchar.cc +++ b/test/test_conversion_c2sql_varchar.cc @@ -85,6 +85,21 @@ TEST_F(ConvertC2SQL_Varchar, WStr2Varchar_ansi_jsonescape) "\"value\": \"START_{xxx}=\\\"yyy\\\"\\r__END\"}]"); } +TEST_F(ConvertC2SQL_Varchar, CStr2Varchar_jsonescape_oct_len_ptr) +{ + prepareStatement(); + + SQLCHAR val[] = "START_{xxx}=\"yyy\"\r__END"; + SQLLEN octet_len = strlen((char *)val); + ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, + SQL_VARCHAR, /*size*/35, /*decdigits*/0, val, sizeof(val), + &octet_len); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); + + assertRequest("[{\"type\": \"KEYWORD\", " + "\"value\": \"START_{xxx}=\\\"yyy\\\"\\r__END\"}]"); +} + /* note: test name used in test */ TEST_F(ConvertC2SQL_Varchar, CStr2Varchar_jsonescape) { @@ -115,6 +130,21 @@ TEST_F(ConvertC2SQL_Varchar, WStr2Varchar_u8_jsonescape) "\"value\": \"START_\\\"A\u00C4o\u00F6U\u00FC\\\"__END\"}]"); } +TEST_F(ConvertC2SQL_Varchar, WStr2Varchar_u8_fullescape_oct_len_ptr) +{ + prepareStatement(); + + SQLWCHAR val[] = L"äöüÄÖÜ"; + SQLLEN octet_len = SQL_NTSL; + ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, + SQL_VARCHAR, /*size*/35, /*decdigits*/0, val, sizeof(val), + &octet_len); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); + + assertRequest("[{\"type\": \"KEYWORD\", " + "\"value\": \"\u00E4\u00F6\u00FC\u00C4\u00D6\u00DC\"}]"); +} + /* note: test name used in test */ TEST_F(ConvertC2SQL_Varchar, WStr2Varchar_u8_fullescape) {