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