diff --git a/driver/catalogue.c b/driver/catalogue.c index d7a020ea..3ff152f6 100644 --- a/driver/catalogue.c +++ b/driver/catalogue.c @@ -87,35 +87,52 @@ SQLRETURN EsSQLStatisticsW( return fake_answer(hstmt, &statistics); } -BOOL TEST_API set_current_catalog(esodbc_dbc_st *dbc, wstr_st *catalog) +void free_current_catalog(esodbc_dbc_st *dbc) { - if (dbc->catalog.cnt) { - DBGH(dbc, "catalog already set to `" LWPDL "`.", LWSTR(&dbc->catalog)); - if (! EQ_WSTR(&dbc->catalog, catalog)) { - /* this should never happen, as cluster's name is not updateable - * on the fly. */ - ERRH(dbc, "overwriting previously set catalog value!"); - free(dbc->catalog.str); - dbc->catalog.str = NULL; - dbc->catalog.cnt = 0; - } else { - return FALSE; + if (dbc->catalog.w.str) { + free(dbc->catalog.w.str); + dbc->catalog.w.str = NULL; + dbc->catalog.w.cnt = 0; + } + if (dbc->catalog.c.str) { + free(dbc->catalog.c.str); + dbc->catalog.c.str = NULL; + dbc->catalog.c.cnt = 0; + } +} + +SQLRETURN set_current_catalog(esodbc_dbc_st *dbc, wstr_st *catalog) +{ + if (dbc->catalog.w.cnt) { + DBGH(dbc, "catalog previously set to `" LWPDL "`.", + LWSTR(&dbc->catalog.w)); + if (! EQ_WSTR(&dbc->catalog.w, catalog)) { + free_current_catalog(dbc); } } - if (! catalog->cnt) { - WARNH(dbc, "attempting to set catalog name to empty value."); - return FALSE; + if (! catalog->cnt || ! catalog->str) { + WARNH(dbc, "catalog name set to empty value."); + return SQL_SUCCESS; } - if (! (dbc->catalog.str = malloc((catalog->cnt + 1) * sizeof(SQLWCHAR)))) { + dbc->catalog.w.str = malloc((catalog->cnt + 1) * sizeof(SQLWCHAR)); + if (! dbc->catalog.w.str) { ERRNH(dbc, "OOM for %zu wchars.", catalog->cnt + 1); - return FALSE; + RET_HDIAGS(dbc, SQL_STATE_HY001); } - wmemcpy(dbc->catalog.str, catalog->str, catalog->cnt); - dbc->catalog.str[catalog->cnt] = L'\0'; - dbc->catalog.cnt = catalog->cnt; - INFOH(dbc, "current catalog name: `" LWPDL "`.", LWSTR(&dbc->catalog)); + wmemcpy(dbc->catalog.w.str, catalog->str, catalog->cnt); + dbc->catalog.w.str[catalog->cnt] = L'\0'; + dbc->catalog.w.cnt = catalog->cnt; - return TRUE; + if (! wstr_to_utf8(catalog, &dbc->catalog.c)) { + goto err; + } + + INFOH(dbc, "current catalog name: `" LWPDL "`.", LWSTR(&dbc->catalog.w)); + return SQL_SUCCESS; + +err: + free_current_catalog(dbc); + RET_HDIAG(dbc, SQL_STATE_HY000, "Saving current catalog failed", 0); } /* writes into 'dest', of size 'room', the current requested attr. of 'dbc'. @@ -198,9 +215,6 @@ SQLSMALLINT fetch_server_attr(esodbc_dbc_st *dbc, SQLINTEGER attr_id, /* 0-term room left out when binding */ buff[attr_val.cnt] = L'\0'; /* write_wstr() expects the 0-term */ } - if (attr_id == SQL_ATTR_CURRENT_CATALOG) { - set_current_catalog(dbc, &attr_val); - } } DBGH(dbc, "attribute %ld value: `" LWPDL "`.", attr_id, LWSTR(&attr_val)); diff --git a/driver/catalogue.h b/driver/catalogue.h index 38e6f33f..fa91b8af 100644 --- a/driver/catalogue.h +++ b/driver/catalogue.h @@ -35,7 +35,8 @@ enum { SQLSMALLINT fetch_server_attr(esodbc_dbc_st *dbc, SQLINTEGER attr_id, SQLWCHAR *dest, SQLSMALLINT room); -BOOL TEST_API set_current_catalog(esodbc_dbc_st *dbc, wstr_st *catalog); +SQLRETURN set_current_catalog(esodbc_dbc_st *dbc, wstr_st *catalog); +void free_current_catalog(esodbc_dbc_st *dbc); SQLRETURN TEST_API update_varchar_defs(esodbc_stmt_st *stmt); diff --git a/driver/connect.c b/driver/connect.c index 683f30ee..1bccd305 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -1501,6 +1501,11 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) /* early execution */ dbc->early_exec = wstr2bool(&attrs->early_exec); INFOH(dbc, "early execution: %s.", dbc->early_exec ? "true" : "false"); + /* default current catalog */ + if (attrs->catalog.cnt && + (! SQL_SUCCEEDED(set_current_catalog(dbc, &attrs->catalog)))) { + goto err; + } /* how to print the floats? */ assert(1 <= attrs->sci_floats.cnt); /* default should apply */ @@ -1657,13 +1662,9 @@ void cleanup_dbc(esodbc_dbc_st *dbc) dbc->srv_ver.str = NULL; dbc->srv_ver.cnt = 0; } - if (dbc->catalog.str) { - free(dbc->catalog.str); - dbc->catalog.str = NULL; - dbc->catalog.cnt = 0; - } else { - assert(dbc->catalog.cnt == 0); - } + free_current_catalog(dbc); + assert(dbc->catalog.w.cnt == 0); + assert(dbc->catalog.c.cnt == 0); if (dbc->varchar_limit_str.str) { free(dbc->varchar_limit_str.str); dbc->varchar_limit_str.str = NULL; @@ -3266,38 +3267,6 @@ SQLRETURN EsSQLDisconnect(SQLHDBC ConnectionHandle) return SQL_SUCCESS; } -/* ES/SQL doesn't support catalogs (yet). This function checks that a - * previously retrieved (and cached) catalog value is the same with what the - * app currently tries to set it to. - * Ideally, the app provided value would be cached here too (as per the spec: - * "SQL_ATTR_CURRENT_CATALOG can be set before or after connecting"), in case - * there's no connection "established" yet and checked at "establishment" - * time. But there's no client reported yet setting a catalog value before - * connecting. */ -static SQLRETURN check_catalog_name(esodbc_dbc_st *dbc, SQLWCHAR *name, - SQLINTEGER len) -{ - wstr_st catalog; - catalog.str = name; - if (len < 0) { - catalog.cnt = wcslen(name); - } else { - catalog.cnt = ((size_t)len)/sizeof(SQLWCHAR); - } - if (! EQ_WSTR(&dbc->catalog, &catalog)) { - if (! dbc->catalog.cnt) { - /* this will happen if the app tries to set a value that it - * discovered over a different connection. - * TODO on a first reported issue. */ - WARNH(dbc, "connection's current catalog not yet set!"); - } - ERRH(dbc, "setting catalog name not supported."); - RET_HDIAGS(dbc, SQL_STATE_HYC00); - } - WARNH(dbc, "ignoring attempt to set the current catalog."); - return SQL_SUCCESS; -} - /* * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/unicode-drivers : * """ @@ -3325,6 +3294,7 @@ SQLRETURN EsSQLSetConnectAttrW( _In_reads_bytes_opt_(StringLength) SQLPOINTER Value, SQLINTEGER StringLength) { + wstr_st catalog; esodbc_dbc_st *dbc = DBCH(ConnectionHandle); switch(Attribute) { @@ -3434,11 +3404,13 @@ SQLRETURN EsSQLSetConnectAttrW( RET_HDIAGS(dbc, SQL_STATE_IM009); case SQL_ATTR_CURRENT_CATALOG: - INFOH(dbc, "setting current catalog to: `" LWPDL "`.", - /* string should be 0-term'd */ - 0 <= StringLength ? StringLength/sizeof(SQLWCHAR) : SHRT_MAX, - (SQLWCHAR *)Value); - return check_catalog_name(dbc, (SQLWCHAR *)Value, StringLength); + if (StringLength < 0) { + ERRH(dbc, "invalid catalog name lenght: %ld", StringLength); + RET_HDIAGS(dbc, SQL_STATE_HY090); + } + catalog.str = (SQLWCHAR *)Value; + catalog.cnt = StringLength/sizeof(SQLWCHAR); + return set_current_catalog(dbc, &catalog); case SQL_ATTR_TRACE: case SQL_ATTR_TRACEFILE: /* DM-only */ @@ -3504,7 +3476,16 @@ SQLRETURN EsSQLGetConnectAttrW( ERRH(dbc, "no connection active."); RET_HDIAGS(dbc, SQL_STATE_08003); } - if ((used = fetch_server_attr(dbc, SQL_ATTR_CURRENT_CATALOG, + if (dbc->catalog.w.cnt) { + if (! SQL_SUCCEEDED(write_wstr(dbc, (SQLWCHAR *)ValuePtr, + &dbc->catalog.w, (SQLSMALLINT)BufferLength, + &used))) { + ERRH(dbc, "failed to copy current catalog out."); + RET_STATE(dbc->hdr.diag.state); + } + used = SHRT_MAX <= dbc->catalog.w.cnt ? SHRT_MAX : + (SQLSMALLINT)dbc->catalog.w.cnt; + } else if ((used = fetch_server_attr(dbc, SQL_ATTR_CURRENT_CATALOG, (SQLWCHAR *)ValuePtr, (SQLSMALLINT)BufferLength)) < 0) { ERRH(dbc, "failed to get current catalog."); @@ -3583,14 +3564,14 @@ SQLRETURN EsSQLGetConnectAttrW( /* MS Access/Jet proprietary info type */ case 30002: ERRH(dbc, "unsupported info type."); - RET_HDIAGS(DBCH(ConnectionHandle), SQL_STATE_HY092); + RET_HDIAGS(dbc, SQL_STATE_HY092); #endif default: ERRH(dbc, "unknown Attribute type %ld.", Attribute); // FIXME: add the other attributes FIXME; - RET_HDIAGS(DBCH(ConnectionHandle), SQL_STATE_HY092); + RET_HDIAGS(dbc, SQL_STATE_HY092); } return SQL_SUCCESS; diff --git a/driver/handles.h b/driver/handles.h index 6dc74874..20e58f0c 100644 --- a/driver/handles.h +++ b/driver/handles.h @@ -131,7 +131,6 @@ typedef struct struct_dbc { wstr_st dsn; /* data source name SQLGetInfo(SQL_DATA_SOURCE_NAME) */ wstr_st server; /* ~ name; requested with SQLGetInfo(SQL_SERVER_NAME) */ - wstr_st catalog; /* cached value; checked against if app setting it */ wstr_st srv_ver; /* server version: SQLGetInfo(SQL_DBMS_VER) */ cstr_st proxy_url; @@ -166,6 +165,10 @@ typedef struct struct_dbc { ESODBC_CMPSS_AUTO, } compression; BOOL apply_tz; /* should the times be converted from UTC to local TZ? */ + struct { + wstr_st w; /* NB: w.str and c.str are co-allocated */ + cstr_st c; + } catalog; /* current ~ */ BOOL early_exec; /* should prepared, non-param queries be exec'd early? */ enum { ESODBC_FLTS_DEFAULT = 0, @@ -559,7 +562,7 @@ SQLRETURN EsSQLSetDescRec( /* return the code associated with the given state (and debug-log) */ #define RET_STATE(_s) \ do { \ - assert(_s < SQL_STATE_MAX); \ + assert(SQL_STATE_00000 <= _s && _s < SQL_STATE_MAX); \ return esodbc_errors[_s].retcode; \ } while (0) diff --git a/driver/queries.c b/driver/queries.c index 0e463394..6a8057e5 100644 --- a/driver/queries.c +++ b/driver/queries.c @@ -3052,28 +3052,40 @@ static SQLRETURN statement_len_cbor(esodbc_stmt_st *stmt, size_t *enc_len, /* "field_multi_value_leniency": true/false */ bodylen += cbor_str_obj_len(sizeof(REQ_KEY_MULTIVAL) - 1); bodylen += CBOR_OBJ_BOOL_LEN; + (*keys) ++; /* "index_include_frozen": true/false */ bodylen += cbor_str_obj_len(sizeof(REQ_KEY_IDX_FROZEN) - 1); bodylen += CBOR_OBJ_BOOL_LEN; + (*keys) ++; /* "time_zone": "-05:45" */ bodylen += cbor_str_obj_len(sizeof(REQ_KEY_TIMEZONE) - 1); bodylen += cbor_str_obj_len(tz_param.cnt); /* lax len */ + (*keys) ++; + /* "catalog": "my_cluster" */ + if (dbc->catalog.c.cnt) { + bodylen += cbor_str_obj_len(sizeof(REQ_KEY_CATALOG) - 1); + bodylen += cbor_str_obj_len(dbc->catalog.c.cnt); + (*keys) ++; + } bodylen += cbor_str_obj_len(sizeof(REQ_KEY_VERSION) - 1); bodylen += cbor_str_obj_len(version.cnt); - *keys += 4; /* field_m._val., idx._inc._frozen, time_zone, version */ + (*keys) ++; } /* mode */ bodylen += cbor_str_obj_len(sizeof(REQ_KEY_MODE) - 1); bodylen += cbor_str_obj_len(sizeof(REQ_VAL_MODE) - 1); + (*keys) ++; /* client_id */ bodylen += cbor_str_obj_len(sizeof(REQ_KEY_CLT_ID) - 1); bodylen += cbor_str_obj_len(sizeof(REQ_VAL_CLT_ID) - 1); + (*keys) ++; /* binary_format */ bodylen += cbor_str_obj_len(sizeof(REQ_KEY_BINARY_FMT) - 1); bodylen += CBOR_OBJ_BOOL_LEN; - *keys += 3; /* mode, client_id, binary_format */ + (*keys) ++; /* TODO: request_/page_timeout */ + assert(*keys <= REST_REQ_KEY_COUNT); *enc_len = bodylen; return SQL_SUCCESS; } @@ -3125,6 +3137,12 @@ static SQLRETURN statement_len_json(esodbc_stmt_st *stmt, size_t *outlen) /* "time_zone": "-05:45" */ bodylen += sizeof(JSON_KEY_TIMEZONE) - 1; bodylen += tz_param.cnt; + /* "catalog": "my_cluster" */ + if (dbc->catalog.c.cnt) { + bodylen += sizeof(JSON_KEY_CATALOG) - 1; + bodylen += dbc->catalog.c.cnt; + bodylen += /* 2x `"` */2; + } /* "version": */ bodylen += sizeof(JSON_KEY_VERSION) - 1; bodylen += version.cnt + /* 2x`"` */2; @@ -3407,6 +3425,14 @@ static SQLRETURN serialize_to_cbor(esodbc_stmt_st *stmt, cstr_st *dest, } err = cbor_encode_text_string(&map, tz.str, tz.cnt); FAIL_ON_CBOR_ERR(stmt, err); + if (dbc->catalog.c.cnt) { + err = cbor_encode_text_string(&map, REQ_KEY_CATALOG, + sizeof(REQ_KEY_CATALOG) - 1); + FAIL_ON_CBOR_ERR(stmt, err); + err = cbor_encode_text_string(&map, dbc->catalog.c.str, + dbc->catalog.c.cnt); + FAIL_ON_CBOR_ERR(stmt, err); + } /* version */ err = cbor_encode_text_string(&map, REQ_KEY_VERSION, sizeof(REQ_KEY_VERSION) - 1); @@ -3535,6 +3561,15 @@ static SQLRETURN serialize_to_json(esodbc_stmt_st *stmt, cstr_st *dest) sizeof(JSON_VAL_TIMEZONE_Z) - 1); pos += sizeof(JSON_VAL_TIMEZONE_Z) - 1; } + if (dbc->catalog.c.cnt) { + /* "catalog": "my_cluster" */ + memcpy(body + pos, JSON_KEY_CATALOG, sizeof(JSON_KEY_CATALOG) - 1); + pos += sizeof(JSON_KEY_CATALOG) - 1; + body[pos ++] = '"'; + memcpy(body + pos, dbc->catalog.c.str, dbc->catalog.c.cnt); + pos += dbc->catalog.c.cnt; + body[pos ++] = '"'; + } /* "version": ... */ memcpy(body + pos, JSON_KEY_VERSION, sizeof(JSON_KEY_VERSION) - 1); pos += sizeof(JSON_KEY_VERSION) - 1; diff --git a/driver/queries.h b/driver/queries.h index cdec1b57..f4c1b9dc 100644 --- a/driver/queries.h +++ b/driver/queries.h @@ -142,12 +142,15 @@ SQLRETURN EsSQLRowCount(_In_ SQLHSTMT StatementHandle, _Out_ SQLLEN *RowCount); #define REQ_KEY_MULTIVAL "field_multi_value_leniency" #define REQ_KEY_IDX_FROZEN "index_include_frozen" #define REQ_KEY_TIMEZONE "time_zone" +#define REQ_KEY_CATALOG "catalog" #define REQ_KEY_BINARY_FMT "binary_format" + +#define REST_REQ_KEY_COUNT 13 /* "query" / "cursor" count as one */ + /* keys for the "params" argument */ #define REQ_KEY_PARAM_TYPE "type" #define REQ_KEY_PARAM_VAL "value" -#define REST_REQ_KEY_COUNT 11 /* "query" or "cursor" */ #ifdef _WIN64 # define REQ_VAL_CLT_ID "odbc64" @@ -172,6 +175,7 @@ SQLRETURN EsSQLRowCount(_In_ SQLHSTMT StatementHandle, _Out_ SQLLEN *RowCount); #define JSON_KEY_MULTIVAL ", \"" REQ_KEY_MULTIVAL "\": " /* n-th */ #define JSON_KEY_IDX_FROZEN ", \"" REQ_KEY_IDX_FROZEN "\": " /* n-th */ #define JSON_KEY_TIMEZONE ", \"" REQ_KEY_TIMEZONE "\": " /* n-th key */ +#define JSON_KEY_CATALOG ", \"" REQ_KEY_CATALOG "\": " /* n-th key */ #define JSON_KEY_BINARY_FMT ", \"" REQ_KEY_BINARY_FMT "\": " /* n-th key */ #define JSON_VAL_TIMEZONE_Z "\"" REQ_VAL_TIMEZONE_Z "\"" diff --git a/test/test_driverconnect.cc b/test/test_driverconnect.cc index 060afe63..5065b996 100644 --- a/test/test_driverconnect.cc +++ b/test/test_driverconnect.cc @@ -74,23 +74,28 @@ TEST_F(DriverConnect, OutputTruncated) } -TEST_F(DriverConnect, ReinitCurrentCatalog) +TEST_F(DriverConnect, ConnectWithDefaultCatalog) { - ret = SQLDriverConnect(my_dbc, (SQLHWND)&types, (SQLWCHAR *)CONNECT_STRING, - sizeof(CONNECT_STRING) / sizeof(CONNECT_STRING[0]) - 1, NULL, 0, +# define MY_CATALOG "my_catalog" +# define CONNECT_STR_CAT CONNECT_STRING "Catalog=" MY_CATALOG ";" + + ret = SQLDriverConnect(my_dbc, (SQLHWND)&types, (SQLWCHAR *)CONNECT_STR_CAT, + sizeof(CONNECT_STR_CAT) / sizeof(CONNECT_STR_CAT[0]) - 1, NULL, 0, &out_avail, ESODBC_SQL_DRIVER_TEST); ASSERT_TRUE(SQL_SUCCEEDED(ret)); - esodbc_dbc_st *dbc = (esodbc_dbc_st *)my_dbc; - wstr_st crr_cat = WSTR_INIT("crr_catalog"); - ASSERT_TRUE(set_current_catalog(dbc, &crr_cat)); - ASSERT_FALSE(set_current_catalog(dbc, &crr_cat)); + SQLWCHAR buff[sizeof(CONNECT_STR_CAT)]; + SQLINTEGER len; + ASSERT_TRUE(SQL_SUCCEEDED(SQLGetConnectAttrW(my_dbc, + SQL_ATTR_CURRENT_CATALOG, (SQLPOINTER)buff, sizeof(buff), &len))); + ASSERT_STREQ(MK_WPTR(MY_CATALOG), buff); + ASSERT_EQ(sizeof(MY_CATALOG) - /*\0*/1, len); - wstr_st other_cat = WSTR_INIT("other_catalog"); - ASSERT_TRUE(set_current_catalog(dbc, &other_cat)); +# undef CONNECT_STR_CAT +# undef MY_CATALOG } -TEST_F(DriverConnect, ResetCurrentCatalog) +TEST_F(DriverConnect, SetResetUnsetCurrentCatalog) { ret = SQLDriverConnect(my_dbc, (SQLHWND)&types, (SQLWCHAR *)CONNECT_STRING, sizeof(CONNECT_STRING) / sizeof(CONNECT_STRING[0]) - 1, NULL, 0, @@ -98,16 +103,51 @@ TEST_F(DriverConnect, ResetCurrentCatalog) ASSERT_TRUE(SQL_SUCCEEDED(ret)); esodbc_dbc_st *dbc = (esodbc_dbc_st *)my_dbc; - wstr_st crr_cat = WSTR_INIT("crr_catalog"); - ASSERT_TRUE(set_current_catalog(dbc, &crr_cat)); + ASSERT_EQ(NULL, dbc->catalog.w.str); + ASSERT_EQ(0, dbc->catalog.w.cnt); + ASSERT_EQ(NULL, dbc->catalog.c.str); + ASSERT_EQ(0, dbc->catalog.c.cnt); + + // + // Set + // + wstr_st crr_cat = WSTR_INIT("current_catalog"); ASSERT_TRUE(SQL_SUCCEEDED(SQLSetConnectAttrW(my_dbc, SQL_ATTR_CURRENT_CATALOG, (SQLPOINTER)crr_cat.str, (SQLINTEGER)(crr_cat.cnt * sizeof(SQLWCHAR))))); + SQLWCHAR buff[32]; // accomodates 'other_cat' too + ASSERT_GT(sizeof(buff)/sizeof(*buff), crr_cat.cnt + /*\0*/1); + SQLINTEGER len; + ASSERT_TRUE(SQL_SUCCEEDED(SQLGetConnectAttrW(my_dbc, + SQL_ATTR_CURRENT_CATALOG, (SQLPOINTER)buff, sizeof(buff), &len))); + ASSERT_STREQ(crr_cat.str, buff); + ASSERT_EQ(crr_cat.cnt, len); + + // + // Reset + // wstr_st other_cat = WSTR_INIT("other_catalog"); - ASSERT_FALSE(SQL_SUCCEEDED(SQLSetConnectAttrW(my_dbc, + ASSERT_TRUE(SQL_SUCCEEDED(SQLSetConnectAttrW(my_dbc, SQL_ATTR_CURRENT_CATALOG, (SQLPOINTER)other_cat.str, (SQLINTEGER)(other_cat.cnt * sizeof(SQLWCHAR))))); + + ASSERT_GT(sizeof(buff)/sizeof(*buff), other_cat.cnt + /*\0*/1); + ASSERT_TRUE(SQL_SUCCEEDED(SQLGetConnectAttrW(my_dbc, + SQL_ATTR_CURRENT_CATALOG, (SQLPOINTER)buff, sizeof(buff), &len))); + ASSERT_STREQ(other_cat.str, buff); + ASSERT_EQ(other_cat.cnt, len); + + // + // Unset + // + ASSERT_TRUE(SQL_SUCCEEDED(SQLSetConnectAttrW(my_dbc, + SQL_ATTR_CURRENT_CATALOG, NULL, 0))); + /* SQLGetConnectAttrW() would now trigger a `SELECT database()` */ + ASSERT_EQ(NULL, dbc->catalog.w.str); + ASSERT_EQ(0, dbc->catalog.w.cnt); + ASSERT_EQ(NULL, dbc->catalog.c.str); + ASSERT_EQ(0, dbc->catalog.c.cnt); } } // test namespace diff --git a/test/test_queries.cc b/test/test_queries.cc index 0e0504dd..3b6f7c48 100644 --- a/test/test_queries.cc +++ b/test/test_queries.cc @@ -328,6 +328,53 @@ TEST_F(Queries, SQLDescribeCol_varchar_lim) { ASSERT_EQ(col_size, INT_MAX); /* binary col's length must not be affected */ } +TEST_F(Queries, serialize_with_catalog) { +# define CURRENT_CATALOG "current_catalog_at_" __TIMESTAMP__ + + wstr_st crr_cat = WSTR_INIT(CURRENT_CATALOG); + ASSERT_TRUE(SQL_SUCCEEDED(SQLSetConnectAttrW(dbc, + SQL_ATTR_CURRENT_CATALOG, (SQLPOINTER)crr_cat.str, + (SQLINTEGER)(crr_cat.cnt * sizeof(SQLWCHAR))))); + + const size_t bsz = 64; + wchar_t buff[bsz]; + wstr_st select = WSTR_INIT("SELECT '"); + cstr_st body = {(SQLCHAR *)buff, /*same size as the Q: force realloc*/bsz}; + + /* construct a valid query */ + wmemset(buff, L'*', bsz); + wmemcpy(buff, select.str, select.cnt); + buff[bsz - 2] = L'\''; + buff[bsz - 1] = L'\0'; + + ASSERT_TRUE(SQL_SUCCEEDED(attach_sql(STMH(stmt), (SQLWCHAR *)buff, + bsz - 1))); + + /* JSON test */ + DBCH(dbc)->pack_json = TRUE; + ASSERT_TRUE(SQL_SUCCEEDED(serialize_statement(STMH(stmt), &body))); + ASSERT_NE((void *)body.str, (void *)buff); + ASSERT_NE(nullptr, + strstr((char *)body.str, JSON_KEY_CATALOG "\"" CURRENT_CATALOG "\"")); + free(body.str); + body.str = NULL; + body.cnt = 0; + + /* CBOR test */ + DBCH(dbc)->pack_json = FALSE; + ASSERT_TRUE(SQL_SUCCEEDED(serialize_statement(STMH(stmt), &body))); + ASSERT_NE((void *)body.str, (void *)buff); + ASSERT_NE(nullptr, + // `x+` : the ASCII (partial) representation of the packed map + strstr((char *)body.str, REQ_KEY_CATALOG "x+" CURRENT_CATALOG)); + free(body.str); + + ASSERT_TRUE(SQL_SUCCEEDED(SQLSetConnectAttrW(dbc, + SQL_ATTR_CURRENT_CATALOG, NULL, 0))); + +# undef CURRENT_CATALOG +} + } // test namespace /* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */