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