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