Bug #37278 SQLDriverConnect truncates the 5th parameter to 52 characters
Submitted: 9 Jun 2008 10:20 Modified: 10 Oct 2008 13:51
Reporter: Bogdan Matasaru Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / ODBC Severity:S2 (Serious)
Version:5.1.4 OS:Windows
Assigned to: Jess Balint CPU Architecture:Any
Tags: Connect string, SQLDriverConnect

[9 Jun 2008 10:20] Bogdan Matasaru
Description:
I am using MFC's CDatabase class to connect to a MySQL db through a DSN and the OpenEx method calls internally SQLDriverConnect. Although the 6th parameter is 512, the output OutputConnectionString parameter is truncated to 52 characters.

How to repeat:
Take any of the database samples from Visual Studio .NET 2003, create a user DSN using the 5.1.4 driver and try to connect to it from the sample.
[9 Jun 2008 11:28] Tonci Grgin
Hi Bogdan and thanks for your report.

> How to repeat:
Take any of the database samples from Visual Studio .NET 2003, create a user DSN using the 5.1.4 driver and try to connect to it from the sample.

Can you be more precise please? Or, even better, attach test case.
My test case is showing no problem in VS2003:
    SQLRETURN retc;
    SQLCHAR connString[256] = "DRIVER={MySQL ODBC 5.1 Driver};SERVER=QCore;PORT=3306;USER=root;PWD=******;Database=test;";
    SQLCHAR     connOut[255]; /* buffer for connection output */
    SQLSMALLINT szConnOut; /* num bytes returned in connOut */

    argv[1] = "Test514GA";
    argv[2] = "root";
    argv[3] = "******";

    retc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
    if (retc != SQL_SUCCESS && retc != SQL_SUCCESS_WITH_INFO)
        return(PrintError(SQL_HANDLE_ENV, henv, retc));

    retc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_NTS);
    if (retc != SQL_SUCCESS && retc != SQL_SUCCESS_WITH_INFO)
        return(PrintError(SQL_HANDLE_ENV, henv, retc));
        
    retc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
    if (retc != SQL_SUCCESS && retc != SQL_SUCCESS_WITH_INFO)
        return(PrintError(SQL_HANDLE_ENV, henv, retc));
        
    std::cout << "Connecting to " << argv[1] << std::endl;

    retc = SQLDriverConnect(hdbc, GetDesktopWindow(), connString, SQL_NTS, connOut, 255, &szConnOut, 0);
	
    if (retc != SQL_SUCCESS && retc != SQL_SUCCESS_WITH_INFO)
        return(PrintError(SQL_HANDLE_DBC, hdbc, retc));

    std::cout << "Connected " << argv[1] << std::endl;
    std::cout << "5th parameter " << szConnOut << std::endl;

and so on.
connString, connOut and szConnOut are all in sync, and in my case, 92. Could it be that MFC function does something wrong?
[9 Jun 2008 11:36] Bogdan Matasaru
It's all happening in the MFC library, in CDatabase::OpenEx.
I am making the call with a NULL connection string, so the selector dialog appears.

CDatabase database;
if( database.OpenEx( NULL, CDatabase::forceOdbcDialog) ) { ... }

This method calls CDatabse::Connect which has a sequence

	TCHAR szConnectOutput[MAX_CONNECT_LEN];
	TCHAR *pszConnectInput = const_cast<LPTSTR>(static_cast<LPCTSTR>(m_strConnect));
	RETCODE nRetCode;
	SWORD nResult;
	UWORD wConnectOption = SQL_DRIVER_COMPLETE;

	if (dwOptions & noOdbcDialog)
		wConnectOption = SQL_DRIVER_NOPROMPT;
	else if (dwOptions & forceOdbcDialog)
		wConnectOption = SQL_DRIVER_PROMPT;
	AFX_SQL_SYNC(::SQLDriverConnect(m_hdbc, hWnd, reinterpret_cast<SQLTCHAR *>(pszConnectInput),
		SQL_NTS, reinterpret_cast<SQLTCHAR *>(szConnectOutput), _countof(szConnectOutput),
		&nResult, wConnectOption));

MAX_CONNECT_LEN is 512, wConnectOptions becomes SQL_DRIVER_PROMPT and at return, nResult is 52 although the connection string fits into the buffer. I tried to null out the output buffer in debug mode but it changes nothing.

I will try to make a standalone sample.

By the way, I have the version 3.51 of the driver installed on the same machine and that seems to work fine.

Regards,
Bogdan
[9 Jun 2008 11:44] Bogdan Matasaru
Select Tools/ODBC Test from the menu to show the retrieved connection string

Attachment: TestODBC.zip (application/zip, text), 29.48 KiB.

[9 Jun 2008 11:46] Bogdan Matasaru
I have uploaded a file that exhibits the behavior.
The method is the last one in the document implementation and it is a trivial test.

Moreover, if the DSN is a system one, when the DSN dialog is displayed all the fields are blank except the name. With a user DSN the fields are correctly populated but the connection string is truncated.

Regards,
Bogdan
[9 Jun 2008 11:56] Bogdan Matasaru
Updated test that displays also the length of resulting connection string.

Attachment: TestODBC.zip (application/zip, text), 29.46 KiB.

[11 Jun 2008 12:04] Tonci Grgin
Bogdan, although standalone test case works as expected (implying there's something wrong with how MFC handles class) I'll give it another look.
[11 Jun 2008 12:49] Bogdan Matasaru
Tonci,

could you send me the test you are using? As I mentioned before, the 3.x driver works fine, only 5.1.4 creates the problem. Do you expect some nulled out string or something like that?

Thanks,
Bogdan
[11 Jun 2008 13:04] Tonci Grgin
Your test case result

Attachment: Bug37278.JPG (image/jpeg, text), 73.65 KiB.

[11 Jun 2008 13:07] Tonci Grgin
Bogdan, as you can see from attached image (password brushed out), I am not able to repeat your problem using:
 MySQL server 5.0.64pb on WinXP Pro SP2 localhost and c/ODBC 5.1.5 rev. 1115.
[11 Jun 2008 13:13] Tonci Grgin
Unfortunately, tests are done in VM which I always revert to snapshot (cause of all the old SW and frameworks) but I'll check if I have something similar. In any case, my snippet looks enough for you to try it out.
[11 Jun 2008 20:18] Bogdan Matasaru
Please try it with a DSN based on 3.5.x or change the DSN name to something longer. Did you omit the password? It should be in the returned string (again, see 3.5.x based DSN) otherwise the connection string is useless.
[12 Jun 2008 4:53] Tonci Grgin
Bogdan, of course I'll try again even though VM is reverted back to clean state (no ODBC drivers and stuff.

> Please try it with a DSN based on 3.5.x or change the DSN name to something longer.

Please explain the purpose of this test. DSN is long enough to prove or disprove your claim, right?

> Did you omit the password? It should be in the returned string (again, see 3.5.x based DSN) otherwise the connection string is useless.

Of course the password was there (8 chars), I just brushed it out from picture.

You do not have to be alarmed by me ruling this as CRp. I will follow up until we find the reason for this misbehavior on your box. I suspect this is due to some MS update... Also, please try driver from snapshots page, http://downloads.mysql.com/snapshots/mysql-connector-odbc-5.1/mysql-connector-odbc-noinsta..., and inform me of result.
[12 Jun 2008 6:34] Bogdan Matasaru
I added code that tries to open the connection based on the returned string.

Attachment: TestODBCDoc.cpp (text/plain), 1.79 KiB.

[20 Jun 2008 8:05] Tonci Grgin
Re-testing.
[20 Jun 2008 9:46] Tonci Grgin
Bogdan, I have re-worked your test case as follows:
		CDatabase database;
		if (database.OpenEx("Provider=MSDASQL.1;Persist Security Info=False;DRIVER={MySQL ODBC 5.1 Driver};SERVER=QCore;PORT=MyPort;USER=MyUserID;PWD=MyPassword;Database=test;OPTION=1 + 2 + 8 + 32 + 16384 + 8388608", CDatabase::noOdbcDialog))
		{
			CString strMessage, strConnect = database.GetConnect();
			strMessage.Format(_T("%s\n%d chars"), (LPCTSTR)strConnect, strConnect.GetLength());
			AfxMessageBox(strMessage);
			database.Close();

			if (database.OpenEx(strConnect, CDatabase::noOdbcDialog))
				AfxMessageBox(_T("Database successfuly opened"));
			else
				AfxMessageBox(_T("Database could not be opened"));
		}

		//if (database.OpenEx(NULL, CDatabase::forceOdbcDialog))
		//{
		//	CString strMessage, strConnect = database.GetConnect();
		//	strMessage.Format(_T("%s\n%d chars"), (LPCTSTR)strConnect, strConnect.GetLength());
		//	AfxMessageBox(strMessage);
		//	database.Close();

		//	if (database.OpenEx(strConnect, CDatabase::noOdbcDialog))
		//		AfxMessageBox(_T("Database successfuly opened"));
		//	else
		//		AfxMessageBox(_T("Database could not be opened"));
		//}
	}
	catch (CException* e)
	{
		e->ReportError();
		e->Delete();
	}

My entire connect string is 183 chars in length. Now, I am certain there is a problem with *strConnect = database.GetConnect();* call! After this call, my connection string is *not* truncated but reworked to "+	strConnect	{0x00327f28 "ODBC;DRIVER={MySQL ODBC 5.1 Driver};PWD=MyPassword;Provider=MSDASQL.1;Persist Security Info=False;SERVER=QCore;PORT=MyPort;USER=MyUserID;Database=test;OPTION=1 + 2 + 8 + 32 + 16384 + 8388608;"}	ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char> > >"

As you can see, although it might appear truncated to you, connstring is *not* actually truncated but reworked according to some MFC internal standards...

I suggest you to report this to Microsoft and ask for explanation on why a simple call to "database.GetConnect()" reworks your connect string and what is the proper way to manage it.

Note: dbcore.cpp, Ln:755 m_strConnect = _afxODBCTrail (m_strConnect is *corect*)
// Connect string must have "ODBC;" << this is where your troubles start as stuff is added to *correct* conn string +, on next line, it is assigned some dubious output (mangled as described above) from szConnectOutput variable. I'd say it happens in dbcore.cpp, Ln 732:
AFX_SQL_SYNC(::SQLDriverConnect(m_hdbc, hWnd, reinterpret_cast<SQLTCHAR *>(pszConnectInput),SQL_NTS, reinterpret_cast<SQLTCHAR *>(szConnectOutput), sizeof(szConnectOutput),&nResult, wConnectOption));

Please try 5.1.5 from url in my last post too.

I consider this matter closed as MyODBC provided correct conn-string regardless of length.
[25 Jun 2008 19:18] Niklas Lindquist
I too ran into this problem, and after some investigation it seems that MFC is not to blame.

If I use a connection string to connect, it all works fine, but if I let the setup library (?) create the connection string on the fly, the returned connection string is truncated.

I will add two images to show this.

It uses the following code:

CDatabase db;
if (db.OpenEx(NULL))
{
  CString strConnectionString = db.GetConnect();
}
[25 Jun 2008 19:22] Niklas Lindquist
Connection

Attachment: mysqlodbc.png (image/png, text), 30.38 KiB.

[25 Jun 2008 19:29] Niklas Lindquist
The immediate result of the connect call

Attachment: mysqlodbcdbg.png (image/png, text), 48.16 KiB.

[25 Jun 2008 22:11] Bogdan Matasaru
Well, it is exactly what I reported. I guess we have to fix it because we are the only once who can/want repeat it...

Again, try it with the version 3.5 of the driver and all is fine.
[2 Sep 2008 18:49] Tonci Grgin
Been busy, catching up on this.
[2 Sep 2008 22:00] Jess Balint
pointer math returns "units", eg. SQLWCHAR in this case, not bytes. didn't need to have the division there

=== modified file 'util/installer.c'
--- util/installer.c    2008-08-09 01:07:56 +0000
+++ util/installer.c    2008-09-02 21:56:27 +0000
@@ -889,7 +889,7 @@
   /* always ends in delimiter, so overwrite it */
   *(attrs - 1)= 0;

-  return (origchars - attrslen) / sizeof(SQLWCHAR);
+  return origchars - attrslen;
 }
[3 Oct 2008 14:54] Jess Balint
Patch pushed to trunk. Will be released in 5.1.6.
[10 Oct 2008 13:51] Tony Bedford
An entry was added to the 5.1.6 changelog:

The SQLDriverConnect method truncated the OutputConnectionString parameter to 52 characters.