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