Morning,

I found an issue this week that exists in both odbc and pdo_odbc with
SQL Server.  The ODBC implemention of Windows returns 0 as the length
for for varchar(max) nad  nvarchar(max).  This makes the allocation of
the strings incorrect and you get back garbage pointers for the
contents.

This was a pretty easy fix for pdo_odbc, simply check if the colsize
is returned as 0 and the type is one of the varchar types, if so
always treat it as a column with "long" data.  This works perfectly
without breaking things.  Attached is a patch that works for both 5.3
and trunk, includes an additional test for the issue.

The odbc extension is turning out to be more of a pain, each function
is doing it's own fetching so it either has to be fixed for each
function, or change odbc to use a single function for all fetching,
which might break bc.

Thanks,
Elizabeth M Smith
Index: ext/pdo_odbc/odbc_stmt.c
===================================================================
--- ext/pdo_odbc/odbc_stmt.c    (revision 308942)
+++ ext/pdo_odbc/odbc_stmt.c    (working copy)
@@ -552,6 +552,24 @@
                        sizeof(S->cols[colno].colname)-1, &colnamelen,
                        &S->cols[colno].coltype, &colsize, NULL, NULL);
 
+       /* This fixes a known issue with SQL Server and (max) lengths,
+       may affect others as well.  If we are SQL_VARCHAR,
+       SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations)
+       and zero is returned from colsize then consider it long */
+       if (0 == colsize && 
+               (S->cols[colno].coltype == SQL_VARCHAR ||
+                S->cols[colno].coltype == SQL_LONGVARCHAR ||
+#ifdef SQL_WVARCHAR
+                S->cols[colno].coltype == SQL_WVARCHAR ||
+#endif
+#ifdef SQL_WLONGVARCHAR
+                S->cols[colno].coltype == SQL_WLONGVARCHAR ||
+#endif
+                S->cols[colno].coltype == SQL_VARBINARY ||
+                S->cols[colno].coltype == SQL_LONGVARBINARY)) {
+                        S->going_long = 1;
+       }
+
        if (rc != SQL_SUCCESS) {
                pdo_odbc_stmt_error("SQLDescribeCol");
                if (rc != SQL_SUCCESS_WITH_INFO) {
Index: ext/pdo_odbc/tests/max_columns.phpt
===================================================================
--- ext/pdo_odbc/tests/max_columns.phpt (revision 0)
+++ ext/pdo_odbc/tests/max_columns.phpt (revision 0)
@@ -0,0 +1,46 @@
+--TEST--
+PDO ODBC varying character with max/no length
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded('pdo_odbc')) print 'skip not loaded';
+?>
+--FILE--
+<?php
+require 'ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory('ext/pdo_odbc/tests/common.phpt');
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
+
+if (false === $db->exec('CREATE TABLE TEST (id INT NOT NULL PRIMARY KEY, data 
varchar(max))')) {
+       if (false === $db->exec('CREATE TABLE TEST (id INT NOT NULL PRIMARY 
KEY, data longtext)')) {
+               if (false === $db->exec('CREATE TABLE TEST (id INT NOT NULL 
PRIMARY KEY, data CLOB)')) {
+                       die("BORK: don't know how to create a long column 
here:\n" . implode(", ", $db->errorInfo()));
+               }
+       }
+}
+
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+$sizes = array(32, 64, 128, 253, 254, 255, 256, 257, 258, 512, 1024, 2048, 
3998, 3999, 4000);
+
+$db->beginTransaction();
+$insert = $db->prepare('INSERT INTO TEST VALUES (?, ?)');
+foreach ($sizes as $num) {
+       $insert->execute(array($num, str_repeat('i', $num)));
+}
+$insert = null;
+$db->commit();
+
+foreach ($db->query('SELECT id, data from TEST') as $row) {
+       $expect = str_repeat('i', $row[0]);
+       if (strcmp($expect, $row[1])) {
+               echo "Failed on size $row[id]:\n";
+               printf("Expected %d bytes, got %d\n", strlen($expect), 
strlen($row['data']));
+               echo bin2hex($expect) . "\n";
+               echo bin2hex($row['data']) . "\n";
+       }
+}
+
+echo "Finished\n";
+
+--EXPECT--
+Finished
-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to