#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) {
}
free_cols(stmt, S TSRMLS_CC);
-
+ if (S->convbuf) {
+ efree(S->convbuf);
+ }
efree(S);
return 1;
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 assume 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 */
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) {
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));
}
}
} 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));
}
case PDO_PARAM_EVT_EXEC_POST:
P = param->driver_data;
if (P->outbuf) {
- switch (P->len) {
- case SQL_NULL_DATA:
- zval_dtor(param->parameter);
- ZVAL_NULL(param->parameter);
- 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';
+ if (P->outbuf) {
+ unsigned long ulen;
+ char *srcbuf;
+ unsigned long srclen;
+
+ switch (P->len) {
+ case SQL_NULL_DATA:
+ zval_dtor(param->parameter);
+ ZVAL_NULL(param->parameter);
+ break;
+ default:
+ convert_to_string(param->parameter);
+ 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;
static int odbc_stmt_fetch(pdo_stmt_t *stmt,
enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
{
- pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
RETCODE rc;
SQLSMALLINT odbcori;
+ pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
switch (ori) {
case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break;
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;
S->cols[colno].data = emalloc(colsize+1);
S->cols[colno].is_long = 0;
- rc = SQLBindCol(S->stmt, colno+1, SQL_C_CHAR, S->cols[colno].data,
- S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
+ 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) {
pdo_odbc_stmt_error("SQLBindCol");
{
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->is_long) {
* 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);
+ 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;
*ptr = buf;
*caller_frees = 1;
*len = used;
+ if (C->is_unicode) {
+ goto unicode_conv;
+ }
return 1;
}
/* it was stored perfectly */
*ptr = C->data;
*len = C->fetched_len;
+ if (C->is_unicode) {
+ goto unicode_conv;
+ }
return 1;
} else {
/* no 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)
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";
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";