xref: /PHP-5.3/ext/sybase_ct/php_sybase_ct.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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: Zeev Suraski <zeev@zend.com>                                |
16    |          Tom May <tom@go2net.com>                                    |
17    |          Timm Friebe <php_sybase_ct@thekid.de>                       |
18    +----------------------------------------------------------------------+
19  */
20 
21 /* $Id$ */
22 
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "php.h"
29 #include "php_sybase_ct.h"
30 #include "ext/standard/php_standard.h"
31 #include "ext/standard/info.h"
32 #include "php_globals.h"
33 #include "php_ini.h"
34 
35 /* True globals, no need for thread safety */
36 static int le_link, le_plink, le_result;
37 
38 #if HAVE_SYBASE_CT
39 
40 ZEND_DECLARE_MODULE_GLOBALS(sybase)
41 static PHP_GINIT_FUNCTION(sybase);
42 static PHP_GSHUTDOWN_FUNCTION(sybase);
43 
44 /* {{{ arginfo */
45 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_connect, 0, 0, 0)
46 	ZEND_ARG_INFO(0, host)
47 	ZEND_ARG_INFO(0, user)
48 	ZEND_ARG_INFO(0, password)
49 	ZEND_ARG_INFO(0, charset)
50 	ZEND_ARG_INFO(0, appname)
51 	ZEND_ARG_INFO(0, new)
52 ZEND_END_ARG_INFO()
53 
54 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_pconnect, 0, 0, 0)
55 	ZEND_ARG_INFO(0, host)
56 	ZEND_ARG_INFO(0, user)
57 	ZEND_ARG_INFO(0, password)
58 	ZEND_ARG_INFO(0, charset)
59 	ZEND_ARG_INFO(0, appname)
60 ZEND_END_ARG_INFO()
61 
62 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_close, 0, 0, 0)
63 	ZEND_ARG_INFO(0, link_id)
64 ZEND_END_ARG_INFO()
65 
66 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_select_db, 0, 0, 1)
67 	ZEND_ARG_INFO(0, database)
68 	ZEND_ARG_INFO(0, link_id)
69 ZEND_END_ARG_INFO()
70 
71 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_query, 0, 0, 1)
72 	ZEND_ARG_INFO(0, query)
73 	ZEND_ARG_INFO(0, link_id)
74 ZEND_END_ARG_INFO()
75 
76 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_unbuffered_query, 0, 0, 1)
77 	ZEND_ARG_INFO(0, query)
78 	ZEND_ARG_INFO(0, link_id)
79 ZEND_END_ARG_INFO()
80 
81 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_free_result, 0, 0, 1)
82 	ZEND_ARG_INFO(0, result)
83 ZEND_END_ARG_INFO()
84 
85 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_get_last_message, 0, 0, 1)
86 	ZEND_ARG_INFO(0, d)
87 ZEND_END_ARG_INFO()
88 
89 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_num_rows, 0, 0, 1)
90 	ZEND_ARG_INFO(0, result)
91 ZEND_END_ARG_INFO()
92 
93 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_num_fields, 0, 0, 1)
94 	ZEND_ARG_INFO(0, result)
95 ZEND_END_ARG_INFO()
96 
97 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_row, 0, 0, 1)
98 	ZEND_ARG_INFO(0, result)
99 ZEND_END_ARG_INFO()
100 
101 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_object, 0, 0, 1)
102 	ZEND_ARG_INFO(0, result)
103 	ZEND_ARG_INFO(0, object)
104 ZEND_END_ARG_INFO()
105 
106 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_array, 0, 0, 1)
107 	ZEND_ARG_INFO(0, result)
108 ZEND_END_ARG_INFO()
109 
110 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_assoc, 0, 0, 1)
111 	ZEND_ARG_INFO(0, result)
112 ZEND_END_ARG_INFO()
113 
114 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_data_seek, 0, 0, 2)
115 	ZEND_ARG_INFO(0, result)
116 	ZEND_ARG_INFO(0, offset)
117 ZEND_END_ARG_INFO()
118 
119 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_field, 0, 0, 1)
120 	ZEND_ARG_INFO(0, result)
121 	ZEND_ARG_INFO(0, offset)
122 ZEND_END_ARG_INFO()
123 
124 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_field_seek, 0, 0, 2)
125 	ZEND_ARG_INFO(0, result)
126 	ZEND_ARG_INFO(0, offset)
127 ZEND_END_ARG_INFO()
128 
129 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_result, 0, 0, 3)
130 	ZEND_ARG_INFO(0, result)
131 	ZEND_ARG_INFO(0, row)
132 	ZEND_ARG_INFO(0, field)
133 ZEND_END_ARG_INFO()
134 
135 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_affected_rows, 0, 0, 0)
136 	ZEND_ARG_INFO(0, link_id)
137 ZEND_END_ARG_INFO()
138 
139 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_min_client_severity, 0, 0, 1)
140 	ZEND_ARG_INFO(0, severity)
141 ZEND_END_ARG_INFO()
142 
143 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_min_server_severity, 0, 0, 1)
144 	ZEND_ARG_INFO(0, severity)
145 ZEND_END_ARG_INFO()
146 
147 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_deadlock_retry_count, 0, 0, 1)
148 	ZEND_ARG_INFO(0, retry_count)
149 ZEND_END_ARG_INFO()
150 
151 ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_set_message_handler, 0, 0, 1)
152 	ZEND_ARG_INFO(0, error_func)
153 	ZEND_ARG_INFO(0, connection)
154 ZEND_END_ARG_INFO()
155 /* }}} */
156 
157 const zend_function_entry sybase_functions[] = {
158 	PHP_FE(sybase_connect,				arginfo_sybase_connect)
159 	PHP_FE(sybase_pconnect,				arginfo_sybase_pconnect)
160 	PHP_FE(sybase_close, 				arginfo_sybase_close)
161 	PHP_FE(sybase_select_db, 			arginfo_sybase_select_db)
162 	PHP_FE(sybase_query,				arginfo_sybase_query)
163 	PHP_FE(sybase_unbuffered_query,		arginfo_sybase_unbuffered_query)
164 	PHP_FE(sybase_free_result,			arginfo_sybase_free_result)
165 	PHP_FE(sybase_get_last_message,		arginfo_sybase_get_last_message)
166 	PHP_FE(sybase_num_rows,				arginfo_sybase_num_rows)
167 	PHP_FE(sybase_num_fields,			arginfo_sybase_num_fields)
168 	PHP_FE(sybase_fetch_row,			arginfo_sybase_fetch_row)
169 	PHP_FE(sybase_fetch_array,			arginfo_sybase_fetch_array)
170 	PHP_FE(sybase_fetch_assoc,			arginfo_sybase_fetch_assoc)
171 	PHP_FE(sybase_fetch_object,			arginfo_sybase_fetch_object)
172 	PHP_FE(sybase_data_seek,			arginfo_sybase_data_seek)
173 	PHP_FE(sybase_fetch_field,			arginfo_sybase_fetch_field)
174 	PHP_FE(sybase_field_seek, 			arginfo_sybase_field_seek)
175 	PHP_FE(sybase_result,				arginfo_sybase_result)
176 	PHP_FE(sybase_affected_rows, 		arginfo_sybase_affected_rows)
177 	PHP_FE(sybase_min_client_severity,	arginfo_sybase_min_client_severity)
178 	PHP_FE(sybase_min_server_severity,	arginfo_sybase_min_server_severity)
179 	PHP_FE(sybase_set_message_handler,	arginfo_sybase_set_message_handler)
180 	PHP_FE(sybase_deadlock_retry_count, arginfo_sybase_deadlock_retry_count)
181 
182 #if !defined(PHP_WIN32) && !defined(HAVE_MSSQL)
183 	PHP_FALIAS(mssql_connect, 	sybase_connect, 	arginfo_sybase_connect)
184 	PHP_FALIAS(mssql_pconnect, 	sybase_pconnect, 	arginfo_sybase_pconnect)
185 	PHP_FALIAS(mssql_close, 	sybase_close, 		arginfo_sybase_close)
186 	PHP_FALIAS(mssql_select_db, sybase_select_db, 	arginfo_sybase_select_db)
187 	PHP_FALIAS(mssql_query, 	sybase_query, 		arginfo_sybase_query)
188 	PHP_FALIAS(mssql_unbuffered_query, 	sybase_unbuffered_query, 	arginfo_sybase_unbuffered_query)
189 	PHP_FALIAS(mssql_free_result, 		sybase_free_result, 		arginfo_sybase_free_result)
190 	PHP_FALIAS(mssql_get_last_message, 	sybase_get_last_message, 	arginfo_sybase_get_last_message)
191 	PHP_FALIAS(mssql_num_rows,	 	sybase_num_rows, 		arginfo_sybase_num_rows)
192 	PHP_FALIAS(mssql_num_fields, 	sybase_num_fields, 		arginfo_sybase_num_fields)
193 	PHP_FALIAS(mssql_fetch_row, 	sybase_fetch_row, 		arginfo_sybase_fetch_row)
194 	PHP_FALIAS(mssql_fetch_array, 	sybase_fetch_array, 	arginfo_sybase_fetch_array)
195 	PHP_FALIAS(mssql_fetch_assoc, 	sybase_fetch_assoc, 	arginfo_sybase_fetch_assoc)
196 	PHP_FALIAS(mssql_fetch_object, 	sybase_fetch_object, 	arginfo_sybase_fetch_object)
197 	PHP_FALIAS(mssql_data_seek, 	sybase_data_seek, 		arginfo_sybase_data_seek)
198 	PHP_FALIAS(mssql_fetch_field, 	sybase_fetch_field, 	arginfo_sybase_fetch_field)
199 	PHP_FALIAS(mssql_field_seek, 	sybase_field_seek, 		arginfo_sybase_field_seek)
200 	PHP_FALIAS(mssql_result, 		sybase_result, 			arginfo_sybase_result)
201 	PHP_FALIAS(mssql_affected_rows, sybase_affected_rows, 	arginfo_sybase_affected_rows)
202 	PHP_FALIAS(mssql_min_client_severity,   sybase_min_client_severity, 	arginfo_sybase_min_client_severity)
203 	PHP_FALIAS(mssql_min_server_severity, 	sybase_min_server_severity, 	arginfo_sybase_min_server_severity)
204 	PHP_FALIAS(mssql_set_message_handler, 	sybase_set_message_handler, 	arginfo_sybase_set_message_handler)
205 	PHP_FALIAS(mssql_deadlock_retry_count, 	sybase_deadlock_retry_count, 	arginfo_sybase_deadlock_retry_count)
206 #endif
207 	PHP_FE_END
208 };
209 
210 zend_module_entry sybase_module_entry = {
211 	STANDARD_MODULE_HEADER,
212 	"sybase_ct",
213 	sybase_functions,
214 	PHP_MINIT(sybase),
215 	PHP_MSHUTDOWN(sybase),
216 	PHP_RINIT(sybase),
217 	PHP_RSHUTDOWN(sybase),
218 	PHP_MINFO(sybase),
219 	NO_VERSION_YET,
220 	PHP_MODULE_GLOBALS(sybase),
221 	PHP_GINIT(sybase),
222 	PHP_GSHUTDOWN(sybase),
223 	NULL,
224 	STANDARD_MODULE_PROPERTIES_EX
225 };
226 
227 /* static CS_CONTEXT *context; */
228 
229 #ifdef COMPILE_DL_SYBASE_CT
230 ZEND_GET_MODULE(sybase)
231 #endif
232 
ZEND_DECLARE_MODULE_GLOBALS(sybase)233 ZEND_DECLARE_MODULE_GLOBALS(sybase)
234 
235 #define CHECK_LINK(link) { if (link==-1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  A link to the server could not be established"); RETURN_FALSE; } }
236 
237 
238 static int _clean_invalid_results(zend_rsrc_list_entry *le TSRMLS_DC)
239 {
240 	if (Z_TYPE_P(le) == le_result) {
241 		sybase_link *sybase_ptr = ((sybase_result *) le->ptr)->sybase_ptr;
242 
243 		if (!sybase_ptr->valid) {
244 			return 1;
245 		}
246 	}
247 	return 0;
248 }
249 
250 #define efree_n(x)  { efree(x); x = NULL; }
251 #define efree_if(x) if (x) efree_n(x)
252 
253 #ifdef PHP_SYBASE_DEBUG
254 #define FREE_SYBASE_RESULT(result)                                                            \
255 	if (result) {                                                                             \
256 	    fprintf(stderr, "_free_sybase_result(%p) called from line #%d\n", result, __LINE__);  \
257 		fflush(stderr);                                                                       \
258 		_free_sybase_result(result);                                                          \
259 		result = NULL;                                                                        \
260 	}
261 #else
262 #define FREE_SYBASE_RESULT(result)                                                            \
263 	if (result) {                                                                             \
264 		_free_sybase_result(result);                                                          \
265 		result = NULL;                                                                        \
266 	}
267 #endif
_free_sybase_result(sybase_result * result)268 static void _free_sybase_result(sybase_result *result)
269 {
270 	int i, j;
271 
272 	if (result->data) {
273 		for (i = 0; i < (result->store ? result->num_rows : MIN(1, result->num_rows)); i++) {
274 			for (j=0; j<result->num_fields; j++) {
275 				zval_dtor(&result->data[i][j]);
276 			}
277 			efree(result->data[i]);
278 		}
279 		efree(result->data);
280 	}
281 
282 	if (result->fields) {
283 		for (i=0; i<result->num_fields; i++) {
284 			STR_FREE(result->fields[i].name);
285 			STR_FREE(result->fields[i].column_source);
286 		}
287 		efree(result->fields);
288 	}
289 
290 	if (result->tmp_buffer) {
291 		for (i=0; i<result->num_fields; i++) {
292 			efree(result->tmp_buffer[i]);
293 		}
294 		efree(result->tmp_buffer);
295 	}
296 
297 	efree_if(result->lengths);
298 	efree_if(result->indicators);
299 	efree_if(result->datafmt);
300 	efree_if(result->numerics);
301 	efree_if(result->types);
302 
303 	efree(result);
304 }
305 
306 /* Forward declaration */
307 static int php_sybase_finish_results (sybase_result *result TSRMLS_DC);
308 
php_free_sybase_result(zend_rsrc_list_entry * rsrc TSRMLS_DC)309 static void php_free_sybase_result(zend_rsrc_list_entry *rsrc TSRMLS_DC)
310 {
311 	sybase_result *result = (sybase_result *)rsrc->ptr;
312 
313 	/* Check to see if we've read all rows */
314 	if (result->sybase_ptr && result->sybase_ptr->active_result_index) {
315 		if (result->sybase_ptr->cmd) {
316 			ct_cancel(NULL, result->sybase_ptr->cmd, CS_CANCEL_ALL);
317 		}
318 		php_sybase_finish_results(result TSRMLS_CC);
319 	}
320 
321 	FREE_SYBASE_RESULT(result);
322 }
323 
_close_sybase_link(zend_rsrc_list_entry * rsrc TSRMLS_DC)324 static void _close_sybase_link(zend_rsrc_list_entry *rsrc TSRMLS_DC)
325 {
326 	sybase_link *sybase_ptr = (sybase_link *)rsrc->ptr;
327 	CS_INT con_status;
328 
329 	sybase_ptr->valid = 0;
330 	if (sybase_ptr->callback_name != NULL) {
331 		zval_ptr_dtor(&sybase_ptr->callback_name);
332 		sybase_ptr->callback_name= NULL;
333 	}
334 	zend_hash_apply(&EG(regular_list), (apply_func_t) _clean_invalid_results TSRMLS_CC);
335 
336 	/* Non-persistent connections will always be connected or we wouldn't
337 	 * get here, but since we want to check the death status anyway
338 	 * we might as well double-check the connect status.
339 	 */
340 	if (ct_con_props(sybase_ptr->connection, CS_GET, CS_CON_STATUS,
341 					 &con_status, CS_UNUSED, NULL)!=CS_SUCCEED) {
342 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to get connection status on close");
343 		/* Assume the worst. */
344 		con_status = CS_CONSTAT_CONNECTED | CS_CONSTAT_DEAD;
345 	}
346 	if (con_status & CS_CONSTAT_CONNECTED) {
347 		if ((con_status & CS_CONSTAT_DEAD) || ct_close(sybase_ptr->connection, CS_UNUSED)!=CS_SUCCEED) {
348 			ct_close(sybase_ptr->connection, CS_FORCE_CLOSE);
349 		}
350 	}
351 
352 	ct_cmd_drop(sybase_ptr->cmd);
353 	ct_con_drop(sybase_ptr->connection);
354 	efree(sybase_ptr);
355 	SybCtG(num_links)--;
356 }
357 
358 
_close_sybase_plink(zend_rsrc_list_entry * rsrc TSRMLS_DC)359 static void _close_sybase_plink(zend_rsrc_list_entry *rsrc TSRMLS_DC)
360 {
361 	sybase_link *sybase_ptr = (sybase_link *)rsrc->ptr;
362 	CS_INT con_status;
363 
364 	/* Persistent connections may have been closed before a failed
365 	 * reopen attempt.
366 	 */
367 	if (ct_con_props(sybase_ptr->connection, CS_GET, CS_CON_STATUS,
368 					 &con_status, CS_UNUSED, NULL)!=CS_SUCCEED) {
369 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to get connection status on close");
370 		/* Assume the worst. */
371 		con_status = CS_CONSTAT_CONNECTED | CS_CONSTAT_DEAD;
372 	}
373 	if (con_status & CS_CONSTAT_CONNECTED) {
374 		if ((con_status & CS_CONSTAT_DEAD) || ct_close(sybase_ptr->connection, CS_UNUSED)!=CS_SUCCEED) {
375 			ct_close(sybase_ptr->connection, CS_FORCE_CLOSE);
376 		}
377 	}
378 
379 	ct_con_drop(sybase_ptr->connection);
380 	free(sybase_ptr);
381 	SybCtG(num_persistent)--;
382 	SybCtG(num_links)--;
383 }
384 
385 
_client_message_handler(CS_CONTEXT * context,CS_CONNECTION * connection,CS_CLIENTMSG * errmsg)386 static CS_RETCODE CS_PUBLIC _client_message_handler(CS_CONTEXT *context, CS_CONNECTION *connection, CS_CLIENTMSG *errmsg)
387 {
388 	TSRMLS_FETCH();
389 
390 	if (CS_SEVERITY(errmsg->msgnumber) >= SybCtG(min_client_severity)) {
391 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Client message:  %s (severity %ld)", errmsg->msgstring, (long)CS_SEVERITY(errmsg->msgnumber));
392 	}
393 	STR_FREE(SybCtG(server_message));
394 	SybCtG(server_message) = estrdup(errmsg->msgstring);
395 
396 
397 	/* If this is a timeout message, return CS_FAIL to cancel the
398 	 * operation and mark the connection as dead.
399 	 */
400 	if (CS_SEVERITY(errmsg->msgnumber) == CS_SV_RETRY_FAIL &&
401 		CS_NUMBER(errmsg->msgnumber) == 63 &&
402 		CS_ORIGIN(errmsg->msgnumber) == 2 &&
403 		CS_LAYER(errmsg->msgnumber) == 1)
404 	{
405 		return CS_FAIL;
406 	}
407 
408 	return CS_SUCCEED;
409 }
410 
_call_message_handler(zval * callback_name,CS_SERVERMSG * srvmsg TSRMLS_DC)411 static int _call_message_handler(zval *callback_name, CS_SERVERMSG *srvmsg TSRMLS_DC)
412 {
413 	int handled = 0;
414 	zval *msgnumber, *severity, *state, *line, *text, *retval = NULL;
415 	zval **args[5];
416 
417 	/* Border case - empty fcall */
418 	if (NULL == callback_name) return 0;
419 
420 	/* Build arguments */
421 	MAKE_STD_ZVAL(msgnumber);
422 	ZVAL_LONG(msgnumber, srvmsg->msgnumber);
423 	args[0] = &msgnumber;
424 
425 	MAKE_STD_ZVAL(severity);
426 	ZVAL_LONG(severity, srvmsg->severity);
427 	args[1] = &severity;
428 
429 	MAKE_STD_ZVAL(state);
430 	ZVAL_LONG(state, srvmsg->state);
431 	args[2] = &state;
432 
433 	MAKE_STD_ZVAL(line);
434 	ZVAL_LONG(line, srvmsg->line);
435 	args[3] = &line;
436 
437 	MAKE_STD_ZVAL(text);
438 	ZVAL_STRING(text, srvmsg->text, 1);
439 	args[4] = &text;
440 
441 	if (call_user_function_ex(EG(function_table), NULL, callback_name, &retval, 5, args, 0, NULL TSRMLS_CC) == FAILURE) {
442 		zval expr_copy;
443 		int use_copy;
444 
445 		zend_make_printable_zval(callback_name, &expr_copy, &use_copy);
446 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Cannot call the messagehandler %s", Z_STRVAL(expr_copy));
447 		zval_dtor(&expr_copy);
448 	}
449 
450 	if (retval) {
451 		handled = ((Z_TYPE_P(retval) != IS_BOOL) || (Z_BVAL_P(retval) != 0));
452 		zval_ptr_dtor(&retval);
453 	} else {
454 		handled = 0;
455 	}
456 
457 	zval_ptr_dtor(&msgnumber);
458 	zval_ptr_dtor(&severity);
459 	zval_ptr_dtor(&state);
460 	zval_ptr_dtor(&line);
461 	zval_ptr_dtor(&text);
462 
463 	return handled;
464 }
465 
_server_message_handler(CS_CONTEXT * context,CS_CONNECTION * connection,CS_SERVERMSG * srvmsg)466 static CS_RETCODE CS_PUBLIC _server_message_handler(CS_CONTEXT *context, CS_CONNECTION *connection, CS_SERVERMSG *srvmsg)
467 {
468 	sybase_link *sybase;
469 	int handled = 0;
470 	TSRMLS_FETCH();
471 
472 	/* Remember the last server message in any case */
473 	STR_FREE(SybCtG(server_message));
474 	SybCtG(server_message) = estrdup(srvmsg->text);
475 
476 	/* Retrieve sybase link */
477 	if (ct_con_props(connection, CS_GET, CS_USERDATA, &sybase, CS_SIZEOF(sybase), NULL) != CS_SUCCEED) {
478 		sybase = NULL;
479 	}
480 
481 	/* If this is a deadlock message, set the connection's deadlock flag
482 	 * so we will retry the request.  Sorry about the bare constant here,
483 	 * but it's not defined anywhere and it's a "well-known" number.
484 	 */
485 	if (sybase && (srvmsg->msgnumber == 1205)) {
486 		sybase->deadlock = 1;
487 	}
488 
489 	/* Check mininum server severity level */
490 	if (srvmsg->severity < SybCtG(min_server_severity)) {
491 		return CS_SUCCEED;
492 	}
493 
494 	/* Call global message handler */
495 	handled = handled | _call_message_handler(SybCtG(callback_name), srvmsg TSRMLS_CC);
496 
497 	/* Call link specific message handler */
498 	if (sybase) {
499 		handled = handled | _call_message_handler(sybase->callback_name, srvmsg TSRMLS_CC);
500 	}
501 
502 	/* Spit out a warning if neither of them has handled this message */
503 	if (!handled) {
504 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Server message:  %s (severity %ld, procedure %s)",
505 				srvmsg->text, (long)srvmsg->severity, ((srvmsg->proclen>0) ? srvmsg->proc : "N/A"));
506 	}
507 
508 	return CS_SUCCEED;
509 }
510 
511 
512 PHP_INI_BEGIN()
513 	STD_PHP_INI_BOOLEAN("sybct.allow_persistent", "1", PHP_INI_SYSTEM, OnUpdateLong, allow_persistent, zend_sybase_globals, sybase_globals)
514 	STD_PHP_INI_ENTRY_EX("sybct.max_persistent", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_persistent, zend_sybase_globals, sybase_globals, display_link_numbers)
515 	STD_PHP_INI_ENTRY_EX("sybct.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_links, zend_sybase_globals, sybase_globals, display_link_numbers)
516 	STD_PHP_INI_ENTRY("sybct.min_server_severity", "10", PHP_INI_ALL, OnUpdateLong, min_server_severity, zend_sybase_globals, sybase_globals)
517 	STD_PHP_INI_ENTRY("sybct.min_client_severity", "10", PHP_INI_ALL, OnUpdateLong, min_client_severity, zend_sybase_globals, sybase_globals)
518 	STD_PHP_INI_ENTRY("sybct.login_timeout", "-1", PHP_INI_ALL, OnUpdateLong, login_timeout, zend_sybase_globals, sybase_globals)
519 	STD_PHP_INI_ENTRY("sybct.hostname", NULL, PHP_INI_ALL, OnUpdateString, hostname, zend_sybase_globals, sybase_globals)
520 	STD_PHP_INI_ENTRY_EX("sybct.deadlock_retry_count", "0", PHP_INI_ALL, OnUpdateLong, deadlock_retry_count, zend_sybase_globals, sybase_globals, display_link_numbers)
PHP_INI_END()521 PHP_INI_END()
522 
523 
524 static PHP_GINIT_FUNCTION(sybase)
525 {
526 	long opt;
527 
528 	if (cs_ctx_alloc(CTLIB_VERSION, &sybase_globals->context)!=CS_SUCCEED || ct_init(sybase_globals->context, CTLIB_VERSION)!=CS_SUCCEED) {
529 		return;
530 	}
531 
532 	/* Initialize message handlers */
533 	if (ct_callback(sybase_globals->context, NULL, CS_SET, CS_SERVERMSG_CB, (CS_VOID *)_server_message_handler)!=CS_SUCCEED) {
534 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to set server message handler");
535 	}
536 
537 	if (ct_callback(sybase_globals->context, NULL, CS_SET, CS_CLIENTMSG_CB, (CS_VOID *)_client_message_handler)!=CS_SUCCEED) {
538 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to set client message handler");
539 	}
540 
541 	/* Set datetime conversion format to "Nov  3 1998  8:06PM".
542 	 * This is the default format for the ct-lib that comes with
543 	 * Sybase ASE 11.5.1 for Solaris, but the Linux libraries that
544 	 * come with 11.0.3.3 default to "03/11/98" which is singularly
545 	 * useless.  This levels the playing field for all platforms.
546 	 */
547 	{
548 		CS_INT dt_convfmt = CS_DATES_SHORT;
549 		if (cs_dt_info(sybase_globals->context, CS_SET, NULL, CS_DT_CONVFMT, CS_UNUSED, &dt_convfmt, sizeof(dt_convfmt), NULL)!=CS_SUCCEED) {
550 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to set datetime conversion format");
551 		}
552 	}
553 
554 	/* Set the timeout, which is per context and can't be set with
555 	 * ct_con_props(), so set it globally from the config value if
556 	 * requested.  The default is CS_NO_LIMIT.
557 	 *
558 	 * Note that despite some noise in the documentation about using
559 	 * signals to implement timeouts, they are actually implemented
560 	 * by using poll() or select() on Solaris and Linux.
561 	 */
562 	if (cfg_get_long("sybct.timeout", &opt)==SUCCESS) {
563 		CS_INT cs_timeout = opt;
564 		if (ct_config(sybase_globals->context, CS_SET, CS_TIMEOUT, &cs_timeout, CS_UNUSED, NULL)!=CS_SUCCEED) {
565 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to update the timeout");
566 		}
567 	}
568 
569 	sybase_globals->num_persistent=0;
570 	sybase_globals->callback_name = NULL;
571 }
572 
573 
PHP_GSHUTDOWN_FUNCTION(sybase)574 static PHP_GSHUTDOWN_FUNCTION(sybase)
575 {
576 	ct_exit(sybase_globals->context, CS_UNUSED);
577 	cs_ctx_drop(sybase_globals->context);
578 }
579 
PHP_MINIT_FUNCTION(sybase)580 PHP_MINIT_FUNCTION(sybase)
581 {
582 	REGISTER_INI_ENTRIES();
583 
584 	le_link = zend_register_list_destructors_ex(_close_sybase_link, NULL, "sybase-ct link", module_number);
585 	le_plink = zend_register_list_destructors_ex(NULL, _close_sybase_plink, "sybase-ct link persistent", module_number);
586 	le_result = zend_register_list_destructors_ex(php_free_sybase_result, NULL, "sybase-ct result", module_number);
587 
588 	return SUCCESS;
589 }
590 
591 
592 
PHP_RINIT_FUNCTION(sybase)593 PHP_RINIT_FUNCTION(sybase)
594 {
595 	SybCtG(default_link)=-1;
596 	SybCtG(num_links) = SybCtG(num_persistent);
597 	SybCtG(appname) = estrndup("PHP " PHP_VERSION, sizeof("PHP " PHP_VERSION));
598 	SybCtG(server_message) = STR_EMPTY_ALLOC();
599 	return SUCCESS;
600 }
601 
602 
603 
PHP_MSHUTDOWN_FUNCTION(sybase)604 PHP_MSHUTDOWN_FUNCTION(sybase)
605 {
606 	UNREGISTER_INI_ENTRIES();
607 #if 0
608 	ct_exit(context, CS_UNUSED);
609 	cs_ctx_drop(context);
610 #endif
611 	return SUCCESS;
612 }
613 
614 
PHP_RSHUTDOWN_FUNCTION(sybase)615 PHP_RSHUTDOWN_FUNCTION(sybase)
616 {
617 	efree(SybCtG(appname));
618 	SybCtG(appname) = NULL;
619 	if (SybCtG(callback_name)) {
620 		zval_ptr_dtor(&SybCtG(callback_name));
621 		SybCtG(callback_name)= NULL;
622 	}
623 	STR_FREE(SybCtG(server_message));
624 	SybCtG(server_message) = NULL;
625 	return SUCCESS;
626 }
627 
628 
php_sybase_do_connect_internal(sybase_link * sybase,char * host,char * user,char * passwd,char * charset,char * appname TSRMLS_DC)629 static int php_sybase_do_connect_internal(sybase_link *sybase, char *host, char *user, char *passwd, char *charset, char *appname TSRMLS_DC)
630 {
631 	CS_LOCALE *tmp_locale;
632 	long packetsize;
633 
634 	/* set a CS_CONNECTION record */
635 	if (ct_con_alloc(SybCtG(context), &sybase->connection)!=CS_SUCCEED) {
636 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to allocate connection record");
637 		return 0;
638 	}
639 
640 	/* Note - this saves a copy of sybase, not a pointer to it. */
641 	if (ct_con_props(sybase->connection, CS_SET, CS_USERDATA, &sybase, CS_SIZEOF(sybase), NULL)!=CS_SUCCEED) {
642 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to set userdata");
643 		ct_con_drop(sybase->connection);
644 		return 0;
645 	}
646 
647 	if (user) {
648 		ct_con_props(sybase->connection, CS_SET, CS_USERNAME, user, CS_NULLTERM, NULL);
649 	}
650 	if (passwd) {
651 		ct_con_props(sybase->connection, CS_SET, CS_PASSWORD, passwd, CS_NULLTERM, NULL);
652 	}
653 	if (appname) {
654 		ct_con_props(sybase->connection, CS_SET, CS_APPNAME, appname, CS_NULLTERM, NULL);
655 	} else {
656 		ct_con_props(sybase->connection, CS_SET, CS_APPNAME, SybCtG(appname), CS_NULLTERM, NULL);
657 	}
658 
659 	if (SybCtG(hostname)) {
660 		ct_con_props(sybase->connection, CS_SET, CS_HOSTNAME, SybCtG(hostname), CS_NULLTERM, NULL);
661 	}
662 
663 	if (charset) {
664 		if (cs_loc_alloc(SybCtG(context), &tmp_locale)!=CS_SUCCEED) {
665 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase: Unable to allocate locale information");
666 		} else {
667 			if (cs_locale(SybCtG(context), CS_SET, tmp_locale, CS_LC_ALL, NULL, CS_NULLTERM, NULL)!=CS_SUCCEED) {
668 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase: Unable to load default locale data");
669 			} else {
670 				if (cs_locale(SybCtG(context), CS_SET, tmp_locale, CS_SYB_CHARSET, charset, CS_NULLTERM, NULL)!=CS_SUCCEED) {
671 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase: Unable to update character set");
672 				} else {
673 					if (ct_con_props(sybase->connection, CS_SET, CS_LOC_PROP, tmp_locale, CS_UNUSED, NULL)!=CS_SUCCEED) {
674 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase: Unable to update connection properties");
675 					}
676 				}
677 			}
678 		}
679 	}
680 
681 	if (cfg_get_long("sybct.packet_size", &packetsize) == SUCCESS) {
682 		if (ct_con_props(sybase->connection, CS_SET, CS_PACKETSIZE, (CS_VOID *)&packetsize, CS_UNUSED, NULL) != CS_SUCCEED) {
683 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase: Unable to update connection packetsize");
684 		}
685 	}
686 
687 	/* Set the login timeout. Actually, the login timeout is per context
688 	 * and not per connection, but we will update the context here to
689 	 * allow for code such as the following:
690 	 *
691 	 *   ini_set('sybct.login_timeout', $timeout);
692 	 *   sybase_connect(...)
693 	 *
694 	 * Note that preceding calls to sybase_connect() will now use the
695 	 * updated value and not the default one!
696 	 *
697   	 * The default value for CS_LOGIN_TIMEOUT is 60 (1 minute).
698 	 */
699 	if (SybCtG(login_timeout) != -1) {
700 		CS_INT cs_login_timeout = SybCtG(login_timeout);
701 		if (ct_config(SybCtG(context), CS_SET, CS_LOGIN_TIMEOUT, &cs_login_timeout, CS_UNUSED, NULL)!=CS_SUCCEED) {
702 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to update the login timeout");
703 		}
704 	}
705 
706 	sybase->valid = 1;
707 	sybase->dead = 0;
708 	sybase->active_result_index = 0;
709 	sybase->callback_name = NULL;
710 
711 	/* create the link */
712 	if (ct_connect(sybase->connection, host, CS_NULLTERM)!=CS_SUCCEED) {
713 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to connect");
714 		ct_con_drop(sybase->connection);
715 		return 0;
716 	}
717 
718 	if (ct_cmd_alloc(sybase->connection, &sybase->cmd)!=CS_SUCCEED) {
719 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to allocate command record");
720 		ct_close(sybase->connection, CS_UNUSED);
721 		ct_con_drop(sybase->connection);
722 		return 0;
723 	}
724 
725 	return 1;
726 }
727 
728 
php_sybase_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent)729 static void php_sybase_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
730 {
731 	char *user = NULL, *passwd = NULL, *host = NULL, *charset = NULL, *appname = NULL;
732 	char *hashed_details;
733 	int hashed_details_length, len;
734 	zend_bool new = 0;
735 	sybase_link *sybase_ptr;
736 
737 	host= user= passwd= charset= appname= NULL;
738 	if (persistent) {
739 		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!s!s!s!", &host, &len, &user, &len, &passwd, &len, &charset, &len, &appname, &len) == FAILURE) {
740 			return;
741 		}
742 	} else {
743 		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!s!s!s!b", &host, &len, &user, &len, &passwd, &len, &charset, &len, &appname, &len, &new) == FAILURE) {
744 			return;
745 		}
746 	}
747 	hashed_details_length = spprintf(
748 		&hashed_details,
749 		0,
750 		"sybase_%s_%s_%s_%s_%s",
751 		host ? host : "",
752 		user ? user : "",
753 		passwd ? passwd : "",
754 		charset ? charset : "",
755 		appname ? appname : ""
756 	);
757 
758 	if (!SybCtG(allow_persistent)) {
759 		persistent=0;
760 	}
761 	if (persistent) {
762 		zend_rsrc_list_entry *le;
763 
764 		/* try to find if we already have this link in our persistent list */
765 		if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_length+1, (void **) &le)==FAILURE) {  /* we don't */
766 			zend_rsrc_list_entry new_le;
767 
768 			if (SybCtG(max_links)!=-1 && SybCtG(num_links)>=SybCtG(max_links)) {
769 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Too many open links (%ld)", SybCtG(num_links));
770 				efree(hashed_details);
771 				RETURN_FALSE;
772 			}
773 			if (SybCtG(max_persistent)!=-1 && SybCtG(num_persistent)>=SybCtG(max_persistent)) {
774 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Too many open persistent links (%ld)", SybCtG(num_persistent));
775 				efree(hashed_details);
776 				RETURN_FALSE;
777 			}
778 
779 			sybase_ptr = (sybase_link *) malloc(sizeof(sybase_link));
780 			if (!sybase_ptr) {
781 				efree(hashed_details);
782 				RETURN_FALSE;
783 			}
784 			if (!php_sybase_do_connect_internal(sybase_ptr, host, user, passwd, charset, appname TSRMLS_CC)) {
785 				free(sybase_ptr);
786 				efree(hashed_details);
787 				RETURN_FALSE;
788 			}
789 
790 			/* hash it up */
791 			Z_TYPE(new_le) = le_plink;
792 			new_le.ptr = sybase_ptr;
793 			if (zend_hash_update(&EG(persistent_list), hashed_details, hashed_details_length+1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL)==FAILURE) {
794 				ct_close(sybase_ptr->connection, CS_UNUSED);
795 				ct_con_drop(sybase_ptr->connection);
796 				free(sybase_ptr);
797 				efree(hashed_details);
798 				RETURN_FALSE;
799 			}
800 			SybCtG(num_persistent)++;
801 			SybCtG(num_links)++;
802 		} else {  /* we do */
803 			CS_INT con_status;
804 
805 			if (Z_TYPE_P(le) != le_plink) {
806 				efree(hashed_details);
807 				RETURN_FALSE;
808 			}
809 
810 			sybase_ptr = (sybase_link *) le->ptr;
811 
812 			/* If the link has died, close it and overwrite it with a new one. */
813 
814 			if (ct_con_props(sybase_ptr->connection, CS_GET, CS_CON_STATUS,
815 							 &con_status, CS_UNUSED, NULL)!=CS_SUCCEED) {
816 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Unable to get connection status");
817 				efree(hashed_details);
818 				RETURN_FALSE;
819 			}
820 			if (!(con_status & CS_CONSTAT_CONNECTED) || (con_status & CS_CONSTAT_DEAD) || sybase_ptr->dead) {
821 				sybase_link sybase;
822 
823 				if (con_status & CS_CONSTAT_CONNECTED) {
824 					ct_close(sybase_ptr->connection, CS_FORCE_CLOSE);
825 				}
826 				/* Create a new connection, then replace the old
827 				 * connection.  If we fail to create a new connection,
828 				 * put the old one back so there will be a connection,
829 				 * even if it is a non-functional one.  This is because
830 				 * code may still be holding an id for this connection
831 				 * so we can't free the CS_CONNECTION.
832 				 * (This is actually totally hokey, it would be better
833 				 * to just ct_con_drop() the connection and set
834 				 * sybase_ptr->connection to NULL, then test it for
835 				 * NULL before trying to use it elsewhere . . .)
836 				 */
837 				memcpy(&sybase, sybase_ptr, sizeof(sybase_link));
838 				if (!php_sybase_do_connect_internal(sybase_ptr, host, user, passwd, charset, appname TSRMLS_CC)) {
839 					memcpy(sybase_ptr, &sybase, sizeof(sybase_link));
840 					efree(hashed_details);
841 					RETURN_FALSE;
842 				}
843 				ct_con_drop(sybase.connection); /* drop old connection */
844 			}
845 		}
846 		ZEND_REGISTER_RESOURCE(return_value, sybase_ptr, le_plink);
847 	} else { /* non persistent */
848 		zend_rsrc_list_entry *index_ptr, new_index_ptr;
849 
850 		/* first we check the hash for the hashed_details key.  if it exists,
851 		 * it should point us to the right offset where the actual sybase link sits.
852 		 * if it doesn't, open a new sybase link, add it to the resource list,
853 		 * and add a pointer to it with hashed_details as the key.
854 		 */
855 		if (!new && zend_hash_find(&EG(regular_list), hashed_details, hashed_details_length+1, (void **) &index_ptr)==SUCCESS) {
856 			int type, link;
857 			void *ptr;
858 
859 			if (Z_TYPE_P(index_ptr) != le_index_ptr) {
860 				efree(hashed_details);
861 				RETURN_FALSE;
862 			}
863 			link = (int) index_ptr->ptr;
864 			ptr = zend_list_find(link, &type);   /* check if the link is still there */
865 			if (ptr && (type==le_link || type==le_plink)) {
866 				zend_list_addref(link);
867 				Z_LVAL_P(return_value) = SybCtG(default_link) = link;
868 				Z_TYPE_P(return_value) = IS_RESOURCE;
869 				efree(hashed_details);
870 				return;
871 			} else {
872 				zend_hash_del(&EG(regular_list), hashed_details, hashed_details_length+1);
873 			}
874 		}
875 		if (SybCtG(max_links)!=-1 && SybCtG(num_links)>=SybCtG(max_links)) {
876 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Too many open links (%ld)", SybCtG(num_links));
877 			efree(hashed_details);
878 			RETURN_FALSE;
879 		}
880 
881 		sybase_ptr = (sybase_link *) emalloc(sizeof(sybase_link));
882 		if (!php_sybase_do_connect_internal(sybase_ptr, host, user, passwd, charset, appname TSRMLS_CC)) {
883 			efree(sybase_ptr);
884 			efree(hashed_details);
885 			RETURN_FALSE;
886 		}
887 
888 		/* add it to the list */
889 		ZEND_REGISTER_RESOURCE(return_value, sybase_ptr, le_link);
890 
891 		/* add it to the hash */
892 		new_index_ptr.ptr = (void *) Z_LVAL_P(return_value);
893 		Z_TYPE(new_index_ptr) = le_index_ptr;
894 		if (zend_hash_update(&EG(regular_list), hashed_details, hashed_details_length+1, (void *) &new_index_ptr, sizeof(zend_rsrc_list_entry), NULL)==FAILURE) {
895 			ct_close(sybase_ptr->connection, CS_UNUSED);
896 			ct_con_drop(sybase_ptr->connection);
897 			efree(sybase_ptr);
898 			efree(hashed_details);
899 			RETURN_FALSE;
900 		}
901 		SybCtG(num_links)++;
902 	}
903 	efree(hashed_details);
904 	SybCtG(default_link)=Z_LVAL_P(return_value);
905 	zend_list_addref(SybCtG(default_link));
906 }
907 
908 
php_sybase_get_default_link(INTERNAL_FUNCTION_PARAMETERS)909 static int php_sybase_get_default_link(INTERNAL_FUNCTION_PARAMETERS)
910 {
911 	if (SybCtG(default_link)==-1) { /* no link opened yet, implicitly open one */
912 		ht = 0;
913 		php_sybase_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
914 	}
915 	return SybCtG(default_link);
916 }
917 
918 
919 /* {{{ proto int sybase_connect([string host [, string user [, string password [, string charset [, string appname [, bool new]]]]]])
920    Open Sybase server connection */
PHP_FUNCTION(sybase_connect)921 PHP_FUNCTION(sybase_connect)
922 {
923 	php_sybase_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
924 }
925 
926 /* }}} */
927 
928 /* {{{ proto int sybase_pconnect([string host [, string user [, string password [, string charset [, string appname]]]]])
929    Open persistent Sybase connection */
PHP_FUNCTION(sybase_pconnect)930 PHP_FUNCTION(sybase_pconnect)
931 {
932 	php_sybase_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
933 }
934 
935 /* }}} */
936 
php_sybase_connection_id(zval * sybase_link_index,int * id TSRMLS_DC)937 inline static int php_sybase_connection_id(zval *sybase_link_index, int *id TSRMLS_DC)
938 {
939 	if (NULL == sybase_link_index) {
940 		if (-1 == SybCtG(default_link)) {
941 			return FAILURE;
942 		}
943 		*id = SybCtG(default_link);
944 	} else {
945 		*id = -1;   /* explicit resource number */
946 	}
947 	return SUCCESS;
948 }
949 
950 /* {{{ proto bool sybase_close([resource link_id])
951    Close Sybase connection */
PHP_FUNCTION(sybase_close)952 PHP_FUNCTION(sybase_close)
953 {
954 	zval *sybase_link_index = NULL;
955 	sybase_link *sybase_ptr;
956 	int id;
957 
958 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &sybase_link_index) == FAILURE) {
959 		return;
960 	}
961 
962 	if (php_sybase_connection_id(sybase_link_index, &id TSRMLS_CC) == FAILURE) {
963 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  No connection to close");
964 		RETURN_FALSE;
965 	}
966 
967 	ZEND_FETCH_RESOURCE2(sybase_ptr, sybase_link *, &sybase_link_index, id, "Sybase-Link", le_link, le_plink);
968 
969 	if (id == -1) {
970 		zend_list_delete(Z_RESVAL_P(sybase_link_index));
971 	}
972 	if (id != -1 || (sybase_link_index && Z_RESVAL_P(sybase_link_index) == SybCtG(default_link))) {
973 		zend_list_delete(SybCtG(default_link));
974 		SybCtG(default_link) = -1;
975 	}
976 
977 	RETURN_TRUE;
978 }
979 
980 /* }}} */
981 
982 
exec_cmd(sybase_link * sybase_ptr,char * cmdbuf)983 static int exec_cmd(sybase_link *sybase_ptr, char *cmdbuf)
984 {
985 	CS_RETCODE retcode;
986 	CS_INT restype;
987 	int failure=0;
988 
989 	/* Fail if we already marked this connection dead. */
990 
991 	if (sybase_ptr->dead) {
992 		return FAILURE;
993 	}
994 
995 	/*
996 	 ** Get a command handle, store the command string in it, and
997 	 ** send it to the server.
998 	 */
999 
1000 	if (ct_command(sybase_ptr->cmd, CS_LANG_CMD, cmdbuf, CS_NULLTERM, CS_UNUSED)!=CS_SUCCEED) {
1001 		sybase_ptr->dead = 1;
1002 		return FAILURE;
1003 	}
1004 	if (ct_send(sybase_ptr->cmd)!=CS_SUCCEED) {
1005 		sybase_ptr->dead = 1;
1006 		return FAILURE;
1007 	}
1008 
1009 	while ((retcode = ct_results(sybase_ptr->cmd, &restype))==CS_SUCCEED) {
1010 		switch ((int) restype) {
1011 			case CS_CMD_SUCCEED:
1012 			case CS_CMD_DONE:
1013 				break;
1014 
1015 			case CS_CMD_FAIL:
1016 				failure=1;
1017 				break;
1018 
1019 			case CS_STATUS_RESULT:
1020 				ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_CURRENT);
1021 				break;
1022 
1023 			default:
1024 				failure=1;
1025 				break;
1026 		}
1027 		if (failure) {
1028 			ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1029 			return FAILURE;
1030 		}
1031 	}
1032 
1033 	switch (retcode) {
1034 		case CS_END_RESULTS:
1035 			return SUCCESS;
1036 			break;
1037 
1038 		case CS_FAIL:
1039 			/* Hopefully this either cleans up the connection, or the
1040 			 * connection ends up marked dead so it will be reopened
1041 			 * if it is persistent.  We may want to do
1042 			 * ct_close(CS_FORCE_CLOSE) if ct_cancel() fails; see the
1043 			 * doc for ct_results()==CS_FAIL.
1044 			 */
1045 			ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1046 			/* Don't take chances with the vagaries of ct-lib.  Mark it
1047 			 * dead ourselves.
1048 			 */
1049 			sybase_ptr->dead = 1;
1050 			return FAILURE;
1051 
1052 		default:
1053 			return FAILURE;
1054 	}
1055 }
1056 
1057 
1058 /* {{{ proto bool sybase_select_db(string database [, resource link_id])
1059    Select Sybase database */
PHP_FUNCTION(sybase_select_db)1060 PHP_FUNCTION(sybase_select_db)
1061 {
1062 	zval *sybase_link_index = NULL;
1063 	char *db, *cmdbuf;
1064 	int id, len;
1065 	sybase_link *sybase_ptr;
1066 
1067 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &db, &len, &sybase_link_index) == FAILURE) {
1068 		return;
1069 	}
1070 
1071 	if (php_sybase_connection_id(sybase_link_index, &id TSRMLS_CC) == FAILURE) {
1072 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  No connection");
1073 		RETURN_FALSE;
1074 	}
1075 
1076 	ZEND_FETCH_RESOURCE2(sybase_ptr, sybase_link *, &sybase_link_index, id, "Sybase-Link", le_link, le_plink);
1077 
1078 	spprintf(&cmdbuf, 4 + len + 1, "use %s", db);
1079 	if (exec_cmd(sybase_ptr, cmdbuf) == FAILURE) {
1080 		efree(cmdbuf);
1081 		RETURN_FALSE;
1082 	} else {
1083 		efree(cmdbuf);
1084 		RETURN_TRUE;
1085 	}
1086 }
1087 
1088 /* }}} */
1089 
php_sybase_finish_results(sybase_result * result TSRMLS_DC)1090 static int php_sybase_finish_results(sybase_result *result TSRMLS_DC)
1091 {
1092 	int i, fail;
1093 	CS_RETCODE retcode;
1094 	CS_INT restype;
1095 
1096 	efree_n(result->datafmt);
1097 	efree_n(result->lengths);
1098 	efree_n(result->indicators);
1099 	efree_n(result->numerics);
1100 	efree_n(result->types);
1101 	for (i=0; i<result->num_fields; i++) {
1102 		efree(result->tmp_buffer[i]);
1103 	}
1104 	efree_n(result->tmp_buffer);
1105 
1106 	/* Indicate we have read all rows */
1107 	result->sybase_ptr->active_result_index= 0;
1108 
1109 	/* The only restype we should get now is CS_CMD_DONE, possibly
1110 	 * followed by a CS_STATUS_RESULT/CS_CMD_SUCCEED/CS_CMD_DONE
1111 	 * sequence if the command was a stored procedure call.  But we
1112 	 * still need to read and discard unexpected results.  We might
1113 	 * want to return a failure in this case because the application
1114 	 * won't be getting all the results it asked for.
1115 	 */
1116 	fail = 0;
1117 	while ((retcode = ct_results(result->sybase_ptr->cmd, &restype))==CS_SUCCEED) {
1118 		switch ((int) restype) {
1119 			case CS_CMD_SUCCEED:
1120 			case CS_CMD_DONE:
1121 				break;
1122 
1123 			case CS_CMD_FAIL:
1124 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Command failed, cancelling rest");
1125 				ct_cancel(NULL, result->sybase_ptr->cmd, CS_CANCEL_ALL);
1126 				fail = 1;
1127 				break;
1128 
1129 			case CS_COMPUTE_RESULT:
1130 			case CS_CURSOR_RESULT:
1131 			case CS_PARAM_RESULT:
1132 			case CS_ROW_RESULT:
1133 				/* Unexpected results, cancel them. */
1134 				php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Sybase:  Unexpected results, cancelling current");
1135 				ct_cancel(NULL, result->sybase_ptr->cmd, CS_CANCEL_CURRENT);
1136 				break;
1137 
1138 			case CS_STATUS_RESULT:
1139 				/* Status result from a stored procedure, cancel it but do not tell user */
1140 				ct_cancel(NULL, result->sybase_ptr->cmd, CS_CANCEL_CURRENT);
1141 				break;
1142 
1143 			default:
1144 				php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Sybase:  Unexpected results, cancelling all");
1145 				ct_cancel(NULL, result->sybase_ptr->cmd, CS_CANCEL_ALL);
1146 				break;
1147 		}
1148 
1149 		if (fail) {
1150 			break;
1151 		}
1152 	}
1153 
1154 	switch (retcode) {
1155 		case CS_END_RESULTS:
1156 			/* Normal. */
1157 			break;
1158 
1159 		case CS_FAIL:
1160 			/* Hopefully this either cleans up the connection, or the
1161 			 * connection ends up marked dead so it will be reopened
1162 			 * if it is persistent.  We may want to do
1163 			 * ct_close(CS_FORCE_CLOSE) if ct_cancel() fails; see the
1164 			 * doc for ct_results()==CS_FAIL.
1165 			 */
1166 			ct_cancel(NULL, result->sybase_ptr->cmd, CS_CANCEL_ALL);
1167 			/* Don't take chances with the vagaries of ct-lib.  Mark it
1168 			 * dead ourselves.
1169 			 */
1170 			result->sybase_ptr->dead = 1;
1171 
1172 		case CS_CANCELED:
1173 		default:
1174 			retcode = CS_FAIL;
1175 			break;
1176 	}
1177 
1178 	return retcode;
1179 }
1180 
1181 #define RETURN_DOUBLE_VAL(result, buf, length)          \
1182 	if ((length - 1) <= EG(precision)) {                \
1183 		errno = 0;                                      \
1184 		Z_DVAL(result) = zend_strtod(buf, NULL);        \
1185 		if (errno != ERANGE) {                          \
1186 			Z_TYPE(result) = IS_DOUBLE;                 \
1187 		} else {                                        \
1188 			ZVAL_STRINGL(&result, buf, length- 1, 1);   \
1189 		}                                               \
1190 	} else {                                            \
1191 		ZVAL_STRINGL(&result, buf, length- 1, 1);       \
1192 	}
1193 
php_sybase_fetch_result_row(sybase_result * result,int numrows)1194 static int php_sybase_fetch_result_row (sybase_result *result, int numrows)
1195 {
1196 	int i, j;
1197 	CS_INT retcode;
1198 	TSRMLS_FETCH();
1199 
1200 	/* We've already fetched everything */
1201 	if (result->last_retcode == CS_END_DATA || result->last_retcode == CS_END_RESULTS) {
1202 		return result->last_retcode;
1203 	}
1204 
1205 	if (numrows!=-1) numrows+= result->num_rows;
1206 	while ((retcode=ct_fetch(result->sybase_ptr->cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED, NULL))==CS_SUCCEED || retcode == CS_ROW_FAIL) {
1207 		result->num_rows++;
1208 		i= result->store ? result->num_rows- 1 : 0;
1209 		if (i >= result->blocks_initialized*SYBASE_ROWS_BLOCK) {
1210 			result->data = (zval **) safe_erealloc(result->data, SYBASE_ROWS_BLOCK*(++result->blocks_initialized), sizeof(zval *), 0);
1211 		}
1212 		if (result->store || 1 == result->num_rows) {
1213 			result->data[i] = (zval *) safe_emalloc(sizeof(zval), result->num_fields, 0);
1214 		}
1215 
1216 		for (j = 0; j < result->num_fields; j++) {
1217 
1218 			/* If we are in non-storing mode, free the previous result */
1219 			if (!result->store && result->num_rows > 1 && Z_TYPE(result->data[i][j]) == IS_STRING) {
1220 				efree(Z_STRVAL(result->data[i][j]));
1221 			}
1222 
1223 			if (result->indicators[j] == -1) { /* null value */
1224 				ZVAL_NULL(&result->data[i][j]);
1225 			} else {
1226 				switch (result->numerics[j]) {
1227 					case 1: {
1228 						/* This indicates a long */
1229 						ZVAL_LONG(&result->data[i][j], strtol(result->tmp_buffer[j], NULL, 10));
1230 						break;
1231 					}
1232 
1233 					case 2: {
1234 						/* This indicates a float */
1235 						RETURN_DOUBLE_VAL(result->data[i][j], result->tmp_buffer[j], result->lengths[j]);
1236 						break;
1237 					}
1238 
1239 					case 3: {
1240 						/* This indicates either a long or a float, which ever fits */
1241 						errno = 0;
1242 						Z_LVAL(result->data[i][j]) = strtol(result->tmp_buffer[j], NULL, 10);
1243 						if (errno == ERANGE) {
1244 
1245 							/* An overflow occurred, so try to fit it into a double */
1246 							RETURN_DOUBLE_VAL(result->data[i][j], result->tmp_buffer[j], result->lengths[j]);
1247 							break;
1248 						}
1249 						Z_TYPE(result->data[i][j]) = IS_LONG;
1250 						break;
1251 					}
1252 
1253 					default: {
1254 						/* This indicates anything else, return it as string
1255 						 * FreeTDS doesn't correctly set result->indicators[j] correctly
1256 						 * for NULL fields in some version in conjunction with ASE 12.5
1257 						 * but instead sets result->lengths[j] to 0, which would lead to
1258 						 * a negative memory allocation (and thus a segfault).
1259 						 */
1260 						if (result->lengths[j] < 1) {
1261 							ZVAL_NULL(&result->data[i][j]);
1262 						} else {
1263 							ZVAL_STRINGL(&result->data[i][j], result->tmp_buffer[j], result->lengths[j]- 1, 1);
1264 						}
1265 						break;
1266 					}
1267 				}
1268 			}
1269 		}
1270 		if (numrows!=-1 && result->num_rows>=numrows) break;
1271 	}
1272 
1273 	if (retcode==CS_ROW_FAIL) {
1274 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Error reading row %d", result->num_rows);
1275 		return retcode;
1276 	}
1277 	result->last_retcode= retcode;
1278 	switch (retcode) {
1279 		case CS_END_DATA:
1280 			retcode = php_sybase_finish_results(result TSRMLS_CC);
1281 			break;
1282 
1283 		case CS_ROW_FAIL:
1284 		case CS_SUCCEED:
1285 			break;
1286 
1287 		default:
1288 			FREE_SYBASE_RESULT(result);
1289 			result = NULL;
1290 			retcode = CS_FAIL;		/* Just to be sure */
1291 			break;
1292 	}
1293 
1294 	return retcode;
1295 }
1296 
php_sybase_fetch_result_set(sybase_link * sybase_ptr,int buffered,int store)1297 static sybase_result * php_sybase_fetch_result_set (sybase_link *sybase_ptr, int buffered, int store)
1298 {
1299 	int num_fields;
1300 	sybase_result *result;
1301 	int i, j;
1302 	CS_INT retcode;
1303 
1304 	/* The following (if unbuffered) is more or less the equivalent of mysql_store_result().
1305 	 * fetch all rows from the server into the row buffer, thus:
1306 	 * 1)  Being able to fire up another query without explicitly reading all rows
1307 	 * 2)  Having numrows accessible
1308 	 */
1309 	if (ct_res_info(sybase_ptr->cmd, CS_NUMDATA, &num_fields, CS_UNUSED, NULL)!=CS_SUCCEED) {
1310 		return NULL;
1311 	}
1312 
1313 	result = (sybase_result *) emalloc(sizeof(sybase_result));
1314 	result->data = (zval **) safe_emalloc(sizeof(zval *), SYBASE_ROWS_BLOCK, 0);
1315 	result->fields = NULL;
1316 	result->sybase_ptr = sybase_ptr;
1317 	result->cur_field=result->cur_row=result->num_rows=0;
1318 	result->num_fields = num_fields;
1319 	result->last_retcode = 0;
1320 	result->store= store;
1321 	result->blocks_initialized= 1;
1322 	result->tmp_buffer = (char **) safe_emalloc(sizeof(char *), num_fields, 0);
1323 	result->lengths = (CS_INT *) safe_emalloc(sizeof(CS_INT), num_fields, 0);
1324 	result->indicators = (CS_SMALLINT *) safe_emalloc(sizeof(CS_INT), num_fields, 0);
1325 	result->datafmt = (CS_DATAFMT *) safe_emalloc(sizeof(CS_DATAFMT), num_fields, 0);
1326 	result->numerics = (unsigned char *) safe_emalloc(sizeof(unsigned char), num_fields, 0);
1327 	result->types = (CS_INT *) safe_emalloc(sizeof(CS_INT), num_fields, 0);
1328 
1329 	for (i=0; i<num_fields; i++) {
1330 		ct_describe(sybase_ptr->cmd, i+1, &result->datafmt[i]);
1331 		result->types[i] = result->datafmt[i].datatype;
1332 		switch (result->datafmt[i].datatype) {
1333 			case CS_CHAR_TYPE:
1334 			case CS_VARCHAR_TYPE:
1335 			case CS_TEXT_TYPE:
1336 			case CS_IMAGE_TYPE:
1337 				result->datafmt[i].maxlength++;
1338 				result->numerics[i] = 0;
1339 				break;
1340 			case CS_BINARY_TYPE:
1341 			case CS_VARBINARY_TYPE:
1342 				result->datafmt[i].maxlength *= 2;
1343 				result->datafmt[i].maxlength++;
1344 				result->numerics[i] = 0;
1345 				break;
1346 			case CS_BIT_TYPE:
1347 			case CS_TINYINT_TYPE:
1348 				result->datafmt[i].maxlength = 4;
1349 				result->numerics[i] = 1;
1350 				break;
1351 			case CS_SMALLINT_TYPE:
1352 				result->datafmt[i].maxlength = 7;
1353 				result->numerics[i] = 1;
1354 				break;
1355 			case CS_INT_TYPE:
1356 				result->datafmt[i].maxlength = 12;
1357 				result->numerics[i] = 1;
1358 				break;
1359 			case CS_REAL_TYPE:
1360 			case CS_FLOAT_TYPE:
1361 				result->datafmt[i].maxlength = 24;
1362 				result->numerics[i] = 2;
1363 				break;
1364 			case CS_MONEY_TYPE:
1365 			case CS_MONEY4_TYPE:
1366 				result->datafmt[i].maxlength = 24;
1367 				result->numerics[i] = 2;
1368 				break;
1369 			case CS_DATETIME_TYPE:
1370 			case CS_DATETIME4_TYPE:
1371 				result->datafmt[i].maxlength = 30;
1372 				result->numerics[i] = 0;
1373 				break;
1374 			case CS_NUMERIC_TYPE:
1375 			case CS_DECIMAL_TYPE:
1376 				result->datafmt[i].maxlength = result->datafmt[i].precision + 3;
1377 				/* numeric(10) vs numeric(10, 1) */
1378 				result->numerics[i] = (result->datafmt[i].scale == 0) ? 3 : 2;
1379 				break;
1380 			default:
1381 				result->datafmt[i].maxlength++;
1382 				result->numerics[i] = 0;
1383 				break;
1384 		}
1385 		result->tmp_buffer[i] = (char *)emalloc(result->datafmt[i].maxlength);
1386 		result->datafmt[i].datatype = CS_CHAR_TYPE;
1387 		result->datafmt[i].format = CS_FMT_NULLTERM;
1388 		ct_bind(sybase_ptr->cmd, i+1, &result->datafmt[i], result->tmp_buffer[i], &result->lengths[i], &result->indicators[i]);
1389 	}
1390 
1391 	result->fields = (sybase_field *) safe_emalloc(sizeof(sybase_field), num_fields, 0);
1392 	j=0;
1393 	for (i=0; i<num_fields; i++) {
1394 		char computed_buf[16];
1395 
1396 		if (result->datafmt[i].namelen>0) {
1397 			result->fields[i].name = estrndup(result->datafmt[i].name, result->datafmt[i].namelen);
1398 		} else {
1399 			if (j>0) {
1400 				snprintf(computed_buf, 16, "computed%d", j);
1401 			} else {
1402 				strcpy(computed_buf, "computed");
1403 			}
1404 			result->fields[i].name = estrdup(computed_buf);
1405 			j++;
1406 		}
1407 		result->fields[i].column_source = STR_EMPTY_ALLOC();
1408 		result->fields[i].max_length = result->datafmt[i].maxlength-1;
1409 		result->fields[i].numeric = result->numerics[i];
1410 		Z_TYPE(result->fields[i]) = result->types[i];
1411 	}
1412 
1413 	if (buffered) {
1414 		retcode = CS_SUCCEED;
1415 	} else {
1416 		if ((retcode = php_sybase_fetch_result_row(result, -1)) == CS_FAIL) {
1417 			return NULL;
1418 		}
1419 	}
1420 
1421 	result->last_retcode = retcode;
1422 	return result;
1423 }
1424 
php_sybase_query(INTERNAL_FUNCTION_PARAMETERS,int buffered)1425 static void php_sybase_query (INTERNAL_FUNCTION_PARAMETERS, int buffered)
1426 {
1427 	zval *sybase_link_index = NULL;
1428 	zend_bool store = 1;
1429 	char *query;
1430 	int len, id, deadlock_count;
1431 	sybase_link *sybase_ptr;
1432 	sybase_result *result;
1433 	CS_INT restype;
1434 	CS_RETCODE retcode;
1435 	enum {
1436 		Q_RESULT,				/* Success with results. */
1437 		Q_SUCCESS,				/* Success but no results. */
1438 		Q_FAILURE,				/* Failure, no results. */
1439 	} status;
1440 
1441 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|rb", &query, &len, &sybase_link_index, &store) == FAILURE) {
1442 		return;
1443 	}
1444 
1445 	if (!store && !buffered) {
1446 		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Sybase:  Cannot use non-storing mode with buffered queries");
1447 		store = 1;
1448 	}
1449 
1450 	if (php_sybase_connection_id(sybase_link_index, &id TSRMLS_CC) == FAILURE) {
1451 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  No connection");
1452 		RETURN_FALSE;
1453 	}
1454 
1455 	ZEND_FETCH_RESOURCE2(sybase_ptr, sybase_link *, &sybase_link_index, id, "Sybase-Link", le_link, le_plink);
1456 
1457 	/* Fail if we already marked this connection dead. */
1458 	if (sybase_ptr->dead) {
1459 		RETURN_FALSE;
1460 	}
1461 
1462 	/* Check to see if a previous sybase_unbuffered_query has read all rows */
1463 	if (sybase_ptr->active_result_index) {
1464 		zval *tmp = NULL;
1465 
1466 		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Sybase:  Called without first fetching all rows from a previous unbuffered query");
1467 		if (sybase_ptr->cmd) {
1468 			ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1469 		}
1470 
1471 		/* Get the resultset and free it */
1472 		ALLOC_ZVAL(tmp);
1473 		Z_LVAL_P(tmp)= sybase_ptr->active_result_index;
1474 		Z_TYPE_P(tmp)= IS_RESOURCE;
1475 		INIT_PZVAL(tmp);
1476 		ZEND_FETCH_RESOURCE(result, sybase_result *, &tmp, -1, "Sybase result", le_result);
1477 
1478 		if (result) {
1479 			php_sybase_finish_results(result TSRMLS_CC);
1480 		}
1481 
1482 		zval_ptr_dtor(&tmp);
1483 		zend_list_delete(sybase_ptr->active_result_index);
1484 		sybase_ptr->active_result_index= 0;
1485 	}
1486 
1487 	/* Repeat until we don't deadlock. */
1488 	deadlock_count= 0;
1489 	for (;;) {
1490 		result = NULL;
1491 		sybase_ptr->deadlock = 0;
1492 		sybase_ptr->affected_rows = 0;
1493 
1494 		/* On Solaris 11.5, ct_command() can be moved outside the
1495 		 * loop, but not on Linux 11.0.
1496 		 */
1497 		if (ct_command(sybase_ptr->cmd, CS_LANG_CMD, query, CS_NULLTERM, CS_UNUSED)!=CS_SUCCEED) {
1498 			/* If this didn't work, the connection is screwed but
1499 			 * ct-lib might not set CS_CONSTAT_DEAD.  So set our own
1500 			 * flag.  This happens sometimes when the database is restarted
1501 			 * and/or its machine is rebooted, and ct_command() returns
1502 			 * CS_BUSY for some reason.
1503 			 */
1504 			sybase_ptr->dead = 1;
1505 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Connection is dead");
1506 			RETURN_FALSE;
1507 		}
1508 
1509 		if (ct_send(sybase_ptr->cmd)!=CS_SUCCEED) {
1510 			ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1511 			sybase_ptr->dead = 1;
1512 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Cannot send command");
1513 			RETURN_FALSE;
1514 		}
1515 
1516 		/* Use the first result set or succeed/fail status and discard the
1517 		 * others.  Applications really shouldn't be making calls that
1518 		 * return multiple result sets, but if they do then we need to
1519 		 * properly read or cancel them or the connection will become
1520 		 * unusable.
1521 		 */
1522 		if (ct_results(sybase_ptr->cmd, &restype)!=CS_SUCCEED) {
1523 			ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1524 			sybase_ptr->dead = 1;
1525 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Cannot read results");
1526 			RETURN_FALSE;
1527 		}
1528 		switch ((int) restype) {
1529 			case CS_CMD_FAIL:
1530 			default:
1531 				status = Q_FAILURE;
1532 				break;
1533 			case CS_CMD_SUCCEED:
1534 			case CS_CMD_DONE: {
1535 					CS_INT row_count;
1536 					if (ct_res_info(sybase_ptr->cmd, CS_ROW_COUNT, &row_count, CS_UNUSED, NULL)==CS_SUCCEED) {
1537 						sybase_ptr->affected_rows = (long)row_count;
1538 					}
1539 				}
1540 				/* Fall through */
1541 			case CS_COMPUTEFMT_RESULT:
1542 			case CS_ROWFMT_RESULT:
1543 			case CS_DESCRIBE_RESULT:
1544 			case CS_MSG_RESULT:
1545 				buffered= 0;				/* These queries have no need for buffering */
1546 				status = Q_SUCCESS;
1547 				break;
1548 			case CS_COMPUTE_RESULT:
1549 			case CS_CURSOR_RESULT:
1550 			case CS_PARAM_RESULT:
1551 			case CS_ROW_RESULT:
1552 			case CS_STATUS_RESULT:
1553 				result = php_sybase_fetch_result_set(sybase_ptr, buffered, store);
1554 				if (result == NULL) {
1555 					ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1556 					RETURN_FALSE;
1557 				}
1558 				status = Q_RESULT;
1559 				break;
1560 		}
1561 
1562 		/* Check for left-over results */
1563 		if (!buffered && status != Q_RESULT) {
1564 			while ((retcode = ct_results(sybase_ptr->cmd, &restype))==CS_SUCCEED) {
1565 				switch ((int) restype) {
1566 					case CS_CMD_SUCCEED:
1567 					case CS_CMD_DONE:
1568 						break;
1569 
1570 					case CS_CMD_FAIL:
1571 						status = Q_FAILURE;
1572 						break;
1573 
1574 					case CS_COMPUTE_RESULT:
1575 					case CS_CURSOR_RESULT:
1576 					case CS_PARAM_RESULT:
1577 					case CS_ROW_RESULT:
1578 						if (status != Q_RESULT) {
1579 							result = php_sybase_fetch_result_set(sybase_ptr, buffered, store);
1580 							if (result == NULL) {
1581 								ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1582 								sybase_ptr->dead = 1;
1583 								RETURN_FALSE;
1584 							}
1585 							status = Q_RESULT;
1586 							retcode = result->last_retcode;
1587 						} else {
1588 							/* Unexpected results, cancel them. */
1589 							ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_CURRENT);
1590 						}
1591 						break;
1592 					case CS_STATUS_RESULT:
1593 						/* Unexpected results, cancel them. */
1594 						ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_CURRENT);
1595 						break;
1596 
1597 					default:
1598 						status = Q_FAILURE;
1599 						break;
1600 				}
1601 				if (status == Q_FAILURE) {
1602 					ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1603 				}
1604 				if (retcode == CS_END_RESULTS) {
1605 					break;
1606 				}
1607 			}
1608 			switch (retcode) {
1609 				case CS_END_RESULTS:
1610 					/* Normal. */
1611 					break;
1612 
1613 				case CS_FAIL:
1614 					/* Hopefully this either cleans up the connection, or the
1615 					 * connection ends up marked dead so it will be reopened
1616 					 * if it is persistent.  We may want to do
1617 					 * ct_close(CS_FORCE_CLOSE) if ct_cancel() fails; see the
1618 					 * doc for ct_results()==CS_FAIL.
1619 					 */
1620 					ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1621 					/* Don't take chances with the vagaries of ct-lib.  Mark it
1622 					 * dead ourselves.
1623 					 */
1624 					sybase_ptr->dead = 1;
1625 				case CS_CANCELED:
1626 				default:
1627 					status = Q_FAILURE;
1628 					break;
1629 			}
1630 		}
1631 
1632 		/* Retry deadlocks up until deadlock_retry_count times */
1633 		if (sybase_ptr->deadlock && SybCtG(deadlock_retry_count) != -1 && ++deadlock_count > SybCtG(deadlock_retry_count)) {
1634 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Retried deadlock %d times [max: %ld], giving up", deadlock_count- 1, SybCtG(deadlock_retry_count));
1635 			FREE_SYBASE_RESULT(result);
1636 			break;
1637 		}
1638 
1639 		/* If query completed without deadlock, break out of the loop.
1640 		 * Sometimes deadlock results in failures and sometimes not,
1641 		 * it seems to depend on the server flavor.  But we want to
1642 		 * retry all deadlocks.
1643 		 */
1644 		if (sybase_ptr->dead || sybase_ptr->deadlock == 0) {
1645 			break;
1646 		}
1647 
1648 		/* Get rid of any results we may have fetched.  This happens:
1649 		 * e.g., our result set may be a stored procedure status which
1650 		 * is returned even if the stored procedure deadlocks.  As an
1651 		 * optimization, we could try not to fetch results in known
1652 		 * deadlock conditions, but deadlock is (should be) rare.
1653 		 */
1654 		FREE_SYBASE_RESULT(result);
1655 	}
1656 
1657 	if (status == Q_SUCCESS) {
1658 		RETURN_TRUE;
1659 	}
1660 
1661 	if (status == Q_FAILURE) {
1662 		FREE_SYBASE_RESULT(result);
1663 		RETURN_FALSE;
1664 	}
1665 
1666 	/* Indicate we have data in case of buffered queries */
1667 	id= ZEND_REGISTER_RESOURCE(return_value, result, le_result);
1668 	sybase_ptr->active_result_index= buffered ? id : 0;
1669 }
1670 
1671 /* {{{ proto int sybase_query(string query [, resource link_id])
1672    Send Sybase query */
PHP_FUNCTION(sybase_query)1673 PHP_FUNCTION(sybase_query)
1674 {
1675 	php_sybase_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1676 }
1677 /* }}} */
1678 
1679 /* {{{ proto int sybase_unbuffered_query(string query [, resource link_id])
1680    Send Sybase query */
PHP_FUNCTION(sybase_unbuffered_query)1681 PHP_FUNCTION(sybase_unbuffered_query)
1682 {
1683 	php_sybase_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1684 }
1685 
1686 /* {{{ proto bool sybase_free_result(resource result)
1687    Free result memory */
PHP_FUNCTION(sybase_free_result)1688 PHP_FUNCTION(sybase_free_result)
1689 {
1690 	zval *sybase_result_index = NULL;
1691 	sybase_result *result;
1692 
1693 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1694 		return;
1695 	}
1696 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1697 
1698 	/* Did we fetch up until the end? */
1699 	if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS) {
1700 		/* php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Cancelling the rest of the results"); */
1701 		ct_cancel(NULL, result->sybase_ptr->cmd, CS_CANCEL_ALL);
1702 		php_sybase_finish_results(result TSRMLS_CC);
1703 	}
1704 
1705 	zend_list_delete(Z_LVAL_P(sybase_result_index));
1706 	RETURN_TRUE;
1707 }
1708 
1709 /* }}} */
1710 
1711 /* {{{ proto string sybase_get_last_message(void)
1712    Returns the last message from server (over min_message_severity) */
PHP_FUNCTION(sybase_get_last_message)1713 PHP_FUNCTION(sybase_get_last_message)
1714 {
1715 	RETURN_STRING(SybCtG(server_message), 1);
1716 }
1717 /* }}} */
1718 
1719 /* {{{ proto int sybase_num_rows(resource result)
1720    Get number of rows in result */
PHP_FUNCTION(sybase_num_rows)1721 PHP_FUNCTION(sybase_num_rows)
1722 {
1723 	zval *sybase_result_index = NULL;
1724 	sybase_result *result;
1725 
1726 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1727 		return;
1728 	}
1729 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1730 
1731 	Z_LVAL_P(return_value) = result->num_rows;
1732 	Z_TYPE_P(return_value) = IS_LONG;
1733 }
1734 
1735 /* }}} */
1736 
1737 /* {{{ proto int sybase_num_fields(resource result)
1738    Get number of fields in result */
PHP_FUNCTION(sybase_num_fields)1739 PHP_FUNCTION(sybase_num_fields)
1740 {
1741 	zval *sybase_result_index = NULL;
1742 	sybase_result *result;
1743 
1744 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1745 		return;
1746 	}
1747 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1748 
1749 	Z_LVAL_P(return_value) = result->num_fields;
1750 	Z_TYPE_P(return_value) = IS_LONG;
1751 }
1752 
1753 /* }}} */
1754 
1755 /* {{{ proto array sybase_fetch_row(resource result)
1756    Get row as enumerated array */
PHP_FUNCTION(sybase_fetch_row)1757 PHP_FUNCTION(sybase_fetch_row)
1758 {
1759 	zval *sybase_result_index = NULL;
1760 	int i;
1761 	sybase_result *result;
1762 	zval *field_content;
1763 
1764 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1765 		return;
1766 	}
1767 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1768 
1769 	/* Unbuffered? */
1770 	if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS) {
1771 		php_sybase_fetch_result_row(result, 1);
1772 	}
1773 
1774 	/* At the end? */
1775 	if (result->cur_row >= result->num_rows) {
1776 		RETURN_FALSE;
1777 	}
1778 
1779 	array_init(return_value);
1780 	for (i=0; i<result->num_fields; i++) {
1781 		ALLOC_ZVAL(field_content);
1782 		*field_content = result->data[result->store ? result->cur_row : 0][i];
1783 		INIT_PZVAL(field_content);
1784 		zval_copy_ctor(field_content);
1785 		zend_hash_index_update(Z_ARRVAL_P(return_value), i, (void *) &field_content, sizeof(zval* ), NULL);
1786 	}
1787 	result->cur_row++;
1788 }
1789 
1790 /* }}} */
1791 
php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS,int numerics)1792 static void php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int numerics)
1793 {
1794 	zval *sybase_result_index = NULL;
1795 	sybase_result *result;
1796 	int i, j;
1797 	zval *tmp;
1798 	char name[32];
1799 
1800 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1801 		return;
1802 	}
1803 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1804 
1805 	/* Unbuffered ? Fetch next row */
1806 	if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS) {
1807 		php_sybase_fetch_result_row(result, 1);
1808 	}
1809 
1810 	/* At the end? */
1811 	if (result->cur_row >= result->num_rows) {
1812 		RETURN_FALSE;
1813 	}
1814 
1815 	array_init(return_value);
1816 
1817 	j= 1;
1818 	for (i=0; i<result->num_fields; i++) {
1819 		ALLOC_ZVAL(tmp);
1820 		*tmp = result->data[result->store ? result->cur_row : 0][i];
1821 		INIT_PZVAL(tmp);
1822 		if (PG(magic_quotes_runtime) && Z_TYPE_P(tmp) == IS_STRING) {
1823 			Z_STRVAL_P(tmp) = php_addslashes(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &Z_STRLEN_P(tmp), 0 TSRMLS_CC);
1824 		} else {
1825 			zval_copy_ctor(tmp);
1826 		}
1827 		if (numerics) {
1828 			zend_hash_index_update(Z_ARRVAL_P(return_value), i, (void *) &tmp, sizeof(zval *), NULL);
1829 			Z_ADDREF_P(tmp);
1830 		}
1831 
1832 		if (zend_hash_exists(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1)) {
1833 			snprintf(name, 32, "%s%d", result->fields[i].name, j);
1834 			result->fields[i].name= estrdup(name);
1835 			j++;
1836 		}
1837 		zend_hash_update(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1, (void *) &tmp, sizeof(zval *), NULL);
1838 	}
1839 	result->cur_row++;
1840 }
1841 
1842 
1843 /* {{{ proto object sybase_fetch_object(resource result [, mixed object])
1844    Fetch row as object */
PHP_FUNCTION(sybase_fetch_object)1845 PHP_FUNCTION(sybase_fetch_object)
1846 {
1847 	zval *object = NULL;
1848 	zval *sybase_result_index = NULL;
1849 	zend_class_entry *ce = NULL;
1850 	sybase_result *result;
1851 
1852 	/* Was a second parameter given? */
1853 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|z", &sybase_result_index, &object) == FAILURE) {
1854 		return;
1855 	}
1856 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1857 
1858 	ce = ZEND_STANDARD_CLASS_DEF_PTR;
1859 	if (NULL != object) {
1860 		switch (Z_TYPE_P(object)) {
1861 			case IS_OBJECT: {
1862 				ce = Z_OBJCE_P(object);
1863 				break;
1864 			}
1865 
1866 			case IS_NULL: {
1867 				/* Use default (ZEND_STANDARD_CLASS_DEF_PTR) */
1868 				break;
1869 			}
1870 
1871 			default: {
1872 				zend_class_entry **pce = NULL;
1873 				convert_to_string(object);
1874 
1875 				if (zend_lookup_class(Z_STRVAL_P(object), Z_STRLEN_P(object), &pce TSRMLS_CC) == FAILURE) {
1876 					php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Sybase:  Class %s has not been declared", Z_STRVAL_P(object));
1877 					/* Use default (ZEND_STANDARD_CLASS_DEF_PTR) */
1878 				} else {
1879 					ce = *pce;
1880 				}
1881 			}
1882 		}
1883 	}
1884 
1885 	/* Reset no. of arguments to 1 so that we can use INTERNAL_FUNCTION_PARAM_PASSTHRU */
1886 	ht= 1;
1887 	php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1888 	if (Z_TYPE_P(return_value) == IS_ARRAY) {
1889 		object_and_properties_init(return_value, ce, Z_ARRVAL_P(return_value));
1890 	}
1891 }
1892 /* }}} */
1893 
1894 /* {{{ proto array sybase_fetch_array(resource result)
1895    Fetch row as array */
PHP_FUNCTION(sybase_fetch_array)1896 PHP_FUNCTION(sybase_fetch_array)
1897 {
1898 	php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1899 }
1900 /* }}} */
1901 
1902 /* {{{ proto array sybase_fetch_assoc(resource result)
1903    Fetch row as array without numberic indices */
PHP_FUNCTION(sybase_fetch_assoc)1904 PHP_FUNCTION(sybase_fetch_assoc)
1905 {
1906 	php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1907 }
1908 /* }}} */
1909 
1910 /* {{{ proto bool sybase_data_seek(resource result, int offset)
1911    Move internal row pointer */
PHP_FUNCTION(sybase_data_seek)1912 PHP_FUNCTION(sybase_data_seek)
1913 {
1914 	zval *sybase_result_index = NULL;
1915 	long offset;
1916 	sybase_result *result;
1917 
1918 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &sybase_result_index, &offset) == FAILURE) {
1919 		return;
1920 	}
1921 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1922 
1923 	/* Unbuffered ? */
1924 	if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS && offset >= result->num_rows) {
1925 		php_sybase_fetch_result_row(result, offset+ 1);
1926 	}
1927 
1928 	if (offset < 0 || offset >= result->num_rows) {
1929 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad row offset %ld, must be betweem 0 and %d", offset, result->num_rows - 1);
1930 		RETURN_FALSE;
1931 	}
1932 
1933 	result->cur_row = offset;
1934 	RETURN_TRUE;
1935 }
1936 /* }}} */
1937 
php_sybase_get_field_name(CS_INT type)1938 static char *php_sybase_get_field_name(CS_INT type)
1939 {
1940 	switch (type) {
1941 		case CS_CHAR_TYPE:
1942 		case CS_VARCHAR_TYPE:
1943 		case CS_TEXT_TYPE:
1944 			return "string";
1945 			break;
1946 		case CS_IMAGE_TYPE:
1947 			return "image";
1948 			break;
1949 		case CS_BINARY_TYPE:
1950 		case CS_VARBINARY_TYPE:
1951 			return "blob";
1952 			break;
1953 		case CS_BIT_TYPE:
1954 			return "bit";
1955 			break;
1956 		case CS_TINYINT_TYPE:
1957 		case CS_SMALLINT_TYPE:
1958 		case CS_INT_TYPE:
1959 			return "int";
1960 			break;
1961 		case CS_REAL_TYPE:
1962 		case CS_FLOAT_TYPE:
1963 		case CS_NUMERIC_TYPE:
1964 		case CS_DECIMAL_TYPE:
1965 			return "real";
1966 			break;
1967 		case CS_MONEY_TYPE:
1968 		case CS_MONEY4_TYPE:
1969 			return "money";
1970 			break;
1971 		case CS_DATETIME_TYPE:
1972 		case CS_DATETIME4_TYPE:
1973 			return "datetime";
1974 			break;
1975 		default:
1976 			return "unknown";
1977 			break;
1978 	}
1979 }
1980 
1981 
1982 /* {{{ proto object sybase_fetch_field(resource result [, int offset])
1983    Get field information */
PHP_FUNCTION(sybase_fetch_field)1984 PHP_FUNCTION(sybase_fetch_field)
1985 {
1986 	zval *sybase_result_index = NULL;
1987 	long field_offset = -1;
1988 	sybase_result *result;
1989 
1990 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &sybase_result_index, &field_offset) == FAILURE) {
1991 		return;
1992 	}
1993 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1994 
1995 	if (field_offset == -1) {
1996 		field_offset = result->cur_field;
1997 		result->cur_field++;
1998 	}
1999 
2000 	if (field_offset < 0 || field_offset >= result->num_fields) {
2001 		if (ZEND_NUM_ARGS() == 2) { /* field specified explicitly */
2002 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad column offset");
2003 		}
2004 		RETURN_FALSE;
2005 	}
2006 
2007 	object_init(return_value);
2008 
2009 	add_property_string(return_value, "name", result->fields[field_offset].name, 1);
2010 	add_property_long(return_value, "max_length", result->fields[field_offset].max_length);
2011 	add_property_string(return_value, "column_source", result->fields[field_offset].column_source, 1);
2012 	add_property_long(return_value, "numeric", result->fields[field_offset].numeric);
2013 	add_property_string(return_value, "type", php_sybase_get_field_name(Z_TYPE(result->fields[field_offset])), 1);
2014 }
2015 /* }}} */
2016 
2017 
2018 /* {{{ proto bool sybase_field_seek(resource result, int offset)
2019    Set field offset */
PHP_FUNCTION(sybase_field_seek)2020 PHP_FUNCTION(sybase_field_seek)
2021 {
2022 	zval *sybase_result_index = NULL;
2023 	long field_offset;
2024 	sybase_result *result;
2025 
2026 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &sybase_result_index, &field_offset) == FAILURE) {
2027 		return;
2028 	}
2029 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
2030 
2031 	if (field_offset < 0 || field_offset >= result->num_fields) {
2032 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad column offset");
2033 		RETURN_FALSE;
2034 	}
2035 
2036 	result->cur_field = field_offset;
2037 	RETURN_TRUE;
2038 }
2039 /* }}} */
2040 
2041 
2042 /* {{{ proto string sybase_result(resource result, int row, mixed field)
2043    Get result data */
PHP_FUNCTION(sybase_result)2044 PHP_FUNCTION(sybase_result)
2045 {
2046 	zval *field;
2047 	zval *sybase_result_index = NULL;
2048 	long row;
2049 	int field_offset = 0;
2050 	sybase_result *result;
2051 
2052 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &sybase_result_index, &row, &field) == FAILURE) {
2053 		return;
2054 	}
2055 	ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
2056 
2057 	/* Unbuffered ? */
2058 	if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS && row >= result->num_rows) {
2059 		php_sybase_fetch_result_row(result, row);
2060 	}
2061 
2062 	if (row < 0 || row >= result->num_rows) {
2063 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad row offset (%ld)", row);
2064 		RETURN_FALSE;
2065 	}
2066 
2067 	switch(Z_TYPE_P(field)) {
2068 		case IS_STRING: {
2069 			int i;
2070 
2071 			for (i = 0; i < result->num_fields; i++) {
2072 				if (strcasecmp(result->fields[i].name, Z_STRVAL_P(field)) == 0) {
2073 					field_offset = i;
2074 					break;
2075 				}
2076 			}
2077 			if (i >= result->num_fields) { /* no match found */
2078 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  %s field not found in result", Z_STRVAL_P(field));
2079 				RETURN_FALSE;
2080 			}
2081 			break;
2082 		}
2083 		default:
2084 			convert_to_long(field);
2085 			field_offset = Z_LVAL_P(field);
2086 			if (field_offset < 0 || field_offset >= result->num_fields) {
2087 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad column offset specified");
2088 				RETURN_FALSE;
2089 			}
2090 			break;
2091 	}
2092 
2093 	*return_value = result->data[row][field_offset];
2094 	zval_copy_ctor(return_value);
2095 }
2096 /* }}} */
2097 
2098 
2099 /* {{{ proto int sybase_affected_rows([resource link_id])
2100    Get number of affected rows in last query */
PHP_FUNCTION(sybase_affected_rows)2101 PHP_FUNCTION(sybase_affected_rows)
2102 {
2103 	zval *sybase_link_index = NULL;
2104 	sybase_link *sybase_ptr;
2105 	int id;
2106 
2107 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &sybase_link_index) == FAILURE) {
2108 		return;
2109 	}
2110 
2111 	if (php_sybase_connection_id(sybase_link_index, &id TSRMLS_CC) == FAILURE) {
2112 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  No connection");
2113 		RETURN_FALSE;
2114 	}
2115 
2116 	ZEND_FETCH_RESOURCE2(sybase_ptr, sybase_link *, &sybase_link_index, id, "Sybase-Link", le_link, le_plink);
2117 
2118 	Z_LVAL_P(return_value) = sybase_ptr->affected_rows;
2119 	Z_TYPE_P(return_value) = IS_LONG;
2120 }
2121 /* }}} */
2122 
2123 
PHP_MINFO_FUNCTION(sybase)2124 PHP_MINFO_FUNCTION(sybase)
2125 {
2126 	char buf[32];
2127 
2128 	php_info_print_table_start();
2129 	php_info_print_table_header(2, "Sybase_CT Support", "enabled" );
2130 	snprintf(buf, sizeof(buf), "%ld", SybCtG(num_persistent));
2131 	php_info_print_table_row(2, "Active Persistent Links", buf);
2132 	snprintf(buf, sizeof(buf), "%ld", SybCtG(num_links));
2133 	php_info_print_table_row(2, "Active Links", buf);
2134 	snprintf(buf, sizeof(buf), "%ld", SybCtG(min_server_severity));
2135 	php_info_print_table_row(2, "Min server severity", buf);
2136 	snprintf(buf, sizeof(buf), "%ld", SybCtG(min_client_severity));
2137 	php_info_print_table_row(2, "Min client severity", buf);
2138 	php_info_print_table_row(2, "Application Name", SybCtG(appname));
2139 	snprintf(buf, sizeof(buf), "%ld", SybCtG(deadlock_retry_count));
2140 	php_info_print_table_row(2, "Deadlock retry count", buf);
2141 	php_info_print_table_end();
2142 
2143 	DISPLAY_INI_ENTRIES();
2144 }
2145 
2146 
2147 /* {{{ proto void sybase_min_client_severity(int severity)
2148    Sets minimum client severity */
PHP_FUNCTION(sybase_min_client_severity)2149 PHP_FUNCTION(sybase_min_client_severity)
2150 {
2151 	long severity;
2152 
2153 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &severity) == FAILURE) {
2154 		return;
2155 	}
2156 
2157 	SybCtG(min_client_severity) = severity;
2158 }
2159 /* }}} */
2160 
2161 
2162 /* {{{ proto void sybase_min_server_severity(int severity)
2163    Sets minimum server severity */
PHP_FUNCTION(sybase_min_server_severity)2164 PHP_FUNCTION(sybase_min_server_severity)
2165 {
2166 	long severity;
2167 
2168 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &severity) == FAILURE) {
2169 		return;
2170 	}
2171 
2172 	SybCtG(min_server_severity) = severity;
2173 }
2174 /* }}} */
2175 
2176 /* {{{ proto void sybase_deadlock_retry_count(int retry_count)
2177    Sets deadlock retry count */
PHP_FUNCTION(sybase_deadlock_retry_count)2178 PHP_FUNCTION(sybase_deadlock_retry_count)
2179 {
2180 	long retry_count;
2181 
2182 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &retry_count) == FAILURE) {
2183 		return;
2184 	}
2185 
2186 	SybCtG(deadlock_retry_count) = retry_count;
2187 }
2188 /* }}} */
2189 
2190 
2191 /* {{{ proto bool sybase_set_message_handler(mixed error_func [, resource connection])
2192    Set the error handler, to be called when a server message is raised.
2193    If error_func is NULL the handler will be deleted */
PHP_FUNCTION(sybase_set_message_handler)2194 PHP_FUNCTION(sybase_set_message_handler)
2195 {
2196 	zend_fcall_info fci = empty_fcall_info;
2197 	zend_fcall_info_cache cache = empty_fcall_info_cache;
2198 	zval *sybase_link_index= NULL;
2199 	sybase_link *sybase_ptr;
2200 	zval **callback;
2201 	int id;
2202 
2203 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!|r", &fci, &cache, &sybase_link_index) == FAILURE) {
2204 		return;
2205 	}
2206 
2207 	if (php_sybase_connection_id(sybase_link_index, &id TSRMLS_CC) == FAILURE) {
2208 
2209 		/* Doesn't matter if we're not connected yet, use default */
2210 		callback= &SybCtG(callback_name);
2211 	} else if (-1 == id) {
2212 
2213 		/* Connection-based message handler */
2214 		ZEND_FETCH_RESOURCE2(sybase_ptr, sybase_link *, &sybase_link_index, id, "Sybase-Link", le_link, le_plink);
2215 		callback= &sybase_ptr->callback_name;
2216 	} else {
2217 
2218 		/* Default message handler */
2219 		callback= &SybCtG(callback_name);
2220 	}
2221 
2222 	/* Clean old callback */
2223 	if (*callback) {
2224 		zval_ptr_dtor(callback);
2225 		*callback = NULL;
2226 	}
2227 
2228 	if (ZEND_FCI_INITIALIZED(fci)) {
2229 		ALLOC_ZVAL(*callback);
2230 		**callback = *fci.function_name;
2231 		INIT_PZVAL(*callback);
2232 		zval_copy_ctor(*callback);
2233 	} else {
2234 		callback= NULL;
2235 	}
2236 
2237 	RETURN_TRUE;
2238 }
2239 /* }}} */
2240 
2241 
2242 #endif
2243 
2244 /*
2245  * Local variables:
2246  * tab-width: 4
2247  * c-basic-offset: 4
2248  * End:
2249  */
2250