Skip to content

Commit 97cfdcd

Browse files
committed
Merge branch 'PHP-7.4' into PHP-8.0
* PHP-7.4: Fix #80783: PDO ODBC truncates BLOB records at every 256th byte
2 parents 465cfc4 + bccca0b commit 97cfdcd

File tree

4 files changed

+81
-2
lines changed

4 files changed

+81
-2
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ PHP NEWS
1414
. Fixed bug #80861 (erronous array key overflow in 2D array with JIT).
1515
(Dmitry)
1616

17+
- PDO_ODBC:
18+
. Fixed bug #80783 (PDO ODBC truncates BLOB records at every 256th byte).
19+
(cmb)
20+
1721
01 Apr 2021, PHP 8.0.4
1822

1923
- Core:

ext/pdo_odbc/odbc_stmt.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong
650650

651651
/* if it is a column containing "long" data, perform late binding now */
652652
if (C->is_long) {
653+
SQLLEN orig_fetched_len = SQL_NULL_DATA;
653654
zend_ulong used = 0;
654655
char *buf;
655656
RETCODE rc;
@@ -660,6 +661,7 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong
660661

661662
rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
662663
256, &C->fetched_len);
664+
orig_fetched_len = C->fetched_len;
663665

664666
if (rc == SQL_SUCCESS) {
665667
/* all the data fit into our little buffer;
@@ -671,7 +673,8 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong
671673
/* this is a 'long column'
672674
673675
read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
674-
in order into the output buffer
676+
in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert
677+
more or less NUL bytes at the end; we cater to that later, if actual length information is available
675678
676679
this loop has to work whether or not SQLGetData() provides the total column length.
677680
calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
@@ -685,7 +688,14 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong
685688
do {
686689
C->fetched_len = 0;
687690
/* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
688-
rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, buf2, 256, &C->fetched_len);
691+
rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len);
692+
693+
/* adjust `used` in case we have length info from the driver */
694+
if (orig_fetched_len >= 0 && C->fetched_len >= 0) {
695+
SQLLEN fixed_used = orig_fetched_len - C->fetched_len;
696+
ZEND_ASSERT(fixed_used <= used + 1);
697+
used = fixed_used;
698+
}
689699

690700
/* resize output buffer and reassemble block */
691701
if (rc==SQL_SUCCESS_WITH_INFO) {

ext/pdo_odbc/tests/bug80783.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
Bug #80783 (PDO ODBC truncates BLOB records at every 256th byte)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('pdo_odbc')) die('skip pdo_odbc extension not available');
6+
require 'ext/pdo/tests/pdo_test.inc';
7+
PDOTest::skip();
8+
?>
9+
--FILE--
10+
<?php
11+
require 'ext/pdo/tests/pdo_test.inc';
12+
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
13+
$db->exec("CREATE TABLE bug80783 (name IMAGE)");
14+
15+
$string = str_repeat("0123456789", 50);
16+
$db->exec("INSERT INTO bug80783 VALUES('$string')");
17+
18+
$stmt = $db->prepare("SELECT name FROM bug80783");
19+
$stmt->bindColumn(1, $data, PDO::PARAM_LOB);
20+
$stmt->execute();
21+
$stmt->fetch(PDO::FETCH_BOUND);
22+
23+
var_dump($data === bin2hex($string));
24+
?>
25+
--CLEAN--
26+
<?php
27+
require 'ext/pdo/tests/pdo_test.inc';
28+
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
29+
$db->exec("DROP TABLE bug80783");
30+
?>
31+
--EXPECT--
32+
bool(true)

ext/pdo_odbc/tests/bug80783a.phpt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Bug #80783 (PDO ODBC truncates BLOB records at every 256th byte)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('pdo_odbc')) die('skip pdo_odbc extension not available');
6+
require 'ext/pdo/tests/pdo_test.inc';
7+
PDOTest::skip();
8+
?>
9+
--FILE--
10+
<?php
11+
require 'ext/pdo/tests/pdo_test.inc';
12+
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
13+
$db->exec("CREATE TABLE bug80783a (name NVARCHAR(MAX))");
14+
15+
$string = str_repeat("0123456789", 50);
16+
$db->exec("INSERT INTO bug80783a VALUES('$string')");
17+
18+
$stmt = $db->prepare("SELECT name FROM bug80783a");
19+
$stmt->setAttribute(PDO::ODBC_ATTR_ASSUME_UTF8, true);
20+
$stmt->bindColumn(1, $data, PDO::PARAM_STR);
21+
$stmt->execute();
22+
$stmt->fetch(PDO::FETCH_BOUND);
23+
24+
var_dump($data === $string);
25+
?>
26+
--CLEAN--
27+
<?php
28+
require 'ext/pdo/tests/pdo_test.inc';
29+
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
30+
$db->exec("DROP TABLE bug80783a");
31+
?>
32+
--EXPECT--
33+
bool(true)

0 commit comments

Comments
 (0)