xref: /PHP-5.5/ext/pdo_odbc/odbc_stmt.c (revision 73c1be26)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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 	SQLULEN 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 = 0;
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 
odbc_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,long offset TSRMLS_DC)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 
odbc_stmt_describe(pdo_stmt_t * stmt,int colno TSRMLS_DC)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 	SQLULEN	colsize;
555 	SQLLEN displaysize;
556 
557 	rc = SQLDescribeCol(S->stmt, colno+1, S->cols[colno].colname,
558 			sizeof(S->cols[colno].colname)-1, &colnamelen,
559 			&S->cols[colno].coltype, &colsize, NULL, NULL);
560 
561 	if (rc != SQL_SUCCESS) {
562 		pdo_odbc_stmt_error("SQLDescribeCol");
563 		if (rc != SQL_SUCCESS_WITH_INFO) {
564 			return 0;
565 		}
566 	}
567 
568 	rc = SQLColAttribute(S->stmt, colno+1,
569 			SQL_DESC_DISPLAY_SIZE,
570 			NULL, 0, NULL, &displaysize);
571 
572 	if (rc != SQL_SUCCESS) {
573 		pdo_odbc_stmt_error("SQLColAttribute");
574 		if (rc != SQL_SUCCESS_WITH_INFO) {
575 			return 0;
576 		}
577 	}
578 	colsize = displaysize;
579 
580 	col->maxlen = S->cols[colno].datalen = colsize;
581 	col->namelen = colnamelen;
582 	col->name = estrdup(S->cols[colno].colname);
583 	S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);
584 
585 	/* returning data as a string */
586 	col->param_type = PDO_PARAM_STR;
587 
588 	/* tell ODBC to put it straight into our buffer, but only if it
589 	 * isn't "long" data, and only if we haven't already bound a long
590 	 * column. */
591 	if (colsize < 256 && !S->going_long) {
592 		S->cols[colno].data = emalloc(colsize+1);
593 		S->cols[colno].is_long = 0;
594 
595 		rc = SQLBindCol(S->stmt, colno+1,
596 			S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
597 			S->cols[colno].data,
598  			S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
599 
600 		if (rc != SQL_SUCCESS) {
601 			pdo_odbc_stmt_error("SQLBindCol");
602 			return 0;
603 		}
604 	} else {
605 		/* allocate a smaller buffer to keep around for smaller
606 		 * "long" columns */
607 		S->cols[colno].data = emalloc(256);
608 		S->going_long = 1;
609 		S->cols[colno].is_long = 1;
610 	}
611 
612 	return 1;
613 }
614 
odbc_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,unsigned long * len,int * caller_frees TSRMLS_DC)615 static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
616 {
617 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
618 	pdo_odbc_column *C = &S->cols[colno];
619 	unsigned long ulen;
620 
621 	/* if it is a column containing "long" data, perform late binding now */
622 	if (C->is_long) {
623 		unsigned long used = 0;
624 		char *buf;
625 		RETCODE rc;
626 
627 		/* fetch it into C->data, which is allocated with a length
628 		 * of 256 bytes; if there is more to be had, we then allocate
629 		 * bigger buffer for the caller to free */
630 
631 		rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
632  			256, &C->fetched_len);
633 
634 		if (rc == SQL_SUCCESS) {
635 			/* all the data fit into our little buffer;
636 			 * jump down to the generic bound data case */
637 			goto in_data;
638 		}
639 
640 		if (rc == SQL_SUCCESS_WITH_INFO) {
641 			/* this is a 'long column'
642 
643 			 read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
644 			 in order into the output buffer
645 
646 			 this loop has to work whether or not SQLGetData() provides the total column length.
647 			 calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
648 			 for that size would be slower except maybe for extremely long columns.*/
649 			char *buf2;
650 
651 			buf2 = emalloc(256);
652 			buf = estrndup(C->data, 256);
653 			used = 255; /* not 256; the driver NUL terminated the buffer */
654 
655 			do {
656 				C->fetched_len = 0;
657 				/* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
658 				rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, buf2, 256, &C->fetched_len);
659 
660 				/* resize output buffer and reassemble block */
661 				if (rc==SQL_SUCCESS_WITH_INFO) {
662 					/* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
663 					 states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size)
664 					 (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */
665 					buf = erealloc(buf, used + 255+1);
666 					memcpy(buf + used, buf2, 255);
667 					used = used + 255;
668 				} else if (rc==SQL_SUCCESS) {
669 					buf = erealloc(buf, used + C->fetched_len+1);
670 					memcpy(buf + used, buf2, C->fetched_len);
671 					used = used + C->fetched_len;
672 				} else {
673 					/* includes SQL_NO_DATA */
674 					break;
675 				}
676 
677 			} while (1);
678 
679 			efree(buf2);
680 
681 			/* NULL terminate the buffer once, when finished, for use with the rest of PHP */
682 			buf[used] = '\0';
683 
684 			*ptr = buf;
685 			*caller_frees = 1;
686 			*len = used;
687 			if (C->is_unicode) {
688 				goto unicode_conv;
689 			}
690 			return 1;
691 		}
692 
693 		/* something went caca */
694 		*ptr = NULL;
695 		*len = 0;
696 		return 1;
697 	}
698 
699 in_data:
700 	/* check the indicator to ensure that the data is intact */
701 	if (C->fetched_len == SQL_NULL_DATA) {
702 		/* A NULL value */
703 		*ptr = NULL;
704 		*len = 0;
705 		return 1;
706 	} else if (C->fetched_len >= 0) {
707 		/* it was stored perfectly */
708 		*ptr = C->data;
709 		*len = C->fetched_len;
710 		if (C->is_unicode) {
711 			goto unicode_conv;
712 		}
713 		return 1;
714 	} else {
715 		/* no data? */
716 		*ptr = NULL;
717 		*len = 0;
718 		return 1;
719 	}
720 
721 	unicode_conv:
722 	switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) {
723 		case PDO_ODBC_CONV_FAIL:
724 			/* oh well.  They can have the binary version of it */
725 		case PDO_ODBC_CONV_NOT_REQUIRED:
726 			/* shouldn't happen... */
727 			return 1;
728 
729 		case PDO_ODBC_CONV_OK:
730 			if (*caller_frees) {
731 				efree(*ptr);
732 			}
733 			*ptr = emalloc(ulen + 1);
734 			*len = ulen;
735 			memcpy(*ptr, S->convbuf, ulen+1);
736 			*caller_frees = 1;
737 			return 1;
738 	}
739 	return 1;
740 }
741 
odbc_stmt_set_param(pdo_stmt_t * stmt,long attr,zval * val TSRMLS_DC)742 static int odbc_stmt_set_param(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
743 {
744 	SQLRETURN rc;
745 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
746 
747 	switch (attr) {
748 		case PDO_ATTR_CURSOR_NAME:
749 			convert_to_string(val);
750 			rc = SQLSetCursorName(S->stmt, Z_STRVAL_P(val), Z_STRLEN_P(val));
751 
752 			if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
753 				return 1;
754 			}
755 			pdo_odbc_stmt_error("SQLSetCursorName");
756 			return 0;
757 
758 		case PDO_ODBC_ATTR_ASSUME_UTF8:
759 			S->assume_utf8 = zval_is_true(val);
760 			return 0;
761 		default:
762 			strcpy(S->einfo.last_err_msg, "Unknown Attribute");
763 			S->einfo.what = "setAttribute";
764 			strcpy(S->einfo.last_state, "IM001");
765 			return -1;
766 	}
767 }
768 
odbc_stmt_get_attr(pdo_stmt_t * stmt,long attr,zval * val TSRMLS_DC)769 static int odbc_stmt_get_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
770 {
771 	SQLRETURN rc;
772 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
773 
774 	switch (attr) {
775 		case PDO_ATTR_CURSOR_NAME:
776 		{
777 			char buf[256];
778 			SQLSMALLINT len = 0;
779 			rc = SQLGetCursorName(S->stmt, buf, sizeof(buf), &len);
780 
781 			if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
782 				ZVAL_STRINGL(val, buf, len, 1);
783 				return 1;
784 			}
785 			pdo_odbc_stmt_error("SQLGetCursorName");
786 			return 0;
787 		}
788 
789 		case PDO_ODBC_ATTR_ASSUME_UTF8:
790 			ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
791 			return 0;
792 
793 		default:
794 			strcpy(S->einfo.last_err_msg, "Unknown Attribute");
795 			S->einfo.what = "getAttribute";
796 			strcpy(S->einfo.last_state, "IM001");
797 			return -1;
798 	}
799 }
800 
odbc_stmt_next_rowset(pdo_stmt_t * stmt TSRMLS_DC)801 static int odbc_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
802 {
803 	SQLRETURN rc;
804 	SQLSMALLINT colcount;
805 	pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
806 
807 	/* NOTE: can't guarantee that output or input/output parameters
808 	 * are set until this fella returns SQL_NO_DATA, according to
809 	 * MSDN ODBC docs */
810 	rc = SQLMoreResults(S->stmt);
811 
812 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
813 		return 0;
814 	}
815 
816 	free_cols(stmt, S TSRMLS_CC);
817 	/* how many columns do we have ? */
818 	SQLNumResultCols(S->stmt, &colcount);
819 	stmt->column_count = (int)colcount;
820 	S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
821 	S->going_long = 0;
822 
823 	return 1;
824 }
825 
826 struct pdo_stmt_methods odbc_stmt_methods = {
827 	odbc_stmt_dtor,
828 	odbc_stmt_execute,
829 	odbc_stmt_fetch,
830 	odbc_stmt_describe,
831 	odbc_stmt_get_col,
832 	odbc_stmt_param_hook,
833 	odbc_stmt_set_param,
834 	odbc_stmt_get_attr, /* get attr */
835 	NULL, /* get column meta */
836 	odbc_stmt_next_rowset
837 };
838 
839 /*
840  * Local variables:
841  * tab-width: 4
842  * c-basic-offset: 4
843  * End:
844  * vim600: noet sw=4 ts=4 fdm=marker
845  * vim<600: noet sw=4 ts=4
846  */
847