From 43d743e75b7b747341b9a5c36a933b490548bebb Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 4 Nov 2017 01:17:16 +0300 Subject: Add implementation --- mysql/libmariadb/mariadb_stmt.c | 2419 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 2419 insertions(+) create mode 100644 mysql/libmariadb/mariadb_stmt.c (limited to 'mysql/libmariadb/mariadb_stmt.c') diff --git a/mysql/libmariadb/mariadb_stmt.c b/mysql/libmariadb/mariadb_stmt.c new file mode 100644 index 0000000..d3eb042 --- /dev/null +++ b/mysql/libmariadb/mariadb_stmt.c @@ -0,0 +1,2419 @@ +/**************************************************************************** + 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 "ma_global.h" +#include +#include +#include +#include "mysql.h" +#include "errmsg.h" +#include +#include +#include +#include +#include +#include + +#define STMT_NUM_OFS(type, a,r) ((type *)(a))[r] +#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 +{ + MA_MEM_ROOT fields_ma_alloc_root; +} MADB_STMT_EXTENSION; + +MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, uint fields); +void free_rows(MYSQL_DATA *cur); +int ma_multi_command(MYSQL *mysql, enum enum_multi_status status); +MYSQL_FIELD * unpack_fields(MYSQL_DATA *data,MA_MEM_ROOT *alloc,uint fields, my_bool default_value, my_bool long_flag_protocol); +static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove); + +static my_bool is_not_null= 0; +static my_bool is_null= 1; + +void stmt_set_error(MYSQL_STMT *stmt, + unsigned int error_nr, + const char *sqlstate, + const char *format, + ...) +{ + va_list ap; + + stmt->last_errno= error_nr; + ma_strmake(stmt->sqlstate, sqlstate, SQLSTATE_LENGTH); + va_start(ap, format); + vsnprintf(stmt->last_error, MYSQL_ERRMSG_SIZE, + format ? format : ER(error_nr), ap); + va_end(ap); + return; +} + +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_JSON: + 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 my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close); +static int stmt_unbuffered_eof(MYSQL_STMT *stmt __attribute__((unused)), + uchar **row __attribute__((unused))) +{ + return MYSQL_NO_DATA; +} + +static int stmt_unbuffered_fetch(MYSQL_STMT *stmt, uchar **row) +{ + ulong pkt_len; + + pkt_len= ma_net_safe_read(stmt->mysql); + + if (pkt_len == packet_error) + { + stmt->fetch_row_func= stmt_unbuffered_eof; + return(1); + } + + if (stmt->mysql->net.read_pos[0] == 254) + { + *row = NULL; + stmt->fetch_row_func= stmt_unbuffered_eof; + return(MYSQL_NO_DATA); + } + else + *row = stmt->mysql->net.read_pos; + stmt->result.rows++; + 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; + + pprevious= &result->data; + + while ((packet_len = ma_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 *)ma_alloc_root(&result->alloc, sizeof(MYSQL_ROWS) + packet_len))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + 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; + 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); + return(1); +} + +static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row) +{ + uchar buf[STMT_ID_LENGTH + 4]; + MYSQL_DATA *result= &stmt->result; + + if (stmt->state < MYSQL_STMT_USE_OR_STORE_CALLED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + return(1); + } + + /* do we have some prefetched rows available ? */ + if (stmt->result_cursor) + return(stmt_buffered_fetch(stmt, row)); + if (stmt->upsert_status.server_status & SERVER_STATUS_LAST_ROW_SENT) + stmt->upsert_status.server_status&= ~SERVER_STATUS_LAST_ROW_SENT; + else + { + int4store(buf, stmt->stmt_id); + int4store(buf + STMT_ID_LENGTH, stmt->prefetch_rows); + + if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, (char *)buf, sizeof(buf), 1, stmt)) + return(1); + + /* free previously allocated buffer */ + ma_free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); + result->data= 0; + result->rows= 0; + + if (stmt->mysql->methods->db_stmt_read_all_rows(stmt)) + return(1); + + return(stmt_buffered_fetch(stmt, row)); + } + /* no more cursor data available */ + *row= NULL; + return(MYSQL_NO_DATA); +} + +/* flush one result set */ +void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt) +{ + ulong packet_len; + int in_resultset= stmt->state > MYSQL_STMT_EXECUTED && + stmt->state < MYSQL_STMT_FETCH_DONE; + while ((packet_len = ma_net_safe_read(stmt->mysql)) != packet_error) + { + uchar *pos= stmt->mysql->net.read_pos; + if (!in_resultset && *pos == 0) /* OK */ + { + pos++; + net_field_length(&pos); + net_field_length(&pos); + stmt->mysql->server_status= uint2korr(pos); + goto end; + } + if (packet_len < 8 && *pos == 254) /* EOF */ + { + stmt->mysql->server_status= uint2korr(pos + 3); + if (in_resultset) + goto end; + in_resultset= 1; + } + } +end: + stmt->state= MYSQL_STMT_FETCH_DONE; +} + +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; + 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].u.row_ptr= NULL; + } else + { + stmt->bind[i].u.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++; + } + } + return((truncations) ? MYSQL_DATA_TRUNCATED : 0); +} + +MYSQL_RES *_mysql_stmt_use_result(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + + if (!stmt->field_count || + (!stmt->cursor_exists && mysql->status != MYSQL_STATUS_STMT_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); + 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; + + return(NULL); +} + +unsigned char *mysql_net_store_length(unsigned char *packet, size_t length) +{ + if (length < (unsigned long long) L64(251)) { + *packet = (unsigned char) length; + return packet + 1; + } + + if (length < (unsigned long long) L64(65536)) { + *packet++ = 252; + int2store(packet,(uint) length); + return packet + 2; + } + + if (length < (unsigned long long) L64(16777216)) { + *packet++ = 253; + int3store(packet,(ulong) length); + return packet + 3; + } + *packet++ = 254; + int8store(packet, length); + return packet + 8; +} + +static long ma_get_length(MYSQL_STMT *stmt, unsigned int param_nr, unsigned long row_nr) +{ + if (!stmt->params[param_nr].length) + return 0; + if (stmt->row_size) + return *(long *)((char *)stmt->params[param_nr].length + row_nr * stmt->row_size); + else + return stmt->params[param_nr].length[row_nr]; +} + +static signed char ma_get_indicator(MYSQL_STMT *stmt, unsigned int param_nr, unsigned long row_nr) +{ + if (!MARIADB_STMT_BULK_SUPPORTED(stmt) || + !stmt->array_size || + !stmt->params[param_nr].u.indicator) + return 0; + if (stmt->row_size) + return *((char *)stmt->params[param_nr].u.indicator + (row_nr * stmt->row_size)); + return stmt->params[param_nr].u.indicator[row_nr]; +} + +static void *ma_get_buffer_offset(MYSQL_STMT *stmt, enum enum_field_types type, + void *buffer, unsigned long row_nr) +{ + if (stmt->array_size) + { + int len; + if (stmt->row_size) + return (void *)((char *)buffer + stmt->row_size * row_nr); + len= mysql_ps_fetch_functions[type].pack_len; + if (len > 0) + return (void *)((char *)buffer + len * row_nr); + return ((void **)buffer)[row_nr]; + } + return buffer; +} + +int store_param(MYSQL_STMT *stmt, int column, unsigned char **p, unsigned long row_nr) +{ + void *buf= ma_get_buffer_offset(stmt, stmt->params[column].buffer_type, + stmt->params[column].buffer, row_nr); + signed char indicator= ma_get_indicator(stmt, column, row_nr); + + switch (stmt->params[column].buffer_type) { + case MYSQL_TYPE_TINY: + int1store(*p, (*(uchar *)buf)); + (*p) += 1; + break; + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + int2store(*p, (*(short *)buf)); + (*p) += 2; + break; + case MYSQL_TYPE_FLOAT: + float4store(*p, (*(float *)buf)); + (*p) += 4; + break; + case MYSQL_TYPE_DOUBLE: + float8store(*p, (*(double *)buf)); + (*p) += 8; + break; + case MYSQL_TYPE_LONGLONG: + int8store(*p, (*(ulonglong *)buf)); + (*p) += 8; + break; + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_INT24: + int4store(*p, (*(int32 *)buf)); + (*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 *)ma_get_buffer_offset(stmt, stmt->params[column].buffer_type, + stmt->params[column].buffer, row_nr); + char t_buffer[MAX_TIME_STR_LEN]; + uint len= 0; + + 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 *)ma_get_buffer_offset(stmt, stmt->params[column].buffer_type, + stmt->params[column].buffer, row_nr); + char t_buffer[MAX_DATETIME_STR_LEN]; + uint len= 0; + + 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= 11; + } + else if (t->hour || t->minute || t->second) + len= 7; + else if (t->year || t->month || t->day) + len= 4; + else + len=0; + 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_JSON: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + { + ulong len; + /* to is after p. The latter hasn't been moved */ + uchar *to; + + if (indicator == STMT_INDICATOR_NTS) + len= -1; + else + len= ma_get_length(stmt, column, row_nr); + + if (len == (ulong)-1) + len= (ulong)strlen((char *)buf); + + to = mysql_net_store_length(*p, len); + + if (len) + memcpy(to, buf, len); + (*p) = to + len; + break; + } + + default: + /* unsupported parameter type */ + SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0); + return 1; + } + return 0; +} + +/* {{{ mysqlnd_stmt_execute_generate_simple_request */ +unsigned char* mysql_stmt_execute_generate_simple_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 + 1st byte: parameter type + 2nd byte flag: + unsigned flag (32768) + indicator variable exists (16384) + ------------------------------------------ + n data from bind_buffer + + */ + + size_t length= 1024; + size_t free_bytes= 0; + size_t null_byte_offset= 0; + uint i; + + uchar *start= NULL, *p; + + /* preallocate length bytes */ + /* check: gr */ + if (!(start= p= (uchar *)malloc(length))) + 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++; + + int4store(p, 1); + p+= 4; + + if (stmt->param_count) + { + size_t 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 *)realloc(start, length))) + 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 *)realloc(start, length))) + 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); + /* check if parameter requires indicator variable */ + int2store(p, buffer_type); + p+= 2; + } + } + + /* calculate data size */ + for (i=0; i < stmt->param_count; i++) + { + size_t size= 0; + my_bool has_data= TRUE; + + if (stmt->params[i].long_data_used) + { + has_data= FALSE; + stmt->params[i].long_data_used= 0; + } + + if (has_data) + { + switch (stmt->params[i].buffer_type) { + case MYSQL_TYPE_NULL: + has_data= FALSE; + 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_JSON: + 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: + size+= 5; /* max 8 bytes for size */ + size+= (size_t)ma_get_length(stmt, i, 0); + break; + default: + size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len; + break; + } + } + free_bytes= length - (p - start); + if (free_bytes < size + 20) + { + size_t offset= p - start; + length= MAX(2 * length, offset + size + 20); + if (!(start= (uchar *)realloc(start, length))) + goto mem_error; + p= start + offset; + } + if (((stmt->params[i].is_null && *stmt->params[i].is_null) || + stmt->params[i].buffer_type == MYSQL_TYPE_NULL || + !stmt->params[i].buffer)) + { + has_data= FALSE; + (start + null_byte_offset)[i/8] |= (unsigned char) (1 << (i & 7)); + } + + if (has_data) + { + store_param(stmt, i, &p, 0); + } + } + } + stmt->send_types_to_server= 0; + *request_len = (size_t)(p - start); + return start; +mem_error: + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + free(start); + *request_len= 0; + return NULL; +} +/* }}} */ + +/* {{{ mysql_stmt_skip_paramset */ +my_bool mysql_stmt_skip_paramset(MYSQL_STMT *stmt, uint row) +{ + uint i; + for (i=0; i < stmt->param_count; i++) + { + if (ma_get_indicator(stmt, i, row) == STMT_INDICATOR_IGNORE_ROW) + return '\1'; + } + + return '\0'; +} +/* }}} */ + +/* {{{ mysql_stmt_execute_generate_bulk_request */ +unsigned char* mysql_stmt_execute_generate_bulk_request(MYSQL_STMT *stmt, size_t *request_len) +{ + /* execute packet has the following format: + Offset Length Description + ----------------------------------------- + 0 4 Statement id + 4 2 Flags (cursor type): + STMT_BULK_FLAG_CLIENT_SEND_TYPES = 128 + STMT_BULK_FLAG_INSERT_ID_REQUEST = 64 + ----------------------------------------- + if (stmt->send_types_to_server): + for (i=0; i < param_count; i++) + 1st byte: parameter type + 2nd byte flag: + unsigned flag (32768) + ------------------------------------------ + for (i=0; i < param_count; i++) + 1 indicator variable + STMT_INDICATOR_NONE 0 + STMT_INDICATOR_NULL 1 + STMT_INDICATOR_DEFAULT 2 + STMT_INDICATOR_IGNORE 3 + STMT_INDICATOR_SKIP_SET 4 + n data from bind buffer + + */ + + size_t length= 1024; + size_t free_bytes= 0; + ushort flags= 0; + uint i, j; + + uchar *start= NULL, *p; + + if (!MARIADB_STMT_BULK_SUPPORTED(stmt)) + { + stmt_set_error(stmt, CR_FUNCTION_NOT_SUPPORTED, "IM001", + CER(CR_FUNCTION_NOT_SUPPORTED), "Bulk operation"); + return NULL; + } + + if (!stmt->param_count) + { + stmt_set_error(stmt, CR_BULK_WITHOUT_PARAMETERS, "IM001", + CER(CR_BULK_WITHOUT_PARAMETERS), "Bulk operation"); + return NULL; + } + + /* preallocate length bytes */ + if (!(start= p= (uchar *)malloc(length))) + goto mem_error; + + int4store(p, stmt->stmt_id); + p += STMT_ID_LENGTH; + + /* todo: request to return auto generated ids */ + if (stmt->send_types_to_server) + flags|= STMT_BULK_FLAG_CLIENT_SEND_TYPES; + int2store(p, flags); + p+=2; + + /* When using mariadb_stmt_execute_direct stmt->paran_count is + not knowm, so we need to assign prebind_params, which was previously + set by mysql_stmt_attr_set + */ + if (!stmt->param_count && stmt->prebind_params) + stmt->param_count= stmt->prebind_params; + + if (stmt->param_count) + { + 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 *)realloc(start, length))) + 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 (j=0; j < stmt->array_size; j++) + { + if (mysql_stmt_skip_paramset(stmt, j)) + continue; + + for (i=0; i < stmt->param_count; i++) + { + size_t size= 0; + my_bool has_data= TRUE; + signed char indicator= ma_get_indicator(stmt, i, j); + /* check if we need to send data */ + if (indicator > 0) + has_data= FALSE; + size= 1; + + /* Please note that mysql_stmt_send_long_data is not supported + current when performing bulk execute */ + + if (has_data) + { + switch (stmt->params[i].buffer_type) { + case MYSQL_TYPE_NULL: + has_data= FALSE; + indicator= STMT_INDICATOR_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_JSON: + 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: + size+= 5; /* max 8 bytes for size */ + if (indicator == STMT_INDICATOR_NTS || + (!stmt->row_size && ma_get_length(stmt,i,j) == -1)) + { + size+= strlen(ma_get_buffer_offset(stmt, + stmt->params[i].buffer_type, + stmt->params[i].buffer,j)); + } + else + size+= (size_t)ma_get_length(stmt, i, j); + break; + default: + size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len; + break; + } + } + free_bytes= length - (p - start); + if (free_bytes < size + 20) + { + size_t offset= p - start; + length= MAX(2 * length, offset + size + 20); + if (!(start= (uchar *)realloc(start, length))) + goto mem_error; + p= start + offset; + } + + int1store(p, indicator > 0 ? indicator : 0); + p++; + if (has_data) + store_param(stmt, i, &p, j); + } + } + + } + stmt->send_types_to_server= 0; + *request_len = (size_t)(p - start); + return start; +mem_error: + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + free(start); + *request_len= 0; + return NULL; +} +/* }}} */ +/*! + ******************************************************************************* + + \fn unsigned long long mysql_stmt_affected_rows + \brief returns the number of affected rows from last mysql_stmt_execute + call + + \param[in] stmt The statement handle + ******************************************************************************* + */ +unsigned long long 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) +{ + 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; + case STMT_ATTR_PREBIND_PARAMS: + *(unsigned int *)value= stmt->prebind_params; + break; + case STMT_ATTR_ARRAY_SIZE: + *(unsigned int *)value= stmt->array_size; + break; + case STMT_ATTR_ROW_SIZE: + *(size_t *)value= stmt->row_size; + break; + default: + return(1); + } + return(0); +} + +my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, const void *value) +{ + 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); + 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; + case STMT_ATTR_PREBIND_PARAMS: + if (stmt->state > MYSQL_STMT_INITTED) + { + mysql_stmt_internal_reset(stmt, 1); + net_stmt_close(stmt, 0); + stmt->state= MYSQL_STMT_INITTED; + stmt->params= 0; + } + stmt->prebind_params= *(unsigned int *)value; + break; + case STMT_ATTR_ARRAY_SIZE: + stmt->array_size= *(unsigned int *)value; + break; + case STMT_ATTR_ROW_SIZE: + stmt->row_size= *(size_t *)value; + break; + default: + SET_CLIENT_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0); + return(1); + } + return(0); +} + +my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind) +{ + MYSQL *mysql= stmt->mysql; + + if (!mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + return(1); + } + + /* If number of parameters was specified via mysql_stmt_attr_set we need to realloc + them, e.g. for mariadb_stmt_execute_direct() + */ + if ((stmt->state < MYSQL_STMT_PREPARED || stmt->state >= MYSQL_STMT_EXECUTED) && + stmt->prebind_params > 0) + { + if (!stmt->params && stmt->prebind_params) + { + if (!(stmt->params= (MYSQL_BIND *)ma_alloc_root(&stmt->mem_root, stmt->prebind_params * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(1); + } + memset(stmt->params, '\0', stmt->prebind_params * sizeof(MYSQL_BIND)); + } + stmt->param_count= stmt->prebind_params; + } + else if (stmt->state < MYSQL_STMT_PREPARED) { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0); + 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); + 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_JSON: + 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); + return(1); + break; + } + } + } + stmt->bind_param_done= stmt->send_types_to_server= 1; + + CLEAR_CLIENT_STMT_ERROR(stmt); + return(0); +} + +my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) +{ + uint i; + + if (stmt->state < MYSQL_STMT_PREPARED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0); + return(1); + } + + if (!stmt->field_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_STMT_METADATA, SQLSTATE_UNKNOWN, 0); + return(1); + } + + if (!bind) + 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) + { + MA_MEM_ROOT *fields_ma_alloc_root= + &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + 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); + 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); + + return(0); +} + +static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove) +{ + char stmt_id[STMT_ID_LENGTH]; + MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + + /* clear memory */ + ma_free_root(&stmt->result.alloc, MYF(0)); /* allocated in mysql_stmt_store_result */ + ma_free_root(&stmt->mem_root,MYF(0)); + ma_free_root(fields_ma_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) + { + do { + stmt->mysql->methods->db_stmt_flush_unbuffered(stmt); + } while(mysql_stmt_more_results(stmt)); + stmt->mysql->status= MYSQL_STATUS_READY; + } + if (stmt->state > MYSQL_STMT_INITTED) + { + int4store(stmt_id, stmt->stmt_id); + if (stmt->mysql->methods->db_command(stmt->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) +{ + my_bool rc; + if (stmt && stmt->mysql && stmt->mysql->net.pvio) + mysql_stmt_internal_reset(stmt, 1); + + rc= net_stmt_close(stmt, 1); + + free(stmt->extension); + free(stmt); + + return(rc); +} + +void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, unsigned long long offset) +{ + unsigned long long i= offset; + MYSQL_ROWS *ptr= stmt->result.data; + + while(i-- && ptr) + ptr= ptr->next; + + stmt->result_cursor= ptr; + stmt->state= MYSQL_STMT_USER_FETCHING; + + 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; + + if (stmt->state <= MYSQL_STMT_EXECUTED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + 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); + return(1); + } else if (stmt->state== MYSQL_STMT_WAITING_USE_OR_STORE) + { + stmt->default_rset_handler(stmt); + } + + if (stmt->state == MYSQL_STMT_FETCH_DONE) + 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 */ + return(rc); + } + + if ((rc= stmt->mysql->methods->db_stmt_fetch_to_bind(stmt, row))) + { + return(rc); + } + + stmt->state= MYSQL_STMT_USER_FETCHING; + CLEAR_CLIENT_ERROR(stmt->mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + return(0); +} + +int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset) +{ + 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); + return(1); + } + + if (!stmt->bind[column].u.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].u.row_ptr; + mysql_ps_fetch_functions[stmt->fields[column].type].func(&bind[0], &stmt->fields[column], &stmt->bind[column].u.row_ptr); + stmt->bind[column].u.row_ptr= save_ptr; + } + 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= NULL; + + if (!(stmt= (MYSQL_STMT *)calloc(1, sizeof(MYSQL_STMT))) || + !(stmt->extension= (MADB_STMT_EXTENSION *)calloc(1, sizeof(MADB_STMT_EXTENSION)))) + { + free(stmt); + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(NULL); + } + + + /* fill mysql's stmt list */ + stmt->list.data= stmt; + stmt->mysql= mysql; + stmt->stmt_id= 0; + 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; + + ma_init_alloc_root(&stmt->mem_root, 2048, 2048); + ma_init_alloc_root(&stmt->result.alloc, 4096, 4096); + ma_init_alloc_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, 2048, 2048); + + return(stmt); +} + +my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt) +{ + ulong packet_length; + uchar *p; + + if ((packet_length= ma_net_safe_read(stmt->mysql)) == packet_error) + return(1); + + p= (uchar *)stmt->mysql->net.read_pos; + + if (0xFF == p[0]) /* Error occured */ + { + return(1); + } + + p++; + stmt->stmt_id= uint4korr(p); + p+= 4; + stmt->field_count= uint2korr(p); + p+= 2; + stmt->param_count= uint2korr(p); + p+= 2; + + /* filler */ + p++; + /* for backward compatibility we also update mysql->warning_count */ + stmt->mysql->warning_count= stmt->upsert_status.warning_count= uint2korr(p); + return(0); +} + +my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt) +{ + MYSQL_DATA *result; + + if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7))) + return(1); + + free_rows(result); + return(0); +} + +my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt) +{ + MYSQL_DATA *result; + MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + + if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7))) + return(1); + if (!(stmt->fields= unpack_fields(result,fields_ma_alloc_root, + stmt->field_count, 0, + stmt->mysql->server_capabilities & CLIENT_LONG_FLAG))) + return(1); + return(0); +} + +int STDCALL mysql_stmt_warning_count(MYSQL_STMT *stmt) +{ + return stmt->upsert_status.warning_count; +} + +int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned long length) +{ + MYSQL *mysql= stmt->mysql; + int rc= 1; + my_bool is_multi= 0; + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + return(1); + } + + if (length == (unsigned long) -1) + length= (unsigned long)strlen(query); + + /* clear flags */ + CLEAR_CLIENT_STMT_ERROR(stmt); + CLEAR_CLIENT_ERROR(stmt->mysql); + stmt->upsert_status.affected_rows= mysql->affected_rows= (unsigned long long) ~0; + + /* check if we have to clear results */ + if (stmt->state > MYSQL_STMT_INITTED) + { + char stmt_id[STMT_ID_LENGTH]; + is_multi= (mysql->net.extension->multi_status > COM_MULTI_OFF); + /* 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 */ + + if (!is_multi) + ma_multi_command(mysql, COM_MULTI_ENABLED); + + if (mysql_stmt_internal_reset(stmt, 1)) + goto fail; + + ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(0)); + + stmt->param_count= 0; + stmt->field_count= 0; + stmt->params= 0; + + int4store(stmt_id, stmt->stmt_id); + if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id, + sizeof(stmt_id), 1, stmt)) + goto fail; + } + if (mysql->methods->db_command(mysql, COM_STMT_PREPARE, query, length, 1, stmt)) + goto fail; + + if (!is_multi && mysql->net.extension->multi_status == COM_MULTI_ENABLED) + ma_multi_command(mysql, COM_MULTI_END); + + if (mysql->net.extension->multi_status > COM_MULTI_OFF) + return 0; + + if (mysql->methods->db_read_prepare_response && + 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->prebind_params) + { + if (stmt->prebind_params != stmt->param_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + goto fail; + } + } else { + if (!(stmt->params= (MYSQL_BIND *)ma_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) + { + MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto fail; + } + memset(stmt->bind, 0, sizeof(MYSQL_BIND) * stmt->field_count); + } + stmt->state = MYSQL_STMT_PREPARED; + return(0); + +fail: + stmt->state= MYSQL_STMT_INITTED; + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + return(rc); +} + +int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) +{ + unsigned int last_server_status; + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + return(1); + } + + if (!stmt->field_count) + return(0); + + /* test_pure_coverage requires checking of error_no */ + if (stmt->last_errno) + 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); + 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 (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, + buff, sizeof(buff), 1, stmt)) + return(1); + /* todo: cursor */ + } + else if (stmt->mysql->status != MYSQL_STATUS_STMT_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); + return(1); + } + + if (stmt->mysql->methods->db_stmt_read_all_rows(stmt)) + { + /* error during read - reset stmt->data */ + ma_free_root(&stmt->result.alloc, 0); + stmt->result.data= NULL; + stmt->result.rows= 0; + stmt->mysql->status= MYSQL_STATUS_READY; + 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; + + return(0); +} + +static int madb_alloc_stmt_fields(MYSQL_STMT *stmt) +{ + uint i; + MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + + if (stmt->mysql->field_count) + { + ma_free_root(fields_ma_alloc_root, MYF(0)); + if (!(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root, + sizeof(MYSQL_FIELD) * stmt->mysql->field_count))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + 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= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].db); + if (stmt->mysql->fields[i].table) + stmt->fields[i].table= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].table); + if (stmt->mysql->fields[i].org_table) + stmt->fields[i].org_table= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].org_table); + if (stmt->mysql->fields[i].name) + stmt->fields[i].name= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].name); + if (stmt->mysql->fields[i].org_name) + stmt->fields[i].org_name= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].org_name); + if (stmt->mysql->fields[i].catalog) + stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].catalog); + stmt->fields[i].def= stmt->mysql->fields[i].def ? ma_strdup_root(fields_ma_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 *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(1); + } + memset(stmt->bind, 0, stmt->field_count * sizeof(MYSQL_BIND)); + stmt->bind_result_done= 0; + } + return(0); +} + +int stmt_read_execute_response(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + int ret; + + if (!mysql) + return(1); + + ret= test((mysql->methods->db_read_stmt_result && + mysql->methods->db_read_stmt_result(mysql))); + /* if a reconnect occured, our connection handle is invalid */ + if (!stmt->mysql) + 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; + 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 */ + { + MA_MEM_ROOT *fields_ma_alloc_root= + &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + uint i; + + ma_free_root(fields_ma_alloc_root, MYF(0)); + if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, + sizeof(MYSQL_BIND) * mysql->field_count)) || + !(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root, + sizeof(MYSQL_FIELD) * mysql->field_count))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(1); + } + memset(stmt->bind, 0, sizeof(MYSQL_BIND) * mysql->field_count); + stmt->field_count= mysql->field_count; + + for (i=0; i < stmt->field_count; i++) + { + if (mysql->fields[i].db) + stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].db); + if (mysql->fields[i].table) + stmt->fields[i].table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].table); + if (mysql->fields[i].org_table) + stmt->fields[i].org_table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_table); + if (mysql->fields[i].name) + stmt->fields[i].name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].name); + if (mysql->fields[i].org_name) + stmt->fields[i].org_name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_name); + if (mysql->fields[i].catalog) + stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].catalog); + stmt->fields[i].def= mysql->fields[i].def ? ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].def) : NULL; + } + } + + 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); + stmt->mysql->status= MYSQL_STATUS_STMT_RESULT; + } else + { + /* preferred is unbuffered read */ + stmt->default_rset_handler = _mysql_stmt_use_result; + stmt->mysql->status= MYSQL_STATUS_STMT_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); + return(1); + } + } + return(0); +} + +int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + char *request; + int ret; + size_t request_len= 0; + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + 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); + return(1); + } + + if (stmt->param_count && !stmt->bind_param_done) + { + SET_CLIENT_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, SQLSTATE_UNKNOWN, 0); + 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) + { + if (!stmt->cursor_exists) + do { + stmt->mysql->methods->db_stmt_flush_unbuffered(stmt); + } while(mysql_stmt_more_results(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) + { + ma_free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC)); + stmt->result_cursor= stmt->result.data= 0; + stmt->result.rows= 0; + } + if (stmt->array_size > 0) + request= (char *)mysql_stmt_execute_generate_bulk_request(stmt, &request_len); + else + request= (char *)mysql_stmt_execute_generate_simple_request(stmt, &request_len); + + if (!request) + return 1; + + ret= stmt->mysql->methods->db_command(mysql, + stmt->array_size > 0 ? COM_STMT_BULK_EXECUTE : COM_STMT_EXECUTE, + request, request_len, 1, stmt); + if (request) + free(request); + + if (ret) + { + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + return(1); + } + + if (mysql->net.extension->multi_status > COM_MULTI_OFF) + return(0); + + return(stmt_read_execute_response(stmt)); +} + +static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) +{ + MYSQL *mysql= stmt->mysql; + my_bool ret= 0; + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + 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) + { + ma_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 && + stmt->mysql->net.pvio) + { + unsigned char cmd_buf[STMT_ID_LENGTH]; + int4store(cmd_buf, stmt->stmt_id); + if ((ret= stmt->mysql->methods->db_command(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); + 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; + } + } + + } + return(ret); +} + +static my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close) +{ + MYSQL *mysql= stmt->mysql; + my_bool ret= 1; + unsigned int flags= MADB_RESET_LONGDATA | MADB_RESET_BUFFER | MADB_RESET_ERROR; + + 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); + 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; + } + } + if (!is_close) + 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; + + return(ret); +} + +MYSQL_RES * STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt) +{ + MYSQL_RES *res; + + if (!stmt->field_count) + return(NULL); + + /* aloocate result set structutr and copy stmt information */ + if (!(res= (MYSQL_RES *)calloc(1, sizeof(MYSQL_RES)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(NULL); + } + + res->eof= 1; + res->fields= stmt->fields; + res->field_count= stmt->field_count; + return(res); +} + +my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) +{ + return mysql_stmt_internal_reset(stmt, 0); +} + +const char * STDCALL mysql_stmt_sqlstate(MYSQL_STMT *stmt) +{ + return stmt->sqlstate; +} + +MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt) +{ + 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 */ + + old_row= stmt->result_cursor; + stmt->result_cursor= new_row; + + return(old_row); +} + +my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, + const char *data, unsigned long length) +{ + 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); + return(1); + } + + if (param_number >= stmt->param_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + return(1); + } + + if (length || !stmt->params[param_number].long_data_used) + { + int ret; + size_t packet_len= STMT_ID_LENGTH + 2 + length; + uchar *cmd_buff= (uchar *)calloc(1, packet_len); + 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= stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_SEND_LONG_DATA, + (char *)cmd_buff, packet_len, 1, stmt); + free(cmd_buff); + return(ret); + } + return(0); +} + +unsigned long long STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt) +{ + return stmt->upsert_status.last_insert_id; +} + +unsigned long long STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) +{ + return stmt->result.rows; +} + +MYSQL_RES* STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt __attribute__((unused))) +{ + /* server doesn't deliver any information yet, + so we just return NULL + */ + 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; + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + 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); + return(1); + } + + if (!mysql_stmt_more_results(stmt)) + 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); + return(1); + } + + if (stmt->mysql->status == MYSQL_STATUS_GET_RESULT) + stmt->mysql->status= MYSQL_STATUS_STMT_RESULT; + + 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; + + return(rc); +} + +int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, + const char *stmt_str, + size_t length) +{ + MYSQL *mysql= stmt->mysql; + my_bool emulate_cmd= !(!(stmt->mysql->server_capabilities & CLIENT_MYSQL) && + (stmt->mysql->extension->mariadb_server_capabilities & + (MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32))); + + if (!mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + goto fail; + } + + /* Server versions < 10.2 don't support execute_direct, so we need to + emulate it */ + if (emulate_cmd) + { + int rc; + + /* avoid sending close + prepare in 2 packets */ + + if ((rc= mysql_stmt_prepare(stmt, stmt_str, length))) + return rc; + return mysql_stmt_execute(stmt); + } + + if (ma_multi_command(mysql, COM_MULTI_ENABLED)) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + goto fail; + } + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + return(1); + } + + if (length == (size_t) -1) + length= strlen(stmt_str); + + /* clear flags */ + CLEAR_CLIENT_STMT_ERROR(stmt); + CLEAR_CLIENT_ERROR(stmt->mysql); + stmt->upsert_status.affected_rows= mysql->affected_rows= (unsigned long long) ~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_internal_reset(stmt, 1)) + goto fail; + + ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(0)); + stmt->field_count= 0; + stmt->param_count= 0; + stmt->params= 0; + + int4store(stmt_id, stmt->stmt_id); + if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id, + sizeof(stmt_id), 1, stmt)) + goto fail; + } + stmt->stmt_id= -1; + if (mysql->methods->db_command(mysql, COM_STMT_PREPARE, stmt_str, length, 1, stmt)) + goto fail; + + stmt->state= MYSQL_STMT_PREPARED; + /* Since we can't determine stmt_id here, we need to set it to -1, so server will know that the + * execute command belongs to previous prepare */ + stmt->stmt_id= -1; + if (mysql_stmt_execute(stmt)) + goto fail; + + /* flush multi buffer */ + if (ma_multi_command(mysql, COM_MULTI_END)) + goto fail; + + /* read prepare response */ + if (mysql->methods->db_read_prepare_response && + 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; + } + + /* allocated bind buffer for result */ + if (stmt->field_count) + { + MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto fail; + } + memset(stmt->bind, 0, sizeof(MYSQL_BIND) * stmt->field_count); + } + stmt->state = MYSQL_STMT_PREPARED; + + /* read execute response packet */ + return stmt_read_execute_response(stmt); +fail: + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + do { + stmt->mysql->methods->db_stmt_flush_unbuffered(stmt); + } while(mysql_stmt_more_results(stmt)); + stmt->state= MYSQL_STMT_INITTED; + return 1; +} -- cgit v1.1