Return to odbc_driver.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / pdo_odbc |
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: */