Bug #69194 SQLSetPos(...SQL_UPDATE...) fails when SQL contains ? parameter
Submitted: 10 May 2013 11:39 Modified: 13 Aug 2024 17:52
Reporter: Frank Munkert Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / ODBC Severity:S2 (Serious)
Version:5.2.4 OS:Windows (Win 7 SP1, 32-bit)
Assigned to: Bogdan Degtyariov CPU Architecture:Any
Tags: ODBC SQLSetPos

[10 May 2013 11:39] Frank Munkert
Description:
A statement like

   retcode = SQLSetPos(hStmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE);

cause a heap violation (resulting in a crash) or fails with an error message, when the SQL statement contains a "?", e.g.:

   retcode = SQLExecDirect(hStmt, (_TUCHAR*)_T("SELECT * FROM `testtab` WHERE `name` = ?"), SQL_NTS);

The crash does not occur if '?' is replaced by a string literal.

The error message is:

   HY000: [MySQL][ODBC 5.2(w) Driver][mysqld-5.6.11]You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '`name`=,`myval`= WHERE `id`= LIMIT 1' at line 1

How to repeat:
Prepare the database:

   CREATE TABLE testtab
   (
      id INTEGER NOT NULL PRIMARY KEY,
	  name CHAR(80) NOT NULL,
	  myval INTEGER NOT NULL
   )

   INSERT INTO testtab VALUES (1, 'This is a test', 100)

Compile and execute the following code:

#include "stdafx.h"

// Put your ODBC connect string here:
#define CONNECT_STRING _T("DSN=FMUMYSQL_FMU1;SERVER=fmumysql;UID=fmu1;PWD=fmu1;DATABASE=fmuschema;PORT=3306")

#define CHECK_ERR(retcode, hStmt, msg) \
	if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) \
	{ \
		Error(hStmt, msg, retcode); \
	}

CString GetOdbcErrorText(HSTMT hStmt)
{
	CString strMsg;
	_TUCHAR szSQLState[256];
	SDWORD fNativeError;
	_TUCHAR szErrorMsg[SQL_MAX_MESSAGE_LENGTH];
	SWORD cbErrorMsg;
	RETCODE retcode;
	for (SQLSMALLINT nRec=1;; nRec++)
	{
		retcode	= SQLGetDiagRec(SQL_HANDLE_STMT, hStmt, nRec,
			szSQLState, &fNativeError, szErrorMsg,
			SQL_MAX_MESSAGE_LENGTH-1, &cbErrorMsg);
		if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
		{
			CString strTmp;
			strTmp.Format(_T("%s: %s  "), szSQLState, szErrorMsg);
			strMsg += strTmp;
		}
		else
		{
			return strMsg;
		}
	}

	return CString();
}

static void Error(HSTMT hStmt, LPCTSTR szMsg, RETCODE retcode)
{
	CString strOdbcErr;
	if (hStmt != NULL)
	{
		strOdbcErr = GetOdbcErrorText(hStmt);
	}
	_ftprintf(stderr, _T("%s %s (retcode %d)\n"), szMsg, (LPCTSTR) strOdbcErr, (int) retcode);
	exit(EXIT_FAILURE);
}

int _tmain(int argc, _TCHAR* argv[])
{
	// Allocate handles:

	HENV hEnv;
	SQLRETURN retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
	CHECK_ERR(retcode, NULL, _T("Cannot allocate an ODBC environment"));

	retcode = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);
	CHECK_ERR(retcode, NULL, _T("Cannot set ODBC version"));

	HDBC hDbc;
	retcode = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
	CHECK_ERR(retcode, NULL, _T("Cannot allocate a connection handle"));
		
	// Connect to database:
	_TUCHAR szBuf[1024];
	short wLen;
	retcode = SQLDriverConnect(hDbc, NULL, (_TUCHAR*) CONNECT_STRING,
		(SQLSMALLINT) _tcslen(CONNECT_STRING), szBuf, sizeof(szBuf), &wLen, 
		SQL_DRIVER_NOPROMPT);
	CHECK_ERR(retcode, NULL, _T("Cannot connect to ODBC data source"));

	// Allocate statement handle:
	HSTMT hStmt;
	retcode = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
	CHECK_ERR(retcode, NULL, _T("Cannot allocate a statement handle"));

	// Bind input parameter for column "name":
	SQLLEN uLen = 28;
	retcode = SQLBindParameter(hStmt,
			1, SQL_PARAM_INPUT, SQL_C_WCHAR,
			SQL_WCHAR, 14, 0,
			L"This is a test", 28, &uLen);
	CHECK_ERR(retcode, hStmt, _T("Cannot bind column 1 for update"));

	// Perform a bulk-insert of all rows:
	retcode = SQLExecDirect(hStmt, (_TUCHAR*)_T("SELECT * FROM `testtab` WHERE `name` = ?"), SQL_NTS);
	CHECK_ERR(retcode, hStmt, _T("Failed to execute SELECT statement"));

	// Bind three columns id, name, and myval:
	long nId = 0;
	SQLLEN lenId = 4;
	retcode = SQLBindCol(hStmt, 1, SQL_C_SLONG, &nId, lenId, &lenId);
	CHECK_ERR(retcode, hStmt, _T("Cannot bind column 'id'"));
	wchar_t szName[81];
	SQLLEN lenName = 0;
	retcode = SQLBindCol(hStmt, 2, SQL_C_WCHAR, szName, 81*sizeof(wchar_t), &lenName);
	CHECK_ERR(retcode, hStmt, _T("Cannot bind column 'name'"));
	long nMyVal = 0;
	SQLLEN lenMyVal = 4;
	retcode = SQLBindCol(hStmt, 3, SQL_C_SLONG, &nMyVal, lenMyVal, &lenMyVal);
	CHECK_ERR(retcode, hStmt, _T("Cannot bind column 'myVal'"));

	// Fetch first row:
	retcode = SQLFetch(hStmt);
	CHECK_ERR(retcode, hStmt, _T("SQLFetch failed"));

	// Change 'myVal' column to 200 when SQLSetPos is called:
	nMyVal = 200;

	// Update values in columns 'id', 'name', and 'myVal':
	retcode = SQLSetPos(hStmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE);
	CHECK_ERR(retcode, hStmt, _T("SQLSetPos failed"));

	assert(_CrtCheckMemory());

	// Free all handles:
	SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
	SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
	SQLFreeHandle(SQL_HANDLE_ENV, hEnv);

	_tprintf(_T("Done\n"));

	return 0;
}

stdafx.h:

#pragma once

#include "targetver.h"

#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // some CString constructors will be explicit

#include <atlbase.h>
#include <atlstr.h>

#include <sql.h>
#include <sqlext.h>

#include <assert.h>
[10 May 2013 11:41] Frank Munkert
Visual Studio 2010 solution with sample code

Attachment: SqlSetPosBug.zip (application/octet-stream, text), 10.32 KiB.

[13 May 2013 11:44] Bogdan Degtyariov
Hi Frank,

Thank you for your interest in MySQL software and a very special thanks for providing a C++ test case, which made the bug verification easier for me.

The error message I received is different from yours:
SQLSetPos failed 21S02: [MySQL][ODBC 5.2(w) Driver][mysqld-5.6.11-log]Degree of derived table does not match column list   (retcode -1)

However, this should not happen anyway, so I am setting the bug status to "Verified".

If you need to make your application working as soon as possible there is a workaround for this particular problem:

 add "NO_SSPS=1;" to the connection string or alternatively set the corresponding option in DSN through the GUI prompt (Details -> Misc -> Prepare statements on the client).

This is only a temporary measure while the actual bug is being fixed.
[8 Jun 2018 21:43] Viorel Preoteasa
What is the status of this bug? It seems that I encounter the same problem. For me SQLSetPos crashes when using parameters (?)
[13 Aug 2024 2:58] Bogdan Degtyariov
Posted by developer:
 
The root cause of this bug was fixed by a patch for bug#18641963.
Only a unit test was added to the source tree for the current ticket.
[13 Aug 2024 17:52] Philip Olson
Posted by developer:
 
Fixed as of the upcoming MySQL Connector/ODBC 9.1.0 release, and here's the proposed changelog entry from the documentation team:

Fixed the SQLBulkOperations() function's generated WHERE clause, which
could potentially cause an application to unexpectedly halt.

Thank you for the bug report. The above release note was for a different bug that also closes this bug, and sorry for the long delay!