summaryrefslogtreecommitdiff
path: root/libmysql/libmysql.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmysql/libmysql.c')
-rw-r--r--libmysql/libmysql.c337
1 files changed, 287 insertions, 50 deletions
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index e63a583a0dd..4ecc1bd0584 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -85,6 +85,7 @@ my_bool net_flush(NET *net);
#define MAX_LONG_DATA_LENGTH 8192
#define unsigned_field(A) ((A)->flags & UNSIGNED_FLAG)
+static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data);
static void append_wild(char *to,char *end,const char *wild);
sig_handler pipe_sig_handler(int sig);
static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
@@ -112,7 +113,9 @@ static my_bool org_my_init_done= 0;
1 could not initialize environment (out of memory or thread keys)
*/
-int STDCALL mysql_server_init(int argc, char **argv, char **groups)
+int STDCALL mysql_server_init(int argc __attribute__((unused)),
+ char **argv __attribute__((unused)),
+ char **groups __attribute__((unused)))
{
int result= 0;
if (!mysql_client_init)
@@ -151,7 +154,9 @@ int STDCALL mysql_server_init(int argc, char **argv, char **groups)
#if defined(SIGPIPE) && !defined(__WIN__)
(void) signal(SIGPIPE, SIG_IGN);
#endif
+#ifdef EMBEDDED_LIBRARY
result= init_embedded_server(argc, argv, groups);
+#endif
}
#ifdef THREAD
else
@@ -163,7 +168,9 @@ int STDCALL mysql_server_init(int argc, char **argv, char **groups)
void STDCALL mysql_server_end()
{
+#ifdef EMBEDDED_LIBRARY
end_embedded_server();
+#endif
/* If library called my_init(), free memory allocated by it */
if (!org_my_init_done)
{
@@ -2131,17 +2138,6 @@ mysql_stmt_param_metadata(MYSQL_STMT *stmt)
Prepare-execute, and param handling
*********************************************************************/
-/*
- Store the buffer type
-*/
-
-static void store_param_type(NET *net, uint type)
-{
- int2store(net->write_pos, type);
- net->write_pos+=2;
-}
-
-
/****************************************************************************
Functions to store parameter data from a prepared statement.
@@ -2389,7 +2385,11 @@ int cli_stmt_execute(MYSQL_STMT *stmt)
that is sent to the server.
*/
for (param= stmt->params; param < param_end ; param++)
- store_param_type(net, (uint) param->buffer_type);
+ {
+ uint typecode= param->buffer_type | (param->is_unsigned ? 32768 : 0);
+ int2store(net->write_pos, typecode);
+ net->write_pos+= 2;
+ }
}
for (param= stmt->params; param < param_end; param++)
@@ -2514,6 +2514,52 @@ stmt_read_row_no_data(MYSQL_STMT *stmt __attribute__((unused)),
return MYSQL_NO_DATA;
}
+
+/*
+ Get/set statement attributes
+
+ SYNOPSIS
+ mysql_stmt_attr_get()
+ mysql_stmt_attr_set()
+
+ attr_type statemenet attribute
+ value casted to const void * pointer to value.
+
+ RETURN VALUE
+ 0 success
+ !0 wrong attribute type
+*/
+
+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= value ? *(const my_bool*) value : 0;
+ break;
+ default:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+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:
+ *(unsigned long *) value= stmt->update_max_length;
+ break;
+ default:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
/*
Execute the prepared query
*/
@@ -2754,12 +2800,13 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number,
DBUG_RETURN(1);
}
/* Mark for execute that the result is already sent */
- param->long_data_used= 1;
- if (length)
+ if (length || param->long_data_used == 0)
{
MYSQL *mysql= stmt->mysql;
char *packet, extra_data[MYSQL_LONG_DATA_HEADER];
+ param->long_data_used= 1;
+
packet= extra_data;
int4store(packet, stmt->stmt_id); packet+=4;
int2store(packet, param_number); packet+=2;
@@ -2888,10 +2935,12 @@ static uint read_binary_date(MYSQL_TIME *tm, uchar **pos)
}
/* Convert Numeric to buffer types */
-static void send_data_long(MYSQL_BIND *param, longlong value)
+static void send_data_long(MYSQL_BIND *param, MYSQL_FIELD *field,
+ longlong value)
{
char *buffer= param->buffer;
-
+ uint field_is_unsigned= (field->flags & UNSIGNED_FLAG);
+
switch (param->buffer_type) {
case MYSQL_TYPE_NULL: /* do nothing */
break;
@@ -2909,20 +2958,24 @@ static void send_data_long(MYSQL_BIND *param, longlong value)
break;
case MYSQL_TYPE_FLOAT:
{
- float data= (float)value;
+ float data= (field_is_unsigned ? (float) ulonglong2double(value) :
+ (float) value);
float4store(buffer, data);
break;
}
case MYSQL_TYPE_DOUBLE:
{
- double data= (double)value;
+ double data= (field_is_unsigned ? ulonglong2double(value) :
+ (double) value);
float8store(buffer, data);
break;
}
default:
{
char tmp[22]; /* Enough for longlong */
- uint length= (uint)(longlong10_to_str(value,(char *)tmp,10)-tmp);
+ uint length= (uint)(longlong10_to_str(value,(char *)tmp,
+ field_is_unsigned ? 10: -10) -
+ tmp);
ulong copy_length= min((ulong)length-param->offset, param->buffer_length);
if ((long) copy_length < 0)
copy_length=0;
@@ -3129,7 +3182,7 @@ static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row)
uint field_is_unsigned= (field->flags & UNSIGNED_FLAG);
longlong data= ((field_is_unsigned) ? (longlong) (unsigned char) value:
(longlong) value);
- send_data_long(param,data);
+ send_data_long(param, field, data);
length= 1;
break;
}
@@ -3140,7 +3193,7 @@ static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row)
uint field_is_unsigned= (field->flags & UNSIGNED_FLAG);
longlong data= ((field_is_unsigned) ? (longlong) (unsigned short) value:
(longlong) value);
- send_data_long(param,data);
+ send_data_long(param, field, data);
length= 2;
break;
}
@@ -3150,14 +3203,14 @@ static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row)
uint field_is_unsigned= (field->flags & UNSIGNED_FLAG);
longlong data= ((field_is_unsigned) ? (longlong) (unsigned long) value:
(longlong) value);
- send_data_long(param,data);
+ send_data_long(param, field, data);
length= 4;
break;
}
case MYSQL_TYPE_LONGLONG:
{
longlong value= (longlong)sint8korr(*row);
- send_data_long(param,value);
+ send_data_long(param, field, value);
length= 8;
break;
}
@@ -3216,28 +3269,28 @@ static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row)
static void fetch_result_tinyint(MYSQL_BIND *param, uchar **row)
{
- *param->buffer= (uchar) **row;
+ *param->buffer= **row;
(*row)++;
}
static void fetch_result_short(MYSQL_BIND *param, uchar **row)
{
short value = (short)sint2korr(*row);
- int2store(param->buffer, value);
+ shortstore(param->buffer, value);
*row+= 2;
}
static void fetch_result_int32(MYSQL_BIND *param, uchar **row)
{
int32 value= (int32)sint4korr(*row);
- int4store(param->buffer, value);
+ longstore(param->buffer, value);
*row+= 4;
}
static void fetch_result_int64(MYSQL_BIND *param, uchar **row)
{
longlong value= (longlong)sint8korr(*row);
- int8store(param->buffer, value);
+ longlongstore(param->buffer, value);
*row+= 8;
}
@@ -3245,7 +3298,7 @@ static void fetch_result_float(MYSQL_BIND *param, uchar **row)
{
float value;
float4get(value,*row);
- float4store(param->buffer, value);
+ floatstore(param->buffer, value);
*row+= 4;
}
@@ -3253,7 +3306,7 @@ static void fetch_result_double(MYSQL_BIND *param, uchar **row)
{
double value;
float8get(value,*row);
- float8store(param->buffer, value);
+ doublestore(param->buffer, value);
*row+= 8;
}
@@ -3298,12 +3351,50 @@ static void fetch_result_str(MYSQL_BIND *param, uchar **row)
/*
+ functions to calculate max lengths for strings during
+ mysql_stmt_store_result()
+*/
+
+static void skip_result_fixed(MYSQL_BIND *param,
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
+
+{
+ (*row)+= param->pack_length;
+}
+
+
+static void skip_result_with_length(MYSQL_BIND *param __attribute__((unused)),
+ MYSQL_FIELD *field __attribute__((unused)),
+ uchar **row)
+
+{
+ ulong length= net_field_length(row);
+ (*row)+= length;
+}
+
+
+static void skip_result_string(MYSQL_BIND *param __attribute__((unused)),
+ MYSQL_FIELD *field,
+ uchar **row)
+
+{
+ ulong length= net_field_length(row);
+ (*row)+= length;
+ if (field->max_length < length)
+ field->max_length= length;
+}
+
+
+
+/*
Setup the bind buffers for resultset processing
*/
my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
{
MYSQL_BIND *param, *end;
+ MYSQL_FIELD *field;
ulong bind_count;
uint param_count= 0;
DBUG_ENTER("mysql_stmt_bind_result");
@@ -3324,10 +3415,11 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
stmt->bind was initialized in mysql_stmt_prepare
*/
- memcpy((char*) stmt->bind, (char*) bind,
- sizeof(MYSQL_BIND)*bind_count);
+ memcpy((char*) stmt->bind, (char*) bind, sizeof(MYSQL_BIND) * bind_count);
- for (param= stmt->bind, end= param+bind_count; param < end ; param++)
+ for (param= stmt->bind, end= param + bind_count, field= stmt->fields ;
+ param < end ;
+ param++, field++)
{
/*
Set param->is_null to point to a dummy variable if it's not set.
@@ -3345,15 +3437,18 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
/* Setup data copy functions for the different supported types */
switch (param->buffer_type) {
case MYSQL_TYPE_NULL: /* for dummy binds */
+ *param->length= 0;
break;
case MYSQL_TYPE_TINY:
param->fetch_result= fetch_result_tinyint;
*param->length= 1;
break;
case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
param->fetch_result= fetch_result_short;
*param->length= 2;
break;
+ case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
param->fetch_result= fetch_result_int32;
*param->length= 4;
@@ -3402,6 +3497,58 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
param->buffer_type, param_count);
DBUG_RETURN(1);
}
+
+ /* Setup skip_result functions (to calculate max_length) */
+ param->skip_result= skip_result_fixed;
+ switch (field->type) {
+ case MYSQL_TYPE_NULL: /* for dummy binds */
+ param->pack_length= 0;
+ break;
+ case MYSQL_TYPE_TINY:
+ param->pack_length= 1;
+ break;
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ param->pack_length= 2;
+ break;
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ param->pack_length= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ param->pack_length= 8;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ param->pack_length= 4;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ param->pack_length= 8;
+ break;
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ param->skip_result= skip_result_with_length;
+ break;
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ param->skip_result= skip_result_string;
+ break;
+ default:
+ strmov(stmt->sqlstate, unknown_sqlstate);
+ sprintf(stmt->last_error,
+ ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
+ field->type, param_count);
+ DBUG_RETURN(1);
+ }
}
stmt->bind_result_done= TRUE;
DBUG_RETURN(0);
@@ -3415,7 +3562,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
{
MYSQL_BIND *bind, *end;
- MYSQL_FIELD *field, *field_end;
+ MYSQL_FIELD *field;
uchar *null_ptr, bit;
/*
Precondition: if stmt->field_count is zero or row is NULL, read_row_*
@@ -3435,17 +3582,25 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
bit= 4; /* first 2 bits are reserved */
/* Copy complete row to application buffers */
- for (bind= stmt->bind, end= (MYSQL_BIND *) bind + stmt->field_count,
- field= stmt->fields,
- field_end= (MYSQL_FIELD *)stmt->fields+stmt->field_count;
- bind < end && field < field_end;
+ for (bind= stmt->bind, end= bind + stmt->field_count, field= stmt->fields ;
+ bind < end ;
bind++, field++)
{
if (*null_ptr & bit)
- *bind->is_null= bind->null_field= 1;
+ {
+ /*
+ We should set both inter_buffer and is_null to be able to see
+ nulls in mysql_stmt_fetch_column. This is because is_null may point
+ to user data which can be overwritten between mysql_stmt_fetch and
+ mysql_stmt_fetch_column, and in this case nullness of column will be
+ lost. See mysql_stmt_fetch_column for details.
+ */
+ bind->inter_buffer= NULL;
+ *bind->is_null= 1;
+ }
else
{
- *bind->is_null= bind->null_field= 0;
+ *bind->is_null= 0;
bind->inter_buffer= row;
if (field->type == bind->buffer_type)
(*bind->fetch_result)(bind, &row);
@@ -3461,6 +3616,7 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
return 0;
}
+
int cli_unbuffered_fetch(MYSQL *mysql, char **row)
{
if (packet_error == net_safe_read(mysql))
@@ -3471,6 +3627,7 @@ int cli_unbuffered_fetch(MYSQL *mysql, char **row)
return 0;
}
+
/*
Fetch and return row data to bound buffers, if any
*/
@@ -3529,14 +3686,8 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind,
DBUG_RETURN(1);
}
-
- if (param->null_field)
+ if (param->inter_buffer)
{
- if (bind->is_null)
- *bind->is_null= 1;
- }
- else
- {
MYSQL_FIELD *field= stmt->fields+column;
uchar *row= param->inter_buffer;
bind->offset= offset;
@@ -3548,6 +3699,11 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind,
bind->length= &param->internal_length; /* Needed for fetch_result() */
fetch_results(bind, field, &row);
}
+ else
+ {
+ if (bind->is_null)
+ *bind->is_null= 1;
+ }
DBUG_RETURN(0);
}
@@ -3568,6 +3724,28 @@ int cli_read_binary_rows(MYSQL_STMT *stmt)
mysql= mysql->last_used_con;
+ if (stmt->update_max_length && !stmt->bind_result_done)
+ {
+ /*
+ We must initalize the bind structure to be able to calculate
+ max_length
+ */
+ MYSQL_BIND *bind, *end;
+ MYSQL_FIELD *field;
+ bzero((char*) stmt->bind, sizeof(*stmt->bind)* stmt->field_count);
+
+ for (bind= stmt->bind, end= bind + stmt->field_count, field= stmt->fields;
+ bind < end ;
+ bind++, field++)
+ {
+ bind->buffer_type= field->type;
+ bind->buffer_length=1;
+ }
+
+ mysql_stmt_bind_result(stmt, stmt->bind);
+ stmt->bind_result_done= 0; /* No normal bind done */
+ }
+
while ((pkt_len= net_safe_read(mysql)) != packet_error)
{
cp= net->read_pos;
@@ -3577,13 +3755,16 @@ int cli_read_binary_rows(MYSQL_STMT *stmt)
sizeof(MYSQL_ROWS) + pkt_len - 1)))
{
set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
- DBUG_RETURN(1);
+ goto err;
}
cur->data= (MYSQL_ROW) (cur+1);
*prev_ptr= cur;
prev_ptr= &cur->next;
memcpy((char *) cur->data, (char *) cp+1, pkt_len-1);
- ++result->rows;
+ cur->length= pkt_len; /* To allow us to do sanity checks */
+ result->rows++;
+ if (stmt->update_max_length)
+ stmt_update_metadata(stmt, cur);
}
else
{
@@ -3595,6 +3776,8 @@ int cli_read_binary_rows(MYSQL_STMT *stmt)
}
}
set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate);
+
+err:
DBUG_RETURN(1);
}
@@ -3805,6 +3988,7 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
{
char buff[MYSQL_STMT_HEADER];
MYSQL *mysql;
+ MYSQL_BIND *param, *param_end;
DBUG_ENTER("mysql_stmt_reset");
DBUG_ASSERT(stmt != 0);
@@ -3814,12 +3998,20 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
mysql= stmt->mysql->last_used_con;
int4store(buff, stmt->stmt_id); /* Send stmt id to server */
- if ((*mysql->methods->advanced_command)(mysql, COM_RESET_STMT,buff,MYSQL_STMT_HEADER,0,0,1))
+ if ((*mysql->methods->advanced_command)(mysql, COM_RESET_STMT, buff,
+ MYSQL_STMT_HEADER,0,0,0))
{
set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
mysql->net.sqlstate);
DBUG_RETURN(1);
}
+
+ /* Clear long_data_used for next call (as we do in mysql_stmt_execute() */
+ for (param= stmt->params, param_end= param + stmt->param_count;
+ param < param_end;
+ param++)
+ param->long_data_used= 0;
+
DBUG_RETURN(0);
}
@@ -3849,6 +4041,51 @@ const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt)
DBUG_RETURN(stmt->last_error);
}
+
+/*
+ Update meta data for statement
+
+ SYNOPSIS
+ stmt_update_metadata()
+ stmt Statement handler
+ row Binary data
+
+ NOTES
+ Only updates MYSQL_FIELD->max_length for strings
+
+*/
+
+static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data)
+{
+ MYSQL_BIND *bind, *end;
+ MYSQL_FIELD *field;
+ uchar *null_ptr, bit;
+ uchar *row= (uchar*) data->data;
+#ifndef DBUG_OFF
+ uchar *row_end= row + data->length;
+#endif
+
+ null_ptr= row;
+ row+= (stmt->field_count+9)/8; /* skip null bits */
+ bit= 4; /* first 2 bits are reserved */
+
+ /* Go throw all fields and calculate metadata */
+ for (bind= stmt->bind, end= bind + stmt->field_count, field= stmt->fields ;
+ bind < end ;
+ bind++, field++)
+ {
+ if (!(*null_ptr & bit))
+ (*bind->skip_result)(bind, field, &row);
+ DBUG_ASSERT(row <= row_end);
+ if (!((bit<<=1) & 255))
+ {
+ bit= 1; /* To next byte */
+ null_ptr++;
+ }
+ }
+}
+
+
/********************************************************************
Transactional APIs
*********************************************************************/