xref: /PHP-5.3/ext/pdo_odbc/odbc_stmt.c (revision a2045ff3)
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$ */
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 
pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt * S,SWORD sqltype)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 
pdo_odbc_utf82ucs2(pdo_stmt_t * stmt,int is_unicode,const char * buf,unsigned long buflen,unsigned long * outlen)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 
pdo_odbc_ucs22utf8(pdo_stmt_t * stmt,int is_unicode,const char * buf,unsigned long buflen,unsigned long * outlen)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 
free_cols(pdo_stmt_t * stmt,pdo_odbc_stmt * S TSRMLS_DC)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 
odbc_stmt_dtor(pdo_stmt_t * stmt TSRMLS_DC)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 
odbc_stmt_execute(pdo_stmt_t * stmt TSRMLS_DC)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 
odbc_stmt_param_hook(pdo_stmt_t * stmt,struct pdo_bound_param_data * param,enum pdo_param_event event_type TSRMLS_DC)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, &param->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 
odbc_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,long offset TSRMLS_DC)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 
odbc_stmt_describe(pdo_stmt_t * stmt,int colno TSRMLS_DC)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 
odbc_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,unsigned long * len,int * caller_frees TSRMLS_DC)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 			/* this is a 'long column'
637 
638 			 read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
639 			 in order into the output buffer
640 
641 			 this loop has to work whether or not SQLGetData() provides the total column length.
642 			 calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
643 			 for that size would be slower except maybe for extremely long columns.*/
644 			char *buf2;
645 
646 			buf2 = emalloc(256);
647 			buf = estrndup(C->data, 256);
648 			used = 255; /* not 256; the driver NUL terminated the buffer */
649 
650 			do {
651 				C->fetched_len = 0;
652 				/* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
653 				rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, buf2, 256, &C->fetched_len);
654 
655 				/* resize output buffer and reassemble block */
656 				if (rc==SQL_SUCCESS_WITH_INFO) {
657 					/* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
658 					 states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size)
659 					 (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */
660 					buf = erealloc(buf, used + 255+1);
661 					memcpy(buf + used, buf2, 255);
662 					used = used + 255;
663 				} else if (rc==SQL_SUCCESS) {
664 					buf = erealloc(buf, used + C->fetched_len+1);
665 					memcpy(buf + used, buf2, C->fetched_len);
666 					used = used + C->fetched_len;
667 				} else {
668 					/* includes SQL_NO_DATA */
669 					break;
670 				}
671 
672 			} while (1);
673 
674 			efree(buf2);
675 
676 			/* NULL terminate the buffer once, when finished, for use with the rest of PHP */
677 			buf[used] = '\0';
678 
679 			*ptr = buf;
680 			*caller_frees = 1;
681 			*len = used;
682 			if (C->is_unicode) {
683 				goto unicode_conv;
684 			}
685 			return 1;
686 		}
687 
688 		/* something went caca */
689 		*ptr = NULL;
690 		*len = 0;
691 		return 1;
692 	}
693 
694 in_data:
695 	/* check the indicator to ensure that the data is intact */
696 	if (C->fetched_len == SQL_NULL_DATA) {
697 		/* A NULL value */
698 		*ptr = NULL;
699 		*len = 0;
700 		return 1;
701 	} else if (C->fetched_len >= 0) {
702 		/* it was stored perfectly */
703 		*ptr = C->data;
704 		*len = C->fetched_len;
705 		if (C->is_unicode) {
706 			goto unicode_conv;
707 		}
708 		return 1;
709 	} else {
710 		/* no data? */
711 		*ptr = NULL;
712 		*len = 0;
713 		return 1;
714 	}
715 
716 	unicode_conv:
717 	switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) {
718 		case PDO_ODBC_CONV_FAIL:
719 			/* oh well.  They can have the binary version of it */
720 		case PDO_ODBC_CONV_NOT_REQUIRED:
721 			/* shouldn't happen... */
722 			return 1;
723 
724 		case PDO_ODBC_CONV_OK:
725 			if (*caller_frees) {
726 				efree(*ptr);
727 			}
728 			*ptr = emalloc(ulen + 1);
729 			*len = ulen;
730 			memcpy(*ptr, S->convbuf, ulen+1);
731 			*caller_frees = 1;
732 			return 1;
733 	}
734 	return 1;
735 }
736 
odbc_stmt_set_param(pdo_stmt_t * stmt,long attr,zval * val TSRMLS_DC)737 static int odbc_stmt_set_param(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
738 {
739 	SQLRETURN rc;
740 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
741 
742 	switch (attr) {
743 		case PDO_ATTR_CURSOR_NAME:
744 			convert_to_string(val);
745 			rc = SQLSetCursorName(S->stmt, Z_STRVAL_P(val), Z_STRLEN_P(val));
746 
747 			if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
748 				return 1;
749 			}
750 			pdo_odbc_stmt_error("SQLSetCursorName");
751 			return 0;
752 
753 		case PDO_ODBC_ATTR_ASSUME_UTF8:
754 			S->assume_utf8 = zval_is_true(val);
755 			return 0;
756 		default:
757 			strcpy(S->einfo.last_err_msg, "Unknown Attribute");
758 			S->einfo.what = "setAttribute";
759 			strcpy(S->einfo.last_state, "IM001");
760 			return -1;
761 	}
762 }
763 
odbc_stmt_get_attr(pdo_stmt_t * stmt,long attr,zval * val TSRMLS_DC)764 static int odbc_stmt_get_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
765 {
766 	SQLRETURN rc;
767 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
768 
769 	switch (attr) {
770 		case PDO_ATTR_CURSOR_NAME:
771 		{
772 			char buf[256];
773 			SQLSMALLINT len = 0;
774 			rc = SQLGetCursorName(S->stmt, buf, sizeof(buf), &len);
775 
776 			if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
777 				ZVAL_STRINGL(val, buf, len, 1);
778 				return 1;
779 			}
780 			pdo_odbc_stmt_error("SQLGetCursorName");
781 			return 0;
782 		}
783 
784 		case PDO_ODBC_ATTR_ASSUME_UTF8:
785 			ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
786 			return 0;
787 
788 		default:
789 			strcpy(S->einfo.last_err_msg, "Unknown Attribute");
790 			S->einfo.what = "getAttribute";
791 			strcpy(S->einfo.last_state, "IM001");
792 			return -1;
793 	}
794 }
795 
odbc_stmt_next_rowset(pdo_stmt_t * stmt TSRMLS_DC)796 static int odbc_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
797 {
798 	SQLRETURN rc;
799 	SQLSMALLINT colcount;
800 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
801 
802 	/* NOTE: can't guarantee that output or input/output parameters
803 	 * are set until this fella returns SQL_NO_DATA, according to
804 	 * MSDN ODBC docs */
805 	rc = SQLMoreResults(S->stmt);
806 
807 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
808 		return 0;
809 	}
810 
811 	free_cols(stmt, S TSRMLS_CC);
812 	/* how many columns do we have ? */
813 	SQLNumResultCols(S->stmt, &colcount);
814 	stmt->column_count = (int)colcount;
815 	S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
816 	S->going_long = 0;
817 
818 	return 1;
819 }
820 
821 struct pdo_stmt_methods odbc_stmt_methods = {
822 	odbc_stmt_dtor,
823 	odbc_stmt_execute,
824 	odbc_stmt_fetch,
825 	odbc_stmt_describe,
826 	odbc_stmt_get_col,
827 	odbc_stmt_param_hook,
828 	odbc_stmt_set_param,
829 	odbc_stmt_get_attr, /* get attr */
830 	NULL, /* get column meta */
831 	odbc_stmt_next_rowset
832 };
833 
834 /*
835  * Local variables:
836  * tab-width: 4
837  * c-basic-offset: 4
838  * End:
839  * vim600: noet sw=4 ts=4 fdm=marker
840  * vim<600: noet sw=4 ts=4
841  */
842