List:Commits« Previous MessageNext Message »
From:ahristov Date:March 3 2007 5:00pm
Subject:PHP mysqlnd svn commit: r86 - trunk/ext/mysqli/mysqlnd
View as plain text  
Author: ahristov
Date: 2007-03-03 18:00:33 +0100 (Sat, 03 Mar 2007)
New Revision: 86

Modified:
   trunk/ext/mysqli/mysqlnd/mysqlnd.c
   trunk/ext/mysqli/mysqlnd/mysqlnd.h
   trunk/ext/mysqli/mysqlnd/mysqlnd_ps.c
   trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c
   trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.h
Log:
Move things around.

Workaround protocol design error for COM_STMT_SEND_LONG_DATA
(bug #26824)



Modified: trunk/ext/mysqli/mysqlnd/mysqlnd.c
===================================================================
--- trunk/ext/mysqli/mysqlnd/mysqlnd.c	2007-03-03 02:55:30 UTC (rev 85)
+++ trunk/ext/mysqli/mysqlnd/mysqlnd.c	2007-03-03 17:00:33 UTC (rev 86)
@@ -28,23 +28,27 @@
 #define MYSQLND_SILENT
 
 
-extern MYSQLND_CHARSET *mysqlnd_charsets;
+/*
+  TODO :
+  - Don't bind so tightly the metadata with the result set. This means
+    that the metadata reading should not expect a MYSQLND_RES pointer, it
+	does not need it, but return a pointer to the metadata (MYSQLND_FIELD *).
+	For normal statements we will then just assign it to a member of
+	MYSQLND_RES. For PS statements, it will stay as part of the statement
+	(MYSQLND_STMT) between prepare and execute. At execute the new metadata
+	will be sent by the server, so we will discard the old one and then
+	finally attach it to the result set. This will make the code more clean,
+	as a prepared statement won't have anymore stmt->result != NULL, as it
+	is now, just to have where to store the metadata.
 
+ - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING
+   terminated by a string with ptr being NULL. Thus, multi-part messages can be
+   sent to the network like writev() and this can save at least for
+   mysqlnd_stmt_send_long_data() new malloc. This change will probably make the
+   code in few other places cleaner.
+*/
 
-/* {{{ mysqlnd_command_to_text 
- */
-static
-const char * const mysqlnd_command_to_text[COM_END] =
-{
-  "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
-  "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
-  "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
-  "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
-  "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
-  "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
-  "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON"
-};
-/* }}} */
+extern MYSQLND_CHARSET *mysqlnd_charsets;
 
 
 
@@ -499,9 +503,6 @@
 		cmd_packet.arg_len  = arg_len;
 	}
 
-	/* Reset packet_no, or we will get bad handshake! */
-	conn->net.packet_no = 0;
-
 	if (! PACKET_WRITE_ALLOCA(cmd_packet, conn)) {
 		if (!silent) {
 			php_error(E_WARNING, "Error while sending %s packet",
mysqlnd_command_to_text[command]);

Modified: trunk/ext/mysqli/mysqlnd/mysqlnd.h
===================================================================
--- trunk/ext/mysqli/mysqlnd/mysqlnd.h	2007-03-03 02:55:30 UTC (rev 85)
+++ trunk/ext/mysqli/mysqlnd/mysqlnd.h	2007-03-03 17:00:33 UTC (rev 86)
@@ -23,7 +23,20 @@
 #ifndef MYSQLND_H
 #define MYSQLND_H
 
-#define MYSQLND_INLINE_OPTIMISATIONS 1
+/* This forces inlining some accessor functions */
+#define MYSQLND_USE_OPTIMISATIONS 1
+/*
+  This force mysqlnd to do a single (or more depending on ammount of data)
+  non-blocking read() calls before sending a command to the server. Useful
+  for debugging, if previous function hasn't consumed all the output sent
+  to it - like stmt_send_long_data() error because the data was larger that
+  max_allowed_packet_size, and COM_STMT_SEND_LONG_DATA by protocol doesn't
+  use response packets, thus letting the next command to fail miserably, if
+  the connector implementor is not aware of this deficiency. Should be off
+  on production systems, if of course measured performance degradation is not
+  minimal.
+*/
+#define MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND 1
 
 
 #include "portability.h"
@@ -455,6 +468,10 @@
 	/* sequence for simple checking of correct packets */
 	zend_uchar		packet_no;
 
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+	zend_uchar		last_command;
+#endif
+
 	/* cmd buffer */
 	MYSQLND_CMD_BUFFER	cmd_buffer;
 } MYSQLND_NET;
@@ -780,7 +797,7 @@
 #define mysqlnd_data_seek(result, row)	(result)->m.seek_data((result), (row))
 
 /*****************************************************************************************************/
-#if MYSQLND_INLINE_OPTIMISATIONS
+#if defined(MYSQLND_USE_OPTIMISATIONS) && MYSQLND_USE_OPTIMISATIONS == 1
 
 /* Errors */
 #define mysqlnd_errno(conn)				(conn)->error_info.error_no
@@ -871,7 +888,7 @@
 #define mysqlnd_stmt_errno(stmt)			(stmt)->m->get_error_no((stmt))
 #define mysqlnd_stmt_error(stmt)			(stmt)->m->get_error_str((stmt))
 #define mysqlnd_stmt_sqlstate(stmt)			(stmt)->m->get_sqlstate((stmt))
-#endif /* MYSQLND_INLINE_OPTIMISATIONS */
+#endif /* MYSQLND_USE_OPTIMISATIONS */
 /*****************************************************************************************************/
 
 
@@ -935,11 +952,12 @@
 void				mysqlnd_palloc_free_cache_reference(MYSQLND_ZVAL_PCACHE **cache);
 void				mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * const cache);
 void				mysqlnd_palloc_rshutdown(MYSQLND_ZVAL_PCACHE * const cache);
+void				mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, zval
*return_value);
 
+/* There two should not be used from outside */
 void *				mysqlnd_palloc_get_zval(MYSQLND_ZVAL_PCACHE * const cache, zend_bool
*allocated);
 void				mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_PCACHE * const cache,
zend_bool ps,
 												 zend_bool *copy_ctor_called);
-void				mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, zval
*return_value);
 #endif	/* MYSQLND_H */
 
 

Modified: trunk/ext/mysqli/mysqlnd/mysqlnd_ps.c
===================================================================
--- trunk/ext/mysqli/mysqlnd/mysqlnd_ps.c	2007-03-03 02:55:30 UTC (rev 85)
+++ trunk/ext/mysqli/mysqlnd/mysqlnd_ps.c	2007-03-03 17:00:33 UTC (rev 86)
@@ -831,6 +831,7 @@
 	MYSQLND * conn = stmt->conn;
 	zend_uchar *cmd_buf;
 	size_t packet_len;
+	enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA;
 	
 
 	if (stmt->state < MYSQLND_STMT_PREPARED || !stmt->param_bind) {
@@ -860,16 +861,41 @@
 		cmd_buf = emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
 
 		int4store(cmd_buf, stmt->stmt_id);
-		int2store(cmd_buf + 4, param_no);
+		int2store(cmd_buf + STMT_ID_LENGTH, param_no);
+		memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length);
 
-		ret = mysqlnd_simple_command(conn, COM_STMT_SEND_LONG_DATA,
-									 (char *)cmd_buf, packet_len,
-									 PROT_LAST /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/,
-									 FALSE TSRMLS_CC);
+		/* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/
+		ret = mysqlnd_simple_command(conn, cmd, (char *)cmd_buf, packet_len,
+									 PROT_LAST , FALSE TSRMLS_CC);
 		efree(cmd_buf);
 		if (FAIL == ret) {
 			stmt->error_info = conn->error_info;
 		}
+		/*
+		  Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not
+		  sent response packets. According to documentation the only way to get an error
+		  is to have out-of-memory on the server-side. However, that's not true, as if
+		  max_allowed_packet_size is smaller than the chunk being sent to the server, the
+		  latter will complain with an error message. However, normally we don't expect
+		  an error message, thus we continue. When sending the next command, which expects
+		  response we will read the unexpected data and error message will look weird.
+		  Therefore we do non-blocking read to clean the line, if there is a need.
+		  Nevertheless, there is a built-in protection when sending a command packet, that
+		  checks if the line is clear - useful for debug purposes and to be switched off
+		  in release builds.
+
+		  Maybe we can make it automatic by checking what's the value of
max_allowed_packet_size on
+		  the server and resending the data.
+		*/
+		if ((packet_len = php_mysqlnd_consume_uneaten_data(conn, cmd TSRMLS_CC))) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error "
+							 "while sending long data. Probably max_allowed_packet_size "
+							 "is smaller than the data. You have to increase it or send "
+							 "smaller chunks of data. Answer was %u bytes long.", packet_len);
+			SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
+							"Server responded to COM_STMT_SEND_LONG_DATA.");
+			ret = FAIL;
+		}
 	}
 
 	return ret;
@@ -931,7 +957,6 @@
 /* }}} */
 
 
-
 /* {{{ _mysqlnd_stmt_bind_result */
 static enum_func_status
 _mysqlnd_stmt_bind_result(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const
result_bind)

Modified: trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c
===================================================================
--- trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c	2007-03-03 02:55:30 UTC (rev 85)
+++ trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c	2007-03-03 17:00:33 UTC (rev 86)
@@ -54,6 +54,21 @@
 static const char	*unknown_sqlstate= "HY000";
 
 
+/* {{{ mysqlnd_command_to_text 
+ */
+const char * const mysqlnd_command_to_text[COM_END] =
+{
+  "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
+  "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
+  "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
+  "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
+  "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
+  "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
+  "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON"
+};
+/* }}} */
+
+
 /* {{{ php_mysqlnd_net_field_length 
    Get next field's length */
 unsigned long php_mysqlnd_net_field_length(zend_uchar **packet)
@@ -100,13 +115,13 @@
 			return (mynd_ulonglong) MYSQLND_NULL_LENGTH;
 		case 252:
 			(*packet) += 3;
-			return (mynd_ulonglong) uint2korr(p+1);
+			return (mynd_ulonglong) uint2korr(p + 1);
 		case 253:
 			(*packet) += 4;
-			return (mynd_ulonglong) uint3korr(p+1);
+			return (mynd_ulonglong) uint3korr(p + 1);
 		default:
 			(*packet) += 9;
-			return (mynd_ulonglong) uint8korr(p+1);
+			return (mynd_ulonglong) uint8korr(p + 1);
 	}
 }
 /* }}} */
@@ -116,28 +131,70 @@
 zend_uchar *php_mysqlnd_net_store_length(zend_uchar *packet, mynd_ulonglong length)
 {
 	if (length < (mynd_ulonglong) L64(251)) {
-		*packet=(zend_uchar) length;
-		return packet+1;
+		*packet = (zend_uchar) length;
+		return packet + 1;
 	}
 
 	if (length < (mynd_ulonglong) L64(65536)) {
-		*packet++= 252;
+		*packet++ = 252;
 		int2store(packet,(uint) length);
-		return packet+2;
+		return packet + 2;
 	}
 
 	if (length < (mynd_ulonglong) L64(16777216)) {
-		*packet++= 253;
+		*packet++ = 253;
 		int3store(packet,(ulong) length);
-		return packet+3;
+		return packet + 3;
 	}
-	*packet++= 254;
-	int8store(packet,length);
-	return  packet+8;
+	*packet++ = 254;
+	int8store(packet, length);
+	return packet + 8;
 }
 /* }}} */
 
 
+/* {{{ php_mysqlnd_consume_uneaten_data */
+size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum
php_mysqlnd_server_command cmd TSRMLS_DC)
+{
+
+	/*
+	  Switch to non-blocking mode and try to consume something from
+	  the line, if possible, then continue. This saves us from looking for
+	  the actuall place where out-of-order packets have been sent.
+	  If someone is completely sure that everything is fine, he can switch it
+	  off.
+	*/
+	char tmp_buf[256];
+	MYSQLND_NET *net = &conn->net;
+	size_t skipped_bytes = 0;
+	int opt = PHP_STREAM_OPTION_BLOCKING;
+	int was_blocked = net->stream->ops->set_option(net->stream, opt, 0, NULL
TSRMLS_CC);
+
+	if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
+		/* Do a read of 1 byte */
+		int bytes_consumed;
+
+		do {
+			skipped_bytes += (bytes_consumed = php_stream_read(net->stream, tmp_buf,
sizeof(tmp_buf)));
+		} while (bytes_consumed == sizeof(tmp_buf));
+
+		if (was_blocked) {
+			net->stream->ops->set_option(net->stream, opt, 1, NULL TSRMLS_CC);
+		}
+
+		if (bytes_consumed) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't
"
+							 "consumed all the output from the server",
+							 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
+		}
+	}
+	net->last_command = cmd;
+
+	return skipped_bytes;
+}
+/* }}} */
+
+
 /* {{{ php_mysqlnd_read_error_from_line */
 static
 enum_func_status php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
@@ -168,6 +225,7 @@
 /* }}} */
 
 
+/* {{{ mysqlnd_set_sock_no_delay */
 int mysqlnd_set_sock_no_delay(php_stream *stream)
 {
 	int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
@@ -180,6 +238,7 @@
 
 	return ret;
 }
+/* }}} */
 
 
 /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
@@ -197,7 +256,7 @@
   `count` is actually the length of the payload data. Thus :
   count + MYSQLND_HEADER_SIZE = sizeof(buf) (not the pointer but the actual buffer)
 */
-size_t mysqlnd_stream_write_w_header(MYSQLND *conn, char * const buf, size_t count
TSRMLS_DC)
+size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count
TSRMLS_DC)
 {
 	zend_uchar safe_storage[MYSQLND_HEADER_SIZE];
 	MYSQLND_NET *net = &conn->net;
@@ -240,9 +299,10 @@
 /* }}} */
 
 
+/* {{{ mysqlnd_stream_write_w_command */
 #if USE_CORK && defined(TCP_CORK)
 static
-size_t mysqlnd_stream_write_w_command(MYSQLND *conn, enum php_mysqlnd_server_command
command,
+size_t mysqlnd_stream_write_w_command(MYSQLND * const conn, enum
php_mysqlnd_server_command command,
 									  const char * const buf, size_t count TSRMLS_DC)
 {
 	zend_uchar safe_storage[MYSQLND_HEADER_SIZE + 1];
@@ -305,16 +365,17 @@
 	return ret;
 }
 #endif
+/* }}} */
 
 
 /* {{{ mysqlnd_read_header */
 static enum_func_status
 mysqlnd_read_header(MYSQLND *conn, mysqlnd_packet_header *header TSRMLS_DC)
 {
+	MYSQLND_NET *net = &conn->net;
 	char buffer[MYSQLND_HEADER_SIZE];
 	char *p = buffer;
 	int to_read = MYSQLND_HEADER_SIZE, ret;
-	MYSQLND_NET *net = &conn->net;
 
 	do {
 		if (!(ret= php_stream_read(net->stream, p, to_read))) {
@@ -475,7 +536,7 @@
 
 /* {{{ php_mysqlnd_crypt */
 static
-void php_mysqlnd_crypt(unsigned char *buffer, const unsigned char *s1, const unsigned
char *s2, size_t len)
+void php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2,
size_t len)
 {
 	const unsigned char *s1_end= s1 + len;
 	while (s1 < s1_end) {
@@ -486,8 +547,7 @@
 
 
 /* {{{ php_mysqlnd_scramble */
-void php_mysqlnd_scramble(unsigned char *buffer, const unsigned char *scramble,
-						  const unsigned char *password)
+void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble,
const zend_uchar * const password)
 {
 	PHP_SHA1_CTX context;
 	unsigned char sha1[SHA1_MAX_LENGTH];
@@ -651,7 +711,6 @@
 /* }}} */
 
 
-
 /* {{{ php_mysqlnd_eof_read */
 static enum_func_status
 php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC)
@@ -693,6 +752,16 @@
 	php_mysql_packet_command *packet= (php_mysql_packet_command *) _packet;
 	MYSQLND_NET *net = &conn->net;
 
+	/*
+	  Reset packet_no, or we will get bad handshake!
+	  Every command starts a new TX and packet numbers are reset to 0.
+	*/
+	net->packet_no = 0;
+
+#if MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+	php_mysqlnd_consume_uneaten_data(conn, packet->command TSRMLS_CC);
+#endif
+
 	if (!packet->argument || !packet->arg_len) {
 		char buffer[MYSQLND_HEADER_SIZE + 1];
 
@@ -704,7 +773,6 @@
 #else
 		size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret;
 		zend_uchar *tmp, *p;
-
 		tmp = (tmp_len > net->cmd_buffer.length)?
emalloc(tmp_len):net->cmd_buffer.buffer;
 		p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
 

Modified: trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.h
===================================================================
--- trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.h	2007-03-03 02:55:30 UTC (rev 85)
+++ trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.h	2007-03-03 17:00:33 UTC (rev 86)
@@ -107,6 +107,8 @@
 	COM_END
 };
 
+extern const char * const mysqlnd_command_to_text[COM_END];
+
 /* Low-level extraction functionality */
 typedef struct st_mysqlnd_packet_methods {
 	size_t				struct_size;
@@ -291,14 +293,14 @@
 } php_mysql_packet_chg_user_resp;
 
 
-size_t mysqlnd_stream_write(MYSQLND *conn, char * const buf, size_t count TSRMLS_DC);
-size_t mysqlnd_stream_write_w_header(MYSQLND *conn, char * const buf, size_t count
TSRMLS_DC);
+size_t mysqlnd_stream_write(MYSQLND * const conn, char * const buf, size_t count
TSRMLS_DC);
+size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count
TSRMLS_DC);
 
+size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum
php_mysqlnd_server_command cmd TSRMLS_DC);
 
-void php_mysqlnd_scramble(unsigned char *buffer, const unsigned char *scramble,
-						  const unsigned char *password);
+void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble,
const zend_uchar * const pass);
 
-unsigned long php_mysqlnd_net_field_length(zend_uchar **packet);
-zend_uchar *php_mysqlnd_net_store_length(zend_uchar *packet, mynd_ulonglong length);
+unsigned long	php_mysqlnd_net_field_length(zend_uchar **packet);
+zend_uchar *	php_mysqlnd_net_store_length(zend_uchar *packet, mynd_ulonglong length);
 
 #endif /* MYSQLND_WIREPROTOCOL_H */

Thread
PHP mysqlnd svn commit: r86 - trunk/ext/mysqli/mysqlndahristov3 Mar