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