summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWez Furlong <wez@php.net>2005-07-07 12:49:21 +0000
committerWez Furlong <wez@php.net>2005-07-07 12:49:21 +0000
commit93024bc26885653fa9b91da02cdd1060bf4a86ce (patch)
treea8c0b0a48b89957a3fd263dbc2e89f54d9310b7d
parent68caaadc6a9a2c4498146cb678e841e2fc696749 (diff)
downloadphp-git-93024bc26885653fa9b91da02cdd1060bf4a86ce.tar.gz
Fix handling of parameter binding.
We need to guess at parameter sizing in some cases (eg: MS Access) as the SQLDescribeParam() API is an optional feature. Tidy up error handling. Add workaround for a shutdown bug that I see with MS ODBC implementation. (working to determine the precise cause of this). PDO core test suite now passes all tests.
-rwxr-xr-xext/pdo_odbc/odbc_driver.c34
-rwxr-xr-xext/pdo_odbc/odbc_stmt.c132
-rwxr-xr-xext/pdo_odbc/pdo_odbc.c4
-rw-r--r--ext/pdo_odbc/php_pdo_odbc.h4
-rwxr-xr-xext/pdo_odbc/php_pdo_odbc_int.h3
5 files changed, 130 insertions, 47 deletions
diff --git a/ext/pdo_odbc/odbc_driver.c b/ext/pdo_odbc/odbc_driver.c
index f3caf3419e..4e183f055b 100755
--- a/ext/pdo_odbc/odbc_driver.c
+++ b/ext/pdo_odbc/odbc_driver.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2004 The PHP Group |
+ | Copyright (c) 1997-2005 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 |
@@ -85,7 +85,7 @@ void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement,
einfo->what = what;
strcpy(*pdo_err, einfo->last_state);
-
+/* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */
if (!dbh->methods) {
zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "SQLSTATE[%s] %s: %d %s",
*pdo_err, what, einfo->last_error, einfo->last_err_msg);
@@ -96,16 +96,18 @@ void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement,
static int odbc_handle_closer(pdo_dbh_t *dbh TSRMLS_DC)
{
pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
-
if (H->dbc != SQL_NULL_HANDLE) {
SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
+#ifndef PHP_WIN32 /* avoiding a bug I've found on my XP box */
SQLDisconnect(H->dbc);
+#endif
SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);
H->dbc = NULL;
}
SQLFreeHandle(SQL_HANDLE_ENV, H->env);
H->env = NULL;
pefree(H, dbh->is_persistent);
+ dbh->driver_data = NULL;
return 0;
}
@@ -174,7 +176,7 @@ static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, p
SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
return 0;
}
-
+
stmt->driver_data = S;
stmt->methods = &odbc_stmt_methods;
@@ -335,17 +337,15 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_D
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3");
- odbc_handle_closer(dbh TSRMLS_CC);
- return 0;
+ goto fail;
}
-#ifdef SQL_ATTR_CONNECTION_POOLING
+#if 0 /*&& def SQL_ATTR_CONNECTION_POOLING */
if (pdo_odbc_pool_on != SQL_CP_OFF) {
rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0);
if (rc != SQL_SUCCESS) {
pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH");
- odbc_handle_closer(dbh TSRMLS_CC);
- return 0;
+ goto fail;
}
}
#endif
@@ -353,16 +353,14 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_D
rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
pdo_odbc_drv_error("SQLAllocHandle (DBC)");
- odbc_handle_closer(dbh TSRMLS_CC);
- return 0;
+ goto fail;
}
rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_NTS);
if (rc != SQL_SUCCESS) {
pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT");
- odbc_handle_closer(dbh TSRMLS_CC);
- return 0;
+ goto fail;
}
/* set up the cursor library, if needed, or if configured explicitly */
@@ -370,8 +368,7 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_D
rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, 0);
if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {
pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS");
- odbc_handle_closer(dbh TSRMLS_CC);
- return 0;
+ goto fail;
}
@@ -399,8 +396,7 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_D
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect");
- odbc_handle_closer(dbh TSRMLS_CC);
- return 0;
+ goto fail;
}
/* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */
@@ -409,6 +405,10 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_D
dbh->alloc_own_columns = 1;
return 1;
+
+fail:
+ dbh->methods = &odbc_methods;
+ return 0;
}
/* }}} */
diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c
index daf8967d9e..63e2512dea 100755
--- a/ext/pdo_odbc/odbc_stmt.c
+++ b/ext/pdo_odbc/odbc_stmt.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2004 The PHP Group |
+ | Copyright (c) 1997-2005 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 |
@@ -140,7 +140,6 @@ static int odbc_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
SQLNumResultCols(S->stmt, &colcount);
stmt->column_count = (int)colcount;
-
S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
}
@@ -152,8 +151,8 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
{
pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
RETCODE rc;
- SWORD sqltype, ctype, scale, nullable;
- UDWORD precision;
+ SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0;
+ UDWORD precision = 0;
pdo_odbc_param *P;
/* we're only interested in parameters for prepared SQL right now */
@@ -176,13 +175,26 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
case PDO_PARAM_STMT:
return 0;
-
- case PDO_PARAM_STR:
+
default:
- convert_to_string(param->parameter);
+ break;
}
- SQLDescribeParam(S->stmt, param->paramno+1, &sqltype, &precision, &scale, &nullable);
+ rc = SQLDescribeParam(S->stmt, param->paramno+1, &sqltype, &precision, &scale, &nullable);
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ /* MS Access, for instance, doesn't support SQLDescribeParam,
+ * so we need to guess */
+ sqltype = PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB ?
+ SQL_LONGVARBINARY :
+ SQL_LONGVARCHAR;
+ precision = 4000;
+ scale = 5;
+ nullable = 1;
+
+ if (param->max_value_len > 0) {
+ precision = param->max_value_len;
+ }
+ }
if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {
ctype = SQL_C_BINARY;
} else {
@@ -193,6 +205,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
param->driver_data = P;
P->len = 0; /* is re-populated each EXEC_PRE */
+ P->outbuf = NULL;
if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {
P->paramtype = SQL_PARAM_INPUT_OUTPUT;
@@ -201,10 +214,15 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
} else {
P->paramtype = SQL_PARAM_OUTPUT;
}
- if (P->paramtype != SQL_PARAM_INPUT && PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_LOB && param->max_value_len > Z_STRLEN_P(param->parameter)) {
- Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), param->max_value_len + 1);
+
+ if (P->paramtype != SQL_PARAM_INPUT) {
+ 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 (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {
pdo_odbc_stmt_error("Can't bind a lob for output");
return 0;
@@ -212,11 +230,10 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
rc = SQLBindParameter(S->stmt, param->paramno+1,
P->paramtype, ctype, sqltype, precision, scale,
- P->paramtype == SQL_PARAM_INPUT &&
- PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB ?
- (SQLPOINTER)param :
- Z_STRVAL_P(param->parameter),
- param->max_value_len <= 0 ? 0 : param->max_value_len,
+ P->paramtype == SQL_PARAM_INPUT ?
+ (SQLPOINTER)param :
+ P->outbuf,
+ P->len,
&P->len
);
@@ -241,24 +258,74 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
}
if (0 == php_stream_stat(stm, &sb)) {
- P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);
+ if (P->outbuf) {
+ int len, amount;
+ char *ptr = P->outbuf;
+ char *end = P->outbuf + P->len;
+
+ P->len = 0;
+ do {
+ amount = end - ptr;
+ if (amount == 0) {
+ break;
+ }
+ if (amount > 8192)
+ amount = 8192;
+ len = php_stream_read(stm, ptr, amount);
+ if (len == 0) {
+ break;
+ }
+ ptr += len;
+ P->len += len;
+ } while (1);
+
+ } else {
+ P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);
+ }
} else {
- P->len = SQL_LEN_DATA_AT_EXEC(0);
+ if (P->outbuf) {
+ P->len = 0;
+ } else {
+ P->len = SQL_LEN_DATA_AT_EXEC(0);
+ }
}
} else {
convert_to_string(param->parameter);
- P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
+ if (P->outbuf) {
+ P->len = Z_STRLEN_P(param->parameter);
+ memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
+ } else {
+ P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
+ }
}
+ } else if (Z_TYPE_P(param->parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {
+ P->len = SQL_NULL_DATA;
} else {
- P->len = Z_STRLEN_P(param->parameter);
+ 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);
+ } else {
+ P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
+ }
}
return 1;
case PDO_PARAM_EVT_EXEC_POST:
- if (Z_TYPE_P(param->parameter) == IS_STRING) {
- P = param->driver_data;
- Z_STRLEN_P(param->parameter) = P->len;
- Z_STRVAL_P(param->parameter)[P->len] = '\0';
+ 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';
+ }
}
return 1;
}
@@ -287,10 +354,12 @@ static int odbc_stmt_fetch(pdo_stmt_t *stmt,
rc = SQLFetchScroll(S->stmt, odbcori, offset);
if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
+ pdo_odbc_stmt_error("SQLFetchScroll");
return 1;
}
if (rc == SQL_NO_DATA) {
+ pdo_odbc_stmt_error("SQLFetchScroll");
return 0;
}
@@ -312,6 +381,11 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
sizeof(S->cols[colno].colname)-1, &colnamelen,
&S->cols[colno].coltype, &colsize, NULL, NULL);
+ if (rc != SQL_SUCCESS) {
+ pdo_odbc_stmt_error("SQLBindCol");
+ return 0;
+ }
+
col->maxlen = S->cols[colno].datalen = colsize;
col->namelen = colnamelen;
col->name = estrdup(S->cols[colno].colname);
@@ -324,7 +398,12 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
/* tell ODBC to put it straight into our buffer */
rc = SQLBindCol(S->stmt, colno+1, 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");
+ return 0;
+ }
+
return 1;
}
@@ -412,6 +491,9 @@ static int odbc_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
free_cols(stmt, S TSRMLS_CC);
+ /* NOTE: can't guarantee that output or input/output parameters
+ * are set until this fella returns SQL_NO_DATA, according to
+ * MSDN ODBC docs */
rc = SQLMoreResults(S->stmt);
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
diff --git a/ext/pdo_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c
index 5a7709e7c8..34b9bc90ae 100755
--- a/ext/pdo_odbc/pdo_odbc.c
+++ b/ext/pdo_odbc/pdo_odbc.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2004 The PHP Group |
+ | Copyright (c) 1997-2005 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 |
@@ -80,7 +80,7 @@ PHP_MINIT_FUNCTION(pdo_odbc)
}
#ifdef SQL_ATTR_CONNECTION_POOLING
- /* ugh, we don't really .ini stuff in PDO, but since ODBC connection
+ /* ugh, we don't really like .ini stuff in PDO, but since ODBC connection
* pooling is process wide, we can't set it from within the scope of a
* request without affecting others, which goes against our isolated request
* policy. So, we use cfg_get_string here to check it this once.
diff --git a/ext/pdo_odbc/php_pdo_odbc.h b/ext/pdo_odbc/php_pdo_odbc.h
index 1c2dcf4197..8d290b38b4 100644
--- a/ext/pdo_odbc/php_pdo_odbc.h
+++ b/ext/pdo_odbc/php_pdo_odbc.h
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2004 The PHP Group |
+ | Copyright (c) 1997-2005 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 |
@@ -12,7 +12,7 @@
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
- | Author: |
+ | Author: Wez Furlong <wez@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/pdo_odbc/php_pdo_odbc_int.h b/ext/pdo_odbc/php_pdo_odbc_int.h
index 4fb493dcfd..72a65ede29 100755
--- a/ext/pdo_odbc/php_pdo_odbc_int.h
+++ b/ext/pdo_odbc/php_pdo_odbc_int.h
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2004 The PHP Group |
+ | Copyright (c) 1997-2005 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 |
@@ -149,6 +149,7 @@ typedef struct {
typedef struct {
SQLINTEGER len;
SQLSMALLINT paramtype;
+ char *outbuf;
} pdo_odbc_param;
extern pdo_driver_t pdo_odbc_driver;