Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
Author |
Topic |
Awal Junanto
Starting Member
2 Posts |
Posted - 2010-02-22 : 11:13:36
|
Dear all,I came across a case where when cursor side is set to SQL_CURSOR_STATIC, under certain circumstances SQL server doesn’t throw any error message although it failed to execute the SQL statement.I use ODBC and C++ to access a remote SQL server.This is the client side code to reproduce this problem (you must compile this using the multi-threaded CRT libraries):#include <stdio.h>#include <process.h>#include <windows.h>#include <sql.h>#include <sqlext.h>SQLHENV henv = SQL_NULL_HENV;bool sql_error(const char* func, SQLRETURN rc, SQLHDBC hdbc=SQL_NULL_HDBC, SQLHSTMT hstmt=SQL_NULL_HSTMT);bool exec_sql(SQLHDBC hdbc, SQLCHAR* sql);void sql_thread(void* arg);/**********/void main(){ SQLRETURN rc; // allocate environment handle rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); if (sql_error("SQLAllocHandle(SQL_HANDLE_ENV)", rc)) return; // set ODBC version rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); if (sql_error("SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)", rc)) return; // run two concurent threads to access the database simultaneously for (int i=0; i<2; i++) { _beginthread(sql_thread, 0, (void*)i); } // wait until user presses return getchar(); return;}/**********/void sql_thread(void* arg){ int thread_num = (int)arg; SQLRETURN rc; SQLHDBC hdbc; // allocate database connection handle rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); if (sql_error("SQLAllocHandle(SQL_HANDLE_DBC)", rc)) return; // establish connection to the database SQLCHAR *inp_dsn = (SQLCHAR*)"DRIVER=SQL Server;SERVER=127.0.0.1;DATABASE=foo;UID=sa;PWD=;"; SQLCHAR out_dsn[1024+1]; SQLSMALLINT out_dsn_len; rc = SQLDriverConnect(hdbc, 0, inp_dsn, SQL_NTS, out_dsn, sizeof(out_dsn), &out_dsn_len, SQL_DRIVER_NOPROMPT); if (sql_error("SQLDriverConnect", rc, hdbc)) return; for (unsigned iter = 1; ; iter++) { char sql[128]; sprintf(sql, "exec poor_sql %d, %d", thread_num, iter); printf("%s\n", sql); if (!exec_sql(hdbc, (SQLCHAR*)sql)) { return; } Sleep(100); }}/**********/bool exec_sql(SQLHDBC hdbc, SQLCHAR* sql){ SQLRETURN rc; SQLHSTMT hstmt; // allocate a statement handle rc = SQLAllocStmt(hdbc, &hstmt); if (sql_error("SQLAllocStmt", rc, hdbc, hstmt)) return false; // set cursor type rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0); if (sql_error("SQLSetStmtAttr(SQL_ATTR_CURSOR_TYPE)", rc, hdbc, hstmt)) return false; // execute SQL statement rc = SQLExecDirect(hstmt, sql, SQL_NTS); if (sql_error("SQLExecDirect", rc, hdbc, hstmt)) return false; // close the handle rc = SQLFreeStmt(hstmt, SQL_DROP); if (sql_error("SQLFreeStmt(SQL_DROP)", rc, hdbc, hstmt)) return false; return true;}/**********/bool sql_error(const char* func, SQLRETURN rc, SQLHDBC hdbc, SQLHSTMT hstmt){ if (rc == SQL_SUCCESS) return false; SQLCHAR err_msg[SQL_MAX_MESSAGE_LENGTH+1] = {0}; SQLCHAR sql_state = {0}; SQLINTEGER native_err = 0; SQLSMALLINT msg_len = 0; SQLError(henv, hdbc, hstmt, sql_state, &native_err, err_msg, sizeof(err_msg), &msg_len); printf("%s: [%s][%d]%s\n", func, sql_state, native_err, err_msg); if (rc == SQL_SUCCESS_WITH_INFO) return false; return true;}And the server side is as follow:CREATE TABLE visit_log(thread INT, iter INT);GOCREATE PROCEDURE poor_sql @thread INT, @iter INTASBEGIN IF object_id('tempdb..#foo') > 0 DROP TABLE #foo; CREATE TABLE #foo(val INT, CONSTRAINT foo_pk PRIMARY KEY(val)); WAITFOR DELAY '000:00:01' -- simulate processing delay DROP TABLE #foo; INSERT INTO visit_log VALUES(@thread, @iter);ENDTheoritically if there is no error, the table visit_log should contain all successful iterations for each thread. However, that is not the case. It looks more like thisthread iter1 10 111 11If I don’t set the cursor type to SQL_CURSOR_STATIC then the following correct error message is displayed: [42S01][Microsoft][ODBC SQL Server Driver][SQL Server]There is already an object named 'foo_pk' in the database.I have tested this on the following platform: SQL 2000 client & SQL 2000 server:odbc32.dll 3.525.1117.0sqlsrv32.dll 3.85.1117and SQL 2008 client & SQL 2008 server:odbc32.dll 3.526.3959.0sqlsrv32.dll 3.86.3959Please advise.Thanks in advanceAJ |
|
Awal Junanto
Starting Member
2 Posts |
Posted - 2010-02-23 : 03:16:06
|
Hi again,Maybe I was not very clear with my previous post.My department is responsible for the database client part (C++ part) while another department is responsible for customizing the SQL part, therefore, I cannot control the validity of each written SQL statement. The real deliverable has a lot of different SQL stored procedures and each of them is usually very long and complex. So when there is a problem which goes silently without any error message like in this case, it is very difficult to track down. The above sample is only a simplified version which contains just enough code to reproduce the issue.So, I am hoping to get some guidance for the experts here about how I can build a robust client which will throw proper error message when it encounters such issue.Or, am I asking in the wrong forum?Thanks again.Awal |
|
|
|
|
|
|
|