1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
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
33enum pdo_odbc_conv_result {
34	PDO_ODBC_CONV_NOT_REQUIRED,
35	PDO_ODBC_CONV_OK,
36	PDO_ODBC_CONV_FAIL
37};
38
39static 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
60static 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
95static 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
126static 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
141static 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
162static 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
282static 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
526static 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
564static 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
648static 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
732in_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
775static 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
802static 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
834static 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
859struct 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