Skip Menu |

This queue is for tickets about the DBD-DB2 CPAN distribution.

Report information
The Basics
Id: 83052
Status: new
Priority: 0/
Queue: DBD-DB2

People
Owner: Nobody in particular
Requestors: merijnb [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in:
  • 1.84
  • 1.85
Fixed in: (no value)



Subject: Connecting to database using different codepage results in double free
Hello, When using a database that was created using a big5 code set, I see a warning at program exit: DBD::MSDB2::db disconnect failed: Free HENV Failed at testdb2.pl line 59. DBD::MSDB2::db disconnect failed: Free HENV Failed at testdb2.pl line 59. Tracing the problem, this is because the DB2CODEPAGE was not set in the environment. If I export DB2CODEPAGE=950 before, this works without the free failing warning message. Debugging the code in dbdimp.c I see in method dbd_db_connect the ret value after the following line: ret = SQLDriverConnect(imp_dbh->hdbc, (SQLHWND)NULL,dbname,SQL_NTS,NULL,0,NULL,SQL_DRIVER_NOPROMPT); being set to 1, which in sqlcli.h is defined: #define SQL_SUCCESS_WITH_INFO 1 and the next line does CHECK_ERROR, which is defined in dbdimp.h and thus calls diagnoseError. In here SQL_SUCCESS_WITH_INFO is treated as an error, and when the code returns, it still is and a goto exit is followed. At this exit: label, the ret value is checked, and if it is not SQL_SUCCESS, cleanup code is run. So in this case the hdbc and henv handles are both freed. This is the root cause of the problem. Then the code returns this ret value to the caller, which is in dbd_db_login2. Here the return code is checked using EOI: ret = dbd_db_connect( dbh, imp_dbh, (SQLCHAR*)dbname, (SQLCHAR*)uid, (SQLCHAR*)pwd, attr ); EOI(ret); This check is defined as: #define EOI(x) if (x < 0 || x == SQL_NO_DATA) return (FALSE) since SQL_SUCCESS_WITH_INFO is equal to 1, this does not return false and the code carries on, registering the handler and returning true for a successful connect. But the connect actually had info and since it was considered an error, the handles were cleaned up. Later, at disconnect time, the code in dbd_db_disconnect will call SQLFreeHandle and this will then fail. We see the warning twice since the DESTROY phase does this again. The SQL_SUCCESS_WITH_INFO that is returned yields this warning: DB2/LINUXX8664] SQL0863W A successful connection was made, but only single byte characters should be used. SQLSTATE=01539 which is a fair warning. But since the dbd_db_login2 does not return FALSE, the Perl code does not get told about any error having occurred. The obvious immediate solution would be to change EOI to say x > 1, so SQL_SUCCESS_WITH_INFO is considered an error like it is in dbd_db_connect. However, this macro is used all over the place, so that might have unintended consequences. So perhaps only in dbd_db_login2 this with-info code should be checked for and return false and consequently the connect call would fail at the DBI level and the user would be made aware of the error. The other option would be not to clean up the handle at all, and continue regardless in case of SQL_SUCCESS_WITH_INFO, ignoring the diagnostic error about the single byte characters. However, the best solution would be to use the W methods instead: http://pic.dhe.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.apdv.cli.doc/doc/c0006840.html like the python ibm_db driver does, with translation from UCS-2 to UTF8. But that would be an extensive reworking of the code. This issue can be seen outside of Perl with the db2 command line tool. $ db2 connect to testbig5 Database Connection Information Database server = DB2/LINUXX8664 9.5.9 SQL authorization ID = XXXX Local database alias = XX010002 SQL0863W A successful connection was made, but only single byte characters should be used. SQLSTATE=01539 $ echo $? 2 $ db2 terminate DB20000I The TERMINATE command completed successfully. $ export DB2CODEPAGE=950 $ db2 connect to testbig5 Database Connection Information Database server = DB2/LINUXX8664 9.5.9 SQL authorization ID = XXXX Local database alias = XX010002 $ echo $? 0 but here the return codes show that there is a problem.