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