Derick,
Your statement is not accurate:
http://cvs.php.net/viewvc.cgi/CVSROOT/avail?r1=1.1200&r2=1.1201
This should not be a blocker to anyone that wants to take this up.
I've been too busy to help Travis (I'm really sorry about that
Travis), and I'm happy to share the pending fix with anyone that wants
to look at this.
The reason that the code hasn't gone into CVS is that Travis had a
knack for picking a tree freeze for bringing up his issues, and then I
would subsequently become too busy to get the code sync'd up after the
freeze.
Anyhow, if anyone does want to take Travis up on his offer, you'll
need the attached patch.Index: odbc_driver.c
===================================================================
RCS file: /repository/php-src/ext/pdo_odbc/odbc_driver.c,v
retrieving revision 1.27.2.2
diff -u -p -r1.27.2.2 odbc_driver.c
--- odbc_driver.c 14 Dec 2005 04:56:22 -0000 1.27.2.2
+++ odbc_driver.c 30 Mar 2006 19:06:44 -0000
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2005 The PHP Group |
+ | Copyright (c) 1997-2006 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -153,6 +153,7 @@ static int odbc_handle_preparer(pdo_dbh_
int nsql_len = 0;
S->H = H;
+ S->assume_utf8 = H->assume_utf8;
/* before we prepare, we need to peek at the query; if it uses named
parameters,
* we want PDO to rewrite them for us */
@@ -319,8 +320,24 @@ static int odbc_handle_rollback(pdo_dbh_
return 1;
}
+static int odbc_handle_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
+{
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
+ switch (attr) {
+ case PDO_ODBC_ATTR_ASSUME_UTF8:
+ H->assume_utf8 = zval_is_true(val);
+ return 1;
+ default:
+ strcpy(H->einfo.last_err_msg, "Unknown Attribute");
+ H->einfo.what = "setAttribute";
+ strcpy(H->einfo.last_state, "IM0001");
+ return -1;
+ }
+}
+
static int odbc_handle_get_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
{
+ pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
switch (attr) {
case PDO_ATTR_CLIENT_VERSION:
ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE, 1);
@@ -333,6 +350,9 @@ static int odbc_handle_get_attr(pdo_dbh_
case PDO_ATTR_CONNECTION_STATUS:
break;
+ case PDO_ODBC_ATTR_ASSUME_UTF8:
+ ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0);
+ return 1;
}
return 0;
}
@@ -345,7 +365,7 @@ static struct pdo_dbh_methods odbc_metho
odbc_handle_begin,
odbc_handle_commit,
odbc_handle_rollback,
- NULL, /* set attr */
+ odbc_handle_set_attr,
NULL, /* last id */
pdo_odbc_fetch_error_func,
odbc_handle_get_attr, /* get attr */
Index: odbc_stmt.c
===================================================================
RCS file: /repository/php-src/ext/pdo_odbc/odbc_stmt.c,v
retrieving revision 1.26.2.2
diff -u -p -r1.26.2.2 odbc_stmt.c
--- odbc_stmt.c 27 Mar 2006 21:04:12 -0000 1.26.2.2
+++ odbc_stmt.c 30 Mar 2006 19:06:44 -0000
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2005 The PHP Group |
+ | Copyright (c) 1997-2006 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -30,6 +30,99 @@
#include "php_pdo_odbc.h"
#include "php_pdo_odbc_int.h"
+enum pdo_odbc_conv_result {
+ PDO_ODBC_CONV_NOT_REQUIRED,
+ PDO_ODBC_CONV_OK,
+ PDO_ODBC_CONV_FAIL
+};
+
+static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype)
+{
+ if (!S->assume_utf8) return 0;
+ switch (sqltype) {
+#ifdef SQL_WCHAR
+ case SQL_WCHAR:
+ return 1;
+#endif
+#ifdef SQL_WLONGVARCHAR
+ case SQL_WLONGVARCHAR:
+ return 1;
+#endif
+#ifdef SQL_WVARCHAR
+ case SQL_WVARCHAR:
+ return 1;
+#endif
+ default:
+ return 0;
+ }
+}
+
+static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char
*buf,
+ unsigned long buflen, unsigned long *outlen)
+{
+#ifdef PHP_WIN32
+ if (is_unicode && buflen) {
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+ DWORD ret;
+
+ ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0);
+ if (ret == 0) {
+ //printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__,
GetLastError(), buflen, buflen, buf);
+ return PDO_ODBC_CONV_FAIL;
+ }
+
+ ret *= sizeof(WCHAR);
+
+ if (S->convbufsize <= ret) {
+ S->convbufsize = ret + sizeof(WCHAR);
+ S->convbuf = erealloc(S->convbuf, S->convbufsize);
+ }
+
+ ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen,
(LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR));
+ if (ret == 0) {
+ //printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__,
GetLastError(), buflen, buflen, buf);
+ return PDO_ODBC_CONV_FAIL;
+ }
+
+ ret *= sizeof(WCHAR);
+ *outlen = ret;
+ return PDO_ODBC_CONV_OK;
+ }
+#endif
+ return PDO_ODBC_CONV_NOT_REQUIRED;
+}
+
+static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, const char
*buf,
+ unsigned long buflen, unsigned long *outlen)
+{
+#ifdef PHP_WIN32
+ if (is_unicode && buflen) {
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
+ DWORD ret;
+
+ ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf,
buflen/sizeof(WCHAR), NULL, 0, NULL, NULL);
+ if (ret == 0) {
+ return PDO_ODBC_CONV_FAIL;
+ }
+
+ if (S->convbufsize <= ret) {
+ S->convbufsize = ret + 1;
+ S->convbuf = erealloc(S->convbuf, S->convbufsize);
+ }
+
+ ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf,
buflen/sizeof(WCHAR), S->convbuf, S->convbufsize, NULL, NULL);
+ if (ret == 0) {
+ return PDO_ODBC_CONV_FAIL;
+ }
+
+ *outlen = ret;
+ S->convbuf[*outlen] = '\0';
+ return PDO_ODBC_CONV_OK;
+ }
+#endif
+ return PDO_ODBC_CONV_NOT_REQUIRED;
+}
+
static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S TSRMLS_DC)
{
if (S->cols) {
@@ -58,7 +151,9 @@ static int odbc_stmt_dtor(pdo_stmt_t *st
}
free_cols(stmt, S TSRMLS_CC);
-
+ if (S->convbuf) {
+ efree(S->convbuf);
+ }
efree(S);
return 1;
@@ -79,18 +174,45 @@ static int odbc_stmt_execute(pdo_stmt_t
while (rc == SQL_NEED_DATA) {
struct pdo_bound_param_data *param;
+
rc = SQLParamData(S->stmt, (SQLPOINTER*)¶m);
if (rc == SQL_NEED_DATA) {
php_stream *stm;
int len;
-
+ pdo_odbc_param *P;
+
+ P = (pdo_odbc_param*)param->driver_data;
if (Z_TYPE_P(param->parameter) != IS_RESOURCE) {
/* they passed in a string */
+ unsigned long ulen;
convert_to_string(param->parameter);
- SQLPutData(S->stmt,
Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
+
+ switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
+
Z_STRVAL_P(param->parameter),
+
Z_STRLEN_P(param->parameter),
+ &ulen)) {
+ case PDO_ODBC_CONV_NOT_REQUIRED:
+ SQLPutData(S->stmt,
Z_STRVAL_P(param->parameter),
+
Z_STRLEN_P(param->parameter));
+ break;
+ case PDO_ODBC_CONV_OK:
+ SQLPutData(S->stmt, S->convbuf,
ulen);
+ break;
+ case PDO_ODBC_CONV_FAIL:
+ pdo_odbc_stmt_error("error
converting input string");
+ SQLCloseCursor(S->stmt);
+ if (buf) {
+ efree(buf);
+ }
+ return 0;
+ }
+
continue;
}
+ /* we assumes that LOBs are binary and don't need
charset
+ * conversion */
+
php_stream_from_zval_no_verify(stm, ¶m->parameter);
if (!stm) {
/* shouldn't happen either */
@@ -213,6 +335,12 @@ static int odbc_stmt_param_hook(pdo_stmt
P->len = 0; /* is re-populated each EXEC_PRE */
P->outbuf = NULL;
+ P->is_unicode = pdo_odbc_sqltype_is_unicode(S,
sqltype);
+ if (P->is_unicode) {
+ /* avoid driver auto-translation: we'll
do it ourselves */
+ ctype = SQL_C_BINARY;
+ }
+
if ((param->param_type &
PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {
P->paramtype = SQL_PARAM_INPUT_OUTPUT;
} else if (param->max_value_len <= 0) {
@@ -225,7 +353,10 @@ static int odbc_stmt_param_hook(pdo_stmt
if (PDO_PARAM_TYPE(param->param_type)
!= PDO_PARAM_NULL) {
/* need an explicit buffer to
hold result */
P->len = param->max_value_len >
0 ? param->max_value_len : precision;
- P->outbuf = emalloc(P->len + 1);
+ if (P->is_unicode) {
+ P->len *= 2;
+ }
+ P->outbuf = emalloc(P->len +
(P->is_unicode ? 2:1));
}
}
@@ -309,8 +440,21 @@ static int odbc_stmt_param_hook(pdo_stmt
} else {
convert_to_string(param->parameter);
if (P->outbuf) {
- P->len =
Z_STRLEN_P(param->parameter);
- memcpy(P->outbuf,
Z_STRVAL_P(param->parameter), P->len);
+ unsigned long ulen;
+ switch
(pdo_odbc_utf82ucs2(stmt, P->is_unicode,
+
Z_STRVAL_P(param->parameter),
+
Z_STRLEN_P(param->parameter),
+ &ulen)) {
+ case PDO_ODBC_CONV_FAIL:
+ case
PDO_ODBC_CONV_NOT_REQUIRED:
+ P->len =
Z_STRLEN_P(param->parameter);
+
memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
+ break;
+ case PDO_ODBC_CONV_OK:
+ P->len = ulen;
+
memcpy(P->outbuf, S->convbuf, P->len);
+ break;
+ }
} else {
P->len =
SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
}
@@ -320,6 +464,10 @@ static int odbc_stmt_param_hook(pdo_stmt
case PDO_PARAM_EVT_EXEC_POST:
P = param->driver_data;
if (P->outbuf) {
+ unsigned long ulen;
+ char *srcbuf;
+ unsigned long srclen;
+
switch (P->len) {
case SQL_NULL_DATA:
zval_dtor(param->parameter);
@@ -327,10 +475,23 @@ static int odbc_stmt_param_hook(pdo_stmt
break;
default:
convert_to_string(param->parameter);
-
Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), P->len+1);
-
memcpy(Z_STRVAL_P(param->parameter), P->outbuf, P->len);
-
Z_STRLEN_P(param->parameter) = P->len;
-
Z_STRVAL_P(param->parameter)[P->len] = '\0';
+ switch
(pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) {
+ case
PDO_ODBC_CONV_FAIL:
+ /*
something fishy, but allow it to come back as binary */
+ case
PDO_ODBC_CONV_NOT_REQUIRED:
+ srcbuf
= P->outbuf;
+ srclen
= P->len;
+ break;
+ case
PDO_ODBC_CONV_OK:
+ srcbuf
= S->convbuf;
+ srclen
= ulen;
+ break;
+ }
+
+
Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), srclen+1);
+
memcpy(Z_STRVAL_P(param->parameter), srcbuf, srclen);
+
Z_STRLEN_P(param->parameter) = srclen;
+
Z_STRVAL_P(param->parameter)[srclen] = '\0';
}
}
return 1;
@@ -395,6 +556,7 @@ static int odbc_stmt_describe(pdo_stmt_t
col->maxlen = S->cols[colno].datalen = colsize;
col->namelen = colnamelen;
col->name = estrdup(S->cols[colno].colname);
+ S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S,
S->cols[colno].coltype);
/* returning data as a string */
col->param_type = PDO_PARAM_STR;
@@ -405,7 +567,9 @@ static int odbc_stmt_describe(pdo_stmt_t
if (colsize < 256 && !S->going_long) {
S->cols[colno].data = emalloc(colsize+1);
- rc = SQLBindCol(S->stmt, colno+1, SQL_C_CHAR,
S->cols[colno].data,
+ rc = SQLBindCol(S->stmt, colno+1,
+ S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
+ S->cols[colno].data,
S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
if (rc != SQL_SUCCESS) {
@@ -426,6 +590,7 @@ static int odbc_stmt_get_col(pdo_stmt_t
{
pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
pdo_odbc_column *C = &S->cols[colno];
+ unsigned long ulen;
/* if it is a column containing "long" data, perform late binding now */
if (C->datalen > 255) {
@@ -438,9 +603,8 @@ static int odbc_stmt_get_col(pdo_stmt_t
* 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,
+ rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY
: 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 */
@@ -491,6 +655,9 @@ static int odbc_stmt_get_col(pdo_stmt_t
*ptr = buf;
*caller_frees = 1;
*len = used;
+ if (C->is_unicode) {
+ goto unicode_conv;
+ }
return 1;
}
@@ -511,6 +678,9 @@ in_data:
/* it was stored perfectly */
*ptr = C->data;
*len = C->fetched_len;
+ if (C->is_unicode) {
+ goto unicode_conv;
+ }
return 1;
} else {
/* no data? */
@@ -518,6 +688,26 @@ in_data:
*len = 0;
return 1;
}
+
+unicode_conv:
+ switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) {
+ case PDO_ODBC_CONV_FAIL:
+ /* oh well. They can have the binary version of it */
+ case PDO_ODBC_CONV_NOT_REQUIRED:
+ /* shouldn't happen... */
+ return 1;
+
+ case PDO_ODBC_CONV_OK:
+ if (*caller_frees) {
+ efree(*ptr);
+ }
+ *ptr = emalloc(ulen + 1);
+ *len = ulen;
+ memcpy(*ptr, S->convbuf, ulen+1);
+ *caller_frees = 1;
+ return 1;
+ }
+ return 1;
}
static int odbc_stmt_set_param(pdo_stmt_t *stmt, long attr, zval *val
TSRMLS_DC)
@@ -536,6 +726,10 @@ static int odbc_stmt_set_param(pdo_stmt_
pdo_odbc_stmt_error("SQLSetCursorName");
return 0;
+ case PDO_ODBC_ATTR_ASSUME_UTF8:
+ S->assume_utf8 = zval_is_true(val);
+ return 0;
+
default:
strcpy(S->einfo.last_err_msg, "Unknown Attribute");
S->einfo.what = "setAttribute";
@@ -564,6 +758,10 @@ static int odbc_stmt_get_attr(pdo_stmt_t
return 0;
}
+ case PDO_ODBC_ATTR_ASSUME_UTF8:
+ ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
+ return 0;
+
default:
strcpy(S->einfo.last_err_msg, "Unknown Attribute");
S->einfo.what = "getAttribute";
Index: pdo_odbc.c
===================================================================
RCS file: /repository/php-src/ext/pdo_odbc/pdo_odbc.c,v
retrieving revision 1.14.2.7
diff -u -p -r1.14.2.7 pdo_odbc.c
--- pdo_odbc.c 4 Dec 2005 22:34:26 -0000 1.14.2.7
+++ pdo_odbc.c 30 Mar 2006 19:06:44 -0000
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2005 The PHP Group |
+ | Copyright (c) 1997-2006 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -134,6 +134,7 @@ PHP_MINIT_FUNCTION(pdo_odbc)
#endif
REGISTER_PDO_CLASS_CONST_LONG("ODBC_ATTR_USE_CURSOR_LIBRARY",
PDO_ODBC_ATTR_USE_CURSOR_LIBRARY);
+ REGISTER_PDO_CLASS_CONST_LONG("ODBC_ATTR_ASSUME_UTF8",
PDO_ODBC_ATTR_ASSUME_UTF8);
REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_IF_NEEDED",
SQL_CUR_USE_IF_NEEDED);
REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_DRIVER",
SQL_CUR_USE_DRIVER);
REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_ODBC", SQL_CUR_USE_ODBC);
Index: php_pdo_odbc.h
===================================================================
RCS file: /repository/php-src/ext/pdo_odbc/php_pdo_odbc.h,v
retrieving revision 1.2
diff -u -p -r1.2 php_pdo_odbc.h
--- php_pdo_odbc.h 7 Jul 2005 12:49:21 -0000 1.2
+++ php_pdo_odbc.h 30 Mar 2006 19:06:44 -0000
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2005 The PHP Group |
+ | Copyright (c) 1997-2006 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
Index: php_pdo_odbc_int.h
===================================================================
RCS file: /repository/php-src/ext/pdo_odbc/php_pdo_odbc_int.h,v
retrieving revision 1.9.2.1
diff -u -p -r1.9.2.1 php_pdo_odbc_int.h
--- php_pdo_odbc_int.h 27 Mar 2006 21:04:12 -0000 1.9.2.1
+++ php_pdo_odbc_int.h 30 Mar 2006 19:06:44 -0000
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2005 The PHP Group |
+ | Copyright (c) 1997-2006 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -129,6 +129,8 @@ typedef struct {
PDO_ODBC_HENV env;
PDO_ODBC_HDBC dbc;
pdo_odbc_errinfo einfo;
+ unsigned assume_utf8:1;
+ unsigned _spare:31;
} pdo_odbc_db_handle;
typedef struct {
@@ -136,6 +138,8 @@ typedef struct {
unsigned long datalen;
long fetched_len;
SWORD coltype;
+ unsigned is_unicode:1;
+ unsigned _spare:31;
char colname[128];
} pdo_odbc_column;
@@ -144,14 +148,19 @@ typedef struct {
pdo_odbc_column *cols;
pdo_odbc_db_handle *H;
pdo_odbc_errinfo einfo;
+ char *convbuf;
+ unsigned long convbufsize;
unsigned going_long:1;
- unsigned _spare:31;
+ unsigned assume_utf8:1;
+ unsigned _spare:30;
} pdo_odbc_stmt;
typedef struct {
SQLINTEGER len;
SQLSMALLINT paramtype;
char *outbuf;
+ unsigned is_unicode:1;
+ unsigned _spare:31;
} pdo_odbc_param;
extern pdo_driver_t pdo_odbc_driver;
@@ -172,6 +181,7 @@ extern SQLUINTEGER pdo_odbc_pool_mode;
enum {
PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC,
+ PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF when
feeding data to unicode columns */
};
/*
--Wez.
PS: More on PDO soon; it has not been forgotten despite sickness, work
and so forth.
On Dec 18, 2007, at 10:53 AM, Derick Rethans wrote:
On Tue, 18 Dec 2007, Travis Raybold wrote:
I've been trying to find someone who could help me with a fix for a
few bugs
in PDO_ODBC, and had no luck so far. I am more than happy to
compensate
someone for the time spent fixing these bugs.
Unfortunately it is not that easy as it seems. For some reason the
pecl/pdo_odbc and php-src/ext/pdo_odb directories are locked out for
the "normal" PHP team and thus we can't commit to it. The comment in
the
rights file says:
# DB2, SDO, IDS have tighter restrictions, so that IBM are not overly
# legally encumbered. If you have contributions for these two
extensions,
# please contact the lead developers. This is a temporary measure
until
# we can put in place a more general process.
It has been in this temporary situation since June 2005 :-/
regards,
Derick
--
Derick Rethans
http://derickrethans.nl | http://ezcomponents.org | http://xdebug.org
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php