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