php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #16960 [Sybase-CT] sybase_fetch_row() and types
Submitted: 2002-05-02 05:37 UTC Modified: 2004-03-02 18:16 UTC
From: thekid at thekid dot de Assigned: thekid (profile)
Status: Closed Package: Feature/Change Request
PHP Version: 4.0CVS-2002-05-02 OS: All
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please !
Your email address:
MUST BE VALID
Solve the problem:
46 + 27 = ?
Subscribe to this entry?

 
 [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;
+				}
 			}
 		}
 	}

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2002-05-04 14:26 UTC] thekid at thekid dot de
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);
 }
 /* }}} */
 [2002-06-10 15:50 UTC] thekid at thekid dot de
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);
 }
 /* }}} */
 [2002-06-10 18:43 UTC] thekid at thekid dot de
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 />
--------------------------------------------------------
 [2002-11-05 15:07 UTC] thekid@php.net
This bug has been fixed in CVS.

In case this was a PHP problem, snapshots of the sources are packaged
every three hours; this change will be in the next snapshot. You can
grab the snapshot at http://snaps.php.net/.
 
In case this was a documentation problem, the fix will show up soon at
http://www.php.net/manual/.

In case this was a PHP.net website problem, the change will show
up on the PHP.net site and on the mirror sites in short time.
 
Thank you for the report, and for helping us make PHP better.

Committed numerous changes to ext/sybase_ct, see CVS log for ext/sybase_ct/php_sybase_ct.c and ext/sybase_ct/php_sybase_ct.h
 [2003-11-17 10:43 UTC] alex dot bazan at bcn dot concatel dot com
Hello, I was recently shocked when I discovered the implementation of
Sybase "identical fields".

The PHP manual for sybase_fetch_array() reads:

<snip>
Note: When selecting fields with identical names (for instance, in a
join),
the associative indices will have a sequential number prepended. See
the
example for details.
</snip>

I would like to say that I am against implementing these sort of
repetitive
field returns.  If a programmer wants multiple (identically named)
fields
returned, they can always modify their SQL statement to reflect unique
field names:

select t1.id as id1, t2.id as id2 from t1,t2

PHP should NOT be mangling field names returned from the database
driver,
it should simply return the name-value of the LAST field of any given
name.

However, if this is to be kept in the code, there are a few important
considerations that should be taken into account regarding the
implementation:

1st. Upgading PHP. There are loads of cases where you can find in a
database different tables with some identical field names. I know this
is
poor database design, but sometimes you find that you have to work with
a
design where this occurs. With this new feature on sybase_fetch_array()
all
the code where one just assumed that the returned value for field X was
from the last table selected, MUST BE RE-WRITTEN.

2nd. IMHO, the implementation of this feature is very poor. When you
have a
JOIN over two tables and they have more that one field with the same
name:

table A:
id
date
user
afield

table B:
id
date
user
bfield

the resulting join will result in

id
date
user
id1
date2
user3
afield
bfield

A single numeric index is incremented for all repetitive fields, making
it
impossible to make it dynamic. (if you made a select using different
fields
you will get different field names!!!)
I suggest correcting this implementation to return field names as
follows:

id
date
user
id1
date1
user1
afield
bfield

One counter for each repeated field instead of one unique counter for
ALL
fields.

3rd. Last but not least... This is a feature only implemented in Sybase
library functions, so when creating cross-db applications, it is much
more
difficult to implement generic code.

I would appreciate hearing from the PHP Team about this.
Thanks in advance,

Alex Bazan
 [2004-03-02 18:16 UTC] thekid@php.net
Regarding the new behaviour of sybase_fetch_array() with multiple keys, use the following patch to accomplish pre-4.3.0 behaviour:

  http://sitten-polizei.de/php/sybase_bc.patch
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Apr 24 20:01:32 2024 UTC