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.01 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_01.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: Ard Biesheuvel <abies@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_firebird.h"
29#include "php_pdo_firebird_int.h"
30
31#include <time.h>
32
33#define RECORD_ERROR(stmt) _firebird_error(NULL, stmt,  __FILE__, __LINE__)
34
35/* free the allocated space for passing field values to the db and back */
36static void free_sqlda(XSQLDA const *sqlda) /* {{{ */
37{
38	int i;
39
40	for (i = 0; i < sqlda->sqld; ++i) {
41		XSQLVAR const *var = &sqlda->sqlvar[i];
42
43		if (var->sqlind) {
44			efree(var->sqlind);
45		}
46	}
47}
48/* }}} */
49
50/* called by PDO to clean up a statement handle */
51static int firebird_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
52{
53	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
54	int result = 1, i;
55
56	/* release the statement */
57	if (isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_drop)) {
58		RECORD_ERROR(stmt);
59		result = 0;
60	}
61
62	/* clean up the fetch buffers if they have been used */
63	for (i = 0; i < S->out_sqlda.sqld; ++i) {
64		if (S->fetch_buf[i]) {
65			efree(S->fetch_buf[i]);
66		}
67	}
68	efree(S->fetch_buf);
69
70	zend_hash_destroy(S->named_params);
71	FREE_HASHTABLE(S->named_params);
72
73	/* clean up the input descriptor */
74	if (S->in_sqlda) {
75		free_sqlda(S->in_sqlda);
76		efree(S->in_sqlda);
77	}
78
79	free_sqlda(&S->out_sqlda);
80	efree(S);
81
82	return result;
83}
84/* }}} */
85
86/* called by PDO to execute a prepared query */
87static int firebird_stmt_execute(pdo_stmt_t *stmt) /* {{{ */
88{
89	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
90	pdo_firebird_db_handle *H = S->H;
91	zend_ulong affected_rows = 0;
92	static char info_count[] = {isc_info_sql_records};
93	char result[64];
94
95	do {
96		/* named or open cursors should be closed first */
97		if ((*S->name || S->cursor_open) && isc_dsql_free_statement(H->isc_status, &S->stmt, DSQL_close)) {
98			break;
99		}
100		S->cursor_open = 0;
101		/* assume all params have been bound */
102
103		if (isc_dsql_execute(H->isc_status, &H->tr, &S->stmt, PDO_FB_SQLDA_VERSION, S->in_sqlda)) {
104			break;
105		}
106
107		/* Determine how many rows have changed. In this case we are
108		 * only interested in rows changed, not rows retrieved. That
109		 * should be handled by the client when fetching. */
110		stmt->row_count = affected_rows;
111
112		switch (S->statement_type) {
113			case isc_info_sql_stmt_insert:
114			case isc_info_sql_stmt_update:
115			case isc_info_sql_stmt_delete:
116			case isc_info_sql_stmt_exec_procedure:
117				if (isc_dsql_sql_info(H->isc_status, &S->stmt, sizeof ( info_count),
118					info_count, sizeof(result), result)) {
119					break;
120				}
121				if (result[0] == isc_info_sql_records) {
122					unsigned i = 3, result_size = isc_vax_integer(&result[1], 2);
123					while (result[i] != isc_info_end && i < result_size) {
124						short len = (short) isc_vax_integer(&result[i + 1], 2);
125						if (result[i] != isc_info_req_select_count) {
126							affected_rows += isc_vax_integer(&result[i + 3], len);
127						}
128						i += len + 3;
129					}
130					stmt->row_count = affected_rows;
131				}
132			default:
133				;
134		}
135
136		/* commit? */
137		if (stmt->dbh->auto_commit && isc_commit_retaining(H->isc_status, &H->tr)) {
138			break;
139		}
140
141		*S->name = 0;
142		S->cursor_open = (S->out_sqlda.sqln > 0);	/* A cursor is opened, when more than zero columns returned */
143		S->exhausted = !S->cursor_open;
144
145		return 1;
146	} while (0);
147
148	RECORD_ERROR(stmt);
149
150	return 0;
151}
152/* }}} */
153
154/* called by PDO to fetch the next row from a statement */
155static int firebird_stmt_fetch(pdo_stmt_t *stmt, /* {{{ */
156	enum pdo_fetch_orientation ori, zend_long offset)
157{
158	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
159	pdo_firebird_db_handle *H = S->H;
160
161	if (!stmt->executed) {
162		strcpy(stmt->error_code, "HY000");
163		H->last_app_error = "Cannot fetch from a closed cursor";
164	} else if (!S->exhausted) {
165		if (isc_dsql_fetch(H->isc_status, &S->stmt, PDO_FB_SQLDA_VERSION, &S->out_sqlda)) {
166			if (H->isc_status[0] && H->isc_status[1]) {
167				RECORD_ERROR(stmt);
168			}
169			S->exhausted = 1;
170			return 0;
171		}
172 		if (S->statement_type == isc_info_sql_stmt_exec_procedure) {
173 			S->exhausted = 1;
174 		}
175		stmt->row_count++;
176		return 1;
177	}
178	return 0;
179}
180/* }}} */
181
182/* called by PDO to retrieve information about the fields being returned */
183static int firebird_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
184{
185	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
186	struct pdo_column_data *col = &stmt->columns[colno];
187	XSQLVAR *var = &S->out_sqlda.sqlvar[colno];
188	int colname_len;
189	char *cp;
190
191	/* allocate storage for the column */
192	var->sqlind = (void*)ecalloc(1, var->sqllen + 2*sizeof(short));
193	var->sqldata = &((char*)var->sqlind)[sizeof(short)];
194
195	colname_len = (S->H->fetch_table_names && var->relname_length)
196					? (var->aliasname_length + var->relname_length + 1)
197					: (var->aliasname_length);
198	col->precision = -var->sqlscale;
199	col->maxlen = var->sqllen;
200	col->name = zend_string_alloc(colname_len, 0);
201	cp = ZSTR_VAL(col->name);
202	if (colname_len > var->aliasname_length) {
203		memmove(cp, var->relname, var->relname_length);
204		cp += var->relname_length;
205		*cp++ = '.';
206	}
207	memmove(cp, var->aliasname, var->aliasname_length);
208	*(cp+var->aliasname_length) = '\0';
209	col->param_type = PDO_PARAM_STR;
210
211	return 1;
212}
213/* }}} */
214
215#define FETCH_BUF(buf,type,len,lenvar) ((buf) = (buf) ? (buf) : \
216	emalloc((len) ? (len * sizeof(type)) : ((*(unsigned long*)lenvar) = sizeof(type))))
217
218#define CHAR_BUF_LEN 24
219
220/* fetch a blob into a fetch buffer */
221static int firebird_fetch_blob(pdo_stmt_t *stmt, int colno, char **ptr, /* {{{ */
222	zend_ulong *len, ISC_QUAD *blob_id)
223{
224	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
225	pdo_firebird_db_handle *H = S->H;
226	isc_blob_handle blobh = NULL;
227	char const bl_item = isc_info_blob_total_length;
228	char bl_info[20];
229	unsigned short i;
230	int result = *len = 0;
231
232	if (isc_open_blob(H->isc_status, &H->db, &H->tr, &blobh, blob_id)) {
233		RECORD_ERROR(stmt);
234		return 0;
235	}
236
237	if (isc_blob_info(H->isc_status, &blobh, 1, const_cast(&bl_item),
238			sizeof(bl_info), bl_info)) {
239		RECORD_ERROR(stmt);
240		goto fetch_blob_end;
241	}
242
243	/* find total length of blob's data */
244	for (i = 0; i < sizeof(bl_info); ) {
245		unsigned short item_len;
246		char item = bl_info[i++];
247
248		if (item == isc_info_end || item == isc_info_truncated || item == isc_info_error
249				|| i >= sizeof(bl_info)) {
250			H->last_app_error = "Couldn't determine BLOB size";
251			goto fetch_blob_end;
252		}
253
254		item_len = (unsigned short) isc_vax_integer(&bl_info[i], 2);
255
256		if (item == isc_info_blob_total_length) {
257			*len = isc_vax_integer(&bl_info[i+2], item_len);
258			break;
259		}
260		i += item_len+2;
261	}
262
263	/* we've found the blob's length, now fetch! */
264
265	if (*len) {
266		zend_ulong cur_len;
267		unsigned short seg_len;
268		ISC_STATUS stat;
269
270		*ptr = S->fetch_buf[colno] = erealloc(*ptr, *len+1);
271
272		for (cur_len = stat = 0; (!stat || stat == isc_segment) && cur_len < *len; cur_len += seg_len) {
273
274			unsigned short chunk_size = (*len-cur_len) > USHRT_MAX ? USHRT_MAX
275				: (unsigned short)(*len-cur_len);
276
277			stat = isc_get_segment(H->isc_status, &blobh, &seg_len, chunk_size, &(*ptr)[cur_len]);
278		}
279
280		(*ptr)[*len++] = '\0';
281
282		if (H->isc_status[0] == 1 && (stat != 0 && stat != isc_segstr_eof && stat != isc_segment)) {
283			H->last_app_error = "Error reading from BLOB";
284			goto fetch_blob_end;
285		}
286	}
287	result = 1;
288
289fetch_blob_end:
290	if (isc_close_blob(H->isc_status, &blobh)) {
291		RECORD_ERROR(stmt);
292		return 0;
293	}
294	return result;
295}
296/* }}} */
297
298static int firebird_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,  /* {{{ */
299	zend_ulong *len, int *caller_frees)
300{
301	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
302	XSQLVAR const *var = &S->out_sqlda.sqlvar[colno];
303
304	if (*var->sqlind == -1) {
305		/* A NULL value */
306		*ptr = NULL;
307		*len = 0;
308	} else {
309		if (var->sqlscale < 0) {
310			static ISC_INT64 const scales[] = { 1, 10, 100, 1000,
311				10000,
312				100000,
313				1000000,
314				10000000,
315				100000000,
316				1000000000,
317				LL_LIT(10000000000),
318				LL_LIT(100000000000),
319				LL_LIT(1000000000000),
320				LL_LIT(10000000000000),
321				LL_LIT(100000000000000),
322				LL_LIT(1000000000000000),
323				LL_LIT(10000000000000000),
324				LL_LIT(100000000000000000),
325				LL_LIT(1000000000000000000)
326			};
327			ISC_INT64 n, f = scales[-var->sqlscale];
328
329			switch (var->sqltype & ~1) {
330				case SQL_SHORT:
331					n = *(short*)var->sqldata;
332					break;
333				case SQL_LONG:
334					n = *(ISC_LONG*)var->sqldata;
335					break;
336				case SQL_INT64:
337					n = *(ISC_INT64*)var->sqldata;
338			}
339
340			*ptr = FETCH_BUF(S->fetch_buf[colno], char, CHAR_BUF_LEN, NULL);
341
342			if (n >= 0) {
343				*len = slprintf(*ptr, CHAR_BUF_LEN, "%" LL_MASK "d.%0*" LL_MASK "d",
344					n / f, -var->sqlscale, n % f);
345			} else if (n <= -f) {
346				*len = slprintf(*ptr, CHAR_BUF_LEN, "%" LL_MASK "d.%0*" LL_MASK "d",
347					n / f, -var->sqlscale, -n % f);
348			 } else {
349				*len = slprintf(*ptr, CHAR_BUF_LEN, "-0.%0*" LL_MASK "d", -var->sqlscale, -n % f);
350			}
351		} else {
352			switch (var->sqltype & ~1) {
353				struct tm t;
354				char *fmt;
355
356				case SQL_VARYING:
357					*ptr = &var->sqldata[2];
358					*len = *(short*)var->sqldata;
359					break;
360				case SQL_TEXT:
361					*ptr = var->sqldata;
362					*len = var->sqllen;
363					break;
364				case SQL_SHORT:
365				    *ptr = FETCH_BUF(S->fetch_buf[colno], char, CHAR_BUF_LEN, NULL);
366					*len = slprintf(*ptr, CHAR_BUF_LEN, "%d", *(short*)var->sqldata);
367					break;
368				case SQL_LONG:
369					*ptr = FETCH_BUF(S->fetch_buf[colno], char, CHAR_BUF_LEN, NULL);
370					*len = slprintf(*ptr, CHAR_BUF_LEN, "%d", *(ISC_LONG*)var->sqldata);
371					break;
372				case SQL_INT64:
373					*ptr = FETCH_BUF(S->fetch_buf[colno], char, CHAR_BUF_LEN, NULL);
374					*len = slprintf(*ptr, CHAR_BUF_LEN, "%" LL_MASK "d", *(ISC_INT64*)var->sqldata);
375					break;
376				case SQL_FLOAT:
377					*ptr = FETCH_BUF(S->fetch_buf[colno], char, CHAR_BUF_LEN, NULL);
378					*len = slprintf(*ptr, CHAR_BUF_LEN, "%F", *(float*)var->sqldata);
379					break;
380				case SQL_DOUBLE:
381					*ptr = FETCH_BUF(S->fetch_buf[colno], char, CHAR_BUF_LEN, NULL);
382					*len = slprintf(*ptr, CHAR_BUF_LEN, "%F" , *(double*)var->sqldata);
383					break;
384				case SQL_TYPE_DATE:
385					isc_decode_sql_date((ISC_DATE*)var->sqldata, &t);
386					fmt = S->H->date_format ? S->H->date_format : PDO_FB_DEF_DATE_FMT;
387					if (0) {
388				case SQL_TYPE_TIME:
389						isc_decode_sql_time((ISC_TIME*)var->sqldata, &t);
390						fmt = S->H->time_format ? S->H->time_format : PDO_FB_DEF_TIME_FMT;
391					} else if (0) {
392				case SQL_TIMESTAMP:
393						isc_decode_timestamp((ISC_TIMESTAMP*)var->sqldata, &t);
394						fmt = S->H->timestamp_format ? S->H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT;
395					}
396					/* convert the timestamp into a string */
397					*len = 80;
398					*ptr = FETCH_BUF(S->fetch_buf[colno], char, *len, NULL);
399					*len = strftime(*ptr, *len, fmt, &t);
400					break;
401				case SQL_BLOB:
402					return firebird_fetch_blob(stmt,colno,ptr,len,
403						(ISC_QUAD*)var->sqldata);
404			}
405		}
406	}
407	return 1;
408}
409/* }}} */
410
411static int firebird_bind_blob(pdo_stmt_t *stmt, ISC_QUAD *blob_id, zval *param)
412{
413	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
414	pdo_firebird_db_handle *H = S->H;
415	isc_blob_handle h = NULL;
416	zend_ulong put_cnt = 0, rem_cnt;
417	unsigned short chunk_size;
418	int result = 1;
419
420	if (isc_create_blob(H->isc_status, &H->db, &H->tr, &h, blob_id)) {
421		RECORD_ERROR(stmt);
422		return 0;
423	}
424
425	SEPARATE_ZVAL(param);
426	convert_to_string_ex(param);
427
428	for (rem_cnt = Z_STRLEN_P(param); rem_cnt > 0; rem_cnt -= chunk_size)  {
429
430		chunk_size = rem_cnt > USHRT_MAX ? USHRT_MAX : (unsigned short)rem_cnt;
431
432		if (isc_put_segment(H->isc_status, &h, chunk_size, &Z_STRVAL_P(param)[put_cnt])) {
433			RECORD_ERROR(stmt);
434			result = 0;
435			break;
436		}
437		put_cnt += chunk_size;
438	}
439
440	zval_dtor(param);
441
442	if (isc_close_blob(H->isc_status, &h)) {
443		RECORD_ERROR(stmt);
444		return 0;
445	}
446	return result;
447}
448
449static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, /* {{{ */
450	enum pdo_param_event event_type)
451{
452	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
453	XSQLDA *sqlda = param->is_param ? S->in_sqlda : &S->out_sqlda;
454	XSQLVAR *var;
455
456	if (event_type == PDO_PARAM_EVT_FREE) { /* not used */
457		return 1;
458	}
459
460	if (!sqlda || param->paramno >= sqlda->sqld) {
461		strcpy(stmt->error_code, "HY093");
462		S->H->last_app_error = "Invalid parameter index";
463		return 0;
464	}
465	if (param->is_param && param->paramno == -1) {
466		zval *index;
467
468		/* try to determine the index by looking in the named_params hash */
469		if ((index = zend_hash_find(S->named_params, param->name)) != NULL) {
470			param->paramno = Z_LVAL_P(index);
471		} else {
472			/* ... or by looking in the input descriptor */
473			int i;
474
475			for (i = 0; i < sqlda->sqld; ++i) {
476				XSQLVAR *var = &sqlda->sqlvar[i];
477
478				if ((var->aliasname_length && !strncasecmp(ZSTR_VAL(param->name), var->aliasname,
479						min(ZSTR_LEN(param->name), var->aliasname_length)))
480						|| (var->sqlname_length && !strncasecmp(ZSTR_VAL(param->name), var->sqlname,
481						min(ZSTR_LEN(param->name), var->sqlname_length)))) {
482					param->paramno = i;
483					break;
484				}
485			}
486			if (i >= sqlda->sqld) {
487				strcpy(stmt->error_code, "HY093");
488				S->H->last_app_error = "Invalid parameter name";
489				return 0;
490			}
491		}
492	}
493
494	var = &sqlda->sqlvar[param->paramno];
495
496	switch (event_type) {
497		char *value;
498		zend_ulong value_len;
499		int caller_frees;
500		zval *parameter;
501
502		case PDO_PARAM_EVT_ALLOC:
503			if (param->is_param) {
504				/* allocate the parameter */
505				if (var->sqlind) {
506					efree(var->sqlind);
507				}
508				var->sqlind = (void*)emalloc(var->sqllen + 2*sizeof(short));
509				var->sqldata = &((char*)var->sqlind)[sizeof(short)];
510			}
511			break;
512
513		case PDO_PARAM_EVT_EXEC_PRE:
514			if (!param->is_param) {
515				break;
516			}
517
518			*var->sqlind = 0;
519			if (Z_ISREF(param->parameter)) {
520				parameter = Z_REFVAL(param->parameter);
521			} else {
522				parameter = &param->parameter;
523			}
524
525			if (Z_TYPE_P(parameter) == IS_RESOURCE) {
526				php_stream *stm = NULL;
527
528				php_stream_from_zval_no_verify(stm, parameter);
529				if (stm) {
530					zend_string *mem =  php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
531					zval_ptr_dtor(parameter);
532					ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC());
533				} else {
534					pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource");
535					return 0;
536				}
537			}
538
539			switch (var->sqltype & ~1) {
540				case SQL_ARRAY:
541					strcpy(stmt->error_code, "HY000");
542					S->H->last_app_error = "Cannot bind to array field";
543					return 0;
544
545				case SQL_BLOB:
546					return firebird_bind_blob(stmt, (ISC_QUAD*)var->sqldata, parameter);
547			}
548
549			/* check if a NULL should be inserted */
550			switch (Z_TYPE_P(parameter)) {
551				int force_null;
552
553				case IS_LONG:
554					/* keep the allow-NULL flag */
555					var->sqltype = (sizeof(zend_long) == 8 ? SQL_INT64 : SQL_LONG) | (var->sqltype & 1);
556					var->sqldata = (void*)&Z_LVAL_P(parameter);
557					var->sqllen = sizeof(zend_long);
558					break;
559				case IS_DOUBLE:
560					/* keep the allow-NULL flag */
561					var->sqltype = SQL_DOUBLE | (var->sqltype & 1);
562					var->sqldata = (void*)&Z_DVAL_P(parameter);
563					var->sqllen = sizeof(double);
564					break;
565				case IS_STRING:
566					force_null = 0;
567
568					/* for these types, an empty string can be handled like a NULL value */
569					switch (var->sqltype & ~1) {
570						case SQL_SHORT:
571						case SQL_LONG:
572						case SQL_INT64:
573						case SQL_FLOAT:
574						case SQL_DOUBLE:
575						case SQL_TIMESTAMP:
576						case SQL_TYPE_DATE:
577						case SQL_TYPE_TIME:
578							force_null = (Z_STRLEN_P(parameter) == 0);
579					}
580					if (!force_null) {
581						/* keep the allow-NULL flag */
582						var->sqltype = SQL_TEXT | (var->sqltype & 1);
583						var->sqldata = Z_STRVAL_P(parameter);
584						var->sqllen	 = Z_STRLEN_P(parameter);
585						break;
586					}
587				case IS_NULL:
588					/* complain if this field doesn't allow NULL values */
589					if (~var->sqltype & 1) {
590						strcpy(stmt->error_code, "HY105");
591						S->H->last_app_error = "Parameter requires non-null value";
592						return 0;
593					}
594					*var->sqlind = -1;
595					break;
596				default:
597					strcpy(stmt->error_code, "HY105");
598					S->H->last_app_error = "Binding arrays/objects is not supported";
599					return 0;
600			}
601			break;
602
603		case PDO_PARAM_EVT_FETCH_POST:
604                        if (param->paramno == -1) {
605                            return 0;
606                        }
607			if (param->is_param) {
608				break;
609			}
610			value = NULL;
611			value_len = 0;
612			caller_frees = 0;
613			if (Z_ISREF(param->parameter)) {
614				parameter = Z_REFVAL(param->parameter);
615			} else {
616				parameter = &param->parameter;
617			}
618			zval_ptr_dtor(parameter);
619			ZVAL_NULL(parameter);
620
621			if (firebird_stmt_get_col(stmt, param->paramno, &value, &value_len, &caller_frees)) {
622				switch (PDO_PARAM_TYPE(param->param_type)) {
623					case PDO_PARAM_STR:
624						if (value) {
625							ZVAL_STRINGL(parameter, value, value_len);
626							break;
627						}
628					case PDO_PARAM_INT:
629						if (value) {
630							ZVAL_LONG(parameter, *(zend_long*)value);
631							break;
632						}
633					case PDO_PARAM_EVT_NORMALIZE:
634							 if (!param->is_param) {
635								  char *s = ZSTR_VAL(param->name);
636								  while (*s != '\0') {
637									   *s = toupper(*s);
638										s++;
639								  }
640							 }
641						break;
642					default:
643						ZVAL_NULL(parameter);
644				}
645				if (value && caller_frees) {
646					efree(value);
647				}
648				return 1;
649			}
650			return 0;
651		default:
652			;
653	}
654	return 1;
655}
656/* }}} */
657
658static int firebird_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) /* {{{ */
659{
660	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
661
662	switch (attr) {
663		default:
664			return 0;
665		case PDO_ATTR_CURSOR_NAME:
666			convert_to_string(val);
667
668			if (isc_dsql_set_cursor_name(S->H->isc_status, &S->stmt, Z_STRVAL_P(val),0)) {
669				RECORD_ERROR(stmt);
670				return 0;
671			}
672			strlcpy(S->name, Z_STRVAL_P(val), sizeof(S->name));
673			break;
674	}
675	return 1;
676}
677/* }}} */
678
679static int firebird_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) /* {{{ */
680{
681	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
682
683	switch (attr) {
684		default:
685			return 0;
686		case PDO_ATTR_CURSOR_NAME:
687			if (*S->name) {
688				ZVAL_STRING(val, S->name);
689			} else {
690				ZVAL_NULL(val);
691			}
692			break;
693	}
694	return 1;
695}
696/* }}} */
697
698static int firebird_stmt_cursor_closer(pdo_stmt_t *stmt) /* {{{ */
699{
700	pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
701
702	/* close the statement handle */
703	if ((*S->name || S->cursor_open) && isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_close)) {
704		RECORD_ERROR(stmt);
705		return 0;
706	}
707	*S->name = 0;
708	S->cursor_open = 0;
709	return 1;
710}
711/* }}} */
712
713
714struct pdo_stmt_methods firebird_stmt_methods = { /* {{{ */
715	firebird_stmt_dtor,
716	firebird_stmt_execute,
717	firebird_stmt_fetch,
718	firebird_stmt_describe,
719	firebird_stmt_get_col,
720	firebird_stmt_param_hook,
721	firebird_stmt_set_attribute,
722	firebird_stmt_get_attribute,
723	NULL, /* get_column_meta_func */
724	NULL, /* next_rowset_func */
725	firebird_stmt_cursor_closer
726};
727/* }}} */
728
729/*
730 * Local variables:
731 * tab-width: 4
732 * c-basic-offset: 4
733 * End:
734 * vim600: noet sw=4 ts=4 fdm=marker
735 * vim<600: noet sw=4 ts=4
736 */
737