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.