Back to index

php5  5.3.10
odbc_driver.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 1997-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.0 of the PHP license,       |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_0.txt.                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Author: Wez Furlong <wez@php.net>                                    |
00016   +----------------------------------------------------------------------+
00017 */
00018 
00019 /* $Id: odbc_driver.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024 
00025 #include "php.h"
00026 #include "php_ini.h"
00027 #include "ext/standard/info.h"
00028 #include "pdo/php_pdo.h"
00029 #include "pdo/php_pdo_driver.h"
00030 #include "php_pdo_odbc.h"
00031 #include "php_pdo_odbc_int.h"
00032 #include "zend_exceptions.h"
00033 
00034 static int pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC)
00035 {
00036        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
00037        pdo_odbc_errinfo *einfo = &H->einfo;
00038        pdo_odbc_stmt *S = NULL;
00039        char *message = NULL;
00040 
00041        if (stmt) {
00042               S = (pdo_odbc_stmt*)stmt->driver_data;
00043               einfo = &S->einfo;
00044        }
00045 
00046        spprintf(&message, 0, "%s (%s[%ld] at %s:%d)",
00047                             einfo->last_err_msg,
00048                             einfo->what, einfo->last_error,
00049                             einfo->file, einfo->line);
00050 
00051        add_next_index_long(info, einfo->last_error);
00052        add_next_index_string(info, message, 0);
00053        add_next_index_string(info, einfo->last_state, 1);
00054 
00055        return 1;
00056 }
00057 
00058 
00059 void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line TSRMLS_DC) /* {{{ */
00060 {
00061        SQLRETURN rc;
00062        SQLSMALLINT   errmsgsize = 0;
00063        SQLHANDLE eh;
00064        SQLSMALLINT htype, recno = 1;
00065        pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
00066        pdo_odbc_errinfo *einfo = &H->einfo;
00067        pdo_odbc_stmt *S = NULL;
00068        pdo_error_type *pdo_err = &dbh->error_code;
00069 
00070        if (stmt) {
00071               S = (pdo_odbc_stmt*)stmt->driver_data;
00072 
00073               einfo = &S->einfo;
00074               pdo_err = &stmt->error_code;
00075        }
00076 
00077        if (statement == SQL_NULL_HSTMT && S) {
00078               statement = S->stmt;
00079        }
00080 
00081        if (statement) {
00082               htype = SQL_HANDLE_STMT;
00083               eh = statement;
00084        } else if (H->dbc) {
00085               htype = SQL_HANDLE_DBC;
00086               eh = H->dbc;
00087        } else {
00088               htype = SQL_HANDLE_ENV;
00089               eh = H->env;
00090        }
00091 
00092        rc = SQLGetDiagRec(htype, eh, recno++, einfo->last_state, &einfo->last_error,
00093                      einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize);
00094 
00095        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00096               errmsgsize = 0;
00097        }
00098 
00099        einfo->last_err_msg[errmsgsize] = '\0';
00100        einfo->file = file;
00101        einfo->line = line;
00102        einfo->what = what;
00103 
00104        strcpy(*pdo_err, einfo->last_state);
00105 /* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */
00106        if (!dbh->methods) {
00107               zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error TSRMLS_CC, "SQLSTATE[%s] %s: %d %s",
00108                             *pdo_err, what, einfo->last_error, einfo->last_err_msg);
00109        }
00110 
00111        /* just like a cursor, once you start pulling, you need to keep
00112         * going until the end; SQL Server (at least) will mess with the
00113         * actual cursor state if you don't finish retrieving all the
00114         * diagnostic records (which can be generated by PRINT statements
00115         * in the query, for instance). */
00116        while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
00117               char discard_state[5];
00118               char discard_buf[1024];
00119               SQLINTEGER code;
00120               rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code,
00121                             discard_buf, sizeof(discard_buf)-1, &errmsgsize);
00122        }
00123 
00124 }
00125 /* }}} */
00126 
00127 static int odbc_handle_closer(pdo_dbh_t *dbh TSRMLS_DC)
00128 {
00129        pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
00130 
00131        if (H->dbc != SQL_NULL_HANDLE) {
00132               SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
00133               SQLDisconnect(H->dbc);
00134               SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);
00135               H->dbc = NULL;
00136        }
00137        SQLFreeHandle(SQL_HANDLE_ENV, H->env);
00138        H->env = NULL;
00139        pefree(H, dbh->is_persistent);
00140        dbh->driver_data = NULL;
00141 
00142        return 0;
00143 }
00144 
00145 static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
00146 {
00147        RETCODE rc;
00148        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
00149        pdo_odbc_stmt *S = ecalloc(1, sizeof(*S));
00150        enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;
00151        int ret;
00152        char *nsql = NULL;
00153        int nsql_len = 0;
00154 
00155        S->H = H;
00156        S->assume_utf8 = H->assume_utf8;
00157 
00158        /* before we prepare, we need to peek at the query; if it uses named parameters,
00159         * we want PDO to rewrite them for us */
00160        stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
00161        ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC);
00162        
00163        if (ret == 1) {
00164               /* query was re-written */
00165               sql = nsql;
00166        } else if (ret == -1) {
00167               /* couldn't grok it */
00168               strcpy(dbh->error_code, stmt->error_code);
00169               efree(S);
00170               return 0;
00171        }
00172        
00173        rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);
00174 
00175        if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {
00176               efree(S);
00177               if (nsql) {
00178                      efree(nsql);
00179               }
00180               pdo_odbc_drv_error("SQLAllocStmt");
00181               return 0;
00182        }
00183 
00184        cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY TSRMLS_CC);
00185        if (cursor_type != PDO_CURSOR_FWDONLY) {
00186               rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0);
00187               if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00188                      pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE");
00189                      SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
00190                      if (nsql) {
00191                             efree(nsql);
00192                      }
00193                      return 0;
00194               }
00195        }
00196        
00197        rc = SQLPrepare(S->stmt, (char*)sql, SQL_NTS);
00198        if (nsql) {
00199               efree(nsql);
00200        }
00201 
00202        stmt->driver_data = S;
00203        stmt->methods = &odbc_stmt_methods;
00204 
00205        if (rc != SQL_SUCCESS) {
00206               pdo_odbc_stmt_error("SQLPrepare");
00207         if (rc != SQL_SUCCESS_WITH_INFO) {
00208             /* clone error information into the db handle */
00209             strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);
00210             H->einfo.file = S->einfo.file;
00211             H->einfo.line = S->einfo.line;
00212             H->einfo.what = S->einfo.what;
00213             strcpy(dbh->error_code, stmt->error_code);
00214         }
00215        }
00216 
00217        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00218               return 0;
00219        }
00220        return 1;
00221 }
00222 
00223 static long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
00224 {
00225        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
00226        RETCODE rc;
00227        SQLLEN row_count = -1;
00228        PDO_ODBC_HSTMT       stmt;
00229        
00230        rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);
00231        if (rc != SQL_SUCCESS) {
00232               pdo_odbc_drv_error("SQLAllocHandle: STMT");
00233               return -1;
00234        }
00235 
00236        rc = SQLExecDirect(stmt, (char *)sql, sql_len);
00237 
00238        if (rc == SQL_NO_DATA) {
00239               /* If SQLExecDirect executes a searched update or delete statement that
00240                * does not affect any rows at the data source, the call to
00241                * SQLExecDirect returns SQL_NO_DATA. */
00242               row_count = 0;
00243               goto out;
00244        }
00245 
00246        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00247               pdo_odbc_doer_error("SQLExecDirect");
00248               goto out;
00249        }
00250 
00251        rc = SQLRowCount(stmt, &row_count);
00252        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00253               pdo_odbc_doer_error("SQLRowCount");
00254               goto out;
00255        }
00256        if (row_count == -1) {
00257               row_count = 0;
00258        }
00259 out:
00260        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00261        return row_count;
00262 }
00263 
00264 static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type param_type  TSRMLS_DC)
00265 {
00266        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
00267        /* TODO: figure it out */
00268        return 0;
00269 }
00270 
00271 static int odbc_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
00272 {
00273        if (dbh->auto_commit) {
00274               /* we need to disable auto-commit now, to be able to initiate a transaction */
00275               RETCODE rc;
00276               pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
00277 
00278               rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);
00279               if (rc != SQL_SUCCESS) {
00280                      pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF");
00281                      return 0;
00282               }
00283        }
00284        return 1;
00285 }
00286 
00287 static int odbc_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
00288 {
00289        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
00290        RETCODE rc;
00291 
00292        rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);
00293 
00294        if (rc != SQL_SUCCESS) {
00295               pdo_odbc_drv_error("SQLEndTran: Commit");
00296 
00297               if (rc != SQL_SUCCESS_WITH_INFO) {
00298                      return 0;
00299               }
00300        }
00301 
00302        if (dbh->auto_commit) {
00303               /* turn auto-commit back on again */
00304               rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
00305               if (rc != SQL_SUCCESS) {
00306                      pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
00307                      return 0;
00308               }
00309        }
00310        return 1;
00311 }
00312 
00313 static int odbc_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
00314 {
00315        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
00316        RETCODE rc;
00317 
00318        rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
00319 
00320        if (rc != SQL_SUCCESS) {
00321               pdo_odbc_drv_error("SQLEndTran: Rollback");
00322 
00323               if (rc != SQL_SUCCESS_WITH_INFO) {
00324                      return 0;
00325               }
00326        }
00327        if (dbh->auto_commit && H->dbc) {
00328               /* turn auto-commit back on again */
00329               rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
00330               if (rc != SQL_SUCCESS) {
00331                      pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
00332                      return 0;
00333               }
00334        }
00335 
00336        return 1;
00337 }
00338 
00339 static int odbc_handle_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
00340 {
00341        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
00342        switch (attr) {
00343               case PDO_ODBC_ATTR_ASSUME_UTF8:
00344                      H->assume_utf8 = zval_is_true(val);
00345                      return 1;
00346               default:
00347                      strcpy(H->einfo.last_err_msg, "Unknown Attribute");
00348                      H->einfo.what = "setAttribute";
00349                      strcpy(H->einfo.last_state, "IM001");
00350                      return -1;
00351        }
00352 }
00353 
00354 static int odbc_handle_get_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
00355 {
00356        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
00357        switch (attr) {
00358               case PDO_ATTR_CLIENT_VERSION:
00359                      ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE, 1);
00360                      return 1;
00361 
00362               case PDO_ATTR_SERVER_VERSION:
00363               case PDO_ATTR_PREFETCH:
00364               case PDO_ATTR_TIMEOUT:
00365               case PDO_ATTR_SERVER_INFO:
00366               case PDO_ATTR_CONNECTION_STATUS:
00367                      break;
00368               case PDO_ODBC_ATTR_ASSUME_UTF8:
00369                      ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0);
00370                      return 1;
00371 
00372        }
00373        return 0;
00374 }
00375 
00376 static struct pdo_dbh_methods odbc_methods = {
00377        odbc_handle_closer,
00378        odbc_handle_preparer,
00379        odbc_handle_doer,
00380        odbc_handle_quoter,
00381        odbc_handle_begin,
00382        odbc_handle_commit,
00383        odbc_handle_rollback,
00384        odbc_handle_set_attr,
00385        NULL,  /* last id */
00386        pdo_odbc_fetch_error_func,
00387        odbc_handle_get_attr,       /* get attr */
00388        NULL,  /* check_liveness */
00389 };
00390 
00391 static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
00392 {
00393        pdo_odbc_db_handle *H;
00394        RETCODE rc;
00395        int use_direct = 0;
00396        SQLUINTEGER cursor_lib;
00397 
00398        H = pecalloc(1, sizeof(*H), dbh->is_persistent);
00399 
00400        dbh->driver_data = H;
00401        
00402        SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);
00403        rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
00404 
00405        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00406               pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3");
00407               goto fail;
00408        }
00409 
00410 #ifdef SQL_ATTR_CONNECTION_POOLING
00411        if (pdo_odbc_pool_on != SQL_CP_OFF) {
00412               rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0);
00413               if (rc != SQL_SUCCESS) {
00414                      pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH");
00415                      goto fail;
00416               }
00417        }
00418 #endif
00419        
00420        rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);
00421        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00422               pdo_odbc_drv_error("SQLAllocHandle (DBC)");
00423               goto fail;
00424        }
00425 
00426        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT,
00427               (SQLPOINTER)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER);
00428        if (rc != SQL_SUCCESS) {
00429               pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT");
00430               goto fail;
00431        }
00432 
00433        /* set up the cursor library, if needed, or if configured explicitly */
00434        cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED TSRMLS_CC);
00435        rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER);
00436        if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {
00437               pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS");
00438               goto fail;
00439        }
00440 
00441        if (strchr(dbh->data_source, ';')) {
00442               char dsnbuf[1024];
00443               short dsnbuflen;
00444 
00445               use_direct = 1;
00446 
00447               /* Force UID and PWD to be set in the DSN */
00448               if (dbh->username && *dbh->username && !strstr(dbh->data_source, "uid")
00449                             && !strstr(dbh->data_source, "UID")) {
00450                      char *dsn;
00451                      spprintf(&dsn, 0, "%s;UID=%s;PWD=%s", dbh->data_source, dbh->username, dbh->password);
00452                      pefree((char*)dbh->data_source, dbh->is_persistent);
00453                      dbh->data_source = dsn;
00454               }
00455 
00456               rc = SQLDriverConnect(H->dbc, NULL, (char*)dbh->data_source, strlen(dbh->data_source),
00457                             dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT);
00458        }
00459        if (!use_direct) {
00460               rc = SQLConnect(H->dbc, (char*)dbh->data_source, SQL_NTS, dbh->username, SQL_NTS, dbh->password, SQL_NTS);
00461        }
00462 
00463        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00464               pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect");
00465               goto fail;
00466        }
00467 
00468        /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */
00469 
00470        dbh->methods = &odbc_methods;
00471        dbh->alloc_own_columns = 1;
00472        
00473        return 1;
00474 
00475 fail:
00476        dbh->methods = &odbc_methods;
00477        return 0;
00478 }
00479 /* }}} */
00480 
00481 pdo_driver_t pdo_odbc_driver = {
00482        PDO_DRIVER_HEADER(odbc),
00483        pdo_odbc_handle_factory
00484 };
00485 
00486 /*
00487  * Local variables:
00488  * tab-width: 4
00489  * c-basic-offset: 4
00490  * End:
00491  * vim600: noet sw=4 ts=4 fdm=marker
00492  * vim<600: noet sw=4 ts=4
00493  */