mirror of
https://github.com/php/php-src.git
synced 2024-11-25 02:44:58 +08:00
Closes #33533 by implementing proper dynamic fetching of long text columns.
This commit is contained in:
parent
7aca138456
commit
43c1a1b73c
@ -386,22 +386,18 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* enforce a practical limitation.
|
||||
* TODO: make this work more nicely */
|
||||
if (colsize > 65535) {
|
||||
colsize = 65535;
|
||||
}
|
||||
|
||||
col->maxlen = S->cols[colno].datalen = colsize;
|
||||
col->namelen = colnamelen;
|
||||
col->name = estrdup(S->cols[colno].colname);
|
||||
|
||||
S->cols[colno].data = emalloc(colsize+1);
|
||||
|
||||
/* returning data as a string */
|
||||
col->param_type = PDO_PARAM_STR;
|
||||
|
||||
/* tell ODBC to put it straight into our buffer */
|
||||
/* tell ODBC to put it straight into our buffer, but only if it
|
||||
* isn't "long" data */
|
||||
if (colsize < 256) {
|
||||
S->cols[colno].data = emalloc(colsize+1);
|
||||
|
||||
rc = SQLBindCol(S->stmt, colno+1, SQL_C_CHAR, S->cols[colno].data,
|
||||
S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
|
||||
|
||||
@ -409,6 +405,11 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
|
||||
pdo_odbc_stmt_error("SQLBindCol");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* allocate a smaller buffer to keep around for smaller
|
||||
* "long" columns */
|
||||
S->cols[colno].data = emalloc(256);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -418,6 +419,80 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned l
|
||||
pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
|
||||
pdo_odbc_column *C = &S->cols[colno];
|
||||
|
||||
/* if it is a column containing "long" data, perform late binding now */
|
||||
if (C->datalen > 255) {
|
||||
unsigned long alloced = 4096;
|
||||
unsigned long used = 0;
|
||||
char *buf;
|
||||
RETCODE rc;
|
||||
|
||||
/* fetch it into C->data, which is allocated with a length
|
||||
* of 256 bytes; if there is more to be had, we then allocate
|
||||
* bigger buffer for the caller to free */
|
||||
|
||||
rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, C->data,
|
||||
256, &C->fetched_len);
|
||||
|
||||
if (rc == SQL_SUCCESS) {
|
||||
/* all the data fit into our little buffer;
|
||||
* jump down to the generic bound data case */
|
||||
goto in_data;
|
||||
}
|
||||
|
||||
if (rc == SQL_SUCCESS_WITH_INFO) {
|
||||
/* promote up to a bigger buffer */
|
||||
|
||||
if (C->fetched_len != SQL_NO_TOTAL) {
|
||||
/* use size suggested by the driver, if it knows it */
|
||||
alloced = C->fetched_len + 1;
|
||||
}
|
||||
|
||||
buf = emalloc(alloced);
|
||||
memcpy(buf, C->data, 256);
|
||||
used = 255; /* not 256; the driver NUL terminated the buffer */
|
||||
|
||||
do {
|
||||
C->fetched_len = 0;
|
||||
rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR,
|
||||
buf + used, alloced - used,
|
||||
&C->fetched_len);
|
||||
|
||||
if (rc == SQL_NO_DATA) {
|
||||
/* we got the lot */
|
||||
break;
|
||||
}
|
||||
|
||||
if (C->fetched_len == SQL_NO_TOTAL) {
|
||||
used += alloced - used;
|
||||
} else {
|
||||
used += C->fetched_len;
|
||||
}
|
||||
|
||||
/* we need to fetch another chunk; resize the
|
||||
* buffer */
|
||||
alloced *= 2;
|
||||
buf = erealloc(buf, alloced);
|
||||
} while (1);
|
||||
|
||||
/* size down */
|
||||
if (used < alloced - 1024) {
|
||||
alloced = used+1;
|
||||
buf = erealloc(buf, used+1);
|
||||
}
|
||||
buf[used] = '\0';
|
||||
*ptr = buf;
|
||||
*caller_frees = 1;
|
||||
*len = used;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* something went caca */
|
||||
*ptr = NULL;
|
||||
*len = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
in_data:
|
||||
/* check the indicator to ensure that the data is intact */
|
||||
if (C->fetched_len == SQL_NULL_DATA) {
|
||||
/* A NULL value */
|
||||
|
38
ext/pdo_odbc/tests/long_columns.phpt
Normal file
38
ext/pdo_odbc/tests/long_columns.phpt
Normal file
@ -0,0 +1,38 @@
|
||||
--TEST--
|
||||
PDO ODBC "long" columns
|
||||
--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->exec('CREATE TABLE TEST (id INT NOT NULL PRIMARY KEY, data longtext)');
|
||||
|
||||
$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)));
|
||||
}
|
||||
$db->Commit();
|
||||
|
||||
$insert = null;
|
||||
|
||||
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
|
Loading…
Reference in New Issue
Block a user