From 35a201cc8ef0c3f5b2df88d2e528aabee1048348 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 30 Apr 2021 18:47:09 +0200 Subject: Initial/Final commit --- mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c | 1921 ++++++++++++++++++++++ 1 file changed, 1921 insertions(+) create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c (limited to 'mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c') diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c new file mode 100644 index 0000000..a978e63 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c @@ -0,0 +1,1921 @@ +/**************************************************************************** + Copyright (C) 2012 Monty Program AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net + *****************************************************************************/ + +/* The implementation for prepared statements was ported from PHP's mysqlnd + extension, written by Andrey Hristov, Georg Richter and Ulf Wendel + + Original file header: + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2011 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 | + | Andrey Hristov | + | Ulf Wendel | + +----------------------------------------------------------------------+ + */ + +#include "my_global.h" +#include +#include +#include +#include +#include "mysql.h" +#include "mysql_priv.h" +#include "mysql_version.h" +#include "mysqld_error.h" +#include "errmsg.h" +#include +#include +#include +#include +#include + +#define MADB_RESET_ERROR 1 +#define MADB_RESET_LONGDATA 2 +#define MADB_RESET_SERVER 4 +#define MADB_RESET_BUFFER 8 +#define MADB_RESET_STORED 16 + +#define MAX_TIME_STR_LEN 13 +#define MAX_DATE_STR_LEN 5 +#define MAX_DATETIME_STR_LEN 12 + +typedef struct +{ + MEM_ROOT fields_alloc_root; +} MADB_STMT_EXTENSION; + +static my_bool is_not_null= 0; +static my_bool is_null= 1; + +my_bool mthd_supported_buffer_type(enum enum_field_types type) +{ + switch (type) { + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_YEAR: + return 1; + break; + default: + return 0; + break; + } +} + +static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags); + +static int stmt_unbuffered_eof(MYSQL_STMT *stmt, uchar **row) +{ + return MYSQL_NO_DATA; +} + +static int stmt_unbuffered_fetch(MYSQL_STMT *stmt, uchar **row) +{ + ulong pkt_len; + + DBUG_ENTER("stmt_unbuffered_fetch"); + + pkt_len= net_safe_read(stmt->mysql); + DBUG_PRINT("info",("packet_length= %ld",pkt_len)); + + if (pkt_len == packet_error) + { + stmt->fetch_row_func= stmt_unbuffered_eof; + DBUG_RETURN(1); + } + + if (stmt->mysql->net.read_pos[0] == 254) + { + *row = NULL; + stmt->fetch_row_func= stmt_unbuffered_eof; + DBUG_RETURN(MYSQL_NO_DATA); + } + else + *row = stmt->mysql->net.read_pos; + stmt->result.rows++; + DBUG_RETURN(0); +} + +static int stmt_buffered_fetch(MYSQL_STMT *stmt, uchar **row) +{ + if (!stmt->result_cursor) + { + *row= NULL; + stmt->state= MYSQL_STMT_FETCH_DONE; + return MYSQL_NO_DATA; + } + stmt->state= MYSQL_STMT_USER_FETCHING; + *row= (uchar *)stmt->result_cursor->data; + + stmt->result_cursor= stmt->result_cursor->next; + return 0; +} + +int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) +{ + MYSQL_DATA *result= &stmt->result; + MYSQL_ROWS *current, **pprevious; + ulong packet_len; + unsigned char *p; + + DBUG_ENTER("stmt_read_all_rows"); + + pprevious= &result->data; + + while ((packet_len = net_safe_read(stmt->mysql)) != packet_error) + { + p= stmt->mysql->net.read_pos; + if (packet_len > 7 || p[0] != 254) + { + /* allocate space for rows */ + if (!(current= (MYSQL_ROWS *)alloc_root(&result->alloc, sizeof(MYSQL_ROWS) + packet_len))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + current->data= (MYSQL_ROW)(current + 1); + *pprevious= current; + pprevious= ¤t->next; + + /* copy binary row, we will encode it during mysql_stmt_fetch */ + memcpy((char *)current->data, (char *)p, packet_len); + + if (stmt->update_max_length) + { + uchar *null_ptr, bit_offset= 4; + uchar *cp= p; + unsigned int i; + + cp++; /* skip first byte */ + null_ptr= cp; + cp+= (stmt->field_count + 9) / 8; + + for (i=0; i < stmt->field_count; i++) + { + if (!(*null_ptr & bit_offset)) + { + if (mysql_ps_fetch_functions[stmt->fields[i].type].pack_len < 0) + { + /* We need to calculate the sizes for date and time types */ + size_t len= net_field_length(&cp); + switch(stmt->fields[i].type) { + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + stmt->fields[i].max_length= mysql_ps_fetch_functions[stmt->fields[i].type].max_len; + break; + default: + if (len > stmt->fields[i].max_length) + stmt->fields[i].max_length= (ulong)len; + break; + } + cp+= len; + } + else + { + if (!stmt->fields[i].max_length) + stmt->fields[i].max_length= mysql_ps_fetch_functions[stmt->fields[i].type].max_len; + cp+= mysql_ps_fetch_functions[stmt->fields[i].type].pack_len; + } + } + if (!((bit_offset <<=1) & 255)) + { + bit_offset= 1; /* To next byte */ + null_ptr++; + } + } + } + current->length= packet_len; + result->rows++; + } else /* end of stream */ + { + *pprevious= 0; + /* sace status info */ + p++; + stmt->upsert_status.warning_count= stmt->mysql->warning_count= uint2korr(p); + p+=2; + stmt->upsert_status.server_status= stmt->mysql->server_status= uint2korr(p); + stmt->result_cursor= result->data; + DBUG_RETURN(0); + } + } + stmt->result_cursor= 0; + SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, + stmt->mysql->net.last_error); + DBUG_RETURN(1); +} + +static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row) +{ + uchar buf[STMT_ID_LENGTH + 4]; + MYSQL_DATA *result= &stmt->result; + + DBUG_ENTER("stmt_cursor_fetch"); + + if (stmt->state < MYSQL_STMT_USE_OR_STORE_CALLED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + /* do we have some prefetched rows available ? */ + if (stmt->result_cursor) + DBUG_RETURN(stmt_buffered_fetch(stmt, row)); + if (stmt->mysql->server_status & SERVER_STATUS_LAST_ROW_SENT) + stmt->mysql->server_status&= ~SERVER_STATUS_LAST_ROW_SENT; + if (!(stmt->upsert_status.server_status & SERVER_STATUS_LAST_ROW_SENT)) + { + int4store(buf, stmt->stmt_id); + int4store(buf + STMT_ID_LENGTH, stmt->prefetch_rows); + + if (simple_command(stmt->mysql, MYSQL_COM_STMT_FETCH, (char *)buf, sizeof(buf), 1, stmt)) + DBUG_RETURN(1); + + /* free previously allocated buffer */ + free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); + result->data= 0; + result->rows= 0; + + if (stmt->mysql->methods->db_stmt_read_all_rows(stmt)) + DBUG_RETURN(1); + + DBUG_RETURN(stmt_buffered_fetch(stmt, row)); + } + /* no more cursor data available */ + *row= NULL; + DBUG_RETURN(MYSQL_NO_DATA); +} + +void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt) +{ + ulong packet_len; + while ((packet_len = net_safe_read(stmt->mysql)) != packet_error) + if (packet_len < 8 && stmt->mysql->net.read_pos[0] == 254) + return; +} + +int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row) +{ + uint i; + size_t truncations= 0; + unsigned char *null_ptr, bit_offset= 4; + + DBUG_ENTER("stmt_fetch_to_bind"); + + row++; /* skip status byte */ + null_ptr= row; + row+= (stmt->field_count + 9) / 8; + + for (i=0; i < stmt->field_count; i++) + { + /* save row position for fetching values in pieces */ + if (*null_ptr & bit_offset) + { + if (!stmt->bind[i].is_null) + stmt->bind[i].is_null= &stmt->bind[i].is_null_value; + *stmt->bind[i].is_null= 1; + stmt->bind[i].row_ptr= NULL; + } else + { + stmt->bind[i].row_ptr= row; + if (!stmt->bind_result_done || + stmt->bind[i].flags & MADB_BIND_DUMMY) + { + unsigned long length; + + if (mysql_ps_fetch_functions[stmt->fields[i].type].pack_len >= 0) + length= mysql_ps_fetch_functions[stmt->fields[i].type].pack_len; + else + length= net_field_length(&row); + row+= length; + if (!stmt->bind[i].length) + stmt->bind[i].length= &stmt->bind[i].length_value; + *stmt->bind[i].length= stmt->bind[i].length_value= length; + } + else + { + if (!stmt->bind[i].length) + stmt->bind[i].length= &stmt->bind[i].length_value; + if (!stmt->bind[i].is_null) + stmt->bind[i].is_null= &stmt->bind[i].is_null_value; + *stmt->bind[i].is_null= 0; + mysql_ps_fetch_functions[stmt->fields[i].type].func(&stmt->bind[i], &stmt->fields[i], &row); + if (stmt->mysql->options.report_data_truncation) + truncations+= *stmt->bind[i].error; + } + } + + if (!((bit_offset <<=1) & 255)) { + bit_offset= 1; /* To next byte */ + null_ptr++; + } + } + DBUG_RETURN((truncations) ? MYSQL_DATA_TRUNCATED : 0); +} + +MYSQL_RES *_mysql_stmt_use_result(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + + DBUG_ENTER("mysql_stmt_use_result"); + + if (!stmt->field_count || + (!stmt->cursor_exists && mysql->status != MYSQL_STATUS_GET_RESULT) || + (stmt->cursor_exists && mysql->status != MYSQL_STATUS_READY) || + (stmt->state != MYSQL_STMT_WAITING_USE_OR_STORE)) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(NULL); + } + + CLEAR_CLIENT_STMT_ERROR(stmt); + + stmt->state = MYSQL_STMT_USE_OR_STORE_CALLED; + if (!stmt->cursor_exists) + stmt->fetch_row_func= stmt_unbuffered_fetch; //mysql_stmt_fetch_unbuffered_row; + else + stmt->fetch_row_func= stmt_cursor_fetch; + + DBUG_RETURN(NULL); +} + +unsigned char *mysql_net_store_length(unsigned char *packet, size_t length) +{ + if (length < (my_ulonglong) L64(251)) { + *packet = (unsigned char) length; + return packet + 1; + } + + if (length < (my_ulonglong) L64(65536)) { + *packet++ = 252; + int2store(packet,(uint) length); + return packet + 2; + } + + if (length < (my_ulonglong) L64(16777216)) { + *packet++ = 253; + int3store(packet,(ulong) length); + return packet + 3; + } + *packet++ = 254; + int8store(packet, length); + return packet + 8; +} + +int store_param(MYSQL_STMT *stmt, int column, unsigned char **p) +{ + DBUG_ENTER("store_param"); + DBUG_PRINT("info", ("column: %d type: x%x", column, stmt->params[column].buffer_type)); + switch (stmt->params[column].buffer_type) { + case MYSQL_TYPE_TINY: + int1store(*p, *(uchar *)stmt->params[column].buffer); + (*p) += 1; + break; + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + int2store(*p, *(short *)stmt->params[column].buffer); + (*p) += 2; + break; + case MYSQL_TYPE_FLOAT: + float4store(*p, *(float *)stmt->params[column].buffer); + (*p) += 4; + break; + case MYSQL_TYPE_DOUBLE: + float8store(*p, *(double *)stmt->params[column].buffer); + (*p) += 8; + break; + case MYSQL_TYPE_LONGLONG: + int8store(*p, *(my_ulonglong *)stmt->params[column].buffer); + (*p) += 8; + break; + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_INT24: + int4store(*p, *(int32 *)stmt->params[column].buffer); + (*p) += 4; + break; + case MYSQL_TYPE_TIME: + { + /* binary encoding: + Offset Length Field + 0 1 Length + 1 1 negative + 2-5 4 day + 6 1 hour + 7 1 ninute + 8 1 second; + 9-13 4 second_part + */ + MYSQL_TIME *t= (MYSQL_TIME *)stmt->params[column].buffer; + char t_buffer[MAX_TIME_STR_LEN]; + uint len= 0; + memset(t_buffer, 0, MAX_TIME_STR_LEN); + + t_buffer[1]= t->neg ? 1 : 0; + int4store(t_buffer + 2, t->day); + t_buffer[6]= (uchar) t->hour; + t_buffer[7]= (uchar) t->minute; + t_buffer[8]= (uchar) t->second; + if (t->second_part) + { + int4store(t_buffer + 9, t->second_part); + len= 12; + } + else if (t->day || t->hour || t->minute || t->second) + len= 8; + t_buffer[0]= len++; + memcpy(*p, t_buffer, len); + (*p)+= len; + break; + } + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + { + /* binary format for date, timestamp and datetime + Offset Length Field + 0 1 Length + 1-2 2 Year + 3 1 Month + 4 1 Day + 5 1 Hour + 6 1 minute + 7 1 second + 8-11 4 secondpart + */ + MYSQL_TIME *t= (MYSQL_TIME *)stmt->params[column].buffer; + char t_buffer[MAX_DATETIME_STR_LEN]; + uint len= 0; + + memset(t_buffer, 0, MAX_DATETIME_STR_LEN); + + int2store(t_buffer + 1, t->year); + t_buffer[3]= (char) t->month; + t_buffer[4]= (char) t->day; + t_buffer[5]= (char) t->hour; + t_buffer[6]= (char) t->minute; + t_buffer[7]= (char) t->second; + if (t->second_part) + { + int4store(t_buffer + 8, t->second_part); + len= 12; + } + else if (t->hour || t->minute || t->second) + len= 7; + else if (t->year || t->month || t->day) + len= 4; + t_buffer[0]= len++; + memcpy(*p, t_buffer, len); + (*p)+= len; + break; + } + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + { + ulong len= (ulong)*stmt->params[column].length; + /* to is after p. The latter hasn't been moved */ + uchar *to = mysql_net_store_length(*p, len); + DBUG_PRINT("info", ("len=x%x", len)); + if (len) + memcpy(to, stmt->params[column].buffer, len); + (*p) = to + len; + break; + } + + default: + /* unsupported parameter type */ + SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +/* {{{ mysqlnd_stmt_execute_generate_request */ +unsigned char* mysql_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *request_len) +{ + /* execute packet has the following format: + Offset Length Description + ----------------------------------------- + 0 4 Statement id + 4 1 Flags (cursor type) + 5 4 Iteration count + ----------------------------------------- + if (stmt->param_count): + 6 (paramcount+7)/8 null bitmap + ------------------------------------------ + if (stmt->send_types_to_server): + param_count*2 parameter types + ------------------------------------------ + n data from bind_buffer + */ + + size_t length= 1024; + size_t free_bytes= 0; + size_t data_size= 0; + uint i; + + uchar *start= NULL, *p; + + DBUG_ENTER("mysql_stmt_execute_generate_request"); + + /* preallocate length bytes */ + if (!(start= p= (uchar *)my_malloc(length, MYF(MY_WME | MY_ZEROFILL)))) + goto mem_error; + + int4store(p, stmt->stmt_id); + p += STMT_ID_LENGTH; + + /* flags is 4 bytes, we store just 1 */ + int1store(p, (unsigned char) stmt->flags); + p++; + + int1store(p, 1); /* and send 1 for iteration count */ + p+= 4; + + if (stmt->param_count) + { + size_t null_byte_offset, + null_count= (stmt->param_count + 7) / 8; + + free_bytes= length - (p - start); + if (null_count + 20 > free_bytes) + { + size_t offset= p - start; + length+= offset + null_count + 20; + if (!(start= (uchar *)my_realloc((gptr)start, length, MYF(MY_WME | MY_ZEROFILL)))) + goto mem_error; + p= start + offset; + } + + null_byte_offset = p - start; + memset(p, 0, null_count); + p += null_count; + + + int1store(p, stmt->send_types_to_server); + p++; + + free_bytes= length - (p - start); + + /* Store type information: + 2 bytes per type + */ + if (stmt->send_types_to_server) + { + if (free_bytes < stmt->param_count * 2 + 20) + { + size_t offset= p - start; + length= offset + stmt->param_count * 2 + 20; + if (!(start= (uchar *)my_realloc((gptr)start, length, MYF(MY_WME | MY_ZEROFILL)))) + goto mem_error; + p= start + offset; + } + for (i = 0; i < stmt->param_count; i++) + { + /* this differs from mysqlnd, c api supports unsinged !! */ + uint buffer_type= stmt->params[i].buffer_type | (stmt->params[i].is_unsigned ? 32768 : 0); + int2store(p, buffer_type); + p+= 2; + } + } + + /* calculate data size */ + for (i = 0; i < stmt->param_count; i++) { + if (stmt->params[i].buffer && !stmt->params[i].is_null) + stmt->params[i].is_null = &is_not_null; + if (!stmt->params[i].length) + stmt->params[i].length= &stmt->params[i].length_value; + if (!(stmt->params[i].is_null && *stmt->params[i].is_null) && !stmt->params[i].long_data_used) + { + switch (stmt->params[i].buffer_type) { + case MYSQL_TYPE_NULL: + stmt->params[i].is_null = &is_null; + break; + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_SET: + data_size+= 5; /* max 8 bytes for size */ + data_size+= (size_t)*stmt->params[i].length; + break; + default: + data_size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len; + break; + } + } + } + + /* store data */ + free_bytes= length - (p - start); + if (free_bytes < data_size + 20) + { + size_t offset= p - start; + length= offset + data_size + 20; + if (!(start= (uchar *)my_realloc((gptr)start, length, MYF(MY_WME | MY_ZEROFILL)))) + goto mem_error; + p= start + offset; + } + for (i = 0; i < stmt->param_count; i++) + { + if (stmt->params[i].long_data_used) { + stmt->params[i].long_data_used= 0; + } + else { + if (!stmt->params[i].buffer || *stmt->params[i].is_null || stmt->params[i].buffer_type == MYSQL_TYPE_NULL) { + (start + null_byte_offset)[i/8] |= (unsigned char) (1 << (i & 7)); + } else { + DBUG_PRINT("info", ("storing parameter %d at offset %d", i, p - start)); + store_param(stmt, i, &p); + } + } + } + } + stmt->send_types_to_server= 0; + *request_len = (size_t)(p - start); + DBUG_RETURN(start); + + +mem_error: + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + my_free(start); + *request_len= 0; + DBUG_RETURN(NULL); +} +/* }}} */ + +/*! + ******************************************************************************* + + \fn my_ulonglong mysql_stmt_affected_rows + \brief returns the number of affected rows from last mysql_stmt_execute + call + + \param[in] stmt The statement handle + ******************************************************************************* + */ +my_ulonglong STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt) +{ + return stmt->upsert_status.affected_rows; +} + +my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, void *value) +{ + DBUG_ENTER("mysql_stmt_attr_get"); + + switch (attr_type) { + case STMT_ATTR_UPDATE_MAX_LENGTH: + *(my_bool *)value= stmt->update_max_length; + break; + case STMT_ATTR_CURSOR_TYPE: + *(unsigned long *)value= stmt->flags; + break; + case STMT_ATTR_PREFETCH_ROWS: + *(unsigned long *)value= stmt->prefetch_rows; + break; + default: + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, const void *value) +{ + DBUG_ENTER("mysql_stmt_attr_get"); + + switch (attr_type) { + case STMT_ATTR_UPDATE_MAX_LENGTH: + stmt->update_max_length= *(my_bool *)value; + break; + case STMT_ATTR_CURSOR_TYPE: + if (*(ulong *)value > (unsigned long) CURSOR_TYPE_READ_ONLY) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + stmt->flags = *(ulong *)value; + break; + case STMT_ATTR_PREFETCH_ROWS: + if (*(ulong *)value == 0) + *(long *)value= MYSQL_DEFAULT_PREFETCH_ROWS; + else + stmt->prefetch_rows= *(long *)value; + break; + default: + SET_CLIENT_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind) +{ + DBUG_ENTER("mysql_stmt_bind_param"); + + if (stmt->state < MYSQL_STMT_PREPARED) { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->param_count && bind) + { + uint i; + + memcpy(stmt->params, bind, sizeof(MYSQL_BIND) * stmt->param_count); + stmt->send_types_to_server= 1; + + for (i=0; i < stmt->param_count; i++) + { + if (stmt->mysql->methods->db_supported_buffer_type && + !stmt->mysql->methods->db_supported_buffer_type(stmt->params[i].buffer_type)) + { + SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + if (!stmt->params[i].is_null) + stmt->params[i].is_null= &is_not_null; + + if (stmt->params[i].long_data_used) + stmt->params[i].long_data_used= 0; + + if (!stmt->params[i].length) + stmt->params[i].length= &stmt->params[i].buffer_length; + + switch(stmt->params[i].buffer_type) { + case MYSQL_TYPE_NULL: + stmt->params[i].is_null= &is_null; + break; + case MYSQL_TYPE_TINY: + stmt->params[i].buffer_length= 1; + break; + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + stmt->params[i].buffer_length= 2; + break; + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + stmt->params[i].buffer_length= 4; + break; + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_DOUBLE: + stmt->params[i].buffer_length= 8; + break; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + stmt->params[i].buffer_length= 12; + break; + case MYSQL_TYPE_TIME: + stmt->params[i].buffer_length= 13; + break; + case MYSQL_TYPE_DATE: + stmt->params[i].buffer_length= 5; + break; + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + break; + default: + SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + break; + } + } + } + stmt->bind_param_done= stmt->send_types_to_server= 1; + + CLEAR_CLIENT_STMT_ERROR(stmt); + DBUG_RETURN(0); +} + +my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) +{ + uint i; + DBUG_ENTER("mysql_stmt_bind_result"); + + if (stmt->state < MYSQL_STMT_PREPARED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!stmt->field_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_STMT_METADATA, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!bind) + DBUG_RETURN(1); + + /* In case of a stored procedure we don't allocate memory for bind + in mysql_stmt_prepare + */ + + if (stmt->field_count && !stmt->bind) + { + MEM_ROOT *fields_alloc_root= + &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; +// free_root(fields_alloc_root, MYF(0)); + if (!(stmt->bind= (MYSQL_BIND *)alloc_root(fields_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + } + memcpy(stmt->bind, bind, sizeof(MYSQL_BIND) * stmt->field_count); + + for (i=0; i < stmt->field_count; i++) + { + if (stmt->mysql->methods->db_supported_buffer_type && + !stmt->mysql->methods->db_supported_buffer_type(bind[i].buffer_type)) + { + SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!stmt->bind[i].is_null) + stmt->bind[i].is_null= &stmt->bind[i].is_null_value; + if (!stmt->bind[i].length) + stmt->bind[i].length= &stmt->bind[i].length_value; + if (!stmt->bind[i].error) + stmt->bind[i].error= &stmt->bind[i].error_value; + + /* set length values for numeric types */ + switch(bind[i].buffer_type) { + case MYSQL_TYPE_NULL: + *stmt->bind[i].length= stmt->bind[i].length_value= 0; + break; + case MYSQL_TYPE_TINY: + *stmt->bind[i].length= stmt->bind[i].length_value= 1; + break; + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + *stmt->bind[i].length= stmt->bind[i].length_value= 2; + break; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + *stmt->bind[i].length= stmt->bind[i].length_value= 4; + break; + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_DOUBLE: + *stmt->bind[i].length= stmt->bind[i].length_value= 8; + break; + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + *stmt->bind[i].length= stmt->bind[i].length_value= sizeof(MYSQL_TIME); + break; + default: + break; + } + } + stmt->bind_result_done= 1; + CLEAR_CLIENT_STMT_ERROR(stmt); + + DBUG_RETURN(0); +} + +my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove) +{ + char stmt_id[STMT_ID_LENGTH]; + MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + + /* clear memory */ + free_root(&stmt->result.alloc, MYF(0)); /* allocated in mysql_stmt_store_result */ + free_root(&stmt->mem_root,MYF(0)); + free_root(fields_alloc_root, MYF(0)); + + if (stmt->mysql) + { + CLEAR_CLIENT_ERROR(stmt->mysql); + + /* remove from stmt list */ + if (remove) + stmt->mysql->stmts= list_delete(stmt->mysql->stmts, &stmt->list); + + /* check if all data are fetched */ + if (stmt->mysql->status != MYSQL_STATUS_READY) + { + stmt->mysql->methods->db_stmt_flush_unbuffered(stmt); + stmt->mysql->status= MYSQL_STATUS_READY; + } + if (stmt->state > MYSQL_STMT_INITTED) + { + int4store(stmt_id, stmt->stmt_id); + if (simple_command(stmt->mysql,MYSQL_COM_STMT_CLOSE, stmt_id, sizeof(stmt_id), 1, stmt)) + { + SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, stmt->mysql->net.last_error); + return 1; + } + } + } + return 0; +} + +my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) +{ + DBUG_ENTER("mysql_stmt_close"); + + if (stmt && stmt->mysql && stmt->mysql->net.vio) + mysql_stmt_reset(stmt); + net_stmt_close(stmt, 1); + + my_free(stmt->extension); + my_free(stmt); + + DBUG_RETURN(0); +} + +void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong offset) +{ + my_ulonglong i= offset; + MYSQL_ROWS *ptr= stmt->result.data; + DBUG_ENTER("mysql_stmt_data_seek"); + + DBUG_PRINT("info", ("total rows: %llu offset: %llu", stmt->result.rows, offset)); + + while(i-- && ptr) + ptr= ptr->next; + + stmt->result_cursor= ptr; + stmt->state= MYSQL_STMT_USER_FETCHING; + + DBUG_VOID_RETURN; +} + +unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT *stmt) +{ + return stmt->last_errno; +} + +const char * STDCALL mysql_stmt_error(MYSQL_STMT *stmt) +{ + return (const char *)stmt->last_error; +} + +int mthd_stmt_fetch_row(MYSQL_STMT *stmt, unsigned char **row) +{ + return stmt->fetch_row_func(stmt, row); +} + +int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt) +{ + unsigned char *row; + int rc; + + DBUG_ENTER("mysql_stmt_fetch"); + + if (stmt->state <= MYSQL_STMT_EXECUTED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->state < MYSQL_STMT_WAITING_USE_OR_STORE || !stmt->field_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } else if (stmt->state== MYSQL_STMT_WAITING_USE_OR_STORE) + { + stmt->default_rset_handler(stmt); + } + + if (stmt->state == MYSQL_STMT_FETCH_DONE) + DBUG_RETURN(MYSQL_NO_DATA); + + if ((rc= stmt->mysql->methods->db_stmt_fetch(stmt, &row))) + { + stmt->state= MYSQL_STMT_FETCH_DONE; + stmt->mysql->status= MYSQL_STATUS_READY; + /* to fetch data again, stmt must be executed again */ + DBUG_RETURN(rc); + } + + rc= stmt->mysql->methods->db_stmt_fetch_to_bind(stmt, row); + + stmt->state= MYSQL_STMT_USER_FETCHING; + CLEAR_CLIENT_ERROR(stmt->mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + DBUG_RETURN(rc); +} + +int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset) +{ + DBUG_ENTER("mysql_stmt_fetch"); + + if (stmt->state < MYSQL_STMT_USER_FETCHING || column >= stmt->field_count || + stmt->state == MYSQL_STMT_FETCH_DONE) { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_DATA, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!stmt->bind[column].row_ptr) + { + /* we set row_ptr only for columns which contain data, so this must be a NULL column */ + if (bind[0].is_null) + *bind[0].is_null= 1; + } + else + { + unsigned char *save_ptr; + if (bind[0].length) + *bind[0].length= *stmt->bind[column].length; + else + bind[0].length= &stmt->bind[column].length_value; + if (bind[0].is_null) + *bind[0].is_null= 0; + else + bind[0].is_null= &bind[0].is_null_value; + if (!bind[0].error) + bind[0].error= &bind[0].error_value; + *bind[0].error= 0; + bind[0].offset= offset; + save_ptr= stmt->bind[column].row_ptr; + mysql_ps_fetch_functions[stmt->fields[column].type].func(&bind[0], &stmt->fields[column], &stmt->bind[column].row_ptr); + stmt->bind[column].row_ptr= save_ptr; + } + DBUG_RETURN(0); +} + +unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt) +{ + return stmt->field_count; +} + +my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) +{ + return madb_reset_stmt(stmt, MADB_RESET_LONGDATA | MADB_RESET_STORED | + MADB_RESET_BUFFER | MADB_RESET_ERROR); +} + +MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql) +{ + + MYSQL_STMT *stmt; + DBUG_ENTER("mysql_stmt_init"); + + if (!(stmt= (MYSQL_STMT *)my_malloc(sizeof(MYSQL_STMT), MYF(MY_WME | MY_ZEROFILL))) || + !(stmt->extension= (MADB_STMT_EXTENSION *)my_malloc(sizeof(MADB_STMT_EXTENSION), + MYF(MY_WME | MY_ZEROFILL)))) + { + my_free(stmt); + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(NULL); + } + + + /* fill mysql's stmt list */ + stmt->list.data= stmt; + stmt->mysql= mysql; + mysql->stmts= list_add(mysql->stmts, &stmt->list); + + + /* clear flags */ + strcpy(stmt->sqlstate, "00000"); + + stmt->state= MYSQL_STMT_INITTED; + + /* set default */ + stmt->prefetch_rows= 1; + + init_alloc_root(&stmt->mem_root, 2048, 0); + init_alloc_root(&stmt->result.alloc, 4096, 0); + init_alloc_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root, 2048, 0); + + DBUG_RETURN(stmt); +} + +my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt) +{ + ulong packet_length; + uchar *p; + + DBUG_ENTER("read_prepare_response"); + + if ((packet_length= net_safe_read(stmt->mysql)) == packet_error) + DBUG_RETURN(1); + + DBUG_PRINT("info",("packet_length= %ld",packet_length)); + + p= (uchar *)stmt->mysql->net.read_pos; + + if (0xFF == p[0]) /* Error occured */ + { + DBUG_RETURN(1); + } + + p++; + stmt->stmt_id= uint4korr(p); + p+= 4; + stmt->field_count= uint2korr(p); + p+= 2; + stmt->param_count= uint2korr(p); + + /* filler */ + p++; + stmt->upsert_status.warning_count= uint2korr(p); + + DBUG_RETURN(0); +} + +my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt) +{ + MYSQL_DATA *result; + + DBUG_ENTER("stmt_get_param_metadata"); + + if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7))) + DBUG_RETURN(1); + + free_rows(result); + DBUG_RETURN(0); +} + +my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt) +{ + MYSQL_DATA *result; + MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + DBUG_ENTER("stmt_read_result_metadata"); + + if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7))) + DBUG_RETURN(1); + if (!(stmt->fields= unpack_fields(result,fields_alloc_root, + stmt->field_count, 0, + stmt->mysql->server_capabilities & CLIENT_LONG_FLAG))) + DBUG_RETURN(1); + DBUG_RETURN(0); +} + +int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned long length) +{ + MYSQL *mysql= stmt->mysql; + int rc= 1; + DBUG_ENTER("mysql_stmt_prepare"); + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + /* clear flags */ + CLEAR_CLIENT_STMT_ERROR(stmt); + CLEAR_CLIENT_ERROR(stmt->mysql); + stmt->upsert_status.affected_rows= mysql->affected_rows= (my_ulonglong) ~0; + + /* check if we have to clear results */ + if (stmt->state > MYSQL_STMT_INITTED) + { + /* We need to semi-close the prepared statement: + reset stmt and free all buffers and close the statement + on server side. Statment handle will get a new stmt_id */ + char stmt_id[STMT_ID_LENGTH]; + + if (mysql_stmt_reset(stmt)) + goto fail; + + free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root, MYF(0)); + + stmt->param_count= 0; + stmt->field_count= 0; + + int4store(stmt_id, stmt->stmt_id); + if (simple_command(mysql, MYSQL_COM_STMT_CLOSE, stmt_id, sizeof(stmt_id), 1, stmt)) + goto fail; + } + if (simple_command(mysql, MYSQL_COM_STMT_PREPARE, query, length, 1, stmt)) + goto fail; + + if (stmt->mysql->methods->db_read_prepare_response && + stmt->mysql->methods->db_read_prepare_response(stmt)) + goto fail; + + /* metadata not supported yet */ + + if (stmt->param_count && + stmt->mysql->methods->db_stmt_get_param_metadata(stmt)) + { + goto fail; + } + + /* allocated bind buffer for parameters */ + if (stmt->field_count && + stmt->mysql->methods->db_stmt_get_result_metadata(stmt)) + { + goto fail; + } + if (stmt->param_count) + { + if (!(stmt->params= (MYSQL_BIND *)alloc_root(&stmt->mem_root, stmt->param_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto fail; + } + memset(stmt->params, '\0', stmt->param_count * sizeof(MYSQL_BIND)); + } + /* allocated bind buffer for result */ + if (stmt->field_count) + { + MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + if (!(stmt->bind= (MYSQL_BIND *)alloc_root(fields_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto fail; + } + } + bzero(stmt->bind, sizeof(MYSQL_BIND) * stmt->field_count); + stmt->state = MYSQL_STMT_PREPARED; + DBUG_RETURN(0); + +fail: + stmt->state= MYSQL_STMT_INITTED; + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + DBUG_RETURN(rc); +} + +int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) +{ + unsigned int last_server_status; + DBUG_ENTER("mysql_stmt_store_result"); + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!stmt->field_count) + DBUG_RETURN(0); + + /* test_pure_coverage requires checking of error_no */ + if (stmt->last_errno) + DBUG_RETURN(1); + + if (stmt->state < MYSQL_STMT_EXECUTED) + { + SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + last_server_status= stmt->mysql->server_status; + + /* if stmt is a cursor, we need to tell server to send all rows */ + if (stmt->cursor_exists && stmt->mysql->status == MYSQL_STATUS_READY) + { + char buff[STMT_ID_LENGTH + 4]; + int4store(buff, stmt->stmt_id); + int4store(buff + STMT_ID_LENGTH, (int)~0); + + if (simple_command(stmt->mysql, MYSQL_COM_STMT_FETCH, buff, sizeof(buff), 1, stmt)) + DBUG_RETURN(1); + /* todo: cursor */ + } + else if (stmt->mysql->status != MYSQL_STATUS_GET_RESULT) + { + SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->mysql->methods->db_stmt_read_all_rows(stmt)) + { + /* error during read - reset stmt->data */ + free_root(&stmt->result.alloc, 0); + stmt->result.data= NULL; + stmt->result.rows= 0; + stmt->mysql->status= MYSQL_STATUS_READY; + DBUG_RETURN(1); + } + + /* workaround for MDEV 6304: + more results not set if the resultset has + SERVER_PS_OUT_PARAMS set + */ + if (last_server_status & SERVER_PS_OUT_PARAMS && + !(stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST)) + stmt->mysql->server_status|= SERVER_MORE_RESULTS_EXIST; + + stmt->result_cursor= stmt->result.data; + stmt->fetch_row_func= stmt_buffered_fetch; + stmt->mysql->status= MYSQL_STATUS_READY; + + if (!stmt->result.rows) + stmt->state= MYSQL_STMT_FETCH_DONE; + else + stmt->state= MYSQL_STMT_USE_OR_STORE_CALLED; + + /* set affected rows: see bug 2247 */ + stmt->upsert_status.affected_rows= stmt->result.rows; + stmt->mysql->affected_rows= stmt->result.rows; + + DBUG_RETURN(0); +} + +static int madb_alloc_stmt_fields(MYSQL_STMT *stmt) +{ + uint i; + MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + + DBUG_ENTER("madb_alloc_stmt_fields"); + + if (stmt->mysql->field_count) + { + free_root(fields_alloc_root, MYF(0)); + if (!(stmt->fields= (MYSQL_FIELD *)alloc_root(fields_alloc_root, + sizeof(MYSQL_FIELD) * stmt->mysql->field_count))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + stmt->field_count= stmt->mysql->field_count; + + for (i=0; i < stmt->field_count; i++) + { + if (stmt->mysql->fields[i].db) + stmt->fields[i].db= strdup_root(fields_alloc_root, stmt->mysql->fields[i].db); + if (stmt->mysql->fields[i].table) + stmt->fields[i].table= strdup_root(fields_alloc_root, stmt->mysql->fields[i].table); + if (stmt->mysql->fields[i].org_table) + stmt->fields[i].org_table= strdup_root(fields_alloc_root, stmt->mysql->fields[i].org_table); + if (stmt->mysql->fields[i].name) + stmt->fields[i].name= strdup_root(fields_alloc_root, stmt->mysql->fields[i].name); + if (stmt->mysql->fields[i].org_name) + stmt->fields[i].org_name= strdup_root(fields_alloc_root, stmt->mysql->fields[i].org_name); + if (stmt->mysql->fields[i].catalog) + stmt->fields[i].catalog= strdup_root(fields_alloc_root, stmt->mysql->fields[i].catalog); + stmt->fields[i].def= stmt->mysql->fields[i].def ? strdup_root(fields_alloc_root, stmt->mysql->fields[i].def) : NULL; + stmt->fields[i].type= stmt->mysql->fields[i].type; + stmt->fields[i].length= stmt->mysql->fields[i].length; + stmt->fields[i].flags= stmt->mysql->fields[i].flags; + stmt->fields[i].decimals= stmt->mysql->fields[i].decimals; + stmt->fields[i].charsetnr= stmt->mysql->fields[i].charsetnr; + stmt->fields[i].max_length= stmt->mysql->fields[i].max_length; + } + if (!(stmt->bind= (MYSQL_BIND *)alloc_root(fields_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + bzero(stmt->bind, stmt->field_count * sizeof(MYSQL_BIND)); + stmt->bind_result_done= 0; + } + DBUG_RETURN(0); +} + + +int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + char *request; + int ret; + size_t request_len= 0; + + + DBUG_ENTER("mysql_stmt_execute"); + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->state < MYSQL_STMT_PREPARED) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->param_count && !stmt->bind_param_done) + { + SET_CLIENT_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE) + { + stmt->default_rset_handler = _mysql_stmt_use_result; + stmt->default_rset_handler(stmt); + } + if (stmt->state > MYSQL_STMT_WAITING_USE_OR_STORE && stmt->state < MYSQL_STMT_FETCH_DONE && !stmt->result.data) + { + mysql->methods->db_stmt_flush_unbuffered(stmt); + stmt->state= MYSQL_STMT_PREPARED; + stmt->mysql->status= MYSQL_STATUS_READY; + } + + /* clear data, in case mysql_stmt_store_result was called */ + if (stmt->result.data) + { + free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC)); + stmt->result_cursor= stmt->result.data= 0; + stmt->result.rows= 0; + } + request= (char *)mysql_stmt_execute_generate_request(stmt, &request_len); + DBUG_PRINT("info",("request_len=%ld", request_len)); + + ret= test(simple_command(mysql, MYSQL_COM_STMT_EXECUTE, request, request_len, 1, stmt) || + (mysql && mysql->methods->db_read_stmt_result && mysql->methods->db_read_stmt_result(mysql))); + if (request) + my_free(request); + + /* if a reconnect occured, our connection handle is invalid */ + if (!stmt->mysql) + DBUG_RETURN(1); + + /* update affected rows, also if an error occured */ + stmt->upsert_status.affected_rows= stmt->mysql->affected_rows; + + if (ret) + { + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + stmt->state= MYSQL_STMT_PREPARED; + DBUG_RETURN(1); + } + stmt->upsert_status.last_insert_id= mysql->insert_id; + stmt->upsert_status.server_status= mysql->server_status; + stmt->upsert_status.warning_count= mysql->warning_count; + + CLEAR_CLIENT_ERROR(mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + + stmt->execute_count++; + stmt->send_types_to_server= 0; + + stmt->state= MYSQL_STMT_EXECUTED; + + if (mysql->field_count) + { + if (!stmt->field_count || + mysql->server_status & SERVER_MORE_RESULTS_EXIST) /* fix for ps_bug: test_misc */ + { + MEM_ROOT *fields_alloc_root= + &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + uint i; + + free_root(fields_alloc_root, MYF(0)); + if (!(stmt->bind= (MYSQL_BIND *)alloc_root(fields_alloc_root, + sizeof(MYSQL_BIND) * mysql->field_count)) || + !(stmt->fields= (MYSQL_FIELD *)alloc_root(fields_alloc_root, + sizeof(MYSQL_FIELD) * mysql->field_count))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + bzero(stmt->bind, sizeof(MYSQL_BIND) * mysql->field_count); + stmt->field_count= mysql->field_count; + + for (i=0; i < stmt->field_count; i++) + { + memcpy(&stmt->fields[i], &mysql->fields[i], sizeof(MYSQL_FIELD)); + + /* since all pointers will be incorrect if another statement will + be executed, so we need to allocate memory and copy the + information */ + stmt->fields[i].extension= 0; /* not in use yet */ + if (mysql->fields[i].db) + stmt->fields[i].db= strdup_root(fields_alloc_root, mysql->fields[i].db); + if (mysql->fields[i].table) + stmt->fields[i].table= strdup_root(fields_alloc_root, mysql->fields[i].table); + if (mysql->fields[i].org_table) + stmt->fields[i].org_table= strdup_root(fields_alloc_root, mysql->fields[i].org_table); + if (mysql->fields[i].name) + stmt->fields[i].name= strdup_root(fields_alloc_root, mysql->fields[i].name); + if (mysql->fields[i].org_name) + stmt->fields[i].org_name= strdup_root(fields_alloc_root, mysql->fields[i].org_name); + if (mysql->fields[i].catalog) + stmt->fields[i].catalog= strdup_root(fields_alloc_root, mysql->fields[i].catalog); + if (mysql->fields[i].def) + stmt->fields[i].def= strdup_root(fields_alloc_root, mysql->fields[i].def); + } + } + + if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) + { + stmt->cursor_exists = TRUE; + mysql->status = MYSQL_STATUS_READY; + + /* Only cursor read */ + stmt->default_rset_handler = _mysql_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 */ + mysql_stmt_store_result(stmt); + } else + { + /* preferred is unbuffered read */ + stmt->default_rset_handler = _mysql_stmt_use_result; + } + stmt->state= MYSQL_STMT_WAITING_USE_OR_STORE; + /* in certain cases parameter types can change: For example see bug + 4026 (SELECT ?), so we need to update field information */ + if (mysql->field_count == stmt->field_count) + { + uint i; + for (i=0; i < stmt->field_count; i++) + { + stmt->fields[i].type= mysql->fields[i].type; + stmt->fields[i].length= mysql->fields[i].length; + stmt->fields[i].flags= mysql->fields[i].flags; + stmt->fields[i].decimals= mysql->fields[i].decimals; + stmt->fields[i].charsetnr= mysql->fields[i].charsetnr; + stmt->fields[i].max_length= mysql->fields[i].max_length; + } + } else + { + /* table was altered, see test_wl4166_2 */ + SET_CLIENT_STMT_ERROR(stmt, CR_NEW_STMT_METADATA, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + +static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) +{ + MYSQL *mysql= stmt->mysql; + my_bool ret= 0; + + DBUG_ENTER("madb_stmt_reset"); + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + /* clear error */ + if (flags & MADB_RESET_ERROR) + { + CLEAR_CLIENT_ERROR(stmt->mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + } + + if (stmt->stmt_id) + { + /* free buffered resultset, previously allocated + * by mysql_stmt_store_result + */ + if (flags & MADB_RESET_STORED && + stmt->result_cursor) + { + free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC)); + stmt->result.data= NULL; + stmt->result.rows= 0; + stmt->result_cursor= NULL; + stmt->mysql->status= MYSQL_STATUS_READY; + stmt->state= MYSQL_STMT_FETCH_DONE; + } + + /* if there is a pending result set, we will flush it */ + if (flags & MADB_RESET_BUFFER) + { + if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE) + { + stmt->default_rset_handler(stmt); + stmt->state = MYSQL_STMT_USER_FETCHING; + } + + if (stmt->mysql->status!= MYSQL_STATUS_READY && stmt->field_count) + { + mysql->methods->db_stmt_flush_unbuffered(stmt); + mysql->status= MYSQL_STATUS_READY; + } + } + + + if (flags & MADB_RESET_SERVER) + { + /* reset statement on server side */ + if (stmt->mysql && stmt->mysql->status == MYSQL_STATUS_READY) + { + unsigned char cmd_buf[STMT_ID_LENGTH]; + int4store(cmd_buf, stmt->stmt_id); + if ((ret= simple_command(mysql,MYSQL_COM_STMT_RESET, (char *)cmd_buf, sizeof(cmd_buf), 0, stmt))) + { + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + DBUG_RETURN(ret); + } + } + } + + if (flags & MADB_RESET_LONGDATA) + { + if (stmt->params) + { + ulonglong i; + for (i=0; i < stmt->param_count; i++) + if (stmt->params[i].long_data_used) + stmt->params[i].long_data_used= 0; + } + } + + } + DBUG_RETURN(ret); +} + +my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + my_bool ret= 1; + unsigned int flags= MADB_RESET_LONGDATA | MADB_RESET_BUFFER | MADB_RESET_ERROR; + + DBUG_ENTER("mysql_stmt_reset"); + + if (!mysql) + { + /* connection could be invalid, e.g. after mysql_stmt_close or failed reconnect + attempt (see bug CONC-97) */ + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->state >= MYSQL_STMT_USER_FETCHING && + stmt->fetch_row_func == stmt_unbuffered_fetch) + flags|= MADB_RESET_BUFFER; + + ret= madb_reset_stmt(stmt, flags); + + if (stmt->stmt_id) + { + if ((stmt->state > MYSQL_STMT_EXECUTED && + stmt->mysql->status != MYSQL_STATUS_READY) || + stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST) + { + /* flush any pending (multiple) result sets */ + if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE) + { + stmt->default_rset_handler(stmt); + stmt->state = MYSQL_STMT_USER_FETCHING; + } + + if (stmt->field_count) + { + while (mysql_stmt_next_result(stmt) == 0); + stmt->mysql->status= MYSQL_STATUS_READY; + } + } + ret= madb_reset_stmt(stmt, MADB_RESET_SERVER); + } + stmt->state= MYSQL_STMT_PREPARED; + stmt->upsert_status.affected_rows= mysql->affected_rows; + stmt->upsert_status.last_insert_id= mysql->insert_id; + stmt->upsert_status.server_status= mysql->server_status; + stmt->upsert_status.warning_count= mysql->warning_count; + mysql->status= MYSQL_STATUS_READY; + + DBUG_RETURN(ret); +} + +MYSQL_RES * STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt) +{ + MYSQL_RES *res; + + DBUG_ENTER("mysql_stmt_result_metadata"); + + if (!stmt->field_count) + DBUG_RETURN(NULL); + + /* aloocate result set structutr and copy stmt information */ + if (!(res= (MYSQL_RES *)my_malloc(sizeof(MYSQL_RES), MYF(MY_WME | MY_ZEROFILL)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(NULL); + } + + res->eof= 1; + res->fields= stmt->fields; + res->field_count= stmt->field_count; + DBUG_RETURN(res);} + +const char * STDCALL mysql_stmt_sqlstate(MYSQL_STMT *stmt) +{ + return stmt->sqlstate; +} + +MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt) +{ + DBUG_ENTER("mysql_stmt_row_tell"); + DBUG_RETURN(stmt->result_cursor); +} + +unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT *stmt) +{ + return stmt->param_count; +} + +MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_seek(MYSQL_STMT *stmt, MYSQL_ROW_OFFSET new_row) +{ + MYSQL_ROW_OFFSET old_row; /* for returning old position */ + DBUG_ENTER("mysql_stmt_row_seek"); + + old_row= stmt->result_cursor; + stmt->result_cursor= new_row; + + DBUG_RETURN(old_row); +} + +my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, + const char *data, ulong length) +{ + DBUG_ENTER("mysql_stmt_send_long_data"); + + CLEAR_CLIENT_ERROR(stmt->mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + + if (stmt->state < MYSQL_STMT_PREPARED || !stmt->params) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (param_number >= stmt->param_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (length || !stmt->params[param_number].long_data_used) + { + int ret; + size_t packet_len; + uchar *cmd_buff= (uchar *)my_malloc(packet_len= STMT_ID_LENGTH + 2 + length, MYF(MY_WME | MY_ZEROFILL)); + int4store(cmd_buff, stmt->stmt_id); + int2store(cmd_buff + STMT_ID_LENGTH, param_number); + memcpy(cmd_buff + STMT_ID_LENGTH + 2, data, length); + stmt->params[param_number].long_data_used= 1; + ret= simple_command(stmt->mysql,MYSQL_COM_STMT_SEND_LONG_DATA, (char *)cmd_buff, packet_len, 1, stmt); + my_free(cmd_buff); + DBUG_RETURN(ret); + } + DBUG_RETURN(0); +} + +my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt) +{ + return stmt->upsert_status.last_insert_id; +} + +my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) +{ + return stmt->result.rows; +} + +MYSQL_RES* STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt) +{ + DBUG_ENTER("mysql_stmt_param_metadata"); + /* server doesn't deliver any information yet, + so we just return NULL + */ + DBUG_RETURN(NULL); +} + +my_bool STDCALL mysql_stmt_more_results(MYSQL_STMT *stmt) +{ + /* MDEV 4604: Server doesn't set MORE_RESULT flag for + OutParam result set, so we need to check + for SERVER_MORE_RESULTS_EXIST and for + SERVER_PS_OUT_PARAMS) + */ + return (stmt && + stmt->mysql && + ((stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST) || + (stmt->mysql->server_status & SERVER_PS_OUT_PARAMS))); +} + +int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt) +{ + int rc= 0; + DBUG_ENTER("mysql_stmt_next_result"); + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->state < MYSQL_STMT_EXECUTED) + { + SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!mysql_stmt_more_results(stmt)) + DBUG_RETURN(-1); + + if (stmt->state > MYSQL_STMT_EXECUTED && + stmt->state < MYSQL_STMT_FETCH_DONE) + madb_reset_stmt(stmt, MADB_RESET_ERROR | MADB_RESET_BUFFER | MADB_RESET_LONGDATA); + stmt->state= MYSQL_STMT_WAITING_USE_OR_STORE; + + if (mysql_next_result(stmt->mysql)) + { + stmt->state= MYSQL_STMT_FETCH_DONE; + SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, + stmt->mysql->net.last_error); + DBUG_RETURN(1); + } + + if (stmt->mysql->field_count) + rc= madb_alloc_stmt_fields(stmt); + else + { + stmt->upsert_status.affected_rows= stmt->mysql->affected_rows; + stmt->upsert_status.last_insert_id= stmt->mysql->insert_id; + stmt->upsert_status.server_status= stmt->mysql->server_status; + stmt->upsert_status.warning_count= stmt->mysql->warning_count; + } + + stmt->field_count= stmt->mysql->field_count; + + DBUG_RETURN(rc); +} -- cgit v1.2.3