alexbrainman / odbc

odbc driver written in go
BSD 3-Clause "New" or "Revised" License
352 stars 140 forks source link

Bugfix for #62 #63

Open JoopClaireIT opened 8 years ago

JoopClaireIT commented 8 years ago

This fixes it for me locally. (First had the second part of the slice set to the size of the buffer, but of course that's completely senseless...)

Maybe check if this doesn't break anything.

alexbrainman commented 8 years ago

I would like to understand what the problem is that we're fixing here. Lets decide that first at issue #62.

Alex

haynesherway commented 5 years ago

please merge this! I was about to submit the same thing

haynesherway commented 5 years ago

It has to do with how certain drivers report nulls. Sometimes they don't send back the SQL_NULL_DATA, I've noticed specifically with null dates. This adds an extra check to make sure we don't get the out of bounds error.

alexbrainman commented 5 years ago

This adds an extra check to make sure we don't get the out of bounds error.

c.Buffer size is passed to SQLBindCol, which returns c.Len. So unless SQLBindCol is broken, it cannot happen.

Another possibility is that the column type you use here cannot be used with SQLBindCol. Then the change in this PR is not going to fix your problem. NewColumn function decides whether to use SQLBindCol or not. Maybe you can adjust it to make this work for you.

Alternatively, if this PR works for you, you can always make this change in your own copy of this code, and keep it for yourself.

Alex

haynesherway commented 5 years ago

I did make the change in my own version, and have not had the problem since. It is a very specific issue, only with accessing a IBM iSeries DB2 from a 64-bit system. It seems the driver does not respond with a SQL_NULL_DATA in 64-bit, but rather in 32-bit, and therefore IsNull() is false, even though it should be true.

I have found a little bit of information on the subject:

I believe this returns a NULL and so when DBD::ODBC binds the column as an SQL_C_LONG the driver sets the returned indicator to say SQL_NULL_DATA which is -1. However, as your driver looks like it thinks SQLLEN/SQLULEN are 4 byte quantities it writes 0xFFFFFFFF (-1 if a 4 byte integer) into a 8 byte quantity which now looks like 4294967295 and DBD::ODBC thinks the column has been truncated.

It is also mentioned here: https://bugs.php.net/bug.php?id=54007

I am not sure if this corresponds to a known issue with the IBM driver, mentioned here https://docs.oracle.com/en/database/oracle/oracle-database/18/odbcr/odbc-driver-release-notes.pdf "SQLBindParameter when used to bind a buffer as SQL_PARAM_INPUT_OUTPUT and having a PL/SQL procedure with IN OUT parameter and if the parameter is not changed in the procedure, then the driver will not return SQL_NULL_DATA in StrLen_or_IndPtr."

I know very little about the internals of the DB interactions, so I'm kind of digging at this blindly. Obviously, the correct solution would be for IBM to fix their driver, but since this has been brought up many times over the last few years and they have done nothing, and for me this is affecting production applications, at least this will be here for others looking for a solution in the future. If anyone else is experiencing the same issue, they can use the fork I created. I have also prefixed all the fields in my queries with COALESCE() to not allow for NULL values anywhere I can.