Skip to content

Commit a76b7ba

Browse files
committed
Add to SQL_BINARY and PDO_PARAM_BINARY in PDO_ODBC
...including a test. I'm not sure if the semantics are right here though. This does binary data if the DB returns it, instead of listening to the PDO param type. This does mean that binary data works like expected, but the binary bound to SQL_C_CHAR converting behaviour in some drivers[1] may be desired and specified instead... [1]: At least Db2i and SQL Server's drivers, when I tested them.
1 parent 3ea731d commit a76b7ba

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

ext/pdo_odbc/odbc_stmt.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ enum pdo_odbc_conv_result {
3232
PDO_ODBC_CONV_FAIL
3333
};
3434

35+
/*
36+
* Used for determing if we should use SQL_C_BINARY on binary columns
37+
* XXX: Callers use what ODBC returns, rather that what user told PDO,
38+
* need to propagate
39+
*/
40+
static bool php_odbc_sqltype_is_binary(SWORD sqltype) {
41+
if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {
42+
return true;
43+
}
44+
return false;
45+
}
46+
3547
static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype)
3648
{
3749
if (!S->assume_utf8) return 0;
@@ -621,7 +633,7 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno)
621633
S->cols[colno].is_long = 0;
622634

623635
rc = SQLBindCol(S->stmt, colno+1,
624-
S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
636+
(php_odbc_sqltype_is_binary(S->cols[colno].coltype) || S->cols[colno].is_unicode) ? SQL_C_BINARY : SQL_C_CHAR,
625637
S->cols[colno].data,
626638
S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
627639

@@ -642,8 +654,15 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno)
642654

643655
static int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
644656
{
657+
pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
658+
pdo_odbc_column *C = &S->cols[colno];
659+
645660
array_init(return_value);
646-
add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
661+
if (php_odbc_sqltype_is_binary(C->coltype)) {
662+
add_assoc_long(return_value, "pdo_type", PDO_PARAM_BINARY);
663+
} else {
664+
add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
665+
}
647666
return 1;
648667
}
649668

@@ -652,6 +671,7 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo
652671
pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
653672
pdo_odbc_column *C = &S->cols[colno];
654673

674+
SQLSMALLINT c_type = (php_odbc_sqltype_is_binary(C->coltype) || C->is_unicode) ? SQL_C_BINARY : SQL_C_CHAR;
655675
/* if it is a column containing "long" data, perform late binding now */
656676
if (C->is_long) {
657677
SQLLEN orig_fetched_len = SQL_NULL_DATA;
@@ -661,7 +681,7 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo
661681
* of 256 bytes; if there is more to be had, we then allocate
662682
* bigger buffer for the caller to free */
663683

664-
rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
684+
rc = SQLGetData(S->stmt, colno+1, c_type, C->data,
665685
256, &C->fetched_len);
666686
orig_fetched_len = C->fetched_len;
667687

@@ -688,7 +708,7 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo
688708
do {
689709
C->fetched_len = 0;
690710
/* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
691-
rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len);
711+
rc = SQLGetData(S->stmt, colno+1, c_type, buf2, 256, &C->fetched_len);
692712

693713
/* adjust `used` in case we have length info from the driver */
694714
if (orig_fetched_len >= 0 && C->fetched_len >= 0) {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
PDO_odbc: Test PARAM_BINARY
3+
--EXTENSIONS--
4+
pdo_odbc
5+
--FILE--
6+
<?php
7+
8+
// This is a one pixel PNG of rgba(0, 0, 0, 255)
9+
$binary = base64_decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2NgYGD4DwABBAEAwS2OUAAAAABJRU5ErkJggg==");
10+
11+
require 'ext/pdo/tests/pdo_test.inc';
12+
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
13+
// XXX: Is this the right type to use?
14+
$db->exec("CREATE TABLE test_binary(field VARBINARY(256))");
15+
16+
$stmt = $db->prepare("INSERT INTO test_binary(field) VALUES (?)");
17+
$stmt->bindParam(1, $binary, PDO::PARAM_BINARY);
18+
$result = $stmt->execute();
19+
var_dump($result);
20+
21+
$stmt = $db->prepare("SELECT field FROM test_binary");
22+
$result = $stmt->execute();
23+
$binary = "";
24+
$stmt->bindColumn(1, $binary, PDO::PARAM_BINARY);
25+
$result = $stmt->execute();
26+
$result = $stmt->fetch();
27+
28+
var_dump(base64_encode($binary));
29+
var_dump($stmt->getColumnMeta(0)["pdo_type"]);
30+
?>
31+
--CLEAN--
32+
<?php
33+
require 'ext/pdo/tests/pdo_test.inc';
34+
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
35+
$db->exec("DROP TABLE test_binary");
36+
?>
37+
--EXPECT--
38+
bool(true)
39+
string(96) "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2NgYGD4DwABBAEAwS2OUAAAAABJRU5ErkJggg=="
40+
int(6)

0 commit comments

Comments
 (0)