Annotation of embedaddon/php/ext/pdo_odbc/odbc_driver.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 The PHP Group |
! 6: +----------------------------------------------------------------------+
! 7: | This source file is subject to version 3.0 of the PHP license, |
! 8: | that is bundled with this package in the file LICENSE, and is |
! 9: | available through the world-wide-web at the following url: |
! 10: | http://www.php.net/license/3_0.txt. |
! 11: | If you did not receive a copy of the PHP license and are unable to |
! 12: | obtain it through the world-wide-web, please send a note to |
! 13: | license@php.net so we can mail you a copy immediately. |
! 14: +----------------------------------------------------------------------+
! 15: | Author: Wez Furlong <wez@php.net> |
! 16: +----------------------------------------------------------------------+
! 17: */
! 18:
! 19: /* $Id: odbc_driver.c 321634 2012-01-01 13:15:04Z felipe $ */
! 20:
! 21: #ifdef HAVE_CONFIG_H
! 22: #include "config.h"
! 23: #endif
! 24:
! 25: #include "php.h"
! 26: #include "php_ini.h"
! 27: #include "ext/standard/info.h"
! 28: #include "pdo/php_pdo.h"
! 29: #include "pdo/php_pdo_driver.h"
! 30: #include "php_pdo_odbc.h"
! 31: #include "php_pdo_odbc_int.h"
! 32: #include "zend_exceptions.h"
! 33:
! 34: static int pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC)
! 35: {
! 36: pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
! 37: pdo_odbc_errinfo *einfo = &H->einfo;
! 38: pdo_odbc_stmt *S = NULL;
! 39: char *message = NULL;
! 40:
! 41: if (stmt) {
! 42: S = (pdo_odbc_stmt*)stmt->driver_data;
! 43: einfo = &S->einfo;
! 44: }
! 45:
! 46: spprintf(&message, 0, "%s (%s[%ld] at %s:%d)",
! 47: einfo->last_err_msg,
! 48: einfo->what, einfo->last_error,
! 49: einfo->file, einfo->line);
! 50:
! 51: add_next_index_long(info, einfo->last_error);
! 52: add_next_index_string(info, message, 0);
! 53: add_next_index_string(info, einfo->last_state, 1);
! 54:
! 55: return 1;
! 56: }
! 57:
! 58:
! 59: 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) /* {{{ */
! 60: {
! 61: SQLRETURN rc;
! 62: SQLSMALLINT errmsgsize = 0;
! 63: SQLHANDLE eh;
! 64: SQLSMALLINT htype, recno = 1;
! 65: pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
! 66: pdo_odbc_errinfo *einfo = &H->einfo;
! 67: pdo_odbc_stmt *S = NULL;
! 68: pdo_error_type *pdo_err = &dbh->error_code;
! 69:
! 70: if (stmt) {
! 71: S = (pdo_odbc_stmt*)stmt->driver_data;
! 72:
! 73: einfo = &S->einfo;
! 74: pdo_err = &stmt->error_code;
! 75: }
! 76:
! 77: if (statement == SQL_NULL_HSTMT && S) {
! 78: statement = S->stmt;
! 79: }
! 80:
! 81: if (statement) {
! 82: htype = SQL_HANDLE_STMT;
! 83: eh = statement;
! 84: } else if (H->dbc) {
! 85: htype = SQL_HANDLE_DBC;
! 86: eh = H->dbc;
! 87: } else {
! 88: htype = SQL_HANDLE_ENV;
! 89: eh = H->env;
! 90: }
! 91:
! 92: rc = SQLGetDiagRec(htype, eh, recno++, einfo->last_state, &einfo->last_error,
! 93: einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize);
! 94:
! 95: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
! 96: errmsgsize = 0;
! 97: }
! 98:
! 99: einfo->last_err_msg[errmsgsize] = '\0';
! 100: einfo->file = file;
! 101: einfo->line = line;
! 102: einfo->what = what;
! 103:
! 104: strcpy(*pdo_err, einfo->last_state);
! 105: /* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */
! 106: if (!dbh->methods) {
! 107: zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error TSRMLS_CC, "SQLSTATE[%s] %s: %d %s",
! 108: *pdo_err, what, einfo->last_error, einfo->last_err_msg);
! 109: }
! 110:
! 111: /* just like a cursor, once you start pulling, you need to keep
! 112: * going until the end; SQL Server (at least) will mess with the
! 113: * actual cursor state if you don't finish retrieving all the
! 114: * diagnostic records (which can be generated by PRINT statements
! 115: * in the query, for instance). */
! 116: while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
! 117: char discard_state[5];
! 118: char discard_buf[1024];
! 119: SQLINTEGER code;
! 120: rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code,
! 121: discard_buf, sizeof(discard_buf)-1, &errmsgsize);
! 122: }
! 123:
! 124: }
! 125: /* }}} */
! 126:
! 127: static int odbc_handle_closer(pdo_dbh_t *dbh TSRMLS_DC)
! 128: {
! 129: pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
! 130:
! 131: if (H->dbc != SQL_NULL_HANDLE) {
! 132: SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
! 133: SQLDisconnect(H->dbc);
! 134: SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);
! 135: H->dbc = NULL;
! 136: }
! 137: SQLFreeHandle(SQL_HANDLE_ENV, H->env);
! 138: H->env = NULL;
! 139: pefree(H, dbh->is_persistent);
! 140: dbh->driver_data = NULL;
! 141:
! 142: return 0;
! 143: }
! 144:
! 145: static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
! 146: {
! 147: RETCODE rc;
! 148: pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
! 149: pdo_odbc_stmt *S = ecalloc(1, sizeof(*S));
! 150: enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;
! 151: int ret;
! 152: char *nsql = NULL;
! 153: int nsql_len = 0;
! 154:
! 155: S->H = H;
! 156: S->assume_utf8 = H->assume_utf8;
! 157:
! 158: /* before we prepare, we need to peek at the query; if it uses named parameters,
! 159: * we want PDO to rewrite them for us */
! 160: stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
! 161: ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC);
! 162:
! 163: if (ret == 1) {
! 164: /* query was re-written */
! 165: sql = nsql;
! 166: } else if (ret == -1) {
! 167: /* couldn't grok it */
! 168: strcpy(dbh->error_code, stmt->error_code);
! 169: efree(S);
! 170: return 0;
! 171: }
! 172:
! 173: rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);
! 174:
! 175: if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {
! 176: efree(S);
! 177: if (nsql) {
! 178: efree(nsql);
! 179: }
! 180: pdo_odbc_drv_error("SQLAllocStmt");
! 181: return 0;
! 182: }
! 183:
! 184: cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY TSRMLS_CC);
! 185: if (cursor_type != PDO_CURSOR_FWDONLY) {
! 186: rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0);
! 187: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
! 188: pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE");
! 189: SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
! 190: if (nsql) {
! 191: efree(nsql);
! 192: }
! 193: return 0;
! 194: }
! 195: }
! 196:
! 197: rc = SQLPrepare(S->stmt, (char*)sql, SQL_NTS);
! 198: if (nsql) {
! 199: efree(nsql);
! 200: }
! 201:
! 202: stmt->driver_data = S;
! 203: stmt->methods = &odbc_stmt_methods;
! 204:
! 205: if (rc != SQL_SUCCESS) {
! 206: pdo_odbc_stmt_error("SQLPrepare");
! 207: if (rc != SQL_SUCCESS_WITH_INFO) {
! 208: /* clone error information into the db handle */
! 209: strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);
! 210: H->einfo.file = S->einfo.file;
! 211: H->einfo.line = S->einfo.line;
! 212: H->einfo.what = S->einfo.what;
! 213: strcpy(dbh->error_code, stmt->error_code);
! 214: }
! 215: }
! 216:
! 217: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
! 218: return 0;
! 219: }
! 220: return 1;
! 221: }
! 222:
! 223: static long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
! 224: {
! 225: pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
! 226: RETCODE rc;
! 227: SQLLEN row_count = -1;
! 228: PDO_ODBC_HSTMT stmt;
! 229:
! 230: rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);
! 231: if (rc != SQL_SUCCESS) {
! 232: pdo_odbc_drv_error("SQLAllocHandle: STMT");
! 233: return -1;
! 234: }
! 235:
! 236: rc = SQLExecDirect(stmt, (char *)sql, sql_len);
! 237:
! 238: if (rc == SQL_NO_DATA) {
! 239: /* If SQLExecDirect executes a searched update or delete statement that
! 240: * does not affect any rows at the data source, the call to
! 241: * SQLExecDirect returns SQL_NO_DATA. */
! 242: row_count = 0;
! 243: goto out;
! 244: }
! 245:
! 246: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
! 247: pdo_odbc_doer_error("SQLExecDirect");
! 248: goto out;
! 249: }
! 250:
! 251: rc = SQLRowCount(stmt, &row_count);
! 252: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
! 253: pdo_odbc_doer_error("SQLRowCount");
! 254: goto out;
! 255: }
! 256: if (row_count == -1) {
! 257: row_count = 0;
! 258: }
! 259: out:
! 260: SQLFreeHandle(SQL_HANDLE_STMT, stmt);
! 261: return row_count;
! 262: }
! 263:
! 264: 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)
! 265: {
! 266: pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
! 267: /* TODO: figure it out */
! 268: return 0;
! 269: }
! 270:
! 271: static int odbc_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
! 272: {
! 273: if (dbh->auto_commit) {
! 274: /* we need to disable auto-commit now, to be able to initiate a transaction */
! 275: RETCODE rc;
! 276: pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
! 277:
! 278: rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);
! 279: if (rc != SQL_SUCCESS) {
! 280: pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF");
! 281: return 0;
! 282: }
! 283: }
! 284: return 1;
! 285: }
! 286:
! 287: static int odbc_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
! 288: {
! 289: pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
! 290: RETCODE rc;
! 291:
! 292: rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);
! 293:
! 294: if (rc != SQL_SUCCESS) {
! 295: pdo_odbc_drv_error("SQLEndTran: Commit");
! 296:
! 297: if (rc != SQL_SUCCESS_WITH_INFO) {
! 298: return 0;
! 299: }
! 300: }
! 301:
! 302: if (dbh->auto_commit) {
! 303: /* turn auto-commit back on again */
! 304: rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
! 305: if (rc != SQL_SUCCESS) {
! 306: pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
! 307: return 0;
! 308: }
! 309: }
! 310: return 1;
! 311: }
! 312:
! 313: static int odbc_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
! 314: {
! 315: pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
! 316: RETCODE rc;
! 317:
! 318: rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
! 319:
! 320: if (rc != SQL_SUCCESS) {
! 321: pdo_odbc_drv_error("SQLEndTran: Rollback");
! 322:
! 323: if (rc != SQL_SUCCESS_WITH_INFO) {
! 324: return 0;
! 325: }
! 326: }
! 327: if (dbh->auto_commit && H->dbc) {
! 328: /* turn auto-commit back on again */
! 329: rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
! 330: if (rc != SQL_SUCCESS) {
! 331: pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
! 332: return 0;
! 333: }
! 334: }
! 335:
! 336: return 1;
! 337: }
! 338:
! 339: static int odbc_handle_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
! 340: {
! 341: pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
! 342: switch (attr) {
! 343: case PDO_ODBC_ATTR_ASSUME_UTF8:
! 344: H->assume_utf8 = zval_is_true(val);
! 345: return 1;
! 346: default:
! 347: strcpy(H->einfo.last_err_msg, "Unknown Attribute");
! 348: H->einfo.what = "setAttribute";
! 349: strcpy(H->einfo.last_state, "IM001");
! 350: return -1;
! 351: }
! 352: }
! 353:
! 354: static int odbc_handle_get_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
! 355: {
! 356: pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
! 357: switch (attr) {
! 358: case PDO_ATTR_CLIENT_VERSION:
! 359: ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE, 1);
! 360: return 1;
! 361:
! 362: case PDO_ATTR_SERVER_VERSION:
! 363: case PDO_ATTR_PREFETCH:
! 364: case PDO_ATTR_TIMEOUT:
! 365: case PDO_ATTR_SERVER_INFO:
! 366: case PDO_ATTR_CONNECTION_STATUS:
! 367: break;
! 368: case PDO_ODBC_ATTR_ASSUME_UTF8:
! 369: ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0);
! 370: return 1;
! 371:
! 372: }
! 373: return 0;
! 374: }
! 375:
! 376: static struct pdo_dbh_methods odbc_methods = {
! 377: odbc_handle_closer,
! 378: odbc_handle_preparer,
! 379: odbc_handle_doer,
! 380: odbc_handle_quoter,
! 381: odbc_handle_begin,
! 382: odbc_handle_commit,
! 383: odbc_handle_rollback,
! 384: odbc_handle_set_attr,
! 385: NULL, /* last id */
! 386: pdo_odbc_fetch_error_func,
! 387: odbc_handle_get_attr, /* get attr */
! 388: NULL, /* check_liveness */
! 389: };
! 390:
! 391: static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
! 392: {
! 393: pdo_odbc_db_handle *H;
! 394: RETCODE rc;
! 395: int use_direct = 0;
! 396: SQLUINTEGER cursor_lib;
! 397:
! 398: H = pecalloc(1, sizeof(*H), dbh->is_persistent);
! 399:
! 400: dbh->driver_data = H;
! 401:
! 402: SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);
! 403: rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
! 404:
! 405: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
! 406: pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3");
! 407: goto fail;
! 408: }
! 409:
! 410: #ifdef SQL_ATTR_CONNECTION_POOLING
! 411: if (pdo_odbc_pool_on != SQL_CP_OFF) {
! 412: rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0);
! 413: if (rc != SQL_SUCCESS) {
! 414: pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH");
! 415: goto fail;
! 416: }
! 417: }
! 418: #endif
! 419:
! 420: rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);
! 421: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
! 422: pdo_odbc_drv_error("SQLAllocHandle (DBC)");
! 423: goto fail;
! 424: }
! 425:
! 426: rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT,
! 427: (SQLPOINTER)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER);
! 428: if (rc != SQL_SUCCESS) {
! 429: pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT");
! 430: goto fail;
! 431: }
! 432:
! 433: /* set up the cursor library, if needed, or if configured explicitly */
! 434: cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED TSRMLS_CC);
! 435: rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER);
! 436: if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {
! 437: pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS");
! 438: goto fail;
! 439: }
! 440:
! 441: if (strchr(dbh->data_source, ';')) {
! 442: char dsnbuf[1024];
! 443: short dsnbuflen;
! 444:
! 445: use_direct = 1;
! 446:
! 447: /* Force UID and PWD to be set in the DSN */
! 448: if (dbh->username && *dbh->username && !strstr(dbh->data_source, "uid")
! 449: && !strstr(dbh->data_source, "UID")) {
! 450: char *dsn;
! 451: spprintf(&dsn, 0, "%s;UID=%s;PWD=%s", dbh->data_source, dbh->username, dbh->password);
! 452: pefree((char*)dbh->data_source, dbh->is_persistent);
! 453: dbh->data_source = dsn;
! 454: }
! 455:
! 456: rc = SQLDriverConnect(H->dbc, NULL, (char*)dbh->data_source, strlen(dbh->data_source),
! 457: dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT);
! 458: }
! 459: if (!use_direct) {
! 460: rc = SQLConnect(H->dbc, (char*)dbh->data_source, SQL_NTS, dbh->username, SQL_NTS, dbh->password, SQL_NTS);
! 461: }
! 462:
! 463: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
! 464: pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect");
! 465: goto fail;
! 466: }
! 467:
! 468: /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */
! 469:
! 470: dbh->methods = &odbc_methods;
! 471: dbh->alloc_own_columns = 1;
! 472:
! 473: return 1;
! 474:
! 475: fail:
! 476: dbh->methods = &odbc_methods;
! 477: return 0;
! 478: }
! 479: /* }}} */
! 480:
! 481: pdo_driver_t pdo_odbc_driver = {
! 482: PDO_DRIVER_HEADER(odbc),
! 483: pdo_odbc_handle_factory
! 484: };
! 485:
! 486: /*
! 487: * Local variables:
! 488: * tab-width: 4
! 489: * c-basic-offset: 4
! 490: * End:
! 491: * vim600: noet sw=4 ts=4 fdm=marker
! 492: * vim<600: noet sw=4 ts=4
! 493: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>