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