]> granicus.if.org Git - php/commitdiff
Fix handling of parameter binding.
authorWez Furlong <wez@php.net>
Thu, 7 Jul 2005 12:49:21 +0000 (12:49 +0000)
committerWez Furlong <wez@php.net>
Thu, 7 Jul 2005 12:49:21 +0000 (12:49 +0000)
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.

ext/pdo_odbc/odbc_driver.c
ext/pdo_odbc/odbc_stmt.c
ext/pdo_odbc/pdo_odbc.c
ext/pdo_odbc/php_pdo_odbc.h
ext/pdo_odbc/php_pdo_odbc_int.h

index f3caf3419e93ef91ab99c086069c533299939ef8..4e183f055b18520f157f55ac2604acf884a50828 100755 (executable)
@@ -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;
 }
 /* }}} */
 
index daf8967d9e2572eba800020dd5b1ae32e9259380..63e2512dea489bb8a83e19bc12f27c1bb98aabd0 100755 (executable)
@@ -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) {
index 5a7709e7c8a914c04c8e6f33f3558b21ac727d2f..34b9bc90ae771c78b78a8191fadf10c799285f9c 100755 (executable)
@@ -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.
index 1c2dcf41978f4a67d34ac5bc6b2eff33e536f0c4..8d290b38b4ef5aa5ec9a0304a8d4624288715ef3 100644 (file)
@@ -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>                                    |
   +----------------------------------------------------------------------+
 */
 
index 4fb493dcfd1e2454e3ba458311ad6838b0a32fbe..72a65ede294e7045cd0c1bf6c9b716235837fa13 100755 (executable)
@@ -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;