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