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