From 01abe681e0b3f25b908348cab2b9b20993ccad21 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Mon, 18 Oct 2021 12:02:13 +0300 Subject: [PATCH] Add catalog support This adds support for multiple catalogs ("databases"). The client application can now set the catalog at connection level - with SQLSetConnectAttr - to a pattern matching a local or remote cluster name and ES/SQL will execute the query on the respective cluster/s. A default catalog can also be set through the connection string, through the "Catalog" DSN attribute. In case the catalog is not set, or set to an empty or null string, the local catalog will be chosen for execution. The driver will simply add a "catalog" request attribute containing the value received from the application, or skip adding the attribute if this is empty. --- driver/catalogue.c | 64 +++++++++++++++++++------------- driver/catalogue.h | 3 +- driver/connect.c | 75 ++++++++++++++------------------------ driver/handles.h | 7 +++- driver/queries.c | 39 +++++++++++++++++++- driver/queries.h | 6 ++- test/test_driverconnect.cc | 66 ++++++++++++++++++++++++++------- test/test_queries.cc | 47 ++++++++++++++++++++++++ 8 files changed, 216 insertions(+), 91 deletions(-) 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 : */