Back to index

php5  5.3.10
odbc_stmt.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_stmt.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 
00033 enum pdo_odbc_conv_result {
00034        PDO_ODBC_CONV_NOT_REQUIRED,
00035        PDO_ODBC_CONV_OK,
00036        PDO_ODBC_CONV_FAIL
00037 };
00038 
00039 static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype)
00040 {
00041        if (!S->assume_utf8) return 0;
00042        switch (sqltype) {
00043 #ifdef SQL_WCHAR
00044               case SQL_WCHAR:
00045                      return 1;
00046 #endif
00047 #ifdef SQL_WLONGVARCHAR
00048               case SQL_WLONGVARCHAR:
00049                      return 1;
00050 #endif
00051 #ifdef SQL_WVARCHAR
00052               case SQL_WVARCHAR:
00053                      return 1;
00054 #endif
00055               default:
00056                      return 0;
00057        }
00058 }
00059 
00060 static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf, 
00061        unsigned long buflen, unsigned long *outlen)
00062 {
00063 #ifdef PHP_WIN32
00064        if (is_unicode && buflen) {
00065               pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00066               DWORD ret;
00067 
00068               ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0);
00069               if (ret == 0) {
00070                      /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/
00071                      return PDO_ODBC_CONV_FAIL;
00072               }
00073 
00074               ret *= sizeof(WCHAR);
00075 
00076               if (S->convbufsize <= ret) {
00077                      S->convbufsize = ret + sizeof(WCHAR);
00078                      S->convbuf = erealloc(S->convbuf, S->convbufsize);
00079               }
00080               
00081               ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR));
00082               if (ret == 0) {
00083                      /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/
00084                      return PDO_ODBC_CONV_FAIL;
00085               }
00086 
00087               ret *= sizeof(WCHAR);
00088               *outlen = ret;
00089               return PDO_ODBC_CONV_OK;
00090        }
00091 #endif
00092        return PDO_ODBC_CONV_NOT_REQUIRED;
00093 }
00094 
00095 static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, const char *buf, 
00096        unsigned long buflen, unsigned long *outlen)
00097 {
00098 #ifdef PHP_WIN32
00099        if (is_unicode && buflen) {
00100               pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00101               DWORD ret;
00102 
00103               ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), NULL, 0, NULL, NULL);
00104               if (ret == 0) {
00105                      return PDO_ODBC_CONV_FAIL;
00106               }
00107 
00108               if (S->convbufsize <= ret) {
00109                      S->convbufsize = ret + 1;
00110                      S->convbuf = erealloc(S->convbuf, S->convbufsize);
00111               }
00112               
00113               ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), S->convbuf, S->convbufsize, NULL, NULL);
00114               if (ret == 0) {
00115                      return PDO_ODBC_CONV_FAIL;
00116               }
00117 
00118               *outlen = ret;
00119               S->convbuf[*outlen] = '\0';
00120               return PDO_ODBC_CONV_OK;
00121        }
00122 #endif
00123        return PDO_ODBC_CONV_NOT_REQUIRED;
00124 }
00125 
00126 static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S TSRMLS_DC)
00127 {
00128        if (S->cols) {
00129               int i;
00130 
00131               for (i = 0; i < stmt->column_count; i++) {
00132                      if (S->cols[i].data) {
00133                             efree(S->cols[i].data);
00134                      }
00135               }
00136               efree(S->cols);
00137               S->cols = NULL;
00138        }
00139 }
00140 
00141 static int odbc_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
00142 {
00143        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00144 
00145        if (S->stmt != SQL_NULL_HANDLE) {
00146               if (stmt->executed) {
00147                      SQLCloseCursor(S->stmt);
00148               }
00149               SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
00150               S->stmt = SQL_NULL_HANDLE;
00151        }
00152 
00153        free_cols(stmt, S TSRMLS_CC);
00154        if (S->convbuf) {
00155               efree(S->convbuf);
00156        }
00157        efree(S);
00158 
00159        return 1;
00160 }
00161 
00162 static int odbc_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
00163 {
00164        RETCODE rc;
00165        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00166        char *buf = NULL;
00167        SQLLEN row_count = -1;
00168 
00169        if (stmt->executed) {
00170               SQLCloseCursor(S->stmt);
00171        }
00172        
00173        rc = SQLExecute(S->stmt);   
00174 
00175        while (rc == SQL_NEED_DATA) {
00176               struct pdo_bound_param_data *param;
00177 
00178               rc = SQLParamData(S->stmt, (SQLPOINTER*)&param);
00179               if (rc == SQL_NEED_DATA) {
00180                      php_stream *stm;
00181                      int len;
00182                      pdo_odbc_param *P;
00183        
00184                      P = (pdo_odbc_param*)param->driver_data;
00185                      if (Z_TYPE_P(param->parameter) != IS_RESOURCE) {
00186                             /* they passed in a string */
00187                             unsigned long ulen;
00188                             convert_to_string(param->parameter);
00189 
00190                             switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, 
00191                                                  Z_STRVAL_P(param->parameter),
00192                                                  Z_STRLEN_P(param->parameter),
00193                                                  &ulen)) {
00194                                    case PDO_ODBC_CONV_NOT_REQUIRED:
00195                                           SQLPutData(S->stmt, Z_STRVAL_P(param->parameter),
00196                                                  Z_STRLEN_P(param->parameter));
00197                                           break;
00198                                    case PDO_ODBC_CONV_OK:
00199                                           SQLPutData(S->stmt, S->convbuf, ulen);
00200                                           break;
00201                                    case PDO_ODBC_CONV_FAIL:
00202                                           pdo_odbc_stmt_error("error converting input string");
00203                                           SQLCloseCursor(S->stmt);
00204                                           if (buf) {
00205                                                  efree(buf);
00206                                           }
00207                                           return 0;
00208                             }
00209                             continue;
00210                      }
00211 
00212                      /* we assume that LOBs are binary and don't need charset
00213                       * conversion */
00214 
00215                      php_stream_from_zval_no_verify(stm, &param->parameter);
00216                      if (!stm) {
00217                             /* shouldn't happen either */
00218                             pdo_odbc_stmt_error("input LOB is no longer a stream");
00219                             SQLCloseCursor(S->stmt);
00220                             if (buf) {
00221                                    efree(buf);
00222                             }
00223                             return 0;
00224                      }
00225 
00226                      /* now suck data from the stream and stick it into the database */
00227                      if (buf == NULL) {
00228                             buf = emalloc(8192);
00229                      }
00230 
00231                      do {
00232                             len = php_stream_read(stm, buf, 8192);
00233                             if (len == 0) {
00234                                    break;
00235                             }
00236                             SQLPutData(S->stmt, buf, len);
00237                      } while (1);
00238               }
00239        }
00240 
00241        if (buf) {
00242               efree(buf);
00243        }
00244 
00245        switch (rc) {
00246               case SQL_SUCCESS:
00247                      break;
00248               case SQL_NO_DATA_FOUND:
00249               case SQL_SUCCESS_WITH_INFO:
00250                      pdo_odbc_stmt_error("SQLExecute");
00251                      break;
00252 
00253               default:
00254                      pdo_odbc_stmt_error("SQLExecute");
00255                      return 0;
00256        }
00257 
00258        SQLRowCount(S->stmt, &row_count);
00259        stmt->row_count = row_count;
00260 
00261        if (!stmt->executed) {
00262               /* do first-time-only definition of bind/mapping stuff */
00263               SQLSMALLINT colcount;
00264 
00265               /* how many columns do we have ? */
00266               SQLNumResultCols(S->stmt, &colcount);
00267 
00268               stmt->column_count = (int)colcount;
00269               S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
00270               S->going_long = 0;
00271        }
00272 
00273        return 1;
00274 }
00275 
00276 static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
00277               enum pdo_param_event event_type TSRMLS_DC)
00278 {
00279        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00280        RETCODE rc;
00281        SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0;
00282        UDWORD precision = 0;
00283        pdo_odbc_param *P;
00284        
00285        /* we're only interested in parameters for prepared SQL right now */
00286        if (param->is_param) {
00287 
00288               switch (event_type) {
00289                      case PDO_PARAM_EVT_FREE:
00290                             P = param->driver_data;
00291                             if (P) {
00292                                    efree(P);
00293                             }
00294                             break;
00295 
00296                      case PDO_PARAM_EVT_ALLOC:
00297                      {
00298                             /* figure out what we're doing */
00299                             switch (PDO_PARAM_TYPE(param->param_type)) {
00300                                    case PDO_PARAM_LOB:
00301                                           break;
00302 
00303                                    case PDO_PARAM_STMT:
00304                                           return 0;
00305                                    
00306                                    default:
00307                                           break;
00308                             }
00309 
00310                             rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable);
00311                             if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00312                                    /* MS Access, for instance, doesn't support SQLDescribeParam,
00313                                     * so we need to guess */
00314                                    sqltype = PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB ?
00315                                                                SQL_LONGVARBINARY :
00316                                                                SQL_LONGVARCHAR;
00317                                    precision = 4000;
00318                                    scale = 5;
00319                                    nullable = 1;
00320 
00321                                    if (param->max_value_len > 0) {
00322                                           precision = param->max_value_len;
00323                                    }
00324                             }
00325                             if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {
00326                                    ctype = SQL_C_BINARY;
00327                             } else {
00328                                    ctype = SQL_C_CHAR;
00329                             }
00330 
00331                             P = emalloc(sizeof(*P));
00332                             param->driver_data = P;
00333 
00334                             P->len = 0; /* is re-populated each EXEC_PRE */
00335                             P->outbuf = NULL;
00336 
00337                             P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);
00338                             if (P->is_unicode) {
00339                                    /* avoid driver auto-translation: we'll do it ourselves */
00340                                    ctype = SQL_C_BINARY;
00341                             }
00342 
00343                             if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {
00344                                    P->paramtype = SQL_PARAM_INPUT_OUTPUT;
00345                             } else if (param->max_value_len <= 0) {
00346                                    P->paramtype = SQL_PARAM_INPUT;
00347                             } else {
00348                                    P->paramtype = SQL_PARAM_OUTPUT;
00349                             }
00350                             
00351                             if (P->paramtype != SQL_PARAM_INPUT) {
00352                                    if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {
00353                                           /* need an explicit buffer to hold result */
00354                                           P->len = param->max_value_len > 0 ? param->max_value_len : precision;
00355                                           if (P->is_unicode) {
00356                                                  P->len *= 2;
00357                                           }
00358                                           P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));
00359                                    }
00360                             }
00361                             
00362                             if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {
00363                                    pdo_odbc_stmt_error("Can't bind a lob for output");
00364                                    return 0;
00365                             }
00366 
00367                             rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1,
00368                                           P->paramtype, ctype, sqltype, precision, scale,
00369                                           P->paramtype == SQL_PARAM_INPUT ? 
00370                                                  (SQLPOINTER)param :
00371                                                  P->outbuf,
00372                                           P->len,
00373                                           &P->len
00374                                           );
00375        
00376                             if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
00377                                    return 1;
00378                             }
00379                             pdo_odbc_stmt_error("SQLBindParameter");
00380                             return 0;
00381                      }
00382 
00383                      case PDO_PARAM_EVT_EXEC_PRE:
00384                             P = param->driver_data;
00385                             if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
00386                                    if (Z_TYPE_P(param->parameter) == IS_RESOURCE) {
00387                                           php_stream *stm;
00388                                           php_stream_statbuf sb;
00389 
00390                                           php_stream_from_zval_no_verify(stm, &param->parameter);
00391 
00392                                           if (!stm) {
00393                                                  return 0;
00394                                           }
00395 
00396                                           if (0 == php_stream_stat(stm, &sb)) {
00397                                                  if (P->outbuf) {
00398                                                         int len, amount;
00399                                                         char *ptr = P->outbuf;
00400                                                         char *end = P->outbuf + P->len;
00401 
00402                                                         P->len = 0;
00403                                                         do {
00404                                                                amount = end - ptr;
00405                                                                if (amount == 0) {
00406                                                                       break;
00407                                                                }
00408                                                                if (amount > 8192)
00409                                                                       amount = 8192;
00410                                                                len = php_stream_read(stm, ptr, amount);
00411                                                                if (len == 0) {
00412                                                                       break;
00413                                                                }
00414                                                                ptr += len;
00415                                                                P->len += len;
00416                                                         } while (1);
00417 
00418                                                  } else {
00419                                                         P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);
00420                                                  }
00421                                           } else {
00422                                                  if (P->outbuf) {
00423                                                         P->len = 0;
00424                                                  } else {
00425                                                         P->len = SQL_LEN_DATA_AT_EXEC(0);
00426                                                  }
00427                                           }
00428                                    } else {
00429                                           convert_to_string(param->parameter);
00430                                           if (P->outbuf) {
00431                                                  P->len = Z_STRLEN_P(param->parameter);
00432                                                  memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
00433                                           } else {
00434                                                  P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
00435                                           }
00436                                    }
00437                             } else if (Z_TYPE_P(param->parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {
00438                                    P->len = SQL_NULL_DATA;
00439                             } else {
00440                                    convert_to_string(param->parameter);
00441                                    if (P->outbuf) {
00442                                           unsigned long ulen;
00443                                           switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
00444                                                         Z_STRVAL_P(param->parameter),
00445                                                         Z_STRLEN_P(param->parameter),
00446                                                         &ulen)) {
00447                                                  case PDO_ODBC_CONV_FAIL:
00448                                                  case PDO_ODBC_CONV_NOT_REQUIRED:
00449                                                         P->len = Z_STRLEN_P(param->parameter);
00450                                                         memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
00451                                                         break;
00452                                                  case PDO_ODBC_CONV_OK:
00453                                                         P->len = ulen;
00454                                                         memcpy(P->outbuf, S->convbuf, P->len);
00455                                                         break;
00456                                           }
00457                                    } else {
00458                                           P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
00459                                    }
00460                             }
00461                             return 1;
00462                      
00463                      case PDO_PARAM_EVT_EXEC_POST:
00464                             P = param->driver_data;
00465                             if (P->outbuf) {
00466                                    if (P->outbuf) {
00467                                           unsigned long ulen;
00468                                           char *srcbuf;
00469                                           unsigned long srclen;
00470 
00471                                           switch (P->len) {
00472                                                  case SQL_NULL_DATA:
00473                                                         zval_dtor(param->parameter);
00474                                                         ZVAL_NULL(param->parameter);
00475                                                         break;
00476                                                  default:
00477                                                         convert_to_string(param->parameter);
00478                                                         switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) {
00479                                                                case PDO_ODBC_CONV_FAIL:
00480                                                                       /* something fishy, but allow it to come back as binary */
00481                                                                case PDO_ODBC_CONV_NOT_REQUIRED:
00482                                                                       srcbuf = P->outbuf;
00483                                                                       srclen = P->len;
00484                                                                       break;
00485                                                                case PDO_ODBC_CONV_OK:
00486                                                                       srcbuf = S->convbuf;
00487                                                                       srclen = ulen;
00488                                                                       break;
00489                                                         }
00490                                                                       
00491                                                         Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), srclen+1);
00492                                                         memcpy(Z_STRVAL_P(param->parameter), srcbuf, srclen);
00493                                                         Z_STRLEN_P(param->parameter) = srclen;
00494                                                         Z_STRVAL_P(param->parameter)[srclen] = '\0';
00495                                           }
00496                                    }
00497                             }
00498                             return 1;
00499               }
00500        }
00501        return 1;
00502 }
00503 
00504 static int odbc_stmt_fetch(pdo_stmt_t *stmt,
00505        enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
00506 {
00507        RETCODE rc;
00508        SQLSMALLINT odbcori;
00509        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00510 
00511        switch (ori) {
00512               case PDO_FETCH_ORI_NEXT:    odbcori = SQL_FETCH_NEXT; break;
00513               case PDO_FETCH_ORI_PRIOR:   odbcori = SQL_FETCH_PRIOR; break;
00514               case PDO_FETCH_ORI_FIRST:   odbcori = SQL_FETCH_FIRST; break;
00515               case PDO_FETCH_ORI_LAST:    odbcori = SQL_FETCH_LAST; break;
00516               case PDO_FETCH_ORI_ABS:            odbcori = SQL_FETCH_ABSOLUTE; break;
00517               case PDO_FETCH_ORI_REL:            odbcori = SQL_FETCH_RELATIVE; break;
00518               default: 
00519                      strcpy(stmt->error_code, "HY106");
00520                      return 0;
00521        }
00522        rc = SQLFetchScroll(S->stmt, odbcori, offset);
00523 
00524        if (rc == SQL_SUCCESS) {
00525               return 1;
00526        }
00527        if (rc == SQL_SUCCESS_WITH_INFO) {
00528               pdo_odbc_stmt_error("SQLFetchScroll");
00529               return 1;
00530        }
00531 
00532        if (rc == SQL_NO_DATA) {
00533               /* pdo_odbc_stmt_error("SQLFetchScroll"); */
00534               return 0;
00535        }
00536 
00537        pdo_odbc_stmt_error("SQLFetchScroll");
00538 
00539        return 0;
00540 }
00541 
00542 static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
00543 {
00544        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00545        struct pdo_column_data *col = &stmt->columns[colno];
00546        zend_bool dyn = FALSE;
00547        RETCODE rc;
00548        SWORD  colnamelen;
00549        SDWORD colsize, displaysize;
00550 
00551        rc = SQLDescribeCol(S->stmt, colno+1, S->cols[colno].colname,
00552                      sizeof(S->cols[colno].colname)-1, &colnamelen,
00553                      &S->cols[colno].coltype, &colsize, NULL, NULL);
00554 
00555        if (rc != SQL_SUCCESS) {
00556               pdo_odbc_stmt_error("SQLDescribeCol");
00557               if (rc != SQL_SUCCESS_WITH_INFO) {
00558                      return 0;
00559               }
00560        }
00561 
00562        rc = SQLColAttribute(S->stmt, colno+1,
00563                      SQL_DESC_DISPLAY_SIZE,
00564                      NULL, 0, NULL, &displaysize);
00565 
00566        if (rc != SQL_SUCCESS) {
00567               pdo_odbc_stmt_error("SQLColAttribute");
00568               if (rc != SQL_SUCCESS_WITH_INFO) {
00569                      return 0;
00570               }
00571        }
00572        colsize = displaysize;
00573 
00574        col->maxlen = S->cols[colno].datalen = colsize;
00575        col->namelen = colnamelen;
00576        col->name = estrdup(S->cols[colno].colname);
00577        S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);
00578 
00579        /* returning data as a string */
00580        col->param_type = PDO_PARAM_STR;
00581 
00582        /* tell ODBC to put it straight into our buffer, but only if it
00583         * isn't "long" data, and only if we haven't already bound a long
00584         * column. */
00585        if (colsize < 256 && !S->going_long) {
00586               S->cols[colno].data = emalloc(colsize+1);
00587               S->cols[colno].is_long = 0;
00588 
00589               rc = SQLBindCol(S->stmt, colno+1,
00590                      S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
00591                      S->cols[colno].data,
00592                      S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
00593 
00594               if (rc != SQL_SUCCESS) {
00595                      pdo_odbc_stmt_error("SQLBindCol");
00596                      return 0;
00597               }
00598        } else {
00599               /* allocate a smaller buffer to keep around for smaller
00600                * "long" columns */
00601               S->cols[colno].data = emalloc(256);
00602               S->going_long = 1;
00603               S->cols[colno].is_long = 1;
00604        }
00605 
00606        return 1;
00607 }
00608 
00609 static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
00610 {
00611        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00612        pdo_odbc_column *C = &S->cols[colno];
00613        unsigned long ulen;
00614 
00615        /* if it is a column containing "long" data, perform late binding now */
00616        if (C->is_long) {
00617               unsigned long alloced = 4096;
00618               unsigned long used = 0;
00619               char *buf;
00620               RETCODE rc;
00621 
00622               /* fetch it into C->data, which is allocated with a length
00623                * of 256 bytes; if there is more to be had, we then allocate
00624                * bigger buffer for the caller to free */
00625 
00626               rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
00627                      256, &C->fetched_len);
00628 
00629               if (rc == SQL_SUCCESS) {
00630                      /* all the data fit into our little buffer;
00631                       * jump down to the generic bound data case */
00632                      goto in_data;
00633               }
00634 
00635               if (rc == SQL_SUCCESS_WITH_INFO) {
00636                      /* promote up to a bigger buffer */
00637 
00638                      if (C->fetched_len != SQL_NO_TOTAL) {
00639                             /* use size suggested by the driver, if it knows it */
00640                             alloced = C->fetched_len + 1;
00641                      }
00642                      
00643                      buf = emalloc(alloced);
00644                      memcpy(buf, C->data, 256);
00645                      used = 255; /* not 256; the driver NUL terminated the buffer */
00646 
00647                      do {
00648                             C->fetched_len = 0;
00649                             rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR,
00650                                    buf + used, alloced - used,
00651                                    &C->fetched_len);
00652 
00653                             if (rc == SQL_NO_DATA) {
00654                                    /* we got the lot */
00655                                    break;
00656                             } else if (rc != SQL_SUCCESS) {
00657                                    pdo_odbc_stmt_error("SQLGetData");
00658                                    if (rc != SQL_SUCCESS_WITH_INFO) {
00659                                           break;
00660                                    }
00661                             }
00662 
00663                             if (C->fetched_len == SQL_NO_TOTAL) {
00664                                    used += alloced - used;
00665                             } else {
00666                                    used += C->fetched_len;
00667                             }
00668 
00669                             if (rc == SQL_SUCCESS) {
00670                                    /* this was the final fetch */
00671                                    break;
00672                             }
00673 
00674                             /* we need to fetch another chunk; resize the
00675                              * buffer */
00676                             alloced *= 2;
00677                             buf = erealloc(buf, alloced);
00678                      } while (1);
00679 
00680                      /* size down */
00681                      if (used < alloced - 1024) {
00682                             alloced = used+1;
00683                             buf = erealloc(buf, used+1);
00684                      }
00685                      buf[used] = '\0';
00686                      *ptr = buf;
00687                      *caller_frees = 1;
00688                      *len = used;
00689                      if (C->is_unicode) {
00690                             goto unicode_conv;
00691                      }
00692                      return 1;
00693               }
00694 
00695               /* something went caca */
00696               *ptr = NULL;
00697               *len = 0;
00698               return 1;
00699        }
00700 
00701 in_data:
00702        /* check the indicator to ensure that the data is intact */
00703        if (C->fetched_len == SQL_NULL_DATA) {
00704               /* A NULL value */
00705               *ptr = NULL;
00706               *len = 0;
00707               return 1;
00708        } else if (C->fetched_len >= 0) {
00709               /* it was stored perfectly */
00710               *ptr = C->data;
00711               *len = C->fetched_len;
00712               if (C->is_unicode) {
00713                      goto unicode_conv;
00714               }
00715               return 1;
00716        } else {
00717               /* no data? */
00718               *ptr = NULL;
00719               *len = 0;
00720               return 1;
00721        }
00722 
00723        unicode_conv:
00724        switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) {
00725               case PDO_ODBC_CONV_FAIL:
00726                      /* oh well.  They can have the binary version of it */
00727               case PDO_ODBC_CONV_NOT_REQUIRED:
00728                      /* shouldn't happen... */
00729                      return 1;
00730 
00731               case PDO_ODBC_CONV_OK:
00732                      if (*caller_frees) {
00733                             efree(*ptr);
00734                      }
00735                      *ptr = emalloc(ulen + 1);
00736                      *len = ulen;
00737                      memcpy(*ptr, S->convbuf, ulen+1);
00738                      *caller_frees = 1;
00739                      return 1;
00740        }
00741        return 1;
00742 }
00743 
00744 static int odbc_stmt_set_param(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
00745 {
00746        SQLRETURN rc;
00747        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00748 
00749        switch (attr) {
00750               case PDO_ATTR_CURSOR_NAME:
00751                      convert_to_string(val);
00752                      rc = SQLSetCursorName(S->stmt, Z_STRVAL_P(val), Z_STRLEN_P(val));
00753 
00754                      if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
00755                             return 1;
00756                      }
00757                      pdo_odbc_stmt_error("SQLSetCursorName");
00758                      return 0;
00759 
00760               case PDO_ODBC_ATTR_ASSUME_UTF8:
00761                      S->assume_utf8 = zval_is_true(val);
00762                      return 0;
00763               default:
00764                      strcpy(S->einfo.last_err_msg, "Unknown Attribute");
00765                      S->einfo.what = "setAttribute";
00766                      strcpy(S->einfo.last_state, "IM001");
00767                      return -1;
00768        }
00769 }
00770 
00771 static int odbc_stmt_get_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
00772 {
00773        SQLRETURN rc;
00774        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00775 
00776        switch (attr) {
00777               case PDO_ATTR_CURSOR_NAME:
00778               {
00779                      char buf[256];
00780                      SQLSMALLINT len = 0;
00781                      rc = SQLGetCursorName(S->stmt, buf, sizeof(buf), &len);
00782 
00783                      if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
00784                             ZVAL_STRINGL(val, buf, len, 1);
00785                             return 1;
00786                      }
00787                      pdo_odbc_stmt_error("SQLGetCursorName");
00788                      return 0;
00789               }
00790 
00791               case PDO_ODBC_ATTR_ASSUME_UTF8:
00792                      ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
00793                      return 0;
00794 
00795               default:
00796                      strcpy(S->einfo.last_err_msg, "Unknown Attribute");
00797                      S->einfo.what = "getAttribute";
00798                      strcpy(S->einfo.last_state, "IM001");
00799                      return -1;
00800        }
00801 }
00802 
00803 static int odbc_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
00804 {
00805        SQLRETURN rc;
00806        SQLSMALLINT colcount;
00807        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
00808 
00809        /* NOTE: can't guarantee that output or input/output parameters
00810         * are set until this fella returns SQL_NO_DATA, according to
00811         * MSDN ODBC docs */
00812        rc = SQLMoreResults(S->stmt);
00813 
00814        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
00815               return 0;
00816        }
00817 
00818        free_cols(stmt, S TSRMLS_CC);
00819        /* how many columns do we have ? */
00820        SQLNumResultCols(S->stmt, &colcount);
00821        stmt->column_count = (int)colcount;
00822        S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
00823        S->going_long = 0;
00824 
00825        return 1;
00826 }
00827 
00828 struct pdo_stmt_methods odbc_stmt_methods = {
00829        odbc_stmt_dtor,
00830        odbc_stmt_execute,
00831        odbc_stmt_fetch,
00832        odbc_stmt_describe,
00833        odbc_stmt_get_col,
00834        odbc_stmt_param_hook,
00835        odbc_stmt_set_param,
00836        odbc_stmt_get_attr, /* get attr */
00837        NULL, /* get column meta */
00838        odbc_stmt_next_rowset
00839 };
00840 
00841 /*
00842  * Local variables:
00843  * tab-width: 4
00844  * c-basic-offset: 4
00845  * End:
00846  * vim600: noet sw=4 ts=4 fdm=marker
00847  * vim<600: noet sw=4 ts=4
00848  */