File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / pdo_odbc / odbc_stmt.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 08:02:28 2013 UTC (10 years, 9 months ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29p0, v5_4_20p0, v5_4_20, HEAD
v 5.4.20

    1: /*
    2:   +----------------------------------------------------------------------+
    3:   | PHP Version 5                                                        |
    4:   +----------------------------------------------------------------------+
    5:   | Copyright (c) 1997-2013 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,v 1.1.1.4 2013/10/14 08:02:28 misho Exp $ */
   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*)&param);
  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, &param->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_FETCH_PRE:
  290: 			case PDO_PARAM_EVT_FETCH_POST:
  291: 			case PDO_PARAM_EVT_NORMALIZE:
  292: 				/* Do nothing */
  293: 				break;
  294: 
  295: 			case PDO_PARAM_EVT_FREE:
  296: 				P = param->driver_data;
  297: 				if (P) {
  298: 					efree(P);
  299: 				}
  300: 				break;
  301: 
  302: 			case PDO_PARAM_EVT_ALLOC:
  303: 			{
  304: 				/* figure out what we're doing */
  305: 				switch (PDO_PARAM_TYPE(param->param_type)) {
  306: 					case PDO_PARAM_LOB:
  307: 						break;
  308: 
  309: 					case PDO_PARAM_STMT:
  310: 						return 0;
  311: 					
  312: 					default:
  313: 						break;
  314: 				}
  315: 
  316: 				rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable);
  317: 				if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  318: 					/* MS Access, for instance, doesn't support SQLDescribeParam,
  319: 					 * so we need to guess */
  320: 					sqltype = PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB ?
  321: 									SQL_LONGVARBINARY :
  322: 									SQL_LONGVARCHAR;
  323: 					precision = 4000;
  324: 					scale = 5;
  325: 					nullable = 1;
  326: 
  327: 					if (param->max_value_len > 0) {
  328: 						precision = param->max_value_len;
  329: 					}
  330: 				}
  331: 				if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {
  332: 					ctype = SQL_C_BINARY;
  333: 				} else {
  334: 					ctype = SQL_C_CHAR;
  335: 				}
  336: 
  337: 				P = emalloc(sizeof(*P));
  338: 				param->driver_data = P;
  339: 
  340: 				P->len = 0; /* is re-populated each EXEC_PRE */
  341: 				P->outbuf = NULL;
  342: 
  343: 				P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);
  344: 				if (P->is_unicode) {
  345: 					/* avoid driver auto-translation: we'll do it ourselves */
  346: 					ctype = SQL_C_BINARY;
  347: 				}
  348: 
  349: 				if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {
  350: 					P->paramtype = SQL_PARAM_INPUT_OUTPUT;
  351: 				} else if (param->max_value_len <= 0) {
  352: 					P->paramtype = SQL_PARAM_INPUT;
  353: 				} else {
  354: 					P->paramtype = SQL_PARAM_OUTPUT;
  355: 				}
  356: 				
  357: 				if (P->paramtype != SQL_PARAM_INPUT) {
  358: 					if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {
  359: 						/* need an explicit buffer to hold result */
  360: 						P->len = param->max_value_len > 0 ? param->max_value_len : precision;
  361: 						if (P->is_unicode) {
  362: 							P->len *= 2;
  363: 						}
  364: 						P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));
  365: 					}
  366: 				}
  367: 				
  368: 				if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {
  369: 					pdo_odbc_stmt_error("Can't bind a lob for output");
  370: 					return 0;
  371: 				}
  372: 
  373: 				rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1,
  374: 						P->paramtype, ctype, sqltype, precision, scale,
  375: 						P->paramtype == SQL_PARAM_INPUT ? 
  376: 							(SQLPOINTER)param :
  377: 							P->outbuf,
  378: 						P->len,
  379: 						&P->len
  380: 						);
  381: 	
  382: 				if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
  383: 					return 1;
  384: 				}
  385: 				pdo_odbc_stmt_error("SQLBindParameter");
  386: 				return 0;
  387: 			}
  388: 
  389: 			case PDO_PARAM_EVT_EXEC_PRE:
  390: 				P = param->driver_data;
  391: 				if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
  392: 					if (Z_TYPE_P(param->parameter) == IS_RESOURCE) {
  393: 						php_stream *stm;
  394: 						php_stream_statbuf sb;
  395: 
  396: 						php_stream_from_zval_no_verify(stm, &param->parameter);
  397: 
  398: 						if (!stm) {
  399: 							return 0;
  400: 						}
  401: 
  402: 						if (0 == php_stream_stat(stm, &sb)) {
  403: 							if (P->outbuf) {
  404: 								int len, amount;
  405: 								char *ptr = P->outbuf;
  406: 								char *end = P->outbuf + P->len;
  407: 
  408: 								P->len = 0;
  409: 								do {
  410: 									amount = end - ptr;
  411: 									if (amount == 0) {
  412: 										break;
  413: 									}
  414: 									if (amount > 8192)
  415: 										amount = 8192;
  416: 									len = php_stream_read(stm, ptr, amount);
  417: 									if (len == 0) {
  418: 										break;
  419: 									}
  420: 									ptr += len;
  421: 									P->len += len;
  422: 								} while (1);
  423: 
  424: 							} else {
  425: 								P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);
  426: 							}
  427: 						} else {
  428: 							if (P->outbuf) {
  429: 								P->len = 0;
  430: 							} else {
  431: 								P->len = SQL_LEN_DATA_AT_EXEC(0);
  432: 							}
  433: 						}
  434: 					} else {
  435: 						convert_to_string(param->parameter);
  436: 						if (P->outbuf) {
  437: 							P->len = Z_STRLEN_P(param->parameter);
  438: 							memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
  439: 						} else {
  440: 							P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
  441: 						}
  442: 					}
  443: 				} else if (Z_TYPE_P(param->parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {
  444: 					P->len = SQL_NULL_DATA;
  445: 				} else {
  446: 					convert_to_string(param->parameter);
  447: 					if (P->outbuf) {
  448: 						unsigned long ulen;
  449: 						switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
  450: 								Z_STRVAL_P(param->parameter),
  451: 								Z_STRLEN_P(param->parameter),
  452: 								&ulen)) {
  453: 							case PDO_ODBC_CONV_FAIL:
  454: 							case PDO_ODBC_CONV_NOT_REQUIRED:
  455: 								P->len = Z_STRLEN_P(param->parameter);
  456: 								memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
  457: 								break;
  458: 							case PDO_ODBC_CONV_OK:
  459: 								P->len = ulen;
  460: 								memcpy(P->outbuf, S->convbuf, P->len);
  461: 								break;
  462: 						}
  463: 					} else {
  464: 						P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
  465: 					}
  466: 				}
  467: 				return 1;
  468: 			
  469: 			case PDO_PARAM_EVT_EXEC_POST:
  470: 				P = param->driver_data;
  471: 				if (P->outbuf) {
  472: 					if (P->outbuf) {
  473: 						unsigned long ulen;
  474: 						char *srcbuf;
  475: 						unsigned long srclen;
  476: 
  477: 						switch (P->len) {
  478: 							case SQL_NULL_DATA:
  479: 								zval_dtor(param->parameter);
  480: 								ZVAL_NULL(param->parameter);
  481: 								break;
  482: 							default:
  483: 								convert_to_string(param->parameter);
  484: 								switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) {
  485: 									case PDO_ODBC_CONV_FAIL:
  486: 										/* something fishy, but allow it to come back as binary */
  487: 									case PDO_ODBC_CONV_NOT_REQUIRED:
  488: 										srcbuf = P->outbuf;
  489: 										srclen = P->len;
  490: 										break;
  491: 									case PDO_ODBC_CONV_OK:
  492: 										srcbuf = S->convbuf;
  493: 										srclen = ulen;
  494: 										break;
  495: 								}
  496: 										
  497: 								Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), srclen+1);
  498: 								memcpy(Z_STRVAL_P(param->parameter), srcbuf, srclen);
  499: 								Z_STRLEN_P(param->parameter) = srclen;
  500: 								Z_STRVAL_P(param->parameter)[srclen] = '\0';
  501: 						}
  502: 					}
  503: 				}
  504: 				return 1;
  505: 		}
  506: 	}
  507: 	return 1;
  508: }
  509: 
  510: static int odbc_stmt_fetch(pdo_stmt_t *stmt,
  511: 	enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
  512: {
  513: 	RETCODE rc;
  514: 	SQLSMALLINT odbcori;
  515: 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
  516: 
  517: 	switch (ori) {
  518: 		case PDO_FETCH_ORI_NEXT:	odbcori = SQL_FETCH_NEXT; break;
  519: 		case PDO_FETCH_ORI_PRIOR:	odbcori = SQL_FETCH_PRIOR; break;
  520: 		case PDO_FETCH_ORI_FIRST:	odbcori = SQL_FETCH_FIRST; break;
  521: 		case PDO_FETCH_ORI_LAST:	odbcori = SQL_FETCH_LAST; break;
  522: 		case PDO_FETCH_ORI_ABS:		odbcori = SQL_FETCH_ABSOLUTE; break;
  523: 		case PDO_FETCH_ORI_REL:		odbcori = SQL_FETCH_RELATIVE; break;
  524: 		default: 
  525: 			strcpy(stmt->error_code, "HY106");
  526: 			return 0;
  527: 	}
  528: 	rc = SQLFetchScroll(S->stmt, odbcori, offset);
  529: 
  530: 	if (rc == SQL_SUCCESS) {
  531: 		return 1;
  532: 	}
  533: 	if (rc == SQL_SUCCESS_WITH_INFO) {
  534: 		pdo_odbc_stmt_error("SQLFetchScroll");
  535: 		return 1;
  536: 	}
  537: 
  538: 	if (rc == SQL_NO_DATA) {
  539: 		/* pdo_odbc_stmt_error("SQLFetchScroll"); */
  540: 		return 0;
  541: 	}
  542: 
  543: 	pdo_odbc_stmt_error("SQLFetchScroll");
  544: 
  545: 	return 0;
  546: }
  547: 
  548: static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
  549: {
  550: 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
  551: 	struct pdo_column_data *col = &stmt->columns[colno];
  552: 	RETCODE rc;
  553: 	SWORD	colnamelen;
  554: 	SDWORD	colsize, displaysize;
  555: 
  556: 	rc = SQLDescribeCol(S->stmt, colno+1, S->cols[colno].colname,
  557: 			sizeof(S->cols[colno].colname)-1, &colnamelen,
  558: 			&S->cols[colno].coltype, &colsize, NULL, NULL);
  559: 
  560: 	if (rc != SQL_SUCCESS) {
  561: 		pdo_odbc_stmt_error("SQLDescribeCol");
  562: 		if (rc != SQL_SUCCESS_WITH_INFO) {
  563: 			return 0;
  564: 		}
  565: 	}
  566: 
  567: 	rc = SQLColAttribute(S->stmt, colno+1,
  568: 			SQL_DESC_DISPLAY_SIZE,
  569: 			NULL, 0, NULL, &displaysize);
  570: 
  571: 	if (rc != SQL_SUCCESS) {
  572: 		pdo_odbc_stmt_error("SQLColAttribute");
  573: 		if (rc != SQL_SUCCESS_WITH_INFO) {
  574: 			return 0;
  575: 		}
  576: 	}
  577: 	colsize = displaysize;
  578: 
  579: 	col->maxlen = S->cols[colno].datalen = colsize;
  580: 	col->namelen = colnamelen;
  581: 	col->name = estrdup(S->cols[colno].colname);
  582: 	S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);
  583: 
  584: 	/* returning data as a string */
  585: 	col->param_type = PDO_PARAM_STR;
  586: 
  587: 	/* tell ODBC to put it straight into our buffer, but only if it
  588: 	 * isn't "long" data, and only if we haven't already bound a long
  589: 	 * column. */
  590: 	if (colsize < 256 && !S->going_long) {
  591: 		S->cols[colno].data = emalloc(colsize+1);
  592: 		S->cols[colno].is_long = 0;
  593: 
  594: 		rc = SQLBindCol(S->stmt, colno+1,
  595: 			S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
  596: 			S->cols[colno].data,
  597:  			S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
  598: 
  599: 		if (rc != SQL_SUCCESS) {
  600: 			pdo_odbc_stmt_error("SQLBindCol");
  601: 			return 0;
  602: 		}
  603: 	} else {
  604: 		/* allocate a smaller buffer to keep around for smaller
  605: 		 * "long" columns */
  606: 		S->cols[colno].data = emalloc(256);
  607: 		S->going_long = 1;
  608: 		S->cols[colno].is_long = 1;
  609: 	}
  610: 
  611: 	return 1;
  612: }
  613: 
  614: static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
  615: {
  616: 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
  617: 	pdo_odbc_column *C = &S->cols[colno];
  618: 	unsigned long ulen;
  619: 
  620: 	/* if it is a column containing "long" data, perform late binding now */
  621: 	if (C->is_long) {
  622: 		unsigned long used = 0;
  623: 		char *buf;
  624: 		RETCODE rc;
  625: 
  626: 		/* fetch it into C->data, which is allocated with a length
  627: 		 * of 256 bytes; if there is more to be had, we then allocate
  628: 		 * bigger buffer for the caller to free */
  629: 
  630: 		rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
  631:  			256, &C->fetched_len);
  632: 
  633: 		if (rc == SQL_SUCCESS) {
  634: 			/* all the data fit into our little buffer;
  635: 			 * jump down to the generic bound data case */
  636: 			goto in_data;
  637: 		}
  638: 
  639: 		if (rc == SQL_SUCCESS_WITH_INFO) {
  640: 			/* this is a 'long column'
  641: 			
  642: 			 read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
  643: 			 in order into the output buffer
  644: 			
  645: 			 this loop has to work whether or not SQLGetData() provides the total column length.
  646: 			 calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
  647: 			 for that size would be slower except maybe for extremely long columns.*/
  648: 			char *buf2;
  649: 
  650: 			buf2 = emalloc(256);
  651: 			buf = estrndup(C->data, 256);
  652: 			used = 255; /* not 256; the driver NUL terminated the buffer */
  653: 			
  654: 			do {
  655: 				C->fetched_len = 0;
  656: 				/* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
  657: 				rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, buf2, 256, &C->fetched_len);
  658: 				
  659: 				/* resize output buffer and reassemble block */
  660: 				if (rc==SQL_SUCCESS_WITH_INFO) {
  661: 					/* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
  662: 					 states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size)
  663: 					 (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */
  664: 					buf = erealloc(buf, used + 255+1);
  665: 					memcpy(buf + used, buf2, 255);
  666: 					used = used + 255;
  667: 				} else if (rc==SQL_SUCCESS) {
  668: 					buf = erealloc(buf, used + C->fetched_len+1);
  669: 					memcpy(buf + used, buf2, C->fetched_len);
  670: 					used = used + C->fetched_len;
  671: 				} else {
  672: 					/* includes SQL_NO_DATA */
  673: 					break;
  674: 				}
  675: 				
  676: 			} while (1);
  677: 			
  678: 			efree(buf2);
  679: 			
  680: 			/* NULL terminate the buffer once, when finished, for use with the rest of PHP */
  681: 			buf[used] = '\0';
  682: 
  683: 			*ptr = buf;
  684: 			*caller_frees = 1;
  685: 			*len = used;
  686: 			if (C->is_unicode) {
  687: 				goto unicode_conv;
  688: 			}
  689: 			return 1;
  690: 		}
  691: 
  692: 		/* something went caca */
  693: 		*ptr = NULL;
  694: 		*len = 0;
  695: 		return 1;
  696: 	}
  697: 
  698: in_data:
  699: 	/* check the indicator to ensure that the data is intact */
  700: 	if (C->fetched_len == SQL_NULL_DATA) {
  701: 		/* A NULL value */
  702: 		*ptr = NULL;
  703: 		*len = 0;
  704: 		return 1;
  705: 	} else if (C->fetched_len >= 0) {
  706: 		/* it was stored perfectly */
  707: 		*ptr = C->data;
  708: 		*len = C->fetched_len;
  709: 		if (C->is_unicode) {
  710: 			goto unicode_conv;
  711: 		}
  712: 		return 1;
  713: 	} else {
  714: 		/* no data? */
  715: 		*ptr = NULL;
  716: 		*len = 0;
  717: 		return 1;
  718: 	}
  719: 
  720: 	unicode_conv:
  721: 	switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) {
  722: 		case PDO_ODBC_CONV_FAIL:
  723: 			/* oh well.  They can have the binary version of it */
  724: 		case PDO_ODBC_CONV_NOT_REQUIRED:
  725: 			/* shouldn't happen... */
  726: 			return 1;
  727: 
  728: 		case PDO_ODBC_CONV_OK:
  729: 			if (*caller_frees) {
  730: 				efree(*ptr);
  731: 			}
  732: 			*ptr = emalloc(ulen + 1);
  733: 			*len = ulen;
  734: 			memcpy(*ptr, S->convbuf, ulen+1);
  735: 			*caller_frees = 1;
  736: 			return 1;
  737: 	}
  738: 	return 1;
  739: }
  740: 
  741: static int odbc_stmt_set_param(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
  742: {
  743: 	SQLRETURN rc;
  744: 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
  745: 
  746: 	switch (attr) {
  747: 		case PDO_ATTR_CURSOR_NAME:
  748: 			convert_to_string(val);
  749: 			rc = SQLSetCursorName(S->stmt, Z_STRVAL_P(val), Z_STRLEN_P(val));
  750: 
  751: 			if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
  752: 				return 1;
  753: 			}
  754: 			pdo_odbc_stmt_error("SQLSetCursorName");
  755: 			return 0;
  756: 
  757: 		case PDO_ODBC_ATTR_ASSUME_UTF8:
  758: 			S->assume_utf8 = zval_is_true(val);
  759: 			return 0;
  760: 		default:
  761: 			strcpy(S->einfo.last_err_msg, "Unknown Attribute");
  762: 			S->einfo.what = "setAttribute";
  763: 			strcpy(S->einfo.last_state, "IM001");
  764: 			return -1;
  765: 	}
  766: }
  767: 
  768: static int odbc_stmt_get_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
  769: {
  770: 	SQLRETURN rc;
  771: 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
  772: 
  773: 	switch (attr) {
  774: 		case PDO_ATTR_CURSOR_NAME:
  775: 		{
  776: 			char buf[256];
  777: 			SQLSMALLINT len = 0;
  778: 			rc = SQLGetCursorName(S->stmt, buf, sizeof(buf), &len);
  779: 
  780: 			if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
  781: 				ZVAL_STRINGL(val, buf, len, 1);
  782: 				return 1;
  783: 			}
  784: 			pdo_odbc_stmt_error("SQLGetCursorName");
  785: 			return 0;
  786: 		}
  787: 
  788: 		case PDO_ODBC_ATTR_ASSUME_UTF8:
  789: 			ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
  790: 			return 0;
  791: 
  792: 		default:
  793: 			strcpy(S->einfo.last_err_msg, "Unknown Attribute");
  794: 			S->einfo.what = "getAttribute";
  795: 			strcpy(S->einfo.last_state, "IM001");
  796: 			return -1;
  797: 	}
  798: }
  799: 
  800: static int odbc_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
  801: {
  802: 	SQLRETURN rc;
  803: 	SQLSMALLINT colcount;
  804: 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
  805: 
  806: 	/* NOTE: can't guarantee that output or input/output parameters
  807: 	 * are set until this fella returns SQL_NO_DATA, according to
  808: 	 * MSDN ODBC docs */
  809: 	rc = SQLMoreResults(S->stmt);
  810: 
  811: 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  812: 		return 0;
  813: 	}
  814: 
  815: 	free_cols(stmt, S TSRMLS_CC);
  816: 	/* how many columns do we have ? */
  817: 	SQLNumResultCols(S->stmt, &colcount);
  818: 	stmt->column_count = (int)colcount;
  819: 	S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
  820: 	S->going_long = 0;
  821: 
  822: 	return 1;
  823: }
  824: 
  825: struct pdo_stmt_methods odbc_stmt_methods = {
  826: 	odbc_stmt_dtor,
  827: 	odbc_stmt_execute,
  828: 	odbc_stmt_fetch,
  829: 	odbc_stmt_describe,
  830: 	odbc_stmt_get_col,
  831: 	odbc_stmt_param_hook,
  832: 	odbc_stmt_set_param,
  833: 	odbc_stmt_get_attr, /* get attr */
  834: 	NULL, /* get column meta */
  835: 	odbc_stmt_next_rowset
  836: };
  837: 
  838: /*
  839:  * Local variables:
  840:  * tab-width: 4
  841:  * c-basic-offset: 4
  842:  * End:
  843:  * vim600: noet sw=4 ts=4 fdm=marker
  844:  * vim<600: noet sw=4 ts=4
  845:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>