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