1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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 */
36static int le_link, le_plink, le_result;
37
38#if HAVE_SYBASE_CT
39
40ZEND_DECLARE_MODULE_GLOBALS(sybase)
41static PHP_GINIT_FUNCTION(sybase);
42static PHP_GSHUTDOWN_FUNCTION(sybase);
43
44/* {{{ arginfo */
45ZEND_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)
52ZEND_END_ARG_INFO()
53
54ZEND_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)
60ZEND_END_ARG_INFO()
61
62ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_close, 0, 0, 0)
63    ZEND_ARG_INFO(0, link_id)
64ZEND_END_ARG_INFO()
65
66ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_select_db, 0, 0, 1)
67    ZEND_ARG_INFO(0, database)
68    ZEND_ARG_INFO(0, link_id)
69ZEND_END_ARG_INFO()
70
71ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_query, 0, 0, 1)
72    ZEND_ARG_INFO(0, query)
73    ZEND_ARG_INFO(0, link_id)
74ZEND_END_ARG_INFO()
75
76ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_unbuffered_query, 0, 0, 1)
77    ZEND_ARG_INFO(0, query)
78    ZEND_ARG_INFO(0, link_id)
79ZEND_END_ARG_INFO()
80
81ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_free_result, 0, 0, 1)
82    ZEND_ARG_INFO(0, result)
83ZEND_END_ARG_INFO()
84
85ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_get_last_message, 0, 0, 1)
86    ZEND_ARG_INFO(0, d)
87ZEND_END_ARG_INFO()
88
89ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_num_rows, 0, 0, 1)
90    ZEND_ARG_INFO(0, result)
91ZEND_END_ARG_INFO()
92
93ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_num_fields, 0, 0, 1)
94    ZEND_ARG_INFO(0, result)
95ZEND_END_ARG_INFO()
96
97ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_row, 0, 0, 1)
98    ZEND_ARG_INFO(0, result)
99ZEND_END_ARG_INFO()
100
101ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_object, 0, 0, 1)
102    ZEND_ARG_INFO(0, result)
103    ZEND_ARG_INFO(0, object)
104ZEND_END_ARG_INFO()
105
106ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_array, 0, 0, 1)
107    ZEND_ARG_INFO(0, result)
108ZEND_END_ARG_INFO()
109
110ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_assoc, 0, 0, 1)
111    ZEND_ARG_INFO(0, result)
112ZEND_END_ARG_INFO()
113
114ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_data_seek, 0, 0, 2)
115    ZEND_ARG_INFO(0, result)
116    ZEND_ARG_INFO(0, offset)
117ZEND_END_ARG_INFO()
118
119ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_fetch_field, 0, 0, 1)
120    ZEND_ARG_INFO(0, result)
121    ZEND_ARG_INFO(0, offset)
122ZEND_END_ARG_INFO()
123
124ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_field_seek, 0, 0, 2)
125    ZEND_ARG_INFO(0, result)
126    ZEND_ARG_INFO(0, offset)
127ZEND_END_ARG_INFO()
128
129ZEND_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)
133ZEND_END_ARG_INFO()
134
135ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_affected_rows, 0, 0, 0)
136    ZEND_ARG_INFO(0, link_id)
137ZEND_END_ARG_INFO()
138
139ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_min_client_severity, 0, 0, 1)
140    ZEND_ARG_INFO(0, severity)
141ZEND_END_ARG_INFO()
142
143ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_min_server_severity, 0, 0, 1)
144    ZEND_ARG_INFO(0, severity)
145ZEND_END_ARG_INFO()
146
147ZEND_BEGIN_ARG_INFO_EX(arginfo_sybase_deadlock_retry_count, 0, 0, 1)
148    ZEND_ARG_INFO(0, retry_count)
149ZEND_END_ARG_INFO()
150
151ZEND_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)
154ZEND_END_ARG_INFO()
155/* }}} */
156
157const 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
210zend_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
230ZEND_GET_MODULE(sybase)
231#endif
232
233ZEND_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
238static 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
268static 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            zend_string_free(result->fields[i].name);
285            zend_string_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 */
307static int php_sybase_finish_results (sybase_result *result TSRMLS_DC);
308
309static 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
324static 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
359static 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
386static 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    zend_string_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
411static 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        use_copy = zend_make_printable_zval(callback_name, &expr_copy TSRMLS_CC);
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
466static 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    zend_string_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
512PHP_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)
521PHP_INI_END()
522
523
524static 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
574static PHP_GSHUTDOWN_FUNCTION(sybase)
575{
576    ct_exit(sybase_globals->context, CS_UNUSED);
577    cs_ctx_drop(sybase_globals->context);
578}
579
580PHP_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
593PHP_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
604PHP_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
615PHP_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    zend_string_free(SybCtG(server_message));
624    SybCtG(server_message) = NULL;
625    return SUCCESS;
626}
627
628
629static 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
729static 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
909static 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 */
921PHP_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 */
930PHP_FUNCTION(sybase_pconnect)
931{
932    php_sybase_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
933}
934
935/* }}} */
936
937inline 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 */
952PHP_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
983static 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 */
1060PHP_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
1090static 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, canceling 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, canceling 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, canceling 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
1194static int php_sybase_fetch_result_row(sybase_result *result, int numrows TSRMLS_DC)
1195{
1196    int i, j;
1197    CS_INT retcode;
1198
1199    /* We've already fetched everything */
1200    if (result->last_retcode == CS_END_DATA || result->last_retcode == CS_END_RESULTS) {
1201        return result->last_retcode;
1202    }
1203
1204    if (numrows!=-1) numrows+= result->num_rows;
1205    while ((retcode=ct_fetch(result->sybase_ptr->cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED, NULL))==CS_SUCCEED || retcode == CS_ROW_FAIL) {
1206        result->num_rows++;
1207        i= result->store ? result->num_rows- 1 : 0;
1208        if (i >= result->blocks_initialized*SYBASE_ROWS_BLOCK) {
1209            result->data = (zval **) safe_erealloc(result->data, SYBASE_ROWS_BLOCK*(++result->blocks_initialized), sizeof(zval *), 0);
1210        }
1211        if (result->store || 1 == result->num_rows) {
1212            result->data[i] = (zval *) safe_emalloc(sizeof(zval), result->num_fields, 0);
1213        }
1214
1215        for (j = 0; j < result->num_fields; j++) {
1216
1217            /* If we are in non-storing mode, free the previous result */
1218            if (!result->store && result->num_rows > 1 && Z_TYPE(result->data[i][j]) == IS_STRING) {
1219                efree(Z_STRVAL(result->data[i][j]));
1220            }
1221
1222            if (result->indicators[j] == -1) { /* null value */
1223                ZVAL_NULL(&result->data[i][j]);
1224            } else {
1225                switch (result->numerics[j]) {
1226                    case 1: {
1227                        /* This indicates a long */
1228                        ZVAL_LONG(&result->data[i][j], strtol(result->tmp_buffer[j], NULL, 10));
1229                        break;
1230                    }
1231
1232                    case 2: {
1233                        /* This indicates a float */
1234                        RETURN_DOUBLE_VAL(result->data[i][j], result->tmp_buffer[j], result->lengths[j]);
1235                        break;
1236                    }
1237
1238                    case 3: {
1239                        /* This indicates either a long or a float, which ever fits */
1240                        errno = 0;
1241                        Z_LVAL(result->data[i][j]) = strtol(result->tmp_buffer[j], NULL, 10);
1242                        if (errno == ERANGE) {
1243
1244                            /* An overflow occurred, so try to fit it into a double */
1245                            RETURN_DOUBLE_VAL(result->data[i][j], result->tmp_buffer[j], result->lengths[j]);
1246                            break;
1247                        }
1248                        Z_TYPE(result->data[i][j]) = IS_LONG;
1249                        break;
1250                    }
1251
1252                    default: {
1253                        /* This indicates anything else, return it as string
1254                         * FreeTDS doesn't correctly set result->indicators[j] correctly
1255                         * for NULL fields in some version in conjunction with ASE 12.5
1256                         * but instead sets result->lengths[j] to 0, which would lead to
1257                         * a negative memory allocation (and thus a segfault).
1258                         */
1259                        if (result->lengths[j] < 1) {
1260                            ZVAL_NULL(&result->data[i][j]);
1261                        } else {
1262                            ZVAL_STRINGL(&result->data[i][j], result->tmp_buffer[j], result->lengths[j]- 1, 1);
1263                        }
1264                        break;
1265                    }
1266                }
1267            }
1268        }
1269        if (numrows!=-1 && result->num_rows>=numrows) break;
1270    }
1271
1272    if (retcode==CS_ROW_FAIL) {
1273        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Error reading row %d", result->num_rows);
1274        return retcode;
1275    }
1276    result->last_retcode= retcode;
1277    switch (retcode) {
1278        case CS_END_DATA:
1279            retcode = php_sybase_finish_results(result TSRMLS_CC);
1280            break;
1281
1282        case CS_ROW_FAIL:
1283        case CS_SUCCEED:
1284            break;
1285
1286        default:
1287            FREE_SYBASE_RESULT(result);
1288            result = NULL;
1289            retcode = CS_FAIL;      /* Just to be sure */
1290            break;
1291    }
1292
1293    return retcode;
1294}
1295
1296static sybase_result * php_sybase_fetch_result_set(sybase_link *sybase_ptr, int buffered, int store TSRMLS_DC)
1297{
1298    int num_fields;
1299    sybase_result *result;
1300    int i, j;
1301    CS_INT retcode;
1302
1303    /* The following (if unbuffered) is more or less the equivalent of mysql_store_result().
1304     * fetch all rows from the server into the row buffer, thus:
1305     * 1)  Being able to fire up another query without explicitly reading all rows
1306     * 2)  Having numrows accessible
1307     */
1308    if (ct_res_info(sybase_ptr->cmd, CS_NUMDATA, &num_fields, CS_UNUSED, NULL)!=CS_SUCCEED) {
1309        return NULL;
1310    }
1311
1312    result = (sybase_result *) emalloc(sizeof(sybase_result));
1313    result->data = (zval **) safe_emalloc(sizeof(zval *), SYBASE_ROWS_BLOCK, 0);
1314    result->fields = NULL;
1315    result->sybase_ptr = sybase_ptr;
1316    result->cur_field=result->cur_row=result->num_rows=0;
1317    result->num_fields = num_fields;
1318    result->last_retcode = 0;
1319    result->store= store;
1320    result->blocks_initialized= 1;
1321    result->tmp_buffer = (char **) safe_emalloc(sizeof(char *), num_fields, 0);
1322    result->lengths = (CS_INT *) safe_emalloc(sizeof(CS_INT), num_fields, 0);
1323    result->indicators = (CS_SMALLINT *) safe_emalloc(sizeof(CS_INT), num_fields, 0);
1324    result->datafmt = (CS_DATAFMT *) safe_emalloc(sizeof(CS_DATAFMT), num_fields, 0);
1325    result->numerics = (unsigned char *) safe_emalloc(sizeof(unsigned char), num_fields, 0);
1326    result->types = (CS_INT *) safe_emalloc(sizeof(CS_INT), num_fields, 0);
1327
1328    for (i=0; i<num_fields; i++) {
1329        ct_describe(sybase_ptr->cmd, i+1, &result->datafmt[i]);
1330        result->types[i] = result->datafmt[i].datatype;
1331        switch (result->datafmt[i].datatype) {
1332            case CS_CHAR_TYPE:
1333            case CS_VARCHAR_TYPE:
1334            case CS_TEXT_TYPE:
1335            case CS_IMAGE_TYPE:
1336                result->datafmt[i].maxlength++;
1337                result->numerics[i] = 0;
1338                break;
1339            case CS_BINARY_TYPE:
1340            case CS_VARBINARY_TYPE:
1341                result->datafmt[i].maxlength *= 2;
1342                result->datafmt[i].maxlength++;
1343                result->numerics[i] = 0;
1344                break;
1345            case CS_BIT_TYPE:
1346            case CS_TINYINT_TYPE:
1347                result->datafmt[i].maxlength = 4;
1348                result->numerics[i] = 1;
1349                break;
1350            case CS_SMALLINT_TYPE:
1351                result->datafmt[i].maxlength = 7;
1352                result->numerics[i] = 1;
1353                break;
1354            case CS_INT_TYPE:
1355                result->datafmt[i].maxlength = 12;
1356                result->numerics[i] = 1;
1357                break;
1358            case CS_REAL_TYPE:
1359            case CS_FLOAT_TYPE:
1360                result->datafmt[i].maxlength = 24;
1361                result->numerics[i] = 2;
1362                break;
1363            case CS_MONEY_TYPE:
1364            case CS_MONEY4_TYPE:
1365                result->datafmt[i].maxlength = 24;
1366                result->numerics[i] = 2;
1367                break;
1368            case CS_DATETIME_TYPE:
1369            case CS_DATETIME4_TYPE:
1370                result->datafmt[i].maxlength = 30;
1371                result->numerics[i] = 0;
1372                break;
1373            case CS_NUMERIC_TYPE:
1374            case CS_DECIMAL_TYPE:
1375                result->datafmt[i].maxlength = result->datafmt[i].precision + 3;
1376                /* numeric(10) vs numeric(10, 1) */
1377                result->numerics[i] = (result->datafmt[i].scale == 0) ? 3 : 2;
1378                break;
1379            default:
1380                result->datafmt[i].maxlength++;
1381                result->numerics[i] = 0;
1382                break;
1383        }
1384        result->tmp_buffer[i] = (char *)emalloc(result->datafmt[i].maxlength);
1385        result->datafmt[i].datatype = CS_CHAR_TYPE;
1386        result->datafmt[i].format = CS_FMT_NULLTERM;
1387        ct_bind(sybase_ptr->cmd, i+1, &result->datafmt[i], result->tmp_buffer[i], &result->lengths[i], &result->indicators[i]);
1388    }
1389
1390    result->fields = (sybase_field *) safe_emalloc(sizeof(sybase_field), num_fields, 0);
1391    j=0;
1392    for (i=0; i<num_fields; i++) {
1393        char computed_buf[16];
1394
1395        if (result->datafmt[i].namelen>0) {
1396            result->fields[i].name = estrndup(result->datafmt[i].name, result->datafmt[i].namelen);
1397        } else {
1398            if (j>0) {
1399                snprintf(computed_buf, 16, "computed%d", j);
1400            } else {
1401                strcpy(computed_buf, "computed");
1402            }
1403            result->fields[i].name = estrdup(computed_buf);
1404            j++;
1405        }
1406        result->fields[i].column_source = STR_EMPTY_ALLOC();
1407        result->fields[i].max_length = result->datafmt[i].maxlength-1;
1408        result->fields[i].numeric = result->numerics[i];
1409        Z_TYPE(result->fields[i]) = result->types[i];
1410    }
1411
1412    if (buffered) {
1413        retcode = CS_SUCCEED;
1414    } else {
1415        if ((retcode = php_sybase_fetch_result_row(result, -1 TSRMLS_CC)) == CS_FAIL) {
1416            return NULL;
1417        }
1418    }
1419
1420    result->last_retcode = retcode;
1421    return result;
1422}
1423
1424static void php_sybase_query (INTERNAL_FUNCTION_PARAMETERS, int buffered)
1425{
1426    zval *sybase_link_index = NULL;
1427    zend_bool store = 1;
1428    char *query;
1429    size_t len, id, deadlock_count;
1430    sybase_link *sybase_ptr;
1431    sybase_result *result;
1432    CS_INT restype;
1433    CS_RETCODE retcode;
1434    enum {
1435        Q_RESULT,               /* Success with results. */
1436        Q_SUCCESS,              /* Success but no results. */
1437        Q_FAILURE,              /* Failure, no results. */
1438    } status;
1439
1440    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|rb", &query, &len, &sybase_link_index, &store) == FAILURE) {
1441        return;
1442    }
1443
1444    if (!store && !buffered) {
1445        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Sybase:  Cannot use non-storing mode with buffered queries");
1446        store = 1;
1447    }
1448
1449    if (php_sybase_connection_id(sybase_link_index, &id TSRMLS_CC) == FAILURE) {
1450        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  No connection");
1451        RETURN_FALSE;
1452    }
1453
1454    ZEND_FETCH_RESOURCE2(sybase_ptr, sybase_link *, &sybase_link_index, id, "Sybase-Link", le_link, le_plink);
1455
1456    /* Fail if we already marked this connection dead. */
1457    if (sybase_ptr->dead) {
1458        RETURN_FALSE;
1459    }
1460
1461    /* Check to see if a previous sybase_unbuffered_query has read all rows */
1462    if (sybase_ptr->active_result_index) {
1463        zval *tmp = NULL;
1464
1465        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Sybase:  Called without first fetching all rows from a previous unbuffered query");
1466        if (sybase_ptr->cmd) {
1467            ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1468        }
1469
1470        /* Get the resultset and free it */
1471        ALLOC_ZVAL(tmp);
1472        Z_LVAL_P(tmp)= sybase_ptr->active_result_index;
1473        Z_TYPE_P(tmp)= IS_RESOURCE;
1474        INIT_PZVAL(tmp);
1475        ZEND_FETCH_RESOURCE(result, sybase_result *, &tmp, -1, "Sybase result", le_result);
1476
1477        if (result) {
1478            php_sybase_finish_results(result TSRMLS_CC);
1479        }
1480
1481        zval_ptr_dtor(&tmp);
1482        zend_list_delete(sybase_ptr->active_result_index);
1483        sybase_ptr->active_result_index= 0;
1484    }
1485
1486    /* Repeat until we don't deadlock. */
1487    deadlock_count= 0;
1488    for (;;) {
1489        result = NULL;
1490        sybase_ptr->deadlock = 0;
1491        sybase_ptr->affected_rows = 0;
1492
1493        /* On Solaris 11.5, ct_command() can be moved outside the
1494         * loop, but not on Linux 11.0.
1495         */
1496        if (ct_command(sybase_ptr->cmd, CS_LANG_CMD, query, CS_NULLTERM, CS_UNUSED)!=CS_SUCCEED) {
1497            /* If this didn't work, the connection is screwed but
1498             * ct-lib might not set CS_CONSTAT_DEAD.  So set our own
1499             * flag.  This happens sometimes when the database is restarted
1500             * and/or its machine is rebooted, and ct_command() returns
1501             * CS_BUSY for some reason.
1502             */
1503            sybase_ptr->dead = 1;
1504            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Connection is dead");
1505            RETURN_FALSE;
1506        }
1507
1508        if (ct_send(sybase_ptr->cmd)!=CS_SUCCEED) {
1509            ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1510            sybase_ptr->dead = 1;
1511            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Cannot send command");
1512            RETURN_FALSE;
1513        }
1514
1515        /* Use the first result set or succeed/fail status and discard the
1516         * others.  Applications really shouldn't be making calls that
1517         * return multiple result sets, but if they do then we need to
1518         * properly read or cancel them or the connection will become
1519         * unusable.
1520         */
1521        if (ct_results(sybase_ptr->cmd, &restype)!=CS_SUCCEED) {
1522            ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1523            sybase_ptr->dead = 1;
1524            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Cannot read results");
1525            RETURN_FALSE;
1526        }
1527        switch ((int) restype) {
1528            case CS_CMD_FAIL:
1529            default:
1530                status = Q_FAILURE;
1531                break;
1532            case CS_CMD_SUCCEED:
1533            case CS_CMD_DONE: {
1534                    CS_INT row_count;
1535                    if (ct_res_info(sybase_ptr->cmd, CS_ROW_COUNT, &row_count, CS_UNUSED, NULL)==CS_SUCCEED) {
1536                        sybase_ptr->affected_rows = (long)row_count;
1537                    }
1538                }
1539                /* Fall through */
1540            case CS_COMPUTEFMT_RESULT:
1541            case CS_ROWFMT_RESULT:
1542            case CS_DESCRIBE_RESULT:
1543            case CS_MSG_RESULT:
1544                buffered= 0;                /* These queries have no need for buffering */
1545                status = Q_SUCCESS;
1546                break;
1547            case CS_COMPUTE_RESULT:
1548            case CS_CURSOR_RESULT:
1549            case CS_PARAM_RESULT:
1550            case CS_ROW_RESULT:
1551            case CS_STATUS_RESULT:
1552                result = php_sybase_fetch_result_set(sybase_ptr, buffered, store TSRMLS_CC);
1553                if (result == NULL) {
1554                    ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1555                    RETURN_FALSE;
1556                }
1557                status = Q_RESULT;
1558                break;
1559        }
1560
1561        /* Check for left-over results */
1562        if (!buffered && status != Q_RESULT) {
1563            while ((retcode = ct_results(sybase_ptr->cmd, &restype))==CS_SUCCEED) {
1564                switch ((int) restype) {
1565                    case CS_CMD_SUCCEED:
1566                    case CS_CMD_DONE:
1567                        break;
1568
1569                    case CS_CMD_FAIL:
1570                        status = Q_FAILURE;
1571                        break;
1572
1573                    case CS_COMPUTE_RESULT:
1574                    case CS_CURSOR_RESULT:
1575                    case CS_PARAM_RESULT:
1576                    case CS_ROW_RESULT:
1577                        if (status != Q_RESULT) {
1578                            result = php_sybase_fetch_result_set(sybase_ptr, buffered, store TSRMLS_CC);
1579                            if (result == NULL) {
1580                                ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1581                                sybase_ptr->dead = 1;
1582                                RETURN_FALSE;
1583                            }
1584                            status = Q_RESULT;
1585                            retcode = result->last_retcode;
1586                        } else {
1587                            /* Unexpected results, cancel them. */
1588                            ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_CURRENT);
1589                        }
1590                        break;
1591                    case CS_STATUS_RESULT:
1592                        /* Unexpected results, cancel them. */
1593                        ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_CURRENT);
1594                        break;
1595
1596                    default:
1597                        status = Q_FAILURE;
1598                        break;
1599                }
1600                if (status == Q_FAILURE) {
1601                    ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1602                }
1603                if (retcode == CS_END_RESULTS) {
1604                    break;
1605                }
1606            }
1607            switch (retcode) {
1608                case CS_END_RESULTS:
1609                    /* Normal. */
1610                    break;
1611
1612                case CS_FAIL:
1613                    /* Hopefully this either cleans up the connection, or the
1614                     * connection ends up marked dead so it will be reopened
1615                     * if it is persistent.  We may want to do
1616                     * ct_close(CS_FORCE_CLOSE) if ct_cancel() fails; see the
1617                     * doc for ct_results()==CS_FAIL.
1618                     */
1619                    ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL);
1620                    /* Don't take chances with the vagaries of ct-lib.  Mark it
1621                     * dead ourselves.
1622                     */
1623                    sybase_ptr->dead = 1;
1624                case CS_CANCELED:
1625                default:
1626                    status = Q_FAILURE;
1627                    break;
1628            }
1629        }
1630
1631        /* Retry deadlocks up until deadlock_retry_count times */
1632        if (sybase_ptr->deadlock && SybCtG(deadlock_retry_count) != -1 && ++deadlock_count > SybCtG(deadlock_retry_count)) {
1633            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Retried deadlock %d times [max: %ld], giving up", deadlock_count- 1, SybCtG(deadlock_retry_count));
1634            FREE_SYBASE_RESULT(result);
1635            break;
1636        }
1637
1638        /* If query completed without deadlock, break out of the loop.
1639         * Sometimes deadlock results in failures and sometimes not,
1640         * it seems to depend on the server flavor.  But we want to
1641         * retry all deadlocks.
1642         */
1643        if (sybase_ptr->dead || sybase_ptr->deadlock == 0) {
1644            break;
1645        }
1646
1647        /* Get rid of any results we may have fetched.  This happens:
1648         * e.g., our result set may be a stored procedure status which
1649         * is returned even if the stored procedure deadlocks.  As an
1650         * optimization, we could try not to fetch results in known
1651         * deadlock conditions, but deadlock is (should be) rare.
1652         */
1653        FREE_SYBASE_RESULT(result);
1654    }
1655
1656    if (status == Q_SUCCESS) {
1657        RETURN_TRUE;
1658    }
1659
1660    if (status == Q_FAILURE) {
1661        FREE_SYBASE_RESULT(result);
1662        RETURN_FALSE;
1663    }
1664
1665    /* Indicate we have data in case of buffered queries */
1666    id= ZEND_REGISTER_RESOURCE(return_value, result, le_result);
1667    sybase_ptr->active_result_index= buffered ? id : 0;
1668}
1669
1670/* {{{ proto int sybase_query(string query [, resource link_id])
1671   Send Sybase query */
1672PHP_FUNCTION(sybase_query)
1673{
1674    php_sybase_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1675}
1676/* }}} */
1677
1678/* {{{ proto int sybase_unbuffered_query(string query [, resource link_id])
1679   Send Sybase query */
1680PHP_FUNCTION(sybase_unbuffered_query)
1681{
1682    php_sybase_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1683}
1684
1685/* {{{ proto bool sybase_free_result(resource result)
1686   Free result memory */
1687PHP_FUNCTION(sybase_free_result)
1688{
1689    zval *sybase_result_index = NULL;
1690    sybase_result *result;
1691
1692    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1693        return;
1694    }
1695    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1696
1697    /* Did we fetch up until the end? */
1698    if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS) {
1699        /* php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  canceling the rest of the results"); */
1700        ct_cancel(NULL, result->sybase_ptr->cmd, CS_CANCEL_ALL);
1701        php_sybase_finish_results(result TSRMLS_CC);
1702    }
1703
1704    zend_list_delete(Z_LVAL_P(sybase_result_index));
1705    RETURN_TRUE;
1706}
1707
1708/* }}} */
1709
1710/* {{{ proto string sybase_get_last_message(void)
1711   Returns the last message from server (over min_message_severity) */
1712PHP_FUNCTION(sybase_get_last_message)
1713{
1714    RETURN_STRING(SybCtG(server_message), 1);
1715}
1716/* }}} */
1717
1718/* {{{ proto int sybase_num_rows(resource result)
1719   Get number of rows in result */
1720PHP_FUNCTION(sybase_num_rows)
1721{
1722    zval *sybase_result_index = NULL;
1723    sybase_result *result;
1724
1725    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1726        return;
1727    }
1728    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1729
1730    Z_LVAL_P(return_value) = result->num_rows;
1731    Z_TYPE_P(return_value) = IS_LONG;
1732}
1733
1734/* }}} */
1735
1736/* {{{ proto int sybase_num_fields(resource result)
1737   Get number of fields in result */
1738PHP_FUNCTION(sybase_num_fields)
1739{
1740    zval *sybase_result_index = NULL;
1741    sybase_result *result;
1742
1743    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1744        return;
1745    }
1746    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1747
1748    Z_LVAL_P(return_value) = result->num_fields;
1749    Z_TYPE_P(return_value) = IS_LONG;
1750}
1751
1752/* }}} */
1753
1754/* {{{ proto array sybase_fetch_row(resource result)
1755   Get row as enumerated array */
1756PHP_FUNCTION(sybase_fetch_row)
1757{
1758    zval *sybase_result_index = NULL;
1759    int i;
1760    sybase_result *result;
1761    zval *field_content;
1762
1763    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1764        return;
1765    }
1766    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1767
1768    /* Unbuffered? */
1769    if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS) {
1770        php_sybase_fetch_result_row(result, 1 TSRMLS_CC);
1771    }
1772
1773    /* At the end? */
1774    if (result->cur_row >= result->num_rows) {
1775        RETURN_FALSE;
1776    }
1777
1778    array_init(return_value);
1779    for (i=0; i<result->num_fields; i++) {
1780        ALLOC_ZVAL(field_content);
1781        *field_content = result->data[result->store ? result->cur_row : 0][i];
1782        INIT_PZVAL(field_content);
1783        zval_copy_ctor(field_content);
1784        zend_hash_index_update(Z_ARRVAL_P(return_value), i, (void *) &field_content, sizeof(zval* ), NULL);
1785    }
1786    result->cur_row++;
1787}
1788
1789/* }}} */
1790
1791static void php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int numerics)
1792{
1793    zval *sybase_result_index = NULL;
1794    sybase_result *result;
1795    int i, j;
1796    zval *tmp;
1797    char name[32];
1798
1799    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &sybase_result_index) == FAILURE) {
1800        return;
1801    }
1802    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1803
1804    /* Unbuffered ? Fetch next row */
1805    if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS) {
1806        php_sybase_fetch_result_row(result, 1 TSRMLS_CC);
1807    }
1808
1809    /* At the end? */
1810    if (result->cur_row >= result->num_rows) {
1811        RETURN_FALSE;
1812    }
1813
1814    array_init(return_value);
1815
1816    j= 1;
1817    for (i=0; i<result->num_fields; i++) {
1818        ALLOC_ZVAL(tmp);
1819        *tmp = result->data[result->store ? result->cur_row : 0][i];
1820        INIT_PZVAL(tmp);
1821        zval_copy_ctor(tmp);
1822        if (numerics) {
1823            zend_hash_index_update(Z_ARRVAL_P(return_value), i, (void *) &tmp, sizeof(zval *), NULL);
1824            Z_ADDREF_P(tmp);
1825        }
1826
1827        if (zend_hash_exists(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1)) {
1828            snprintf(name, 32, "%s%d", result->fields[i].name, j);
1829            result->fields[i].name= estrdup(name);
1830            j++;
1831        }
1832        zend_hash_update(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1, (void *) &tmp, sizeof(zval *), NULL);
1833    }
1834    result->cur_row++;
1835}
1836
1837
1838/* {{{ proto object sybase_fetch_object(resource result [, mixed object])
1839   Fetch row as object */
1840PHP_FUNCTION(sybase_fetch_object)
1841{
1842    zval *object = NULL;
1843    zval *sybase_result_index = NULL;
1844    zend_class_entry *ce = NULL;
1845    sybase_result *result;
1846
1847    /* Was a second parameter given? */
1848    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|z", &sybase_result_index, &object) == FAILURE) {
1849        return;
1850    }
1851    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1852
1853    ce = ZEND_STANDARD_CLASS_DEF_PTR;
1854    if (NULL != object) {
1855        switch (Z_TYPE_P(object)) {
1856            case IS_OBJECT: {
1857                ce = Z_OBJCE_P(object);
1858                break;
1859            }
1860
1861            case IS_NULL: {
1862                /* Use default (ZEND_STANDARD_CLASS_DEF_PTR) */
1863                break;
1864            }
1865
1866            default: {
1867                zend_class_entry **pce = NULL;
1868                convert_to_string(object);
1869
1870                if (zend_lookup_class(Z_STRVAL_P(object), Z_STRLEN_P(object), &pce TSRMLS_CC) == FAILURE) {
1871                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Sybase:  Class %s has not been declared", Z_STRVAL_P(object));
1872                    /* Use default (ZEND_STANDARD_CLASS_DEF_PTR) */
1873                } else {
1874                    ce = *pce;
1875                }
1876            }
1877        }
1878    }
1879
1880    /* Reset no. of arguments to 1 so that we can use INTERNAL_FUNCTION_PARAM_PASSTHRU */
1881    ht= 1;
1882    php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1883    if (Z_TYPE_P(return_value) == IS_ARRAY) {
1884        object_and_properties_init(return_value, ce, Z_ARRVAL_P(return_value));
1885    }
1886}
1887/* }}} */
1888
1889/* {{{ proto array sybase_fetch_array(resource result)
1890   Fetch row as array */
1891PHP_FUNCTION(sybase_fetch_array)
1892{
1893    php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1894}
1895/* }}} */
1896
1897/* {{{ proto array sybase_fetch_assoc(resource result)
1898   Fetch row as array without numberic indices */
1899PHP_FUNCTION(sybase_fetch_assoc)
1900{
1901    php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1902}
1903/* }}} */
1904
1905/* {{{ proto bool sybase_data_seek(resource result, int offset)
1906   Move internal row pointer */
1907PHP_FUNCTION(sybase_data_seek)
1908{
1909    zval *sybase_result_index = NULL;
1910    long offset;
1911    sybase_result *result;
1912
1913    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &sybase_result_index, &offset) == FAILURE) {
1914        return;
1915    }
1916    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1917
1918    /* Unbuffered ? */
1919    if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS && offset >= result->num_rows) {
1920        php_sybase_fetch_result_row(result, offset+ 1 TSRMLS_CC);
1921    }
1922
1923    if (offset < 0 || offset >= result->num_rows) {
1924        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad row offset %ld, must be betweem 0 and %d", offset, result->num_rows - 1);
1925        RETURN_FALSE;
1926    }
1927
1928    result->cur_row = offset;
1929    RETURN_TRUE;
1930}
1931/* }}} */
1932
1933static char *php_sybase_get_field_name(CS_INT type)
1934{
1935    switch (type) {
1936        case CS_CHAR_TYPE:
1937        case CS_VARCHAR_TYPE:
1938        case CS_TEXT_TYPE:
1939            return "string";
1940            break;
1941        case CS_IMAGE_TYPE:
1942            return "image";
1943            break;
1944        case CS_BINARY_TYPE:
1945        case CS_VARBINARY_TYPE:
1946            return "blob";
1947            break;
1948        case CS_BIT_TYPE:
1949            return "bit";
1950            break;
1951        case CS_TINYINT_TYPE:
1952        case CS_SMALLINT_TYPE:
1953        case CS_INT_TYPE:
1954            return "int";
1955            break;
1956        case CS_REAL_TYPE:
1957        case CS_FLOAT_TYPE:
1958        case CS_NUMERIC_TYPE:
1959        case CS_DECIMAL_TYPE:
1960            return "real";
1961            break;
1962        case CS_MONEY_TYPE:
1963        case CS_MONEY4_TYPE:
1964            return "money";
1965            break;
1966        case CS_DATETIME_TYPE:
1967        case CS_DATETIME4_TYPE:
1968            return "datetime";
1969            break;
1970        default:
1971            return "unknown";
1972            break;
1973    }
1974}
1975
1976
1977/* {{{ proto object sybase_fetch_field(resource result [, int offset])
1978   Get field information */
1979PHP_FUNCTION(sybase_fetch_field)
1980{
1981    zval *sybase_result_index = NULL;
1982    long field_offset = -1;
1983    sybase_result *result;
1984
1985    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &sybase_result_index, &field_offset) == FAILURE) {
1986        return;
1987    }
1988    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
1989
1990    if (field_offset == -1) {
1991        field_offset = result->cur_field;
1992        result->cur_field++;
1993    }
1994
1995    if (field_offset < 0 || field_offset >= result->num_fields) {
1996        if (ZEND_NUM_ARGS() == 2) { /* field specified explicitly */
1997            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad column offset");
1998        }
1999        RETURN_FALSE;
2000    }
2001
2002    object_init(return_value);
2003
2004    add_property_string(return_value, "name", result->fields[field_offset].name);
2005    add_property_long(return_value, "max_length", result->fields[field_offset].max_length);
2006    add_property_string(return_value, "column_source", result->fields[field_offset].column_source);
2007    add_property_long(return_value, "numeric", result->fields[field_offset].numeric);
2008    add_property_string(return_value, "type", php_sybase_get_field_name(Z_TYPE(result->fields[field_offset])));
2009}
2010/* }}} */
2011
2012
2013/* {{{ proto bool sybase_field_seek(resource result, int offset)
2014   Set field offset */
2015PHP_FUNCTION(sybase_field_seek)
2016{
2017    zval *sybase_result_index = NULL;
2018    long field_offset;
2019    sybase_result *result;
2020
2021    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &sybase_result_index, &field_offset) == FAILURE) {
2022        return;
2023    }
2024    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
2025
2026    if (field_offset < 0 || field_offset >= result->num_fields) {
2027        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad column offset");
2028        RETURN_FALSE;
2029    }
2030
2031    result->cur_field = field_offset;
2032    RETURN_TRUE;
2033}
2034/* }}} */
2035
2036
2037/* {{{ proto string sybase_result(resource result, int row, mixed field)
2038   Get result data */
2039PHP_FUNCTION(sybase_result)
2040{
2041    zval *field;
2042    zval *sybase_result_index = NULL;
2043    long row;
2044    int field_offset = 0;
2045    sybase_result *result;
2046
2047    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &sybase_result_index, &row, &field) == FAILURE) {
2048        return;
2049    }
2050    ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result);
2051
2052    /* Unbuffered ? */
2053    if (result->last_retcode != CS_END_DATA && result->last_retcode != CS_END_RESULTS && row >= result->num_rows) {
2054        php_sybase_fetch_result_row(result, row TSRMLS_CC);
2055    }
2056
2057    if (row < 0 || row >= result->num_rows) {
2058        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad row offset (%ld)", row);
2059        RETURN_FALSE;
2060    }
2061
2062    switch(Z_TYPE_P(field)) {
2063        case IS_STRING: {
2064            int i;
2065
2066            for (i = 0; i < result->num_fields; i++) {
2067                if (strcasecmp(result->fields[i].name, Z_STRVAL_P(field)) == 0) {
2068                    field_offset = i;
2069                    break;
2070                }
2071            }
2072            if (i >= result->num_fields) { /* no match found */
2073                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  %s field not found in result", Z_STRVAL_P(field));
2074                RETURN_FALSE;
2075            }
2076            break;
2077        }
2078        default:
2079            convert_to_long(field);
2080            field_offset = Z_LVAL_P(field);
2081            if (field_offset < 0 || field_offset >= result->num_fields) {
2082                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  Bad column offset specified");
2083                RETURN_FALSE;
2084            }
2085            break;
2086    }
2087
2088    *return_value = result->data[row][field_offset];
2089    zval_copy_ctor(return_value);
2090}
2091/* }}} */
2092
2093
2094/* {{{ proto int sybase_affected_rows([resource link_id])
2095   Get number of affected rows in last query */
2096PHP_FUNCTION(sybase_affected_rows)
2097{
2098    zval *sybase_link_index = NULL;
2099    sybase_link *sybase_ptr;
2100    int id;
2101
2102    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &sybase_link_index) == FAILURE) {
2103        return;
2104    }
2105
2106    if (php_sybase_connection_id(sybase_link_index, &id TSRMLS_CC) == FAILURE) {
2107        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase:  No connection");
2108        RETURN_FALSE;
2109    }
2110
2111    ZEND_FETCH_RESOURCE2(sybase_ptr, sybase_link *, &sybase_link_index, id, "Sybase-Link", le_link, le_plink);
2112
2113    Z_LVAL_P(return_value) = sybase_ptr->affected_rows;
2114    Z_TYPE_P(return_value) = IS_LONG;
2115}
2116/* }}} */
2117
2118
2119PHP_MINFO_FUNCTION(sybase)
2120{
2121    char buf[32];
2122
2123    php_info_print_table_start();
2124    php_info_print_table_header(2, "Sybase_CT Support", "enabled" );
2125    snprintf(buf, sizeof(buf), "%ld", SybCtG(num_persistent));
2126    php_info_print_table_row(2, "Active Persistent Links", buf);
2127    snprintf(buf, sizeof(buf), "%ld", SybCtG(num_links));
2128    php_info_print_table_row(2, "Active Links", buf);
2129    snprintf(buf, sizeof(buf), "%ld", SybCtG(min_server_severity));
2130    php_info_print_table_row(2, "Min server severity", buf);
2131    snprintf(buf, sizeof(buf), "%ld", SybCtG(min_client_severity));
2132    php_info_print_table_row(2, "Min client severity", buf);
2133    php_info_print_table_row(2, "Application Name", SybCtG(appname));
2134    snprintf(buf, sizeof(buf), "%ld", SybCtG(deadlock_retry_count));
2135    php_info_print_table_row(2, "Deadlock retry count", buf);
2136    php_info_print_table_end();
2137
2138    DISPLAY_INI_ENTRIES();
2139}
2140
2141
2142/* {{{ proto void sybase_min_client_severity(int severity)
2143   Sets minimum client severity */
2144PHP_FUNCTION(sybase_min_client_severity)
2145{
2146    long severity;
2147
2148    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &severity) == FAILURE) {
2149        return;
2150    }
2151
2152    SybCtG(min_client_severity) = severity;
2153}
2154/* }}} */
2155
2156
2157/* {{{ proto void sybase_min_server_severity(int severity)
2158   Sets minimum server severity */
2159PHP_FUNCTION(sybase_min_server_severity)
2160{
2161    long severity;
2162
2163    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &severity) == FAILURE) {
2164        return;
2165    }
2166
2167    SybCtG(min_server_severity) = severity;
2168}
2169/* }}} */
2170
2171/* {{{ proto void sybase_deadlock_retry_count(int retry_count)
2172   Sets deadlock retry count */
2173PHP_FUNCTION(sybase_deadlock_retry_count)
2174{
2175    long retry_count;
2176
2177    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &retry_count) == FAILURE) {
2178        return;
2179    }
2180
2181    SybCtG(deadlock_retry_count) = retry_count;
2182}
2183/* }}} */
2184
2185
2186/* {{{ proto bool sybase_set_message_handler(mixed error_func [, resource connection])
2187   Set the error handler, to be called when a server message is raised.
2188   If error_func is NULL the handler will be deleted */
2189PHP_FUNCTION(sybase_set_message_handler)
2190{
2191    zend_fcall_info fci = empty_fcall_info;
2192    zend_fcall_info_cache cache = empty_fcall_info_cache;
2193    zval *sybase_link_index= NULL;
2194    sybase_link *sybase_ptr;
2195    zval **callback;
2196    int id;
2197
2198    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!|r", &fci, &cache, &sybase_link_index) == FAILURE) {
2199        return;
2200    }
2201
2202    if (php_sybase_connection_id(sybase_link_index, &id TSRMLS_CC) == FAILURE) {
2203
2204        /* Doesn't matter if we're not connected yet, use default */
2205        callback= &SybCtG(callback_name);
2206    } else if (-1 == id) {
2207
2208        /* Connection-based message handler */
2209        ZEND_FETCH_RESOURCE2(sybase_ptr, sybase_link *, &sybase_link_index, id, "Sybase-Link", le_link, le_plink);
2210        callback= &sybase_ptr->callback_name;
2211    } else {
2212
2213        /* Default message handler */
2214        callback= &SybCtG(callback_name);
2215    }
2216
2217    /* Clean old callback */
2218    if (*callback) {
2219        zval_ptr_dtor(callback);
2220        *callback = NULL;
2221    }
2222
2223    if (ZEND_FCI_INITIALIZED(fci)) {
2224        ALLOC_ZVAL(*callback);
2225        **callback = *fci.function_name;
2226        INIT_PZVAL(*callback);
2227        zval_copy_ctor(*callback);
2228    } else {
2229        callback= NULL;
2230    }
2231
2232    RETURN_TRUE;
2233}
2234/* }}} */
2235
2236
2237#endif
2238
2239/*
2240 * Local variables:
2241 * tab-width: 4
2242 * c-basic-offset: 4
2243 * End:
2244 */
2245