|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2002-05-02 05:37 UTC] thekid at thekid dot de
First of all: I'm putting this in here since my mail to php-dev@list.php.net was rejected thanks to an overefficient spam protection filter (kundenserver.de). Please see http://relaytest.kundenserver.de/ for details. Here's my mail: -------------------------------------------------------- Hello, as you might know, the sybase extension is very bad when it comes to data types, since it returns almost everything as a string, except for NULL values, which become FALSE. Short example: --- SQL ------------------------------- select NULL as "null", "string" as string, 6100 as integer, 6.1 as float, getdate() as date --- var_dump() of resultset ----------- array(10) { [0]=> string(0) "" ["null"]=> string(0) "" [1]=> string(6) "string" ["string"]=> string(6) "string" [2]=> string(4) "6100" ["integer"]=> string(4) "6100" [3]=> string(3) "6.1" ["float"]=> string(3) "6.1" [4]=> string(4) " :" ["date"]=> string(4) " :" } Well, when writing applications communicating via SOAP with type-secure languages - in my case PHP as server, Java as client - this comes quite unhandy, since on ends up going through the resultset and changing types for each entry, thus producing unnecessary overhead. Something like this would be much nicer: --- var_dump() of resultset ----------- array(10) { [0]=> NULL ["null"]=> NULL [1]=> string(6) "string" ["string"]=> string(6) "string" [2]=> int(6100) ["integer"]=> int(6100) [3]=> float(6.1) ["float"]=> float(6.1) [4]=> string(19) "May 02 2002 11:15AM" ["date"]=> string(19) "May 02 2002 11:15AM" } OK, so I went ahead and patched ext/sybase_ct/php_sybase_ct.c to accomplish my wishes. I must admit though I'm a total lamer when it goes to writing C sourcecode and utilizing the Zend API, so - after studying a couple of files, wrote a couple of lines, compiled, watched the thing segfault once or twice, I finally managed to get a running PHP binary and a sybase_ct.so compiled. Attached is the patch, however ugly it may be, it compiles and produces the resultset as intended - maybe you guys want to take a look at it and correct it to be much nicer and much better:-) -- Timm Friebe Systems developer Schlund+Partner AG Karlsruhe, Germany [patch-ext_sybase_ct_php_sybase_ct.c] --- php4-200205012100/ext/sybase_ct/php_sybase_ct.c Tue Mar 12 21:34:06 2002 +++ __build__/ext/sybase_ct/php_sybase_ct.c Thu May 2 10:47:53 2002 @@ -953,33 +953,33 @@ break; case CS_SMALLINT_TYPE: datafmt[i].maxlength = 7; - numerics[i] = 1; + numerics[i] = 1; break; case CS_INT_TYPE: datafmt[i].maxlength = 12; - numerics[i] = 1; + numerics[i] = 1; break; case CS_REAL_TYPE: case CS_FLOAT_TYPE: datafmt[i].maxlength = 24; - numerics[i] = 1; - break; + numerics[i] = 2; + break; case CS_MONEY_TYPE: case CS_MONEY4_TYPE: datafmt[i].maxlength = 24; - numerics[i] = 0; + numerics[i] = 0; break; case CS_DATETIME_TYPE: case CS_DATETIME4_TYPE: datafmt[i].maxlength = 30; numerics[i] = 0; - break; + break; case CS_NUMERIC_TYPE: case CS_DECIMAL_TYPE: datafmt[i].maxlength = datafmt[i].precision + 3; - numerics[i] = 1; + numerics[i] = 2; break; - default: + default: datafmt[i].maxlength++; numerics[i] = 0; break; @@ -1004,11 +1004,19 @@ result->data[i] = (pval *) emalloc(sizeof(pval)*num_fields); for (j=0; j<num_fields; j++) { if (indicators[j] == -1) { /* null value */ - ZVAL_FALSE(&result->data[i][j]); + ZVAL_NULL(&result->data[i][j]); } else { Z_STRLEN(result->data[i][j]) = lengths[j]-1; /* we don't need the NULL in the length */ Z_STRVAL(result->data[i][j]) = estrndup(tmp_buffer[j], lengths[j]); Z_TYPE(result->data[i][j]) = IS_STRING; + + // Here we go, i want those types!:-) + // Perhaps there is a nicer way of doing this, instead of making strings first + // and then converting them back, but I'm a Zend-API-lamer. + switch (numerics[j]) { + case 1: convert_to_long(&result->data[i][j]); break; + case 2: convert_to_double(&result->data[i][j]); numerics[j]= 1; break; + } } } } PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Thu Oct 30 21:00:01 2025 UTC |
OK, more to come. The following patch solves the issues of corrupt objects, a missing function sybase_fetch_assoc() function and the loss of fields with similar names while including my previous patch. * Corrupt objects sybase_fetch_object() returns object with numeric member variables, which cannot be accessed, e.g. object() { 0 => 'bar', 'foo' => 'bar' } $obj->0 will fail. * Identical fieldnames Joining over 2 or more tables and selecting fields with the same fieldname, you will lose information using the string keys: select a.lastchange, b.lastchange [...] array() { 0 => 'May 02 2002 05:49PM', 'lastchange' => 'May 02 2002 05:49PM', 1 => 'Apr 03 2002 12:22PM' } Patch returns this using sybase_fetch_assoc(): array() { 'lastchange' => 'May 02 2002 05:49PM', 'lastchange1'=> 'Apr 03 2002 12:22PM' ) No BC issues on this, AFAIS. [patch-ext_sybase_ct_php_sybase_ct.c] --- php4-200205012100/ext/sybase_ct/php_sybase_ct.c Tue Mar 12 21:34:06 2002 +++ __build__/ext/sybase_ct/php_sybase_ct.c Thu May 2 17:39:46 2002 @@ -48,6 +48,7 @@ PHP_FE(sybase_num_fields, NULL) PHP_FE(sybase_fetch_row, NULL) PHP_FE(sybase_fetch_array, NULL) + PHP_FE(sybase_fetch_assoc, NULL) PHP_FE(sybase_fetch_object, NULL) PHP_FE(sybase_data_seek, NULL) PHP_FE(sybase_fetch_field, NULL) @@ -57,22 +58,23 @@ PHP_FE(sybase_min_client_severity, NULL) PHP_FE(sybase_min_server_severity, NULL) - PHP_FALIAS(mssql_connect, sybase_connect, NULL) - PHP_FALIAS(mssql_pconnect, sybase_pconnect, NULL) - PHP_FALIAS(mssql_close, sybase_close, NULL) - PHP_FALIAS(mssql_select_db, sybase_select_db, NULL) - PHP_FALIAS(mssql_query, sybase_query, NULL) + PHP_FALIAS(mssql_connect, sybase_connect, NULL) + PHP_FALIAS(mssql_pconnect, sybase_pconnect, NULL) + PHP_FALIAS(mssql_close, sybase_close, NULL) + PHP_FALIAS(mssql_select_db, sybase_select_db, NULL) + PHP_FALIAS(mssql_query, sybase_query, NULL) PHP_FALIAS(mssql_free_result, sybase_free_result, NULL) PHP_FALIAS(mssql_get_last_message, sybase_get_last_message,NULL) - PHP_FALIAS(mssql_num_rows, sybase_num_rows, NULL) + PHP_FALIAS(mssql_num_rows, sybase_num_rows, NULL) PHP_FALIAS(mssql_num_fields, sybase_num_fields, NULL) - PHP_FALIAS(mssql_fetch_row, sybase_fetch_row, NULL) + PHP_FALIAS(mssql_fetch_row, sybase_fetch_row, NULL) PHP_FALIAS(mssql_fetch_array, sybase_fetch_array, NULL) + PHP_FALIAS(mssql_fetch_assoc, sybase_fetch_assoc, NULL) PHP_FALIAS(mssql_fetch_object, sybase_fetch_object, NULL) - PHP_FALIAS(mssql_data_seek, sybase_data_seek, NULL) + PHP_FALIAS(mssql_data_seek, sybase_data_seek, NULL) PHP_FALIAS(mssql_fetch_field, sybase_fetch_field, NULL) PHP_FALIAS(mssql_field_seek, sybase_field_seek, NULL) - PHP_FALIAS(mssql_result, sybase_result, NULL) + PHP_FALIAS(mssql_result, sybase_result, NULL) PHP_FALIAS(mssql_affected_rows, sybase_affected_rows, NULL) PHP_FALIAS(mssql_min_client_severity, sybase_min_client_severity, NULL) PHP_FALIAS(mssql_min_server_severity, sybase_min_server_severity, NULL) @@ -230,7 +232,8 @@ TSRMLS_FETCH(); if (srvmsg->severity >= SybCtG(min_server_severity)) { - php_error(E_WARNING, "Sybase: Server message: %s (severity %d, procedure %s)", + php_error(E_WARNING, "Sybase: Server message #%d: %s (severity %d, procedure %s)", + srvmsg->msgnumber, srvmsg->text, srvmsg->severity, ((srvmsg->proclen>0) ? srvmsg->proc : "N/A")); } STR_FREE(SybCtG(server_message)); @@ -953,33 +956,33 @@ break; case CS_SMALLINT_TYPE: datafmt[i].maxlength = 7; - numerics[i] = 1; + numerics[i] = 1; break; case CS_INT_TYPE: datafmt[i].maxlength = 12; - numerics[i] = 1; + numerics[i] = 1; break; case CS_REAL_TYPE: case CS_FLOAT_TYPE: datafmt[i].maxlength = 24; - numerics[i] = 1; - break; + numerics[i] = 2; + break; case CS_MONEY_TYPE: case CS_MONEY4_TYPE: datafmt[i].maxlength = 24; - numerics[i] = 0; + numerics[i] = 0; break; case CS_DATETIME_TYPE: case CS_DATETIME4_TYPE: datafmt[i].maxlength = 30; numerics[i] = 0; - break; + break; case CS_NUMERIC_TYPE: case CS_DECIMAL_TYPE: datafmt[i].maxlength = datafmt[i].precision + 3; - numerics[i] = 1; + numerics[i] = 2; break; - default: + default: datafmt[i].maxlength++; numerics[i] = 0; break; @@ -1004,11 +1007,20 @@ result->data[i] = (pval *) emalloc(sizeof(pval)*num_fields); for (j=0; j<num_fields; j++) { if (indicators[j] == -1) { /* null value */ - ZVAL_FALSE(&result->data[i][j]); + ZVAL_NULL(&result->data[i][j]); } else { Z_STRLEN(result->data[i][j]) = lengths[j]-1; /* we don't need the NULL in the length */ Z_STRVAL(result->data[i][j]) = estrndup(tmp_buffer[j], lengths[j]); Z_TYPE(result->data[i][j]) = IS_STRING; + + /* Here we go, i want those types!:-) + * Perhaps there is a nicer way of doing this, instead of making strings first + * and then converting them back, but I'm a Zend-API-lamer. + */ + switch (numerics[j]) { + case 1: convert_to_long(&result->data[i][j]); break; + case 2: convert_to_double(&result->data[i][j]); numerics[j]= 1; break; + } } } } @@ -1021,7 +1033,7 @@ j=0; for (i=0; i<num_fields; i++) { char computed_buf[16]; - + if (datafmt[i].namelen>0) { result->fields[i].name = estrndup(datafmt[i].name, datafmt[i].namelen); } else { @@ -1404,15 +1416,65 @@ result->cur_row++; } +static void php_sybase_fetch_assoc(INTERNAL_FUNCTION_PARAMETERS) +{ + pval *sybase_result_index; + sybase_result *result; + int i, j; + pval *tmp; + char name[32]; + + if (ZEND_NUM_ARGS()!=1 || getParameters(ht, 1, &sybase_result_index)==FAILURE) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result); + + if (result->cur_row >= result->num_rows) { + RETURN_FALSE; + } + + if (array_init(return_value)==FAILURE) { + RETURN_FALSE; + } + + j= 1; + for (i=0; i<result->num_fields; i++) { + ALLOC_ZVAL(tmp); + *tmp = result->data[result->cur_row][i]; + INIT_PZVAL(tmp); + if (PG(magic_quotes_runtime) && Z_TYPE_P(tmp) == IS_STRING) { + Z_STRVAL_P(tmp) = php_addslashes(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &Z_STRLEN_P(tmp), 0 TSRMLS_CC); + } else { + pval_copy_constructor(tmp); + } + if (zend_hash_exists(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1)) { + snprintf(name, 32, "%s%d", result->fields[i].name, j); + j++; + zend_hash_update(Z_ARRVAL_P(return_value), name, strlen(name)+1, (void *) &tmp, sizeof(pval *), NULL); + } else { + zend_hash_update(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1, (void *) &tmp, sizeof(pval *), NULL); + } + } + result->cur_row++; +} /* {{{ proto object sybase_fetch_object(int result) Fetch row as object */ PHP_FUNCTION(sybase_fetch_object) { - php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU); + php_sybase_fetch_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU); if (Z_TYPE_P(return_value)==IS_ARRAY) { object_and_properties_init(return_value, ZEND_STANDARD_CLASS_DEF_PTR, Z_ARRVAL_P(return_value)); } +} +/* }}} */ + +/* {{{ proto array sybase_fetch_assoc(int result) + Fetch row as array */ +PHP_FUNCTION(sybase_fetch_assoc) +{ + php_sybase_fetch_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */With this last patch, you will be able to handle all of sybase's error messages via a callback function. This comes in quite handy when having to check for deadlocks (the 1205:-)). $deadlock= strstr($php_errormsg, 'deadlock'); is, of course, possible right now, but in my eyes not a very generalistic way of going about it... --- php4-200205012100/ext/sybase_ct/php_sybase_ct.h Thu Feb 28 09:38:19 2002 +++ __build__/ext/sybase_ct/php_sybase_ct.h Mon Jun 10 21:32:25 2002 @@ -45,6 +45,7 @@ PHP_FUNCTION(sybase_num_fields); PHP_FUNCTION(sybase_fetch_row); PHP_FUNCTION(sybase_fetch_array); +PHP_FUNCTION(sybase_fetch_assoc); PHP_FUNCTION(sybase_fetch_object); PHP_FUNCTION(sybase_data_seek); PHP_FUNCTION(sybase_result); @@ -53,7 +54,7 @@ PHP_FUNCTION(sybase_min_client_severity); PHP_FUNCTION(sybase_min_server_severity); PHP_FUNCTION(sybase_fetch_field); - +PHP_FUNCTION(sybase_set_message_handler); #include <ctpublic.h> @@ -66,6 +67,7 @@ char *hostname; char *server_message; long min_server_severity, min_client_severity; + zval *callback_name; CS_CONTEXT *context; ZEND_END_MODULE_GLOBALS(sybase) @@ -93,7 +95,6 @@ int cur_row,cur_field; int num_rows,num_fields; } sybase_result; - #ifdef ZTS # define SybCtG(v) TSRMG(sybase_globals_id, zend_sybase_globals *, v) --- php4-200205012100/ext/sybase_ct/php_sybase_ct.c Tue Mar 12 21:34:06 2002 +++ __build__/ext/sybase_ct/php_sybase_ct.c Wed May 8 04:12:38 2002 @@ -48,6 +48,7 @@ PHP_FE(sybase_num_fields, NULL) PHP_FE(sybase_fetch_row, NULL) PHP_FE(sybase_fetch_array, NULL) + PHP_FE(sybase_fetch_assoc, NULL) PHP_FE(sybase_fetch_object, NULL) PHP_FE(sybase_data_seek, NULL) PHP_FE(sybase_fetch_field, NULL) @@ -56,23 +57,26 @@ PHP_FE(sybase_affected_rows, NULL) PHP_FE(sybase_min_client_severity, NULL) PHP_FE(sybase_min_server_severity, NULL) + PHP_FE(sybase_set_message_handler, NULL) - PHP_FALIAS(mssql_connect, sybase_connect, NULL) - PHP_FALIAS(mssql_pconnect, sybase_pconnect, NULL) - PHP_FALIAS(mssql_close, sybase_close, NULL) - PHP_FALIAS(mssql_select_db, sybase_select_db, NULL) - PHP_FALIAS(mssql_query, sybase_query, NULL) + PHP_FALIAS(mssql_set_message_handler, sybase_set_message_handler, NULL) + PHP_FALIAS(mssql_connect, sybase_connect, NULL) + PHP_FALIAS(mssql_pconnect, sybase_pconnect, NULL) + PHP_FALIAS(mssql_close, sybase_close, NULL) + PHP_FALIAS(mssql_select_db, sybase_select_db, NULL) + PHP_FALIAS(mssql_query, sybase_query, NULL) PHP_FALIAS(mssql_free_result, sybase_free_result, NULL) PHP_FALIAS(mssql_get_last_message, sybase_get_last_message,NULL) - PHP_FALIAS(mssql_num_rows, sybase_num_rows, NULL) + PHP_FALIAS(mssql_num_rows, sybase_num_rows, NULL) PHP_FALIAS(mssql_num_fields, sybase_num_fields, NULL) - PHP_FALIAS(mssql_fetch_row, sybase_fetch_row, NULL) + PHP_FALIAS(mssql_fetch_row, sybase_fetch_row, NULL) PHP_FALIAS(mssql_fetch_array, sybase_fetch_array, NULL) + PHP_FALIAS(mssql_fetch_assoc, sybase_fetch_assoc, NULL) PHP_FALIAS(mssql_fetch_object, sybase_fetch_object, NULL) - PHP_FALIAS(mssql_data_seek, sybase_data_seek, NULL) + PHP_FALIAS(mssql_data_seek, sybase_data_seek, NULL) PHP_FALIAS(mssql_fetch_field, sybase_fetch_field, NULL) PHP_FALIAS(mssql_field_seek, sybase_field_seek, NULL) - PHP_FALIAS(mssql_result, sybase_result, NULL) + PHP_FALIAS(mssql_result, sybase_result, NULL) PHP_FALIAS(mssql_affected_rows, sybase_affected_rows, NULL) PHP_FALIAS(mssql_min_client_severity, sybase_min_client_severity, NULL) PHP_FALIAS(mssql_min_server_severity, sybase_min_server_severity, NULL) @@ -199,12 +203,36 @@ } +/* {{{ proto void sybase_set_message_handler(mixed error_func) + Set the error handler, to be called when a server message is raise */ +PHP_FUNCTION(sybase_set_message_handler) +{ + char *c; + int c_len; + + if( + (ZEND_NUM_ARGS() != 1) || + (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &c, &c_len)!= SUCCESS)) + { + WRONG_PARAM_COUNT; + } + + /* + * Is it a good idea to put this in sybase_globals? + */ + MAKE_STD_ZVAL(SybCtG(callback_name)); + ZVAL_STRINGL(SybCtG(callback_name), c, c_len, 1); + + RETURN_TRUE; +} +/* }}} */ + static CS_RETCODE CS_PUBLIC _client_message_handler(CS_CONTEXT *context, CS_CONNECTION *connection, CS_CLIENTMSG *errmsg) { TSRMLS_FETCH(); if (CS_SEVERITY(errmsg->msgnumber) >= SybCtG(min_client_severity)) { - php_error(E_WARNING, "Sybase: Client message: %s (severity %d)", errmsg->msgstring, CS_SEVERITY(errmsg->msgnumber)); + php_error(E_WARNING, "Sybase: Client message: %s (severity %d)", errmsg->msgstring, CS_SEVERITY(errmsg->msgnumber)); } STR_FREE(SybCtG(server_message)); SybCtG(server_message) = estrdup(errmsg->msgstring); @@ -227,13 +255,61 @@ static CS_RETCODE CS_PUBLIC _server_message_handler(CS_CONTEXT *context, CS_CONNECTION *connection, CS_SERVERMSG *srvmsg) { + zval *retval = NULL; + zval severity, msgnumber, state, line, text; + zval *ptrs[5]= {&msgnumber, &severity, &state, &line, &text}; + zval **args[5]= {&ptrs[0], &ptrs[1], &ptrs[2], &ptrs[3], &ptrs[4]}; + int handled; + TSRMLS_FETCH(); if (srvmsg->severity >= SybCtG(min_server_severity)) { - php_error(E_WARNING, "Sybase: Server message: %s (severity %d, procedure %s)", - srvmsg->text, srvmsg->severity, ((srvmsg->proclen>0) ? srvmsg->proc : "N/A")); + handled= 0; + if (SybCtG(callback_name) != NULL) { + INIT_ZVAL(msgnumber); + INIT_ZVAL(severity); + INIT_ZVAL(state); + INIT_ZVAL(line); + INIT_ZVAL(text); + + ZVAL_LONG(&msgnumber, srvmsg->msgnumber); + ZVAL_LONG(&severity, srvmsg->severity); + ZVAL_LONG(&state, srvmsg->state); + ZVAL_LONG(&line, srvmsg->line); + ZVAL_STRING(&text, srvmsg->text, 0); + + if (call_user_function_ex(CG(function_table), NULL, SybCtG(callback_name), &retval, 5, args, 0, NULL TSRMLS_CC)== FAILURE) { + php_error(E_WARNING, "Sybase: Cannot call the messagehandler %s", Z_STRVAL_P(SybCtG(callback_name))); + } + + /* + * Returning FALSE, the user function indicates it can't cope + * with this error. Any other return value will be ignored. + * + */ + if (retval) { + handled= ( + (Z_TYPE_P(retval) != IS_BOOL) || + (Z_BVAL_P(retval) != 0) + ); + zval_ptr_dtor(&retval); + } + + } + if (!handled) { + php_error( + E_WARNING, + "Sybase: Server message #%d: %s (severity %d, procedure %s)", + srvmsg->msgnumber, + srvmsg->text, + srvmsg->severity, + ((srvmsg->proclen>0) ? srvmsg->proc : "N/A") + ); + } + } STR_FREE(SybCtG(server_message)); + SybCtG(server_message) = estrdup(srvmsg->text); /* If this is a deadlock message, set the connection's deadlock flag @@ -267,7 +343,7 @@ static void php_sybase_init_globals(zend_sybase_globals *sybase_globals) { - long timeout; + long timeout, packet_size; if (cs_ctx_alloc(CTLIB_VERSION, &sybase_globals->context)!=CS_SUCCEED || ct_init(sybase_globals->context, CTLIB_VERSION)!=CS_SUCCEED) { return; @@ -316,7 +392,19 @@ php_error(E_WARNING, "Sybase: Unable to set timeout"); } } + + /* + * Packet size + */ + if (cfg_get_long("sybct.packet_size", &packet_size)==SUCCESS) { + CS_INT cs_packet_size = packet_size; + if (ct_config(sybase_globals->context, CS_SET, CS_PACKETSIZE, &cs_packet_size, CS_UNUSED, NULL)!=CS_SUCCEED) { + php_error(E_WARNING, "Sybase: Unable to set timeout"); + } + } + sybase_globals->num_persistent=0; + sybase_globals->callback_name = NULL; } @@ -366,6 +454,7 @@ PHP_RSHUTDOWN_FUNCTION(sybase) { efree(SybCtG(appname)); + if (NULL != SybCtG(callback_name)) zval_ptr_dtor(&SybCtG(callback_name)); STR_FREE(SybCtG(server_message)); return SUCCESS; } @@ -893,8 +982,8 @@ static sybase_result * php_sybase_fetch_result_set (sybase_link *sybase_ptr) { + sybase_result *result; int num_fields; - sybase_result *result; char **tmp_buffer; CS_INT *lengths; CS_SMALLINT *indicators; @@ -920,7 +1009,6 @@ result->sybase_ptr = sybase_ptr; result->cur_field=result->cur_row=result->num_rows=0; result->num_fields = num_fields; - tmp_buffer = (char **) emalloc(sizeof(char *)*num_fields); lengths = (CS_INT *) emalloc(sizeof(CS_INT)*num_fields); indicators = (CS_SMALLINT *) emalloc(sizeof(CS_INT)*num_fields); @@ -953,33 +1041,33 @@ break; case CS_SMALLINT_TYPE: datafmt[i].maxlength = 7; - numerics[i] = 1; + numerics[i] = 1; break; case CS_INT_TYPE: datafmt[i].maxlength = 12; - numerics[i] = 1; + numerics[i] = 1; break; case CS_REAL_TYPE: case CS_FLOAT_TYPE: datafmt[i].maxlength = 24; - numerics[i] = 1; - break; + numerics[i] = 2; + break; case CS_MONEY_TYPE: case CS_MONEY4_TYPE: datafmt[i].maxlength = 24; - numerics[i] = 0; + numerics[i] = 2; break; case CS_DATETIME_TYPE: case CS_DATETIME4_TYPE: datafmt[i].maxlength = 30; numerics[i] = 0; - break; + break; case CS_NUMERIC_TYPE: case CS_DECIMAL_TYPE: datafmt[i].maxlength = datafmt[i].precision + 3; - numerics[i] = 1; + numerics[i] = (datafmt[i].scale == 0) ? 1 : 2; // numeric(10) vs numeric(10, 1) break; - default: + default: datafmt[i].maxlength++; numerics[i] = 0; break; @@ -1004,11 +1092,20 @@ result->data[i] = (pval *) emalloc(sizeof(pval)*num_fields); for (j=0; j<num_fields; j++) { if (indicators[j] == -1) { /* null value */ - ZVAL_FALSE(&result->data[i][j]); + ZVAL_NULL(&result->data[i][j]); } else { Z_STRLEN(result->data[i][j]) = lengths[j]-1; /* we don't need the NULL in the length */ Z_STRVAL(result->data[i][j]) = estrndup(tmp_buffer[j], lengths[j]); Z_TYPE(result->data[i][j]) = IS_STRING; + + /* Here we go, i want those types!:-) + * Perhaps there is a nicer way of doing this, instead of making strings first + * and then converting them back, but I'm a Zend-API-lamer. + */ + switch (numerics[j]) { + case 1: convert_to_long(&result->data[i][j]); break; + case 2: convert_to_double(&result->data[i][j]); numerics[j]= 1; break; + } } } } @@ -1021,7 +1118,7 @@ j=0; for (i=0; i<num_fields; i++) { char computed_buf[16]; - + if (datafmt[i].namelen>0) { result->fields[i].name = estrndup(datafmt[i].name, datafmt[i].namelen); } else { @@ -1133,7 +1230,6 @@ */ if (ct_results(sybase_ptr->cmd, &restype)!=CS_SUCCEED) { ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_ALL); - sybase_ptr->dead = 1; RETURN_FALSE; } @@ -1192,9 +1288,9 @@ case CS_CURSOR_RESULT: case CS_PARAM_RESULT: case CS_ROW_RESULT: - /* Unexpected results, cancel them. */ case CS_STATUS_RESULT: - ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_CURRENT); + /* Discard... */ + // ct_cancel(NULL, sybase_ptr->cmd, CS_CANCEL_CURRENT); break; default: @@ -1371,8 +1467,9 @@ { pval *sybase_result_index; sybase_result *result; - int i; + int i, j; pval *tmp; + char name[32]; if (ZEND_NUM_ARGS()!=1 || getParameters(ht, 1, &sybase_result_index)==FAILURE) { WRONG_PARAM_COUNT; @@ -1388,6 +1485,7 @@ RETURN_FALSE; } + j= 1; for (i=0; i<result->num_fields; i++) { ALLOC_ZVAL(tmp); *tmp = result->data[result->cur_row][i]; @@ -1399,20 +1497,76 @@ } zend_hash_index_update(Z_ARRVAL_P(return_value), i, (void *) &tmp, sizeof(pval *), NULL); tmp->refcount++; - zend_hash_update(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1, (void *) &tmp, sizeof(pval *), NULL); + if (zend_hash_exists(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1)) { + snprintf(name, 32, "%s%d", result->fields[i].name, j); + j++; + zend_hash_update(Z_ARRVAL_P(return_value), name, strlen(name)+1, (void *) &tmp, sizeof(pval *), NULL); + } else { + zend_hash_update(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1, (void *) &tmp, sizeof(pval *), NULL); + } } result->cur_row++; } +static void php_sybase_fetch_assoc(INTERNAL_FUNCTION_PARAMETERS) +{ + pval *sybase_result_index; + sybase_result *result; + int i, j; + pval *tmp; + char name[32]; + + if (ZEND_NUM_ARGS()!=1 || getParameters(ht, 1, &sybase_result_index)==FAILURE) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(result, sybase_result *, &sybase_result_index, -1, "Sybase result", le_result); + + if (result->cur_row >= result->num_rows) { + RETURN_FALSE; + } + + if (array_init(return_value)==FAILURE) { + RETURN_FALSE; + } + + j= 1; + for (i=0; i<result->num_fields; i++) { + ALLOC_ZVAL(tmp); + *tmp = result->data[result->cur_row][i]; + INIT_PZVAL(tmp); + if (PG(magic_quotes_runtime) && Z_TYPE_P(tmp) == IS_STRING) { + Z_STRVAL_P(tmp) = php_addslashes(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &Z_STRLEN_P(tmp), 0 TSRMLS_CC); + } else { + pval_copy_constructor(tmp); + } + if (zend_hash_exists(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1)) { + snprintf(name, 32, "%s%d", result->fields[i].name, j); + j++; + zend_hash_update(Z_ARRVAL_P(return_value), name, strlen(name)+1, (void *) &tmp, sizeof(pval *), NULL); + } else { + zend_hash_update(Z_ARRVAL_P(return_value), result->fields[i].name, strlen(result->fields[i].name)+1, (void *) &tmp, sizeof(pval *), NULL); + } + } + result->cur_row++; +} /* {{{ proto object sybase_fetch_object(int result) Fetch row as object */ PHP_FUNCTION(sybase_fetch_object) { - php_sybase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU); + php_sybase_fetch_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU); if (Z_TYPE_P(return_value)==IS_ARRAY) { object_and_properties_init(return_value, ZEND_STANDARD_CLASS_DEF_PTR, Z_ARRVAL_P(return_value)); } +} +/* }}} */ + +/* {{{ proto array sybase_fetch_assoc(int result) + Fetch row as array */ +PHP_FUNCTION(sybase_fetch_assoc) +{ + php_sybase_fetch_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */Sorry, here's an example script for sybase_set_message_handler(): <?php function sybase_error($msgnumber, $severity, $state, $line, $text) { if (5701 == $msgnumber) return; // Changed database context to ... if (257 == $msgnumber) return FALSE; // Implicit conversion... printf( "Caught Sybase Server Message #%d [Severity %d, state %d] at line %d\n'%s'\n\n", $msgnumber, $severity, $state, $line, chop($text) ); } sybase_set_message_handler('sybase_error'); $dbh= sybase_connect([...]); // Erroneous call sybase_query('select @does_not_exist'); // Another erroneous call, assume foo_id is numeric(10) // This will result in an "...implicit conversion..." // which is not handled by the above function sybase_query('select * from foo where foo_id= "1"'); ?> The output is: -------------------------------------------------------- Caught Sybase Server Message #137 [Severity 15, state 2] at line 1 'Must declare variable '@does_not_exist'.' <br /> <b>Warning</b>: Sybase: Server message #257: Implicit conversion from datatype 'VARCHAR' to 'NUMERIC' is not allowed. Use the CONVERT function to run this query. (severity 16, procedure N/A) in <b>/usr/home/thekid/devel/php/sybase_test.php</b> on line <b>23</b><br /> --------------------------------------------------------