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