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