From: Wez Furlong Date: Thu, 7 Jul 2005 12:49:21 +0000 (+0000) Subject: Fix handling of parameter binding. X-Git-Tag: php-5.1.0b3~145 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=93024bc26885653fa9b91da02cdd1060bf4a86ce;p=php 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. --- 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 | +----------------------------------------------------------------------+ */ 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;