Annotation of embedaddon/php/ext/pdo_odbc/odbc_stmt.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_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: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>