xref: /PHP-8.2/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_header(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 (%ld)", 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 (%ld)", 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 (%ld)",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 
2566 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|b", &pv_conn, &pv_onoff) == FAILURE) {
2567 		RETURN_THROWS();
2568 	}
2569 
2570 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
2571 		RETURN_THROWS();
2572 	}
2573 
2574 	if (ZEND_NUM_ARGS() > 1) {
2575 		rc = SQLSetConnectOption(conn->hdbc, SQL_AUTOCOMMIT, pv_onoff ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
2576 		if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2577 			odbc_sql_error(conn, SQL_NULL_HSTMT, "Set autocommit");
2578 			RETURN_FALSE;
2579 		}
2580 		RETVAL_TRUE;
2581 	} else {
2582 		SQLINTEGER status;
2583 
2584 		rc = SQLGetConnectOption(conn->hdbc, SQL_AUTOCOMMIT, (PTR)&status);
2585 		if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2586 			odbc_sql_error(conn, SQL_NULL_HSTMT, "Get commit status");
2587 			RETURN_FALSE;
2588 		}
2589 		RETVAL_LONG((zend_long)status);
2590 	}
2591 }
2592 /* }}} */
2593 
2594 /* {{{ Commit an ODBC transaction */
PHP_FUNCTION(odbc_commit)2595 PHP_FUNCTION(odbc_commit)
2596 {
2597 	odbc_transact(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2598 }
2599 /* }}} */
2600 
2601 /* {{{ Rollback a transaction */
PHP_FUNCTION(odbc_rollback)2602 PHP_FUNCTION(odbc_rollback)
2603 {
2604 	odbc_transact(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2605 }
2606 /* }}} */
2607 
2608 /* {{{ php_odbc_lasterror */
php_odbc_lasterror(INTERNAL_FUNCTION_PARAMETERS,int mode)2609 static void php_odbc_lasterror(INTERNAL_FUNCTION_PARAMETERS, int mode)
2610 {
2611 	odbc_connection *conn;
2612 	zval *pv_handle = NULL;
2613 	char *ret;
2614 
2615 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|r!", &pv_handle) == FAILURE) {
2616 		RETURN_THROWS();
2617 	}
2618 
2619 	if (pv_handle) {
2620 		if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_handle), "ODBC-Link", le_conn, le_pconn))) {
2621 			RETURN_THROWS();
2622 		}
2623 		if (mode == 0) {
2624 			ret = conn->laststate;
2625 		} else {
2626 			ret = conn->lasterrormsg;
2627 		}
2628 	} else {
2629 		if (mode == 0) {
2630 			ret = ODBCG(laststate);
2631 		} else {
2632 			ret = ODBCG(lasterrormsg);
2633 		}
2634 	}
2635 
2636 	RETURN_STRING(ret);
2637 }
2638 /* }}} */
2639 
2640 /* {{{ Get the last error code */
PHP_FUNCTION(odbc_error)2641 PHP_FUNCTION(odbc_error)
2642 {
2643 	php_odbc_lasterror(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2644 }
2645 /* }}} */
2646 
2647 /* {{{ Get the last error message */
PHP_FUNCTION(odbc_errormsg)2648 PHP_FUNCTION(odbc_errormsg)
2649 {
2650 	php_odbc_lasterror(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2651 }
2652 /* }}} */
2653 
2654 /* {{{ Sets connection or statement options */
2655 /* This one has to be used carefully. We can't allow to set connection options for
2656    persistent connections. I think that SetStmtOption is of little use, since most
2657    of those can only be specified before preparing/executing statements.
2658    On the other hand, they can be made connection wide default through SetConnectOption
2659    - but will be overridden by calls to SetStmtOption() in odbc_prepare/odbc_do
2660 */
PHP_FUNCTION(odbc_setoption)2661 PHP_FUNCTION(odbc_setoption)
2662 {
2663 	odbc_connection *conn;
2664 	odbc_result	*result;
2665 	RETCODE rc;
2666 	zval *pv_handle;
2667 	zend_long pv_which, pv_opt, pv_val;
2668 
2669 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll", &pv_handle, &pv_which, &pv_opt, &pv_val) == FAILURE) {
2670 		RETURN_THROWS();
2671 	}
2672 
2673 	switch (pv_which) {
2674 		case 1:		/* SQLSetConnectOption */
2675 			if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_handle), "ODBC-Link", le_conn, le_pconn))) {
2676 				RETURN_THROWS();
2677 			}
2678 
2679 			if (conn->persistent) {
2680 				php_error_docref(NULL, E_WARNING, "Unable to set option for persistent connection");
2681 				RETURN_FALSE;
2682 			}
2683 			rc = SQLSetConnectOption(conn->hdbc, (unsigned short) pv_opt, pv_val);
2684 			if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2685 				odbc_sql_error(conn, SQL_NULL_HSTMT, "SetConnectOption");
2686 				RETURN_FALSE;
2687 			}
2688 			break;
2689 		case 2:		/* SQLSetStmtOption */
2690 			if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_handle), "ODBC result", le_result)) == NULL) {
2691 				RETURN_THROWS();
2692 			}
2693 
2694 			rc = SQLSetStmtOption(result->stmt, (unsigned short) pv_opt, pv_val);
2695 
2696 			if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2697 				odbc_sql_error(result->conn_ptr, result->stmt, "SetStmtOption");
2698 				RETURN_FALSE;
2699 			}
2700 			break;
2701 		default:
2702 			zend_argument_value_error(2, "must be 1 for SQLSetConnectOption(), or 2 for SQLSetStmtOption()");
2703 			RETURN_THROWS();
2704 	}
2705 
2706 	RETURN_TRUE;
2707 }
2708 /* }}} */
2709 
2710 /*
2711  * metadata functions
2712  */
2713 
2714 /* {{{ Call the SQLTables function */
PHP_FUNCTION(odbc_tables)2715 PHP_FUNCTION(odbc_tables)
2716 {
2717 	zval *pv_conn;
2718 	odbc_result   *result = NULL;
2719 	odbc_connection *conn;
2720 	char *cat = NULL, *schema = NULL, *table = NULL, *type = NULL;
2721 	size_t cat_len = 0, schema_len = 0, table_len = 0, type_len = 0;
2722 	RETCODE rc;
2723 
2724 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len,
2725 		&table, &table_len, &type, &type_len) == FAILURE) {
2726 		RETURN_THROWS();
2727 	}
2728 
2729 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
2730 		RETURN_THROWS();
2731 	}
2732 
2733 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
2734 
2735 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
2736 	if (rc == SQL_INVALID_HANDLE) {
2737 		efree(result);
2738 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
2739 		RETURN_FALSE;
2740 	}
2741 
2742 	if (rc == SQL_ERROR) {
2743 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
2744 		efree(result);
2745 		RETURN_FALSE;
2746 	}
2747 
2748 	/* This hack is needed to access table information in Access databases (fmk) */
2749 	if (schema && schema_len == 0 && table && table_len) {
2750 		schema = NULL;
2751 	}
2752 
2753 	rc = SQLTables(result->stmt,
2754 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
2755 			(SQLCHAR *) schema,	SAFE_SQL_NTS(schema),
2756 			(SQLCHAR *) table, SAFE_SQL_NTS(table),
2757 			(SQLCHAR *) type, SAFE_SQL_NTS(type));
2758 
2759 	if (rc == SQL_ERROR) {
2760 		odbc_sql_error(conn, result->stmt, "SQLTables");
2761 		efree(result);
2762 		RETURN_FALSE;
2763 	}
2764 
2765 	result->numparams = 0;
2766 	SQLNumResultCols(result->stmt, &(result->numcols));
2767 
2768 	if (result->numcols > 0) {
2769 		if (!odbc_bindcols(result)) {
2770 			efree(result);
2771 			RETURN_FALSE;
2772 		}
2773 	} else {
2774 		result->values = NULL;
2775 	}
2776 	result->conn_ptr = conn;
2777 	result->fetched = 0;
2778 	RETURN_RES(zend_register_resource(result, le_result));
2779 }
2780 /* }}} */
2781 
2782 /* {{{ Returns a result identifier that can be used to fetch a list of column names in specified tables */
PHP_FUNCTION(odbc_columns)2783 PHP_FUNCTION(odbc_columns)
2784 {
2785 	zval *pv_conn;
2786 	odbc_result *result = NULL;
2787 	odbc_connection *conn;
2788 	char *cat = NULL, *schema = NULL, *table = NULL, *column = NULL;
2789 	size_t cat_len = 0, schema_len = 0, table_len = 0, column_len = 0;
2790 	RETCODE rc;
2791 
2792 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len,
2793 		&table, &table_len, &column, &column_len) == FAILURE) {
2794 		RETURN_THROWS();
2795 	}
2796 
2797 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
2798 		RETURN_THROWS();
2799 	}
2800 
2801 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
2802 
2803 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
2804 	if (rc == SQL_INVALID_HANDLE) {
2805 		efree(result);
2806 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
2807 		RETURN_FALSE;
2808 	}
2809 
2810 	if (rc == SQL_ERROR) {
2811 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
2812 		efree(result);
2813 		RETURN_FALSE;
2814 	}
2815 
2816 	/*
2817 	 * Needed to make MS Access happy
2818 	 */
2819 	if (table && table_len && schema && schema_len == 0) {
2820 		schema = NULL;
2821 	}
2822 
2823 	rc = SQLColumns(result->stmt,
2824 			(SQLCHAR *) cat, (SQLSMALLINT) cat_len,
2825 			(SQLCHAR *) schema, (SQLSMALLINT) schema_len,
2826 			(SQLCHAR *) table, (SQLSMALLINT) table_len,
2827 			(SQLCHAR *) column, (SQLSMALLINT) column_len);
2828 
2829 	if (rc == SQL_ERROR) {
2830 		odbc_sql_error(conn, result->stmt, "SQLColumns");
2831 		efree(result);
2832 		RETURN_FALSE;
2833 	}
2834 
2835 	result->numparams = 0;
2836 	SQLNumResultCols(result->stmt, &(result->numcols));
2837 
2838 	if (result->numcols > 0) {
2839 		if (!odbc_bindcols(result)) {
2840 			efree(result);
2841 			RETURN_FALSE;
2842 		}
2843 	} else {
2844 		result->values = NULL;
2845 	}
2846 	result->conn_ptr = conn;
2847 	result->fetched = 0;
2848 	RETURN_RES(zend_register_resource(result, le_result));
2849 }
2850 /* }}} */
2851 
2852 #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
2853 /* {{{ 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)2854 PHP_FUNCTION(odbc_columnprivileges)
2855 {
2856 	zval *pv_conn;
2857 	odbc_result *result = NULL;
2858 	odbc_connection *conn;
2859 	char *cat = NULL, *schema, *table, *column;
2860 	size_t cat_len = 0, schema_len, table_len, column_len;
2861 	RETCODE rc;
2862 
2863 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!sss", &pv_conn, &cat, &cat_len, &schema, &schema_len,
2864 		&table, &table_len, &column, &column_len) == FAILURE) {
2865 		RETURN_THROWS();
2866 	}
2867 
2868 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
2869 		RETURN_THROWS();
2870 	}
2871 
2872 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
2873 
2874 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
2875 	if (rc == SQL_INVALID_HANDLE) {
2876 		efree(result);
2877 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
2878 		RETURN_FALSE;
2879 	}
2880 
2881 	if (rc == SQL_ERROR) {
2882 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
2883 		efree(result);
2884 		RETURN_FALSE;
2885 	}
2886 
2887 	rc = SQLColumnPrivileges(result->stmt,
2888 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
2889 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
2890 			(SQLCHAR *) table, SAFE_SQL_NTS(table),
2891 			(SQLCHAR *) column, SAFE_SQL_NTS(column));
2892 
2893 	if (rc == SQL_ERROR) {
2894 		odbc_sql_error(conn, result->stmt, "SQLColumnPrivileges");
2895 		efree(result);
2896 		RETURN_FALSE;
2897 	}
2898 
2899 	result->numparams = 0;
2900 	SQLNumResultCols(result->stmt, &(result->numcols));
2901 
2902 	if (result->numcols > 0) {
2903 		if (!odbc_bindcols(result)) {
2904 			efree(result);
2905 			RETURN_FALSE;
2906 		}
2907 	} else {
2908 		result->values = NULL;
2909 	}
2910 	result->conn_ptr = conn;
2911 	result->fetched = 0;
2912 	RETURN_RES(zend_register_resource(result, le_result));
2913 }
2914 /* }}} */
2915 #endif /* HAVE_DBMAKER || HAVE_SOLID*/
2916 
2917 #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
2918 /* {{{ 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)2919 PHP_FUNCTION(odbc_foreignkeys)
2920 {
2921 	zval *pv_conn;
2922 	odbc_result *result = NULL;
2923 	odbc_connection *conn;
2924 	char *pcat = NULL, *pschema, *ptable, *fcat, *fschema, *ftable;
2925 	size_t pcat_len = 0, pschema_len, ptable_len, fcat_len, fschema_len, ftable_len;
2926 	RETCODE rc;
2927 
2928 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!sssss", &pv_conn, &pcat, &pcat_len, &pschema, &pschema_len,
2929 		&ptable, &ptable_len, &fcat, &fcat_len, &fschema, &fschema_len, &ftable, &ftable_len) == FAILURE) {
2930 		RETURN_THROWS();
2931 	}
2932 
2933 #if defined(HAVE_DBMAKER) || defined(HAVE_IBMDB2)
2934 #define EMPTY_TO_NULL(xstr) \
2935 	if ((int)strlen((xstr)) == 0) (xstr) = NULL
2936 
2937 		EMPTY_TO_NULL(pcat);
2938 		EMPTY_TO_NULL(pschema);
2939 		EMPTY_TO_NULL(ptable);
2940 		EMPTY_TO_NULL(fcat);
2941 		EMPTY_TO_NULL(fschema);
2942 		EMPTY_TO_NULL(ftable);
2943 #endif
2944 
2945 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
2946 		RETURN_THROWS();
2947 	}
2948 
2949 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
2950 
2951 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
2952 	if (rc == SQL_INVALID_HANDLE) {
2953 		efree(result);
2954 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
2955 		RETURN_FALSE;
2956 	}
2957 
2958 	if (rc == SQL_ERROR) {
2959 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
2960 		efree(result);
2961 		RETURN_FALSE;
2962 	}
2963 
2964 	rc = SQLForeignKeys(result->stmt,
2965 			(SQLCHAR *) pcat, SAFE_SQL_NTS(pcat),
2966 			(SQLCHAR *) pschema, SAFE_SQL_NTS(pschema),
2967 			(SQLCHAR *) ptable, SAFE_SQL_NTS(ptable),
2968 			(SQLCHAR *) fcat, SAFE_SQL_NTS(fcat),
2969 			(SQLCHAR *) fschema, SAFE_SQL_NTS(fschema),
2970 			(SQLCHAR *) ftable, SAFE_SQL_NTS(ftable) );
2971 
2972 	if (rc == SQL_ERROR) {
2973 		odbc_sql_error(conn, result->stmt, "SQLForeignKeys");
2974 		efree(result);
2975 		RETURN_FALSE;
2976 	}
2977 
2978 	result->numparams = 0;
2979 	SQLNumResultCols(result->stmt, &(result->numcols));
2980 
2981 	if (result->numcols > 0) {
2982 		if (!odbc_bindcols(result)) {
2983 			efree(result);
2984 			RETURN_FALSE;
2985 		}
2986 	} else {
2987 		result->values = NULL;
2988 	}
2989 	result->conn_ptr = conn;
2990 	result->fetched = 0;
2991 	RETURN_RES(zend_register_resource(result, le_result));
2992 }
2993 /* }}} */
2994 #endif /* HAVE_SOLID */
2995 
2996 /* {{{ Returns a result identifier containing information about data types supported by the data source */
PHP_FUNCTION(odbc_gettypeinfo)2997 PHP_FUNCTION(odbc_gettypeinfo)
2998 {
2999 	zval *pv_conn;
3000 	zend_long pv_data_type = SQL_ALL_TYPES;
3001 	odbc_result *result = NULL;
3002 	odbc_connection *conn;
3003 	RETCODE rc;
3004 	SQLSMALLINT data_type;
3005 
3006 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &pv_conn, &pv_data_type) == FAILURE) {
3007 		RETURN_THROWS();
3008 	}
3009 
3010 	data_type = (SQLSMALLINT) pv_data_type;
3011 
3012 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
3013 		RETURN_THROWS();
3014 	}
3015 
3016 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
3017 
3018 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3019 	if (rc == SQL_INVALID_HANDLE) {
3020 		efree(result);
3021 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3022 		RETURN_FALSE;
3023 	}
3024 
3025 	if (rc == SQL_ERROR) {
3026 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3027 		efree(result);
3028 		RETURN_FALSE;
3029 	}
3030 
3031 	rc = SQLGetTypeInfo(result->stmt, data_type );
3032 
3033 	if (rc == SQL_ERROR) {
3034 		odbc_sql_error(conn, result->stmt, "SQLGetTypeInfo");
3035 		efree(result);
3036 		RETURN_FALSE;
3037 	}
3038 
3039 	result->numparams = 0;
3040 	SQLNumResultCols(result->stmt, &(result->numcols));
3041 
3042 	if (result->numcols > 0) {
3043 		if (!odbc_bindcols(result)) {
3044 			efree(result);
3045 			RETURN_FALSE;
3046 		}
3047 	} else {
3048 		result->values = NULL;
3049 	}
3050 	result->conn_ptr = conn;
3051 	result->fetched = 0;
3052 	RETURN_RES(zend_register_resource(result, le_result));
3053 }
3054 /* }}} */
3055 
3056 /* {{{ Returns a result identifier listing the column names that comprise the primary key for a table */
PHP_FUNCTION(odbc_primarykeys)3057 PHP_FUNCTION(odbc_primarykeys)
3058 {
3059 	zval *pv_conn;
3060 	odbc_result   *result = NULL;
3061 	odbc_connection *conn;
3062 	char *cat = NULL, *schema = NULL, *table = NULL;
3063 	size_t cat_len = 0, schema_len, table_len;
3064 	RETCODE rc;
3065 
3066 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!ss", &pv_conn, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) {
3067 		RETURN_THROWS();
3068 	}
3069 
3070 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
3071 		RETURN_THROWS();
3072 	}
3073 
3074 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
3075 
3076 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3077 	if (rc == SQL_INVALID_HANDLE) {
3078 		efree(result);
3079 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3080 		RETURN_FALSE;
3081 	}
3082 
3083 	if (rc == SQL_ERROR) {
3084 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3085 		efree(result);
3086 		RETURN_FALSE;
3087 	}
3088 
3089 	rc = SQLPrimaryKeys(result->stmt,
3090 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3091 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3092 			(SQLCHAR *) table, SAFE_SQL_NTS(table) );
3093 
3094 	if (rc == SQL_ERROR) {
3095 		odbc_sql_error(conn, result->stmt, "SQLPrimaryKeys");
3096 		efree(result);
3097 		RETURN_FALSE;
3098 	}
3099 
3100 	result->numparams = 0;
3101 	SQLNumResultCols(result->stmt, &(result->numcols));
3102 
3103 	if (result->numcols > 0) {
3104 		if (!odbc_bindcols(result)) {
3105 			efree(result);
3106 			RETURN_FALSE;
3107 		}
3108 	} else {
3109 		result->values = NULL;
3110 	}
3111 	result->conn_ptr = conn;
3112 	result->fetched = 0;
3113 	RETURN_RES(zend_register_resource(result, le_result));
3114 }
3115 /* }}} */
3116 
3117 #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
3118 /* {{{ 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)3119 PHP_FUNCTION(odbc_procedurecolumns)
3120 {
3121 	zval *pv_conn;
3122 	odbc_result *result = NULL;
3123 	odbc_connection *conn;
3124 	char *cat = NULL, *schema = NULL, *proc = NULL, *col = NULL;
3125 	size_t cat_len = 0, schema_len = 0, proc_len = 0, col_len = 0;
3126 	RETCODE rc;
3127 
3128 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len,
3129 		&proc, &proc_len, &col, &col_len) == FAILURE) {
3130 		RETURN_THROWS();
3131 	}
3132 
3133 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
3134 		RETURN_THROWS();
3135 	}
3136 
3137 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
3138 
3139 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3140 	if (rc == SQL_INVALID_HANDLE) {
3141 		efree(result);
3142 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3143 		RETURN_FALSE;
3144 	}
3145 
3146 	if (rc == SQL_ERROR) {
3147 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3148 		efree(result);
3149 		RETURN_FALSE;
3150 	}
3151 
3152 	rc = SQLProcedureColumns(result->stmt,
3153 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3154 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3155 			(SQLCHAR *) proc, SAFE_SQL_NTS(proc),
3156 			(SQLCHAR *) col, SAFE_SQL_NTS(col) );
3157 
3158 	if (rc == SQL_ERROR) {
3159 		odbc_sql_error(conn, result->stmt, "SQLProcedureColumns");
3160 		efree(result);
3161 		RETURN_FALSE;
3162 	}
3163 
3164 	result->numparams = 0;
3165 	SQLNumResultCols(result->stmt, &(result->numcols));
3166 
3167 	if (result->numcols > 0) {
3168 		if (!odbc_bindcols(result)) {
3169 			efree(result);
3170 			RETURN_FALSE;
3171 		}
3172 	} else {
3173 		result->values = NULL;
3174 	}
3175 	result->conn_ptr = conn;
3176 	result->fetched = 0;
3177 	RETURN_RES(zend_register_resource(result, le_result));
3178 }
3179 /* }}} */
3180 #endif /* HAVE_SOLID */
3181 
3182 #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
3183 /* {{{ Returns a result identifier containing the list of procedure names in a datasource */
PHP_FUNCTION(odbc_procedures)3184 PHP_FUNCTION(odbc_procedures)
3185 {
3186 	zval *pv_conn;
3187 	odbc_result   *result = NULL;
3188 	odbc_connection *conn;
3189 	char *cat = NULL, *schema = NULL, *proc = NULL;
3190 	size_t cat_len = 0, schema_len = 0, proc_len = 0;
3191 	RETCODE rc;
3192 
3193 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len, &proc, &proc_len) == FAILURE) {
3194 		RETURN_THROWS();
3195 	}
3196 
3197 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
3198 		RETURN_THROWS();
3199 	}
3200 
3201 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
3202 
3203 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3204 	if (rc == SQL_INVALID_HANDLE) {
3205 		efree(result);
3206 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3207 		RETURN_FALSE;
3208 	}
3209 
3210 	if (rc == SQL_ERROR) {
3211 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3212 		efree(result);
3213 		RETURN_FALSE;
3214 	}
3215 
3216 	rc = SQLProcedures(result->stmt,
3217 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3218 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3219 			(SQLCHAR *) proc, SAFE_SQL_NTS(proc) );
3220 
3221 	if (rc == SQL_ERROR) {
3222 		odbc_sql_error(conn, result->stmt, "SQLProcedures");
3223 		efree(result);
3224 		RETURN_FALSE;
3225 	}
3226 
3227 	result->numparams = 0;
3228 	SQLNumResultCols(result->stmt, &(result->numcols));
3229 
3230 	if (result->numcols > 0) {
3231 		if (!odbc_bindcols(result)) {
3232 			efree(result);
3233 			RETURN_FALSE;
3234 		}
3235 	} else {
3236 		result->values = NULL;
3237 	}
3238 	result->conn_ptr = conn;
3239 	result->fetched = 0;
3240 	RETURN_RES(zend_register_resource(result, le_result));
3241 }
3242 /* }}} */
3243 #endif /* HAVE_SOLID */
3244 
3245 /* {{{ 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)3246 PHP_FUNCTION(odbc_specialcolumns)
3247 {
3248 	zval *pv_conn;
3249 	zend_long vtype, vscope, vnullable;
3250 	odbc_result *result = NULL;
3251 	odbc_connection *conn;
3252 	char *cat = NULL, *schema = NULL, *name = NULL;
3253 	size_t cat_len = 0, schema_len, name_len;
3254 	SQLUSMALLINT type, scope, nullable;
3255 	RETCODE rc;
3256 
3257 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rls!ssll", &pv_conn, &vtype, &cat, &cat_len, &schema, &schema_len,
3258 		&name, &name_len, &vscope, &vnullable) == FAILURE) {
3259 		RETURN_THROWS();
3260 	}
3261 
3262 	type = (SQLUSMALLINT) vtype;
3263 	scope = (SQLUSMALLINT) vscope;
3264 	nullable = (SQLUSMALLINT) vnullable;
3265 
3266 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
3267 		RETURN_THROWS();
3268 	}
3269 
3270 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
3271 
3272 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3273 	if (rc == SQL_INVALID_HANDLE) {
3274 		efree(result);
3275 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3276 		RETURN_FALSE;
3277 	}
3278 
3279 	if (rc == SQL_ERROR) {
3280 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3281 		efree(result);
3282 		RETURN_FALSE;
3283 	}
3284 
3285 	rc = SQLSpecialColumns(result->stmt, type,
3286 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3287 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3288 			(SQLCHAR *) name, SAFE_SQL_NTS(name),
3289 			scope,
3290 			nullable);
3291 
3292 	if (rc == SQL_ERROR) {
3293 		odbc_sql_error(conn, result->stmt, "SQLSpecialColumns");
3294 		efree(result);
3295 		RETURN_FALSE;
3296 	}
3297 
3298 	result->numparams = 0;
3299 	SQLNumResultCols(result->stmt, &(result->numcols));
3300 
3301 	if (result->numcols > 0) {
3302 		if (!odbc_bindcols(result)) {
3303 			efree(result);
3304 			RETURN_FALSE;
3305 		}
3306 	} else {
3307 		result->values = NULL;
3308 	}
3309 	result->conn_ptr = conn;
3310 	result->fetched = 0;
3311 	RETURN_RES(zend_register_resource(result, le_result));
3312 }
3313 /* }}} */
3314 
3315 /* {{{ Returns a result identifier that contains statistics about a single table and the indexes associated with the table */
PHP_FUNCTION(odbc_statistics)3316 PHP_FUNCTION(odbc_statistics)
3317 {
3318 	zval *pv_conn;
3319 	zend_long vunique, vreserved;
3320 	odbc_result *result = NULL;
3321 	odbc_connection *conn;
3322 	char *cat = NULL, *schema, *name;
3323 	size_t cat_len = 0, schema_len, name_len;
3324 	SQLUSMALLINT unique, reserved;
3325 	RETCODE rc;
3326 
3327 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!ssll", &pv_conn, &cat, &cat_len, &schema, &schema_len,
3328 		&name, &name_len, &vunique, &vreserved) == FAILURE) {
3329 		RETURN_THROWS();
3330 	}
3331 
3332 	unique = (SQLUSMALLINT) vunique;
3333 	reserved = (SQLUSMALLINT) vreserved;
3334 
3335 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
3336 		RETURN_THROWS();
3337 	}
3338 
3339 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
3340 
3341 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3342 	if (rc == SQL_INVALID_HANDLE) {
3343 		efree(result);
3344 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3345 		RETURN_FALSE;
3346 	}
3347 
3348 	if (rc == SQL_ERROR) {
3349 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3350 		efree(result);
3351 		RETURN_FALSE;
3352 	}
3353 
3354 	rc = SQLStatistics(result->stmt,
3355 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3356 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3357 			(SQLCHAR *) name, SAFE_SQL_NTS(name),
3358 			unique,
3359 			reserved);
3360 
3361 	if (rc == SQL_ERROR) {
3362 		odbc_sql_error(conn, result->stmt, "SQLStatistics");
3363 		efree(result);
3364 		RETURN_FALSE;
3365 	}
3366 
3367 	result->numparams = 0;
3368 	SQLNumResultCols(result->stmt, &(result->numcols));
3369 
3370 	if (result->numcols > 0) {
3371 		if (!odbc_bindcols(result)) {
3372 			efree(result);
3373 			RETURN_FALSE;
3374 		}
3375 	} else {
3376 		result->values = NULL;
3377 	}
3378 	result->conn_ptr = conn;
3379 	result->fetched = 0;
3380 	RETURN_RES(zend_register_resource(result, le_result));
3381 }
3382 /* }}} */
3383 
3384 #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35)
3385 /* {{{ Returns a result identifier containing a list of tables and the privileges associated with each table */
PHP_FUNCTION(odbc_tableprivileges)3386 PHP_FUNCTION(odbc_tableprivileges)
3387 {
3388 	zval *pv_conn;
3389 	odbc_result   *result = NULL;
3390 	odbc_connection *conn;
3391 	char *cat = NULL, *schema = NULL, *table = NULL;
3392 	size_t cat_len = 0, schema_len, table_len;
3393 	RETCODE rc;
3394 
3395 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!ss", &pv_conn, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) {
3396 		RETURN_THROWS();
3397 	}
3398 
3399 	if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) {
3400 		RETURN_THROWS();
3401 	}
3402 
3403 	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));
3404 
3405 	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
3406 	if (rc == SQL_INVALID_HANDLE) {
3407 		efree(result);
3408 		php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
3409 		RETURN_FALSE;
3410 	}
3411 
3412 	if (rc == SQL_ERROR) {
3413 		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
3414 		efree(result);
3415 		RETURN_FALSE;
3416 	}
3417 
3418 	rc = SQLTablePrivileges(result->stmt,
3419 			(SQLCHAR *) cat, SAFE_SQL_NTS(cat),
3420 			(SQLCHAR *) schema, SAFE_SQL_NTS(schema),
3421 			(SQLCHAR *) table, SAFE_SQL_NTS(table));
3422 
3423 	if (rc == SQL_ERROR) {
3424 		odbc_sql_error(conn, result->stmt, "SQLTablePrivileges");
3425 		efree(result);
3426 		RETURN_FALSE;
3427 	}
3428 
3429 	result->numparams = 0;
3430 	SQLNumResultCols(result->stmt, &(result->numcols));
3431 
3432 	if (result->numcols > 0) {
3433 		if (!odbc_bindcols(result)) {
3434 			efree(result);
3435 			RETURN_FALSE;
3436 		}
3437 	} else {
3438 		result->values = NULL;
3439 	}
3440 	result->conn_ptr = conn;
3441 	result->fetched = 0;
3442 	RETURN_RES(zend_register_resource(result, le_result));
3443 }
3444 /* }}} */
3445 #endif /* HAVE_DBMAKER */
3446 
3447 #endif /* HAVE_UODBC */
3448