1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-14 03:37:16 +01:00
SqMod/module/Vendor/MDBC/libmariadb/my_stmt.c
2020-03-22 14:54:40 +02:00

1922 lines
56 KiB
C

/****************************************************************************
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 <http://www.gnu.org/licenses>
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 <georg@mysql.com> |
| Andrey Hristov <andrey@mysql.com> |
| Ulf Wendel <uwendel@mysql.com> |
+----------------------------------------------------------------------+
*/
#include "my_global.h"
#include <my_sys.h>
#include <mysys_err.h>
#include <m_string.h>
#include <m_ctype.h>
#include "mysql.h"
#include "mysql_priv.h"
#include "mysql_version.h"
#include "mysqld_error.h"
#include "errmsg.h"
#include <violite.h>
#include <sys/stat.h>
#include <signal.h>
#include <time.h>
#include <mysql/client_plugin.h>
#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= &current->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);
}