xref: /php-src/ext/odbc/php_odbc.c (revision 8de92952)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Stig Sæther Bakken <ssb@php.net>                            |
14    |          Andreas Karajannis <Andreas.Karajannis@gmd.de>              |
15    |          Frank M. Kromann <frank@kromann.info>  Support for DB/2 CLI |
16    |          Kevin N. Shallow <kshallow@tampabay.rr.com>                 |
17    |          Daniel R. Kalowsky <kalowsky@php.net>                       |
18    +----------------------------------------------------------------------+
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "php.h"
26 #include "php_globals.h"
27 #include "zend_attributes.h"
28 
29 #include "ext/standard/info.h"
30 #include "ext/standard/php_string.h"
31 #include "ext/standard/php_standard.h"
32 #include "Zend/zend_interfaces.h"
33 #include "zend_smart_str.h"
34 
35 #include "php_odbc.h"
36 #include "php_odbc_includes.h"
37 #include "php_globals.h"
38 
39 /* actually lives in main/ */
40 #include "php_odbc_utils.h"
41 
42 #ifdef HAVE_UODBC
43 
44 #include <fcntl.h>
45 #include "ext/standard/head.h"
46 #include "php_ini.h"
47 
48 #define PHP_ODBC_BINMODE_PASSTHRU 0
49 #define PHP_ODBC_BINMODE_RETURN 1
50 #define PHP_ODBC_BINMODE_CONVERT 2
51 
52 #include "odbc_arginfo.h"
53 
54 #define CHECK_ODBC_CONNECTION(conn) \
55 	if (conn == NULL) { \
56 		zend_throw_error(NULL, "ODBC connection has already been closed"); \
57 		RETURN_THROWS(); \
58 	}
59 
60 #define CHECK_ODBC_RESULT(result) \
61 	if (result->conn_ptr == NULL) { \
62 		zend_throw_error(NULL, "ODBC result has already been closed"); \
63 		RETURN_THROWS(); \
64 	}
65 
66 void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent);
67 static void safe_odbc_disconnect(void *handle);
68 static void close_results_with_connection(odbc_connection *conn);
69 static inline odbc_result *odbc_result_from_obj(zend_object *obj);
70 
71 static int le_pconn;
72 
73 static zend_class_entry *odbc_connection_ce, *odbc_result_ce;
74 static zend_object_handlers odbc_connection_object_handlers, odbc_result_object_handlers;
75 
76 #define Z_ODBC_LINK_P(zv) odbc_link_from_obj(Z_OBJ_P(zv))
77 #define Z_ODBC_CONNECTION_P(zv) Z_ODBC_LINK_P(zv)->connection
78 #define Z_ODBC_RESULT_P(zv) odbc_result_from_obj(Z_OBJ_P(zv))
79 
odbc_insert_new_result(odbc_connection * connection,zval * result)80 static void odbc_insert_new_result(odbc_connection *connection, zval *result)
81 {
82 	ZEND_ASSERT(Z_TYPE_P(result) == IS_OBJECT);
83 #if ZEND_DEBUG
84 	ZEND_ASSERT(instanceof_function(Z_OBJCE_P(result), odbc_result_ce));
85 #endif
86 
87 	odbc_result *res = Z_ODBC_RESULT_P(result);
88 
89 	res->index = connection->results.nNextFreeElement;
90 	zend_hash_index_add_new(&connection->results, res->index, result);
91 	Z_ADDREF_P(result);
92 }
93 
odbc_link_from_obj(zend_object * obj)94 static inline odbc_link *odbc_link_from_obj(zend_object *obj)
95 {
96 	return (odbc_link *)((char *)(obj) - XtOffsetOf(odbc_link, std));
97 }
98 
_close_pconn_with_res(zval * zv,void * p)99 static int _close_pconn_with_res(zval *zv, void *p)
100 {
101 	zend_resource *le = Z_RES_P(zv);
102 
103 	if (le->ptr == p) {
104 		return ZEND_HASH_APPLY_REMOVE;
105 	}
106 
107 	return ZEND_HASH_APPLY_KEEP;
108 }
109 
_close_pconn(zval * zv)110 static int _close_pconn(zval *zv)
111 {
112 	zend_resource *le = Z_RES_P(zv);
113 	if (le->type == le_pconn) {
114 		return ZEND_HASH_APPLY_REMOVE;
115 	} else {
116 		return ZEND_HASH_APPLY_KEEP;
117 	}
118 }
119 
120 /* disconnect, and if it fails, then issue a rollback for any pending transaction (lurcher) */
safe_odbc_disconnect(void * handle)121 static void safe_odbc_disconnect( void *handle )
122 {
123 	int ret = SQLDisconnect( handle );
124 
125 	if ( ret == SQL_ERROR )
126 	{
127 		SQLTransact( NULL, handle, SQL_ROLLBACK );
128 		SQLDisconnect( handle );
129 	}
130 }
131 
free_connection(odbc_connection * conn,bool persistent)132 static void free_connection(odbc_connection *conn, bool persistent)
133 {
134 	/* If aborted via timer expiration, don't try to call any unixODBC function */
135 	if (!(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) {
136 		safe_odbc_disconnect(conn->hdbc);
137 		SQLFreeConnect(conn->hdbc);
138 		SQLFreeEnv(conn->henv);
139 	}
140 
141 	conn->hdbc = NULL;
142 	conn->henv = NULL;
143 
144 	zend_hash_destroy(&conn->results);
145 
146 	pefree(conn, persistent);
147 
148 	ODBCG(num_links)--;
149 	if (persistent) {
150 		ODBCG(num_persistent)--;
151 	}
152 }
153 
odbc_link_free(odbc_link * link)154 static void odbc_link_free(odbc_link *link)
155 {
156 	ZEND_ASSERT(link->connection && "link has already been closed");
157 
158 	close_results_with_connection(link->connection);
159 
160 	if (!link->persistent) {
161 		free_connection(link->connection, link->persistent);
162 	}
163 
164 	link->connection = NULL;
165 
166 	if (link->hash) {
167 		zend_hash_del(&ODBCG(connections), link->hash);
168 		zend_string_release_ex(link->hash, link->persistent);
169 		link->hash = NULL;
170 	}
171 }
172 
odbc_connection_create_object(zend_class_entry * class_type)173 static zend_object *odbc_connection_create_object(zend_class_entry *class_type)
174 {
175 	odbc_link *intern = zend_object_alloc(sizeof(odbc_link), class_type);
176 
177 	zend_object_std_init(&intern->std, class_type);
178 	object_properties_init(&intern->std, class_type);
179 
180 	return &intern->std;
181 }
182 
odbc_connection_get_constructor(zend_object * object)183 static zend_function *odbc_connection_get_constructor(zend_object *object)
184 {
185 	zend_throw_error(NULL, "Cannot directly construct Odbc\\Connection, use odbc_connect() or odbc_pconnect() instead");
186 	return NULL;
187 }
188 
odbc_connection_cast_object(zend_object * obj,zval * result,int type)189 static zend_result odbc_connection_cast_object(zend_object *obj, zval *result, int type)
190 {
191 	if (type == IS_LONG) {
192 		ZVAL_LONG(result, obj->handle);
193 
194 		return SUCCESS;
195 	}
196 
197 	return zend_std_cast_object_tostring(obj, result, type);
198 }
199 
odbc_connection_free_obj(zend_object * obj)200 static void odbc_connection_free_obj(zend_object *obj)
201 {
202 	odbc_link *link = odbc_link_from_obj(obj);
203 
204 	if (link->connection) {
205 		odbc_link_free(link);
206 	}
207 
208 	zend_object_std_dtor(&link->std);
209 }
210 
odbc_result_from_obj(zend_object * obj)211 static inline odbc_result *odbc_result_from_obj(zend_object *obj)
212 {
213 	return (odbc_result *)((char *)(obj) - XtOffsetOf(odbc_result, std));
214 }
215 
odbc_result_create_object(zend_class_entry * class_type)216 static zend_object *odbc_result_create_object(zend_class_entry *class_type)
217 {
218 	odbc_result *intern = zend_object_alloc(sizeof(odbc_result), class_type);
219 
220 	zend_object_std_init(&intern->std, class_type);
221 	object_properties_init(&intern->std, class_type);
222 
223 	return &intern->std;
224 }
225 
odbc_result_get_constructor(zend_object * object)226 static zend_function *odbc_result_get_constructor(zend_object *object)
227 {
228 	zend_throw_error(NULL, "Cannot directly construct Odbc\\Result, use an appropriate odbc_* function instead");
229 	return NULL;
230 }
231 
odbc_result_cast_object(zend_object * obj,zval * result,int type)232 static zend_result odbc_result_cast_object(zend_object *obj, zval *result, int type)
233 {
234 	if (type == IS_LONG) {
235 		ZVAL_LONG(result, obj->handle);
236 
237 		return SUCCESS;
238 	}
239 
240 	return zend_std_cast_object_tostring(obj, result, type);
241 }
242 
odbc_result_free(odbc_result * res)243 static void odbc_result_free(odbc_result *res)
244 {
245 	ZEND_ASSERT(res->conn_ptr && "result has already been closed");
246 
247 	if (res->values) {
248 		for (int i = 0; i < res->numcols; i++) {
249 			if (res->values[i].value) {
250 				efree(res->values[i].value);
251 			}
252 		}
253 		efree(res->values);
254 		res->values = NULL;
255 		res->numcols = 0;
256 	}
257 
258 	/* If aborted via timer expiration, don't try to call any unixODBC function */
259 	if (res->stmt && !(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) {
260 #if defined(HAVE_SOLID) || defined(HAVE_SOLID_30) || defined(HAVE_SOLID_35)
261 		SQLTransact(res->conn_ptr->henv, res->conn_ptr->hdbc,
262 					(SQLUSMALLINT) SQL_COMMIT);
263 #endif
264 		SQLFreeStmt(res->stmt,SQL_DROP);
265 		/* We don't want the connection to be closed after the last statement has been closed
266 		 * Connections will be closed on shutdown
267 		 */
268 		res->stmt = NULL;
269 	}
270 	if (res->param_info) {
271 		efree(res->param_info);
272 		res->param_info = NULL;
273 	}
274 
275 	HashTable *results = &res->conn_ptr->results;
276 	res->conn_ptr = NULL;
277 	zend_result status = zend_hash_index_del(results, res->index);
278 	ZEND_ASSERT(status == SUCCESS);
279 }
280 
odbc_result_free_obj(zend_object * obj)281 static void odbc_result_free_obj(zend_object *obj)
282 {
283 	odbc_result *result = odbc_result_from_obj(obj);
284 
285 	if (result->conn_ptr) {
286 		odbc_result_free(result);
287 	}
288 
289 	zend_object_std_dtor(&result->std);
290 }
291 
292 #define SAFE_SQL_NTS(n) ((SQLSMALLINT) ((n)?(SQL_NTS):0))
293 
294 PHP_ODBC_API ZEND_DECLARE_MODULE_GLOBALS(odbc)
295 static PHP_GINIT_FUNCTION(odbc);
296 static PHP_GSHUTDOWN_FUNCTION(odbc);
297 
298 /* {{{ odbc_module_entry */
299 zend_module_entry odbc_module_entry = {
300 	STANDARD_MODULE_HEADER,
301 	"odbc",
302 	ext_functions,
303 	PHP_MINIT(odbc),
304 	PHP_MSHUTDOWN(odbc),
305 	PHP_RINIT(odbc),
306 	PHP_RSHUTDOWN(odbc),
307 	PHP_MINFO(odbc),
308 	PHP_ODBC_VERSION,
309 	PHP_MODULE_GLOBALS(odbc),
310 	PHP_GINIT(odbc),
311 	PHP_GSHUTDOWN(odbc),
312 	NULL,
313 	STANDARD_MODULE_PROPERTIES_EX
314 };
315 /* }}} */
316 
317 #ifdef COMPILE_DL_ODBC
318 #ifdef ZTS
319 ZEND_TSRMLS_CACHE_DEFINE()
320 #endif
ZEND_GET_MODULE(odbc)321 ZEND_GET_MODULE(odbc)
322 #endif
323 
324 static void close_results_with_connection(odbc_connection *conn)
325 {
326 	zval *p;
327 
328 	ZEND_HASH_FOREACH_VAL(&conn->results, p) {
329 		odbc_result *result = Z_ODBC_RESULT_P(p);
330 		if (result->conn_ptr) {
331 			odbc_result_free(result);
332 		}
333 	} ZEND_HASH_FOREACH_END();
334 
335 	zend_hash_clean(&conn->results);
336 }
337 
338 /* {{{ void _close_odbc_pconn */
_close_odbc_pconn(zend_resource * rsrc)339 static void _close_odbc_pconn(zend_resource *rsrc)
340 {
341 	odbc_connection *conn = (odbc_connection *)rsrc->ptr;
342 
343 	close_results_with_connection(conn);
344 	free_connection(conn, true);
345 
346 	rsrc->ptr = NULL;
347 }
348 /* }}} */
349 
350 /* {{{ PHP_INI_DISP(display_link_nums) */
PHP_INI_DISP(display_link_nums)351 static PHP_INI_DISP(display_link_nums)
352 {
353 	char *value;
354 
355 	if (type == PHP_INI_DISPLAY_ORIG && ini_entry->modified) {
356 		value = ZSTR_VAL(ini_entry->orig_value);
357 	} else if (ini_entry->value) {
358 		value = ZSTR_VAL(ini_entry->value);
359 	} else {
360 		value = NULL;
361 	}
362 
363 	if (value) {
364 		if (atoi(value) == -1) {
365 			PUTS("Unlimited");
366 		} else {
367 			php_printf("%s", value);
368 		}
369 	}
370 }
371 /* }}} */
372 
373 /* {{{ PHP_INI_DISP(display_defPW) */
PHP_INI_DISP(display_defPW)374 static PHP_INI_DISP(display_defPW)
375 {
376 	char *value;
377 
378 	if (type == PHP_INI_DISPLAY_ORIG && ini_entry->modified) {
379 		value = ZSTR_VAL(ini_entry->orig_value);
380 	} else if (ini_entry->value) {
381 		value = ZSTR_VAL(ini_entry->value);
382 	} else {
383 		value = NULL;
384 	}
385 
386 	if (value) {
387 #if PHP_DEBUG
388 		php_printf("%s", value);
389 #else
390 		PUTS("********");
391 #endif
392 	} else {
393 		if (PG(html_errors)) {
394 			PUTS("<i>no value</i>");
395 		} else {
396 			PUTS("no value");
397 		}
398 	}
399 }
400 /* }}} */
401 
402 /* {{{ PHP_INI_DISP(display_binmode) */
PHP_INI_DISP(display_binmode)403 static PHP_INI_DISP(display_binmode)
404 {
405 	char *value;
406 
407 	if (type == PHP_INI_DISPLAY_ORIG && ini_entry->modified) {
408 		value = ZSTR_VAL(ini_entry->orig_value);
409 	} else if (ini_entry->value) {
410 		value = ZSTR_VAL(ini_entry->value);
411 	} else {
412 		value = NULL;
413 	}
414 
415 	if (value) {
416 		switch(atoi(value)) {
417 			case 0:
418 				PUTS("passthru");
419 				break;
420 			case 1:
421 				PUTS("return as is");
422 				break;
423 			case 2:
424 				PUTS("return as char");
425 				break;
426 		}
427 	}
428 }
429 /* }}} */
430 
431 /* {{{ PHP_INI_DISP(display_lrl) */
PHP_INI_DISP(display_lrl)432 static PHP_INI_DISP(display_lrl)
433 {
434 	char *value;
435 
436 	if (type == PHP_INI_DISPLAY_ORIG && ini_entry->modified) {
437 		value = ZSTR_VAL(ini_entry->orig_value);
438 	} else if (ini_entry->value) {
439 		value = ZSTR_VAL(ini_entry->value);
440 	} else {
441 		value = NULL;
442 	}
443 
444 	if (value) {
445 		if (atoi(value) <= 0) {
446 			PUTS("Passthru");
447 		} else {
448 			php_printf("return up to %s bytes", value);
449 		}
450 	}
451 }
452 /* }}} */
453 
454 
455 /* {{{ PHP_INI_DISP(display_cursortype) */
PHP_INI_DISP(display_cursortype)456 static PHP_INI_DISP(display_cursortype)
457 {
458 	char *value;
459 
460 	if (type == PHP_INI_DISPLAY_ORIG && ini_entry->modified) {
461 		value = ZSTR_VAL(ini_entry->orig_value);
462 	} else if (ini_entry->value) {
463 		value = ZSTR_VAL(ini_entry->value);
464 	} else {
465 		value = NULL;
466 	}
467 
468 	if (value) {
469 		switch (atoi (value))
470 		  {
471 		    case SQL_CURSOR_FORWARD_ONLY:
472 				PUTS ("Forward Only cursor");
473 				break;
474 
475 			case SQL_CURSOR_STATIC:
476 			    PUTS ("Static cursor");
477 				break;
478 
479 			case SQL_CURSOR_KEYSET_DRIVEN:
480 				PUTS ("Keyset driven cursor");
481 				break;
482 
483 			case SQL_CURSOR_DYNAMIC:
484 				PUTS ("Dynamic cursor");
485 				break;
486 
487 			default:
488 				php_printf("Unknown cursor model %s", value);
489 				break;
490 		  }
491 	}
492 }
493 
494 /* }}} */
495 
496 /* {{{ PHP_INI_BEGIN */
497 PHP_INI_BEGIN()
498 	STD_PHP_INI_BOOLEAN("odbc.allow_persistent", "1", PHP_INI_SYSTEM, OnUpdateBool,
499 			allow_persistent, zend_odbc_globals, odbc_globals)
500 	STD_PHP_INI_ENTRY_EX("odbc.max_persistent",  "-1", PHP_INI_SYSTEM, OnUpdateLong,
501 			max_persistent, zend_odbc_globals, odbc_globals, display_link_nums)
502 	STD_PHP_INI_ENTRY_EX("odbc.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong,
503 			max_links, zend_odbc_globals, odbc_globals, display_link_nums)
504 	STD_PHP_INI_ENTRY("odbc.default_db", NULL, PHP_INI_ALL, OnUpdateString,
505 			defDB, zend_odbc_globals, odbc_globals)
506 	STD_PHP_INI_ENTRY("odbc.default_user", NULL, PHP_INI_ALL, OnUpdateString,
507 			defUser, zend_odbc_globals, odbc_globals)
508 	STD_PHP_INI_ENTRY_EX("odbc.default_pw", NULL, PHP_INI_ALL, OnUpdateString,
509 			defPW, zend_odbc_globals, odbc_globals, display_defPW)
510 	STD_PHP_INI_ENTRY_EX("odbc.defaultlrl", "4096", PHP_INI_ALL, OnUpdateLong,
511 			defaultlrl, zend_odbc_globals, odbc_globals, display_lrl)
512 	STD_PHP_INI_ENTRY_EX("odbc.defaultbinmode", "1", PHP_INI_ALL, OnUpdateLong,
513 			defaultbinmode, zend_odbc_globals, odbc_globals, display_binmode)
514 	STD_PHP_INI_BOOLEAN("odbc.check_persistent", "1", PHP_INI_SYSTEM, OnUpdateBool,
515 			check_persistent, zend_odbc_globals, odbc_globals)
516 	STD_PHP_INI_ENTRY_EX("odbc.default_cursortype", "3", PHP_INI_ALL, OnUpdateLong,
517 			default_cursortype, zend_odbc_globals, odbc_globals, display_cursortype)
PHP_INI_END()518 PHP_INI_END()
519 /* }}} */
520 
521 static PHP_GINIT_FUNCTION(odbc)
522 {
523 #if defined(COMPILE_DL_ODBC) && defined(ZTS)
524 	ZEND_TSRMLS_CACHE_UPDATE();
525 #endif
526 	odbc_globals->num_persistent = 0;
527 	zend_hash_init(&odbc_globals->connections, 0, NULL, NULL, true);
528 	GC_MAKE_PERSISTENT_LOCAL(&odbc_globals->connections);
529 }
530 
PHP_GSHUTDOWN_FUNCTION(odbc)531 static PHP_GSHUTDOWN_FUNCTION(odbc)
532 {
533 	zend_hash_destroy(&odbc_globals->connections);
534 }
535 
536 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(odbc)537 PHP_MINIT_FUNCTION(odbc)
538 {
539 #ifdef SQLANY_BUG
540 	ODBC_SQL_CONN_T foobar;
541 	RETCODE rc;
542 #endif
543 
544 	REGISTER_INI_ENTRIES();
545 	le_pconn = zend_register_list_destructors_ex(NULL, _close_odbc_pconn, "odbc link persistent", module_number);
546 	odbc_module_entry.type = type;
547 
548 	register_odbc_symbols(module_number);
549 
550 	odbc_connection_ce = register_class_Odbc_Connection();
551 	odbc_connection_ce->create_object = odbc_connection_create_object;
552 	odbc_connection_ce->default_object_handlers = &odbc_connection_object_handlers;
553 
554 	memcpy(&odbc_connection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
555 	odbc_connection_object_handlers.offset = XtOffsetOf(odbc_link, std);
556 	odbc_connection_object_handlers.free_obj = odbc_connection_free_obj;
557 	odbc_connection_object_handlers.get_constructor = odbc_connection_get_constructor;
558 	odbc_connection_object_handlers.clone_obj = NULL;
559 	odbc_connection_object_handlers.cast_object = odbc_connection_cast_object;
560 	odbc_connection_object_handlers.compare = zend_objects_not_comparable;
561 
562 	odbc_result_ce = register_class_Odbc_Result();
563 	odbc_result_ce->create_object = odbc_result_create_object;
564 	odbc_result_ce->default_object_handlers = &odbc_result_object_handlers;
565 
566 	memcpy(&odbc_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
567 	odbc_result_object_handlers.offset = XtOffsetOf(odbc_result, std);
568 	odbc_result_object_handlers.free_obj = odbc_result_free_obj;
569 	odbc_result_object_handlers.get_constructor = odbc_result_get_constructor;
570 	odbc_result_object_handlers.clone_obj = NULL;
571 	odbc_result_object_handlers.cast_object = odbc_result_cast_object;
572 	odbc_result_object_handlers.compare = zend_objects_not_comparable;
573 
574 #if defined(HAVE_IBMDB2) && defined(_AIX)
575 	/* atexit() handler in the DB2/AIX library segfaults in PHP CLI */
576 	/* DB2NOEXITLIST env variable prevents DB2 from invoking atexit() */
577 	putenv("DB2NOEXITLIST=TRUE");
578 #endif
579 
580 	return SUCCESS;
581 }
582 /* }}} */
583 
584 /* {{{ PHP_RINIT_FUNCTION */
PHP_RINIT_FUNCTION(odbc)585 PHP_RINIT_FUNCTION(odbc)
586 {
587 	ODBCG(defConn) = -1;
588 	ODBCG(num_links) = ODBCG(num_persistent);
589 	memset(ODBCG(laststate), '\0', 6);
590 	memset(ODBCG(lasterrormsg), '\0', SQL_MAX_MESSAGE_LENGTH);
591 	return SUCCESS;
592 }
593 /* }}} */
594 
595 /* {{{ PHP_RSHUTDOWN_FUNCTION */
PHP_RSHUTDOWN_FUNCTION(odbc)596 PHP_RSHUTDOWN_FUNCTION(odbc)
597 {
598 	return SUCCESS;
599 }
600 /* }}} */
601 
602 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(odbc)603 PHP_MSHUTDOWN_FUNCTION(odbc)
604 {
605 	UNREGISTER_INI_ENTRIES();
606 	return SUCCESS;
607 }
608 /* }}} */
609 
610 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(odbc)611 PHP_MINFO_FUNCTION(odbc)
612 {
613 	char buf[32];
614 
615 	php_info_print_table_start();
616 	php_info_print_table_row(2, "ODBC Support", "enabled");
617 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ODBCG(num_persistent));
618 	php_info_print_table_row(2, "Active Persistent Links", buf);
619 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ODBCG(num_links));
620 	php_info_print_table_row(2, "Active Links", buf);
621 	php_info_print_table_row(2, "ODBC library", PHP_ODBC_TYPE);
622 #ifdef ODBCVER
623 	snprintf(buf, sizeof(buf), "0x%.4x", ODBCVER);
624 	php_info_print_table_row(2, "ODBCVER", buf);
625 #endif
626 #ifndef PHP_WIN32
627 	php_info_print_table_row(2, "ODBC_CFLAGS", PHP_ODBC_CFLAGS);
628 	php_info_print_table_row(2, "ODBC_LFLAGS", PHP_ODBC_LFLAGS);
629 	php_info_print_table_row(2, "ODBC_LIBS", PHP_ODBC_LIBS);
630 #endif
631 	php_info_print_table_end();
632 
633 	DISPLAY_INI_ENTRIES();
634 
635 }
636 /* }}} */
637 
638 /* {{{ odbc_sql_error */
odbc_sql_error(ODBC_SQL_ERROR_PARAMS)639 void odbc_sql_error(ODBC_SQL_ERROR_PARAMS)
640 {
641 	SQLINTEGER	error;        /* Not used */
642 	SQLSMALLINT	errormsgsize; /* Not used */
643 	RETCODE rc;
644 	ODBC_SQL_ENV_T henv;
645 	ODBC_SQL_CONN_T conn;
646 
647 	if (conn_resource) {
648 		henv = conn_resource->henv;
649 		conn = conn_resource->hdbc;
650 	} else {
651 		henv = SQL_NULL_HENV;
652 		conn = SQL_NULL_HDBC;
653 	}
654 
655 	/* This leads to an endless loop in many drivers!
656 	 *
657 	   while(henv != SQL_NULL_HENV){
658 		do {
659 	 */
660 	rc = SQLError(henv, conn, stmt, (SQLCHAR *) ODBCG(laststate), &error, (SQLCHAR *) ODBCG(lasterrormsg), sizeof(ODBCG(lasterrormsg))-1, &errormsgsize);
661 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
662 		snprintf(ODBCG(laststate), sizeof(ODBCG(laststate)), "HY000");
663 		snprintf(ODBCG(lasterrormsg), sizeof(ODBCG(lasterrormsg)), "Failed to fetch error message");
664 	}
665 	if (conn_resource) {
666 		memcpy(conn_resource->laststate, ODBCG(laststate), sizeof(ODBCG(laststate)));
667 		memcpy(conn_resource->lasterrormsg, ODBCG(lasterrormsg), sizeof(ODBCG(lasterrormsg)));
668 	}
669 	if (func) {
670 		php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s in %s", ODBCG(lasterrormsg), ODBCG(laststate), func);
671 	} else {
672 		php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s", ODBCG(lasterrormsg), ODBCG(laststate));
673 	}
674 	/*
675 		} while (SQL_SUCCEEDED(rc));
676 	}
677 	*/
678 }
679 /* }}} */
680 
681 /* {{{ php_odbc_fetch_attribs */
php_odbc_fetch_attribs(INTERNAL_FUNCTION_PARAMETERS,int mode)682 void php_odbc_fetch_attribs(INTERNAL_FUNCTION_PARAMETERS, int mode)
683 {
684 	odbc_result *result;
685 	zval *pv_res;
686 	zend_long flag;
687 
688 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &flag) == FAILURE) {
689 		RETURN_THROWS();
690 	}
691 
692 	result = Z_ODBC_RESULT_P(pv_res);
693 	CHECK_ODBC_RESULT(result);
694 
695 	if (mode) {
696 		result->longreadlen = flag;
697 	} else {
698 		result->binmode = flag;
699 	}
700 
701 	RETURN_TRUE;
702 }
703 /* }}} */
704 
705 /* {{{ odbc_bindcols */
odbc_bindcols(odbc_result * result)706 void odbc_bindcols(odbc_result *result)
707 {
708 	RETCODE rc;
709 	int i;
710 	SQLSMALLINT 	colnamelen; /* Not used */
711 	SQLLEN      	displaysize;
712 	SQLUSMALLINT	colfieldid;
713 	int		charextraalloc;
714 
715 	result->values = (odbc_result_value *) safe_emalloc(sizeof(odbc_result_value), result->numcols, 0);
716 
717 	result->longreadlen = ODBCG(defaultlrl);
718 	result->binmode = ODBCG(defaultbinmode);
719 
720 	for(i = 0; i < result->numcols; i++) {
721 		charextraalloc = 0;
722 		colfieldid = SQL_COLUMN_DISPLAY_SIZE;
723 
724 		rc = PHP_ODBC_SQLCOLATTRIBUTE(result->stmt, (SQLUSMALLINT)(i+1), PHP_ODBC_SQL_DESC_NAME,
725 				result->values[i].name, sizeof(result->values[i].name), &colnamelen, 0);
726 		result->values[i].coltype = 0;
727 		rc = PHP_ODBC_SQLCOLATTRIBUTE(result->stmt, (SQLUSMALLINT)(i+1), SQL_COLUMN_TYPE,
728 				NULL, 0, NULL, &result->values[i].coltype);
729 
730 		/* Don't bind LONG / BINARY columns, so that fetch behaviour can
731 		 * be controlled by odbc_binmode() / odbc_longreadlen()
732 		 */
733 
734 		switch(result->values[i].coltype) {
735 			case SQL_BINARY:
736 			case SQL_VARBINARY:
737 			case SQL_LONGVARBINARY:
738 			case SQL_LONGVARCHAR:
739 #if defined(ODBCVER) && (ODBCVER >= 0x0300)
740 			case SQL_WLONGVARCHAR:
741 #endif
742 				result->values[i].value = NULL;
743 				break;
744 
745 #ifdef HAVE_ADABAS
746 			case SQL_TIMESTAMP:
747 				result->values[i].value = (char *)emalloc(27);
748 				SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, result->values[i].value,
749 							27, &result->values[i].vallen);
750 				break;
751 #endif /* HAVE_ADABAS */
752 			case SQL_CHAR:
753 			case SQL_VARCHAR:
754 #if defined(ODBCVER) && (ODBCVER >= 0x0300)
755 			case SQL_WCHAR:
756 			case SQL_WVARCHAR:
757 				colfieldid = SQL_DESC_OCTET_LENGTH;
758 #else
759 				charextraalloc = 1;
760 #endif
761 				/* TODO: Check this is the intended behaviour */
762 				ZEND_FALLTHROUGH;
763 			default:
764 				rc = PHP_ODBC_SQLCOLATTRIBUTE(result->stmt, (SQLUSMALLINT)(i+1), colfieldid,
765 								NULL, 0, NULL, &displaysize);
766 				if (rc != SQL_SUCCESS) {
767 					displaysize = 0;
768 				}
769 #if defined(ODBCVER) && (ODBCVER >= 0x0300)
770 				if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO && colfieldid == SQL_DESC_OCTET_LENGTH) {
771 					SQLINTEGER err;
772 					SQLCHAR errtxt[128];
773 					SQLCHAR state[6];
774 
775 					memset(errtxt, '\0', 128);
776 					memset(state, '\0', 6);
777 
778 					if (SQL_SUCCESS == SQLGetDiagRec(SQL_HANDLE_STMT, result->stmt, 1, state, &err, errtxt, 128, NULL)) {
779 						errtxt[127] = '\0';
780 						state[5] = '\0';
781 						php_error_docref(NULL, E_WARNING, "SQLColAttribute can't handle SQL_DESC_OCTET_LENGTH: [%s] %s", state, errtxt);
782 					}
783 					 /* This is  a quirk for ODBC 2.0 compatibility for broken driver implementations.
784 					  */
785 					charextraalloc = 1;
786 					rc = SQLColAttributes(result->stmt, (SQLUSMALLINT)(i+1), SQL_COLUMN_DISPLAY_SIZE,
787 								NULL, 0, NULL, &displaysize);
788 					if (rc != SQL_SUCCESS) {
789 						displaysize = 0;
790 					}
791 				}
792 
793 				/* Workaround for drivers that report NVARCHAR(MAX) columns as SQL_WVARCHAR with size 0 (bug #69975) */
794 				if (result->values[i].coltype == SQL_WVARCHAR && displaysize == 0) {
795 					result->values[i].coltype = SQL_WLONGVARCHAR;
796 					result->values[i].value = NULL;
797 					break;
798 				}
799 #endif
800 				/* Workaround for drivers that report VARCHAR(MAX) columns as SQL_VARCHAR (bug #73725) */
801 				if (SQL_VARCHAR == result->values[i].coltype && displaysize == 0) {
802 					result->values[i].coltype = SQL_LONGVARCHAR;
803 					result->values[i].value = NULL;
804 					break;
805 				}
806 
807 				/* Workaround for Oracle ODBC Driver bug (#50162) when fetching TIMESTAMP column */
808 				if (result->values[i].coltype == SQL_TIMESTAMP) {
809 					displaysize += 3;
810 				}
811 
812 				if (charextraalloc) {
813 					/* Since we don't know the exact # of bytes, allocate extra */
814 					displaysize *= 4;
815 				}
816 				result->values[i].value = (char *)emalloc(displaysize + 1);
817 				rc = SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, result->values[i].value,
818 							displaysize + 1, &result->values[i].vallen);
819 				break;
820 		}
821 	}
822 }
823 /* }}} */
824 
825 /* {{{ odbc_transact */
odbc_transact(INTERNAL_FUNCTION_PARAMETERS,int type)826 void odbc_transact(INTERNAL_FUNCTION_PARAMETERS, int type)
827 {
828 	RETCODE rc;
829 	zval *pv_conn;
830 
831 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_conn, odbc_connection_ce) == FAILURE) {
832 		RETURN_THROWS();
833 	}
834 
835 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
836 	CHECK_ODBC_CONNECTION(conn);
837 
838 	rc = SQLTransact(conn->henv, conn->hdbc, (SQLUSMALLINT)((type)?SQL_COMMIT:SQL_ROLLBACK));
839 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
840 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLTransact");
841 		RETURN_FALSE;
842 	}
843 
844 	RETURN_TRUE;
845 }
846 /* }}} */
847 
848 /* {{{ odbc_column_lengths */
odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS,int type)849 void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type)
850 {
851 	odbc_result *result;
852 #if defined(HAVE_SOLID) || defined(HAVE_SOLID_30)
853 	/* this seems to be necessary for Solid2.3 ( tested by
854 	 * tammy@synchronis.com) and Solid 3.0 (tested by eric@terra.telemediair.nl)
855 	 * Solid does not seem to declare a SQLINTEGER, but it does declare a
856 	 * SQL_INTEGER which does not work (despite being the same type as a SDWORD.
857 	 * Solid 3.5 does not have this issue.
858 	 */
859 	SDWORD len;
860 #else
861 	SQLLEN len;
862 #endif
863 	zval *pv_res;
864 	zend_long pv_num;
865 
866 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) {
867 		RETURN_THROWS();
868 	}
869 
870 	result = Z_ODBC_RESULT_P(pv_res);
871 	CHECK_ODBC_RESULT(result);
872 
873 	if (pv_num < 1) {
874 		zend_argument_value_error(2, "must be greater than 0");
875 		RETURN_THROWS();
876 	}
877 
878 	if (result->numcols == 0) {
879 		php_error_docref(NULL, E_WARNING, "No tuples available at this result index");
880 		RETURN_FALSE;
881 	}
882 
883 	if (pv_num > result->numcols) {
884 		php_error_docref(NULL, E_WARNING, "Field index larger than number of fields");
885 		RETURN_FALSE;
886 	}
887 
888 	PHP_ODBC_SQLCOLATTRIBUTE(result->stmt, (SQLUSMALLINT)pv_num, (SQLUSMALLINT) (type?SQL_COLUMN_SCALE:SQL_COLUMN_PRECISION), NULL, 0, NULL, &len);
889 
890 	RETURN_LONG(len);
891 }
892 /* }}} */
893 
894 /* Main User Functions */
895 
896 /* {{{ Close all ODBC connections */
PHP_FUNCTION(odbc_close_all)897 PHP_FUNCTION(odbc_close_all)
898 {
899 	zval *zv;
900 
901 	if (zend_parse_parameters_none() == FAILURE) {
902 		RETURN_THROWS();
903 	}
904 
905 	/* Loop through the link list, now close all links and their results */
906 	ZEND_HASH_FOREACH_VAL(&ODBCG(connections), zv) {
907 		odbc_link *link = Z_ODBC_LINK_P(zv);
908 		if (link->connection) {
909 			odbc_link_free(link);
910 		}
911 	} ZEND_HASH_FOREACH_END();
912 
913 	zend_hash_clean(&ODBCG(connections));
914 
915 	zend_hash_apply(&EG(persistent_list), _close_pconn);
916 }
917 /* }}} */
918 
919 /* {{{ Handle binary column data */
PHP_FUNCTION(odbc_binmode)920 PHP_FUNCTION(odbc_binmode)
921 {
922 	php_odbc_fetch_attribs(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
923 }
924 /* }}} */
925 
926 /* {{{ Handle LONG columns */
PHP_FUNCTION(odbc_longreadlen)927 PHP_FUNCTION(odbc_longreadlen)
928 {
929 	php_odbc_fetch_attribs(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
930 }
931 /* }}} */
932 
933 /* {{{ Prepares a statement for execution */
PHP_FUNCTION(odbc_prepare)934 PHP_FUNCTION(odbc_prepare)
935 {
936 	zval *pv_conn;
937 	char *query;
938 	size_t query_len;
939 	odbc_result *result = NULL;
940 	RETCODE rc;
941 	int i;
942 #ifdef HAVE_SQL_EXTENDED_FETCH
943 	SQLUINTEGER      scrollopts;
944 #endif
945 
946 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_conn, odbc_connection_ce, &query, &query_len) == FAILURE) {
947 		RETURN_THROWS();
948 	}
949 
950 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
951 	CHECK_ODBC_CONNECTION(conn);
952 
953 	object_init_ex(return_value, odbc_result_ce);
954 	result = Z_ODBC_RESULT_P(return_value);
955 
956 	result->numparams = 0;
957 	result->param_info = NULL;
958 
959 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
960 	if (rc == SQL_INVALID_HANDLE) {
961 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
962 		zval_ptr_dtor(return_value);
963 		RETURN_FALSE;
964 	}
965 
966 	if (rc == SQL_ERROR) {
967 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
968 		zval_ptr_dtor(return_value);
969 		RETURN_FALSE;
970 	}
971 
972 #ifdef HAVE_SQL_EXTENDED_FETCH
973 	/* Solid doesn't have ExtendedFetch, if DriverManager is used, get Info,
974 	   whether Driver supports ExtendedFetch */
975 	rc = SQLGetInfo(conn->hdbc, SQL_FETCH_DIRECTION, (void *) &scrollopts, sizeof(scrollopts), NULL);
976 	if (rc == SQL_SUCCESS) {
977 		if ((result->fetch_abs = (scrollopts & SQL_FD_FETCH_ABSOLUTE))) {
978 			/* Try to set CURSOR_TYPE to dynamic. Driver will replace this with other
979 			   type if not possible.
980 			*/
981 			SQLSetStmtOption(result->stmt, SQL_CURSOR_TYPE, ODBCG(default_cursortype));
982 		}
983 	} else {
984 		result->fetch_abs = 0;
985 	}
986 #endif
987 
988 	rc = SQLPrepare(result->stmt, (SQLCHAR *) query, SQL_NTS);
989 	switch (rc) {
990 		case SQL_SUCCESS:
991 			break;
992 		case SQL_SUCCESS_WITH_INFO:
993 			odbc_sql_error(conn, result->stmt, "SQLPrepare");
994 			break;
995 		default:
996 			odbc_sql_error(conn, result->stmt, "SQLPrepare");
997 			zval_ptr_dtor(return_value);
998 			RETURN_FALSE;
999 	}
1000 
1001 	SQLNumParams(result->stmt, &(result->numparams));
1002 	SQLNumResultCols(result->stmt, &(result->numcols));
1003 
1004 	if (result->numcols > 0) {
1005 		odbc_bindcols(result);
1006 	} else {
1007 		result->values = NULL;
1008 	}
1009 	result->conn_ptr = conn;
1010 	result->fetched = 0;
1011 
1012 	result->param_info = (odbc_param_info *) safe_emalloc(sizeof(odbc_param_info), result->numparams, 0);
1013 	for (i=0;i<result->numparams;i++) {
1014 		rc = SQLDescribeParam(result->stmt, (SQLUSMALLINT)(i+1), &result->param_info[i].sqltype, &result->param_info[i].precision,
1015 													&result->param_info[i].scale, &result->param_info[i].nullable);
1016 		if (rc == SQL_ERROR) {
1017 			odbc_sql_error(result->conn_ptr, result->stmt, "SQLDescribeParameter");
1018 			SQLFreeStmt(result->stmt, SQL_RESET_PARAMS);
1019 			efree(result->param_info);
1020 			zval_ptr_dtor(return_value);
1021 			RETURN_FALSE;
1022 		}
1023 	}
1024 
1025 	odbc_insert_new_result(conn, return_value);
1026 }
1027 /* }}} */
1028 
1029 /*
1030  * Execute prepared SQL statement. Supports only input parameters.
1031  */
1032 
1033 typedef struct odbc_params_t {
1034 	SQLLEN vallen;
1035 	int fp;
1036 	zend_string *zstr;
1037 } odbc_params_t;
1038 
odbc_release_params(odbc_result * result,odbc_params_t * params)1039 static void odbc_release_params(odbc_result *result, odbc_params_t *params) {
1040 	SQLFreeStmt(result->stmt, SQL_RESET_PARAMS);
1041 	for (int i = 0; i < result->numparams; i++) {
1042 		if (params[i].fp != -1) {
1043 			close(params[i].fp);
1044 		}
1045 		if (params[i].zstr) {
1046 			zend_string_release(params[i].zstr);
1047 		}
1048 	}
1049 	efree(params);
1050 }
1051 
1052 /* {{{ Execute a prepared statement */
PHP_FUNCTION(odbc_execute)1053 PHP_FUNCTION(odbc_execute)
1054 {
1055 	zval *pv_res, *tmp;
1056 	HashTable *pv_param_ht = (HashTable *) &zend_empty_array;
1057 	odbc_params_t *params = NULL;
1058 	char *filename;
1059 	SQLSMALLINT ctype;
1060 	odbc_result *result;
1061 	int i, ne;
1062 	RETCODE rc;
1063 
1064 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|h", &pv_res, odbc_result_ce, &pv_param_ht) == FAILURE) {
1065 		RETURN_THROWS();
1066 	}
1067 
1068 	result = Z_ODBC_RESULT_P(pv_res);
1069 	CHECK_ODBC_RESULT(result);
1070 
1071 	if (result->numparams > 0) {
1072 		if ((ne = zend_hash_num_elements(pv_param_ht)) < result->numparams) {
1073 			php_error_docref(NULL, E_WARNING, "Not enough parameters (%d should be %d) given", ne, result->numparams);
1074 			RETURN_FALSE;
1075 		}
1076 
1077 		params = (odbc_params_t *)safe_emalloc(sizeof(odbc_params_t), result->numparams, 0);
1078 		for(i = 0; i < result->numparams; i++) {
1079 			params[i].fp = -1;
1080 			params[i].zstr = NULL;
1081 		}
1082 
1083 		i = 1;
1084 		ZEND_HASH_FOREACH_VAL(pv_param_ht, tmp) {
1085 			unsigned char otype = Z_TYPE_P(tmp);
1086 			zend_string *tmpstr = zval_try_get_string(tmp);
1087 			if (!tmpstr) {
1088 				odbc_release_params(result, params);
1089 				RETURN_THROWS();
1090 			}
1091 
1092 			params[i-1].vallen = ZSTR_LEN(tmpstr);
1093 			params[i-1].fp = -1;
1094 			params[i-1].zstr = tmpstr;
1095 
1096 			if (IS_SQL_BINARY(result->param_info[i-1].sqltype)) {
1097 				ctype = SQL_C_BINARY;
1098 			} else {
1099 				ctype = SQL_C_CHAR;
1100 			}
1101 
1102 			if (ZSTR_LEN(tmpstr) > 2 &&
1103 				ZSTR_VAL(tmpstr)[0] == '\'' &&
1104 				ZSTR_VAL(tmpstr)[ZSTR_LEN(tmpstr) - 1] == '\'') {
1105 
1106 				if (ZSTR_LEN(tmpstr) != strlen(ZSTR_VAL(tmpstr))) {
1107 					odbc_release_params(result, params);
1108 					RETURN_FALSE;
1109 				}
1110 				filename = estrndup(&ZSTR_VAL(tmpstr)[1], ZSTR_LEN(tmpstr) - 2);
1111 				filename[strlen(filename)] = '\0';
1112 
1113 				/* Check the basedir */
1114 				if (php_check_open_basedir(filename)) {
1115 					efree(filename);
1116 					odbc_release_params(result, params);
1117 					RETURN_FALSE;
1118 				}
1119 
1120 				if ((params[i-1].fp = open(filename,O_RDONLY)) == -1) {
1121 					php_error_docref(NULL, E_WARNING,"Can't open file %s", filename);
1122 					odbc_release_params(result, params);
1123 					efree(filename);
1124 					RETURN_FALSE;
1125 				}
1126 
1127 				efree(filename);
1128 
1129 				params[i-1].vallen = SQL_LEN_DATA_AT_EXEC(0);
1130 
1131 				rc = SQLBindParameter(result->stmt, (SQLUSMALLINT)i, SQL_PARAM_INPUT,
1132 									  ctype, result->param_info[i-1].sqltype, result->param_info[i-1].precision, result->param_info[i-1].scale,
1133 									  (void *)(intptr_t)params[i-1].fp, 0,
1134 									  &params[i-1].vallen);
1135 			} else {
1136 #ifdef HAVE_DBMAKER
1137 				precision = params[i-1].vallen;
1138 #endif
1139 				if (otype == IS_NULL) {
1140 					params[i-1].vallen = SQL_NULL_DATA;
1141 				}
1142 
1143 				rc = SQLBindParameter(result->stmt, (SQLUSMALLINT)i, SQL_PARAM_INPUT,
1144 									  ctype, result->param_info[i-1].sqltype, result->param_info[i-1].precision, result->param_info[i-1].scale,
1145 									  ZSTR_VAL(tmpstr), 0,
1146 									  &params[i-1].vallen);
1147 			}
1148 			if (rc == SQL_ERROR) {
1149 				odbc_sql_error(result->conn_ptr, result->stmt, "SQLBindParameter");
1150 				odbc_release_params(result, params);
1151 				RETURN_FALSE;
1152 			}
1153 			if (++i > result->numparams) break;
1154 		} ZEND_HASH_FOREACH_END();
1155 	}
1156 	/* Close cursor, needed for doing multiple selects */
1157 	rc = SQLFreeStmt(result->stmt, SQL_CLOSE);
1158 
1159 	if (rc == SQL_ERROR) {
1160 		odbc_sql_error(result->conn_ptr, result->stmt, "SQLFreeStmt");
1161 	}
1162 
1163 	result->fetched = 0;
1164 	rc = SQLExecute(result->stmt);
1165 	switch (rc) {
1166 		case SQL_NEED_DATA: {
1167 			char buf[4096];
1168 			int fp, nbytes;
1169 			while (rc == SQL_NEED_DATA) {
1170 				rc = SQLParamData(result->stmt, (void*)&fp);
1171 				if (rc == SQL_NEED_DATA) {
1172 					while ((nbytes = read(fp, &buf, 4096)) > 0) {
1173 						SQLPutData(result->stmt, (void*)&buf, nbytes);
1174 					}
1175 				}
1176 			}
1177 			break;
1178 		}
1179 		case SQL_SUCCESS:
1180 			break;
1181 		case SQL_NO_DATA_FOUND:
1182 		case SQL_SUCCESS_WITH_INFO:
1183 			odbc_sql_error(result->conn_ptr, result->stmt, "SQLExecute");
1184 			break;
1185 		default:
1186 			odbc_sql_error(result->conn_ptr, result->stmt, "SQLExecute");
1187 			RETVAL_FALSE;
1188 	}
1189 
1190 	if (result->numparams > 0) {
1191 		odbc_release_params(result, params);
1192 	}
1193 
1194 	if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO || rc == SQL_NO_DATA_FOUND) {
1195 		RETVAL_TRUE;
1196 	}
1197 
1198 	if (result->numcols == 0) {
1199 		SQLNumResultCols(result->stmt, &(result->numcols));
1200 
1201 		if (result->numcols > 0) {
1202 			odbc_bindcols(result);
1203 		} else {
1204 			result->values = NULL;
1205 		}
1206 	}
1207 }
1208 /* }}} */
1209 
1210 /* {{{ Get cursor name */
PHP_FUNCTION(odbc_cursor)1211 PHP_FUNCTION(odbc_cursor)
1212 {
1213 	zval *pv_res;
1214 	SQLUSMALLINT max_len;
1215 	SQLSMALLINT len;
1216 	char *cursorname;
1217 	odbc_result *result;
1218 	RETCODE rc;
1219 
1220 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) {
1221 		RETURN_THROWS();
1222 	}
1223 
1224 	result = Z_ODBC_RESULT_P(pv_res);
1225 	CHECK_ODBC_RESULT(result);
1226 
1227 	rc = SQLGetInfo(result->conn_ptr->hdbc,SQL_MAX_CURSOR_NAME_LEN, (void *)&max_len,sizeof(max_len),&len);
1228 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
1229 		RETURN_FALSE;
1230 	}
1231 
1232 	if (max_len > 0) {
1233 		cursorname = emalloc(max_len + 1);
1234 		rc = SQLGetCursorName(result->stmt, (SQLCHAR *) cursorname, (SQLSMALLINT)max_len, &len);
1235 		if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
1236 			char        state[6];     /* Not used */
1237 	 		SQLINTEGER  error;        /* Not used */
1238 			char        errormsg[SQL_MAX_MESSAGE_LENGTH];
1239 			SQLSMALLINT errormsgsize; /* Not used */
1240 
1241 			SQLError( result->conn_ptr->henv, result->conn_ptr->hdbc,
1242 						result->stmt, (SQLCHAR *) state, &error, (SQLCHAR *) errormsg,
1243 						sizeof(errormsg)-1, &errormsgsize);
1244 			if (!strncmp(state,"S1015",5)) {
1245 				snprintf(cursorname, max_len+1, "php_curs_" ZEND_ULONG_FMT, (zend_ulong)result->stmt);
1246 				if (SQLSetCursorName(result->stmt, (SQLCHAR *) cursorname, SQL_NTS) != SQL_SUCCESS) {
1247 					odbc_sql_error(result->conn_ptr, result->stmt, "SQLSetCursorName");
1248 					RETVAL_FALSE;
1249 				} else {
1250 					RETVAL_STRING(cursorname);
1251 				}
1252 			} else {
1253 				php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s", errormsg, state);
1254 				RETVAL_FALSE;
1255 			}
1256 		} else {
1257 			RETVAL_STRING(cursorname);
1258 		}
1259 		efree(cursorname);
1260 	} else {
1261 		RETVAL_FALSE;
1262 	}
1263 }
1264 /* }}} */
1265 
1266 #ifdef HAVE_SQLDATASOURCES
1267 /* {{{ Return information about the currently connected data source */
PHP_FUNCTION(odbc_data_source)1268 PHP_FUNCTION(odbc_data_source)
1269 {
1270 	zval *zv_conn;
1271 	zend_long zv_fetch_type;
1272 	RETCODE rc = 0; /* assume all is good */
1273 	UCHAR server_name[100], desc[200];
1274 	SQLSMALLINT len1=0, len2=0, fetch_type;
1275 
1276 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &zv_conn, odbc_connection_ce, &zv_fetch_type) == FAILURE) {
1277 		RETURN_THROWS();
1278 	}
1279 
1280 	fetch_type = (SQLSMALLINT) zv_fetch_type;
1281 
1282 	if (!(fetch_type == SQL_FETCH_FIRST || fetch_type == SQL_FETCH_NEXT)) {
1283 		zend_argument_value_error(2, "must be either SQL_FETCH_FIRST or SQL_FETCH_NEXT");
1284 		RETURN_THROWS();
1285 	}
1286 
1287 	odbc_connection *conn = Z_ODBC_CONNECTION_P(zv_conn);
1288 	CHECK_ODBC_CONNECTION(conn);
1289 
1290 	/* now we have the "connection" lets call the DataSource object */
1291 	rc = SQLDataSources(conn->henv,
1292 			fetch_type,
1293 			server_name,
1294 			(SQLSMALLINT)sizeof(server_name),
1295 			&len1,
1296 			desc,
1297 			(SQLSMALLINT)sizeof(desc),
1298 			&len2);
1299 
1300 	if (SQL_NO_DATA == rc) {
1301 		/* System has no data sources, no error. Signal it by returning NULL,
1302 			not false. */
1303 		RETURN_NULL();
1304 	} else if (rc != SQL_SUCCESS) {
1305 		/* ummm.... he did it */
1306 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLDataSources");
1307 		RETURN_FALSE;
1308 	}
1309 
1310 	if (len1 == 0 || len2 == 0) {
1311 		/* we have a non-valid entry... so stop the looping */
1312 		RETURN_FALSE;
1313 	}
1314 
1315 	array_init(return_value);
1316 
1317 	add_assoc_string_ex(return_value, "server", sizeof("server")-1, (char *) server_name);
1318 	add_assoc_string_ex(return_value, "description", sizeof("description")-1, (char *) desc);
1319 
1320 }
1321 /* }}} */
1322 #endif /* HAVE_SQLDATASOURCES */
1323 
1324 /* {{{ Prepare and execute an SQL statement */
1325 /* XXX Use flags */
PHP_FUNCTION(odbc_exec)1326 PHP_FUNCTION(odbc_exec)
1327 {
1328 	zval *pv_conn;
1329 	char *query;
1330 	size_t query_len;
1331 	odbc_result *result = NULL;
1332 	RETCODE rc;
1333 #ifdef HAVE_SQL_EXTENDED_FETCH
1334 	SQLUINTEGER      scrollopts;
1335 #endif
1336 
1337 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_conn, odbc_connection_ce, &query, &query_len) == FAILURE) {
1338 		RETURN_THROWS();
1339 	}
1340 
1341 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
1342 	CHECK_ODBC_CONNECTION(conn);
1343 
1344 	object_init_ex(return_value, odbc_result_ce);
1345 	result = Z_ODBC_RESULT_P(return_value);
1346 
1347 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
1348 	if (rc == SQL_INVALID_HANDLE) {
1349 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
1350 		zval_ptr_dtor(return_value);
1351 		RETURN_FALSE;
1352 	}
1353 
1354 	if (rc == SQL_ERROR) {
1355 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
1356 		zval_ptr_dtor(return_value);
1357 		RETURN_FALSE;
1358 	}
1359 
1360 #ifdef HAVE_SQL_EXTENDED_FETCH
1361 	/* Solid doesn't have ExtendedFetch, if DriverManager is used, get Info,
1362 	   whether Driver supports ExtendedFetch */
1363 	rc = SQLGetInfo(conn->hdbc, SQL_FETCH_DIRECTION, (void *) &scrollopts, sizeof(scrollopts), NULL);
1364 	if (rc == SQL_SUCCESS) {
1365 		if ((result->fetch_abs = (scrollopts & SQL_FD_FETCH_ABSOLUTE))) {
1366 			/* Try to set CURSOR_TYPE to dynamic. Driver will replace this with other
1367 			   type if not possible.
1368 			 */
1369 			SQLSetStmtOption(result->stmt, SQL_CURSOR_TYPE, ODBCG(default_cursortype));
1370 		}
1371 	} else {
1372 		result->fetch_abs = 0;
1373 	}
1374 #endif
1375 
1376 	rc = SQLExecDirect(result->stmt, (SQLCHAR *) query, SQL_NTS);
1377 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA_FOUND) {
1378 		/* XXX FIXME we should really check out SQLSTATE with SQLError
1379 		 * in case rc is SQL_SUCCESS_WITH_INFO here.
1380 		 */
1381 		odbc_sql_error(conn, result->stmt, "SQLExecDirect");
1382 		SQLFreeStmt(result->stmt, SQL_DROP);
1383 		zval_ptr_dtor(return_value);
1384 		RETURN_FALSE;
1385 	}
1386 
1387 	SQLNumResultCols(result->stmt, &(result->numcols));
1388 
1389 	/* For insert, update etc. cols == 0 */
1390 	if (result->numcols > 0) {
1391 		odbc_bindcols(result);
1392 	} else {
1393 		result->values = NULL;
1394 	}
1395 	result->conn_ptr = conn;
1396 	result->fetched = 0;
1397 
1398 	odbc_insert_new_result(conn, return_value);
1399 }
1400 /* }}} */
1401 
1402 #ifdef PHP_ODBC_HAVE_FETCH_HASH
1403 #define ODBC_NUM  1
1404 #define ODBC_OBJECT  2
1405 
1406 /* {{{ php_odbc_fetch_hash */
php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS,int result_type)1407 static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type)
1408 {
1409 	int i;
1410 	odbc_result *result;
1411 	RETCODE rc;
1412 	SQLSMALLINT sql_c_type;
1413 	char *buf = NULL;
1414 	zend_long pv_row = 0;
1415 	bool pv_row_is_null = true;
1416 	zval *pv_res, tmp;
1417 #ifdef HAVE_SQL_EXTENDED_FETCH
1418 	SQLULEN crow;
1419 	SQLUSMALLINT RowStatus[1];
1420 #endif
1421 
1422 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!", &pv_res, odbc_result_ce, &pv_row, &pv_row_is_null) == FAILURE) {
1423 		RETURN_THROWS();
1424 	}
1425 
1426 	result = Z_ODBC_RESULT_P(pv_res);
1427 	CHECK_ODBC_RESULT(result);
1428 
1429 	/* TODO deprecate $row argument values less than 1 after PHP 8.4 */
1430 
1431 #ifndef HAVE_SQL_EXTENDED_FETCH
1432 	if (!pv_row_is_null && pv_row > 0) {
1433 		php_error_docref(NULL, E_WARNING, "Extended fetch functionality is not available, argument #3 ($row) is ignored");
1434 	}
1435 #endif
1436 
1437 	if (result->numcols == 0) {
1438 		php_error_docref(NULL, E_WARNING, "No tuples available at this result index");
1439 		RETURN_FALSE;
1440 	}
1441 
1442 #ifdef HAVE_SQL_EXTENDED_FETCH
1443 	if (result->fetch_abs) {
1444 		if (!pv_row_is_null && pv_row > 0) {
1445 			rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,(SQLLEN)pv_row,&crow,RowStatus);
1446 		} else {
1447 			rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus);
1448 		}
1449 	} else
1450 #endif
1451 	rc = SQLFetch(result->stmt);
1452 
1453 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
1454 		RETURN_FALSE;
1455 	}
1456 
1457 	array_init(return_value);
1458 
1459 #ifdef HAVE_SQL_EXTENDED_FETCH
1460 	if (!pv_row_is_null && pv_row > 0 && result->fetch_abs)
1461 		result->fetched = (SQLLEN)pv_row;
1462 	else
1463 #endif
1464 		result->fetched++;
1465 
1466 	for(i = 0; i < result->numcols; i++) {
1467 		sql_c_type = SQL_C_CHAR;
1468 
1469 		switch(result->values[i].coltype) {
1470 			case SQL_BINARY:
1471 			case SQL_VARBINARY:
1472 			case SQL_LONGVARBINARY:
1473 				if (result->binmode <= 0) {
1474 					ZVAL_EMPTY_STRING(&tmp);
1475 					break;
1476 				}
1477 				if (result->binmode == 1) {
1478 					sql_c_type = SQL_C_BINARY;
1479 				}
1480 				ZEND_FALLTHROUGH;
1481 			case SQL_LONGVARCHAR:
1482 #if defined(ODBCVER) && (ODBCVER >= 0x0300)
1483 			case SQL_WLONGVARCHAR:
1484 #endif
1485 				if (IS_SQL_LONG(result->values[i].coltype) && result->longreadlen <= 0) {
1486 					ZVAL_EMPTY_STRING(&tmp);
1487 					break;
1488 				}
1489 				if (buf == NULL) {
1490 					buf = emalloc(result->longreadlen + 1);
1491 				}
1492 
1493 				rc = SQLGetData(result->stmt, (SQLUSMALLINT)(i + 1), sql_c_type, buf, result->longreadlen + 1, &result->values[i].vallen);
1494 
1495 				if (rc == SQL_ERROR) {
1496 					odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData");
1497 					efree(buf);
1498 					RETURN_FALSE;
1499 				}
1500 
1501 				if (rc == SQL_SUCCESS_WITH_INFO) {
1502 					ZVAL_STRINGL(&tmp, buf, result->longreadlen);
1503 				} else if (rc != SQL_SUCCESS) {
1504 					php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (retcode %u)", i + 1, rc);
1505 					ZVAL_FALSE(&tmp);
1506 				} else if (result->values[i].vallen == SQL_NULL_DATA) {
1507 					ZVAL_NULL(&tmp);
1508 					break;
1509 				} else if (result->values[i].vallen == SQL_NO_TOTAL) {
1510 					php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", i + 1);
1511 					ZVAL_FALSE(&tmp);
1512 				} else {
1513 					ZVAL_STRINGL(&tmp, buf, result->values[i].vallen);
1514 				}
1515 				break;
1516 
1517 			default:
1518 				if (result->values[i].vallen == SQL_NULL_DATA) {
1519 					ZVAL_NULL(&tmp);
1520 					break;
1521 				} else if (result->values[i].vallen == SQL_NO_TOTAL) {
1522 					php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", i + 1);
1523 					ZVAL_FALSE(&tmp);
1524 					break;
1525 				}
1526 				ZVAL_STRINGL(&tmp, result->values[i].value, result->values[i].vallen);
1527 				break;
1528 		}
1529 
1530 		if (result_type & ODBC_NUM) {
1531 			zend_hash_index_update(Z_ARRVAL_P(return_value), i, &tmp);
1532 		} else {
1533 			if (!*(result->values[i].name) && Z_TYPE(tmp) == IS_STRING) {
1534 				zend_hash_update(Z_ARRVAL_P(return_value), Z_STR(tmp), &tmp);
1535 			} else {
1536 				zend_hash_str_update(Z_ARRVAL_P(return_value), result->values[i].name, strlen(result->values[i].name), &tmp);
1537 			}
1538 		}
1539 	}
1540 	if (buf) {
1541 		efree(buf);
1542 	}
1543 }
1544 /* }}} */
1545 
1546 
1547 /* {{{ Fetch a result row as an object */
PHP_FUNCTION(odbc_fetch_object)1548 PHP_FUNCTION(odbc_fetch_object)
1549 {
1550 	php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, ODBC_OBJECT);
1551 	if (Z_TYPE_P(return_value) == IS_ARRAY) {
1552 		object_and_properties_init(return_value, ZEND_STANDARD_CLASS_DEF_PTR, Z_ARRVAL_P(return_value));
1553 	}
1554 }
1555 /* }}} */
1556 
1557 /* {{{ Fetch a result row as an associative array */
PHP_FUNCTION(odbc_fetch_array)1558 PHP_FUNCTION(odbc_fetch_array)
1559 {
1560 	php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, ODBC_OBJECT);
1561 }
1562 /* }}} */
1563 #endif
1564 
1565 /* {{{ Fetch one result row into an array */
PHP_FUNCTION(odbc_fetch_into)1566 PHP_FUNCTION(odbc_fetch_into)
1567 {
1568 	int i;
1569 	odbc_result *result;
1570 	RETCODE rc;
1571 	SQLSMALLINT sql_c_type;
1572 	char *buf = NULL;
1573 	zval *pv_res, *pv_res_arr, tmp;
1574 	zend_long pv_row = 0;
1575 	bool pv_row_is_null = true;
1576 #ifdef HAVE_SQL_EXTENDED_FETCH
1577 	SQLULEN crow;
1578 	SQLUSMALLINT RowStatus[1];
1579 #endif /* HAVE_SQL_EXTENDED_FETCH */
1580 
1581 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|l!", &pv_res, odbc_result_ce, &pv_res_arr, &pv_row, &pv_row_is_null) == FAILURE) {
1582 		RETURN_THROWS();
1583 	}
1584 
1585 	result = Z_ODBC_RESULT_P(pv_res);
1586 	CHECK_ODBC_RESULT(result);
1587 
1588 	/* TODO deprecate $row argument values less than 1 after PHP 8.4 */
1589 
1590 #ifndef HAVE_SQL_EXTENDED_FETCH
1591 	if (!pv_row_is_null && pv_row > 0) {
1592 		php_error_docref(NULL, E_WARNING, "Extended fetch functionality is not available, argument #3 ($row) is ignored");
1593 	}
1594 #endif
1595 
1596 	if (result->numcols == 0) {
1597 		php_error_docref(NULL, E_WARNING, "No tuples available at this result index");
1598 		RETURN_FALSE;
1599 	}
1600 
1601 	pv_res_arr = zend_try_array_init(pv_res_arr);
1602 	if (!pv_res_arr) {
1603 		RETURN_THROWS();
1604 	}
1605 
1606 #ifdef HAVE_SQL_EXTENDED_FETCH
1607 	if (result->fetch_abs) {
1608 		if (!pv_row_is_null && pv_row > 0) {
1609 			rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,(SQLLEN)pv_row,&crow,RowStatus);
1610 		} else {
1611 			rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus);
1612 		}
1613 	} else
1614 #endif
1615 		rc = SQLFetch(result->stmt);
1616 
1617 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
1618 		RETURN_FALSE;
1619 	}
1620 
1621 #ifdef HAVE_SQL_EXTENDED_FETCH
1622 	if (!pv_row_is_null && pv_row > 0 && result->fetch_abs)
1623 		result->fetched = (SQLLEN)pv_row;
1624 	else
1625 #endif
1626 		result->fetched++;
1627 
1628 	for(i = 0; i < result->numcols; i++) {
1629 		sql_c_type = SQL_C_CHAR;
1630 
1631 		switch(result->values[i].coltype) {
1632 			case SQL_BINARY:
1633 			case SQL_VARBINARY:
1634 			case SQL_LONGVARBINARY:
1635 				if (result->binmode <= 0) {
1636 					ZVAL_EMPTY_STRING(&tmp);
1637 					break;
1638 				}
1639 				if (result->binmode == 1) sql_c_type = SQL_C_BINARY;
1640 
1641 				/* TODO: Check this is the intended behaviour */
1642 				ZEND_FALLTHROUGH;
1643 			case SQL_LONGVARCHAR:
1644 #if defined(ODBCVER) && (ODBCVER >= 0x0300)
1645 			case SQL_WLONGVARCHAR:
1646 #endif
1647 				if (IS_SQL_LONG(result->values[i].coltype) && result->longreadlen <= 0) {
1648 					ZVAL_EMPTY_STRING(&tmp);
1649 					break;
1650 				}
1651 
1652 				if (buf == NULL) {
1653 					buf = emalloc(result->longreadlen + 1);
1654 				}
1655 				rc = SQLGetData(result->stmt, (SQLUSMALLINT)(i + 1),sql_c_type, buf, result->longreadlen + 1, &result->values[i].vallen);
1656 
1657 				if (rc == SQL_ERROR) {
1658 					odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData");
1659 					efree(buf);
1660 					RETURN_FALSE;
1661 				}
1662 				if (rc == SQL_SUCCESS_WITH_INFO) {
1663 					ZVAL_STRINGL(&tmp, buf, result->longreadlen);
1664 				} else if (rc != SQL_SUCCESS) {
1665 					php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (retcode %u)", i + 1, rc);
1666 					ZVAL_FALSE(&tmp);
1667 				} else if (result->values[i].vallen == SQL_NULL_DATA) {
1668 					ZVAL_NULL(&tmp);
1669 					break;
1670 				} else if (result->values[i].vallen == SQL_NO_TOTAL) {
1671 					php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", i + 1);
1672 					ZVAL_FALSE(&tmp);
1673 				} else {
1674 					ZVAL_STRINGL(&tmp, buf, result->values[i].vallen);
1675 				}
1676 				break;
1677 
1678 			default:
1679 				if (result->values[i].vallen == SQL_NULL_DATA) {
1680 					ZVAL_NULL(&tmp);
1681 					break;
1682 				} else if (result->values[i].vallen == SQL_NO_TOTAL) {
1683 					php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", i + 1);
1684 					ZVAL_FALSE(&tmp);
1685 					break;
1686 				}
1687 				ZVAL_STRINGL(&tmp, result->values[i].value, result->values[i].vallen);
1688 				break;
1689 		}
1690 		zend_hash_index_update(Z_ARRVAL_P(pv_res_arr), i, &tmp);
1691 	}
1692 	if (buf) efree(buf);
1693 	RETURN_LONG(result->numcols);
1694 }
1695 /* }}} */
1696 
1697 /* {{{ Fetch a row */
PHP_FUNCTION(odbc_fetch_row)1698 PHP_FUNCTION(odbc_fetch_row)
1699 {
1700 	odbc_result *result;
1701 	RETCODE rc;
1702 	zval *pv_res;
1703 	zend_long pv_row = 0;
1704 	bool pv_row_is_null = true;
1705 #ifdef HAVE_SQL_EXTENDED_FETCH
1706 	SQLULEN crow;
1707 	SQLUSMALLINT RowStatus[1];
1708 #endif
1709 
1710 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!", &pv_res, odbc_result_ce, &pv_row, &pv_row_is_null) == FAILURE) {
1711 		RETURN_THROWS();
1712 	}
1713 
1714 	result = Z_ODBC_RESULT_P(pv_res);
1715 	CHECK_ODBC_RESULT(result);
1716 
1717 #ifndef HAVE_SQL_EXTENDED_FETCH
1718 	if (!pv_row_is_null) {
1719 		php_error_docref(NULL, E_WARNING, "Extended fetch functionality is not available, argument #3 ($row) is ignored");
1720 	}
1721 #else
1722 	if (!pv_row_is_null && pv_row < 1) {
1723 		php_error_docref(NULL, E_WARNING, "Argument #3 ($row) must be greater than or equal to 1");
1724 		RETURN_FALSE;
1725 	}
1726 #endif
1727 
1728 	if (result->numcols == 0) {
1729 		php_error_docref(NULL, E_WARNING, "No tuples available at this result index");
1730 		RETURN_FALSE;
1731 	}
1732 
1733 #ifdef HAVE_SQL_EXTENDED_FETCH
1734 	if (result->fetch_abs) {
1735 		if (!pv_row_is_null) {
1736 			rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,(SQLLEN)pv_row,&crow,RowStatus);
1737 		} else {
1738 			rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus);
1739 		}
1740 	} else
1741 #endif
1742 		rc = SQLFetch(result->stmt);
1743 
1744 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
1745 		RETURN_FALSE;
1746 	}
1747 
1748 #ifdef HAVE_SQL_EXTENDED_FETCH
1749 	if (!pv_row_is_null) {
1750 		result->fetched = (SQLLEN)pv_row;
1751 	} else
1752 #endif
1753 		result->fetched++;
1754 
1755 	RETURN_TRUE;
1756 }
1757 /* }}} */
1758 
1759 /* {{{ Get result data */
PHP_FUNCTION(odbc_result)1760 PHP_FUNCTION(odbc_result)
1761 {
1762 	char *field;
1763 	zend_string *field_str, *pv_field_str;
1764 	zend_long pv_field_long;
1765 	int field_ind;
1766 	SQLSMALLINT sql_c_type = SQL_C_CHAR;
1767 	odbc_result *result;
1768 	int i = 0;
1769 	RETCODE rc;
1770 	SQLLEN	fieldsize;
1771 	zval *pv_res;
1772 #ifdef HAVE_SQL_EXTENDED_FETCH
1773 	SQLULEN crow;
1774 	SQLUSMALLINT RowStatus[1];
1775 #endif
1776 
1777 	ZEND_PARSE_PARAMETERS_START(2, 2)
1778 		Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce)
1779 		Z_PARAM_STR_OR_LONG(pv_field_str, pv_field_long)
1780 	ZEND_PARSE_PARAMETERS_END();
1781 
1782 	if (pv_field_str) {
1783 		field = ZSTR_VAL(pv_field_str);
1784 		field_ind = -1;
1785 	} else {
1786 		field = NULL;
1787 		field_ind = (int) pv_field_long - 1;
1788 	}
1789 
1790 	result = Z_ODBC_RESULT_P(pv_res);
1791 	CHECK_ODBC_RESULT(result);
1792 
1793 	if (result->numcols == 0) {
1794 		php_error_docref(NULL, E_WARNING, "No tuples available at this result index");
1795 		RETURN_FALSE;
1796 	}
1797 
1798 	/* get field index if the field parameter was a string */
1799 	if (field != NULL) {
1800 		if (result->values == NULL) {
1801 			php_error_docref(NULL, E_WARNING, "Result set contains no data");
1802 			RETURN_FALSE;
1803 		}
1804 
1805 		for(i = 0; i < result->numcols; i++) {
1806 			if (!strcasecmp(result->values[i].name, field)) {
1807 				field_ind = i;
1808 				break;
1809 			}
1810 		}
1811 
1812 		if (field_ind < 0) {
1813 			php_error_docref(NULL, E_WARNING, "Field %s not found", field);
1814 			RETURN_FALSE;
1815 		}
1816 	} else {
1817 		/* check for limits of field_ind if the field parameter was an int */
1818 		if (field_ind >= result->numcols || field_ind < 0) {
1819 			php_error_docref(NULL, E_WARNING, "Field index is larger than the number of fields");
1820 			RETURN_FALSE;
1821 		}
1822 	}
1823 
1824 	if (result->fetched == 0) {
1825 		/* User forgot to call odbc_fetch_row(), or wants to reload the results, do it now */
1826 #ifdef HAVE_SQL_EXTENDED_FETCH
1827 		if (result->fetch_abs)
1828 			rc = SQLExtendedFetch(result->stmt, SQL_FETCH_NEXT, 1, &crow,RowStatus);
1829 		else
1830 #endif
1831 			rc = SQLFetch(result->stmt);
1832 
1833 		if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
1834 			RETURN_FALSE;
1835 		}
1836 
1837 		result->fetched++;
1838 	}
1839 
1840 	switch(result->values[field_ind].coltype) {
1841 		case SQL_BINARY:
1842 		case SQL_VARBINARY:
1843 		case SQL_LONGVARBINARY:
1844 			if (result->binmode <= 1) {
1845 				sql_c_type = SQL_C_BINARY;
1846 			}
1847 			if (result->binmode <= 0) {
1848 				break;
1849 			}
1850 			/* TODO: Check this is the intended behaviour */
1851 			ZEND_FALLTHROUGH;
1852 
1853 		case SQL_LONGVARCHAR:
1854 #if defined(ODBCVER) && (ODBCVER >= 0x0300)
1855 		case SQL_WLONGVARCHAR:
1856 #endif
1857 			if (IS_SQL_LONG(result->values[field_ind].coltype)) {
1858 				if (result->longreadlen <= 0) {
1859 				   break;
1860 				} else {
1861 				   fieldsize = result->longreadlen;
1862 				}
1863 			} else {
1864 			   PHP_ODBC_SQLCOLATTRIBUTE(result->stmt, (SQLUSMALLINT)(field_ind + 1),
1865 					   			(SQLUSMALLINT)((sql_c_type == SQL_C_BINARY) ? SQL_COLUMN_LENGTH :
1866 					   			SQL_COLUMN_DISPLAY_SIZE),
1867 					   			NULL, 0, NULL, &fieldsize);
1868 			}
1869 			/* For char data, the length of the returned string will be longreadlen - 1 */
1870 			fieldsize = (result->longreadlen <= 0) ? 4096 : result->longreadlen;
1871 			field_str = zend_string_alloc(fieldsize, 0);
1872 
1873 		/* SQLGetData will truncate CHAR data to fieldsize - 1 bytes and append \0.
1874 		 * For binary data it is truncated to fieldsize bytes.
1875 		 */
1876 			rc = SQLGetData(result->stmt, (SQLUSMALLINT)(field_ind + 1), sql_c_type,
1877 							ZSTR_VAL(field_str), fieldsize, &result->values[field_ind].vallen);
1878 
1879 			if (rc == SQL_ERROR) {
1880 				odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData");
1881 				zend_string_efree(field_str);
1882 				RETURN_FALSE;
1883 			}
1884 
1885 			if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
1886 				zend_string_efree(field_str);
1887 				php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (retcode %u)", field_ind + 1, rc);
1888 				RETURN_FALSE;
1889 			} else if (result->values[field_ind].vallen == SQL_NULL_DATA) {
1890 				zend_string_efree(field_str);
1891 				RETURN_NULL();
1892 			} else if (result->values[field_ind].vallen == SQL_NO_TOTAL) {
1893 				zend_string_efree(field_str);
1894 				php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", field_ind + 1);
1895 				RETURN_FALSE;
1896 			}
1897 			/* Reduce fieldlen by 1 if we have char data. One day we might
1898 			   have binary strings... */
1899 			if ((result->values[field_ind].coltype == SQL_LONGVARCHAR)
1900 #if defined(ODBCVER) && (ODBCVER >= 0x0300)
1901 			    || (result->values[field_ind].coltype == SQL_WLONGVARCHAR)
1902 #endif
1903 			) {
1904 				fieldsize -= 1;
1905 			}
1906 			/* Don't duplicate result, saves one emalloc.
1907 			   For SQL_SUCCESS, the length is in vallen.
1908 			 */
1909 			if (rc != SQL_SUCCESS_WITH_INFO) {
1910 				field_str = zend_string_truncate(field_str, result->values[field_ind].vallen, 0);
1911 			}
1912 			ZSTR_VAL(field_str)[ZSTR_LEN(field_str)] = '\0';
1913 			RETURN_NEW_STR(field_str);
1914 			break;
1915 
1916 		default:
1917 			if (result->values[field_ind].vallen == SQL_NULL_DATA) {
1918 				RETURN_NULL();
1919 			} else if (result->values[field_ind].vallen == SQL_NO_TOTAL) {
1920 				php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", field_ind + 1);
1921 				RETURN_FALSE;
1922 			} else {
1923 				RETURN_STRINGL(result->values[field_ind].value, result->values[field_ind].vallen);
1924 			}
1925 			break;
1926 	}
1927 
1928 /* If we come here, output unbound LONG and/or BINARY column data to the client */
1929 
1930 	/* We emalloc 1 byte more for SQL_C_CHAR (trailing \0) */
1931 	fieldsize = (sql_c_type == SQL_C_CHAR) ? 4096 : 4095;
1932 	field = emalloc(fieldsize);
1933 
1934 	/* Call SQLGetData() until SQL_SUCCESS is returned */
1935 	while(1) {
1936 		rc = SQLGetData(result->stmt, (SQLUSMALLINT)(field_ind + 1),sql_c_type, field, fieldsize, &result->values[field_ind].vallen);
1937 
1938 		if (rc == SQL_ERROR) {
1939 			odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData");
1940 			efree(field);
1941 			RETURN_FALSE;
1942 		}
1943 
1944 		if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
1945 			php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (retcode %u)", field_ind + 1, rc);
1946 			efree(field);
1947 			RETURN_FALSE;
1948 		}
1949 
1950 		if (result->values[field_ind].vallen == SQL_NULL_DATA) {
1951 			efree(field);
1952 			RETURN_NULL();
1953 		} else if (result->values[field_ind].vallen == SQL_NO_TOTAL) {
1954 			php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", field_ind + 1);
1955 			efree(field);
1956 			RETURN_FALSE;
1957 		}
1958 		/* chop the trailing \0 by outputting only 4095 bytes */
1959 		PHPWRITE(field,(rc == SQL_SUCCESS_WITH_INFO) ? 4095 : result->values[field_ind].vallen);
1960 
1961 		if (rc == SQL_SUCCESS) { /* no more data avail */
1962 			efree(field);
1963 			RETURN_TRUE;
1964 		}
1965 	}
1966 	RETURN_TRUE;
1967 }
1968 /* }}} */
1969 
1970 /* {{{ Print result as HTML table */
PHP_FUNCTION(odbc_result_all)1971 PHP_FUNCTION(odbc_result_all)
1972 {
1973 	char *buf = NULL;
1974 	odbc_result *result;
1975 	RETCODE rc;
1976 	zval *pv_res;
1977 	char *pv_format = NULL;
1978 	size_t i, pv_format_len = 0;
1979 	SQLSMALLINT sql_c_type;
1980 #ifdef HAVE_SQL_EXTENDED_FETCH
1981 	SQLULEN crow;
1982 	SQLUSMALLINT RowStatus[1];
1983 #endif
1984 
1985 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s", &pv_res, odbc_result_ce, &pv_format, &pv_format_len) == FAILURE) {
1986 		RETURN_THROWS();
1987 	}
1988 
1989 	result = Z_ODBC_RESULT_P(pv_res);
1990 	CHECK_ODBC_RESULT(result);
1991 
1992 	if (result->numcols == 0) {
1993 		php_error_docref(NULL, E_WARNING, "No tuples available at this result index");
1994 		RETURN_FALSE;
1995 	}
1996 #ifdef HAVE_SQL_EXTENDED_FETCH
1997 	if (result->fetch_abs)
1998 		rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus);
1999 	else
2000 #endif
2001 		rc = SQLFetch(result->stmt);
2002 
2003 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2004 		php_printf("<h2>No rows found</h2>\n");
2005 		RETURN_LONG(0);
2006 	}
2007 
2008 	/* Start table tag */
2009 	if (ZEND_NUM_ARGS() == 1) {
2010 		php_printf("<table><tr>");
2011 	} else {
2012 		php_printf("<table %s ><tr>", pv_format);
2013 	}
2014 
2015 	for (i = 0; i < result->numcols; i++) {
2016 		php_printf("<th>%s</th>", result->values[i].name);
2017 	}
2018 
2019 	php_printf("</tr>\n");
2020 
2021 	while(rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
2022 		result->fetched++;
2023 		php_printf("<tr>");
2024 		for(i = 0; i < result->numcols; i++) {
2025 			sql_c_type = SQL_C_CHAR;
2026 			switch(result->values[i].coltype) {
2027 				case SQL_BINARY:
2028 				case SQL_VARBINARY:
2029 				case SQL_LONGVARBINARY:
2030 					if (result->binmode <= 0) {
2031 						php_printf("<td>Not printable</td>");
2032 						break;
2033 					}
2034 					if (result->binmode <= 1) sql_c_type = SQL_C_BINARY;
2035 
2036 					/* TODO: Check this is the intended behaviour */
2037 					ZEND_FALLTHROUGH;
2038 				case SQL_LONGVARCHAR:
2039 #if defined(ODBCVER) && (ODBCVER >= 0x0300)
2040 				case SQL_WLONGVARCHAR:
2041 #endif
2042 					if (IS_SQL_LONG(result->values[i].coltype) &&
2043 						result->longreadlen <= 0) {
2044 						php_printf("<td>Not printable</td>");
2045 						break;
2046 					}
2047 
2048 					if (buf == NULL) {
2049 						buf = emalloc(result->longreadlen);
2050 					}
2051 
2052 					rc = SQLGetData(result->stmt, (SQLUSMALLINT)(i + 1),sql_c_type, buf, result->longreadlen, &result->values[i].vallen);
2053 
2054 					php_printf("<td>");
2055 
2056 					if (rc == SQL_ERROR) {
2057 						odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData");
2058 						php_printf("</td></tr></table>");
2059 						efree(buf);
2060 						RETURN_FALSE;
2061 					}
2062 					if (rc == SQL_SUCCESS_WITH_INFO) {
2063 						if (result->values[i].vallen == SQL_NO_TOTAL) {
2064 							php_printf("</td></tr></table>");
2065 							php_error_docref(NULL, E_WARNING, "Cannot get data of column #%zu (driver cannot determine length)", i + 1);
2066 							efree(buf);
2067 							RETURN_FALSE;
2068 						} else {
2069 							PHPWRITE(buf, result->longreadlen);
2070 						}
2071 					} else if (rc != SQL_SUCCESS) {
2072 						php_printf("</td></tr></table>");
2073 						php_error_docref(NULL, E_WARNING, "Cannot get data of column #%zu (retcode %u)", i + 1, rc);
2074 						efree(buf);
2075 						RETURN_FALSE;
2076 					} else if (result->values[i].vallen == SQL_NULL_DATA) {
2077 						php_printf("<td>NULL</td>");
2078 						break;
2079 					} else {
2080 						PHPWRITE(buf, result->values[i].vallen);
2081 					}
2082 					php_printf("</td>");
2083 					break;
2084 				default:
2085 					if (result->values[i].vallen == SQL_NULL_DATA) {
2086 						php_printf("<td>NULL</td>");
2087 					} else if (result->values[i].vallen == SQL_NO_TOTAL) {
2088 						php_error_docref(NULL, E_WARNING, "Cannot get data of column #%zu (driver cannot determine length)", i + 1);
2089 						php_printf("<td>FALSE</td>");
2090 					} else {
2091 						php_printf("<td>%s</td>", result->values[i].value);
2092 					}
2093 					break;
2094 			}
2095 		}
2096 		php_printf("</tr>\n");
2097 
2098 #ifdef HAVE_SQL_EXTENDED_FETCH
2099 		if (result->fetch_abs)
2100 			rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus);
2101 		else
2102 #endif
2103 			rc = SQLFetch(result->stmt);
2104 	}
2105 	php_printf("</table>\n");
2106 	if (buf) efree(buf);
2107 	RETURN_LONG(result->fetched);
2108 }
2109 /* }}} */
2110 
2111 /* {{{ Free resources associated with a result */
PHP_FUNCTION(odbc_free_result)2112 PHP_FUNCTION(odbc_free_result)
2113 {
2114 	zval *pv_res;
2115 	odbc_result *result;
2116 
2117 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) {
2118 		RETURN_THROWS();
2119 	}
2120 
2121 	result = Z_ODBC_RESULT_P(pv_res);
2122 	CHECK_ODBC_RESULT(result);
2123 
2124 	odbc_result_free(result);
2125 
2126 	RETURN_TRUE;
2127 }
2128 /* }}} */
2129 
2130 /* {{{ Connect to a datasource */
PHP_FUNCTION(odbc_connect)2131 PHP_FUNCTION(odbc_connect)
2132 {
2133 	odbc_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2134 }
2135 /* }}} */
2136 
2137 /* {{{ Establish a persistent connection to a datasource */
PHP_FUNCTION(odbc_pconnect)2138 PHP_FUNCTION(odbc_pconnect)
2139 {
2140 	odbc_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2141 }
2142 /* }}} */
2143 
2144 /* {{{ odbc_sqlconnect */
odbc_sqlconnect(zval * zv,char * db,char * uid,char * pwd,int cur_opt,bool persistent,char * hash,int hash_len)2145 bool odbc_sqlconnect(zval *zv, char *db, char *uid, char *pwd, int cur_opt, bool persistent, char *hash, int hash_len)
2146 {
2147 	RETCODE rc;
2148 	SQLRETURN ret;
2149 	odbc_link *link;
2150 
2151 	object_init_ex(zv, odbc_connection_ce);
2152 	link = Z_ODBC_LINK_P(zv);
2153 	link->connection = pecalloc(1, sizeof(odbc_connection), persistent);
2154 	zend_hash_init(&link->connection->results, 0, NULL, ZVAL_PTR_DTOR, true);
2155 	link->persistent = persistent;
2156 	link->hash = zend_string_init(hash, hash_len, persistent);
2157 	if (persistent) {
2158 		GC_MAKE_PERSISTENT_LOCAL(link->hash);
2159 	}
2160 	ret = SQLAllocEnv(&link->connection->henv);
2161 	if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
2162 		odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLAllocEnv");
2163 		return false;
2164 	}
2165 
2166 	ret = SQLAllocConnect(link->connection->henv, &link->connection->hdbc);
2167 	if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
2168 		odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLAllocConnect");
2169 		return false;
2170 	}
2171 
2172 #if defined(HAVE_SOLID) || defined(HAVE_SOLID_30)
2173 	SQLSetConnectOption((link->connection->hdbc, SQL_TRANSLATE_OPTION,
2174 			SQL_SOLID_XLATOPT_NOCNV);
2175 #endif
2176 #ifdef HAVE_OPENLINK
2177 	{
2178 		char dsnbuf[1024];
2179 		short dsnbuflen;
2180 
2181 		rc = SQLDriverConnect(link->connection->hdbc, NULL, db, SQL_NTS,	dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT);
2182 	}
2183 #else
2184 	if (cur_opt != SQL_CUR_DEFAULT) {
2185 		rc = SQLSetConnectOption(link->connection->hdbc, SQL_ODBC_CURSORS, cur_opt);
2186 		if (rc != SQL_SUCCESS) {  /* && rc != SQL_SUCCESS_WITH_INFO ? */
2187 			odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLSetConnectOption");
2188 			return false;
2189 		}
2190 	}
2191 /*  Possible fix for bug #10250
2192  *  Needs testing on UnixODBC < 2.0.5 though. */
2193 #if defined(HAVE_EMPRESS) || defined(HAVE_UNIXODBC) || defined(PHP_WIN32) || defined (HAVE_IODBC)
2194 /* *  Uncomment the line above, and comment line below to fully test
2195  * #ifdef HAVE_EMPRESS */
2196 	{
2197 		int     direct = 0;
2198 		SQLCHAR dsnbuf[1024];
2199 		short   dsnbuflen;
2200 		char    *ldb = 0;
2201 		int		ldb_len = 0;
2202 
2203 		/* a connection string may have = but not ; - i.e. "DSN=PHP" */
2204 		if (strstr((char*)db, "=")) {
2205 			direct = 1;
2206 
2207 			/* This should be identical to the code in the PDO driver and vice versa. */
2208 			size_t db_len = strlen(db);
2209 			char *db_end = db + db_len;
2210 			bool use_uid_arg = uid != NULL && !php_memnistr(db, "uid=", strlen("uid="), db_end);
2211 			bool use_pwd_arg = pwd != NULL && !php_memnistr(db, "pwd=", strlen("pwd="), db_end);
2212 
2213 			/* Force UID and PWD to be set in the DSN */
2214 			if (use_uid_arg || use_pwd_arg) {
2215 				db_end--;
2216 				if ((unsigned char)*(db_end) == ';') {
2217 					*db_end = '\0';
2218 				}
2219 
2220 				char *uid_quoted = NULL, *pwd_quoted = NULL;
2221 				bool should_quote_uid, should_quote_pwd;
2222 				if (use_uid_arg) {
2223 					should_quote_uid = !php_odbc_connstr_is_quoted(uid) && php_odbc_connstr_should_quote(uid);
2224 					if (should_quote_uid) {
2225 						size_t estimated_length = php_odbc_connstr_estimate_quote_length(uid);
2226 						uid_quoted = emalloc(estimated_length);
2227 						php_odbc_connstr_quote(uid_quoted, uid, estimated_length);
2228 					} else {
2229 						uid_quoted = uid;
2230 					}
2231 
2232 					if (!use_pwd_arg) {
2233 						spprintf(&ldb, 0, "%s;UID=%s;", db, uid_quoted);
2234 					}
2235 				}
2236 
2237 				if (use_pwd_arg) {
2238 					should_quote_pwd = !php_odbc_connstr_is_quoted(pwd) && php_odbc_connstr_should_quote(pwd);
2239 					if (should_quote_pwd) {
2240 						size_t estimated_length = php_odbc_connstr_estimate_quote_length(pwd);
2241 						pwd_quoted = emalloc(estimated_length);
2242 						php_odbc_connstr_quote(pwd_quoted, pwd, estimated_length);
2243 					} else {
2244 						pwd_quoted = pwd;
2245 					}
2246 
2247 					if (!use_uid_arg) {
2248 						spprintf(&ldb, 0, "%s;PWD=%s;", db, pwd_quoted);
2249 					}
2250 				}
2251 
2252 				if (use_uid_arg && use_pwd_arg) {
2253 					spprintf(&ldb, 0, "%s;UID=%s;PWD=%s;", db, uid_quoted, pwd_quoted);
2254 				}
2255 
2256 				if (uid_quoted && should_quote_uid) {
2257 					efree(uid_quoted);
2258 				}
2259 				if (pwd_quoted && should_quote_pwd) {
2260 					efree(pwd_quoted);
2261 				}
2262 			} else {
2263 				ldb_len = strlen(db)+1;
2264 				ldb = (char*) emalloc(ldb_len);
2265 				memcpy(ldb, db, ldb_len);
2266 			}
2267 		}
2268 
2269 		if (direct) {
2270 			rc = SQLDriverConnect(link->connection->hdbc, NULL, (SQLCHAR *) ldb, strlen(ldb), dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT);
2271 		} else {
2272 			rc = SQLConnect(link->connection->hdbc, (SQLCHAR *) db, SQL_NTS, (SQLCHAR *) uid, SQL_NTS, (SQLCHAR *) pwd, SQL_NTS);
2273 		}
2274 
2275 		if (ldb) {
2276 			efree(ldb);
2277 		}
2278 	}
2279 #else
2280 	rc = SQLConnect(link->connection->hdbc, (SQLCHAR *) db, SQL_NTS, uid, SQL_NTS, pwd, SQL_NTS);
2281 #endif
2282 #endif
2283 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2284 		odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLConnect");
2285 		return false;
2286 	}
2287 	return true;
2288 }
2289 /* }}} */
2290 
2291 /* {{{ odbc_do_connect */
2292 void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
2293 {
2294 	char *db, *uid=NULL, *pwd=NULL;
2295 	size_t db_len, uid_len, pwd_len;
2296 	zend_long pv_opt = SQL_CUR_DEFAULT;
2297 	odbc_connection *db_conn;
2298 	int cur_opt;
2299 
2300 	ZEND_PARSE_PARAMETERS_START(1, 4)
2301 		Z_PARAM_STRING(db, db_len)
2302 		Z_PARAM_OPTIONAL
2303 		Z_PARAM_STRING_OR_NULL(uid, uid_len)
2304 		Z_PARAM_STRING_OR_NULL(pwd, pwd_len)
2305 		Z_PARAM_LONG(pv_opt)
2306 	ZEND_PARSE_PARAMETERS_END();
2307 
2308 	cur_opt = pv_opt;
2309 
2310 	if (ZEND_NUM_ARGS() > 3) {
2311 		/* Confirm the cur_opt range */
2312 		if (! (cur_opt == SQL_CUR_USE_IF_NEEDED ||
2313 			cur_opt == SQL_CUR_USE_ODBC ||
2314 			cur_opt == SQL_CUR_USE_DRIVER ||
2315 			cur_opt == SQL_CUR_DEFAULT) ) {
2316 			zend_argument_value_error(4, "must be one of SQL_CUR_USE_IF_NEEDED, "
2317 				"SQL_CUR_USE_ODBC, or SQL_CUR_USE_DRIVER");
2318 			RETURN_THROWS();
2319 		}
2320 	}
2321 
2322 	if (!ODBCG(allow_persistent)) {
2323 		persistent = 0;
2324 	}
2325 
2326 	char *hashed_details;
2327 	size_t hashed_details_len = spprintf(&hashed_details, 0, "odbc_%d_%s_%s_%s_%s_%d", persistent, ODBC_TYPE, db, uid, pwd, cur_opt);
2328 
2329 try_and_get_another_connection:
2330 
2331 	if (persistent) {
2332 		zend_resource *le;
2333 
2334 		/* the link is not in the persistent list */
2335 		if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_details_len)) == NULL) {
2336 			if (ODBCG(max_links) != -1 && ODBCG(num_links) >= ODBCG(max_links)) {
2337 				php_error_docref(NULL, E_WARNING, "Too many open links (" ZEND_LONG_FMT ")", ODBCG(num_links));
2338 				efree(hashed_details);
2339 				RETURN_FALSE;
2340 			}
2341 			if (ODBCG(max_persistent) != -1 && ODBCG(num_persistent) >= ODBCG(max_persistent)) {
2342 				php_error_docref(NULL, E_WARNING,"Too many open persistent links (" ZEND_LONG_FMT ")", ODBCG(num_persistent));
2343 				efree(hashed_details);
2344 				RETURN_FALSE;
2345 			}
2346 
2347 			if (!odbc_sqlconnect(return_value, db, uid, pwd, cur_opt, true, hashed_details, hashed_details_len)) {
2348 				efree(hashed_details);
2349 				zval_ptr_dtor(return_value);
2350 				RETURN_FALSE;
2351 			}
2352 
2353 			db_conn = Z_ODBC_CONNECTION_P(return_value);
2354 
2355 			if (zend_register_persistent_resource(hashed_details, hashed_details_len, db_conn, le_pconn) == NULL) {
2356 				efree(hashed_details);
2357 				zval_ptr_dtor(return_value);
2358 				RETURN_FALSE;
2359 			}
2360 
2361 			zend_hash_str_add_new(&ODBCG(connections), hashed_details, hashed_details_len, return_value);
2362 
2363 			ODBCG(num_persistent)++;
2364 			ODBCG(num_links)++;
2365 		} else { /* found connection */
2366 			ZEND_ASSERT(le->type == le_pconn);
2367 
2368 			/*
2369 			 * check to see if the connection is still valid
2370 			 */
2371 			db_conn = (odbc_connection *)le->ptr;
2372 
2373 			/*
2374 			 * check to see if the connection is still in place (lurcher)
2375 			 */
2376 			if(ODBCG(check_persistent)){
2377 				RETCODE ret;
2378 				UCHAR d_name[32];
2379 				SQLSMALLINT len;
2380 				SQLUINTEGER dead = SQL_CD_FALSE;
2381 
2382 				ret = SQLGetConnectAttr(db_conn->hdbc,
2383 					SQL_ATTR_CONNECTION_DEAD,
2384 					&dead, 0, NULL);
2385 				if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) {
2386 					/* Bail early here, since we know it's gone */
2387 					zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len);
2388 					goto try_and_get_another_connection;
2389 				}
2390 				/* If the driver doesn't support it, or returns
2391 				 * false (could be a false positive), fall back
2392 				 * to the old heuristic.
2393 				 */
2394 				ret = SQLGetInfo(db_conn->hdbc,
2395 					SQL_DATA_SOURCE_READ_ONLY,
2396 					d_name, sizeof(d_name), &len);
2397 
2398 				if(ret != SQL_SUCCESS || len == 0) {
2399 					zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len);
2400 					/* Commented out to fix a possible double closure error
2401 					 * when working with persistent connections as submitted by
2402 					 * bug #15758
2403 					 *
2404 					 * safe_odbc_disconnect(db_conn->hdbc);
2405 					 * SQLFreeConnect(db_conn->hdbc);
2406 					 */
2407 					goto try_and_get_another_connection;
2408 				}
2409 			}
2410 
2411 			zval *link_zval;
2412 			if ((link_zval = zend_hash_str_find(&ODBCG(connections), hashed_details, hashed_details_len)) == NULL) {
2413 				object_init_ex(return_value, odbc_connection_ce);
2414 				odbc_link *link = Z_ODBC_LINK_P(return_value);
2415 				link->connection = db_conn;
2416 				link->hash = zend_string_init(hashed_details, hashed_details_len, persistent);
2417 				link->persistent = true;
2418 			} else {
2419 				ZVAL_COPY(return_value, link_zval);
2420 
2421 				ZEND_ASSERT(Z_ODBC_CONNECTION_P(return_value) == db_conn && "Persistent connection has changed");
2422 			}
2423 		}
2424 	} else { /* non-persistent */
2425 		zval *link_zval;
2426 		if ((link_zval = zend_hash_str_find(&ODBCG(connections), hashed_details, hashed_details_len)) == NULL) { /* non-persistent, new */
2427 			if (ODBCG(max_links) != -1 && ODBCG(num_links) >= ODBCG(max_links)) {
2428 				php_error_docref(NULL, E_WARNING, "Too many open connections (" ZEND_LONG_FMT ")", ODBCG(num_links));
2429 				efree(hashed_details);
2430 				RETURN_FALSE;
2431 			}
2432 
2433 			if (!odbc_sqlconnect(return_value, db, uid, pwd, cur_opt, false, hashed_details, hashed_details_len)) {
2434 				efree(hashed_details);
2435 				zval_ptr_dtor(return_value);
2436 				RETURN_FALSE;
2437 			}
2438 			ODBCG(num_links)++;
2439 
2440 			zend_hash_str_add_new(&ODBCG(connections), hashed_details, hashed_details_len, return_value);
2441 		} else { /* non-persistent, pre-existing */
2442 			ZVAL_COPY(return_value, link_zval);
2443 		}
2444 	}
2445 	efree(hashed_details);
2446 }
2447 /* }}} */
2448 
2449 /* {{{ Close an ODBC connection */
2450 PHP_FUNCTION(odbc_close)
2451 {
2452 	zval *pv_conn;
2453 	odbc_link *link;
2454 
2455 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_conn, odbc_connection_ce) == FAILURE) {
2456 		RETURN_THROWS();
2457 	}
2458 
2459 	link = Z_ODBC_LINK_P(pv_conn);
2460 	odbc_connection *connection = Z_ODBC_CONNECTION_P(pv_conn);
2461 	CHECK_ODBC_CONNECTION(connection);
2462 
2463 	odbc_link_free(link);
2464 
2465 	if (link->persistent) {
2466 		zend_hash_apply_with_argument(&EG(persistent_list), _close_pconn_with_res, (void *) connection);
2467 	}
2468 }
2469 /* }}} */
2470 
2471 /* {{{ Get number of rows in a result */
2472 PHP_FUNCTION(odbc_num_rows)
2473 {
2474 	odbc_result *result;
2475 	SQLLEN rows;
2476 	zval *pv_res;
2477 
2478 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) {
2479 		RETURN_THROWS();
2480 	}
2481 
2482 	result = Z_ODBC_RESULT_P(pv_res);
2483 	CHECK_ODBC_RESULT(result);
2484 
2485 	SQLRowCount(result->stmt, &rows);
2486 	RETURN_LONG(rows);
2487 }
2488 /* }}} */
2489 
2490 #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30)
2491 /* {{{ Checks if multiple results are available */
2492 PHP_FUNCTION(odbc_next_result)
2493 {
2494 	odbc_result *result;
2495 	zval *pv_res;
2496 	int rc, i;
2497 
2498 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) {
2499 		RETURN_THROWS();
2500 	}
2501 
2502 	result = Z_ODBC_RESULT_P(pv_res);
2503 	CHECK_ODBC_RESULT(result);
2504 
2505 	if (result->values) {
2506 		for(i = 0; i < result->numcols; i++) {
2507 			if (result->values[i].value) {
2508 				efree(result->values[i].value);
2509 			}
2510 		}
2511 		efree(result->values);
2512 		result->values = NULL;
2513 		result->numcols = 0;
2514 	}
2515 
2516 	result->fetched = 0;
2517 	rc = SQLMoreResults(result->stmt);
2518 	if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) {
2519 		rc = SQLFreeStmt(result->stmt, SQL_UNBIND);
2520 		SQLNumParams(result->stmt, &(result->numparams));
2521 		SQLNumResultCols(result->stmt, &(result->numcols));
2522 
2523 		if (result->numcols > 0) {
2524 			odbc_bindcols(result);
2525 		} else {
2526 			result->values = NULL;
2527 		}
2528 		RETURN_TRUE;
2529 	} else if (rc == SQL_NO_DATA_FOUND) {
2530 		RETURN_FALSE;
2531 	} else {
2532 		odbc_sql_error(result->conn_ptr, result->stmt, "SQLMoreResults");
2533 		RETURN_FALSE;
2534 	}
2535 }
2536 /* }}} */
2537 #endif
2538 
2539 /* {{{ Get number of columns in a result */
2540 PHP_FUNCTION(odbc_num_fields)
2541 {
2542 	odbc_result *result;
2543 	zval *pv_res;
2544 
2545 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) {
2546 		RETURN_THROWS();
2547 	}
2548 
2549 	result = Z_ODBC_RESULT_P(pv_res);
2550 	CHECK_ODBC_RESULT(result);
2551 
2552 	RETURN_LONG(result->numcols);
2553 }
2554 /* }}} */
2555 
2556 /* {{{ Get a column name */
2557 PHP_FUNCTION(odbc_field_name)
2558 {
2559 	odbc_result *result;
2560 	zval *pv_res;
2561 	zend_long pv_num;
2562 
2563 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) {
2564 		RETURN_THROWS();
2565 	}
2566 
2567 	result = Z_ODBC_RESULT_P(pv_res);
2568 	CHECK_ODBC_RESULT(result);
2569 
2570 	if (pv_num < 1) {
2571 		zend_argument_value_error(2, "must be greater than 0");
2572 		RETURN_THROWS();
2573 	}
2574 
2575 	if (result->numcols == 0) {
2576 		php_error_docref(NULL, E_WARNING, "No tuples available at this result index");
2577 		RETURN_FALSE;
2578 	}
2579 
2580 	if (pv_num > result->numcols) {
2581 		php_error_docref(NULL, E_WARNING, "Field index larger than number of fields");
2582 		RETURN_FALSE;
2583 	}
2584 
2585 	RETURN_STRING(result->values[pv_num - 1].name);
2586 }
2587 /* }}} */
2588 
2589 /* {{{ Get the datatype of a column */
2590 PHP_FUNCTION(odbc_field_type)
2591 {
2592 	odbc_result	*result;
2593 	char    	tmp[32];
2594 	SQLSMALLINT	tmplen;
2595 	zval		*pv_res;
2596 	zend_long		pv_num;
2597 
2598 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) {
2599 		RETURN_THROWS();
2600 	}
2601 
2602 	result = Z_ODBC_RESULT_P(pv_res);
2603 	CHECK_ODBC_RESULT(result);
2604 
2605 	if (pv_num < 1) {
2606 		zend_argument_value_error(2, "must be greater than 0");
2607 		RETURN_THROWS();
2608 	}
2609 
2610 	if (result->numcols == 0) {
2611 		php_error_docref(NULL, E_WARNING, "No tuples available at this result index");
2612 		RETURN_FALSE;
2613 	}
2614 
2615 	if (pv_num > result->numcols) {
2616 		php_error_docref(NULL, E_WARNING, "Field index larger than number of fields");
2617 		RETURN_FALSE;
2618 	}
2619 
2620 	PHP_ODBC_SQLCOLATTRIBUTE(result->stmt, (SQLUSMALLINT)pv_num, SQL_COLUMN_TYPE_NAME, tmp, 31, &tmplen, NULL);
2621 	RETURN_STRING(tmp);
2622 }
2623 /* }}} */
2624 
2625 /* {{{ Get the length (precision) of a column */
2626 PHP_FUNCTION(odbc_field_len)
2627 {
2628 	odbc_column_lengths(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2629 }
2630 /* }}} */
2631 
2632 /* {{{ Get the scale of a column */
2633 PHP_FUNCTION(odbc_field_scale)
2634 {
2635 	odbc_column_lengths(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2636 }
2637 /* }}} */
2638 
2639 /* {{{ Return column number */
2640 PHP_FUNCTION(odbc_field_num)
2641 {
2642 	char *fname;
2643 	size_t i, field_ind, fname_len;
2644 	odbc_result *result;
2645 	zval *pv_res;
2646 
2647 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_res, odbc_result_ce, &fname, &fname_len) == FAILURE) {
2648 		RETURN_THROWS();
2649 	}
2650 
2651 	result = Z_ODBC_RESULT_P(pv_res);
2652 	CHECK_ODBC_RESULT(result);
2653 
2654 	if (result->numcols == 0) {
2655 		php_error_docref(NULL, E_WARNING, "No tuples available at this result index");
2656 		RETURN_FALSE;
2657 	}
2658 
2659 	field_ind = -1;
2660 	for(i = 0; i < result->numcols; i++) {
2661 		if (strcasecmp(result->values[i].name, fname) == 0) {
2662 			field_ind = i + 1;
2663 		}
2664 	}
2665 
2666 	if (field_ind == -1) {
2667 		RETURN_FALSE;
2668 	}
2669 	RETURN_LONG(field_ind);
2670 }
2671 /* }}} */
2672 
2673 /* {{{ Toggle autocommit mode or get status */
2674 /* There can be problems with pconnections!*/
2675 PHP_FUNCTION(odbc_autocommit)
2676 {
2677 	RETCODE rc;
2678 	zval *pv_conn;
2679 	bool pv_onoff = 0;
2680 	bool pv_onoff_is_null = true;
2681 
2682 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b!", &pv_conn, odbc_connection_ce, &pv_onoff, &pv_onoff_is_null) == FAILURE) {
2683 		RETURN_THROWS();
2684 	}
2685 
2686 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
2687 	CHECK_ODBC_CONNECTION(conn);
2688 
2689 	if (!pv_onoff_is_null) {
2690 		rc = SQLSetConnectOption(conn->hdbc, SQL_AUTOCOMMIT, pv_onoff ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
2691 		if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2692 			odbc_sql_error(conn, SQL_NULL_HSTMT, "Set autocommit");
2693 			RETURN_FALSE;
2694 		}
2695 		RETURN_TRUE;
2696 	} else {
2697 		SQLINTEGER status;
2698 
2699 		rc = SQLGetConnectOption(conn->hdbc, SQL_AUTOCOMMIT, (PTR)&status);
2700 		if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2701 			odbc_sql_error(conn, SQL_NULL_HSTMT, "Get commit status");
2702 			RETURN_FALSE;
2703 		}
2704 		RETURN_LONG((zend_long)status);
2705 	}
2706 }
2707 /* }}} */
2708 
2709 /* {{{ Commit an ODBC transaction */
2710 PHP_FUNCTION(odbc_commit)
2711 {
2712 	odbc_transact(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2713 }
2714 /* }}} */
2715 
2716 /* {{{ Rollback a transaction */
2717 PHP_FUNCTION(odbc_rollback)
2718 {
2719 	odbc_transact(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2720 }
2721 /* }}} */
2722 
2723 /* {{{ php_odbc_lasterror */
2724 static void php_odbc_lasterror(INTERNAL_FUNCTION_PARAMETERS, int mode)
2725 {
2726 	odbc_connection *conn;
2727 	zval *pv_handle = NULL;
2728 	char *ret;
2729 
2730 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pv_handle, odbc_connection_ce) == FAILURE) {
2731 		RETURN_THROWS();
2732 	}
2733 
2734 	if (pv_handle) {
2735 		conn = Z_ODBC_CONNECTION_P(pv_handle);
2736 		CHECK_ODBC_CONNECTION(conn);
2737 
2738 		if (mode == 0) {
2739 			ret = conn->laststate;
2740 		} else {
2741 			ret = conn->lasterrormsg;
2742 		}
2743 	} else {
2744 		if (mode == 0) {
2745 			ret = ODBCG(laststate);
2746 		} else {
2747 			ret = ODBCG(lasterrormsg);
2748 		}
2749 	}
2750 
2751 	RETURN_STRING(ret);
2752 }
2753 /* }}} */
2754 
2755 /* {{{ Get the last error code */
2756 PHP_FUNCTION(odbc_error)
2757 {
2758 	php_odbc_lasterror(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2759 }
2760 /* }}} */
2761 
2762 /* {{{ Get the last error message */
2763 PHP_FUNCTION(odbc_errormsg)
2764 {
2765 	php_odbc_lasterror(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2766 }
2767 /* }}} */
2768 
2769 /* {{{ Sets connection or statement options */
2770 /* This one has to be used carefully. We can't allow to set connection options for
2771    persistent connections. I think that SetStmtOption is of little use, since most
2772    of those can only be specified before preparing/executing statements.
2773    On the other hand, they can be made connection wide default through SetConnectOption
2774    - but will be overridden by calls to SetStmtOption() in odbc_prepare/odbc_do
2775 */
2776 PHP_FUNCTION(odbc_setoption)
2777 {
2778 	odbc_link *link;
2779 	odbc_result	*result;
2780 	RETCODE rc;
2781 	zval *pv_handle;
2782 	zend_long pv_which, pv_opt, pv_val;
2783 
2784 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "olll", &pv_handle, &pv_which, &pv_opt, &pv_val) == FAILURE) {
2785 		RETURN_THROWS();
2786 	}
2787 
2788 	switch (pv_which) {
2789 		case 1:		/* SQLSetConnectOption */
2790 			if (!instanceof_function(Z_OBJCE_P(pv_handle), odbc_connection_ce)) {
2791 				zend_argument_type_error(1, "must be of type Odbc\\Connection for SQLSetConnectOption()");
2792 				RETURN_THROWS();
2793 			}
2794 			link = Z_ODBC_LINK_P(pv_handle);
2795 			CHECK_ODBC_CONNECTION(link->connection);
2796 
2797 			if (link->persistent) {
2798 				php_error_docref(NULL, E_WARNING, "Unable to set option for persistent connection");
2799 				RETURN_FALSE;
2800 			}
2801 			rc = SQLSetConnectOption(link->connection->hdbc, (unsigned short) pv_opt, pv_val);
2802 			if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2803 				odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SetConnectOption");
2804 				RETURN_FALSE;
2805 			}
2806 			break;
2807 		case 2:		/* SQLSetStmtOption */
2808 			if (!instanceof_function(Z_OBJCE_P(pv_handle), odbc_result_ce)) {
2809 				zend_argument_type_error(1, "must be of type Odbc\\Result for SQLSetStmtOption()");
2810 				RETURN_THROWS();
2811 			}
2812 			result = Z_ODBC_RESULT_P(pv_handle);
2813 			CHECK_ODBC_RESULT(result);
2814 
2815 			rc = SQLSetStmtOption(result->stmt, (unsigned short) pv_opt, pv_val);
2816 
2817 			if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2818 				odbc_sql_error(result->conn_ptr, result->stmt, "SetStmtOption");
2819 				RETURN_FALSE;
2820 			}
2821 			break;
2822 		default:
2823 			zend_argument_value_error(2, "must be 1 for SQLSetConnectOption(), or 2 for SQLSetStmtOption()");
2824 			RETURN_THROWS();
2825 	}
2826 
2827 	RETURN_TRUE;
2828 }
2829 /* }}} */
2830 
2831 /*
2832  * metadata functions
2833  */
2834 
2835 /* {{{ Call the SQLTables function */
2836 PHP_FUNCTION(odbc_tables)
2837 {
2838 	zval *pv_conn;
2839 	odbc_result   *result = NULL;
2840 	char *cat = NULL, *schema = NULL, *table = NULL, *type = NULL;
2841 	size_t cat_len = 0, schema_len = 0, table_len = 0, type_len = 0;
2842 	RETCODE rc;
2843 
2844 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len,
2845 		&table, &table_len, &type, &type_len) == FAILURE) {
2846 		RETURN_THROWS();
2847 	}
2848 
2849 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
2850 	CHECK_ODBC_CONNECTION(conn);
2851 
2852 	object_init_ex(return_value, odbc_result_ce);
2853 	result = Z_ODBC_RESULT_P(return_value);
2854 
2855 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
2856 	if (rc == SQL_INVALID_HANDLE) {
2857 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
2858 		zval_ptr_dtor(return_value);
2859 		RETURN_FALSE;
2860 	}
2861 
2862 	if (rc == SQL_ERROR) {
2863 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
2864 		zval_ptr_dtor(return_value);
2865 		RETURN_FALSE;
2866 	}
2867 
2868 	/* This hack is needed to access table information in Access databases (fmk) */
2869 	if (schema && schema_len == 0 && table && table_len) {
2870 		schema = NULL;
2871 	}
2872 
2873 	rc = SQLTables(result->stmt,
2874 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
2875 			(SQLCHAR *) schema,	SAFE_SQL_NTS(schema),
2876 			(SQLCHAR *) table, SAFE_SQL_NTS(table),
2877 			(SQLCHAR *) type, SAFE_SQL_NTS(type));
2878 
2879 	if (rc == SQL_ERROR) {
2880 		odbc_sql_error(conn, result->stmt, "SQLTables");
2881 		zval_ptr_dtor(return_value);
2882 		RETURN_FALSE;
2883 	}
2884 
2885 	result->numparams = 0;
2886 	SQLNumResultCols(result->stmt, &(result->numcols));
2887 
2888 	if (result->numcols > 0) {
2889 		odbc_bindcols(result);
2890 	} else {
2891 		result->values = NULL;
2892 	}
2893 	result->conn_ptr = conn;
2894 	result->fetched = 0;
2895 
2896 	odbc_insert_new_result(conn, return_value);
2897 }
2898 /* }}} */
2899 
2900 /* {{{ Returns a result identifier that can be used to fetch a list of column names in specified tables */
2901 PHP_FUNCTION(odbc_columns)
2902 {
2903 	zval *pv_conn;
2904 	odbc_result *result = NULL;
2905 	char *cat = NULL, *schema = NULL, *table = NULL, *column = NULL;
2906 	size_t cat_len = 0, schema_len = 0, table_len = 0, column_len = 0;
2907 	RETCODE rc;
2908 
2909 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len,
2910 		&table, &table_len, &column, &column_len) == FAILURE) {
2911 		RETURN_THROWS();
2912 	}
2913 
2914 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
2915 	CHECK_ODBC_CONNECTION(conn);
2916 
2917 	object_init_ex(return_value, odbc_result_ce);
2918 	result = Z_ODBC_RESULT_P(return_value);
2919 
2920 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
2921 	if (rc == SQL_INVALID_HANDLE) {
2922 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
2923 		zval_ptr_dtor(return_value);
2924 		RETURN_FALSE;
2925 	}
2926 
2927 	if (rc == SQL_ERROR) {
2928 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
2929 		zval_ptr_dtor(return_value);
2930 		RETURN_FALSE;
2931 	}
2932 
2933 	/*
2934 	 * Needed to make MS Access happy
2935 	 */
2936 	if (table && table_len && schema && schema_len == 0) {
2937 		schema = NULL;
2938 	}
2939 
2940 	rc = SQLColumns(result->stmt,
2941 			(SQLCHAR *) cat, (SQLSMALLINT) cat_len,
2942 			(SQLCHAR *) schema, (SQLSMALLINT) schema_len,
2943 			(SQLCHAR *) table, (SQLSMALLINT) table_len,
2944 			(SQLCHAR *) column, (SQLSMALLINT) column_len);
2945 
2946 	if (rc == SQL_ERROR) {
2947 		odbc_sql_error(conn, result->stmt, "SQLColumns");
2948 		zval_ptr_dtor(return_value);
2949 		RETURN_FALSE;
2950 	}
2951 
2952 	result->numparams = 0;
2953 	SQLNumResultCols(result->stmt, &(result->numcols));
2954 
2955 	if (result->numcols > 0) {
2956 		odbc_bindcols(result);
2957 	} else {
2958 		result->values = NULL;
2959 	}
2960 	result->conn_ptr = conn;
2961 	result->fetched = 0;
2962 
2963 	odbc_insert_new_result(conn, return_value);
2964 }
2965 /* }}} */
2966 
2967 #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
2968 /* {{{ Returns a result identifier that can be used to fetch a list of columns and associated privileges for the specified table */
2969 PHP_FUNCTION(odbc_columnprivileges)
2970 {
2971 	zval *pv_conn;
2972 	odbc_result *result = NULL;
2973 	char *cat = NULL, *schema, *table, *column;
2974 	size_t cat_len = 0, schema_len, table_len, column_len;
2975 	RETCODE rc;
2976 
2977 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!sss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len,
2978 		&table, &table_len, &column, &column_len) == FAILURE) {
2979 		RETURN_THROWS();
2980 	}
2981 
2982 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
2983 	CHECK_ODBC_CONNECTION(conn);
2984 
2985 	object_init_ex(return_value, odbc_result_ce);
2986 	result = Z_ODBC_RESULT_P(return_value);
2987 
2988 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
2989 	if (rc == SQL_INVALID_HANDLE) {
2990 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
2991 		zval_ptr_dtor(return_value);
2992 		RETURN_FALSE;
2993 	}
2994 
2995 	if (rc == SQL_ERROR) {
2996 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
2997 		zval_ptr_dtor(return_value);
2998 		RETURN_FALSE;
2999 	}
3000 
3001 	rc = SQLColumnPrivileges(result->stmt,
3002 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3003 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3004 			(SQLCHAR *) table, SAFE_SQL_NTS(table),
3005 			(SQLCHAR *) column, SAFE_SQL_NTS(column));
3006 
3007 	if (rc == SQL_ERROR) {
3008 		odbc_sql_error(conn, result->stmt, "SQLColumnPrivileges");
3009 		zval_ptr_dtor(return_value);
3010 		RETURN_FALSE;
3011 	}
3012 
3013 	result->numparams = 0;
3014 	SQLNumResultCols(result->stmt, &(result->numcols));
3015 
3016 	if (result->numcols > 0) {
3017 		odbc_bindcols(result);
3018 	} else {
3019 		result->values = NULL;
3020 	}
3021 	result->conn_ptr = conn;
3022 	result->fetched = 0;
3023 
3024 	odbc_insert_new_result(conn, return_value);
3025 }
3026 /* }}} */
3027 #endif /* HAVE_DBMAKER || HAVE_SOLID*/
3028 
3029 #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
3030 /* {{{ Returns a result identifier to either a list of foreign keys in the specified table or a list of foreign keys in other tables that refer to the primary key in the specified table */
3031 PHP_FUNCTION(odbc_foreignkeys)
3032 {
3033 	zval *pv_conn;
3034 	odbc_result *result = NULL;
3035 	char *pcat = NULL, *pschema, *ptable, *fcat, *fschema, *ftable;
3036 	size_t pcat_len = 0, pschema_len, ptable_len, fcat_len, fschema_len, ftable_len;
3037 	RETCODE rc;
3038 
3039 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!sssss", &pv_conn, odbc_connection_ce, &pcat, &pcat_len, &pschema, &pschema_len,
3040 		&ptable, &ptable_len, &fcat, &fcat_len, &fschema, &fschema_len, &ftable, &ftable_len) == FAILURE) {
3041 		RETURN_THROWS();
3042 	}
3043 
3044 #if defined(HAVE_DBMAKER) || defined(HAVE_IBMDB2)
3045 #define EMPTY_TO_NULL(xstr) \
3046 	if ((int)strlen((xstr)) == 0) (xstr) = NULL
3047 
3048 		EMPTY_TO_NULL(pcat);
3049 		EMPTY_TO_NULL(pschema);
3050 		EMPTY_TO_NULL(ptable);
3051 		EMPTY_TO_NULL(fcat);
3052 		EMPTY_TO_NULL(fschema);
3053 		EMPTY_TO_NULL(ftable);
3054 #endif
3055 
3056 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
3057 	CHECK_ODBC_CONNECTION(conn);
3058 
3059 	object_init_ex(return_value, odbc_result_ce);
3060 	result = Z_ODBC_RESULT_P(return_value);
3061 
3062 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3063 	if (rc == SQL_INVALID_HANDLE) {
3064 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3065 		zval_ptr_dtor(return_value);
3066 		RETURN_FALSE;
3067 	}
3068 
3069 	if (rc == SQL_ERROR) {
3070 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3071 		zval_ptr_dtor(return_value);
3072 		RETURN_FALSE;
3073 	}
3074 
3075 	rc = SQLForeignKeys(result->stmt,
3076 			(SQLCHAR *) pcat, SAFE_SQL_NTS(pcat),
3077 			(SQLCHAR *) pschema, SAFE_SQL_NTS(pschema),
3078 			(SQLCHAR *) ptable, SAFE_SQL_NTS(ptable),
3079 			(SQLCHAR *) fcat, SAFE_SQL_NTS(fcat),
3080 			(SQLCHAR *) fschema, SAFE_SQL_NTS(fschema),
3081 			(SQLCHAR *) ftable, SAFE_SQL_NTS(ftable) );
3082 
3083 	if (rc == SQL_ERROR) {
3084 		odbc_sql_error(conn, result->stmt, "SQLForeignKeys");
3085 		zval_ptr_dtor(return_value);
3086 		RETURN_FALSE;
3087 	}
3088 
3089 	result->numparams = 0;
3090 	SQLNumResultCols(result->stmt, &(result->numcols));
3091 
3092 	if (result->numcols > 0) {
3093 		odbc_bindcols(result);
3094 	} else {
3095 		result->values = NULL;
3096 	}
3097 	result->conn_ptr = conn;
3098 	result->fetched = 0;
3099 
3100 	odbc_insert_new_result(conn, return_value);
3101 }
3102 /* }}} */
3103 #endif /* HAVE_SOLID */
3104 
3105 /* {{{ Returns a result identifier containing information about data types supported by the data source */
3106 PHP_FUNCTION(odbc_gettypeinfo)
3107 {
3108 	zval *pv_conn;
3109 	zend_long pv_data_type = SQL_ALL_TYPES;
3110 	odbc_result *result = NULL;
3111 	RETCODE rc;
3112 	SQLSMALLINT data_type;
3113 
3114 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &pv_conn, odbc_connection_ce, &pv_data_type) == FAILURE) {
3115 		RETURN_THROWS();
3116 	}
3117 
3118 	data_type = (SQLSMALLINT) pv_data_type;
3119 
3120 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
3121 	CHECK_ODBC_CONNECTION(conn);
3122 
3123 	object_init_ex(return_value, odbc_result_ce);
3124 	result = Z_ODBC_RESULT_P(return_value);
3125 
3126 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3127 	if (rc == SQL_INVALID_HANDLE) {
3128 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3129 		zval_ptr_dtor(return_value);
3130 		RETURN_FALSE;
3131 	}
3132 
3133 	if (rc == SQL_ERROR) {
3134 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3135 		zval_ptr_dtor(return_value);
3136 		RETURN_FALSE;
3137 	}
3138 
3139 	rc = SQLGetTypeInfo(result->stmt, data_type );
3140 
3141 	if (rc == SQL_ERROR) {
3142 		odbc_sql_error(conn, result->stmt, "SQLGetTypeInfo");
3143 		zval_ptr_dtor(return_value);
3144 		RETURN_FALSE;
3145 	}
3146 
3147 	result->numparams = 0;
3148 	SQLNumResultCols(result->stmt, &(result->numcols));
3149 
3150 	if (result->numcols > 0) {
3151 		odbc_bindcols(result);
3152 	} else {
3153 		result->values = NULL;
3154 	}
3155 	result->conn_ptr = conn;
3156 	result->fetched = 0;
3157 
3158 	odbc_insert_new_result(conn, return_value);
3159 }
3160 /* }}} */
3161 
3162 /* {{{ Returns a result identifier listing the column names that comprise the primary key for a table */
3163 PHP_FUNCTION(odbc_primarykeys)
3164 {
3165 	zval *pv_conn;
3166 	odbc_result   *result = NULL;
3167 	char *cat = NULL, *schema = NULL, *table = NULL;
3168 	size_t cat_len = 0, schema_len, table_len;
3169 	RETCODE rc;
3170 
3171 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) {
3172 		RETURN_THROWS();
3173 	}
3174 
3175 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
3176 	CHECK_ODBC_CONNECTION(conn);
3177 
3178 	object_init_ex(return_value, odbc_result_ce);
3179 	result = Z_ODBC_RESULT_P(return_value);
3180 
3181 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3182 	if (rc == SQL_INVALID_HANDLE) {
3183 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3184 		zval_ptr_dtor(return_value);
3185 		RETURN_FALSE;
3186 	}
3187 
3188 	if (rc == SQL_ERROR) {
3189 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3190 		zval_ptr_dtor(return_value);
3191 		RETURN_FALSE;
3192 	}
3193 
3194 	rc = SQLPrimaryKeys(result->stmt,
3195 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3196 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3197 			(SQLCHAR *) table, SAFE_SQL_NTS(table) );
3198 
3199 	if (rc == SQL_ERROR) {
3200 		odbc_sql_error(conn, result->stmt, "SQLPrimaryKeys");
3201 		zval_ptr_dtor(return_value);
3202 		RETURN_FALSE;
3203 	}
3204 
3205 	result->numparams = 0;
3206 	SQLNumResultCols(result->stmt, &(result->numcols));
3207 
3208 	if (result->numcols > 0) {
3209 		odbc_bindcols(result);
3210 	} else {
3211 		result->values = NULL;
3212 	}
3213 	result->conn_ptr = conn;
3214 	result->fetched = 0;
3215 
3216 	odbc_insert_new_result(conn, return_value);
3217 }
3218 /* }}} */
3219 
3220 #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
3221 /* {{{ Returns a result identifier containing the list of input and output parameters, as well as the columns that make up the result set for the specified procedures */
3222 PHP_FUNCTION(odbc_procedurecolumns)
3223 {
3224 	zval *pv_conn;
3225 	odbc_result *result = NULL;
3226 	char *cat = NULL, *schema = NULL, *proc = NULL, *col = NULL;
3227 	size_t cat_len = 0, schema_len = 0, proc_len = 0, col_len = 0;
3228 	RETCODE rc;
3229 
3230 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len,
3231 		&proc, &proc_len, &col, &col_len) == FAILURE) {
3232 		RETURN_THROWS();
3233 	}
3234 
3235 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
3236 	CHECK_ODBC_CONNECTION(conn);
3237 
3238 	object_init_ex(return_value, odbc_result_ce);
3239 	result = Z_ODBC_RESULT_P(return_value);
3240 
3241 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3242 	if (rc == SQL_INVALID_HANDLE) {
3243 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3244 		zval_ptr_dtor(return_value);
3245 		RETURN_FALSE;
3246 	}
3247 
3248 	if (rc == SQL_ERROR) {
3249 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3250 		zval_ptr_dtor(return_value);
3251 		RETURN_FALSE;
3252 	}
3253 
3254 	rc = SQLProcedureColumns(result->stmt,
3255 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3256 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3257 			(SQLCHAR *) proc, SAFE_SQL_NTS(proc),
3258 			(SQLCHAR *) col, SAFE_SQL_NTS(col) );
3259 
3260 	if (rc == SQL_ERROR) {
3261 		odbc_sql_error(conn, result->stmt, "SQLProcedureColumns");
3262 		zval_ptr_dtor(return_value);
3263 		RETURN_FALSE;
3264 	}
3265 
3266 	result->numparams = 0;
3267 	SQLNumResultCols(result->stmt, &(result->numcols));
3268 
3269 	if (result->numcols > 0) {
3270 		odbc_bindcols(result);
3271 	} else {
3272 		result->values = NULL;
3273 	}
3274 	result->conn_ptr = conn;
3275 	result->fetched = 0;
3276 
3277 	odbc_insert_new_result(conn, return_value);
3278 }
3279 /* }}} */
3280 #endif /* HAVE_SOLID */
3281 
3282 #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
3283 /* {{{ Returns a result identifier containing the list of procedure names in a datasource */
3284 PHP_FUNCTION(odbc_procedures)
3285 {
3286 	zval *pv_conn;
3287 	odbc_result   *result = NULL;
3288 	char *cat = NULL, *schema = NULL, *proc = NULL;
3289 	size_t cat_len = 0, schema_len = 0, proc_len = 0;
3290 	RETCODE rc;
3291 
3292 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &proc, &proc_len) == FAILURE) {
3293 		RETURN_THROWS();
3294 	}
3295 
3296 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
3297 	CHECK_ODBC_CONNECTION(conn);
3298 
3299 	object_init_ex(return_value, odbc_result_ce);
3300 	result = Z_ODBC_RESULT_P(return_value);
3301 
3302 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3303 	if (rc == SQL_INVALID_HANDLE) {
3304 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3305 		zval_ptr_dtor(return_value);
3306 		RETURN_FALSE;
3307 	}
3308 
3309 	if (rc == SQL_ERROR) {
3310 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3311 		zval_ptr_dtor(return_value);
3312 		RETURN_FALSE;
3313 	}
3314 
3315 	rc = SQLProcedures(result->stmt,
3316 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3317 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3318 			(SQLCHAR *) proc, SAFE_SQL_NTS(proc) );
3319 
3320 	if (rc == SQL_ERROR) {
3321 		odbc_sql_error(conn, result->stmt, "SQLProcedures");
3322 		zval_ptr_dtor(return_value);
3323 		RETURN_FALSE;
3324 	}
3325 
3326 	result->numparams = 0;
3327 	SQLNumResultCols(result->stmt, &(result->numcols));
3328 
3329 	if (result->numcols > 0) {
3330 		odbc_bindcols(result);
3331 	} else {
3332 		result->values = NULL;
3333 	}
3334 	result->conn_ptr = conn;
3335 	result->fetched = 0;
3336 
3337 	odbc_insert_new_result(conn, return_value);
3338 }
3339 /* }}} */
3340 #endif /* HAVE_SOLID */
3341 
3342 /* {{{ Returns a result identifier containing either the optimal set of columns that uniquely identifies a row in the table or columns that are automatically updated when any value in the row is updated by a transaction */
3343 PHP_FUNCTION(odbc_specialcolumns)
3344 {
3345 	zval *pv_conn;
3346 	zend_long vtype, vscope, vnullable;
3347 	odbc_result *result = NULL;
3348 	char *cat = NULL, *schema = NULL, *name = NULL;
3349 	size_t cat_len = 0, schema_len, name_len;
3350 	SQLUSMALLINT type, scope, nullable;
3351 	RETCODE rc;
3352 
3353 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ols!ssll", &pv_conn, odbc_connection_ce, &vtype, &cat, &cat_len, &schema, &schema_len,
3354 		&name, &name_len, &vscope, &vnullable) == FAILURE) {
3355 		RETURN_THROWS();
3356 	}
3357 
3358 	type = (SQLUSMALLINT) vtype;
3359 	scope = (SQLUSMALLINT) vscope;
3360 	nullable = (SQLUSMALLINT) vnullable;
3361 
3362 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
3363 	CHECK_ODBC_CONNECTION(conn);
3364 
3365 	object_init_ex(return_value, odbc_result_ce);
3366 	result = Z_ODBC_RESULT_P(return_value);
3367 
3368 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3369 	if (rc == SQL_INVALID_HANDLE) {
3370 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3371 		zval_ptr_dtor(return_value);
3372 		RETURN_FALSE;
3373 	}
3374 
3375 	if (rc == SQL_ERROR) {
3376 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3377 		zval_ptr_dtor(return_value);
3378 		RETURN_FALSE;
3379 	}
3380 
3381 	rc = SQLSpecialColumns(result->stmt, type,
3382 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3383 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3384 			(SQLCHAR *) name, SAFE_SQL_NTS(name),
3385 			scope,
3386 			nullable);
3387 
3388 	if (rc == SQL_ERROR) {
3389 		odbc_sql_error(conn, result->stmt, "SQLSpecialColumns");
3390 		zval_ptr_dtor(return_value);
3391 		RETURN_FALSE;
3392 	}
3393 
3394 	result->numparams = 0;
3395 	SQLNumResultCols(result->stmt, &(result->numcols));
3396 
3397 	if (result->numcols > 0) {
3398 		odbc_bindcols(result);
3399 	} else {
3400 		result->values = NULL;
3401 	}
3402 	result->conn_ptr = conn;
3403 	result->fetched = 0;
3404 
3405 	odbc_insert_new_result(conn, return_value);
3406 }
3407 /* }}} */
3408 
3409 /* {{{ Returns a result identifier that contains statistics about a single table and the indexes associated with the table */
3410 PHP_FUNCTION(odbc_statistics)
3411 {
3412 	zval *pv_conn;
3413 	zend_long vunique, vreserved;
3414 	odbc_result *result = NULL;
3415 	char *cat = NULL, *schema, *name;
3416 	size_t cat_len = 0, schema_len, name_len;
3417 	SQLUSMALLINT unique, reserved;
3418 	RETCODE rc;
3419 
3420 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ssll", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len,
3421 		&name, &name_len, &vunique, &vreserved) == FAILURE) {
3422 		RETURN_THROWS();
3423 	}
3424 
3425 	unique = (SQLUSMALLINT) vunique;
3426 	reserved = (SQLUSMALLINT) vreserved;
3427 
3428 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
3429 	CHECK_ODBC_CONNECTION(conn);
3430 
3431 	object_init_ex(return_value, odbc_result_ce);
3432 	result = Z_ODBC_RESULT_P(return_value);
3433 
3434 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3435 	if (rc == SQL_INVALID_HANDLE) {
3436 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3437 		zval_ptr_dtor(return_value);
3438 		RETURN_FALSE;
3439 	}
3440 
3441 	if (rc == SQL_ERROR) {
3442 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3443 		zval_ptr_dtor(return_value);
3444 		RETURN_FALSE;
3445 	}
3446 
3447 	rc = SQLStatistics(result->stmt,
3448 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3449 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3450 			(SQLCHAR *) name, SAFE_SQL_NTS(name),
3451 			unique,
3452 			reserved);
3453 
3454 	if (rc == SQL_ERROR) {
3455 		odbc_sql_error(conn, result->stmt, "SQLStatistics");
3456 		zval_ptr_dtor(return_value);
3457 		RETURN_FALSE;
3458 	}
3459 
3460 	result->numparams = 0;
3461 	SQLNumResultCols(result->stmt, &(result->numcols));
3462 
3463 	if (result->numcols > 0) {
3464 		odbc_bindcols(result);
3465 	} else {
3466 		result->values = NULL;
3467 	}
3468 	result->conn_ptr = conn;
3469 	result->fetched = 0;
3470 
3471 	odbc_insert_new_result(conn, return_value);
3472 }
3473 /* }}} */
3474 
3475 #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
3476 /* {{{ Returns a result identifier containing a list of tables and the privileges associated with each table */
3477 PHP_FUNCTION(odbc_tableprivileges)
3478 {
3479 	zval *pv_conn;
3480 	odbc_result   *result = NULL;
3481 	char *cat = NULL, *schema = NULL, *table = NULL;
3482 	size_t cat_len = 0, schema_len, table_len;
3483 	RETCODE rc;
3484 
3485 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) {
3486 		RETURN_THROWS();
3487 	}
3488 
3489 	odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn);
3490 	CHECK_ODBC_CONNECTION(conn);
3491 
3492 	object_init_ex(return_value, odbc_result_ce);
3493 	result = Z_ODBC_RESULT_P(return_value);
3494 
3495 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3496 	if (rc == SQL_INVALID_HANDLE) {
3497 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3498 		zval_ptr_dtor(return_value);
3499 		RETURN_FALSE;
3500 	}
3501 
3502 	if (rc == SQL_ERROR) {
3503 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3504 		zval_ptr_dtor(return_value);
3505 		RETURN_FALSE;
3506 	}
3507 
3508 	rc = SQLTablePrivileges(result->stmt,
3509 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3510 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3511 			(SQLCHAR *) table, SAFE_SQL_NTS(table));
3512 
3513 	if (rc == SQL_ERROR) {
3514 		odbc_sql_error(conn, result->stmt, "SQLTablePrivileges");
3515 		zval_ptr_dtor(return_value);
3516 		RETURN_FALSE;
3517 	}
3518 
3519 	result->numparams = 0;
3520 	SQLNumResultCols(result->stmt, &(result->numcols));
3521 
3522 	if (result->numcols > 0) {
3523 		odbc_bindcols(result);
3524 	} else {
3525 		result->values = NULL;
3526 	}
3527 	result->conn_ptr = conn;
3528 	result->fetched = 0;
3529 
3530 	odbc_insert_new_result(conn, return_value);
3531 }
3532 /* }}} */
3533 #endif /* HAVE_DBMAKER */
3534 
3535 #endif /* HAVE_UODBC */
3536