Re: MS query notifications patch
Richard Hughes <[email protected]>
| Newsgroups | gmane.comp.db.tds.freetds |
|---|---|
| Message-ID | <[email protected]> |
On 2014-05-10 at 09:55, Frediano Ziglio wrote: > 2014-04-23 18:50 GMT+01:00 Richard Hughes <[email protected]>: >> On 2014-04-18 at 09:39, Frediano Ziglio wrote: >>> 2014-04-16 19:12 GMT+01:00 Richard Hughes <[email protected]>: >>>> I needed support for SQL Server 2005's Query Notifications feature, so >>>> here's a patch against tip. > Sorry for late reply. I was on holiday and actually I'm without my own computer. This entire thread seems to be plagued with late replies - work got busy for me :-) > I like the idea of splitting in query.c the code that handle tds7 from > other protocol however I would not expose all these definitions > outside it and instead all function calls should add the head > parameter. I had ruled out that option because I didn't think I was allowed to change the TDS API. If that breakage is allowed then absolutely adding a new parameter is cleaner. This version of the patch does, however, undo the splitting-out of tds7. I find it difficult to manage and review patches (even my own) which combine cosmetic changes with functional changes. Once we're happy with the functional stuff then either you or I can do the split as a separate task. ... > It make sense but perhaps here would > be more sensible to add a tds_start_query_head (or similar) function. Done. >>>> @@ -228,6 +229,11 @@ prepare_call(struct _hstmt * stmt) >>>> else >>>> return SQL_ERROR; >>>> >>>> + if (tds_dstr_isempty(&stmt->attr.qn_msgtext) != tds_dstr_isempty(&stmt->attr.qn_options)) >>>> + return SQL_SUCCESS_WITH_INFO; >>>> + if (!tds_dstr_isempty(&stmt->attr.qn_msgtext) && !IS_TDS72_PLUS(stmt->dbc->tds_socket->conn)) >>>> + return SQL_SUCCESS_WITH_INFO; >>>> + >>> >>> I'm not sure this is the right place. Also I think MS add some error >>> to the statement. I didn't find from documentation which error should >>> be reported. >> >> Yes - the _WITH_INFO part should have been a clue that I needed to >> return some info. MS Native Client returns HY000 for the first case >> and still executes the query, but I don't have a pre-2005 server to >> find out what should happen in the second case so I just left it as a >> generic error. >> > > I'll try with a former server. If it's inconvenient for you, I can get all the installation media for 2000 - I just need to find the time to spin up a virtual machine and get it running and tested. Richard. _______________________________________________ FreeTDS mailing list [email protected] http://lists.ibiblio.org/mailman/listinfo/freetds
freetds-mssql-query-notifications-v3.diff
(application/octet-stream, 24 KB)
diff --git a/include/freetds/odbc.h b/include/freetds/odbc.h
index 4413b61..17ee0f4 100644
--- a/include/freetds/odbc.h
+++ b/include/freetds/odbc.h
@@ -379,6 +379,9 @@ struct _hsattr
/* SQLGetStmtAttr only */
/* TDS_DESC *imp_row_desc; */
/* TDS_DESC *imp_param_desc; */
+ DSTR qn_msgtext;
+ DSTR qn_options;
+ SQLUINTEGER qn_timeout;
};
typedef enum
diff --git a/include/freetds/tds.h b/include/freetds/tds.h
index 413f1d8..79d5d83 100644
--- a/include/freetds/tds.h
+++ b/include/freetds/tds.h
@@ -587,6 +587,14 @@ typedef struct tds_login
unsigned int valid_configuration:1;
} TDSLOGIN;
+typedef struct tds_headers
+{
+ const char *qn_options;
+ const char *qn_msgtext;
+ TDS_INT qn_timeout;
+ /* TDS 7.4+: trace activity ID char[20] */
+} TDSHEADERS;
+
typedef struct tds_locale
{
char *language;
@@ -1274,10 +1282,10 @@ int tds_connect_and_login(TDSSOCKET * tds, TDSLOGIN * login);
/* query.c */
TDSRET tds_submit_query(TDSSOCKET * tds, const char *query);
-TDSRET tds_submit_query_params(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params);
+TDSRET tds_submit_query_params(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head);
TDSRET tds_submit_queryf(TDSSOCKET * tds, const char *queryf, ...);
TDSRET tds_submit_prepare(TDSSOCKET * tds, const char *query, const char *id, TDSDYNAMIC ** dyn_out, TDSPARAMINFO * params);
-TDSRET tds_submit_execdirect(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params);
+TDSRET tds_submit_execdirect(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head);
TDSRET tds71_submit_prepexec(TDSSOCKET * tds, const char *query, const char *id, TDSDYNAMIC ** dyn_out, TDSPARAMINFO * params);
TDSRET tds_submit_execute(TDSSOCKET * tds, TDSDYNAMIC * dyn);
TDSRET tds_send_cancel(TDSSOCKET * tds);
@@ -1285,7 +1293,7 @@ const char *tds_next_placeholder(const char *start);
int tds_count_placeholders(const char *query);
int tds_needs_unprepare(TDSSOCKET * tds, TDSDYNAMIC * dyn);
TDSRET tds_submit_unprepare(TDSSOCKET * tds, TDSDYNAMIC * dyn);
-TDSRET tds_submit_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params);
+TDSRET tds_submit_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params, TDSHEADERS * head);
TDSRET tds_submit_optioncmd(TDSSOCKET * tds, TDS_OPTION_CMD command, TDS_OPTION option, TDS_OPTION_ARG *param, TDS_INT param_size);
TDSRET tds_submit_begin_tran(TDSSOCKET *tds);
TDSRET tds_submit_rollback(TDSSOCKET *tds, int cont);
@@ -1312,7 +1320,7 @@ TDSRET tds_cursor_dealloc(TDSSOCKET * tds, TDSCURSOR * cursor);
TDSRET tds_cursor_update(TDSSOCKET * tds, TDSCURSOR * cursor, TDS_CURSOR_OPERATION op, TDS_INT i_row, TDSPARAMINFO * params);
TDSRET tds_cursor_setname(TDSSOCKET * tds, TDSCURSOR * cursor);
-TDSRET tds_multiple_init(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDS_MULTIPLE_TYPE type);
+TDSRET tds_multiple_init(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDS_MULTIPLE_TYPE type, TDSHEADERS * head);
TDSRET tds_multiple_done(TDSSOCKET *tds, TDSMULTIPLE *multiple);
TDSRET tds_multiple_query(TDSSOCKET *tds, TDSMULTIPLE *multiple, const char *query, TDSPARAMINFO * params);
TDSRET tds_multiple_execute(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDSDYNAMIC * dyn);
diff --git a/include/odbcss.h b/include/odbcss.h
index 6582559..bb5f5b3 100644
--- a/include/odbcss.h
+++ b/include/odbcss.h
@@ -22,3 +22,6 @@
#define SQL_DIAG_SS_MSGSTATE (-1150)
#define SQL_DIAG_SS_LINE (-1154)
+#define SQL_SOPT_SS_QUERYNOTIFICATION_TIMEOUT 1233
+#define SQL_SOPT_SS_QUERYNOTIFICATION_MSGTEXT 1234
+#define SQL_SOPT_SS_QUERYNOTIFICATION_OPTIONS 1235
diff --git a/src/ctlib/ct.c b/src/ctlib/ct.c
index 38a51cf..722de95 100644
--- a/src/ctlib/ct.c
+++ b/src/ctlib/ct.c
@@ -949,7 +949,7 @@ ct_send(CS_COMMAND * cmd)
}
pparam_info = paraminfoalloc(tds, rpc->param_list);
- ret = tds_submit_rpc(tds, rpc->name, pparam_info);
+ ret = tds_submit_rpc(tds, rpc->name, pparam_info, NULL);
tds_free_param_results(pparam_info);
@@ -967,7 +967,7 @@ ct_send(CS_COMMAND * cmd)
ret = TDS_FAIL;
if (cmd->input_params) {
pparam_info = paraminfoalloc(tds, cmd->input_params);
- ret = tds_submit_query_params(tds, cmd->query, pparam_info);
+ ret = tds_submit_query_params(tds, cmd->query, pparam_info, NULL);
tds_free_param_results(pparam_info);
} else {
ret = tds_submit_query(tds, cmd->query);
diff --git a/src/dblib/rpc.c b/src/dblib/rpc.c
index 6b35bca..5e322d5 100644
--- a/src/dblib/rpc.c
+++ b/src/dblib/rpc.c
@@ -301,7 +301,7 @@ dbrpcsend(DBPROCESS * dbproc)
if (!pparam_info)
return FAIL;
}
- erc = tds_submit_rpc(dbproc->tds_socket, dbproc->rpc->name, pparam_info);
+ erc = tds_submit_rpc(dbproc->tds_socket, dbproc->rpc->name, pparam_info, NULL);
tds_free_param_results(pparam_info);
if (erc == TDS_FAIL) {
tdsdump_log(TDS_DBG_INFO1, "returning FAIL: tds_submit_rpc() failed\n");
diff --git a/src/odbc/native.c b/src/odbc/native.c
index decfcb9..6581bb0 100644
--- a/src/odbc/native.c
+++ b/src/odbc/native.c
@@ -36,6 +36,7 @@
#include <assert.h>
#include <freetds/odbc.h>
+#include <freetds/string.h>
#ifdef DMALLOC
#include <dmalloc.h>
@@ -228,6 +229,11 @@ prepare_call(struct _hstmt * stmt)
else
return SQL_ERROR;
+ if ((!tds_dstr_isempty(&stmt->attr.qn_msgtext) || !tds_dstr_isempty(&stmt->attr.qn_options)) && !IS_TDS72_PLUS(stmt->dbc->tds_socket->conn)) {
+ odbc_errs_add(&stmt->errs, "HY000", "Feature is not supported by this server");
+ return SQL_SUCCESS_WITH_INFO;
+ }
+
if ((rc = to_native(stmt->dbc, stmt, buf)) != SQL_SUCCESS)
return rc;
diff --git a/src/odbc/odbc.c b/src/odbc/odbc.c
index 2f57ac3..c3b2e99 100644
--- a/src/odbc/odbc.c
+++ b/src/odbc/odbc.c
@@ -55,6 +55,7 @@
#include <freetds/convert.h>
#include "replacements.h"
#include "sqlwparams.h"
+#include <odbcss.h>
#ifdef DMALLOC
#include <dmalloc.h>
@@ -71,9 +72,9 @@ static SQLRETURN _SQLFreeEnv(SQLHENV henv);
static SQLRETURN _SQLFreeStmt(SQLHSTMT hstmt, SQLUSMALLINT fOption, int force);
static SQLRETURN _SQLFreeDesc(SQLHDESC hdesc);
static SQLRETURN _SQLExecute(TDS_STMT * stmt);
-static SQLRETURN _SQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
+static SQLRETURN _SQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength WIDE);
static SQLRETURN _SQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER BufferLength,
- SQLINTEGER * StringLength);
+ SQLINTEGER * StringLength WIDE);
static SQLRETURN _SQLColAttribute(SQLHSTMT hstmt, SQLUSMALLINT icol, SQLUSMALLINT fDescType, SQLPOINTER rgbDesc,
SQLSMALLINT cbDescMax, SQLSMALLINT FAR * pcbDesc, SQLLEN FAR * pfDesc _WIDE);
static SQLRETURN _SQLFetch(TDS_STMT * stmt, SQLSMALLINT FetchOrientation, SQLLEN FetchOffset);
@@ -1070,10 +1071,10 @@ SQLParamOptions(SQLHSTMT hstmt, SQLULEN crow, SQLULEN FAR * pirow)
tdsdump_log(TDS_DBG_FUNC, "SQLParamOptions(%p, %lu, %p)\n", hstmt, (unsigned long int)crow, pirow);
/* emulate for ODBC 2 DM */
- res = _SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, pirow, 0);
+ res = _SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, pirow, 0 _wide0);
if (res != SQL_SUCCESS)
return res;
- return _SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) (TDS_INTPTR) crow, 0);
+ return _SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) (TDS_INTPTR) crow, 0 _wide0);
}
ODBC_FUNC(SQLPrimaryKeys, (P(SQLHSTMT,hstmt), PCHARIN(CatalogName,SQLSMALLINT),
@@ -1744,6 +1745,9 @@ _SQLAllocStmt(SQLHDBC hdbc, SQLHSTMT FAR * phstmt)
assert(stmt->ird->header.sql_desc_rows_processed_ptr == NULL);
stmt->attr.simulate_cursor = SQL_SC_NON_UNIQUE;
stmt->attr.use_bookmarks = SQL_UB_OFF;
+ tds_dstr_init(&stmt->attr.qn_msgtext);
+ tds_dstr_init(&stmt->attr.qn_options);
+ stmt->attr.qn_timeout = 432000;
stmt->sql_rowset_size = 1;
@@ -1760,7 +1764,7 @@ _SQLAllocStmt(SQLHDBC hdbc, SQLHSTMT FAR * phstmt)
*phstmt = (SQLHSTMT) stmt;
if (dbc->attr.cursor_type != SQL_CURSOR_FORWARD_ONLY)
- _SQLSetStmtAttr(stmt, SQL_CURSOR_TYPE, (SQLPOINTER) (TDS_INTPTR) dbc->attr.cursor_type, SQL_IS_INTEGER);
+ _SQLSetStmtAttr(stmt, SQL_CURSOR_TYPE, (SQLPOINTER) (TDS_INTPTR) dbc->attr.cursor_type, SQL_IS_INTEGER _wide0);
ODBC_EXIT_(dbc);
}
@@ -3236,6 +3240,19 @@ odbc_cursor_execute(TDS_STMT * stmt)
return ret;
}
+static TDSHEADERS *
+odbc_init_headers(TDS_STMT * stmt, TDSHEADERS * head)
+{
+ if (tds_dstr_isempty(&stmt->attr.qn_msgtext) || tds_dstr_isempty(&stmt->attr.qn_options))
+ return NULL;
+
+ memset(head, 0, sizeof(*head));
+ head->qn_timeout = stmt->attr.qn_timeout;
+ head->qn_msgtext = tds_dstr_cstr(&stmt->attr.qn_msgtext);
+ head->qn_options = tds_dstr_cstr(&stmt->attr.qn_options);
+ return head;
+}
+
static SQLRETURN
_SQLExecute(TDS_STMT * stmt)
{
@@ -3247,6 +3264,7 @@ _SQLExecute(TDS_STMT * stmt)
SQLUSMALLINT param_status;
int found_info = 0, found_error = 0;
SQLLEN total_rows = TDS_NO_COUNT;
+ TDSHEADERS head;
tdsdump_log(TDS_DBG_FUNC, "_SQLExecute(%p)\n",
stmt);
@@ -3300,7 +3318,7 @@ _SQLExecute(TDS_STMT * stmt)
stmt->prepared_pos = end;
tmp = *end;
*end = 0;
- ret = tds_submit_rpc(tds, name, stmt->params);
+ ret = tds_submit_rpc(tds, name, stmt->params, odbc_init_headers(stmt, &head));
*end = tmp;
} else if (stmt->attr.cursor_type != SQL_CURSOR_FORWARD_ONLY || stmt->attr.concurrency != SQL_CONCUR_READ_ONLY) {
ret = odbc_cursor_execute(stmt);
@@ -3310,15 +3328,15 @@ _SQLExecute(TDS_STMT * stmt)
/* SQLExecDirect */
if (stmt->num_param_rows <= 1) {
if (!stmt->params) {
- ret = tds_submit_query(tds, stmt->query);
+ ret = tds_submit_query_params(tds, stmt->query, NULL, odbc_init_headers(stmt, &head));
} else {
- ret = tds_submit_execdirect(tds, stmt->query, stmt->params);
+ ret = tds_submit_execdirect(tds, stmt->query, stmt->params, odbc_init_headers(stmt, &head));
}
} else {
/* pack multiple submit using language */
TDSMULTIPLE multiple;
- ret = tds_multiple_init(tds, &multiple, TDS_MULTIPLE_QUERY);
+ ret = tds_multiple_init(tds, &multiple, TDS_MULTIPLE_QUERY, odbc_init_headers(stmt, &head));
for (stmt->curr_param_row = 0; TDS_SUCCEED(ret); ) {
/* submit a query */
ret = tds_multiple_query(tds, &multiple, stmt->query, stmt->params);
@@ -3381,7 +3399,7 @@ _SQLExecute(TDS_STMT * stmt)
} else {
TDSMULTIPLE multiple;
- ret = tds_multiple_init(tds, &multiple, TDS_MULTIPLE_EXECUTE);
+ ret = tds_multiple_init(tds, &multiple, TDS_MULTIPLE_EXECUTE, NULL);
for (stmt->curr_param_row = 0; TDS_SUCCEED(ret); ) {
dyn = stmt->dyn;
tds_free_input_params(dyn);
@@ -3480,6 +3498,10 @@ _SQLExecute(TDS_STMT * stmt)
}
if ((found_info || found_error) && stmt->errs.lastrc != SQL_ERROR)
stmt->errs.lastrc = SQL_SUCCESS_WITH_INFO;
+ if (tds_dstr_isempty(&stmt->attr.qn_msgtext) != tds_dstr_isempty(&stmt->attr.qn_options)) {
+ odbc_errs_add(&stmt->errs, "HY000", "Attribute ignored");
+ stmt->errs.lastrc = SQL_SUCCESS_WITH_INFO;
+ }
if (found_error && stmt->num_param_rows <= 1)
stmt->errs.lastrc = SQL_ERROR;
if (stmt->curr_param_row < stmt->num_param_rows) {
@@ -4203,6 +4225,8 @@ _SQLFreeStmt(SQLHSTMT hstmt, SQLUSMALLINT fOption, int force)
odbc_errs_reset(&stmt->errs);
odbc_unlock_statement(stmt);
tds_dstr_free(&stmt->cursor_name);
+ tds_dstr_free(&stmt->attr.qn_msgtext);
+ tds_dstr_free(&stmt->attr.qn_options);
desc_free(stmt->ird);
desc_free(stmt->ipd);
desc_free(stmt->orig_ard);
@@ -4283,7 +4307,7 @@ _SQLFreeDesc(SQLHDESC hdesc)
}
static SQLRETURN
-_SQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER BufferLength, SQLINTEGER * StringLength)
+_SQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER BufferLength, SQLINTEGER * StringLength WIDE)
{
void *src;
size_t size;
@@ -4438,6 +4462,20 @@ _SQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEG
size = sizeof(stmt->sql_rowset_size);
src = &stmt->sql_rowset_size;
break;
+ case SQL_SOPT_SS_QUERYNOTIFICATION_TIMEOUT:
+ size = sizeof(stmt->attr.qn_timeout);
+ src = &stmt->attr.qn_timeout;
+ break;
+ case SQL_SOPT_SS_QUERYNOTIFICATION_MSGTEXT:
+ {
+ SQLRETURN rc = odbc_set_string_oct(stmt->dbc, Value, BufferLength, StringLength, tds_dstr_cstr(&stmt->attr.qn_msgtext), tds_dstr_len(&stmt->attr.qn_msgtext));
+ ODBC_EXIT(stmt, rc);
+ }
+ case SQL_SOPT_SS_QUERYNOTIFICATION_OPTIONS:
+ {
+ SQLRETURN rc = odbc_set_string_oct(stmt->dbc, Value, BufferLength, StringLength, tds_dstr_cstr(&stmt->attr.qn_options), tds_dstr_len(&stmt->attr.qn_options));
+ ODBC_EXIT(stmt, rc);
+ }
/* TODO SQL_COLUMN_SEARCHABLE, although ODBC2 */
default:
odbc_errs_add(&stmt->errs, "HY092", NULL);
@@ -4458,7 +4496,7 @@ SQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGE
tdsdump_log(TDS_DBG_FUNC, "SQLGetStmtAttr(%p, %d, %p, %d, %p)\n",
hstmt, (int)Attribute, Value, (int)BufferLength, StringLength);
- return _SQLGetStmtAttr(hstmt, Attribute, Value, BufferLength, StringLength);
+ return _SQLGetStmtAttr(hstmt, Attribute, Value, BufferLength, StringLength _wide0);
}
#ifdef ENABLE_ODBC_WIDE
@@ -4468,7 +4506,7 @@ SQLGetStmtAttrW(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEG
tdsdump_log(TDS_DBG_FUNC, "SQLGetStmtAttr(%p, %d, %p, %d, %p)\n",
hstmt, (int)Attribute, Value, (int)BufferLength, StringLength);
- return _SQLGetStmtAttr(hstmt, Attribute, Value, BufferLength, StringLength);
+ return _SQLGetStmtAttr(hstmt, Attribute, Value, BufferLength, StringLength, 1);
}
#endif
#endif
@@ -4479,7 +4517,7 @@ SQLGetStmtOption(SQLHSTMT hstmt, SQLUSMALLINT fOption, SQLPOINTER pvParam)
tdsdump_log(TDS_DBG_FUNC, "SQLGetStmtOption(%p, %d, %p)\n",
hstmt, fOption, pvParam);
- return _SQLGetStmtAttr(hstmt, (SQLINTEGER) fOption, pvParam, SQL_MAX_OPTION_STRING_LENGTH, NULL);
+ return _SQLGetStmtAttr(hstmt, (SQLINTEGER) fOption, pvParam, SQL_MAX_OPTION_STRING_LENGTH, NULL _wide0);
}
SQLRETURN ODBC_PUBLIC ODBC_API
@@ -6253,7 +6291,7 @@ SQLSetConnectOptionW(SQLHDBC hdbc, SQLUSMALLINT fOption, SQLULEN vParam)
#endif
static SQLRETURN
-_SQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength)
+_SQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength WIDE)
{
SQLULEN ui = (SQLULEN) (TDS_INTPTR) ValuePtr;
SQLUSMALLINT *usip = (SQLUSMALLINT *) ValuePtr;
@@ -6525,6 +6563,33 @@ _SQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLIN
}
stmt->sql_rowset_size = ui;
break;
+ case SQL_SOPT_SS_QUERYNOTIFICATION_TIMEOUT:
+ if (*lp < 1) {
+ odbc_errs_add(&stmt->errs, "HY024", NULL);
+ break;
+ }
+ stmt->attr.qn_timeout = (SQLUINTEGER)*ulp;
+ break;
+ case SQL_SOPT_SS_QUERYNOTIFICATION_MSGTEXT:
+ if (!IS_VALID_LEN(StringLength)) {
+ odbc_errs_add(&stmt->errs, "HY090", NULL);
+ break;
+ }
+ if (!odbc_dstr_copy_oct(stmt->dbc, &stmt->attr.qn_msgtext, StringLength, (ODBC_CHAR*) ValuePtr)) {
+ odbc_errs_add(&stmt->errs, "HY001", NULL);
+ break;
+ }
+ break;
+ case SQL_SOPT_SS_QUERYNOTIFICATION_OPTIONS:
+ if (!IS_VALID_LEN(StringLength)) {
+ odbc_errs_add(&stmt->errs, "HY090", NULL);
+ break;
+ }
+ if (!odbc_dstr_copy_oct(stmt->dbc, &stmt->attr.qn_options, StringLength, (ODBC_CHAR*) ValuePtr)) {
+ odbc_errs_add(&stmt->errs, "HY001", NULL);
+ break;
+ }
+ break;
default:
odbc_errs_add(&stmt->errs, "HY092", NULL);
break;
@@ -6539,7 +6604,7 @@ SQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINT
tdsdump_log(TDS_DBG_FUNC, "SQLSetStmtAttr(%p, %d, %p, %d)\n",
hstmt, (int)Attribute, ValuePtr, (int)StringLength);
- return _SQLSetStmtAttr(hstmt, Attribute, ValuePtr, StringLength);
+ return _SQLSetStmtAttr(hstmt, Attribute, ValuePtr, StringLength _wide0);
}
#ifdef ENABLE_ODBC_WIDE
@@ -6549,7 +6614,7 @@ SQLSetStmtAttrW(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLIN
tdsdump_log(TDS_DBG_FUNC, "SQLSetStmtAttr(%p, %d, %p, %d)\n",
hstmt, (int)Attribute, ValuePtr, (int)StringLength);
- return _SQLSetStmtAttr(hstmt, Attribute, ValuePtr, StringLength);
+ return _SQLSetStmtAttr(hstmt, Attribute, ValuePtr, StringLength, 1);
}
#endif
#endif
@@ -6560,7 +6625,7 @@ SQLSetStmtOption(SQLHSTMT hstmt, SQLUSMALLINT fOption, SQLULEN vParam)
tdsdump_log(TDS_DBG_FUNC, "SQLSetStmtOption(%p, %u, %u)\n", hstmt, fOption, (unsigned)vParam);
/* XXX: Lost precision */
- return _SQLSetStmtAttr(hstmt, (SQLINTEGER) fOption, (SQLPOINTER) (TDS_INTPTR) vParam, SQL_NTS);
+ return _SQLSetStmtAttr(hstmt, (SQLINTEGER) fOption, (SQLPOINTER) (TDS_INTPTR) vParam, SQL_NTS _wide0);
}
ODBC_FUNC(SQLSpecialColumns, (P(SQLHSTMT,hstmt), P(SQLUSMALLINT,fColType), PCHARIN(CatalogName,SQLSMALLINT),
@@ -7269,10 +7334,10 @@ SQLSetScrollOptions(SQLHSTMT hstmt, SQLUSMALLINT fConcurrency, SQLLEN crowKeyset
ODBC_EXIT_(stmt);
}
- _SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) (TDS_INTPTR) cursor_type, 0);
- _SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER) (TDS_INTPTR) fConcurrency, 0);
- _SQLSetStmtAttr(hstmt, SQL_ATTR_KEYSET_SIZE, (SQLPOINTER) (TDS_INTPTR) crowKeyset, 0);
- _SQLSetStmtAttr(hstmt, SQL_ROWSET_SIZE, (SQLPOINTER) (TDS_INTPTR) crowRowset, 0);
+ _SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) (TDS_INTPTR) cursor_type, 0 _wide0);
+ _SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER) (TDS_INTPTR) fConcurrency, 0 _wide0);
+ _SQLSetStmtAttr(hstmt, SQL_ATTR_KEYSET_SIZE, (SQLPOINTER) (TDS_INTPTR) crowKeyset, 0 _wide0);
+ _SQLSetStmtAttr(hstmt, SQL_ROWSET_SIZE, (SQLPOINTER) (TDS_INTPTR) crowRowset, 0 _wide0);
ODBC_EXIT_(stmt);
}
diff --git a/src/tds/query.c b/src/tds/query.c
index a076255..8f72345 100644
--- a/src/tds/query.c
+++ b/src/tds/query.c
@@ -208,7 +208,7 @@ tds_set_cur_dyn(TDSSOCKET *tds, TDSDYNAMIC *dyn)
TDSRET
tds_submit_query(TDSSOCKET * tds, const char *query)
{
- return tds_submit_query_params(tds, query, NULL);
+ return tds_submit_query_params(tds, query, NULL, NULL);
}
/**
@@ -280,20 +280,62 @@ tds_put_data(TDSSOCKET * tds, TDSCOLUMN * curcol)
}
/**
- * Start of TDS 7.2+ query packet.
+ * Start query packet of a given type
+ * \tds
+ * \param packet_type packet type
+ * \param head extra information to put in a TDS7 header
*/
-static const TDS_UCHAR tds72_query_start[] = {
- /* total length */
- 0x16, 0, 0, 0,
- /* length */
- 0x12, 0, 0, 0,
- /* type */
- 0x02, 0,
- /* transaction */
- 0, 0, 0, 0, 0, 0, 0, 0,
- /* request count */
- 1, 0, 0, 0
-};
+static TDSRET
+tds_start_query_head(TDSSOCKET *tds, unsigned char packet_type, TDSHEADERS * head)
+{
+ tds->out_flag = packet_type;
+ if (IS_TDS72_PLUS(tds->conn)) {
+ int qn_len = 0;
+ const char *converted_msgtext = NULL;
+ const char *converted_options = NULL;
+ size_t converted_msgtext_len = 0;
+ size_t converted_options_len = 0;
+
+ if (head && head->qn_msgtext && head->qn_options) {
+ converted_msgtext = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], head->qn_msgtext, (int)strlen(head->qn_msgtext), &converted_msgtext_len);
+ if (!converted_msgtext) {
+ tds_set_state(tds, TDS_IDLE);
+ return TDS_FAIL;
+ }
+
+ converted_options = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], head->qn_options, (int)strlen(head->qn_options), &converted_options_len);
+ if (!converted_options) {
+ tds_convert_string_free(head->qn_msgtext, converted_msgtext);
+ tds_set_state(tds, TDS_IDLE);
+ return TDS_FAIL;
+ }
+
+ qn_len = 6 + 2 + converted_msgtext_len + 2 + converted_options_len;
+ if (head->qn_timeout != 0)
+ qn_len += 4;
+ }
+
+ tds_put_int(tds, 4 + 18 + qn_len); /* total length */
+ tds_put_int(tds, 18); /* length: transaction descriptor */
+ tds_put_smallint(tds, 2); /* type: transaction descriptor */
+ tds_put_n(tds, tds->conn->tds72_transaction, 8); /* transaction */
+ tds_put_int(tds, 1); /* request count */
+ if (qn_len != 0) {
+ tds_put_int(tds, qn_len); /* length: query notification */
+ tds_put_smallint(tds, 1); /* type: query notification */
+ tds_put_smallint(tds, converted_msgtext_len); /* notifyid */
+ tds_put_n(tds, converted_msgtext, converted_msgtext_len);
+ tds_put_smallint(tds, converted_options_len); /* ssbdeployment */
+ tds_put_n(tds, converted_options, converted_options_len);
+ if (head->qn_timeout != 0)
+ tds_put_int(tds, head->qn_timeout); /* timeout */
+
+ tds_convert_string_free(head->qn_options, converted_options);
+ tds_convert_string_free(head->qn_msgtext, converted_msgtext);
+ }
+ }
+ return TDS_SUCCESS;
+}
/**
* Start query packet of a given type
@@ -303,12 +345,9 @@ static const TDS_UCHAR tds72_query_start[] = {
static void
tds_start_query(TDSSOCKET *tds, unsigned char packet_type)
{
- tds->out_flag = packet_type;
- if (IS_TDS72_PLUS(tds->conn)) {
- tds_put_n(tds, tds72_query_start, 10);
- tds_put_n(tds, tds->conn->tds72_transaction, 8);
- tds_put_n(tds, tds72_query_start + 10 + 8, 4);
- }
+ /* no need to check return value here because tds_start_query_head() cannot
+ fail when given a NULL head parameter */
+ tds_start_query_head(tds, packet_type, NULL);
}
/**
@@ -322,7 +361,7 @@ tds_start_query(TDSSOCKET *tds, unsigned char packet_type)
* \return TDS_FAIL or TDS_SUCCESS
*/
TDSRET
-tds_submit_query_params(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params)
+tds_submit_query_params(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head)
{
size_t query_len;
int num_params = params ? params->num_cols : 0;
@@ -400,7 +439,11 @@ tds_submit_query_params(TDSSOCKET * tds, const char *query, TDSPARAMINFO * param
}
}
- tds_start_query(tds, TDS_RPC);
+ if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS) {
+ tds_convert_string_free(query, converted_query);
+ free(param_definition);
+ return TDS_FAIL;
+ }
/* procedure name */
if (IS_TDS71_PLUS(tds->conn)) {
tds_put_smallint(tds, -1);
@@ -1266,7 +1309,7 @@ failure:
* \return TDS_FAIL or TDS_SUCCESS
*/
TDSRET
-tds_submit_execdirect(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params)
+tds_submit_execdirect(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head)
{
size_t query_len;
TDSCOLUMN *param;
@@ -1303,7 +1346,11 @@ tds_submit_execdirect(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params)
return TDS_FAIL;
}
- tds_start_query(tds, TDS_RPC);
+ if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS) {
+ tds_convert_string_free(query, converted_query);
+ free(param_definition);
+ return TDS_FAIL;
+ }
/* procedure name */
if (IS_TDS71_PLUS(tds->conn)) {
tds_put_smallint(tds, -1);
@@ -1946,7 +1993,7 @@ tds_send_emulated_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * para
* \param params parameters informations. NULL for no parameters
*/
TDSRET
-tds_submit_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params)
+tds_submit_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params, TDSHEADERS * head)
{
TDSCOLUMN *param;
int rpc_name_len, i;
@@ -1976,7 +2023,10 @@ tds_submit_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params)
tds_set_state(tds, TDS_IDLE);
return TDS_FAIL;
}
- tds_start_query(tds, TDS_RPC);
+ if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS) {
+ tds_convert_string_free(rpc_name, converted_name);
+ return TDS_FAIL;
+ }
TDS_PUT_SMALLINT(tds, converted_name_len / 2);
tds_put_n(tds, converted_name, (int)converted_name_len);
@@ -3222,7 +3272,7 @@ tds_send_emulated_execute(TDSSOCKET * tds, const char *query, TDSPARAMINFO * par
enum { MUL_STARTED = 1 };
TDSRET
-tds_multiple_init(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDS_MULTIPLE_TYPE type)
+tds_multiple_init(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDS_MULTIPLE_TYPE type, TDSHEADERS * head)
{
unsigned char packet_type;
multiple->type = type;
@@ -3241,7 +3291,8 @@ tds_multiple_init(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDS_MULTIPLE_TYPE type)
packet_type = TDS_RPC;
break;
}
- tds_start_query(tds, packet_type);
+ if (tds_start_query_head(tds, packet_type, head) != TDS_SUCCESS)
+ return TDS_FAIL;
return TDS_SUCCESS;
}