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