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