Index: driver/driver.h =================================================================== --- driver/driver.h (revision 814) +++ driver/driver.h (working copy) @@ -321,7 +321,7 @@ MYERROR error; FILE *query_log; char *dsn,*database, - *user,*password,*server; + *user,*password,*server,*socket; char st_error_prefix[255]; ulong flag; SQLUINTEGER login_timeout; Index: driver/execute.c =================================================================== --- driver/execute.c (revision 814) +++ driver/execute.c (working copy) @@ -31,7 +31,6 @@ * SQLExecute (ISO 92) * * SQLParamData (ISO 92) * * SQLPutData (ISO 92) * - * SQLCancel (ISO 92) * * * ****************************************************************************/ @@ -791,12 +790,63 @@ } -/* - @type : ODBC 1.0 API - @purpose : cancels the processing on a statement +/** + Cancel the query by opening another connection and using KILL when called + from another thread while the query lock is being held. Otherwise, treat as + SQLFreeStmt(hstmt, SQL_CLOSE). + + @param[in] hstmt Statement handle + + @return Standard ODBC result code */ - SQLRETURN SQL_API SQLCancel(SQLHSTMT hstmt) { - return my_SQLFreeStmt(hstmt,SQL_CLOSE); + SQLRETURN rc= SQL_ERROR; + DBC *dbc= ((STMT *)hstmt)->dbc; + MYSQL *second= NULL; + int error; + + error= pthread_mutex_trylock(&dbc->lock); + + /* If there's no query going on, just close the statement. */ + if (error == 0) + { + pthread_mutex_unlock(&dbc->lock); + return my_SQLFreeStmt(hstmt, SQL_CLOSE); + } + + /* If we got a non-BUSY error, it's just an error. */ + if (error != EBUSY) + return SQL_ERROR; + + /* + If the mutex was locked, we need to make a new connection and KILL the + ongoing query. + */ + mysql_thread_init(); + second= mysql_init(second); + + /** @todo need to preserve and use ssl params */ + + if (!mysql_real_connect(second, dbc->server, dbc->user, dbc->password, + NULL, dbc->port, dbc->socket, dbc->flag)) + { + return SQL_ERROR; + } + + { + char buff[40]; + /* buff is always big enough because max length of %lu is 15 */ + sprintf(buff, "KILL /*!50000 QUERY */ %lu", mysql_thread_id(&dbc->mysql)); + if (mysql_real_query(second, buff, strlen(buff))) + goto error; + } + + rc= SQL_SUCCESS; + +error: + mysql_close(second); + mysql_thread_end(); + + return rc; } Index: driver/dll.c =================================================================== --- driver/dll.c (revision 814) +++ driver/dll.c (working copy) @@ -85,33 +85,31 @@ #endif } + /* @type : myodbc3 internal @purpose : clean all resources while unloading.. */ - void myodbc_end() { - if (!--myodbc_inited) - { - my_free(decimal_point,MYF(0)); - my_free(default_locale,MYF(0)); - my_free(thousands_sep,MYF(0)); -#ifdef NOT_YET_USED - mysql_server_end(); -#endif + if (!--myodbc_inited) + { + my_free(decimal_point,MYF(0)); + my_free(default_locale,MYF(0)); + my_free(thousands_sep,MYF(0)); #ifdef MY_DONT_FREE_DBUG - /* - Function my_end() was changed to deallocate DBUG memory by default, - a flag MY_DONT_FREE_DBUG was added to disable this new behaviour - */ - my_end(MY_DONT_FREE_DBUG); + /* + Function my_end() was changed to deallocate DBUG memory by default, + a flag MY_DONT_FREE_DBUG was added to disable this new behaviour + */ + my_end(MY_DONT_FREE_DBUG); #else - my_end(0); + my_end(0); #endif - } + } } + /* @type : myodbc3 internal @purpose : main entry point Index: driver/connect.c =================================================================== --- driver/connect.c (revision 814) +++ driver/connect.c (working copy) @@ -244,6 +244,9 @@ if (ds->database) dbc->database= my_strdup(ds_get_utf8attr(ds->database, &ds->database8), MYF(MY_WME)); + if (ds->socket) + dbc->socket= my_strdup(ds_get_utf8attr(ds->socket, &ds->socket8), + MYF(MY_WME)); dbc->port= ds->port; dbc->flag= options; Index: driver/handle.c =================================================================== --- driver/handle.c (revision 814) +++ driver/handle.c (working copy) @@ -249,6 +249,7 @@ my_free(dbc->dsn,MYF(MY_ALLOW_ZERO_PTR)); my_free(dbc->database,MYF(MY_ALLOW_ZERO_PTR)); my_free(dbc->server,MYF(MY_ALLOW_ZERO_PTR)); + my_free(dbc->socket,MYF(MY_ALLOW_ZERO_PTR)); my_free(dbc->user,MYF(MY_ALLOW_ZERO_PTR)); my_free(dbc->password,MYF(MY_ALLOW_ZERO_PTR)); pthread_mutex_destroy(&dbc->lock); Index: test/my_basics.c =================================================================== --- test/my_basics.c (revision 814) +++ test/my_basics.c (working copy) @@ -592,6 +592,44 @@ } +#ifdef WIN32 +DECLARE_TEST(sqlcancel) +{ + skip("Windows versions of this test not written yet."); +} +#else +void *cancel_in_one_second(void *arg) +{ + HSTMT *hstmt= arg; + + sleep(1); + + if (SQLCancel(hstmt) != SQL_SUCCESS) + printMessage("SQLCancel failed!"); + + return NULL; +} + +#include + +DECLARE_TEST(sqlcancel) +{ + pthread_t thread; + + pthread_create(&thread, NULL, cancel_in_one_second, hstmt); + + /* SLEEP(n) returns 1 when it is killed. */ + ok_sql(hstmt, "SELECT SLEEP(200)"); + ok_stmt(hstmt, SQLFetch(hstmt)); + is_num(my_fetch_int(hstmt, 1), 1); + + pthread_join(&thread, NULL); + + return OK; +} +#endif + + BEGIN_TESTS ADD_TEST(my_basics) ADD_TEST(t_max_select) @@ -609,6 +647,7 @@ ADD_TEST(t_bug30983) /* TODO fix this test create a comparable output string */ ADD_TODO(t_driverconnect_outstring) + ADD_TEST(sqlcancel) END_TESTS Index: ChangeLog =================================================================== --- ChangeLog (revision 814) +++ ChangeLog (working copy) @@ -16,8 +16,6 @@ * Implement SQLBrowseConnect(): http://msdn2.microsoft.com/en-us/library/ms714565.aspx http://msdn2.microsoft.com/en-us/library/ms712446.aspx - * Implement SQLCancel() (Bug #15601): - http://msdn2.microsoft.com/en-us/library/ms714112.aspx * Implement arrays of parameters: http://msdn2.microsoft.com/en-us/library/ms711818.aspx @@ -26,6 +24,7 @@ 5.1.1 Functionality added or changed: + * Implemented SQLCancel() (Bug #15601). * Replaced the internal library which handles creation and loading of DSN information. The new library, which was originally a part of Connector/ODBC 5.0, supports Unicode option values.