ID:               41539
 User updated by:  frode at coretrek dot com
 Reported By:      frode at coretrek dot com
 Status:           Assigned
 Bug Type:         MSSQL related
 Operating System: Linux and win32
 PHP Version:      5.2.3
 Assigned To:      fmk
 New Comment:

Is there a chance to get a fix for this in before 5.2.5?


Previous Comments:
------------------------------------------------------------------------

[2007-08-13 08:19:34] frode at coretrek dot com

Is there a chance to get a fix for this in before 5.2.4?

------------------------------------------------------------------------

[2007-06-19 05:58:41] frode at coretrek dot com

I don't get it - the patch you posted is partly already in 5.2.3, i.e.
it's already there for php_mssql_get_column_content_with_type. But not
in php_mssql_get_column_content_without_type. But that's not the real
issue here:

As I mentioned in the original submission, bug 39213 fixed the problem
for most datatypes but doesn't help for the datatype NVARCHAR(MAX).
That's because FreeTDS does not return NULL for dbdata() on an
NVARCHAR(MAX). Please, take another look at my second comment to this
bug, where I posted an ugly work-around that requires the use of
FreeTDS' underlying libTDS, which seems to be the only API that can tell
the difference between a NULL and a "" in BLOBs (which is what the
NVARCHAR(MAX) datatype basically is); the difference is that the size is
-1 for NULL values, and 0 for "" values, but the dblib API converts -1
to 0 in dbdatlen().

Thanks again for looking at this bug a second time :)

------------------------------------------------------------------------

[2007-06-18 20:27:16] erudd at netfor dot com

Isn't this a dup of #39213.

------------------------------------------------------------------------

[2007-06-18 20:16:31] erudd at netfor dot com

Patch Developed w/ freetds maintainer to fix this issue

--- mssql/php_mssql.c.orig      2006-04-04 14:49:12.000000000 -0400
+++ mssql/php_mssql.c   2006-10-24 16:41:18.000000000 -0400
@@ -818,7 +818,7 @@
 
 static void php_mssql_get_column_content_with_type(mssql_link
*mssql_ptr,int offset,zval *result, int column_type  TSRMLS_DC)
 {
-       if (dbdatlen(mssql_ptr->link,offset) == 0) {
+       if (dbdatlen(mssql_ptr->link,offset) == 0 &&
dbdata(mssql_ptr->link,offset) == NULL) {
                ZVAL_NULL(result);
                return;
        }
@@ -941,7 +941,7 @@
 
 static void php_mssql_get_column_content_without_type(mssql_link
*mssql_ptr,int offset,zval *result, int column_type TSRMLS_DC)
 {
-       if (dbdatlen(mssql_ptr->link,offset) == 0) {
+       if (dbdatlen(mssql_ptr->link,offset) == 0 &&
dbdata(mssql_ptr->link,offset) == NULL) {
                ZVAL_NULL(result);
                return;
        }

------------------------------------------------------------------------

[2007-06-04 14:16:12] frode at coretrek dot com

So I had a go at hacking on the C source for the mssql extension, and I
think I have come up with some sort of solution.

First of all, it appears that the regular FreeTDS dblib api has no way
to distinguish "" from NULL for varchar(max). dbdatlen() returns 0
either way, and dbdata() returns a pointer to 0x0. 

Looking at the source code for the FreeTDS tsql application, it became
obvious that when result set columns contains a real NULL, the
"column_cur_size" is always less than 0. Relevant piece of code from
tsql.c:

  if (col->column_cur_size < 0) {
    if (print_rows)
      fprintf(stdout, "NULL\t");
    continue;
  }

Unfortunately, negative column_cur_size is masked in dblib. Relevant
piece of code from
dblib.c's implementation of dbdatlen():

  if (colinfo->column_cur_size < 0)
    ret = 0;
  else
    ret = colinfo->column_cur_size;

So it seems we need to access the low level TDS structures to tell the
difference. 
Playing around with gdb, it seems that column_cur_size is 0 for strings
and -1 for real NULLs.

So I came up with an ugly hack for php_mssql.c. It does produce the
expected output,
but it's been a long time since I did anything serious in C, so I don't
know if it is 
the correct way of doing things. 

Tested on Windows 2003 x86, compiled with Microsoft Visual C++ 2005
(Compiler version 
14.00.50727.42 for 80x86), FreeTDS 0.64, running against an instance of
SQL Server 2005,
and c:\freetds.conf contains:
[global]
  tds version = 7.0
  
  
I also added a "dbdata(..) == NULL" condition to the if()
in php_mssql_get_column_content_without_type(), just because that
function seems to be similar to
what was fixed in
http://cvs.php.net/viewcvs.cgi/php-src/ext/mssql/php_mssql.c?r1=1.152.2.13.2.2&r2=1.152.2.13.2.3&pathrev=PHP_5_2
but I don't know if it is necessary or even correct. 
  
Here is the diff against PHP 5.2.3. 


diff -u /devel2/x2www/src/php-5.2.3/ext/mssql/php_mssql.c
./php_mssql.c
--- /devel2/x2www/src/php-5.2.3/ext/mssql/php_mssql.c   2007-02-24
03:17:25.000000000 +0100
+++ ./php_mssql.c       2007-06-04 15:37:25.265625000 +0200
@@ -814,8 +814,21 @@
 static void php_mssql_get_column_content_with_type(mssql_link
*mssql_ptr,int offset,zval *result, int column_type  TSRMLS_DC)
 {
        if (dbdata(mssql_ptr->link,offset) == NULL &&
dbdatlen(mssql_ptr->link,offset) == 0) {
-               ZVAL_NULL(result);
-               return;
+#ifdef HAVE_FREETDS
+               /* double check that it is a real null, it could also
be a zero-length varchar(max) string */
+               TDSCOLUMN *colinfo;
+               TDSRESULTINFO *resinfo;
+
+               resinfo =
((DBPROCESS*)(mssql_ptr->link))->tds_socket->res_info;
+               colinfo = resinfo->columns[offset-1];
+               if (colinfo->column_cur_size < 0) {
+#endif
+                       ZVAL_NULL(result);
+                       return;
+#ifdef HAVE_FREETDS
+               }
+#endif
+
        }
 
        switch (column_type)
@@ -935,7 +948,7 @@
 
 static void php_mssql_get_column_content_without_type(mssql_link
*mssql_ptr,int offset,zval *result, int column_type TSRMLS_DC)
 {
-       if (dbdatlen(mssql_ptr->link,offset) == 0) {
+       if (dbdata(mssql_ptr->link,offset) == NULL &&
dbdatlen(mssql_ptr->link,offset) == 0) {
                ZVAL_NULL(result);
                return;
        }
diff -u /devel2/x2www/src/php-5.2.3/ext/mssql/php_mssql.h
./php_mssql.h
--- /devel2/x2www/src/php-5.2.3/ext/mssql/php_mssql.h   2007-01-01
10:36:03.000000000 +0100
+++ ./php_mssql.h       2007-06-04 15:37:07.343750000 +0200
@@ -31,8 +31,17 @@
 #define PHP_MSSQL_API
 #endif
 
+#if HAVE_FREETDS
+/* mega hack to get hold of TDSCOLUMN->column_cur_size to check for
NULL column values in VARCHAR(MAX) */
+#define _FREETDS_LIBRARY_SOURCE 1
+#include <tds.h>
+#include <sybfront.h>
+#include <sybdb.h>
+#include <dblib.h>
+#else
 #include <sqlfront.h>
 #include <sqldb.h>
+#endif
 
 typedef short TDS_SHORT;
 #ifdef HAVE_FREETDS




Finally, does anyone know if there's a chance that php_dblib.dll will
be distributed
with the regular php.net win32 binary release in the future?

------------------------------------------------------------------------

The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
    http://bugs.php.net/41539

-- 
Edit this bug report at http://bugs.php.net/?id=41539&edit=1

Reply via email to