diff --git a/driver/connect.c b/driver/connect.c index 43b95126..75a6d942 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -313,6 +313,8 @@ SQLRETURN post_json(esodbc_stmt_st *stmt, const cstr_st *u8body) DBGH(stmt, "POSTing JSON [%zd] `" LCPDL "`.", u8body->cnt, LCSTR(u8body)); + ESODBC_MUX_LOCK(&dbc->curl_mux); + if (! dbc->curl) { init_curl(dbc); } @@ -370,6 +372,8 @@ SQLRETURN post_json(esodbc_stmt_st *stmt, const cstr_st *u8body) DBGH(stmt, "libcurl: request succesfull, received code %ld and %zd bytes" " back.", code, apos); + ESODBC_MUX_UNLOCK(&dbc->curl_mux); + if (code != 200) { ERRH(stmt, "libcurl: non-200 HTTP response code %ld received.", code); /* expect a 200 with body; everything else is failure (todo?) */ @@ -387,6 +391,8 @@ SQLRETURN post_json(esodbc_stmt_st *stmt, const cstr_st *u8body) res != CURLE_OK ? curl_easy_strerror(res) : "", res); err_net: /* the error occured after the request hit hit the network */ cleanup_curl(dbc); + ESODBC_MUX_UNLOCK(&dbc->curl_mux); + if (abuff) { free(abuff); abuff = NULL; @@ -594,7 +600,7 @@ void cleanup_dbc(esodbc_dbc_st *dbc) } else { assert(dbc->no_types == 0); } - assert(dbc->abuff == NULL); /* reminder for when going multithreaded */ + assert(dbc->abuff == NULL); cleanup_curl(dbc); } @@ -1575,10 +1581,26 @@ SQLRETURN EsSQLSetConnectAttrW( DBGH(dbc, "setting metadata_id to %u.", (SQLULEN)Value); dbc->metadata_id = (SQLULEN)Value; break; + case SQL_ATTR_ASYNC_ENABLE: - DBGH(dbc, "setting async enable to %u.", (SQLULEN)Value); - dbc->async_enable = (SQLULEN)Value; + ERRH(dbc, "no support for async API (setting param: %llu)", + (SQLULEN)(uintptr_t)Value); + if ((SQLULEN)(uintptr_t)Value == SQL_ASYNC_ENABLE_ON) { + RET_HDIAGS(dbc, SQL_STATE_HYC00); + } + break; + case SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE: + ERRH(dbc, "no support for async API (setting param: %llu)", + (SQLULEN)(uintptr_t)Value); + if ((SQLULEN)(uintptr_t)Value == SQL_ASYNC_DBC_ENABLE_ON) { + RET_HDIAGS(dbc, SQL_STATE_HY114); + } break; + case SQL_ATTR_ASYNC_DBC_EVENT: + // case SQL_ATTR_ASYNC_DBC_PCALLBACK: + // case SQL_ATTR_ASYNC_DBC_PCONTEXT: + ERRH(dbc, "no support for async API (attr: %ld)", Attribute); + RET_HDIAGS(dbc, SQL_STATE_S1118); case SQL_ATTR_QUIET_MODE: DBGH(dbc, "setting window handler to 0x%p.", Value); @@ -1631,8 +1653,9 @@ SQLRETURN EsSQLGetConnectAttrW( ERRH(dbc, "failed to get current catalog."); RET_STATE(dbc->hdr.diag.state); } - if (StringLengthPtr); - *StringLengthPtr = (SQLINTEGER)used; + if (StringLengthPtr) { + *StringLengthPtr = (SQLINTEGER)used; + } break; case SQL_ATTR_METADATA_ID: @@ -1640,8 +1663,8 @@ SQLRETURN EsSQLGetConnectAttrW( *(SQLULEN *)ValuePtr = dbc->metadata_id; break; case SQL_ATTR_ASYNC_ENABLE: - DBGH(dbc, "requested: async enable: %u.", dbc->async_enable); - *(SQLULEN *)ValuePtr = dbc->async_enable; + DBGH(dbc, "getting async mode: %llu", SQL_ASYNC_ENABLE_OFF); + *(SQLULEN *)ValuePtr = SQL_ASYNC_ENABLE_OFF; break; case SQL_ATTR_QUIET_MODE: diff --git a/driver/defs.h b/driver/defs.h index 78294572..46b4fdd4 100644 --- a/driver/defs.h +++ b/driver/defs.h @@ -53,8 +53,7 @@ #define ESODBC_PARAM_MARKER "?" /* max # of active statements for a connection */ -/* TODO: review@alpha */ -#define ESODBC_MAX_CONCURRENT_ACTIVITIES 16 +#define ESODBC_MAX_CONCURRENT_ACTIVITIES 0 /* maximum identifer length */ /* TODO: review@alpha */ /* match 'keyword' ES type length */ diff --git a/driver/error.h b/driver/error.h index 83434350..1ecf02f1 100644 --- a/driver/error.h +++ b/driver/error.h @@ -120,6 +120,7 @@ typedef enum { SQL_STATE_HY109, SQL_STATE_HY110, SQL_STATE_HY111, + SQL_STATE_HY114, SQL_STATE_HYC00, SQL_STATE_HYT00, SQL_STATE_HYT01, @@ -138,6 +139,7 @@ typedef enum { SQL_STATE_IM013, SQL_STATE_IM014, SQL_STATE_IM015, + SQL_STATE_S1118, SQL_STATE_MAX } esodbc_state_et; @@ -379,6 +381,8 @@ static esodbc_errors_st esodbc_errors[] = { SQL_ERROR}, {MK_WPTR("HY111"), MK_WPTR("Invalid bookmark value"), SQL_ERROR}, + {MK_WPTR("HY114"), MK_WPTR("Driver does not support connection-level " + "asynchronous function execution"), SQL_ERROR}, {MK_WPTR("HYC00"), MK_WPTR("Optional feature not implemented"), SQL_ERROR}, {MK_WPTR("HYT00"), MK_WPTR("Timeout expired"), @@ -417,6 +421,8 @@ static esodbc_errors_st esodbc_errors[] = { SQL_ERROR}, {MK_WPTR("IM015"), MK_WPTR("Corrupt file data source"), SQL_ERROR}, + {MK_WPTR("S1118"), MK_WPTR("Driver does not support asynchronous " + "notification"), SQL_ERROR}, }; /*INDENT-ON*/ diff --git a/driver/handles.c b/driver/handles.c index 069af88f..561398d5 100644 --- a/driver/handles.c +++ b/driver/handles.c @@ -51,6 +51,7 @@ static void init_hheader(esodbc_hhdr_st *hdr, SQLSMALLINT type, void *parent) { hdr->type = type; init_diagnostic(&hdr->diag); + ESODBC_MUX_INIT(&hdr->mutex); hdr->parent = parent; } @@ -225,7 +226,6 @@ SQLRETURN EsSQLAllocHandle(SQLSMALLINT HandleType, } dbc->dsn.str = MK_WPTR(""); /* see explanation in cleanup_dbc() */ dbc->metadata_id = SQL_FALSE; - dbc->async_enable = SQL_ASYNC_ENABLE_OFF; dbc->txn_isolation = ESODBC_DEF_TXN_ISOLATION; /* rest of initialization done at connect time */ @@ -261,9 +261,10 @@ SQLRETURN EsSQLAllocHandle(SQLSMALLINT HandleType, * Note: these attributes won't propagate at statement level when * set at connection level. */ stmt->metadata_id = dbc->metadata_id; - stmt->async_enable = dbc->async_enable; stmt->sql2c_conversion = CONVERSION_UNCHECKED; + ESODBC_MUX_INIT(&dbc->curl_mux); + DBGH(dbc, "new Statement handle allocated @0x%p.", *OutputHandle); break; @@ -303,6 +304,7 @@ SQLRETURN EsSQLAllocHandle(SQLSMALLINT HandleType, SQLRETURN EsSQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) { + esodbc_dbc_st *dbc; esodbc_stmt_st *stmt; esodbc_desc_st *desc; @@ -314,13 +316,13 @@ SQLRETURN EsSQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) switch(HandleType) { case SQL_HANDLE_ENV: /* Environment Handle */ // TODO: check if there are connections (_DBC) - free(Handle); break; case SQL_HANDLE_DBC: /* Connection Handle */ // TODO: remove from (potential) list? + dbc = DBCH(Handle); /* app/DM should have SQLDisconnect'ed, but just in case */ - cleanup_dbc(DBCH(Handle)); - free(Handle); + cleanup_dbc(dbc); + ESODBC_MUX_DEL(&dbc->curl_mux); break; case SQL_HANDLE_STMT: // TODO: remove from (potential) list? @@ -332,7 +334,6 @@ SQLRETURN EsSQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) clear_desc(stmt->ird, FALSE); clear_desc(stmt->apd, FALSE); clear_desc(stmt->ipd, FALSE); - free(stmt); break; // FIXME: @@ -356,6 +357,8 @@ SQLRETURN EsSQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) return SQL_INVALID_HANDLE; } + ESODBC_MUX_DEL(&HDRH(Handle)->mutex); + free(Handle); return SQL_SUCCESS; } @@ -479,7 +482,7 @@ SQLRETURN EsSQLSetEnvAttr(SQLHENV EnvironmentHandle, case SQL_ATTR_CONNECTION_POOLING: case SQL_ATTR_CP_MATCH: RET_HDIAG(ENVH(EnvironmentHandle), SQL_STATE_HYC00, - "Connection pooling not yet supported", 0); + "Connection pooling not supported", 0); case SQL_ATTR_ODBC_VERSION: switch ((intptr_t)Value) { @@ -767,10 +770,19 @@ SQLRETURN EsSQLSetStmtAttrW( DBGH(stmt, "setting metadata_id to: %u", (SQLULEN)ValuePtr); stmt->metadata_id = (SQLULEN)ValuePtr; break; + case SQL_ATTR_ASYNC_ENABLE: - DBGH(stmt, "setting async_enable to: %u", (SQLULEN)ValuePtr); - stmt->async_enable = (SQLULEN)ValuePtr; + ERRH(stmt, "no support for async API (setting param: %llu)", + (SQLULEN)(uintptr_t)ValuePtr); + if ((SQLULEN)(uintptr_t)ValuePtr == SQL_ASYNC_ENABLE_ON) { + RET_HDIAGS(stmt, SQL_STATE_HYC00); + } break; + case SQL_ATTR_ASYNC_STMT_EVENT: + // case SQL_ATTR_ASYNC_STMT_PCALLBACK: + // case SQL_ATTR_ASYNC_STMT_PCONTEXT: + ERRH(stmt, "no support for async API (attr: %ld)", Attribute); + RET_HDIAGS(stmt, SQL_STATE_S1118); case SQL_ATTR_MAX_LENGTH: ulen = (SQLULEN)ValuePtr; @@ -834,8 +846,8 @@ SQLRETURN EsSQLGetStmtAttrW( *(SQLULEN *)ValuePtr = stmt->metadata_id; break; case SQL_ATTR_ASYNC_ENABLE: - DBGH(stmt, "getting async_enable: %llu", stmt->async_enable); - *(SQLULEN *)ValuePtr = stmt->async_enable; + DBGH(stmt, "getting async mode: %llu", SQL_ASYNC_ENABLE_OFF); + *(SQLULEN *)ValuePtr = SQL_ASYNC_ENABLE_OFF; break; case SQL_ATTR_MAX_LENGTH: DBGH(stmt, "getting max_length: %llu", stmt->max_length); diff --git a/driver/handles.h b/driver/handles.h index 0355d31a..aef5aebf 100644 --- a/driver/handles.h +++ b/driver/handles.h @@ -28,6 +28,8 @@ typedef struct struct_hheader { /* handle header */ SQLSMALLINT type; /* diagnostic/state keeping */ esodbc_diag_st diag; + /* ODBC API multi-threading exclusive lock */ + esodbc_mutex_lt mutex; /* back reference to "parent" structure (in type hierarchy) */ union { struct struct_env *env; @@ -145,17 +147,13 @@ typedef struct struct_dbc { size_t alen; /* size of abuff */ size_t apos; /* current write position in the abuff */ size_t amax; /* maximum length (bytes) that abuff can grow to */ + esodbc_mutex_lt curl_mux; /* mutex for above 'networking' members */ /* window handler */ HWND hwin; - /* "the catalog is a database", "For a single-tier driver, the catalog - * might be a directory" */ - SQLWCHAR *catalog; - // TODO: statement list? /* options */ SQLULEN metadata_id; // default: SQL_FALSE - SQLULEN async_enable; // default: SQL_ASYNC_ENABLE_OFF SQLUINTEGER txn_isolation; // default: SQL_TXN_* } esodbc_dbc_st; @@ -307,7 +305,6 @@ typedef struct struct_stmt { /* options */ SQLULEN bookmarks; //default: SQL_UB_OFF SQLULEN metadata_id; // default: copied from connection - SQLULEN async_enable; // default: copied from connection /* "the maximum amount of data that the driver returns from a character or * binary column" */ SQLULEN max_length; @@ -437,6 +434,9 @@ SQLRETURN EsSQLSetDescRec( _Inout_opt_ SQLLEN *Indicator); +/* + * Macros to convert ODBC API generic handles into implementation types. + */ #define ENVH(_h) ((esodbc_env_st *)(_h)) #define DBCH(_h) ((esodbc_dbc_st *)(_h)) #define STMH(_h) ((esodbc_stmt_st *)(_h)) @@ -444,6 +444,13 @@ SQLRETURN EsSQLSetDescRec( /* this will only work if member stays first in handles (see struct decl). */ #define HDRH(_h) ((esodbc_hhdr_st *)(_h)) +/* + * Locking macros for ODBC API calls. + */ +#define HND_LOCK(_h) ESODBC_MUX_LOCK(&HDRH(_h)->mutex) +#define HND_TRYLOCK(_h) ESODBC_MUX_TRYLOCK(&HDRH(_h)->mutex) +#define HND_UNLOCK(_h) ESODBC_MUX_UNLOCK(&HDRH(_h)->mutex) + /* wraper of RET_CDIAG, compatible with any defined handle */ #define RET_HDIAG(_hp/*handle ptr*/, _s/*tate*/, _t/*char text*/, _c/*ode*/) \ diff --git a/driver/info.c b/driver/info.c index c4e12292..6cfb718f 100644 --- a/driver/info.c +++ b/driver/info.c @@ -123,19 +123,29 @@ SQLRETURN EsSQLGetInfoW(SQLHDBC ConnectionHandle, return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_SQL_SPEC_STRING), BufferLength, StringLengthPtr); + case SQL_ASYNC_MODE: + DBGH(dbc, "requested: async mode (%lu).", SQL_AM_NONE); + *(SQLUINTEGER *)InfoValue = SQL_AM_NONE; + break; + /* "if the driver can execute functions asynchronously on the * connection handle" */ case SQL_ASYNC_DBC_FUNCTIONS: - /* TODO: review@alpha */ - *(SQLUSMALLINT *)InfoValue = SQL_FALSE; - DBGH(dbc, "requested: support for async fuctions: no."); + DBGH(dbc, "requested: async DBC functions (no - %lu).", + SQL_ASYNC_DBC_NOT_CAPABLE); + *(SQLUINTEGER *)InfoValue = SQL_ASYNC_DBC_NOT_CAPABLE; break; /* "if the driver supports asynchronous notification" */ case SQL_ASYNC_NOTIFICATION: - // FIXME: review@alpha */ + DBGH(dbc, "requested: async notification (no - %lu).", + SQL_ASYNC_NOTIFICATION_NOT_CAPABLE); *(SQLUINTEGER *)InfoValue = SQL_ASYNC_NOTIFICATION_NOT_CAPABLE; - DBGH(dbc, "requested: support for async notifications: no."); + break; + + case SQL_MAX_ASYNC_CONCURRENT_STATEMENTS: + DBGH(dbc, "requested: async concurrent statements (0)."); + *(SQLUINTEGER *)InfoValue = 0; break; /* "the maximum number of active statements that the driver can @@ -159,8 +169,6 @@ SQLRETURN EsSQLGetInfoW(SQLHDBC ConnectionHandle, case SQL_GETDATA_EXTENSIONS: DBGH(dbc, "requested: GetData extentions."); - // FIXME: review@alpha - // TODO: GetData review *(SQLUINTEGER *)InfoValue = ESODBC_GETDATA_EXTENSIONS; break; @@ -735,9 +743,10 @@ SQLRETURN EsSQLGetDiagFieldW( return write_wstr(&dummy, DiagInfoPtr, wstrp, BufferLength, StringLengthPtr); - case SQL_DIAG_CONNECTION_NAME: /* same as SQLGetInfo(SQL_DATA_SOURCE_NAME) */ - case SQL_DIAG_SERVER_NAME: /* TODO: keep same as _CONNECTION_NAME? */ + case SQL_DIAG_CONNECTION_NAME: + /* TODO: this must be the server name, not DSN. */ + case SQL_DIAG_SERVER_NAME: switch (HandleType) { case SQL_HANDLE_DBC: wstrp = &DBCH(Handle)->dsn; @@ -912,7 +921,6 @@ SQLRETURN EsSQLGetFunctions(SQLHDBC ConnectionHandle, } } - // TODO: does this require connecting to the server? return SQL_SUCCESS; } diff --git a/driver/log.c b/driver/log.c index 4dbd4513..5ff40d5c 100644 --- a/driver/log.c +++ b/driver/log.c @@ -32,11 +32,7 @@ int _esodbc_log_level = LOG_LEVEL_DISABLED; /* log file path -- process variable */ static TCHAR *log_path = NULL; /* log file mutex -- process variable */ -static SRWLOCK log_mux = SRWLOCK_INIT; - -#define MUTEX_LOCK(_mux) \ - (WaitForSingleObject(_mux, INFINITE) == WAIT_OBJECT_0) -#define MUTEX_UNLOCK(_mux) ReleaseMutex(_mux) +static esodbc_mutex_lt log_mux = ESODBC_MUX_SINIT; static inline HANDLE log_file_handle(BOOL open) @@ -143,8 +139,8 @@ BOOL log_init() void log_cleanup() { - /* There is no need/function to destroy the SRWLOCK */ - log_file_handle(/* close */FALSE); + log_file_handle(/*open?*/FALSE); + ESODBC_MUX_DEL(&log_mux); } @@ -258,9 +254,9 @@ static inline void log_file(int level, int werrno, const char *func, } assert(pos <= sizeof(buff)); - AcquireSRWLockExclusive(&log_mux); + ESODBC_MUX_LOCK(&log_mux); log_file_write(buff, pos); - ReleaseSRWLockExclusive(&log_mux); + ESODBC_MUX_UNLOCK(&log_mux); } void _esodbc_log(int lvl, int werrno, const char *func, diff --git a/driver/odbc.c b/driver/odbc.c index a423a813..3f64ad98 100644 --- a/driver/odbc.c +++ b/driver/odbc.c @@ -71,7 +71,7 @@ BOOL WINAPI DllMain( // Perform any necessary cleanup. case DLL_PROCESS_DETACH: - INFO("process %u dettached.", GetCurrentProcessId()); + INFO("process %u detaching.", GetCurrentProcessId()); driver_cleanup(); break; } @@ -88,6 +88,7 @@ SQLRETURN SQL_API SQLAllocHandle(SQLSMALLINT HandleType, { SQLRETURN ret; TRACE3(_IN, "dpp", HandleType, InputHandle, OutputHandle); + /* no synchronization required */ ret = EsSQLAllocHandle(HandleType, InputHandle, OutputHandle); TRACE4(_OUT, "ddpp", ret, HandleType, InputHandle, *OutputHandle); return ret; @@ -132,8 +133,10 @@ SQLRETURN SQL_API SQLDriverConnectW SQLRETURN ret; TRACE8(_IN, "pppdpdpd", hdbc, hwnd, szConnStrIn, cchConnStrIn, szConnStrOut, cchConnStrOutMax, pcchConnStrOut, fDriverCompletion); + HND_LOCK(hdbc); ret = EsSQLDriverConnectW(hdbc, hwnd, szConnStrIn, cchConnStrIn, szConnStrOut, cchConnStrOutMax, pcchConnStrOut, fDriverCompletion); + HND_UNLOCK(hdbc); TRACE9(_OUT, "dppWdWdtd", ret, hdbc, hwnd, szConnStrIn, cchConnStrIn, szConnStrOut, cchConnStrOutMax, pcchConnStrOut, fDriverCompletion); return ret; @@ -208,8 +211,14 @@ SQLRETURN SQL_API SQLGetInfoW(SQLHDBC ConnectionHandle, SQLRETURN ret; TRACE5(_IN, "pupdp", ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); + /* Note_sync: no synchronization really required for setting/getting of + * integer attributes (atomic) or string ones (= reading static string + * locations), but error handling involves posting an SQL state & string, + * which needs serialization. */ + HND_LOCK(ConnectionHandle); ret = EsSQLGetInfoW(ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); + HND_UNLOCK(ConnectionHandle); TRACE6(_OUT, "dpupdt", ret, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); return ret; @@ -223,6 +232,7 @@ SQLRETURN SQL_API SQLGetFunctions(SQLHDBC ConnectionHandle, { SQLRETURN ret; TRACE3(_IN, "pdp", ConnectionHandle, FunctionId, Supported); + /* no synchronization required */ ret = EsSQLGetFunctions(ConnectionHandle, FunctionId, Supported); TRACE4(_IN, "dpdT", ret, ConnectionHandle, FunctionId, Supported); return ret; @@ -234,7 +244,9 @@ SQLRETURN SQL_API SQLGetTypeInfoW( { SQLRETURN ret; TRACE2(_IN, "pd", StatementHandle, DataType); + HND_LOCK(StatementHandle); ret = EsSQLGetTypeInfoW(StatementHandle, DataType); + HND_UNLOCK(StatementHandle); TRACE3(_OUT, "dpd", ret, StatementHandle, DataType); return ret; //RET_NOT_IMPLEMENTED; @@ -255,8 +267,10 @@ SQLRETURN SQL_API SQLSetConnectAttrW( { SQLRETURN ret; TRACE4(_IN, "pdpd", ConnectionHandle, Attribute, Value, StringLength); + HND_LOCK(ConnectionHandle); /* see Note_sync above */ ret = EsSQLSetConnectAttrW(ConnectionHandle, Attribute, Value, StringLength); + HND_UNLOCK(ConnectionHandle); TRACE5(_OUT, "dpdpd", ret, ConnectionHandle, Attribute, Value, StringLength); return ret; @@ -272,8 +286,10 @@ SQLRETURN SQL_API SQLGetConnectAttrW( SQLRETURN ret; TRACE5(_IN, "pdpdp", ConnectionHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr); + HND_LOCK(ConnectionHandle); ret = EsSQLGetConnectAttrW(ConnectionHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr); + HND_UNLOCK(ConnectionHandle); TRACE6(_OUT, "dpdpdg", ret, ConnectionHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr); return ret; @@ -287,7 +303,9 @@ SQLRETURN SQL_API SQLSetEnvAttr(SQLHENV EnvironmentHandle, { SQLRETURN ret; TRACE4(_IN, "pdpd", EnvironmentHandle, Attribute, Value, StringLength); + HND_LOCK(EnvironmentHandle); /* see Note_sync above */ ret = EsSQLSetEnvAttr(EnvironmentHandle, Attribute, Value, StringLength); + HND_UNLOCK(EnvironmentHandle); TRACE5(_OUT, "dpdpd", ret, EnvironmentHandle, Attribute, Value, StringLength); return ret; @@ -301,8 +319,10 @@ SQLRETURN SQL_API SQLGetEnvAttr(SQLHENV EnvironmentHandle, SQLRETURN ret; TRACE5(_IN, "pdpdp", EnvironmentHandle, Attribute, Value, BufferLength, StringLength); + HND_LOCK(EnvironmentHandle); /* see Note_sync above */ ret = EsSQLGetEnvAttr(EnvironmentHandle, Attribute, Value, BufferLength, StringLength); + HND_UNLOCK(EnvironmentHandle); TRACE6(_OUT, "dpdpdg", ret, EnvironmentHandle, Attribute, Value, BufferLength, StringLength); return ret; @@ -316,8 +336,10 @@ SQLRETURN SQL_API SQLSetStmtAttrW( { SQLRETURN ret; TRACE4(_IN, "pdpd", StatementHandle, Attribute, ValuePtr, BufferLength); + HND_LOCK(StatementHandle); ret = EsSQLSetStmtAttrW(StatementHandle, Attribute, ValuePtr, BufferLength); + HND_UNLOCK(StatementHandle); TRACE5(_OUT, "dpdpd", ret, StatementHandle, Attribute, ValuePtr, BufferLength); return ret; @@ -333,8 +355,10 @@ SQLRETURN SQL_API SQLGetStmtAttrW( SQLRETURN ret; TRACE5(_IN, "pdpdp", StatementHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr); + HND_LOCK(StatementHandle); ret = EsSQLGetStmtAttrW(StatementHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr); + HND_UNLOCK(StatementHandle); TRACE6(_OUT, "dpdpdg", ret, StatementHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr); return ret; @@ -359,13 +383,42 @@ SQLRETURN SQL_API SQLGetDescFieldW( SQLRETURN ret; TRACE6(_IN, "pddpdp", DescriptorHandle, RecNumber, FieldIdentifier, ValuePtr, BufferLength, StringLengthPtr); + /* Note_stmt_sync: API descriptor access is serialized by statement's + * mutex, not descriptor's, since statement functions working on + * descriptors won't lock these, but will lock the statement instead + * (which keeps the code simpler; besides, no "high-speed" concurrent + * thread access on descriptors is necessary anyway). */ + HND_LOCK(DSCH(DescriptorHandle)->hdr.stmt); ret = EsSQLGetDescFieldW(DescriptorHandle, RecNumber, FieldIdentifier, ValuePtr, BufferLength, StringLengthPtr); + HND_UNLOCK(DSCH(DescriptorHandle)->hdr.stmt); TRACE7(_OUT, "dpddpdg", ret, DescriptorHandle, RecNumber, FieldIdentifier, ValuePtr, BufferLength, StringLengthPtr); return ret; } +SQLRETURN SQL_API SQLSetDescFieldW +( + SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, + SQLSMALLINT FieldIdentifier, + SQLPOINTER Value, + SQLINTEGER BufferLength +) +{ + SQLRETURN ret; + TRACE5(_IN, "pddpd", DescriptorHandle, RecNumber, FieldIdentifier, + Value, BufferLength); + HND_LOCK(DSCH(DescriptorHandle)->hdr.stmt); /* see Note_stmt_sync */ + ret = EsSQLSetDescFieldW(DescriptorHandle, RecNumber, FieldIdentifier, + Value, BufferLength); + HND_UNLOCK(DSCH(DescriptorHandle)->hdr.stmt); + TRACE6(_OUT, "dpddpd", ret, DescriptorHandle, RecNumber, FieldIdentifier, + Value, BufferLength); + return ret; +} + +#if WITH_EMPTY SQLRETURN SQL_API SQLGetDescRecW( SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, @@ -388,43 +441,10 @@ SQLRETURN SQL_API SQLGetDescRecW( _Out_opt_ SQLSMALLINT *NullablePtr) { -#if 0 - SQLRETURN ret; - TRACE11(_IN, "pdpdppppppp", DescriptorHandle, RecNumber, Name, - BufferLength, StringLengthPtr, TypePtr, SubTypePtr, LengthPtr, - PrecisionPtr, ScalePtr, NullablePtr); - ret = EsSQLGetDescRecW(DescriptorHandle, RecNumber, Name, - BufferLength, StringLengthPtr, TypePtr, SubTypePtr, LengthPtr, - PrecisionPtr, ScalePtr, NullablePtr); - TRACE12(_OUT, "dpdWdttttttt", ret, DescriptorHandle, RecNumber, Name, - BufferLength, StringLengthPtr, TypePtr, SubTypePtr, LengthPtr, - PrecisionPtr, ScalePtr, NullablePtr); - return ret; -#else //0 RET_NOT_IMPLEMENTED; -#endif //0 } -SQLRETURN SQL_API SQLSetDescFieldW -( - SQLHDESC DescriptorHandle, - SQLSMALLINT RecNumber, - SQLSMALLINT FieldIdentifier, - SQLPOINTER Value, - SQLINTEGER BufferLength -) -{ - SQLRETURN ret; - TRACE5(_IN, "pddpd", DescriptorHandle, RecNumber, FieldIdentifier, - Value, BufferLength); - ret = EsSQLSetDescFieldW(DescriptorHandle, RecNumber, FieldIdentifier, - Value, BufferLength); - TRACE6(_OUT, "dpddpd", ret, DescriptorHandle, RecNumber, FieldIdentifier, - Value, BufferLength); - return ret; -} - SQLRETURN SQL_API SQLSetDescRec( SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, @@ -437,21 +457,9 @@ SQLRETURN SQL_API SQLSetDescRec( _Inout_opt_ SQLLEN *StringLength, _Inout_opt_ SQLLEN *Indicator) { -#if 0 - SQLRETURN ret; - TRACE10(_IN, "pddddddppp", DescriptorHandle, RecNumber, Type, SubType, - Length, Precision, Scale, Data, StringLength, Indicator); - ret = EsSQLSetDescRec(DescriptorHandle, RecNumber, Type, SubType, - Length, Precision, Scale, Data, StringLength, Indicator); - TRACE11(_OUT, "dpddddddpnn", ret, DescriptorHandle, RecNumber, Type, - SubType, Length, Precision, Scale, Data, StringLength, Indicator); - return ret; -#else //0 RET_NOT_IMPLEMENTED; -#endif //0 } -#if WITH_EMPTY /* * "SQLCopyDesc function is called to copy the fields of one descriptor to * another descriptor. Fields can be copied only to an application descriptor @@ -487,7 +495,9 @@ SQLRETURN SQL_API SQLPrepareW { SQLRETURN ret; TRACE3(_IN, "ppd", hstmt, szSqlStr, cchSqlStr); + HND_LOCK(hstmt); ret = EsSQLPrepareW(hstmt, szSqlStr, cchSqlStr); + HND_UNLOCK(hstmt); TRACE4(_OUT, "dpWd", ret, hstmt, szSqlStr, cchSqlStr); return ret; } @@ -513,8 +523,10 @@ SQLRETURN SQL_API SQLBindParameter( SQLRETURN ret; TRACE10(_IN, "pudddudpdp", hstmt, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue); + HND_LOCK(hstmt); ret = EsSQLBindParameter(hstmt, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue); + HND_UNLOCK(hstmt); TRACE11(_OUT, "dpudddudpdn", ret, hstmt, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue); return ret; @@ -573,10 +585,11 @@ SQLRETURN SQL_API SQLSetScrollOptions( /* Use SQLSetStmtOptions */ */ SQLRETURN SQL_API SQLExecute(SQLHSTMT hstmt) { - // TODO: set .stmt_curs = 0; in resultset SQLRETURN ret; TRACE1(_IN, "p", hstmt); + HND_LOCK(hstmt); ret = EsSQLExecute(hstmt); + HND_UNLOCK(hstmt); TRACE2(_OUT, "dp", ret, hstmt); return ret; } @@ -602,7 +615,9 @@ SQLRETURN SQL_API SQLExecDirectW { SQLRETURN ret; TRACE3(_IN, "ppd", hstmt, szSqlStr, cchSqlStr); + HND_LOCK(hstmt); ret = EsSQLExecDirectW(hstmt, szSqlStr, cchSqlStr); + HND_UNLOCK(hstmt); TRACE4(_OUT, "dpWd", ret, hstmt, szSqlStr, cchSqlStr); return ret; } @@ -652,7 +667,9 @@ SQLRETURN SQL_API SQLNumParams( { SQLRETURN ret; TRACE2(_IN, "pp", hstmt, pcpar); + HND_LOCK(hstmt); ret = EsSQLNumParams(hstmt, pcpar); + HND_UNLOCK(hstmt); TRACE3(_OUT, "dpt", ret, hstmt, pcpar); return ret; } @@ -683,7 +700,9 @@ SQLRETURN SQL_API SQLRowCount(_In_ SQLHSTMT StatementHandle, { SQLRETURN ret; TRACE2(_IN, "pp", StatementHandle, RowCount); + HND_LOCK(StatementHandle); /* see Note_sync */ ret = EsSQLRowCount(StatementHandle, RowCount); + HND_UNLOCK(StatementHandle); TRACE3(_OUT, "dpn", ret, StatementHandle, RowCount); return ret; } @@ -693,7 +712,9 @@ SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, { SQLRETURN ret; TRACE2(_IN, "pp", StatementHandle, ColumnCount); + HND_LOCK(StatementHandle); /* see Note_sync */ ret = EsSQLNumResultCols(StatementHandle, ColumnCount); + HND_UNLOCK(StatementHandle); TRACE3(_OUT, "dpt", ret, StatementHandle, ColumnCount); return ret; } @@ -721,8 +742,10 @@ SQLRETURN SQL_API SQLDescribeColW SQLRETURN ret; TRACE9(_IN, "pupdppppp", hstmt, icol, szColName, cchColNameMax, pcchColName, pfSqlType, pcbColDef, pibScale, pfNullable); + HND_LOCK(hstmt); ret = EsSQLDescribeColW(hstmt, icol, szColName, cchColNameMax, pcchColName, pfSqlType, pcbColDef, pibScale, pfNullable); + HND_UNLOCK(hstmt); TRACE10(_OUT, "dpuWdttNtt", ret, hstmt, icol, szColName, cchColNameMax, pcchColName, pfSqlType, pcbColDef, pibScale, pfNullable); return ret; @@ -749,8 +772,10 @@ SQLRETURN SQL_API SQLColAttributeW SQLRETURN ret; TRACE7(_IN, "pddpdtp", hstmt, iCol, iField, pCharAttr, cbDescMax, pcbCharAttr, pNumAttr); + HND_LOCK(hstmt); ret = EsSQLColAttributeW(hstmt, iCol, iField, pCharAttr, cbDescMax, pcbCharAttr, pNumAttr); + HND_UNLOCK(hstmt); #ifdef _WIN64 TRACE8(_OUT, "dpddpdtn", ret, hstmt, iCol, iField, pCharAttr, cbDescMax, pcbCharAttr, pNumAttr); @@ -771,8 +796,10 @@ SQLRETURN SQL_API SQLBindCol(SQLHSTMT StatementHandle, SQLRETURN ret; TRACE6(_IN, "pddpdp", StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); + HND_LOCK(StatementHandle); ret = EsSQLBindCol(StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); + HND_UNLOCK(StatementHandle); TRACE7(_OUT, "dpddpdn", ret, StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); return ret; @@ -783,7 +810,9 @@ SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) { SQLRETURN ret; TRACE1(_IN, "p", StatementHandle); + HND_LOCK(StatementHandle); ret = EsSQLFetch(StatementHandle); + HND_UNLOCK(StatementHandle); TRACE2(_OUT, "dp", ret, StatementHandle); return ret; } @@ -833,8 +862,10 @@ SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, SQLRETURN ret; TRACE6(_IN, "pHhplp", StatementHandle, ColumnNumber, TargetType, TargetValuePtr, BufferLength, StrLen_or_IndPtr); + HND_LOCK(StatementHandle); ret = EsSQLGetData(StatementHandle, ColumnNumber, TargetType, TargetValuePtr, BufferLength, StrLen_or_IndPtr); + HND_UNLOCK(StatementHandle); TRACE7(_OUT, "dpHhpln", ret, StatementHandle, ColumnNumber, TargetType, TargetValuePtr, BufferLength, StrLen_or_IndPtr); return ret; @@ -848,7 +879,9 @@ SQLRETURN SQL_API SQLSetPos( { SQLRETURN ret; TRACE4(_IN, "puuu", StatementHandle, RowNumber, Operation, LockType); + HND_LOCK(StatementHandle); ret = EsSQLSetPos(StatementHandle, RowNumber, Operation, LockType); + HND_UNLOCK(StatementHandle); TRACE5(_OUT, "dpuuu", ret,StatementHandle, RowNumber, Operation, LockType); return ret; } @@ -859,7 +892,9 @@ SQLRETURN SQL_API SQLBulkOperations( { SQLRETURN ret; TRACE2(_IN, "pd", StatementHandle, Operation); + HND_LOCK(StatementHandle); ret = EsSQLBulkOperations(StatementHandle, Operation); + HND_UNLOCK(StatementHandle); TRACE3(_OUT, "dpd", ret, StatementHandle, Operation); return ret; } @@ -868,6 +903,7 @@ SQLRETURN SQL_API SQLMoreResults(SQLHSTMT StatementHandle) { SQLRETURN ret; TRACE1(_IN, "p", StatementHandle); + /* no synchronization required */ ret = EsSQLMoreResults(StatementHandle); TRACE2(_OUT, "dp", ret, StatementHandle); return ret; @@ -886,8 +922,12 @@ SQLRETURN SQL_API SQLGetDiagFieldW( SQLRETURN ret; TRACE7(_IN, "dpddpdp", HandleType, Handle, RecNumber, DiagIdentifier, DiagInfoPtr, BufferLength, StringLengthPtr); + /* Note_diag: locking here only really makes sense for stmt and dbc, but + * uniformly locking env and desc too is harmless and simple */ + HND_LOCK(Handle); ret = EsSQLGetDiagFieldW(HandleType, Handle, RecNumber, DiagIdentifier, DiagInfoPtr, BufferLength, StringLengthPtr); + HND_UNLOCK(Handle); TRACE8(_OUT, "ddpddpdt", ret, HandleType, Handle, RecNumber, DiagIdentifier, DiagInfoPtr, BufferLength, StringLengthPtr); return ret; @@ -908,8 +948,10 @@ SQLRETURN SQL_API SQLGetDiagRecW SQLRETURN ret; TRACE8(_IN, "dpdpppdp", HandleType, Handle, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength); + HND_LOCK(Handle); /* see Note_diag */ ret = EsSQLGetDiagRecW(HandleType, Handle, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength); + HND_UNLOCK(Handle); TRACE9(_OUT, "ddpdWgWdt", ret, HandleType, Handle, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength); return ret; @@ -956,9 +998,11 @@ SQLRETURN SQL_API SQLColumnsW TRACE9(_IN, "ppdpdpdpd", hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, szColumnName, cchColumnName); + HND_LOCK(hstmt); ret = EsSQLColumnsW(hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, szColumnName, cchColumnName); + HND_UNLOCK(hstmt); TRACE10(_OUT, "dpWdWdWdWd", ret, hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, szColumnName, cchColumnName); @@ -990,6 +1034,7 @@ SQLRETURN SQL_API SQLForeignKeysW szFkCatalogName, cchFkCatalogName, szFkSchemaName, cchFkSchemaName, szFkTableName, cchFkTableName); + HND_LOCK(hstmt); ret = EsSQLForeignKeysW(hstmt, szPkCatalogName, cchPkCatalogName, szPkSchemaName, cchPkSchemaName, @@ -997,6 +1042,7 @@ SQLRETURN SQL_API SQLForeignKeysW szFkCatalogName, cchFkCatalogName, szFkSchemaName, cchFkSchemaName, szFkTableName, cchFkTableName); + HND_UNLOCK(hstmt); TRACE14(_OUT, "dpWdWdWdWdWdWd", ret, hstmt, szPkCatalogName, cchPkCatalogName, szPkSchemaName, cchPkSchemaName, @@ -1024,10 +1070,12 @@ SQLRETURN SQL_API SQLPrimaryKeysW szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName); + HND_LOCK(hstmt); ret = EsSQLPrimaryKeysW(hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName); + HND_UNLOCK(hstmt); TRACE8(_OUT, "dpWdWdWd", ret, hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, @@ -1085,9 +1133,11 @@ SQLRETURN SQL_API SQLSpecialColumnsW TRACE10(_IN, "pupdpdpduu", hstmt, fColType, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, fScope, fNullable); + HND_LOCK(hstmt); ret = EsSQLSpecialColumnsW(hstmt, fColType, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, fScope, fNullable); + HND_UNLOCK(hstmt); TRACE11(_OUT, "dpuWdWdWduu", ret, hstmt, fColType, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, fScope, fNullable); @@ -1144,9 +1194,11 @@ SQLRETURN SQL_API SQLTablesW TRACE9(_IN, "ppdpdpdpd", hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, szTableType, cchTableType); + HND_LOCK(hstmt); ret = EsSQLTablesW(hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, szTableType, cchTableType); + HND_UNLOCK(hstmt); TRACE10(_OUT, "dpWdWdWdWd", ret, hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, szTableType, cchTableType); @@ -1164,7 +1216,9 @@ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, SQLUSMALLINT Option) { SQLRETURN ret; TRACE2(_IN, "pd", StatementHandle, Option); + HND_LOCK(StatementHandle); ret = EsSQLFreeStmt(StatementHandle, Option); + HND_UNLOCK(StatementHandle); TRACE3(_OUT, "dpd", ret, StatementHandle, Option); return ret; } @@ -1173,30 +1227,42 @@ SQLRETURN SQL_API SQLCloseCursor(SQLHSTMT StatementHandle) { SQLRETURN ret; TRACE1(_IN, "p", StatementHandle); + HND_LOCK(StatementHandle); ret = EsSQLCloseCursor(StatementHandle); - TRACE2(_OUT, "dpd", ret, StatementHandle); + HND_UNLOCK(StatementHandle); + TRACE2(_OUT, "dp", ret, StatementHandle); return ret; } -#if WITH_EMPTY - SQLRETURN SQL_API SQLCancel(SQLHSTMT StatementHandle) { - RET_NOT_IMPLEMENTED; + SQLRETURN ret; + TRACE1(_IN, "p", StatementHandle); + HND_LOCK(StatementHandle); + ret = EsSQLCancel(StatementHandle); + HND_UNLOCK(StatementHandle); + TRACE2(_OUT, "dp", ret, StatementHandle); + return ret; } SQLRETURN SQL_API SQLCancelHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle) { - RET_NOT_IMPLEMENTED; + SQLRETURN ret; + TRACE2(_IN, "hp", HandleType, InputHandle); + HND_LOCK(InputHandle); + ret = EsSQLCancelHandle(HandleType, InputHandle); + HND_UNLOCK(InputHandle); + TRACE3(_IN, "dhp", ret, HandleType, InputHandle); + return ret; } +#if WITH_EMPTY SQLRETURN SQL_API SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { RET_NOT_IMPLEMENTED; } - #endif /* WITH_EMPTY */ @@ -1209,7 +1275,9 @@ SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle) { SQLRETURN ret; TRACE1(_IN, "p", ConnectionHandle); + HND_LOCK(ConnectionHandle); ret = EsSQLDisconnect(ConnectionHandle); + HND_UNLOCK(ConnectionHandle); TRACE2(_OUT, "dp", ret, ConnectionHandle); return ret; } @@ -1218,126 +1286,14 @@ SQLRETURN SQL_API SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) { SQLRETURN ret; TRACE2(_IN, "dp", HandleType, Handle); + if (! HND_TRYLOCK(Handle)) { + BUGH(Handle, "handle still locked while freeing attempt."); + return SQL_ERROR; + } ret = EsSQLFreeHandle(HandleType, Handle); TRACE3(_OUT, "ddp", ret, HandleType, Handle); return ret; } -#if 0 -// API - -SQLAllocHandle -SQLConnect -SQLDriverConnect -SQLBrowseConnect - -SQLDataSources -SQLDrivers -SQLGetInfo -SQLGetFunctions -SQLGetTypeInfo - -SQLSetConnectAttr -SQLGetConnectAttr -SQLSetEnvAttr -SQLGetEnvAttr -SQLSetStmtAttr -SQLGetStmtAttr - -SQLGetDescField -SQLGetDescRec -SQLSetDescField -SQLSetDescRec -SQLCopyDesc -SQLPrepare -SQLBindParameter -SQLGetCursorName -SQLSetCursorName -SQLSetScrollOptions - -SQLExecute -SQLExecDirect -SQLNativeSql -SQLDescribeParam -SQLNumParams -SQLParamData -SQLPutData - -SQLRowCount -SQLNumResultCols -SQLDescribeCol -SQLColAttribute -SQLBindCol -SQLFetch -SQLFetchScroll -SQLGetData -SQLSetPos -SQLBulkOperations -SQLMoreResults -SQLGetDiagField -SQLGetDiagRec - -SQLColumnPrivileges -SQLColumns -SQLForeignKeys -SQLPrimaryKeys -SQLProcedureColumns -SQLProcedures -SQLSpecialColumns -SQLStatistics -SQLTablePrivileges -SQLTables - -SQLFreeStmt -SQLCloseCursor -SQLCancel -SQLCancelHandle -SQLEndTran - -SQLDisconnect -SQLFreeHandle -#endif - -#if 0 -//ANSI / Unicode - -SQLBrowseConnect -SQLColAttribute -SQLColAttributes -SQLColumnPrivileges -SQLColumns -SQLConnect -SQLDataSources -SQLDescribeCol -SQLDriverConnect -SQLDrivers -SQLError -SQLExecDirect -SQLForeignKeys -SQLGetConnectAttr -SQLGetConnectOption -SQLGetCursorName -SQLGetDescField -SQLGetDescRec -SQLGetDiagField -SQLGetDiagRec -SQLGetInfo -SQLGetStmtAttr -SQLNativeSQL -SQLPrepare -SQLPrimaryKeys -SQLProcedureColumns -SQLProcedures -SQLSetConnectAttr -SQLSetConnectOption -SQLSetCursorName -SQLSetDescField -SQLSetStmtAttr -SQLSpecialColumns -SQLStatistics -SQLTablePrivileges -SQLTables -#endif - /* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */ diff --git a/driver/queries.c b/driver/queries.c index 2b526c77..429950cb 100644 --- a/driver/queries.c +++ b/driver/queries.c @@ -1180,6 +1180,32 @@ SQLRETURN EsSQLCloseCursor(SQLHSTMT StatementHandle) return EsSQLFreeStmt(StatementHandle, SQL_CLOSE); } +SQLRETURN EsSQLCancel(SQLHSTMT StatementHandle) +{ + esodbc_stmt_st *stmt = STMH(StatementHandle); + + /* + * Use cases: + * - "A function running asynchronously on the statement.": no async + * support. + * - "A function on a statement that needs data." TODO: if data-at-exec. + * - "A function running on the statement on another thread.": this could + * theoretically cancel an ongoing fetch/connect/etc. For now libcurl is + * left to timeout -- TODO: if swiching to "multi" API in libcurl. + * XXX: for this last case: stmt lock is being held here. + */ + + DBGH(stmt, "canceling current statement operation -- NOOP."); + return SQL_SUCCESS; +} + +SQLRETURN EsSQLCancelHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle) +{ + /* see EsSQLCancel() */ + DBGH(InputHandle, "canceling current handle operation -- NOOP."); + return SQL_SUCCESS; +} + SQLRETURN EsSQLNumResultCols(SQLHSTMT StatementHandle, _Out_ SQLSMALLINT *ColumnCount) { diff --git a/driver/queries.h b/driver/queries.h index ea018450..becfeab6 100644 --- a/driver/queries.h +++ b/driver/queries.h @@ -45,6 +45,8 @@ SQLRETURN EsSQLBulkOperations( SQLSMALLINT Operation); SQLRETURN EsSQLMoreResults(SQLHSTMT hstmt); SQLRETURN EsSQLCloseCursor(SQLHSTMT StatementHandle); +SQLRETURN EsSQLCancel(SQLHSTMT StatementHandle); +SQLRETURN EsSQLCancelHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle); SQLRETURN EsSQLNumResultCols(SQLHSTMT StatementHandle, _Out_ SQLSMALLINT *ColumnCount); diff --git a/driver/util.h b/driver/util.h index 9efd0c05..781dd171 100644 --- a/driver/util.h +++ b/driver/util.h @@ -172,7 +172,22 @@ size_t ui64tot(uint64_t ui64, void *buff, BOOL wide); (GetLastError() == ERROR_INSUFFICIENT_BUFFER) #define WCS2U8_ERRNO() GetLastError() + +/* + * Locking type and primitives. + */ +typedef SRWLOCK esodbc_mutex_lt; + +#define ESODBC_MUX_SINIT SRWLOCK_INIT +#define ESODBC_MUX_INIT(_m) InitializeSRWLock(_m) +#define ESODBC_MUX_DEL(_m) /* not needed/possible */ + +#define ESODBC_MUX_LOCK(_m) AcquireSRWLockExclusive(_m) +#define ESODBC_MUX_TRYLOCK(_m) TryAcquireSRWLockExclusive(_m) +#define ESODBC_MUX_UNLOCK(_m) ReleaseSRWLockExclusive(_m) + #else /* _WIN32 */ + #error "unsupported platform" /* TODO */ /* "[R]eturns the number of bytes written into the multibyte output * string, excluding the terminating NULL (if any)". Copies until \0 is