ID: 16960 Updated by: [EMAIL PROTECTED] Reported By: [EMAIL PROTECTED] Status: Open Bug Type: Feature/Change Request Operating System: All PHP Version: 4.0CVS-2002-05-02 New Comment:
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); } /* }}} */ Previous Comments: ------------------------------------------------------------------------ [2002-05-02 05:37:17] [EMAIL PROTECTED] First of all: I'm putting this in here since my mail to [EMAIL PROTECTED] 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; + } } } } ------------------------------------------------------------------------ -- Edit this bug report at http://bugs.php.net/?id=16960&edit=1