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