Return to odbc_stmt.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_stmt.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: ! 33: enum pdo_odbc_conv_result { ! 34: PDO_ODBC_CONV_NOT_REQUIRED, ! 35: PDO_ODBC_CONV_OK, ! 36: PDO_ODBC_CONV_FAIL ! 37: }; ! 38: ! 39: static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype) ! 40: { ! 41: if (!S->assume_utf8) return 0; ! 42: switch (sqltype) { ! 43: #ifdef SQL_WCHAR ! 44: case SQL_WCHAR: ! 45: return 1; ! 46: #endif ! 47: #ifdef SQL_WLONGVARCHAR ! 48: case SQL_WLONGVARCHAR: ! 49: return 1; ! 50: #endif ! 51: #ifdef SQL_WVARCHAR ! 52: case SQL_WVARCHAR: ! 53: return 1; ! 54: #endif ! 55: default: ! 56: return 0; ! 57: } ! 58: } ! 59: ! 60: static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf, ! 61: unsigned long buflen, unsigned long *outlen) ! 62: { ! 63: #ifdef PHP_WIN32 ! 64: if (is_unicode && buflen) { ! 65: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 66: DWORD ret; ! 67: ! 68: ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0); ! 69: if (ret == 0) { ! 70: /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ ! 71: return PDO_ODBC_CONV_FAIL; ! 72: } ! 73: ! 74: ret *= sizeof(WCHAR); ! 75: ! 76: if (S->convbufsize <= ret) { ! 77: S->convbufsize = ret + sizeof(WCHAR); ! 78: S->convbuf = erealloc(S->convbuf, S->convbufsize); ! 79: } ! 80: ! 81: ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR)); ! 82: if (ret == 0) { ! 83: /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ ! 84: return PDO_ODBC_CONV_FAIL; ! 85: } ! 86: ! 87: ret *= sizeof(WCHAR); ! 88: *outlen = ret; ! 89: return PDO_ODBC_CONV_OK; ! 90: } ! 91: #endif ! 92: return PDO_ODBC_CONV_NOT_REQUIRED; ! 93: } ! 94: ! 95: static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, const char *buf, ! 96: unsigned long buflen, unsigned long *outlen) ! 97: { ! 98: #ifdef PHP_WIN32 ! 99: if (is_unicode && buflen) { ! 100: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 101: DWORD ret; ! 102: ! 103: ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), NULL, 0, NULL, NULL); ! 104: if (ret == 0) { ! 105: return PDO_ODBC_CONV_FAIL; ! 106: } ! 107: ! 108: if (S->convbufsize <= ret) { ! 109: S->convbufsize = ret + 1; ! 110: S->convbuf = erealloc(S->convbuf, S->convbufsize); ! 111: } ! 112: ! 113: ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), S->convbuf, S->convbufsize, NULL, NULL); ! 114: if (ret == 0) { ! 115: return PDO_ODBC_CONV_FAIL; ! 116: } ! 117: ! 118: *outlen = ret; ! 119: S->convbuf[*outlen] = '\0'; ! 120: return PDO_ODBC_CONV_OK; ! 121: } ! 122: #endif ! 123: return PDO_ODBC_CONV_NOT_REQUIRED; ! 124: } ! 125: ! 126: static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S TSRMLS_DC) ! 127: { ! 128: if (S->cols) { ! 129: int i; ! 130: ! 131: for (i = 0; i < stmt->column_count; i++) { ! 132: if (S->cols[i].data) { ! 133: efree(S->cols[i].data); ! 134: } ! 135: } ! 136: efree(S->cols); ! 137: S->cols = NULL; ! 138: } ! 139: } ! 140: ! 141: static int odbc_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) ! 142: { ! 143: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 144: ! 145: if (S->stmt != SQL_NULL_HANDLE) { ! 146: if (stmt->executed) { ! 147: SQLCloseCursor(S->stmt); ! 148: } ! 149: SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); ! 150: S->stmt = SQL_NULL_HANDLE; ! 151: } ! 152: ! 153: free_cols(stmt, S TSRMLS_CC); ! 154: if (S->convbuf) { ! 155: efree(S->convbuf); ! 156: } ! 157: efree(S); ! 158: ! 159: return 1; ! 160: } ! 161: ! 162: static int odbc_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) ! 163: { ! 164: RETCODE rc; ! 165: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 166: char *buf = NULL; ! 167: SQLLEN row_count = -1; ! 168: ! 169: if (stmt->executed) { ! 170: SQLCloseCursor(S->stmt); ! 171: } ! 172: ! 173: rc = SQLExecute(S->stmt); ! 174: ! 175: while (rc == SQL_NEED_DATA) { ! 176: struct pdo_bound_param_data *param; ! 177: ! 178: rc = SQLParamData(S->stmt, (SQLPOINTER*)¶m); ! 179: if (rc == SQL_NEED_DATA) { ! 180: php_stream *stm; ! 181: int len; ! 182: pdo_odbc_param *P; ! 183: ! 184: P = (pdo_odbc_param*)param->driver_data; ! 185: if (Z_TYPE_P(param->parameter) != IS_RESOURCE) { ! 186: /* they passed in a string */ ! 187: unsigned long ulen; ! 188: convert_to_string(param->parameter); ! 189: ! 190: switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, ! 191: Z_STRVAL_P(param->parameter), ! 192: Z_STRLEN_P(param->parameter), ! 193: &ulen)) { ! 194: case PDO_ODBC_CONV_NOT_REQUIRED: ! 195: SQLPutData(S->stmt, Z_STRVAL_P(param->parameter), ! 196: Z_STRLEN_P(param->parameter)); ! 197: break; ! 198: case PDO_ODBC_CONV_OK: ! 199: SQLPutData(S->stmt, S->convbuf, ulen); ! 200: break; ! 201: case PDO_ODBC_CONV_FAIL: ! 202: pdo_odbc_stmt_error("error converting input string"); ! 203: SQLCloseCursor(S->stmt); ! 204: if (buf) { ! 205: efree(buf); ! 206: } ! 207: return 0; ! 208: } ! 209: continue; ! 210: } ! 211: ! 212: /* we assume that LOBs are binary and don't need charset ! 213: * conversion */ ! 214: ! 215: php_stream_from_zval_no_verify(stm, ¶m->parameter); ! 216: if (!stm) { ! 217: /* shouldn't happen either */ ! 218: pdo_odbc_stmt_error("input LOB is no longer a stream"); ! 219: SQLCloseCursor(S->stmt); ! 220: if (buf) { ! 221: efree(buf); ! 222: } ! 223: return 0; ! 224: } ! 225: ! 226: /* now suck data from the stream and stick it into the database */ ! 227: if (buf == NULL) { ! 228: buf = emalloc(8192); ! 229: } ! 230: ! 231: do { ! 232: len = php_stream_read(stm, buf, 8192); ! 233: if (len == 0) { ! 234: break; ! 235: } ! 236: SQLPutData(S->stmt, buf, len); ! 237: } while (1); ! 238: } ! 239: } ! 240: ! 241: if (buf) { ! 242: efree(buf); ! 243: } ! 244: ! 245: switch (rc) { ! 246: case SQL_SUCCESS: ! 247: break; ! 248: case SQL_NO_DATA_FOUND: ! 249: case SQL_SUCCESS_WITH_INFO: ! 250: pdo_odbc_stmt_error("SQLExecute"); ! 251: break; ! 252: ! 253: default: ! 254: pdo_odbc_stmt_error("SQLExecute"); ! 255: return 0; ! 256: } ! 257: ! 258: SQLRowCount(S->stmt, &row_count); ! 259: stmt->row_count = row_count; ! 260: ! 261: if (!stmt->executed) { ! 262: /* do first-time-only definition of bind/mapping stuff */ ! 263: SQLSMALLINT colcount; ! 264: ! 265: /* how many columns do we have ? */ ! 266: SQLNumResultCols(S->stmt, &colcount); ! 267: ! 268: stmt->column_count = (int)colcount; ! 269: S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); ! 270: S->going_long = 0; ! 271: } ! 272: ! 273: return 1; ! 274: } ! 275: ! 276: static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, ! 277: enum pdo_param_event event_type TSRMLS_DC) ! 278: { ! 279: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 280: RETCODE rc; ! 281: SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0; ! 282: UDWORD precision = 0; ! 283: pdo_odbc_param *P; ! 284: ! 285: /* we're only interested in parameters for prepared SQL right now */ ! 286: if (param->is_param) { ! 287: ! 288: switch (event_type) { ! 289: case PDO_PARAM_EVT_FREE: ! 290: P = param->driver_data; ! 291: if (P) { ! 292: efree(P); ! 293: } ! 294: break; ! 295: ! 296: case PDO_PARAM_EVT_ALLOC: ! 297: { ! 298: /* figure out what we're doing */ ! 299: switch (PDO_PARAM_TYPE(param->param_type)) { ! 300: case PDO_PARAM_LOB: ! 301: break; ! 302: ! 303: case PDO_PARAM_STMT: ! 304: return 0; ! 305: ! 306: default: ! 307: break; ! 308: } ! 309: ! 310: rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable); ! 311: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ! 312: /* MS Access, for instance, doesn't support SQLDescribeParam, ! 313: * so we need to guess */ ! 314: sqltype = PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB ? ! 315: SQL_LONGVARBINARY : ! 316: SQL_LONGVARCHAR; ! 317: precision = 4000; ! 318: scale = 5; ! 319: nullable = 1; ! 320: ! 321: if (param->max_value_len > 0) { ! 322: precision = param->max_value_len; ! 323: } ! 324: } ! 325: if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) { ! 326: ctype = SQL_C_BINARY; ! 327: } else { ! 328: ctype = SQL_C_CHAR; ! 329: } ! 330: ! 331: P = emalloc(sizeof(*P)); ! 332: param->driver_data = P; ! 333: ! 334: P->len = 0; /* is re-populated each EXEC_PRE */ ! 335: P->outbuf = NULL; ! 336: ! 337: P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); ! 338: if (P->is_unicode) { ! 339: /* avoid driver auto-translation: we'll do it ourselves */ ! 340: ctype = SQL_C_BINARY; ! 341: } ! 342: ! 343: if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) { ! 344: P->paramtype = SQL_PARAM_INPUT_OUTPUT; ! 345: } else if (param->max_value_len <= 0) { ! 346: P->paramtype = SQL_PARAM_INPUT; ! 347: } else { ! 348: P->paramtype = SQL_PARAM_OUTPUT; ! 349: } ! 350: ! 351: if (P->paramtype != SQL_PARAM_INPUT) { ! 352: if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) { ! 353: /* need an explicit buffer to hold result */ ! 354: P->len = param->max_value_len > 0 ? param->max_value_len : precision; ! 355: if (P->is_unicode) { ! 356: P->len *= 2; ! 357: } ! 358: P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1)); ! 359: } ! 360: } ! 361: ! 362: if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) { ! 363: pdo_odbc_stmt_error("Can't bind a lob for output"); ! 364: return 0; ! 365: } ! 366: ! 367: rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1, ! 368: P->paramtype, ctype, sqltype, precision, scale, ! 369: P->paramtype == SQL_PARAM_INPUT ? ! 370: (SQLPOINTER)param : ! 371: P->outbuf, ! 372: P->len, ! 373: &P->len ! 374: ); ! 375: ! 376: if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { ! 377: return 1; ! 378: } ! 379: pdo_odbc_stmt_error("SQLBindParameter"); ! 380: return 0; ! 381: } ! 382: ! 383: case PDO_PARAM_EVT_EXEC_PRE: ! 384: P = param->driver_data; ! 385: if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { ! 386: if (Z_TYPE_P(param->parameter) == IS_RESOURCE) { ! 387: php_stream *stm; ! 388: php_stream_statbuf sb; ! 389: ! 390: php_stream_from_zval_no_verify(stm, ¶m->parameter); ! 391: ! 392: if (!stm) { ! 393: return 0; ! 394: } ! 395: ! 396: if (0 == php_stream_stat(stm, &sb)) { ! 397: if (P->outbuf) { ! 398: int len, amount; ! 399: char *ptr = P->outbuf; ! 400: char *end = P->outbuf + P->len; ! 401: ! 402: P->len = 0; ! 403: do { ! 404: amount = end - ptr; ! 405: if (amount == 0) { ! 406: break; ! 407: } ! 408: if (amount > 8192) ! 409: amount = 8192; ! 410: len = php_stream_read(stm, ptr, amount); ! 411: if (len == 0) { ! 412: break; ! 413: } ! 414: ptr += len; ! 415: P->len += len; ! 416: } while (1); ! 417: ! 418: } else { ! 419: P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size); ! 420: } ! 421: } else { ! 422: if (P->outbuf) { ! 423: P->len = 0; ! 424: } else { ! 425: P->len = SQL_LEN_DATA_AT_EXEC(0); ! 426: } ! 427: } ! 428: } else { ! 429: convert_to_string(param->parameter); ! 430: if (P->outbuf) { ! 431: P->len = Z_STRLEN_P(param->parameter); ! 432: memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len); ! 433: } else { ! 434: P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter)); ! 435: } ! 436: } ! 437: } else if (Z_TYPE_P(param->parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) { ! 438: P->len = SQL_NULL_DATA; ! 439: } else { ! 440: convert_to_string(param->parameter); ! 441: if (P->outbuf) { ! 442: unsigned long ulen; ! 443: switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, ! 444: Z_STRVAL_P(param->parameter), ! 445: Z_STRLEN_P(param->parameter), ! 446: &ulen)) { ! 447: case PDO_ODBC_CONV_FAIL: ! 448: case PDO_ODBC_CONV_NOT_REQUIRED: ! 449: P->len = Z_STRLEN_P(param->parameter); ! 450: memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len); ! 451: break; ! 452: case PDO_ODBC_CONV_OK: ! 453: P->len = ulen; ! 454: memcpy(P->outbuf, S->convbuf, P->len); ! 455: break; ! 456: } ! 457: } else { ! 458: P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter)); ! 459: } ! 460: } ! 461: return 1; ! 462: ! 463: case PDO_PARAM_EVT_EXEC_POST: ! 464: P = param->driver_data; ! 465: if (P->outbuf) { ! 466: if (P->outbuf) { ! 467: unsigned long ulen; ! 468: char *srcbuf; ! 469: unsigned long srclen; ! 470: ! 471: switch (P->len) { ! 472: case SQL_NULL_DATA: ! 473: zval_dtor(param->parameter); ! 474: ZVAL_NULL(param->parameter); ! 475: break; ! 476: default: ! 477: convert_to_string(param->parameter); ! 478: switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) { ! 479: case PDO_ODBC_CONV_FAIL: ! 480: /* something fishy, but allow it to come back as binary */ ! 481: case PDO_ODBC_CONV_NOT_REQUIRED: ! 482: srcbuf = P->outbuf; ! 483: srclen = P->len; ! 484: break; ! 485: case PDO_ODBC_CONV_OK: ! 486: srcbuf = S->convbuf; ! 487: srclen = ulen; ! 488: break; ! 489: } ! 490: ! 491: Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), srclen+1); ! 492: memcpy(Z_STRVAL_P(param->parameter), srcbuf, srclen); ! 493: Z_STRLEN_P(param->parameter) = srclen; ! 494: Z_STRVAL_P(param->parameter)[srclen] = '\0'; ! 495: } ! 496: } ! 497: } ! 498: return 1; ! 499: } ! 500: } ! 501: return 1; ! 502: } ! 503: ! 504: static int odbc_stmt_fetch(pdo_stmt_t *stmt, ! 505: enum pdo_fetch_orientation ori, long offset TSRMLS_DC) ! 506: { ! 507: RETCODE rc; ! 508: SQLSMALLINT odbcori; ! 509: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 510: ! 511: switch (ori) { ! 512: case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break; ! 513: case PDO_FETCH_ORI_PRIOR: odbcori = SQL_FETCH_PRIOR; break; ! 514: case PDO_FETCH_ORI_FIRST: odbcori = SQL_FETCH_FIRST; break; ! 515: case PDO_FETCH_ORI_LAST: odbcori = SQL_FETCH_LAST; break; ! 516: case PDO_FETCH_ORI_ABS: odbcori = SQL_FETCH_ABSOLUTE; break; ! 517: case PDO_FETCH_ORI_REL: odbcori = SQL_FETCH_RELATIVE; break; ! 518: default: ! 519: strcpy(stmt->error_code, "HY106"); ! 520: return 0; ! 521: } ! 522: rc = SQLFetchScroll(S->stmt, odbcori, offset); ! 523: ! 524: if (rc == SQL_SUCCESS) { ! 525: return 1; ! 526: } ! 527: if (rc == SQL_SUCCESS_WITH_INFO) { ! 528: pdo_odbc_stmt_error("SQLFetchScroll"); ! 529: return 1; ! 530: } ! 531: ! 532: if (rc == SQL_NO_DATA) { ! 533: /* pdo_odbc_stmt_error("SQLFetchScroll"); */ ! 534: return 0; ! 535: } ! 536: ! 537: pdo_odbc_stmt_error("SQLFetchScroll"); ! 538: ! 539: return 0; ! 540: } ! 541: ! 542: static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) ! 543: { ! 544: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 545: struct pdo_column_data *col = &stmt->columns[colno]; ! 546: zend_bool dyn = FALSE; ! 547: RETCODE rc; ! 548: SWORD colnamelen; ! 549: SDWORD colsize, displaysize; ! 550: ! 551: rc = SQLDescribeCol(S->stmt, colno+1, S->cols[colno].colname, ! 552: sizeof(S->cols[colno].colname)-1, &colnamelen, ! 553: &S->cols[colno].coltype, &colsize, NULL, NULL); ! 554: ! 555: if (rc != SQL_SUCCESS) { ! 556: pdo_odbc_stmt_error("SQLDescribeCol"); ! 557: if (rc != SQL_SUCCESS_WITH_INFO) { ! 558: return 0; ! 559: } ! 560: } ! 561: ! 562: rc = SQLColAttribute(S->stmt, colno+1, ! 563: SQL_DESC_DISPLAY_SIZE, ! 564: NULL, 0, NULL, &displaysize); ! 565: ! 566: if (rc != SQL_SUCCESS) { ! 567: pdo_odbc_stmt_error("SQLColAttribute"); ! 568: if (rc != SQL_SUCCESS_WITH_INFO) { ! 569: return 0; ! 570: } ! 571: } ! 572: colsize = displaysize; ! 573: ! 574: col->maxlen = S->cols[colno].datalen = colsize; ! 575: col->namelen = colnamelen; ! 576: col->name = estrdup(S->cols[colno].colname); ! 577: S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype); ! 578: ! 579: /* returning data as a string */ ! 580: col->param_type = PDO_PARAM_STR; ! 581: ! 582: /* tell ODBC to put it straight into our buffer, but only if it ! 583: * isn't "long" data, and only if we haven't already bound a long ! 584: * column. */ ! 585: if (colsize < 256 && !S->going_long) { ! 586: S->cols[colno].data = emalloc(colsize+1); ! 587: S->cols[colno].is_long = 0; ! 588: ! 589: rc = SQLBindCol(S->stmt, colno+1, ! 590: S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR, ! 591: S->cols[colno].data, ! 592: S->cols[colno].datalen+1, &S->cols[colno].fetched_len); ! 593: ! 594: if (rc != SQL_SUCCESS) { ! 595: pdo_odbc_stmt_error("SQLBindCol"); ! 596: return 0; ! 597: } ! 598: } else { ! 599: /* allocate a smaller buffer to keep around for smaller ! 600: * "long" columns */ ! 601: S->cols[colno].data = emalloc(256); ! 602: S->going_long = 1; ! 603: S->cols[colno].is_long = 1; ! 604: } ! 605: ! 606: return 1; ! 607: } ! 608: ! 609: static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) ! 610: { ! 611: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 612: pdo_odbc_column *C = &S->cols[colno]; ! 613: unsigned long ulen; ! 614: ! 615: /* if it is a column containing "long" data, perform late binding now */ ! 616: if (C->is_long) { ! 617: unsigned long alloced = 4096; ! 618: unsigned long used = 0; ! 619: char *buf; ! 620: RETCODE rc; ! 621: ! 622: /* fetch it into C->data, which is allocated with a length ! 623: * of 256 bytes; if there is more to be had, we then allocate ! 624: * bigger buffer for the caller to free */ ! 625: ! 626: rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, ! 627: 256, &C->fetched_len); ! 628: ! 629: if (rc == SQL_SUCCESS) { ! 630: /* all the data fit into our little buffer; ! 631: * jump down to the generic bound data case */ ! 632: goto in_data; ! 633: } ! 634: ! 635: if (rc == SQL_SUCCESS_WITH_INFO) { ! 636: /* promote up to a bigger buffer */ ! 637: ! 638: if (C->fetched_len != SQL_NO_TOTAL) { ! 639: /* use size suggested by the driver, if it knows it */ ! 640: alloced = C->fetched_len + 1; ! 641: } ! 642: ! 643: buf = emalloc(alloced); ! 644: memcpy(buf, C->data, 256); ! 645: used = 255; /* not 256; the driver NUL terminated the buffer */ ! 646: ! 647: do { ! 648: C->fetched_len = 0; ! 649: rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, ! 650: buf + used, alloced - used, ! 651: &C->fetched_len); ! 652: ! 653: if (rc == SQL_NO_DATA) { ! 654: /* we got the lot */ ! 655: break; ! 656: } else if (rc != SQL_SUCCESS) { ! 657: pdo_odbc_stmt_error("SQLGetData"); ! 658: if (rc != SQL_SUCCESS_WITH_INFO) { ! 659: break; ! 660: } ! 661: } ! 662: ! 663: if (C->fetched_len == SQL_NO_TOTAL) { ! 664: used += alloced - used; ! 665: } else { ! 666: used += C->fetched_len; ! 667: } ! 668: ! 669: if (rc == SQL_SUCCESS) { ! 670: /* this was the final fetch */ ! 671: break; ! 672: } ! 673: ! 674: /* we need to fetch another chunk; resize the ! 675: * buffer */ ! 676: alloced *= 2; ! 677: buf = erealloc(buf, alloced); ! 678: } while (1); ! 679: ! 680: /* size down */ ! 681: if (used < alloced - 1024) { ! 682: alloced = used+1; ! 683: buf = erealloc(buf, used+1); ! 684: } ! 685: buf[used] = '\0'; ! 686: *ptr = buf; ! 687: *caller_frees = 1; ! 688: *len = used; ! 689: if (C->is_unicode) { ! 690: goto unicode_conv; ! 691: } ! 692: return 1; ! 693: } ! 694: ! 695: /* something went caca */ ! 696: *ptr = NULL; ! 697: *len = 0; ! 698: return 1; ! 699: } ! 700: ! 701: in_data: ! 702: /* check the indicator to ensure that the data is intact */ ! 703: if (C->fetched_len == SQL_NULL_DATA) { ! 704: /* A NULL value */ ! 705: *ptr = NULL; ! 706: *len = 0; ! 707: return 1; ! 708: } else if (C->fetched_len >= 0) { ! 709: /* it was stored perfectly */ ! 710: *ptr = C->data; ! 711: *len = C->fetched_len; ! 712: if (C->is_unicode) { ! 713: goto unicode_conv; ! 714: } ! 715: return 1; ! 716: } else { ! 717: /* no data? */ ! 718: *ptr = NULL; ! 719: *len = 0; ! 720: return 1; ! 721: } ! 722: ! 723: unicode_conv: ! 724: switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) { ! 725: case PDO_ODBC_CONV_FAIL: ! 726: /* oh well. They can have the binary version of it */ ! 727: case PDO_ODBC_CONV_NOT_REQUIRED: ! 728: /* shouldn't happen... */ ! 729: return 1; ! 730: ! 731: case PDO_ODBC_CONV_OK: ! 732: if (*caller_frees) { ! 733: efree(*ptr); ! 734: } ! 735: *ptr = emalloc(ulen + 1); ! 736: *len = ulen; ! 737: memcpy(*ptr, S->convbuf, ulen+1); ! 738: *caller_frees = 1; ! 739: return 1; ! 740: } ! 741: return 1; ! 742: } ! 743: ! 744: static int odbc_stmt_set_param(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC) ! 745: { ! 746: SQLRETURN rc; ! 747: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 748: ! 749: switch (attr) { ! 750: case PDO_ATTR_CURSOR_NAME: ! 751: convert_to_string(val); ! 752: rc = SQLSetCursorName(S->stmt, Z_STRVAL_P(val), Z_STRLEN_P(val)); ! 753: ! 754: if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { ! 755: return 1; ! 756: } ! 757: pdo_odbc_stmt_error("SQLSetCursorName"); ! 758: return 0; ! 759: ! 760: case PDO_ODBC_ATTR_ASSUME_UTF8: ! 761: S->assume_utf8 = zval_is_true(val); ! 762: return 0; ! 763: default: ! 764: strcpy(S->einfo.last_err_msg, "Unknown Attribute"); ! 765: S->einfo.what = "setAttribute"; ! 766: strcpy(S->einfo.last_state, "IM001"); ! 767: return -1; ! 768: } ! 769: } ! 770: ! 771: static int odbc_stmt_get_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC) ! 772: { ! 773: SQLRETURN rc; ! 774: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 775: ! 776: switch (attr) { ! 777: case PDO_ATTR_CURSOR_NAME: ! 778: { ! 779: char buf[256]; ! 780: SQLSMALLINT len = 0; ! 781: rc = SQLGetCursorName(S->stmt, buf, sizeof(buf), &len); ! 782: ! 783: if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { ! 784: ZVAL_STRINGL(val, buf, len, 1); ! 785: return 1; ! 786: } ! 787: pdo_odbc_stmt_error("SQLGetCursorName"); ! 788: return 0; ! 789: } ! 790: ! 791: case PDO_ODBC_ATTR_ASSUME_UTF8: ! 792: ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0); ! 793: return 0; ! 794: ! 795: default: ! 796: strcpy(S->einfo.last_err_msg, "Unknown Attribute"); ! 797: S->einfo.what = "getAttribute"; ! 798: strcpy(S->einfo.last_state, "IM001"); ! 799: return -1; ! 800: } ! 801: } ! 802: ! 803: static int odbc_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) ! 804: { ! 805: SQLRETURN rc; ! 806: SQLSMALLINT colcount; ! 807: pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; ! 808: ! 809: /* NOTE: can't guarantee that output or input/output parameters ! 810: * are set until this fella returns SQL_NO_DATA, according to ! 811: * MSDN ODBC docs */ ! 812: rc = SQLMoreResults(S->stmt); ! 813: ! 814: if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ! 815: return 0; ! 816: } ! 817: ! 818: free_cols(stmt, S TSRMLS_CC); ! 819: /* how many columns do we have ? */ ! 820: SQLNumResultCols(S->stmt, &colcount); ! 821: stmt->column_count = (int)colcount; ! 822: S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); ! 823: S->going_long = 0; ! 824: ! 825: return 1; ! 826: } ! 827: ! 828: struct pdo_stmt_methods odbc_stmt_methods = { ! 829: odbc_stmt_dtor, ! 830: odbc_stmt_execute, ! 831: odbc_stmt_fetch, ! 832: odbc_stmt_describe, ! 833: odbc_stmt_get_col, ! 834: odbc_stmt_param_hook, ! 835: odbc_stmt_set_param, ! 836: odbc_stmt_get_attr, /* get attr */ ! 837: NULL, /* get column meta */ ! 838: odbc_stmt_next_rowset ! 839: }; ! 840: ! 841: /* ! 842: * Local variables: ! 843: * tab-width: 4 ! 844: * c-basic-offset: 4 ! 845: * End: ! 846: * vim600: noet sw=4 ts=4 fdm=marker ! 847: * vim<600: noet sw=4 ts=4 ! 848: */