⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mysqlnd_ps.c

📁 linux下安装不上mysql5与php5的可用此关联
💻 C
📖 第 1 页 / 共 3 页
字号:
/*  +----------------------------------------------------------------------+  | PHP Version 6                                                        |  +----------------------------------------------------------------------+  | Copyright (c) 2006-2007 The PHP Group                                |  +----------------------------------------------------------------------+  | This source file is subject to version 3.01 of the PHP license,      |  | that is bundled with this package in the file LICENSE, and is        |  | available through the world-wide-web at the following url:           |  | http://www.php.net/license/3_01.txt                                  |  | If you did not receive a copy of the PHP license and are unable to   |  | obtain it through the world-wide-web, please send a note to          |  | license@php.net so we can mail you a copy immediately.               |  +----------------------------------------------------------------------+  | Authors: Georg Richter <georg@mysql.com>                             |  |          Andrey Hristov <andrey@mysql.com>                           |  |          Ulf Wendel <uwendel@mysql.com>                              |  +----------------------------------------------------------------------+*//* $Id: header,v 1.17 2006/01/01 13:09:48 sniper Exp $ */#include "php.h"#include "mysqlnd.h"#include "mysqlnd_wireprotocol.h"#include "mysqlnd_priv.h"#include "mysqlnd_statistics.h"#define MYSQLND_SILENTconst char * const mysqlnd_not_bound_as_blob = "Can't send long data for non-string/non-binary data types";const char * const mysqlnd_stmt_not_prepared = "Statement not prepared";/* Exported by mysqlnd.c */enum_func_status mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command,										const char * const arg, size_t arg_len,										enum php_mysql_packet_type ok_packet,										zend_bool silent TSRMLS_DC);/* Exported by mysqlnd_ps_codec.c */zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len,												  zend_bool *free_buffer);MYSQLND_RES * _mysqlnd_stmt_use_result(MYSQLND_STMT *stmt TSRMLS_DC);enum_func_status mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param,												unsigned int flags,												zend_bool *fetched_anything TSRMLS_DC);enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param,											   unsigned int flags,											   zend_bool *fetched_anything TSRMLS_DC);void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt);/* {{{ _mysqlnd_stmt_store_result */staticMYSQLND_RES * _mysqlnd_stmt_store_result(MYSQLND_STMT * const stmt TSRMLS_DC){	enum_func_status ret;	MYSQLND *conn = stmt->conn;	int next_extend = 32, free_rows;	php_mysql_packet_row row_packet;	MYSQLND_RES *result;	zend_bool to_cache = FALSE;	/* be compliant with libmysql - NULL will turn */	if (!stmt->field_count) {		return NULL;	}	if (stmt->cursor_exists) {		/* Silently convert buffered to unbuffered, for now */		return _mysqlnd_stmt_use_result(stmt TSRMLS_CC);	}	/* Nothing to store for UPSERT/LOAD DATA*/	if (conn->state != CONN_FETCHING_DATA ||		stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)	{		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,						 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); 		return NULL;	}	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_PS_BUFFERED_SETS);	result = stmt->result;	result->type			= MYSQLND_RES_PS;	result->m.fetch_row		= mysqlnd_fetch_stmt_row_buffered;	result->m.fetch_lengths	= NULL;/* makes no sense */	result->zval_cache		= NULL;	/* Create room for 'next_extend' rows */	/* Not set for SHOW statements at PREPARE stage */	if (result->conn) {		result->conn->m->free_reference(result->conn TSRMLS_CC);		result->conn = NULL;	/* store result does not reference  the connection */	}	result->data			= pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache);	result->data->data		= pemalloc(next_extend * sizeof(zval **), to_cache);	result->data->row_buffers = pemalloc(next_extend * sizeof(zend_uchar *), to_cache);	result->data->persistent= to_cache;	free_rows = next_extend;	PACKET_INIT_ALLOCA(row_packet, PROT_ROW_PACKET);	row_packet.field_count = stmt->field_count;	row_packet.binary_protocol = TRUE;	row_packet.fields_metadata = stmt->result->meta->fields;	/* Let the row packet fill our buffer and skip additional malloc + memcpy */	while (FAIL != (ret = PACKET_READ_ALLOCA(row_packet, conn)) && !row_packet.eof) {		int i;		zval **current_row;		if (!free_rows) {			unsigned long total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend with 33% */			total_rows += result->data->row_count;			result->data->data = perealloc(result->data->data, total_rows * sizeof(zval **), to_cache);			result->data->row_buffers = perealloc(result->data->row_buffers,												  total_rows * sizeof(zend_uchar *), to_cache);		}		free_rows--;		current_row = result->data->data[result->data->row_count] = row_packet.fields;		result->data->row_buffers[result->data->row_count] = row_packet.row_buffer;		result->data->row_count++;		/* So row_packet's destructor function won't efree() it */		row_packet.fields = NULL;		row_packet.row_buffer = NULL;		if (stmt->update_max_length == TRUE) {			for (i = 0; i < row_packet.field_count; i++) {				if (Z_TYPE_P(current_row[i]) >= IS_STRING) {					unsigned long len = Z_STRLEN_P(current_row[i]);					if (result->meta->fields[i].max_length < len) {						result->meta->fields[i].max_length = len;					}				}			}		}		/*		  No need to FREE_ALLOCA as we can reuse the		  'lengths' and 'fields' arrays. For lengths its absolutely safe.		  'fields' is reused because the ownership of the strings has been		  transfered above. 		*/	}	/* Finally clean */	if (row_packet.eof) {		conn->upsert_status.warning_count = row_packet.warning_count;		conn->upsert_status.server_status = row_packet.server_status;	}	PACKET_FREE_ALLOCA(row_packet);	/* We can realloc here to save some memory, if free_rows > 0 ?*/	if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {		conn->state = CONN_NEXT_RESULT_PENDING;	} else {		conn->state = CONN_READY;	}	if (PASS == ret) {		/* Position at the first row */		result->data->data_cursor = result->data->data;		stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;		/* libmysql API docs say it should be so for SELECT statements */		stmt->upsert_status.affected_rows =			stmt->conn->upsert_status.affected_rows =				stmt->result->data->row_count;	} else {		mysqlnd_internal_free_result_contents(stmt->result TSRMLS_CC);		efree(stmt->result);		stmt->result = NULL;		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pretty serious error");		stmt->state = MYSQLND_STMT_PREPARED;	}	conn->state = CONN_READY;	return result;}/* }}} *//* {{{ mysqlnd_stmt_skip_metadata */static enum_func_statusmysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC){	/* Follows parameter metadata, we have just to skip it, as libmysql does */	unsigned int i = 0;	php_mysql_packet_res_field field_packet;	PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET);	field_packet.skip_parsing = TRUE;	for (;i < stmt->param_count; i++) {		if (FAIL == PACKET_READ_ALLOCA(field_packet, stmt->conn)) {			PACKET_FREE_ALLOCA(field_packet);			return FAIL;		}	}	PACKET_FREE_ALLOCA(field_packet);	return PASS;}/* }}} *//* {{{ mysqlnd_stmt_read_prepare_response */static enum_func_statusmysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC){	php_mysql_packet_prepare_response prepare_resp;	PACKET_INIT_ALLOCA(prepare_resp, PROT_PREPARE_RESP_PACKET);	if (FAIL == PACKET_READ_ALLOCA(prepare_resp, stmt->conn)) {		PACKET_FREE_ALLOCA(prepare_resp);		return FAIL;	}	if (0xFF == prepare_resp.error_code) {		stmt->error_info = stmt->conn->error_info = prepare_resp.error_info;		return FAIL;	}	stmt->stmt_id = prepare_resp.stmt_id;	stmt->warning_count = stmt->conn->upsert_status.warning_count = prepare_resp.warning_count;	stmt->field_count = stmt->conn->field_count = prepare_resp.field_count;	stmt->param_count = prepare_resp.param_count;	PACKET_FREE_ALLOCA(prepare_resp);	return PASS;}/* }}} *//* {{{ mysqlnd_stmt_prepare_read_eof */static enum_func_statusmysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC){	php_mysql_packet_eof fields_eof;	enum_func_status ret;	PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET);	if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, stmt->conn))) {		if (stmt->result) {			mysqlnd_internal_free_result_contents(stmt->result TSRMLS_CC);			efree(stmt->result);			memset(stmt, 0, sizeof(MYSQLND_STMT));			stmt->state = MYSQLND_STMT_INITTED;		}	} else {		stmt->upsert_status.server_status = fields_eof.server_status;		stmt->upsert_status.warning_count = fields_eof.warning_count;		stmt->state = MYSQLND_STMT_PREPARED;	}	PACKET_FREE_ALLOCA(fields_eof);	return ret;}/* }}} *//* {{{ _mysqlnd_stmt_prepare */static enum_func_status_mysqlnd_stmt_prepare(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC){	MYSQLND_STMT *stmt_to_prepare = stmt;	SET_ERROR_AFF_ROWS(stmt);	SET_ERROR_AFF_ROWS(stmt->conn);	if (stmt->state > MYSQLND_STMT_INITTED) {		/* See if we have to clean the wire */		if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {			/* Do implicit use_result and then flush the result */			stmt->default_rset_handler = _mysqlnd_stmt_use_result;			stmt->default_rset_handler(stmt TSRMLS_CC);		}		/* No 'else' here please :) */		if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {			stmt->result->m.skip_result(stmt->result TSRMLS_CC);		}		/*		  Create a new test statement, which we will prepare, but if anything		  fails, we will scrap it.		*/		stmt_to_prepare = mysqlnd_stmt_init(stmt->conn);	}	if (FAIL == mysqlnd_simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, query,									   query_len, PROT_LAST, FALSE TSRMLS_CC) ||		FAIL == mysqlnd_stmt_read_prepare_response(stmt_to_prepare TSRMLS_CC)) {		goto fail;	}	SET_EMPTY_ERROR(stmt->error_info);	SET_EMPTY_ERROR(stmt->conn->error_info);	if (stmt_to_prepare->param_count) {		if (FAIL == mysqlnd_stmt_skip_metadata(stmt_to_prepare TSRMLS_CC) ||			FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC))		{			goto fail;		}	}	/*	  Read metadata only if there is actual result set.	  Beware that SHOW statements bypass the PS framework and thus they send	  no metadata at prepare.	*/	if (stmt_to_prepare->field_count) {		MYSQLND_RES *result = NULL;		/* Allocate the result now as it is needed for the reading of metadata */		stmt_to_prepare->result = result = mysqlnd_result_init(stmt_to_prepare->field_count, NULL);		result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn);		result->type = MYSQLND_RES_PS;		if (FAIL == mysqlnd_read_result_metadata(stmt_to_prepare->conn, result TSRMLS_CC) ||			FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC)) {			goto fail;		}	}	if (stmt_to_prepare != stmt) {		/* Free old buffers, binding and resources on server */		stmt->m->close(stmt, TRUE TSRMLS_CC);		/* Now we will have a clean new statement object */		memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT));		efree(stmt_to_prepare);	}	stmt->state = MYSQLND_STMT_PREPARED;	return PASS;fail:	if (stmt_to_prepare != stmt) {		stmt_to_prepare->m->dtor(stmt_to_prepare, TRUE TSRMLS_CC);	}	stmt->state = MYSQLND_STMT_INITTED;	return FAIL;}/* }}} *//* {{{ _mysqlnd_stmt_execute */static enum_func_status_mysqlnd_stmt_execute(MYSQLND_STMT * const stmt TSRMLS_DC){	enum_func_status ret;	MYSQLND		*conn = stmt->conn;	zend_uchar	*request;	size_t		request_len;	zend_bool	free_request;	SET_ERROR_AFF_ROWS(stmt);	SET_ERROR_AFF_ROWS(stmt->conn);		if (stmt->state > MYSQLND_STMT_PREPARED && stmt->field_count) {		if (stmt->result_bind &&			stmt->result_zvals_separated_once == TRUE && 			stmt->state >= MYSQLND_STMT_USER_FETCHING)		{			/*			  We need to copy the data from the buffers which we will clean.			  The bound variables point to them only if the user has started			  to fetch data (MYSQLND_STMT_USER_FETCHING).			  We need to check 'result_zvals_separated_once' or we will leak			  in the following scenation			  prepare("select 1 from dual");			  execute();			  fetch(); <-- no binding, but that's not a problem			  bind_result();			  execute(); <-- here we will leak because we separate without need			*/			unsigned int i;			for (i = 0; i < stmt->field_count; i++) {				if (stmt->result_bind[i].bound == TRUE) {					zval_copy_ctor(stmt->result_bind[i].zv);				}			}		}		/*		  Executed, but the user hasn't started to fetch		  This will clean also the metadata, but after the EXECUTE call we will		  have it again.		*/		stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);	} else if (stmt->state < MYSQLND_STMT_PREPARED) {		/* Only initted - error */		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,						 mysqlnd_out_of_sync);		SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);		return FAIL;	}	if (stmt->param_count && !stmt->param_bind) {		SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE,						 "No data supplied for parameters in prepared statement");		return FAIL;	}	request = mysqlnd_stmt_execute_generate_request(stmt, &request_len, &free_request);		/* support for buffer types should be added here ! */	ret = mysqlnd_simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len,								 PROT_LAST /* we will handle the response packet*/,								 FALSE TSRMLS_CC);	if (free_request) {		efree(request);	}	if (ret == FAIL) {		stmt->error_info = conn->error_info;		return FAIL;	}	ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC);	if (ret == FAIL) {		stmt->error_info = conn->error_info;		stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;		if (conn->state == CONN_QUIT_SENT) {			/* close the statement here, the connection has been closed */		}	} else {		SET_EMPTY_ERROR(stmt->error_info);		SET_EMPTY_ERROR(stmt->conn->error_info);		stmt->send_types_to_server = 0;		stmt->upsert_status = conn->upsert_status;		stmt->state = MYSQLND_STMT_EXECUTED;		if (conn->last_query_type == QUERY_UPSERT) {			stmt->upsert_status = conn->upsert_status;			return PASS;		} else if (conn->last_query_type == QUERY_LOAD_LOCAL) {			return PASS;		}		stmt->result->type = MYSQLND_RES_PS;		if (!stmt->result->conn) {			/*			  For SHOW we don't create (bypasses PS in server)			  a result set at prepare and thus a connection was missing			*/			stmt->result->conn = stmt->conn->m->get_reference(stmt->conn);		}		/* Update stmt->field_count as SHOW sets it to 0 at prepare */		stmt->field_count = stmt->result->field_count = conn->field_count;		stmt->result->lengths = NULL;		if (stmt->field_count) {			stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;			/*			  We need to set this because the user might not call			  use_result() or store_result() and we should be able to scrap the			  data on the line, if he just decides to close the statement.			*/			if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) {				stmt->cursor_exists = TRUE;				conn->state = CONN_READY;				/* Only cursor read */				stmt->default_rset_handler = _mysqlnd_stmt_use_result;			} else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {				/*				  We have asked for CURSOR but got no cursor, because the condition				  above is not fulfilled. Then...				  This is a single-row result set, a result set with no rows, EXPLAIN,				  SHOW VARIABLES, or some other command which either a) bypasses the				  cursors framework in the server and writes rows directly to the				  network or b) is more efficient if all (few) result set rows are				  precached on client and server's resources are freed.				*/				/* preferred is buffered read */				stmt->default_rset_handler = _mysqlnd_stmt_store_result;			} else {				/* preferred is unbuffered read */				stmt->default_rset_handler = _mysqlnd_stmt_use_result;			}		}	}	return ret;}/* }}} *//* {{{ mysqlnd_fetch_stmt_row_buffered */enum_func_status

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -