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