summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BitKeeper/etc/logging_ok2
-rw-r--r--include/errmsg.h2
-rw-r--r--include/my_pthread.h5
-rw-r--r--include/mysql.h23
-rw-r--r--libmysql/libmysql.c511
-rw-r--r--libmysqld/lib_sql.cc10
-rw-r--r--myisam/mi_open.c6
-rw-r--r--mysql-test/r/analyse.result2
-rw-r--r--mysql-test/r/row.result19
-rw-r--r--mysql-test/t/row.test17
-rw-r--r--mysys/mf_keycache.c2229
-rw-r--r--sql/field.cc51
-rw-r--r--sql/ha_innodb.cc21
-rw-r--r--sql/item.cc29
-rw-r--r--sql/item.h7
-rw-r--r--sql/item_cmpfunc.cc68
-rw-r--r--sql/item_cmpfunc.h19
-rw-r--r--sql/item_strfunc.cc7
-rw-r--r--sql/mysqld.cc274
-rw-r--r--sql/protocol.cc1
-rw-r--r--sql/sql_parse.cc11
-rw-r--r--sql/sql_prepare.cc93
-rw-r--r--sql/sql_select.cc5
-rw-r--r--sql/sql_string.cc2
-rw-r--r--sql/sql_yacc.yy72
-rw-r--r--strings/ctype-big5.c6
-rw-r--r--strings/ctype-bin.c6
-rw-r--r--strings/ctype-euc_kr.c6
-rw-r--r--strings/ctype-gb2312.c6
-rw-r--r--strings/ctype-gbk.c6
-rw-r--r--strings/ctype-simple.c8
-rw-r--r--strings/ctype-sjis.c6
-rw-r--r--strings/ctype-ujis.c3
-rw-r--r--strings/ctype-utf8.c6
-rw-r--r--tests/client_test.c1686
35 files changed, 3203 insertions, 2022 deletions
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index c5970cdd45d..cc11f43b899 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -26,6 +26,7 @@ hf@bison.(none)
hf@bisonxp.(none)
hf@deer.mysql.r18.ru
hf@genie.(none)
+igor@hundin.mysql.fi
jani@dsl-jkl1657.dial.inet.fi
jani@dsl-kpogw4gb5.dial.inet.fi
jani@hynda.(none)
@@ -95,6 +96,7 @@ tonu@hundin.mysql.fi
tonu@volk.internalnet
tonu@x153.internalnet
tonu@x3.internalnet
+venu@hundin.mysql.fi
venu@myvenu.com
venu@work.mysql.com
vva@eagle.mysql.r18.ru
diff --git a/include/errmsg.h b/include/errmsg.h
index 5f462565e33..1f4e6e12f00 100644
--- a/include/errmsg.h
+++ b/include/errmsg.h
@@ -30,7 +30,7 @@ extern const char *client_errors[]; /* Error messages */
#define CR_MAX_ERROR 2999
#if defined(OS2) && defined(MYSQL_SERVER)
#define CER(X) client_errors[(X)-CR_MIN_ERROR]
-#else
+#elif !defined(ER)
#define ER(X) client_errors[(X)-CR_MIN_ERROR]
#endif
#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */
diff --git a/include/my_pthread.h b/include/my_pthread.h
index e0394bc978a..355d7c2e147 100644
--- a/include/my_pthread.h
+++ b/include/my_pthread.h
@@ -1,3 +1,4 @@
+
/* Copyright (C) 2000 MySQL AB
This program is free software; you can redistribute it and/or modify
@@ -587,7 +588,7 @@ extern int pthread_dummy(int);
*/
#define DEFAULT_THREAD_STACK (192*1024L)
#else
-#define DEFAULT_THREAD_STACK (192*1024L)
+#define DEFAULT_THREAD_STACK (192*1024)
#endif
struct st_my_thread_var
@@ -601,6 +602,8 @@ struct st_my_thread_var
long id;
int cmp_length;
int volatile abort;
+ struct st_my_thread_var *next,**prev;
+ void *opt_info;
#ifndef DBUG_OFF
gptr dbug;
char name[THREAD_NAME_SIZE+1];
diff --git a/include/mysql.h b/include/mysql.h
index d41b82172a9..82badb99694 100644
--- a/include/mysql.h
+++ b/include/mysql.h
@@ -470,6 +470,24 @@ int STDCALL mysql_manager_fetch_line(MYSQL_MANAGER* con,
/* statement state */
enum PREP_STMT_STATE { MY_ST_UNKNOWN, MY_ST_PREPARE, MY_ST_EXECUTE };
+/*
+ client TIME structure to handle TIME, DATE and TIMESTAMP directly in
+ binary protocol
+*/
+enum mysql_st_timestamp_type { MYSQL_TIMESTAMP_NONE, MYSQL_TIMESTAMP_DATE,
+ MYSQL_TIMESTAMP_FULL, MYSQL_TIMESTAMP_TIME };
+
+typedef struct mysql_st_time
+{
+ unsigned int year,month,day,hour,minute,second;
+ unsigned long second_part;
+ my_bool neg;
+
+ enum mysql_st_timestamp_type time_type;
+
+} MYSQL_TIME;
+
+
/* bind structure */
typedef struct st_mysql_bind
{
@@ -481,7 +499,6 @@ typedef struct st_mysql_bind
unsigned long buffer_length; /* buffer length */
/* The following are for internal use. Set by mysql_bind_param */
- unsigned long bind_length; /* Default length of data */
unsigned int param_number; /* For null count and error messages */
my_bool long_data_used; /* If used with mysql_send_long_data */
void (*store_param_func)(NET *net, struct st_mysql_bind *param);
@@ -499,12 +516,9 @@ typedef struct st_mysql_stmt
MYSQL_FIELD *fields; /* prepare meta info */
LIST list; /* list to keep track of all stmts */
char *query; /* query buffer */
- char *buffer; /* buffer to hold results */
MEM_ROOT mem_root; /* root allocations */
- MYSQL_RES tmp_result; /* Used by mysql_prepare_result */
unsigned long param_count; /* parameters count */
unsigned long field_count; /* fields count */
- unsigned long buffer_length; /* long buffer alloced length */
unsigned long stmt_id; /* Id for prepared statement */
unsigned int last_errno; /* error code */
enum PREP_STMT_STATE state; /* statement state */
@@ -535,6 +549,7 @@ my_bool STDCALL mysql_send_long_data(MYSQL_STMT *stmt,
const char *data,
unsigned long length);
MYSQL_RES *STDCALL mysql_prepare_result(MYSQL_STMT *stmt);
+MYSQL_RES *STDCALL mysql_param_result(MYSQL_STMT *stmt);
my_ulonglong STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt);
int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt);
my_bool STDCALL mysql_more_results(MYSQL *mysql);
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 3e31f254913..2b97f6b90e4 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -115,7 +115,6 @@ static sig_handler pipe_sig_handler(int sig);
static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
const char *from, ulong length);
static my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list);
-static unsigned int get_binary_length(uint type);
static my_bool org_my_init_done=0;
@@ -3952,6 +3951,27 @@ mysql_prepare_result(MYSQL_STMT *stmt)
DBUG_RETURN(result);
}
+/*
+ Returns parameter columns meta information in the form of
+ resultset.
+*/
+
+MYSQL_RES * STDCALL
+mysql_param_result(MYSQL_STMT *stmt)
+{
+ DBUG_ENTER("mysql_param_result");
+
+ if (!stmt->param_count)
+ DBUG_RETURN(0);
+
+ /*
+ TODO: Fix this when server sends the information.
+ Till then keep a dummy prototype
+ */
+ DBUG_RETURN(0);
+}
+
+
/********************************************************************
Prepare-execute, and param handling
@@ -4056,9 +4076,74 @@ static void store_param_double(NET *net, MYSQL_BIND *param)
net->write_pos+= 8;
}
+static void store_param_time(NET *net, MYSQL_BIND *param)
+{
+ MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer;
+ char buff[15], *pos;
+ uint length;
+
+ pos= buff+1;
+ pos[0]= tm->neg ? 1: 0;
+ int4store(pos+1, tm->day);
+ pos[5]= (uchar) tm->hour;
+ pos[6]= (uchar) tm->minute;
+ pos[7]= (uchar) tm->second;
+ int4store(pos+8, tm->second_part);
+ if (tm->second_part)
+ length= 11;
+ else if (tm->hour || tm->minute || tm->second || tm->day)
+ length= 8;
+ else
+ length= 0;
+ buff[0]= (char) length++;
+ memcpy((char *)net->write_pos, buff, length);
+ net->write_pos+= length;
+}
+
+static void net_store_datetime(NET *net, MYSQL_TIME *tm)
+{
+ char buff[12], *pos;
+ uint length;
+
+ pos= buff+1;
+
+ int2store(pos, tm->year);
+ pos[2]= (uchar) tm->month;
+ pos[3]= (uchar) tm->day;
+ pos[4]= (uchar) tm->hour;
+ pos[5]= (uchar) tm->minute;
+ pos[6]= (uchar) tm->second;
+ int4store(pos+7, tm->second_part);
+ if (tm->second_part)
+ length= 11;
+ else if (tm->hour || tm->minute || tm->second)
+ length= 7;
+ else if (tm->year || tm->month || tm->day)
+ length= 4;
+ else
+ length= 0;
+ buff[0]= (char) length++;
+ memcpy((char *)net->write_pos, buff, length);
+ net->write_pos+= length;
+}
+
+static void store_param_date(NET *net, MYSQL_BIND *param)
+{
+ MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer;
+ tm->hour= tm->minute= tm->second= 0;
+ tm->second_part= 0;
+ net_store_datetime(net, tm);
+}
+
+static void store_param_datetime(NET *net, MYSQL_BIND *param)
+{
+ MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer;
+ net_store_datetime(net, tm);
+}
+
static void store_param_str(NET *net, MYSQL_BIND *param)
{
- ulong length= *param->length;
+ ulong length= min(*param->length, param->buffer_length);
char *to= (char *) net_store_length((char *) net->write_pos, length);
memcpy(to, param->buffer, length);
net->write_pos= (uchar*) to+length;
@@ -4107,10 +4192,13 @@ static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param)
{
/*
Param->length should ALWAYS point to the correct length for the type
- Either to the length pointer given by the user or param->bind_length
+ Either to the length pointer given by the user or param->buffer_length
*/
if ((my_realloc_str(net, 9 + *param->length)))
+ {
+ set_stmt_error(stmt, CR_OUT_OF_MEMORY);
DBUG_RETURN(1);
+ }
(*param->store_param_func)(net, param);
}
DBUG_RETURN(0);
@@ -4277,12 +4365,14 @@ my_bool STDCALL mysql_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
param++)
{
param->param_number= count++;
+ param->long_data_used= 0;
+
/*
- If param->length is not given, change it to point to bind_length.
+ If param->length is not given, change it to point to buffer_length.
This way we can always use *param->length to get the length of data
*/
if (!param->length)
- param->length= &param->bind_length;
+ param->length= &param->buffer_length;
/* If param->is_null is not set, then the value can never be NULL */
if (!param->is_null)
@@ -4295,42 +4385,52 @@ my_bool STDCALL mysql_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
break;
case MYSQL_TYPE_TINY:
/* Force param->length as this is fixed for this type */
- param->length= &param->bind_length;
- param->bind_length= param->buffer_length= 1;
+ param->length= &param->buffer_length;
+ param->buffer_length= 1;
param->store_param_func= store_param_tinyint;
break;
case MYSQL_TYPE_SHORT:
- param->length= &param->bind_length;
- param->bind_length= param->buffer_length= 2;
+ param->length= &param->buffer_length;
+ param->buffer_length= 2;
param->store_param_func= store_param_short;
break;
case MYSQL_TYPE_LONG:
- param->length= &param->bind_length;
- param->bind_length= param->buffer_length= 4;
+ param->length= &param->buffer_length;
+ param->buffer_length= 4;
param->store_param_func= store_param_int32;
break;
case MYSQL_TYPE_LONGLONG:
- param->length= &param->bind_length;
- param->bind_length= param->buffer_length= 8;
+ param->length= &param->buffer_length;
+ param->buffer_length= 8;
param->store_param_func= store_param_int64;
break;
case MYSQL_TYPE_FLOAT:
- param->length= &param->bind_length;
- param->bind_length= param->buffer_length= 4;
+ param->length= &param->buffer_length;
+ param->buffer_length= 4;
param->store_param_func= store_param_float;
break;
case MYSQL_TYPE_DOUBLE:
- param->length= &param->bind_length;
- param->bind_length= param->buffer_length= 8;
+ param->length= &param->buffer_length;
+ param->buffer_length= 8;
param->store_param_func= store_param_double;
break;
+ case MYSQL_TYPE_TIME:
+ /* Buffer length ignored for DATE, TIME and DATETIME */
+ param->store_param_func= store_param_time;
+ break;
+ case MYSQL_TYPE_DATE:
+ param->store_param_func= store_param_date;
+ break;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ param->store_param_func= store_param_datetime;
+ break;
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->bind_length= param->buffer_length;
param->store_param_func= store_param_str;
break;
default:
@@ -4440,24 +4540,91 @@ mysql_send_long_data(MYSQL_STMT *stmt, uint param_number,
1 Error (Can't alloc net->buffer)
****************************************************************************/
-/* Return the default binary data length for the common types */
-static unsigned int get_binary_length(uint type)
+static void set_zero_time(MYSQL_TIME *tm)
{
- switch(type) {
- case MYSQL_TYPE_TINY:
- return 1;
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_YEAR:
- return 2;
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_FLOAT:
- return 4;
- case MYSQL_TYPE_LONGLONG:
- case MYSQL_TYPE_DOUBLE:
- return 8;
- default:
+ tm->year= tm->month= tm->day= 0;
+ tm->hour= tm->minute= tm->second= 0;
+ tm->second_part= 0;
+ tm->neg= (bool)0;
+}
+
+/* Read TIME from binary packet and return it to MYSQL_TIME */
+static uint read_binary_time(MYSQL_TIME *tm, uchar **pos)
+{
+ uchar *to;
+ uint length;
+
+ if (!(length= net_field_length(pos)))
+ {
+ set_zero_time(tm);
return 0;
}
+
+ to= *pos;
+ tm->second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0;
+
+ tm->day= (ulong) sint4korr(to+1);
+ tm->hour= (uint) to[5];
+ tm->minute= (uint) to[6];
+ tm->second= (uint) to[7];
+
+ tm->year= tm->month= 0;
+ tm->neg= (bool)to[0];
+ return length;
+}
+
+/* Read DATETIME from binary packet and return it to MYSQL_TIME */
+static uint read_binary_datetime(MYSQL_TIME *tm, uchar **pos)
+{
+ uchar *to;
+ uint length;
+
+ if (!(length= net_field_length(pos)))
+ {
+ set_zero_time(tm);
+ return 0;
+ }
+
+ to= *pos;
+ tm->second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0;
+
+ if (length > 4)
+ {
+ tm->hour= (uint) to[4];
+ tm->minute= (uint) to[5];
+ tm->second= (uint) to[6];
+ }
+ else
+ tm->hour= tm->minute= tm->second= 0;
+
+ tm->year= (uint) sint2korr(to);
+ tm->month= (uint) to[2];
+ tm->day= (uint) to[3];
+ tm->neg= 0;
+ return length;
+}
+
+/* Read DATE from binary packet and return it to MYSQL_TIME */
+static uint read_binary_date(MYSQL_TIME *tm, uchar **pos)
+{
+ uchar *to;
+ uint length;
+
+ if (!(length= net_field_length(pos)))
+ {
+ set_zero_time(tm);
+ return 0;
+ }
+
+ to= *pos;
+ tm->year = (uint) sint2korr(to);
+ tm->month= (uint) to[2];
+ tm->day= (uint) to[3];
+
+ tm->hour= tm->minute= tm->second= 0;
+ tm->second_part= 0;
+ tm->neg= 0;
+ return length;
}
/* Convert Numeric to buffer types */
@@ -4492,13 +4659,14 @@ static void send_data_long(MYSQL_BIND *param, longlong value)
}
default:
{
- uint length= sprintf(buffer,"%lld",value);
+ uint length= (uint)(longlong10_to_str(value,buffer,10)-buffer);
*param->length= length;
buffer[length]='\0';
}
}
}
+
/* Convert Double to buffer types */
static void send_data_double(MYSQL_BIND *param, double value)
{
@@ -4531,7 +4699,7 @@ static void send_data_double(MYSQL_BIND *param, double value)
}
default:
{
- uint length= sprintf(buffer,"%g",value);
+ uint length= my_sprintf(buffer,(buffer,"%g",value));
*param->length= length;
buffer[length]='\0';
}
@@ -4586,25 +4754,79 @@ static void send_data_str(MYSQL_BIND *param, char *value, uint length)
}
default:
*param->length= length;
+ length= min(length, param->buffer_length);
memcpy(buffer, value, length);
- buffer[length]='\0';
+ if (length != param->buffer_length)
+ buffer[length]='\0';
}
}
+static void send_data_time(MYSQL_BIND *param, MYSQL_TIME ltime,
+ uint length)
+{
+ switch (param->buffer_type) {
+
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
+
+ tm->year= ltime.year;
+ tm->month= ltime.month;
+ tm->day= ltime.day;
+
+ tm->hour= ltime.hour;
+ tm->minute= ltime.minute;
+ tm->second= ltime.second;
+
+ tm->second_part= ltime.second_part;
+ tm->neg= ltime.neg;
+ break;
+ }
+ default:
+ {
+ char buff[25];
+
+ if (!length)
+ ltime.time_type= MYSQL_TIMESTAMP_NONE;
+ switch (ltime.time_type) {
+ case MYSQL_TIMESTAMP_DATE:
+ length= my_sprintf(buff,(buff, "%04d-%02d-%02d", ltime.year,
+ ltime.month,ltime.day));
+ break;
+ case MYSQL_TIMESTAMP_FULL:
+ length= my_sprintf(buff,(buff, "%04d-%02d-%02d %02d:%02d:%02d",
+ ltime.year,ltime.month,ltime.day,
+ ltime.hour,ltime.minute,ltime.second));
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ length= my_sprintf(buff, (buff, "%02d:%02d:%02d",
+ ltime.hour,ltime.minute,ltime.second));
+ break;
+ default:
+ length= 0;
+ buff[0]='\0';
+ }
+ send_data_str(param, (char *)buff, length);
+ }
+ }
+}
+
+
/* Fetch data to buffers */
-static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param,
- uint field_type, uchar **row)
+static void fetch_results(MYSQL_BIND *param, uint field_type, uchar **row)
{
ulong length;
- length= (ulong)get_binary_length(field_type);
-
switch (field_type) {
case MYSQL_TYPE_TINY:
{
uchar value= (uchar) **row;
send_data_long(param,(longlong)value);
+ length= 1;
break;
}
case MYSQL_TYPE_SHORT:
@@ -4612,18 +4834,21 @@ static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param,
{
short value= (short)sint2korr(*row);
send_data_long(param,(longlong)value);
+ length= 2;
break;
}
case MYSQL_TYPE_LONG:
{
int32 value= (int32)sint4korr(*row);
send_data_long(param,(int32)value);
+ length= 4;
break;
}
case MYSQL_TYPE_LONGLONG:
{
longlong value= (longlong)sint8korr(*row);
send_data_long(param,value);
+ length= 8;
break;
}
case MYSQL_TYPE_FLOAT:
@@ -4631,6 +4856,7 @@ static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param,
float value;
float4get(value,*row);
send_data_double(param,(double)value);
+ length= 4;
break;
}
case MYSQL_TYPE_DOUBLE:
@@ -4638,115 +4864,35 @@ static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param,
double value;
float8get(value,*row);
send_data_double(param,(double)value);
+ length= 8;
break;
}
case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
{
- uchar month,day;
- short year;
- int arg_length;
- char ts[50],frac[10],time[20],date[20];
-
- if (!(length= net_field_length(row)))
- {
- *param->length= 0;
- break;
- }
- if (param->buffer_type < MYSQL_TYPE_VAR_STRING ||
- param->buffer_type > MYSQL_TYPE_STRING)
- {
- /*
- Don't allow fetching of date/time/ts to non-string types
- TODO: Allow fetching of date or time to long types.
- */
- sprintf(stmt->last_error,
- ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
- param->buffer_type, param->param_number);
- return 1;
- }
-
- arg_length= 0;
- if (length > 7)
- {
- int sec_part= sint4korr(*row+7);
- sprintf(frac,".%04d", sec_part);
- arg_length+= 5;
- }
- if (length == 7)
- {
- uchar hour, minute, sec;
- hour= *(*row+4);
- minute= *(*row+5);
- sec= *(*row+6);
- sprintf((char *)time," %02d:%02d:%02d",hour,minute,sec);
- arg_length+= 9;
- }
-
- year= sint2korr(*row);
- month= *(*row+2);
- day= *(*row+3);
- sprintf((char*) date,"%04d-%02d-%02d",year,month,day);
- arg_length+= 10;
-
- if (arg_length != 19)
- time[0]='\0';
- if (arg_length != 24)
- frac[0]='\0';
-
- strxmov(ts,date,time,frac,NullS);
- send_data_str(param,ts,arg_length);
+ MYSQL_TIME tm;
+
+ length= read_binary_date(&tm,row);
+ tm.time_type= MYSQL_TIMESTAMP_DATE;
+ send_data_time(param, tm, length);
break;
}
case MYSQL_TYPE_TIME:
{
- int day, arg_length;
- uchar hour, minute, sec;
- char ts[255], frac[20], time[20];
- const char *sign= "";
-
- if (!(length= net_field_length(row)))
- {
- *param->length= 0;
- break;
- }
- if (param->buffer_type < MYSQL_TYPE_VAR_STRING ||
- param->buffer_type > MYSQL_TYPE_STRING)
- {
- /*
- Don't allow fetching of date/time/ts to non-string types
-
- TODO: Allow fetching of time to long types without
- any conversion.
- */
- sprintf(stmt->last_error,
- ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
- param->buffer_type, param->param_number);
- return 1;
- }
- arg_length= 0;
- if (length > 8)
- {
- int sec_part= sint4korr(*row+8);
- sprintf(frac,".%04d", sec_part);
- arg_length+= 5;
- }
-
- if (**row)
- sign="-";
-
- day= sint4korr(*row); /* TODO: how to handle this case */
- hour= *(*row+5);
- minute= *(*row+6);
- sec= *(*row+7);
- arg_length+= sprintf((char *)time,"%s%02d:%02d:%02d",sign,hour,minute,sec);
-
- if (arg_length <= 9)
- frac[0]='\0';
-
- strxmov(ts,time,frac,NullS);
- send_data_str(param,ts,arg_length);
+ MYSQL_TIME tm;
+
+ length= read_binary_time(&tm, row);
+ tm.time_type= MYSQL_TIMESTAMP_TIME;
+ send_data_time(param, tm, length);
+ break;
+ }
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ MYSQL_TIME tm;
+
+ length= read_binary_datetime(&tm, row);
+ tm.time_type= MYSQL_TIMESTAMP_FULL;
+ send_data_time(param, tm, length);
break;
}
default:
@@ -4755,7 +4901,6 @@ static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param,
break;
}
*row+= length;
- return 0;
}
static void fetch_result_tinyint(MYSQL_BIND *param, uchar **row)
@@ -4801,12 +4946,33 @@ static void fetch_result_double(MYSQL_BIND *param, uchar **row)
*row+= 8;
}
+static void fetch_result_time(MYSQL_BIND *param, uchar **row)
+{
+ MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
+ *row+= read_binary_time(tm, row);
+}
+
+static void fetch_result_date(MYSQL_BIND *param, uchar **row)
+{
+ MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
+ *row+= read_binary_date(tm, row);
+}
+
+static void fetch_result_datetime(MYSQL_BIND *param, uchar **row)
+{
+ MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
+ *row+= read_binary_datetime(tm, row);
+}
+
static void fetch_result_str(MYSQL_BIND *param, uchar **row)
{
ulong length= net_field_length(row);
- memcpy(param->buffer, (char *)*row, length);
- *(param->buffer+length)= '\0';
- *param->length= length;
+ ulong copy_length= min(length, param->buffer_length);
+ memcpy(param->buffer, (char *)*row, copy_length);
+ /* Add an end null if there is room in the buffer */
+ if (copy_length != param->buffer_length)
+ *(param->buffer+copy_length)= '\0';
+ *param->length= length; // return total length
*row+= length;
}
@@ -4846,25 +5012,47 @@ my_bool STDCALL mysql_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
if (!param->is_null)
param->is_null= &int_is_null_dummy;
+ if (!param->length)
+ param->length= &param->buffer_length;
+
/* Setup data copy functions for the different supported types */
switch (param->buffer_type) {
case MYSQL_TYPE_TINY:
param->fetch_result= fetch_result_tinyint;
+ *param->length= 1;
break;
case MYSQL_TYPE_SHORT:
param->fetch_result= fetch_result_short;
+ *param->length= 2;
break;
case MYSQL_TYPE_LONG:
param->fetch_result= fetch_result_int32;
+ *param->length= 4;
break;
case MYSQL_TYPE_LONGLONG:
param->fetch_result= fetch_result_int64;
+ *param->length= 8;
break;
case MYSQL_TYPE_FLOAT:
param->fetch_result= fetch_result_float;
+ *param->length= 4;
break;
case MYSQL_TYPE_DOUBLE:
param->fetch_result= fetch_result_double;
+ *param->length= 8;
+ break;
+ case MYSQL_TYPE_TIME:
+ param->fetch_result= fetch_result_time;
+ *param->length= sizeof(MYSQL_TIME);
+ break;
+ case MYSQL_TYPE_DATE:
+ param->fetch_result= fetch_result_date;
+ *param->length= sizeof(MYSQL_TIME);
+ break;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ param->fetch_result= fetch_result_datetime;
+ *param->length= sizeof(MYSQL_TIME);
break;
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
@@ -4881,9 +5069,6 @@ my_bool STDCALL mysql_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
param->buffer_type, param->param_number);
DBUG_RETURN(1);
}
- if (!param->length)
- param->length= &param->bind_length;
- *param->length= (long)get_binary_length(param->buffer_type);
}
stmt->res_buffers= 1;
DBUG_RETURN(0);
@@ -4920,8 +5105,8 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
*bind->is_null= 0;
if (field->type == bind->buffer_type)
(*bind->fetch_result)(bind, &row);
- else if (fetch_results(stmt, bind, field->type, &row))
- return 1;
+ else
+ fetch_results(bind, field->type, &row);
}
if (! ((bit<<=1) & 255))
{
@@ -5052,7 +5237,7 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
{
MYSQL *mysql= stmt->mysql;
MYSQL_RES *result;
- DBUG_ENTER("mysql_stmt_tore_result");
+ DBUG_ENTER("mysql_stmt_store_result");
mysql= mysql->last_used_con;
@@ -5060,9 +5245,8 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
DBUG_RETURN(0);
if (mysql->status != MYSQL_STATUS_GET_RESULT)
{
- strmov(mysql->net.last_error,
- ER(mysql->net.last_errno= CR_COMMANDS_OUT_OF_SYNC));
- DBUG_RETURN(0);
+ set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC);
+ DBUG_RETURN(1);
}
mysql->status= MYSQL_STATUS_READY; /* server is ready */
if (!(result= (MYSQL_RES*) my_malloc((uint) (sizeof(MYSQL_RES)+
@@ -5070,8 +5254,7 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
stmt->field_count),
MYF(MY_WME | MY_ZEROFILL))))
{
- mysql->net.last_errno= CR_OUT_OF_MEMORY;
- strmov(mysql->net.last_error, ER(mysql->net.last_errno));
+ set_stmt_error(stmt, CR_OUT_OF_MEMORY);
DBUG_RETURN(1);
}
stmt->result_buffered= 1;
@@ -5106,7 +5289,7 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
*/
static my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list)
{
- my_bool error=0;
+ my_bool error= 0;
DBUG_ENTER("mysql_stmt_close");
DBUG_ASSERT(stmt != 0);
@@ -5116,12 +5299,14 @@ static my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list)
int4store(buff, stmt->stmt_id);
error= simple_command(stmt->mysql, COM_CLOSE_STMT, buff, 4, 1);
}
- mysql_free_result(stmt->result);
- free_root(&stmt->mem_root, MYF(0));
- my_free((gptr) stmt->query, MYF(MY_WME | MY_ALLOW_ZERO_PTR));
- if (!skip_list)
- stmt->mysql->stmts= list_delete(stmt->mysql->stmts, &stmt->list);
- my_free((gptr) stmt, MYF(MY_WME));
+ if (!error)
+ {
+ mysql_free_result(stmt->result);
+ free_root(&stmt->mem_root, MYF(0));
+ if (!skip_list)
+ stmt->mysql->stmts= list_delete(stmt->mysql->stmts, &stmt->list);
+ my_free((gptr) stmt, MYF(MY_WME));
+ }
DBUG_RETURN(error);
}
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index 04aeca71a20..714d9cc02f7 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.cc
@@ -47,8 +47,9 @@ static bool check_user(THD *thd, enum_server_command command,
char * get_mysql_home(){ return mysql_home;};
char * get_mysql_real_data_home(){ return mysql_real_data_home;};
-my_bool simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
- ulong length, my_bool skipp_check)
+my_bool simple_command(MYSQL *mysql,enum enum_server_command command,
+ const char *arg,
+ ulong length, my_bool skipp_check)
{
my_bool result= 1;
THD *thd=(THD *) mysql->thd;
@@ -56,7 +57,8 @@ my_bool simple_command(MYSQL *mysql,enum enum_server_command command, const char
/* Check that we are calling the client functions in right order */
if (mysql->status != MYSQL_STATUS_READY)
{
- strmov(thd->net.last_error,ER(thd->net.last_errno=CR_COMMANDS_OUT_OF_SYNC));
+ strmov(thd->net.last_error,
+ ER(thd->net.last_errno=CR_COMMANDS_OUT_OF_SYNC));
return 1;
}
@@ -199,7 +201,7 @@ int STDCALL mysql_server_init(int argc, char **argv, char **groups)
if (!opt_mysql_tmpdir || !opt_mysql_tmpdir[0])
opt_mysql_tmpdir=(char*) P_tmpdir; /* purecov: inspected */
- if (init_thread_environement())
+ if (init_thread_environment())
{
mysql_server_end();
return 1;
diff --git a/myisam/mi_open.c b/myisam/mi_open.c
index 250e0483fe5..4eb7376cdb6 100644
--- a/myisam/mi_open.c
+++ b/myisam/mi_open.c
@@ -329,7 +329,11 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
{
*pos=ft_keysegs[j];
pos[0].language= pos[-1].language;
- pos[0].charset= pos[-1].charset;
+ if (!(pos[0].charset= pos[-1].charset))
+ {
+ my_errno=HA_ERR_CRASHED;
+ goto err;
+ }
pos++;
}
}
diff --git a/mysql-test/r/analyse.result b/mysql-test/r/analyse.result
index 0cb33526457..48882f42219 100644
--- a/mysql-test/r/analyse.result
+++ b/mysql-test/r/analyse.result
@@ -3,7 +3,7 @@ create table t1 (i int, j int);
insert into t1 values (1,2), (3,4), (5,6), (7,8);
select count(*) from t1 procedure analyse();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
- -6510615555426900571 -6510615555426900571 -6510615555426900571 -6510615555426900571
+count(*) 4 4 1 1 0 0 4.0000 0.0000 ENUM('4') NOT NULL
select * from t1 procedure analyse();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
t1.i 1 7 1 1 0 0 4.0000 2.2361 ENUM('1','3','5','7') NOT NULL
diff --git a/mysql-test/r/row.result b/mysql-test/r/row.result
index f3522e05380..d86ae6c7961 100644
--- a/mysql-test/r/row.result
+++ b/mysql-test/r/row.result
@@ -1,6 +1,6 @@
drop table if exists t1;
-select row(1,2,3) IN (row(3,2,3), row(1,2,3), row(1,3,3));
-row(1,2,3) IN (row(3,2,3), row(1,2,3), row(1,3,3))
+select (1,2,3) IN ((3,2,3), (1,2,3), (1,3,3));
+(1,2,3) IN ((3,2,3), (1,2,3), (1,3,3))
1
select row(10,2,3) IN (row(3,2,3), row(1,2,3), row(1,3,3));
row(10,2,3) IN (row(3,2,3), row(1,2,3), row(1,3,3))
@@ -32,14 +32,23 @@ NULL
select row('b',1.5,3) IN (row('b',NULL,4), row('a',1.5,3), row(1,3,3));
row('b',1.5,3) IN (row('b',NULL,4), row('a',1.5,3), row(1,3,3))
0
-select row(1,2,row(3,4)) IN (row(3,2,row(3,4)), row(1,2,row(3,4)));
-row(1,2,row(3,4)) IN (row(3,2,row(3,4)), row(1,2,row(3,4)))
+select (1,2,(3,4)) IN ((3,2,(3,4)), (1,2,(3,4)));
+(1,2,(3,4)) IN ((3,2,(3,4)), (1,2,(3,4)))
1
select row(1,2,row(3,4)) IN (row(3,2,row(3,4)), row(1,2,4));
Cardinality error (more/less than 2 columns)
select row(1,2,row(3,4)) IN (row(3,2,row(3,4)), row(1,2,row(3,NULL)));
row(1,2,row(3,4)) IN (row(3,2,row(3,4)), row(1,2,row(3,NULL)))
NULL
+SELECT (1,2,3)=(0,NULL,3);
+(1,2,3)=(0,NULL,3)
+0
+SELECT (1,2,3)=(1,NULL,3);
+(1,2,3)=(1,NULL,3)
+NULL
+SELECT (1,2,3)=(1,NULL,0);
+(1,2,3)=(1,NULL,0)
+NULL
SELECT ROW(1,2,3)=ROW(1,2,3);
ROW(1,2,3)=ROW(1,2,3)
1
@@ -132,4 +141,6 @@ select 1 from t1 where ROW(1,1);
Cardinality error (more/less than 1 columns)
select count(*) from t1 order by ROW(1,1);
Cardinality error (more/less than 1 columns)
+select count(*) from t1 having (1,1) order by i;
+Cardinality error (more/less than 1 columns)
drop table t1;
diff --git a/mysql-test/t/row.test b/mysql-test/t/row.test
index 099eaa95c77..98fd640f12e 100644
--- a/mysql-test/t/row.test
+++ b/mysql-test/t/row.test
@@ -3,7 +3,7 @@
drop table if exists t1;
--enable_warnings
-select row(1,2,3) IN (row(3,2,3), row(1,2,3), row(1,3,3));
+select (1,2,3) IN ((3,2,3), (1,2,3), (1,3,3));
select row(10,2,3) IN (row(3,2,3), row(1,2,3), row(1,3,3));
select row(1,2,3) IN (row(3,NULL,3), row(1,2,3), row(1,3,3));
select row(10,2,3) IN (row(3,NULL,3), row(1,2,3), row(1,3,3));
@@ -14,11 +14,16 @@ select row('a',1.5,3) IN (row(3,NULL,3), row('a',1.5,3), row(1,3,3));
select row('b',1.5,3) IN (row(3,NULL,3), row('a',1.5,3), row(1,3,3));
select row('b',1.5,3) IN (row('b',NULL,3), row('a',1.5,3), row(1,3,3));
select row('b',1.5,3) IN (row('b',NULL,4), row('a',1.5,3), row(1,3,3));
-select row(1,2,row(3,4)) IN (row(3,2,row(3,4)), row(1,2,row(3,4)));
+select (1,2,(3,4)) IN ((3,2,(3,4)), (1,2,(3,4)));
-- error 1239
select row(1,2,row(3,4)) IN (row(3,2,row(3,4)), row(1,2,4));
select row(1,2,row(3,4)) IN (row(3,2,row(3,4)), row(1,2,row(3,NULL)));
+SELECT (1,2,3)=(0,NULL,3);
+SELECT (1,2,3)=(1,NULL,3);
+# here's something for Sanja to fix :)
+SELECT (1,2,3)=(1,NULL,0);
+
SELECT ROW(1,2,3)=ROW(1,2,3);
SELECT ROW(2,2,3)=ROW(1+1,2,3);
SELECT ROW(1,2,3)=ROW(1+1,2,3);
@@ -58,9 +63,7 @@ create table t1 (i int);
select 1 from t1 where ROW(1,1);
-- error 1239
select count(*) from t1 order by ROW(1,1);
-#TODO remove comments after parser fixing
-#-- error 1239
-#select count(*) from t1 order by i having (1,1);
-#-- error 1239
-#select 1 from t1 limit (1,1), (1,1);
+-- error 1239
+select count(*) from t1 having (1,1) order by i;
drop table t1;
+
diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c
index 6a037f13f05..9d45ec8d539 100644
--- a/mysys/mf_keycache.c
+++ b/mysys/mf_keycache.c
@@ -15,855 +15,1856 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
- This functions is handle keyblock cacheing for NISAM, MISAM and PISAM
- databases.
- One cache can handle many files. Every different blocksize has it owns
- set of buffers that are allocated from block_mem.
+ These functions are to handle keyblock cacheing
+ for NISAM, MISAM and PISAM databases.
+ One cache can handle many files.
+ It must contain buffers of the same blocksize.
init_key_cache() should be used to init cache handler.
- */
+*/
#include "mysys_priv.h"
#include "my_static.h"
#include <m_string.h>
#include <errno.h>
+#include <assert.h>
+#include <stdarg.h>
+
+/*
+ Some compilation flags have been added specifically for this module
+ to control the following:
+ - not to let a thread to yield the control when reading directly
+ from key cache, which might improve performance in many cases;
+ to enable this add:
+ #define SERIALIZED_READ_FROM_CACHE
+ - to set an upper bound for number of threads simultaneously
+ using the key cache; this setting helps to determine an optimal
+ size for hash table and improve performance when the number of
+ blocks in the key cache much less than the number of threads
+ accessing it;
+ to set this number equal to <N> add
+ #define MAX_THREADS <N>
+ - to substitute calls of pthread_cond_wait for calls of
+ pthread_cond_timedwait (wait with timeout set up);
+ this setting should be used only when you want to trap a deadlock
+ situation, which theoretically should not happen;
+ to set timeout equal to <T> seconds add
+ #define KEYCACHE_TIMEOUT <T>
+ - to enable the module traps and to send debug information from
+ key cache module to a special debug log add:
+ #define KEYCACHE_DEBUG
+ the name of this debug log file <LOG NAME> can be set through:
+ #define KEYCACHE_DEBUG_LOG <LOG NAME>
+ if the name is not defined, it's set by default;
+ if the KEYCACHE_DEBUG flag is not set up and we are in a debug
+ mode, i.e. when ! defined(DBUG_OFF), the debug information from the
+ module is sent to the regular debug log.
+
+ Example of the settings:
+ #define SERIALIZED_READ_FROM_CACHE
+ #define MAX_THREADS 100
+ #define KEYCACHE_TIMEOUT 1
+ #define KEYCACHE_DEBUG
+ #define KEYCACHE_DEBUG_LOG "my_key_cache_debug.log"
+*/
#if defined(MSDOS) && !defined(M_IC80386)
- /* We nead much memory */
+/* we nead much memory */
#undef my_malloc_lock
#undef my_free_lock
-#define my_malloc_lock(A,B) halloc((long) (A/IO_SIZE),IO_SIZE)
-#define my_free_lock(A,B) hfree(A)
-#endif
+#define my_malloc_lock(A,B) halloc((long) (A/IO_SIZE),IO_SIZE)
+#define my_free_lock(A,B) hfree(A)
+#endif /* defined(MSDOS) && !defined(M_IC80386) */
+
+#define STRUCT_PTR(TYPE, MEMBER, a) \
+ (TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER))
+
+/* types of condition variables */
+#define COND_FOR_REQUESTED 0
+#define COND_FOR_SAVED 1
+#define COND_FOR_READERS 2
+
+typedef pthread_cond_t KEYCACHE_CONDVAR;
+typedef struct st_keycache_wqueue
+{ /* info about requests in a waiting queue */
+ struct st_my_thread_var *last_thread; /* circular list of waiting threads */
+} KEYCACHE_WQUEUE;
+
+typedef struct st_keycache_page
+{ /* descriptor of the page in the key cache block buffer */
+ int file; /* file to which the page belongs to */
+ my_off_t filepos; /* position of the page in the file */
+} KEYCACHE_PAGE;
+
+typedef struct st_hash_link
+{ /* element in the chain of a hash table bucket */
+ struct st_hash_link *next, **prev; /* to connect links in the same bucket */
+ struct st_block_link *block; /* reference to the block for the page: */
+ File file; /* from such a file */
+ my_off_t diskpos; /* with such an offset */
+ uint requests; /* number of requests for the page */
+} HASH_LINK; /* offset is always alighed for key_cache_block_size */
+
+/* simple states of a block */
+#define BLOCK_ERROR 1 /* an error occured when performing disk i/o */
+#define BLOCK_READ 2 /* the is page in the block buffer */
+#define BLOCK_IN_SWITCH 4 /* block is preparing to read new page */
+#define BLOCK_REASSIGNED 8 /* block does not accept requests for old page */
+#define BLOCK_IN_FLUSH 16 /* block is in flush operation */
+#define BLOCK_CHANGED 32 /* block buffer contains a dirty page */
+
+/* page status, returned by find_key_block */
+#define PAGE_READ 0
+#define PAGE_TO_BE_READ 1
+#define PAGE_WAIT_TO_BE_READ 2
+
+typedef struct st_block_link
+{ /* key cache block */
+ struct st_block_link
+ *next_used, **prev_used; /* to connect links in the LRU chain (ring) */
+ struct st_block_link
+ *next_changed, **prev_changed; /* for lists of file dirty/clean blocks */
+ struct st_hash_link *hash_link; /* backward ptr to referring hash_link */
+ KEYCACHE_WQUEUE wqueue[2]; /* queues on waiting requests for new/old pages */
+ uint requests; /* number of requests for the block */
+ byte *buffer; /* buffer for the block page */
+ uint offset; /* beginning of modified data in the buffer */
+ uint length; /* end of data in the buffer */
+ uint status; /* state of the block */
+ KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event */
+} BLOCK_LINK;
-/* size of map to be used to find changed files */
+static int flush_all_key_blocks();
+static void test_key_cache(const char *where, my_bool lock);
-#define CHANGED_BLOCKS_HASH 128 /* Must be power of 2 */
-#define CHANGED_BLOCKS_MASK (CHANGED_BLOCKS_HASH-1)
-#define FLUSH_CACHE 2000 /* Sort this many blocks at once */
+uint key_cache_block_size= /* size of the page buffer of a cache block */
+ DEFAULT_KEYCACHE_BLOCK_SIZE;
+static uint key_cache_shift;
+
+#define CHANGED_BLOCKS_HASH 128 /* must be power of 2 */
+#define FLUSH_CACHE 2000 /* sort this many blocks at once */
+
+static KEYCACHE_WQUEUE
+ waiting_for_hash_link; /* queue of requests waiting for a free hash link */
+static KEYCACHE_WQUEUE
+ waiting_for_block; /* queue of requests waiting for a free block */
+
+static HASH_LINK **_my_hash_root; /* arr. of entries into hash table buckets */
+static uint _my_hash_entries; /* max number of entries in the hash table */
+static HASH_LINK *_my_hash_link_root; /* memory for hash table links */
+static int _my_hash_links; /* max number of hash links */
+static int _my_hash_links_used; /* number of hash links currently used */
+static HASH_LINK *_my_free_hash_list; /* list of free hash links */
+static BLOCK_LINK *_my_block_root; /* memory for block links */
+static int _my_disk_blocks; /* max number of blocks in the cache */
+static byte HUGE_PTR *_my_block_mem; /* memory for block buffers */
+static BLOCK_LINK *_my_used_last; /* ptr to the last block of the LRU chain */
+ulong _my_blocks_used, /* number of currently used blocks */
+ _my_blocks_changed; /* number of currently dirty blocks */
+#if defined(KEYCACHE_DEBUG)
+static
+ulong _my_blocks_available; /* number of blocks available in the LRU chain */
+#endif /* defined(KEYCACHE_DEBUG) */
+ulong _my_cache_w_requests,_my_cache_write, /* counters */
+ _my_cache_r_requests,_my_cache_read; /* for statistics */
+static BLOCK_LINK
+ *changed_blocks[CHANGED_BLOCKS_HASH]; /* hash table for file dirty blocks */
+static BLOCK_LINK
+ *file_blocks[CHANGED_BLOCKS_HASH]; /* hash table for other file blocks */
+ /* that are not free */
+#ifndef DBUG_OFF
+static my_bool _my_printed;
+#endif
-typedef struct sec_link {
- struct sec_link *next_hash,**prev_hash;/* Blocks linked acc. to hash-value */
- struct sec_link *next_used,*prev_used;
- struct sec_link *next_changed,**prev_changed;
- File file;
- my_off_t diskpos;
- byte *buffer;
- my_bool changed;
-} SEC_LINK;
+#define KEYCACHE_HASH(f, pos) \
+ (((ulong) ((pos) >> key_cache_shift)+(ulong) (f)) & (_my_hash_entries-1))
+#define FILE_HASH(f) ((uint) (f) & (CHANGED_BLOCKS_HASH-1))
+#define DEFAULT_KEYCACHE_DEBUG_LOG "keycache_debug.log"
-static SEC_LINK *find_key_block(int file,my_off_t filepos,int *error);
-static int flush_all_key_blocks();
+#if defined(KEYCACHE_DEBUG) && ! defined(KEYCACHE_DEBUG_LOG)
+#define KEYCACHE_DEBUG_LOG DEFAULT_KEYCACHE_DEBUG_LOG
+#endif
- /* static variables in this file */
-static SEC_LINK *_my_block_root,**_my_hash_root,
- *_my_used_first,*_my_used_last;
-static int _my_disk_blocks;
-static uint _my_disk_blocks_used, _my_hash_blocks;
-static uint key_cache_shift;
-ulong _my_blocks_used,_my_blocks_changed;
-ulong _my_cache_w_requests,_my_cache_write,_my_cache_r_requests,
- _my_cache_read;
-uint key_cache_block_size=DEFAULT_KEYCACHE_BLOCK_SIZE;
-static byte HUGE_PTR *_my_block_mem;
-static SEC_LINK *changed_blocks[CHANGED_BLOCKS_HASH];
-static SEC_LINK *file_blocks[CHANGED_BLOCKS_HASH];
-#ifndef DBUG_OFF
-static my_bool _my_printed;
+#if defined(KEYCACHE_DEBUG_LOG)
+static FILE *keycache_debug_log=NULL;
+static void keycache_debug_print _VARARGS((const char *fmt,...));
+#define KEYCACHE_DEBUG_OPEN \
+ keycache_debug_log=fopen(KEYCACHE_DEBUG_LOG, "w")
+
+#define KEYCACHE_DEBUG_CLOSE \
+ if (keycache_debug_log) fclose(keycache_debug_log)
+#else
+#define KEYCACHE_DEBUG_OPEN
+#define KEYCACHE_DEBUG_CLOSE
+#endif /* defined(KEYCACHE_DEBUG_LOG) */
+
+#if defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG)
+#define KEYCACHE_DBUG_PRINT(l, m) \
+ { if (keycache_debug_log) fprintf(keycache_debug_log, "%s: ", l); \
+ keycache_debug_print m; }
+
+#define KEYCACHE_DBUG_ASSERT(a) \
+ { if (! (a) && keycache_debug_log) fclose(keycache_debug_log); \
+ assert(a); }
+#else
+#define KEYCACHE_DBUG_PRINT(l, m) DBUG_PRINT(l, m)
+#define KEYCACHE_DBUG_ASSERT(a) DBUG_ASSERT(a)
+#endif /* defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) */
+
+#if defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF)
+static long keycache_thread_id;
+#define KEYCACHE_THREAD_TRACE(l) \
+ KEYCACHE_DBUG_PRINT(l,("|thread %ld",keycache_thread_id))
+
+#define KEYCACHE_THREAD_TRACE_BEGIN(l) \
+ { struct st_my_thread_var *thread_var =my_thread_var; \
+ keycache_thread_id=my_thread_var->id; \
+ KEYCACHE_DBUG_PRINT(l,("[thread %ld",keycache_thread_id)) }
+
+#define KEYCACHE_THREAD_TRACE_END(l) \
+ KEYCACHE_DBUG_PRINT(l,("]thread %ld",keycache_thread_id))
+#else
+#define KEYCACHE_THREAD_TRACE_BEGIN(l)
+#define KEYCACHE_THREAD_TRACE_END(l)
+#define KEYCACHE_THREAD_TRACE(l)
+#endif /* defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) */
+
+#define BLOCK_NUMBER(b) \
+ ((uint) (((char*)(b) - (char *) _my_block_root) / sizeof(BLOCK_LINK)))
+#define HASH_LINK_NUMBER(h) \
+ ((uint) (((char*)(h) - (char *) _my_hash_link_root) / sizeof(HASH_LINK)))
+
+#if (defined(KEYCACHE_TIMEOUT) && !defined(__WIN__)) || defined(KEYCACHE_DEBUG)
+static int keycache_pthread_cond_wait(pthread_cond_t *cond,
+ pthread_mutex_t *mutex);
+#else
+#define keycache_pthread_cond_wait pthread_cond_wait
#endif
+#if defined(KEYCACHE_DEBUG)
+static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex);
+static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex);
+static int keycache_pthread_cond_signal(pthread_cond_t *cond);
+static int keycache_pthread_cond_broadcast(pthread_cond_t *cond);
+#else
+#define keycache_pthread_mutex_lock pthread_mutex_lock
+#define keycache_pthread_mutex_unlock pthread_mutex_unlock
+#define keycache_pthread_cond_signal pthread_cond_signal
+#define keycache_pthread_cond_broadcast pthread_cond_broadcast
+#endif /* defined(KEYCACHE_DEBUG) */
+
+static uint next_power(uint value)
+{
+ uint old_value=1;
+ while (value)
+ {
+ old_value=value;
+ value&= value-1;
+ }
+ return (old_value << 1);
+}
- /* Init of disk_buffert */
- /* Returns blocks in use */
- /* ARGSUSED */
+/*
+ Initialize the key cache,
+ return number of blocks in it
+*/
int init_key_cache(ulong use_mem)
{
- uint blocks,length;
+ uint blocks, hash_links, length;
+ int error;
+
DBUG_ENTER("init_key_cache");
-
+
+ KEYCACHE_DEBUG_OPEN;
if (key_cache_inited && _my_disk_blocks > 0)
{
- DBUG_PRINT("warning",("key cache already in use")); /* purecov: inspected */
- DBUG_RETURN(0); /* purecov: inspected */
+ DBUG_PRINT("warning",("key cache already in use"));
+ DBUG_RETURN(0);
}
if (! key_cache_inited)
{
key_cache_inited=TRUE;
_my_disk_blocks= -1;
key_cache_shift=my_bit_log2(key_cache_block_size);
- DBUG_PRINT("info",("key_cache_block_size: %u key_cache_shift: %u",
- key_cache_block_size, key_cache_shift));
+ DBUG_PRINT("info",("key_cache_block_size: %u",
+ key_cache_block_size));
#ifndef DBUG_OFF
_my_printed=0;
#endif
}
-
- blocks= (uint) (use_mem/(sizeof(SEC_LINK)+sizeof(SEC_LINK*)*5/4+
- key_cache_block_size));
- /* No use to have very few blocks */
+
+ _my_cache_w_requests=_my_cache_r_requests=_my_cache_read=_my_cache_write=0;
+
+ _my_block_mem=NULL;
+ _my_block_root=NULL;
+
+ blocks= (uint) (use_mem/(sizeof(BLOCK_LINK)+2*sizeof(HASH_LINK)+
+ sizeof(HASH_LINK*)*5/4+key_cache_block_size));
+ /* It doesn't make sense to have too few blocks (less than 8) */
if (blocks >= 8 && _my_disk_blocks < 0)
{
for (;;)
{
- /* Set my_hash_blocks to the next bigger 2 power */
- _my_hash_blocks=(uint) 1 << (my_bit_log2(blocks*5/4)+1);
- while ((length=(uint) blocks*sizeof(SEC_LINK)+
- sizeof(SEC_LINK*)*_my_hash_blocks)+
- ((ulong) blocks << key_cache_shift) >
- use_mem)
- blocks--;
- if ((_my_block_mem=my_malloc_lock((ulong) blocks << key_cache_shift,
- MYF(0))))
+ /* Set _my_hash_entries to the next bigger 2 power */
+ if ((_my_hash_entries=next_power(blocks)) < blocks*5/4)
+ _my_hash_entries<<=1;
+ hash_links=2*blocks;
+#if defined(MAX_THREADS)
+ if (hash_links < MAX_THREADS + blocks - 1)
+ hash_links=MAX_THREADS + blocks - 1;
+#endif
+ while ((length=blocks*sizeof(BLOCK_LINK)+hash_links*sizeof(HASH_LINK)+
+ sizeof(HASH_LINK*)*_my_hash_entries)+
+ ((ulong) blocks << key_cache_shift) >
+ use_mem)
+ blocks--;
+ /* Allocate memory for cache page buffers */
+ if ((_my_block_mem=my_malloc_lock((ulong) blocks*key_cache_block_size,
+ MYF(0))))
+ {
+ /*
+ Allocate memory for blocks, hash_links and hash entries;
+ For each block 2 hash links are allocated
+ */
+ if ((_my_block_root=(BLOCK_LINK*) my_malloc((uint) length,MYF(0))))
+ break;
+ my_free_lock(_my_block_mem,MYF(0));
+ }
+ if (blocks < 8)
{
- if ((_my_block_root=(SEC_LINK*) my_malloc((uint) length,MYF(0))) != 0)
- break;
- my_free_lock(_my_block_mem,MYF(0));
+ my_errno=ENOMEM;
+ goto err;
}
- if (blocks < 8)
- goto err;
blocks=blocks/4*3;
}
_my_disk_blocks=(int) blocks;
- _my_hash_root= (SEC_LINK**) (_my_block_root+blocks);
- bzero((byte*) _my_hash_root,_my_hash_blocks*sizeof(SEC_LINK*));
- _my_used_first=_my_used_last=0;
- _my_blocks_used=_my_disk_blocks_used=_my_blocks_changed=0;
- _my_cache_w_requests=_my_cache_r_requests=_my_cache_read=_my_cache_write=0;
- DBUG_PRINT("exit",("disk_blocks: %d block_root: %lx _my_hash_blocks: %d hash_root: %lx",
- _my_disk_blocks,_my_block_root,_my_hash_blocks,
- _my_hash_root));
+ _my_hash_links=hash_links;
+ _my_hash_root=(HASH_LINK**) (_my_block_root+blocks);
+ _my_hash_link_root=(HASH_LINK*) (_my_hash_root+_my_hash_entries);
+ bzero((byte*) _my_block_root,_my_disk_blocks*sizeof(BLOCK_LINK));
+ bzero((byte*) _my_hash_root,_my_hash_entries*sizeof(HASH_LINK*));
+ bzero((byte*) _my_hash_link_root,_my_hash_links*sizeof(HASH_LINK));
+ _my_hash_links_used=0;
+ _my_free_hash_list=NULL;
+ _my_blocks_used=_my_blocks_changed=0;
+#if defined(KEYCACHE_DEBUG)
+ _my_blocks_available=0;
+#endif
+ /* The LRU chain is empty after initialization */
+ _my_used_last=NULL;
+
+ waiting_for_hash_link.last_thread=NULL;
+ waiting_for_block.last_thread=NULL;
+ DBUG_PRINT("exit",
+ ("disk_blocks: %d block_root: %lx hash_entries: %d hash_root: %lx \
+ hash_links: %d hash_link_root %lx",
+ _my_disk_blocks,_my_block_root,_my_hash_entries,_my_hash_root,
+ _my_hash_links,_my_hash_link_root));
}
bzero((gptr) changed_blocks,sizeof(changed_blocks[0])*CHANGED_BLOCKS_HASH);
bzero((gptr) file_blocks,sizeof(file_blocks[0])*CHANGED_BLOCKS_HASH);
+
DBUG_RETURN((int) blocks);
-
+
err:
- my_errno=ENOMEM;
+ error=my_errno;
+ if (_my_block_mem)
+ my_free_lock((gptr) _my_block_mem,MYF(0));
+ if (_my_block_mem)
+ my_free((gptr) _my_block_root,MYF(0));
+ my_errno=error;
DBUG_RETURN(0);
-} /* init_key_cache */
+}
/*
- Resize the key cache
-
- SYNOPSIS
- resize_key_cache()
- use_mem Bytes to use for new key cache
-
- RETURN VALUES
- 0 Error
- # number of blocks in key cache
+ Resize the key cache
*/
-
-
int resize_key_cache(ulong use_mem)
{
- int block;
- pthread_mutex_lock(&THR_LOCK_keycache);
+ int blocks;
+ keycache_pthread_mutex_lock(&THR_LOCK_keycache);
if (flush_all_key_blocks())
{
- /* TODO: If this happens, we should write a warning in the log file ! */
- pthread_mutex_unlock(&THR_LOCK_keycache);
+ /* TODO: if this happens, we should write a warning in the log file ! */
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
return 0;
}
end_key_cache();
- /* The following will work even if memory is 0 */
- block=init_key_cache(use_mem);
- pthread_mutex_unlock(&THR_LOCK_keycache);
- return block;
+ /* the following will work even if memory is 0 */
+ blocks=init_key_cache(use_mem);
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+ return blocks;
}
- /* Remove key_cache from memory */
-
+/*
+ Remove key_cache from memory
+*/
void end_key_cache(void)
{
DBUG_ENTER("end_key_cache");
- if (! _my_blocks_changed)
+ if (_my_disk_blocks > 0)
{
- if (_my_disk_blocks > 0)
+ if (_my_block_mem)
{
my_free_lock((gptr) _my_block_mem,MYF(0));
my_free((gptr) _my_block_root,MYF(0));
- _my_disk_blocks= -1;
}
+ _my_disk_blocks= -1;
}
+ KEYCACHE_DEBUG_CLOSE;
key_cache_inited=0;
- _my_hash_blocks=_my_blocks_used=0;
DBUG_PRINT("status",
- ("used: %d changed: %d w_requests: %ld writes: %ld r_requests: %ld reads: %ld",
- _my_blocks_used,_my_blocks_changed,_my_cache_w_requests,
- _my_cache_write,_my_cache_r_requests,_my_cache_read));
+ ("used: %d changed: %d w_requests: %ld \
+ writes: %ld r_requests: %ld reads: %ld",
+ _my_blocks_used,_my_blocks_changed,_my_cache_w_requests,
+ _my_cache_write,_my_cache_r_requests,_my_cache_read));
DBUG_VOID_RETURN;
} /* end_key_cache */
-static inline void link_into_file_blocks(SEC_LINK *next, int file)
+/*
+ Link a thread into double-linked queue of waiting threads
+*/
+static inline void link_into_queue(KEYCACHE_WQUEUE *wqueue,
+ struct st_my_thread_var *thread)
+{
+ struct st_my_thread_var *last;
+ if (! (last=wqueue->last_thread))
+ { /* Queue is empty */
+ thread->next=thread;
+ thread->prev=&thread->next;
+ }
+ else
+ {
+ thread->prev=last->next->prev;
+ last->next->prev=&thread->next;
+ thread->next=last->next;
+ last->next=thread;
+ }
+ wqueue->last_thread=thread;
+}
+
+/*
+ Unlink a thread from double-linked queue of waiting threads
+*/
+static inline void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
+ struct st_my_thread_var *thread)
+{
+ KEYCACHE_DBUG_PRINT("unlink_from_queue", ("thread %ld", thread->id));
+ if (thread->next == thread)
+ /* The queue contains only one member */
+ wqueue->last_thread=NULL;
+ else
+ {
+ thread->next->prev=thread->prev;
+ *thread->prev=thread->next;
+ if (wqueue->last_thread == thread)
+ wqueue->last_thread=STRUCT_PTR(struct st_my_thread_var, next,
+ thread->prev);
+ }
+ thread->next=NULL;
+}
+
+
+/*
+ Add a thread to single-linked queue of waiting threads
+*/
+static inline void add_to_queue(KEYCACHE_WQUEUE *wqueue,
+ struct st_my_thread_var *thread)
+{
+ struct st_my_thread_var *last;
+ if (! (last=wqueue->last_thread))
+ thread->next=thread;
+ else
+ {
+ thread->next=last->next;
+ last->next=thread;
+ }
+ wqueue->last_thread=thread;
+}
+
+
+/*
+ Remove all threads from queue signaling them to proceed
+*/
+static inline void release_queue(KEYCACHE_WQUEUE *wqueue)
+{
+ struct st_my_thread_var *last=wqueue->last_thread;
+ struct st_my_thread_var *next=last->next;
+ struct st_my_thread_var *thread;
+ do
+ {
+ thread=next;
+ keycache_pthread_cond_signal(&thread->suspend);
+ KEYCACHE_DBUG_PRINT("release_queue: signal", ("thread %ld", thread->id));
+ next=thread->next;
+ thread->next=NULL;
+ }
+ while (thread != last);
+ wqueue->last_thread=NULL;
+}
+
+
+/*
+ Unlink a block from the chain of dirty/clean blocks
+*/
+static inline void unlink_changed(BLOCK_LINK *block)
{
- reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- next->prev_changed= ptr;
- if ((next->next_changed= *ptr))
- (*ptr)->prev_changed= &next->next_changed;
- *ptr=next;
+ if (block->next_changed)
+ block->next_changed->prev_changed=block->prev_changed;
+ *block->prev_changed=block->next_changed;
}
-static inline void relink_into_file_blocks(SEC_LINK *next, int file)
+/*
+ Link a block into the chain of dirty/clean blocks
+*/
+static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead)
{
- reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- if (next->next_changed)
- next->next_changed->prev_changed=next->prev_changed;
- *next->prev_changed=next->next_changed;
- next->prev_changed= ptr;
- if ((next->next_changed= *ptr))
- (*ptr)->prev_changed= &next->next_changed;
- *ptr=next;
+ block->prev_changed=phead;
+ if ((block->next_changed=*phead))
+ (*phead)->prev_changed= &block->next_changed;
+ *phead=block;
}
-static inline void link_changed_to_file(SEC_LINK *next,int file)
+
+/*
+ Unlink a block from the chain of dirty/clean blocks, if it's asked for,
+ and link it to the chain of clean blocks for the specified file
+*/
+static inline void link_to_file_list(BLOCK_LINK *block,int file,
+ my_bool unlink)
{
- reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- if (next->next_changed)
- next->next_changed->prev_changed=next->prev_changed;
- *next->prev_changed=next->next_changed;
- next->prev_changed= ptr;
- if ((next->next_changed= *ptr))
- (*ptr)->prev_changed= &next->next_changed;
- *ptr=next;
- next->changed=0;
- _my_blocks_changed--;
+ if (unlink)
+ unlink_changed(block);
+ link_changed(block,&file_blocks[FILE_HASH(file)]);
+ if (block->status & BLOCK_CHANGED)
+ {
+ block->status&=~BLOCK_CHANGED;
+ _my_blocks_changed--;
+ }
}
-static inline void link_file_to_changed(SEC_LINK *next)
+
+/*
+ Unlink a block from the chain of clean blocks for the specified
+ file and link it to the chain of dirty blocks for this file
+*/
+static inline void link_to_changed_list(BLOCK_LINK *block)
{
- reg1 SEC_LINK **ptr= &changed_blocks[(uint) next->file & CHANGED_BLOCKS_MASK];
- if (next->next_changed)
- next->next_changed->prev_changed=next->prev_changed;
- *next->prev_changed=next->next_changed;
- next->prev_changed= ptr;
- if ((next->next_changed= *ptr))
- (*ptr)->prev_changed= &next->next_changed;
- *ptr=next;
- next->changed=1;
+ unlink_changed(block);
+ link_changed(block,&changed_blocks[FILE_HASH(block->hash_link->file)]);
+ block->status|=BLOCK_CHANGED;
_my_blocks_changed++;
}
-#if !defined(DBUG_OFF) && !defined(EXTRA_DEBUG)
-#define DBUG_OFF /* This should work */
+/*
+ Link a block to the LRU chain at the beginning or at the end
+*/
+static void link_block(BLOCK_LINK *block, my_bool at_end)
+{
+ KEYCACHE_DBUG_ASSERT(! (block->hash_link && block->hash_link->requests));
+ if (waiting_for_block.last_thread) {
+ /* Signal that in the LRU chain an available block has appeared */
+ struct st_my_thread_var *last_thread=waiting_for_block.last_thread;
+ struct st_my_thread_var *first_thread=last_thread->next;
+ struct st_my_thread_var *next_thread=first_thread;
+ HASH_LINK *hash_link= (HASH_LINK *) first_thread->opt_info;
+ struct st_my_thread_var *thread;
+ do
+ {
+ thread=next_thread;
+ next_thread=thread->next;
+ /*
+ We notify about the event all threads that ask
+ for the same page as the first thread in the queue
+ */
+ if ((HASH_LINK *) thread->opt_info == hash_link)
+ {
+ keycache_pthread_cond_signal(&thread->suspend);
+ unlink_from_queue(&waiting_for_block, thread);
+ block->requests++;
+ }
+ }
+ while (thread != last_thread);
+ hash_link->block=block;
+ KEYCACHE_THREAD_TRACE("link_block: after signaling");
+#if defined(KEYCACHE_DEBUG)
+ KEYCACHE_DBUG_PRINT("link_block",
+ ("linked,unlinked block %u status=%x #requests=%u #available=%u",
+ BLOCK_NUMBER(block),block->status,
+ block->requests,_my_blocks_available));
#endif
+ return;
+ }
+ if (_my_used_last)
+ {
+ _my_used_last->next_used->prev_used=&block->next_used;
+ block->next_used=_my_used_last->next_used;
+ block->prev_used=&_my_used_last->next_used;
+ _my_used_last->next_used=block;
+ if (at_end)
+ _my_used_last=block;
+ }
+ else
+ {
+ /* The LRU chain is empty */
+ _my_used_last=block->next_used=block;
+ block->prev_used=&block->next_used;
+ }
+ KEYCACHE_THREAD_TRACE("link_block");
+#if defined(KEYCACHE_DEBUG)
+ _my_blocks_available++;
+ KEYCACHE_DBUG_PRINT("link_block",
+ ("linked block %u:%1u status=%x #requests=%u #available=%u",
+ BLOCK_NUMBER(block),at_end,block->status,
+ block->requests,_my_blocks_available));
+ KEYCACHE_DBUG_ASSERT(_my_blocks_available <= _my_blocks_used);
+#endif
+}
-#ifndef DBUG_OFF
-static void test_key_cache(const char *where, my_bool lock);
+
+/*
+ Unlink a block from the LRU chain
+*/
+static inline void unlink_block(BLOCK_LINK *block)
+{
+ if (block->next_used == block)
+ /* The list contains only one member */
+ _my_used_last=NULL;
+ else
+ {
+ block->next_used->prev_used=block->prev_used;
+ *block->prev_used=block->next_used;
+ if (_my_used_last == block)
+ _my_used_last=STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used);
+ }
+ block->next_used=NULL;
+
+ KEYCACHE_THREAD_TRACE("unlink_block");
+#if defined(KEYCACHE_DEBUG)
+ _my_blocks_available--;
+ KEYCACHE_DBUG_PRINT("unlink_block",
+ ("unlinked block %u status=%x #requests=%u #available=%u",
+ BLOCK_NUMBER(block),block->status,
+ block->requests,_my_blocks_available));
+ KEYCACHE_DBUG_ASSERT(_my_blocks_available >= 0);
+#endif
+}
+
+
+/*
+ Register requests for a block
+*/
+static inline void reg_requests(BLOCK_LINK *block, int count)
+{
+ if (! block->requests)
+ /* First request for the block unlinks it */
+ unlink_block(block);
+ block->requests+=count;
+}
+
+
+/*
+ Unregister request for a block
+ linking it to the LRU chain if it's the last request
+*/
+static inline void unreg_request(BLOCK_LINK *block, int at_end)
+{
+ if (! --block->requests)
+ link_block(block, at_end);
+}
+
+/*
+ Remove a reader of the page in block
+*/
+static inline void remove_reader(BLOCK_LINK *block)
+{
+ if (! --block->hash_link->requests && block->condvar)
+ keycache_pthread_cond_signal(block->condvar);
+}
+
+
+/*
+ Wait until the last reader of the page in block
+ signals on its termination
+*/
+static inline void wait_for_readers(BLOCK_LINK *block)
+{
+ struct st_my_thread_var *thread=my_thread_var;
+ while (block->hash_link->requests)
+ {
+ block->condvar=&thread->suspend;
+ keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache);
+ block->condvar=NULL;
+ }
+}
+
+
+/*
+ add a hash link to a bucket in the hash_table
+*/
+static inline void link_hash(HASH_LINK **start, HASH_LINK *hash_link)
+{
+ if (*start)
+ (*start)->prev=&hash_link->next;
+ hash_link->next=*start;
+ hash_link->prev=start;
+ *start=hash_link;
+}
+
+
+/*
+ Remove a hash link from the hash table
+*/
+static inline void unlink_hash(HASH_LINK *hash_link)
+{
+ KEYCACHE_DBUG_PRINT("unlink_hash", ("file %u, filepos %lu #requests=%u",
+ (uint) hash_link->file,(ulong) hash_link->diskpos, hash_link->requests));
+ KEYCACHE_DBUG_ASSERT(hash_link->requests == 0);
+ if ((*hash_link->prev=hash_link->next))
+ hash_link->next->prev=hash_link->prev;
+ hash_link->block=NULL;
+ if (waiting_for_hash_link.last_thread)
+ { /* Signal that A free hash link appeared */
+ struct st_my_thread_var *last_thread=waiting_for_hash_link.last_thread;
+ struct st_my_thread_var *first_thread=last_thread->next;
+ struct st_my_thread_var *next_thread=first_thread;
+ KEYCACHE_PAGE *first_page= (KEYCACHE_PAGE *) (first_thread->opt_info);
+ struct st_my_thread_var *thread;
+
+ hash_link->file=first_page->file;
+ hash_link->diskpos=first_page->filepos;
+ do
+ {
+ KEYCACHE_PAGE *page;
+ thread=next_thread;
+ page= (KEYCACHE_PAGE *) thread->opt_info;
+ next_thread=thread->next;
+ /*
+ We notify about the event all threads that ask
+ for the same page as the first thread in the queue
+ */
+ if (page->file == hash_link->file && page->filepos == hash_link->diskpos)
+ {
+ keycache_pthread_cond_signal(&thread->suspend);
+ unlink_from_queue(&waiting_for_hash_link, thread);
+ }
+ }
+ while (thread != last_thread);
+ link_hash(&_my_hash_root[KEYCACHE_HASH(hash_link->file,
+ hash_link->diskpos)], hash_link);
+ return;
+ }
+ hash_link->next=_my_free_hash_list;
+ _my_free_hash_list=hash_link;
+}
+
+/*
+ Get the hash link for a page
+*/
+static inline HASH_LINK *get_hash_link(int file, my_off_t filepos)
+{
+ reg1 HASH_LINK *hash_link, **start;
+ KEYCACHE_PAGE page;
+#if defined(KEYCACHE_DEBUG)
+ int cnt;
+#endif
+
+ KEYCACHE_DBUG_PRINT("get_hash_link", ("file %u, filepos %lu",
+ (uint) file,(ulong) filepos));
+
+restart:
+ /*
+ Find the bucket in the hash table for the pair (file, filepos);
+ start contains the head of the bucket list,
+ hash_link points to the first member of the list
+ */
+ hash_link=*(start=&_my_hash_root[KEYCACHE_HASH(file, filepos)]);
+#if defined(KEYCACHE_DEBUG)
+ cnt=0;
+#endif
+ /* Look for an element for the pair (file, filepos) in the bucket chain */
+ while (hash_link &&
+ (hash_link->diskpos != filepos || hash_link->file != file))
+ {
+ hash_link= hash_link->next;
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ if (! (cnt <= _my_hash_links_used))
+ {
+ int i;
+ for (i=0, hash_link=*start ;
+ i < cnt ; i++, hash_link=hash_link->next)
+ {
+ KEYCACHE_DBUG_PRINT("get_hash_link", ("file %u, filepos %lu",
+ (uint) hash_link->file,(ulong) hash_link->diskpos));
+ }
+ }
+ KEYCACHE_DBUG_ASSERT(n <= _my_hash_links_used);
+#endif
+ }
+ if (! hash_link)
+ { /* There is no hash link in the hash table for the pair (file, filepos) */
+ if (_my_free_hash_list)
+ {
+ hash_link=_my_free_hash_list;
+ _my_free_hash_list=hash_link->next;
+ }
+ else if (_my_hash_links_used < _my_hash_links)
+ {
+ hash_link= &_my_hash_link_root[_my_hash_links_used++];
+ }
+ else
+ { /* Wait for a free hash link */
+ struct st_my_thread_var *thread=my_thread_var;
+ KEYCACHE_DBUG_PRINT("get_hash_link", ("waiting"));
+ page.file=file; page.filepos=filepos;
+ thread->opt_info= (void *) &page;
+ link_into_queue(&waiting_for_hash_link, thread);
+ keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache);
+ thread->opt_info=NULL;
+ goto restart;
+ }
+ hash_link->file=file;
+ hash_link->diskpos=filepos;
+ link_hash(start, hash_link);
+ }
+ /* Register the request for the page */
+ hash_link->requests++;
+
+ return hash_link;
+}
+
+
+/*
+ Get a block for the file page requested by a keycache read/write operation;
+ If the page is not in the cache return a free block, if there is none
+ return the lru block after saving its buffer if the page is dirty
+*/
+static BLOCK_LINK *find_key_block(int file, my_off_t filepos,
+ int wrmode, int *page_st)
+{
+ HASH_LINK *hash_link;
+ BLOCK_LINK *block;
+ int error=0;
+ int page_status;
+
+ DBUG_ENTER("find_key_block");
+ KEYCACHE_THREAD_TRACE("find_key_block:begin");
+ DBUG_PRINT("enter", ("file %u, filepos %lu, wrmode %lu",
+ (uint) file,(ulong) filepos,(uint) wrmode));
+ KEYCACHE_DBUG_PRINT("find_key_block", ("file %u, filepos %lu, wrmode %lu",
+ (uint) file,(ulong) filepos,(uint) wrmode));
+#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
+ DBUG_EXECUTE("check_keycache2",test_key_cache("start of find_key_block",0););
#endif
+
+restart:
+ /* Find the hash link for the requested page (file, filepos) */
+ hash_link=get_hash_link(file, filepos);
+
+ page_status=-1;
+ if ((block=hash_link->block) &&
+ block->hash_link == hash_link && (block->status & BLOCK_READ))
+ page_status=PAGE_READ;
+
+ if (page_status == PAGE_READ && (block->status & BLOCK_IN_SWITCH))
+ { /* This is a request for a page to be removed from cache */
+
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("request for old page in block %u",BLOCK_NUMBER(block)));
+ /*
+ Only reading requests can proceed until the old dirty page is flushed,
+ all others are to be suspended, then resubmitted
+ */
+ if (!wrmode && !(block->status & BLOCK_REASSIGNED))
+ reg_requests(block,1);
+ else
+ {
+ hash_link->requests--;
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("request waiting for old page to be saved"));
+ {
+ struct st_my_thread_var *thread=my_thread_var;
+ /* Put the request into the queue of those waiting for the old page */
+ add_to_queue(&block->wqueue[COND_FOR_SAVED], thread);
+ /* Wait until the request can be resubmitted */
+ do
+ {
+ keycache_pthread_cond_wait(&thread->suspend, &THR_LOCK_keycache);
+ }
+ while(thread->next);
+ }
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("request for old page resubmitted"));
+ /* Resubmit the request */
+ goto restart;
+ }
+ }
+ else
+ { /* This is a request for a new page or for a page not to be removed */
+ if (! block)
+ { /* No block is assigned for the page yet */
+ if (_my_blocks_used < (uint) _my_disk_blocks)
+ { /* There are some never used blocks, take first of them */
+ hash_link->block=block= &_my_block_root[_my_blocks_used];
+ block->buffer=ADD_TO_PTR(_my_block_mem,
+ ((ulong) _my_blocks_used*key_cache_block_size),
+ byte*);
+ block->status=0;
+ block->length=0;
+ block->offset=key_cache_block_size;
+ block->requests=1;
+ _my_blocks_used++;
+ link_to_file_list(block, file, 0);
+ block->hash_link=hash_link;
+ page_status=PAGE_TO_BE_READ;
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("got never used block %u",BLOCK_NUMBER(block)));
+ }
+ else
+ { /* There are no never used blocks, use a block from the LRU chain */
+ /*
+ Wait until a new block is added to the LRU chain;
+ several threads might wait here for the same page,
+ all of them must get the same block
+ */
+
+ if (! _my_used_last)
+ {
+ struct st_my_thread_var *thread=my_thread_var;
+ thread->opt_info=(void *) hash_link;
+ link_into_queue(&waiting_for_block, thread);
+ do
+ {
+ keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache);
+ }
+ while (thread->next);
+ thread->opt_info=NULL;
+ }
+ block=hash_link->block;
+ if (! block)
+ {
+ /*
+ Take the first block from the LRU chain
+ unlinking it from the chain
+ */
+ block=_my_used_last->next_used;
+ reg_requests(block,1);
+ hash_link->block=block;
+ }
+
+ if (block->hash_link != hash_link && ! (block->status & BLOCK_IN_SWITCH) )
+ { /* this is a primary request for a new page */
+ block->status|=BLOCK_IN_SWITCH;
+
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("got block %u for new page",BLOCK_NUMBER(block)));
+
+ if (block->status & BLOCK_CHANGED)
+ { /* The block contains a dirty page - push it out of the cache */
+
+ KEYCACHE_DBUG_PRINT("find_key_block",("block is dirty"));
+
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+ /*
+ The call is thread safe because only the current
+ thread might change the block->hash_link value
+ */
+ error=my_pwrite(block->hash_link->file,block->buffer,
+ block->length,block->hash_link->diskpos,
+ MYF(MY_NABP | MY_WAIT_IF_FULL));
+ keycache_pthread_mutex_lock(&THR_LOCK_keycache);
+ _my_cache_write++;
+ }
+
+ block->status|=BLOCK_REASSIGNED;
+ if (block->hash_link)
+ {
+ /*
+ Wait until all pending read requests
+ for this page are executed
+ (we could have avoided this waiting, if we had read
+ a page in the cache in a sweep, without yielding control)
+ */
+ wait_for_readers(block);
+
+ /* Remove the hash link for this page from the hash table */
+ unlink_hash(block->hash_link);
+ /* All pending requests for this page must be resubmitted */
+ if (block->wqueue[COND_FOR_SAVED].last_thread)
+ release_queue(&block->wqueue[COND_FOR_SAVED]);
+ }
+ link_to_file_list(block, file, block->hash_link ? 1 : 0);
+ block->status=error? BLOCK_ERROR : 0;
+ block->length=0;
+ block->offset=key_cache_block_size;
+ block->hash_link=hash_link;
+ page_status=PAGE_TO_BE_READ;
+
+ KEYCACHE_DBUG_ASSERT(block->hash_link->block == block);
+ KEYCACHE_DBUG_ASSERT(hash_link->block->hash_link == hash_link);
+ }
+ else
+ {
+ /* This is for secondary requests for a new page only */
+ page_status = block->hash_link == hash_link &&
+ (block->status & BLOCK_READ) ?
+ PAGE_READ : PAGE_WAIT_TO_BE_READ;
+ }
+ }
+
+ _my_cache_read++;
+ }
+ else
+ {
+ reg_requests(block,1);
+ page_status = block->hash_link == hash_link &&
+ (block->status & BLOCK_READ) ?
+ PAGE_READ : PAGE_WAIT_TO_BE_READ;
+ }
+ }
+
+ KEYCACHE_DBUG_ASSERT(page_status != -1);
+ *page_st=page_status;
+
+#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
+ DBUG_EXECUTE("check_keycache2",test_key_cache("end of find_key_block",0););
+#endif
+ KEYCACHE_THREAD_TRACE("find_key_block:end");
+ DBUG_RETURN(block);
+}
- /*
- ** read a key_buffer
- ** filepos must point at a even key_cache_block_size block
- ** if return_buffer is set then the intern buffer is returned if
- ** it can be used
- ** Returns adress to where data is read
- */
+/*
+ Read into a key cache block buffer from disk;
+ do not to report error when the size of successfully read
+ portion is less than read_length, but not less than min_length
+*/
+static void read_block(BLOCK_LINK *block, uint read_length,
+ uint min_length, my_bool primary)
+{
+ uint got_length;
+
+ /* On entry THR_LOCK_keycache is locked */
+
+ KEYCACHE_THREAD_TRACE("read_block");
+ if (primary)
+ { /*
+ This code is executed only by threads
+ that submitted primary requests
+ */
+
+ KEYCACHE_DBUG_PRINT("read_block",
+ ("page to be read by primary request"));
+
+ /* Page is not in buffer yet, is to be read from disk */
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+ got_length=my_pread(block->hash_link->file,block->buffer,
+ read_length,block->hash_link->diskpos,MYF(0));
+ keycache_pthread_mutex_lock(&THR_LOCK_keycache);
+ if (got_length < min_length)
+ block->status|=BLOCK_ERROR;
+ else
+ {
+ block->status=BLOCK_READ;
+ block->length=got_length;
+ }
+ KEYCACHE_DBUG_PRINT("read_block",
+ ("primary request: new page in cache"));
+ /* Signal that all pending requests for this page now can be processed */
+ if (block->wqueue[COND_FOR_REQUESTED].last_thread)
+ release_queue(&block->wqueue[COND_FOR_REQUESTED]);
+ }
+ else
+ { /*
+ This code is executed only by threads
+ that submitted secondary requests
+ */
+ KEYCACHE_DBUG_PRINT("read_block",
+ ("secondary request waiting for new page to be read"));
+ {
+ struct st_my_thread_var *thread=my_thread_var;
+ /* Put the request into a queue and wait until it can be processed */
+ add_to_queue(&block->wqueue[COND_FOR_REQUESTED],thread);
+ do
+ {
+ keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache);
+ }
+ while (thread->next);
+ }
+ KEYCACHE_DBUG_PRINT("read_block",
+ ("secondary request: new page in cache"));
+ }
+}
+
+
+/*
+ Read a block of data from a cached file into a buffer;
+ if return_buffer is set then the cache buffer is returned if
+ it can be used;
+ filepos must be a multiple of 'block_length', but it doesn't
+ have to be a multiple of key_cache_block_size;
+ returns adress from where data is read
+*/
byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length,
- uint block_length __attribute__((unused)),
- int return_buffer __attribute__((unused)))
+ uint block_length __attribute__((unused)),
+ int return_buffer __attribute__((unused)))
{
- reg1 SEC_LINK *next;
int error=0;
DBUG_ENTER("key_cache_read");
DBUG_PRINT("enter", ("file %u, filepos %lu, length %u",
- (uint) file, (ulong) filepos, length));
-
-#ifndef THREAD
- if (block_length > key_cache_block_size)
- return_buffer=0;
-#endif
+ (uint) file,(ulong) filepos,length));
+
if (_my_disk_blocks > 0)
- { /* We have key_cacheing */
+ { /* Key cache is used */
+ reg1 BLOCK_LINK *block;
+ uint offset= (uint) (filepos & (key_cache_block_size-1));
byte *start=buff;
uint read_length;
- pthread_mutex_lock(&THR_LOCK_keycache);
- if (_my_disk_blocks <= 0) /* Resize failed */
- {
- pthread_mutex_unlock(&THR_LOCK_keycache);
- goto no_key_cache;
- }
+ uint status;
+ int page_st;
+
+#ifndef THREAD
+ if (block_length > key_cache_block_size || offset)
+ return_buffer=0;
+#endif
+
+ /* Read data in key_cache_block_size increments */
+ filepos-= offset;
do
{
+ read_length= length > key_cache_block_size ?
+ key_cache_block_size : length;
+ KEYCACHE_DBUG_ASSERT(read_length > 0);
+ keycache_pthread_mutex_lock(&THR_LOCK_keycache);
_my_cache_r_requests++;
- read_length= (length > key_cache_block_size ? key_cache_block_size :
- length);
- if (!(next=find_key_block(file,filepos,&error)))
+ block=find_key_block(file,filepos,0,&page_st);
+ if (page_st != PAGE_READ)
{
- pthread_mutex_unlock(&THR_LOCK_keycache);
- DBUG_RETURN ((byte*) 0); /* Got a fatal error */
+ /* The requested page is to be read into the block buffer */
+ read_block(block,key_cache_block_size,read_length+offset,
+ page_st == PAGE_TO_BE_READ);
}
- if (error)
- { /* Didn't find it in cache */
- if (my_pread(file,next->buffer,read_length,filepos,MYF(MY_NABP)))
- {
- pthread_mutex_unlock(&THR_LOCK_keycache);
- DBUG_RETURN((byte*) 0);
- }
- _my_cache_read++;
+ else if (! (block->status & BLOCK_ERROR) &&
+ block->length < read_length + offset)
+ {
+ /*
+ Impossible if nothing goes wrong:
+ this could only happen if we are using a file with
+ small key blocks and are trying to read outside the file
+ */
+ my_errno=-1;
+ block->status|=BLOCK_ERROR;
}
-#ifndef THREAD /* buffer may be used a long time */
- if (return_buffer)
+
+ if (! ((status=block->status) & BLOCK_ERROR))
{
- pthread_mutex_unlock(&THR_LOCK_keycache);
- DBUG_RETURN (next->buffer);
+#ifndef THREAD
+ if (! return_buffer)
+#endif
+ {
+#if !defined(SERIALIZED_READ_FROM_CACHE)
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+#endif
+
+ /* Copy data from the cache buffer */
+ if (!(read_length & 511))
+ bmove512(buff,block->buffer+offset,read_length);
+ else
+ memcpy(buff,block->buffer+offset,(size_t) read_length);
+
+#if !defined(SERIALIZED_READ_FROM_CACHE)
+ keycache_pthread_mutex_lock(&THR_LOCK_keycache);
+#endif
+ }
}
+
+ remove_reader(block);
+ /*
+ Link the block into the LRU chain
+ if it's the last submitted request for the block
+ */
+ unreg_request(block,1);
+
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+
+ if (status & BLOCK_ERROR)
+ DBUG_RETURN((byte *) 0);
+
+#ifndef THREAD
+ if (return_buffer)
+ return (block->buffer);
#endif
- if (! (read_length & 511))
- bmove512(buff,next->buffer,read_length);
- else
- memcpy(buff,next->buffer,(size_t) read_length);
+
buff+=read_length;
filepos+=read_length;
+ offset=0;
+
} while ((length-= read_length));
- pthread_mutex_unlock(&THR_LOCK_keycache);
DBUG_RETURN(start);
}
-
-no_key_cache:
- _my_cache_r_requests++;
- _my_cache_read++;
+
+ /* Key cache is not used */
+ statistic_increment(_my_cache_r_requests,&THR_LOCK_keycache);
+ statistic_increment(_my_cache_read,&THR_LOCK_keycache);
if (my_pread(file,(byte*) buff,length,filepos,MYF(MY_NABP)))
error=1;
- DBUG_RETURN(error ? (byte*) 0 : buff);
-} /* key_cache_read */
-
+ DBUG_RETURN(error? (byte*) 0 : buff);
+}
- /* write a key_buffer */
- /* We don't have to use pwrite because of write locking */
- /* buff must point at a even key_cache_block_size block */
+/*
+ Write a buffer into disk;
+ filepos must be a multiple of 'block_length', but it doesn't
+ have to be a multiple of key cache block size;
+ if !dont_write then all dirty pages involved in writing should
+ have been flushed from key cache before the function starts
+*/
int key_cache_write(File file, my_off_t filepos, byte *buff, uint length,
- uint block_length __attribute__((unused)),
- int dont_write)
+ uint block_length __attribute__((unused)),
+ int dont_write)
{
- reg1 SEC_LINK *next;
+ reg1 BLOCK_LINK *block;
int error=0;
+
DBUG_ENTER("key_cache_write");
- DBUG_PRINT("enter", ("file %u, filepos %lu, length %u",
- (uint) file, (ulong) filepos, length));
+ DBUG_PRINT("enter", ("file %u, filepos %lu, length %u block_length %u",
+ (uint) file,(ulong) filepos,length,block_length));
if (!dont_write)
- { /* Forced write of buffer */
- _my_cache_write++;
+ { /* Force writing from buff into disk */
+ statistic_increment(_my_cache_write, &THR_LOCK_keycache);
if (my_pwrite(file,buff,length,filepos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
DBUG_RETURN(1);
}
-
+
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
DBUG_EXECUTE("check_keycache",test_key_cache("start of key_cache_write",1););
#endif
+
if (_my_disk_blocks > 0)
- { /* We have key_cacheing */
+ { /* Key cache is used */
uint read_length;
- pthread_mutex_lock(&THR_LOCK_keycache);
- if (_my_disk_blocks <= 0) /* If resize failed */
- {
- pthread_mutex_unlock(&THR_LOCK_keycache);
- goto no_key_cache;
- }
-
- _my_cache_w_requests++;
+ uint offset= (uint) (filepos & (key_cache_block_size-1));
+ int page_st;
+
+ /* Write data in key_cache_block_size increments */
+ filepos-= offset;
do
{
- read_length= length > key_cache_block_size ? key_cache_block_size : length;
- if (!(next=find_key_block(file,filepos,&error)))
- goto end; /* Fatal error */
- if (!dont_write) /* If we wrote buff at start */
+ read_length= length > key_cache_block_size ?
+ key_cache_block_size : length;
+ KEYCACHE_DBUG_ASSERT(read_length > 0);
+ keycache_pthread_mutex_lock(&THR_LOCK_keycache);
+ _my_cache_w_requests++;
+ block=find_key_block(file, filepos, 1, &page_st);
+ if (page_st != PAGE_READ &&
+ (offset || read_length < key_cache_block_size))
+ read_block(block,
+ offset + read_length >= key_cache_block_size?
+ offset : key_cache_block_size,
+ offset,page_st == PAGE_TO_BE_READ);
+
+ if (!dont_write)
+ { /* buff has been written to disk at start */
+ if ((block->status & BLOCK_CHANGED) &&
+ (!offset && read_length >= key_cache_block_size))
+ link_to_file_list(block, block->hash_link->file, 1);
+ }
+ else if (! (block->status & BLOCK_CHANGED))
+ link_to_changed_list(block);
+
+ set_if_smaller(block->offset,offset)
+ set_if_bigger(block->length,read_length+offset);
+
+ if (! (block->status & BLOCK_ERROR))
{
- if (next->changed) /* Unlink from changed list */
- link_changed_to_file(next,next->file);
+ if (!(read_length & 511))
+ bmove512(block->buffer+offset,buff,read_length);
+ else
+ memcpy(block->buffer+offset,buff,(size_t) read_length);
}
- else if (!next->changed)
- link_file_to_changed(next); /* Add to changed list */
-
- if (!(read_length & 511))
- bmove512(next->buffer,buff,read_length);
- else
- memcpy(next->buffer,buff,(size_t) read_length);
+
+ block->status|=BLOCK_READ;
+
+ /* Unregister the request */
+ block->hash_link->requests--;
+ unreg_request(block,1);
+
+ if (block->status & BLOCK_ERROR)
+ {
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+ error=1;
+ break;
+ }
+
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+
buff+=read_length;
filepos+=read_length;
+ offset=0;
+
} while ((length-= read_length));
- error=0;
- pthread_mutex_unlock(&THR_LOCK_keycache);
- goto end;
- }
-
-no_key_cache:
- if (dont_write)
- { /* We must write, no cache */
- _my_cache_w_requests++;
- _my_cache_write++;
- if (my_pwrite(file,(byte*) buff,length,filepos,
- MYF(MY_NABP | MY_WAIT_IF_FULL)))
- error=1;
- }
-
-end:
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
- DBUG_EXECUTE("check_keycache",test_key_cache("end of key_cache_write",1););
-#endif
- DBUG_RETURN(error);
-} /* key_cache_write */
-
-
- /* Find block in cache */
- /* IF found sector and error is set then next->changed is cleared */
-
-static SEC_LINK *find_key_block(int file, my_off_t filepos, int *error)
-{
- reg1 SEC_LINK *next,**start;
- DBUG_ENTER("find_key_block");
- DBUG_PRINT("enter", ("file %u, filepos %lu",
- (uint) file, (ulong) filepos));
-
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
- DBUG_EXECUTE("check_keycache2",test_key_cache("start of find_key_block",0););
-#endif
-
- *error=0;
- next= *(start= &_my_hash_root[((ulong) (filepos >> key_cache_shift)+(ulong) file) &
- (_my_hash_blocks-1)]);
- while (next && (next->diskpos != filepos || next->file != file))
- next= next->next_hash;
-
- if (next)
- { /* Found block */
- if (next != _my_used_last)
- { /* Relink used-chain */
- if (next == _my_used_first)
- _my_used_first=next->next_used;
- else
- {
- next->prev_used->next_used = next->next_used;
- next->next_used->prev_used = next->prev_used;
- }
- next->prev_used=_my_used_last;
- _my_used_last->next_used=next;
- }
}
else
- { /* New block */
- if (_my_disk_blocks_used+1 <= (uint) _my_disk_blocks)
- { /* There are unused blocks */
- next= &_my_block_root[_my_blocks_used++]; /* Link in hash-chain */
- next->buffer=ADD_TO_PTR(_my_block_mem,
- ((ulong) _my_disk_blocks_used << key_cache_shift),
- byte*);
- /* link first in file_blocks */
- next->changed=0;
- link_into_file_blocks(next,file);
- _my_disk_blocks_used++;
- if (!_my_used_first)
- _my_used_first=next;
- if (_my_used_last)
- _my_used_last->next_used=next; /* Last in used-chain */
+ {
+ /* Key cache is not used */
+ if (dont_write)
+ {
+ statistic_increment(_my_cache_w_requests, &THR_LOCK_keycache);
+ statistic_increment(_my_cache_write, &THR_LOCK_keycache);
+ if (my_pwrite(file,(byte*) buff,length,filepos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ error=1;
}
- else
- { /* Reuse old block */
- next= _my_used_first;
- if (next->changed)
- {
- if (my_pwrite(next->file,next->buffer,key_cache_block_size,
- next->diskpos,
- MYF(MY_NABP | MY_WAIT_IF_FULL)))
- {
- *error=1;
- return((SEC_LINK*) 0);
- }
- _my_cache_write++;
- link_changed_to_file(next,file);
- }
- else
- {
- if (next->file == -1)
- link_into_file_blocks(next,file);
- else
- relink_into_file_blocks(next,file);
- }
- if (next->prev_hash) /* If in hash-link */
- if ((*next->prev_hash=next->next_hash) != 0) /* Remove from link */
- next->next_hash->prev_hash= next->prev_hash;
+ }
- _my_used_last->next_used=next;
- _my_used_first=next->next_used;
- }
- if (*start) /* Link in first in h.-chain */
- (*start)->prev_hash= &next->next_hash;
- next->next_hash= *start; next->prev_hash=start; *start=next;
- next->prev_used=_my_used_last;
- next->file=file;
- next->diskpos=filepos;
- *error=1; /* Block wasn't in memory */
- }
- _my_used_last=next;
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
- DBUG_EXECUTE("check_keycache2",test_key_cache("end of find_key_block",0););
+ DBUG_EXECUTE("exec",test_key_cache("end of key_cache_write",1););
#endif
- DBUG_RETURN(next);
-} /* find_key_block */
+ DBUG_RETURN(error);
+}
-static void free_block(SEC_LINK *used)
+/*
+ Free block: remove reference to it from hash table,
+ remove it from the chain file of dirty/clean blocks
+ and add it at the beginning of the LRU chain
+*/
+static void free_block(BLOCK_LINK *block)
{
- used->file= -1;
- used->changed=0;
- if (used != _my_used_first) /* Relink used-chain */
+ KEYCACHE_THREAD_TRACE("free block");
+ KEYCACHE_DBUG_PRINT("free_block",
+ ("block %u to be freed",BLOCK_NUMBER(block)));
+ if (block->hash_link)
{
- if (used == _my_used_last)
- _my_used_last=used->prev_used;
- else
- {
- used->prev_used->next_used = used->next_used;
- used->next_used->prev_used = used->prev_used;
- }
- used->next_used=_my_used_first;
- used->next_used->prev_used=used;
- _my_used_first=used;
+ block->status|=BLOCK_REASSIGNED;
+ wait_for_readers(block);
+ unlink_hash(block->hash_link);
}
- if ((*used->prev_hash=used->next_hash)) /* Relink hash-chain */
- used->next_hash->prev_hash= used->prev_hash;
- if (used->next_changed) /* Relink changed/file list */
- used->next_changed->prev_changed=used->prev_changed;
- *used->prev_changed=used->next_changed;
- used->prev_hash=0; used->next_hash=0; /* Safety */
+
+ unlink_changed(block);
+ block->status=0;
+ block->length=0;
+ block->offset=key_cache_block_size;
+ KEYCACHE_THREAD_TRACE("free block");
+ KEYCACHE_DBUG_PRINT("free_block",
+ ("block is freed"));
+ unreg_request(block,0);
+ block->hash_link=NULL;
}
- /* Flush all changed blocks to disk. Free used blocks if requested */
-
-static int cmp_sec_link(SEC_LINK **a, SEC_LINK **b)
+static int cmp_sec_link(BLOCK_LINK **a, BLOCK_LINK **b)
{
- return (((*a)->diskpos < (*b)->diskpos) ? -1 :
- ((*a)->diskpos > (*b)->diskpos) ? 1 : 0);
+ return (((*a)->hash_link->diskpos < (*b)->hash_link->diskpos) ? -1 :
+ ((*a)->hash_link->diskpos > (*b)->hash_link->diskpos) ? 1 : 0);
}
-static int flush_cached_blocks(File file, SEC_LINK **cache, uint count)
+/*
+ Flush a portion of changed blocks to disk,
+ free used blocks if requested
+*/
+static int flush_cached_blocks(File file, BLOCK_LINK **cache,
+ BLOCK_LINK **end,
+ enum flush_type type)
{
- uint last_errno=0;
- qsort((byte*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link);
- for ( ; count-- ; cache++)
+ int error;
+ int last_errno=0;
+ uint count=end-cache;
+
+ /* Don't lock the cache during the flush */
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+ /*
+ As all blocks referred in 'cache' are marked by BLOCK_IN_FLUSH
+ we are guarunteed no thread will change them
+ */
+ qsort((byte*) cache,count,sizeof(*cache),(qsort_cmp) cmp_sec_link);
+
+ keycache_pthread_mutex_lock(&THR_LOCK_keycache);
+ for ( ; cache != end ; cache++)
{
- if (my_pwrite(file,(*cache)->buffer,key_cache_block_size,
- (*cache)->diskpos,
- MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ BLOCK_LINK *block= *cache;
+
+ KEYCACHE_DBUG_PRINT("flush_cached_blocks",
+ ("block %u to be flushed", BLOCK_NUMBER(block)));
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+ error=my_pwrite(file,block->buffer+block->offset,block->length,
+ block->hash_link->diskpos,MYF(MY_NABP | MY_WAIT_IF_FULL));
+ keycache_pthread_mutex_lock(&THR_LOCK_keycache);
+ _my_cache_write++;
+ if (error)
{
+ block->status|= BLOCK_ERROR;
if (!last_errno)
- last_errno=errno ? errno : -1;
+ last_errno=errno ? errno : -1;
+ }
+ /* type will never be FLUSH_IGNORE_CHANGED here */
+ if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE))
+ {
+ _my_blocks_changed--;
+ free_block(block);
+ }
+ else
+ {
+ block->status&=~BLOCK_IN_FLUSH;
+ link_to_file_list(block,file,1);
+ unreg_request(block,1);
}
+
}
return last_errno;
}
-static int flush_key_blocks_int(File file, enum flush_type type)
+/*
+ Flush all blocks for a file to disk
+*/
+int flush_key_blocks(File file, enum flush_type type)
{
- int error=0,last_errno=0;
- uint count=0;
- SEC_LINK *cache_buff[FLUSH_CACHE],**cache,**pos,**end;
- SEC_LINK *used,*next;
- DBUG_ENTER("flush_key_blocks_int");
+ int last_errno=0;
+ BLOCK_LINK *cache_buff[FLUSH_CACHE],**cache;
+ DBUG_ENTER("flush_key_blocks");
DBUG_PRINT("enter",("file: %d blocks_used: %d blocks_changed: %d",
- file,_my_blocks_used,_my_blocks_changed));
-
- cache=cache_buff; /* If no key cache */
- if (_my_disk_blocks > 0 &&
- (!my_disable_flush_key_blocks || type != FLUSH_KEEP))
- {
+ file,_my_blocks_used,_my_blocks_changed));
+
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
DBUG_EXECUTE("check_keycache",test_key_cache("start of flush_key_blocks",0););
#endif
+
+ keycache_pthread_mutex_lock(&THR_LOCK_keycache);
+
+ cache=cache_buff;
+ if (_my_disk_blocks > 0 &&
+ (!my_disable_flush_key_blocks || type != FLUSH_KEEP))
+ { /* Key cache exists and flush is not disabled */
+ int error=0;
+ uint count=0;
+ BLOCK_LINK **pos,**end;
+ BLOCK_LINK *first_in_switch=NULL;
+ BLOCK_LINK *block, *next;
+#if defined(KEYCACHE_DEBUG)
+ uint cnt=0;
+#endif
+
if (type != FLUSH_IGNORE_CHANGED)
{
- /* Count how many key blocks we have to cache to be able to
- write everything with so few seeks as possible */
-
- for (used=changed_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- used ;
- used=used->next_changed)
+ /*
+ Count how many key blocks we have to cache to be able
+ to flush all dirty pages with minimum seek moves
+ */
+ for (block=changed_blocks[FILE_HASH(file)] ;
+ block ;
+ block=block->next_changed)
{
- if (used->file == file)
- count++;
- }
- /* Only allocate a new buffer if its bigger than the one we have */
- if (count > FLUSH_CACHE)
- {
- if (!(cache=(SEC_LINK**) my_malloc(sizeof(SEC_LINK*)*count,MYF(0))))
+ if (block->hash_link->file == file)
{
- cache=cache_buff; /* Fall back to safe buffer */
- count=FLUSH_CACHE;
+ count++;
+ KEYCACHE_DBUG_ASSERT(count<=_my_blocks_used);
}
}
+ /* Allocate a new buffer only if its bigger than the one we have */
+ if (count > FLUSH_CACHE &&
+ !(cache=(BLOCK_LINK**) my_malloc(sizeof(BLOCK_LINK*)*count,MYF(0))))
+ {
+ cache=cache_buff;
+ count=FLUSH_CACHE;
+ }
}
-
- /* Go through the keys and write them to buffer to be flushed */
+
+ /* Retrieve the blocks and write them to a buffer to be flushed */
+restart:
end=(pos=cache)+count;
- for (used=changed_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- used ;
- used=next)
+ for (block=changed_blocks[FILE_HASH(file)] ;
+ block ;
+ block=next)
{
- next=used->next_changed;
- if (used->file == file)
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ KEYCACHE_DBUG_ASSERT(cnt <= _my_blocks_used);
+#endif
+ next=block->next_changed;
+ if (block->hash_link->file == file)
{
- if (type != FLUSH_IGNORE_CHANGED)
- {
- if (pos == end)
- {
- if ((error=flush_cached_blocks(file, cache, count)))
- last_errno=error;
- pos=cache;
- }
- *pos++=used;
- _my_cache_write++;
- }
- if (type != FLUSH_KEEP && type != FLUSH_FORCE_WRITE)
- {
- /* This will not destroy position or data */
- _my_blocks_changed--;
- free_block(used);
- }
- else
- link_changed_to_file(used,file);
+ /*
+ Mark the block with BLOCK_IN_FLUSH in order not to let
+ other threads to use it for new pages and interfere with
+ our sequence ot flushing dirty file pages
+ */
+ block->status|= BLOCK_IN_FLUSH;
+
+ if (! (block->status & BLOCK_IN_SWITCH))
+ { /*
+ We care only for the blocks for which flushing was not
+ initiated by other threads as a result of page swapping
+ */
+ reg_requests(block,1);
+ if (type != FLUSH_IGNORE_CHANGED)
+ { /* It's not a temporary file */
+ if (pos == end)
+ { /*
+ This happens only if there is not enough
+ memory for the big block
+ */
+ if ((error=flush_cached_blocks(file,cache,end,type)))
+ last_errno=error;
+ /*
+ Restart the scan as some other thread might have changed
+ the changed blocks chain: the blocks that were in switch
+ state before the flush started have to be excluded
+ */
+ goto restart;
+ }
+ *pos++=block;
+ }
+ else
+ {
+ /* It's a temporary file */
+ _my_blocks_changed--;
+ free_block(block);
+ }
+ }
+ else
+ { /* Link the block into a list of blocks 'in switch' */
+ unlink_changed(block);
+ link_changed(block,&first_in_switch);
+ }
}
}
if (pos != cache)
{
- if ((error=flush_cached_blocks(file, cache, (uint) (pos-cache))))
- last_errno=error;
+ if ((error=flush_cached_blocks(file,cache,pos,type)))
+ last_errno=error;
+ }
+ /* Wait until list of blocks in switch is empty */
+ while (first_in_switch)
+ {
+#if defined(KEYCACHE_DEBUG)
+ cnt=0;
+#endif
+ block=first_in_switch;
+ {
+ struct st_my_thread_var *thread=my_thread_var;
+ add_to_queue(&block->wqueue[COND_FOR_SAVED], thread);
+ do
+ {
+ keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache);
+ }
+ while (thread->next);
+ }
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ KEYCACHE_DBUG_ASSERT(cnt <= _my_blocks_used);
+#endif
}
/* The following happens very seldom */
- if (type != FLUSH_KEEP && type != FLUSH_FORCE_WRITE)
+ if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE))
{
- for (used=file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- used ;
- used=next)
+#if defined(KEYCACHE_DEBUG)
+ cnt=0;
+#endif
+ for (block=file_blocks[FILE_HASH(file)] ;
+ block ;
+ block=next)
{
- next=used->next_changed;
- if (used->file == file && (!used->changed ||
- type == FLUSH_IGNORE_CHANGED))
- free_block(used);
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ KEYCACHE_DBUG_ASSERT(cnt <= _my_blocks_used);
+#endif
+ next=block->next_changed;
+ if (block->hash_link->file == file &&
+ (! (block->status & BLOCK_CHANGED)
+ || type == FLUSH_IGNORE_CHANGED))
+ {
+ reg_requests(block,1);
+ free_block(block);
+ }
}
}
+ }
+
+ keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+
#ifndef DBUG_OFF
- DBUG_EXECUTE("check_keycache",test_key_cache("end of flush_key_blocks",0););
+ DBUG_EXECUTE("check_keycache",
+ test_key_cache("end of flush_key_blocks",0););
#endif
- }
if (cache != cache_buff)
my_free((gptr) cache,MYF(0));
if (last_errno)
- errno=last_errno; /* Return first error */
+ errno=last_errno; /* Return first error */
DBUG_RETURN(last_errno != 0);
}
-/*
- Flush all blocks for a specific file to disk
+/*
+ Flush all blocks in the key cache to disk
+*/
+static int flush_all_key_blocks()
+{
+#if defined(KEYCACHE_DEBUG)
+ uint cnt=0;
+#endif
+ while (_my_blocks_changed > 0)
+ {
+ BLOCK_LINK *block;
+ for (block=_my_used_last->next_used ; ; block=block->next_used)
+ {
+ if (block->hash_link)
+ {
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ KEYCACHE_DBUG_ASSERT(cnt <= _my_blocks_used);
+#endif
+ if (flush_key_blocks(block->hash_link->file, FLUSH_RELEASE))
+ return 1;
+ break;
+ }
+ if (block == _my_used_last)
+ break;
+ }
+ }
+ return 0;
+}
- SYNOPSIS
- flush_all_key_blocks()
- file File descriptor
- type Type of flush operation
- RETURN VALUES
- 0 Ok
- 1 Error
+#ifndef DBUG_OFF
+/*
+ Test if disk-cache is ok
*/
+static void test_key_cache(const char *where __attribute__((unused)),
+ my_bool lock __attribute__((unused)))
+{
+ /* TODO */
+}
+#endif
-int flush_key_blocks(File file, enum flush_type type)
+#if defined(KEYCACHE_TIMEOUT)
+
+#define KEYCACHE_DUMP_FILE "keycache_dump.txt"
+#define MAX_QUEUE_LEN 100
+
+
+static void keycache_dump()
{
- int res;
- pthread_mutex_lock(&THR_LOCK_keycache);
- res=flush_key_blocks_int(file, type);
- pthread_mutex_unlock(&THR_LOCK_keycache);
- return res;
+ FILE *keycache_dump_file=fopen(KEYCACHE_DUMP_FILE, "w");
+ struct st_my_thread_var *thread_var =my_thread_var;
+ struct st_my_thread_var *last;
+ struct st_my_thread_var *thread;
+ BLOCK_LINK *block;
+ HASH_LINK *hash_link;
+ KEYCACHE_PAGE *page;
+ uint i;
+
+ fprintf(keycache_dump_file, "thread:%u\n", thread->id);
+
+ i=0;
+ thread=last=waiting_for_hash_link.last_thread;
+ fprintf(keycache_dump_file, "queue of threads waiting for hash link\n");
+ if (thread)
+ do
+ {
+ thread=thread->next;
+ page= (KEYCACHE_PAGE *) thread->opt_info;
+ fprintf(keycache_dump_file,
+ "thread:%u, (file,filepos)=(%u,%lu)\n",
+ thread->id,(uint) page->file,(ulong) page->filepos);
+ if (++i == MAX_QUEUE_LEN)
+ break;
+ }
+ while (thread != last);
+
+ i=0;
+ thread=last=waiting_for_block.last_thread;
+ fprintf(keycache_dump_file, "queue of threads waiting for block\n");
+ if (thread)
+ do
+ {
+ thread=thread->next;
+ hash_link= (HASH_LINK *) thread->opt_info;
+ fprintf(keycache_dump_file,
+ "thread:%u hash_link:%u (file,filepos)=(%u,%lu)\n",
+ thread->id, (uint) HASH_LINK_NUMBER(hash_link),
+ (uint) hash_link->file,(ulong) hash_link->diskpos);
+ if (++i == MAX_QUEUE_LEN)
+ break;
+ }
+ while (thread != last);
+
+ for (i=0 ; i< _my_blocks_used ; i++)
+ {
+ int j;
+ block=&_my_block_root[i];
+ hash_link=block->hash_link;
+ fprintf(keycache_dump_file,
+ "block:%u hash_link:%d status:%x #requests=%u waiting_for_readers:%d\n",
+ i, (int) (hash_link ? HASH_LINK_NUMBER(hash_link) : -1),
+ block->status, block->requests, block->condvar ? 1 : 0);
+ for (j=0 ; j < 2; j++)
+ {
+ KEYCACHE_WQUEUE *wqueue=&block->wqueue[j];
+ thread=last=wqueue->last_thread;
+ fprintf(keycache_dump_file, "queue #%d\n", j);
+ if (thread)
+ do
+ {
+ thread=thread->next;
+ fprintf(keycache_dump_file,
+ "thread:%u\n", thread->id);
+ if (++i == MAX_QUEUE_LEN)
+ break;
+ }
+ while (thread != last);
+ }
+ }
+ fprintf(keycache_dump_file, "LRU chain:");
+ block=_my_used_last;
+ if (block)
+ do
+ {
+ block=block->next_used;
+ fprintf(keycache_dump_file,
+ "block:%u, ", BLOCK_NUMBER(block));
+ }
+ while (block != _my_used_last);
+ fprintf(keycache_dump_file, "\n");
+
+ fclose(keycache_dump_file);
}
+#endif /* defined(KEYCACHE_TIMEOUT) */
-/*
- Flush all blocks in the key cache to disk
+#if defined(KEYCACHE_TIMEOUT) && !defined(__WIN__)
- SYNOPSIS
- flush_all_key_blocks()
- NOTE
- We must have a lock on THR_LOCK_keycache before calling this function
+static int keycache_pthread_cond_wait(pthread_cond_t *cond,
+ pthread_mutex_t *mutex)
+{
+ int rc;
+ struct timeval now; /* time when we started waiting */
+ struct timespec timeout; /* timeout value for the wait function */
+ struct timezone tz;
+#if defined(KEYCACHE_DEBUG)
+ int cnt=0;
+#endif
+
+ /* Get current time */
+ gettimeofday(&now, &tz);
+ /* Prepare timeout value */
+ timeout.tv_sec = now.tv_sec + KEYCACHE_TIMEOUT;
+ timeout.tv_nsec = now.tv_usec * 1000; /* timeval uses microseconds. */
+ /* timespec uses nanoseconds. */
+ /* 1 nanosecond = 1000 micro seconds. */
+ KEYCACHE_THREAD_TRACE_END("started waiting");
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ if (cnt % 100 == 0)
+ fprintf(keycache_debug_log, "waiting...\n");
+ fflush(keycache_debug_log);
+#endif
+ rc = pthread_cond_timedwait(cond, mutex, &timeout);
+ KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
+#if defined(KEYCACHE_DEBUG)
+ if (rc == ETIMEDOUT)
+ {
+ fprintf(keycache_debug_log,"aborted by keycache timeout\n");
+ fclose(keycache_debug_log);
+ abort();
+ }
+#endif
+
+ if (rc == ETIMEDOUT)
+ keycache_dump();
+
+#if defined(KEYCACHE_DEBUG)
+ KEYCACHE_DBUG_ASSERT(rc != ETIMEDOUT);
+#else
+ assert(rc != ETIMEDOUT);
+#endif
+ return rc;
+}
+#else
+#if defined(KEYCACHE_DEBUG)
+static int keycache_pthread_cond_wait(pthread_cond_t *cond,
+ pthread_mutex_t *mutex)
+{
+ int rc;
+ KEYCACHE_THREAD_TRACE_END("started waiting");
+ rc = pthread_cond_wait(cond, mutex);
+ KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
+ return rc;
+}
+#endif
+#endif /* defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) */
- RETURN VALUES
- 0 Ok
- 1 Error
-*/
+#if defined(KEYCACHE_DEBUG)
-static int flush_all_key_blocks()
+static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex)
{
- int error=0;
- while (_my_blocks_changed > 0)
- if (flush_key_blocks_int(_my_used_first->file, FLUSH_RELEASE))
- error=1;
- return error;
+ int rc;
+ rc=pthread_mutex_lock(mutex);
+ KEYCACHE_THREAD_TRACE_BEGIN("");
+ return rc;
}
-#ifndef DBUG_OFF
+static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+ KEYCACHE_THREAD_TRACE_END("");
+ pthread_mutex_unlock(mutex);
+}
- /* Test if disk-cache is ok */
-static void test_key_cache(const char *where, my_bool lock)
+static int keycache_pthread_cond_signal(pthread_cond_t *cond)
{
- reg1 uint i,error;
- ulong found,changed;
- SEC_LINK *pos,**prev;
+ int rc;
+ KEYCACHE_THREAD_TRACE("signal");
+ rc=pthread_cond_signal(cond);
+ return rc;
+}
- if (lock)
- {
- pthread_mutex_lock(&THR_LOCK_keycache);
- if (_my_disk_blocks <= 0) /* No active key cache */
- {
- pthread_mutex_unlock(&THR_LOCK_keycache);
- return;
- }
- }
- found=error=0;
- for (i= 0 ; i < _my_hash_blocks ; i++)
- {
- for (pos= *(prev= &_my_hash_root[i]) ;
- pos && found < _my_blocks_used+2 ;
- found++, pos= *(prev= &pos->next_hash))
- {
- if (prev != pos->prev_hash)
- {
- error=1;
- DBUG_PRINT("error",
- ("hash: %d pos: %lx : prev: %lx != pos->prev: %lx",
- i,(ulong) pos,(ulong) prev,(ulong) pos->prev_hash));
- }
+static int keycache_pthread_cond_broadcast(pthread_cond_t *cond)
+{
+ int rc;
+ KEYCACHE_THREAD_TRACE("signal");
+ rc=pthread_cond_broadcast(cond);
+ return rc;
+}
- if (((pos->diskpos >> key_cache_shift)+pos->file) % _my_hash_blocks != i)
- {
- DBUG_PRINT("error",("hash: %d pos: %lx : Wrong disk_buffer %ld",
- i,(ulong) pos,(ulong) pos->diskpos));
- error=1;
- }
- }
- }
- if (found > _my_blocks_used)
- {
- DBUG_PRINT("error",("Found too many hash_pointers"));
- error=1;
- }
- if (error && !_my_printed)
- { /* Write all hash-pointers */
- _my_printed=1;
- for (i=0 ; i < _my_hash_blocks ; i++)
- {
- DBUG_PRINT("loop",("hash: %d _my_hash_root: %lx",i,&_my_hash_root[i]));
- pos= _my_hash_root[i]; found=0;
- while (pos && found < 10)
- {
- DBUG_PRINT("loop",("pos: %lx prev: %lx next: %lx file: %d disk_buffer: %ld", (ulong) pos, (ulong) pos->prev_hash, (ulong) pos->next_hash, (ulong) pos->file, (ulong) pos->diskpos));
- found++; pos= pos->next_hash;
- }
- }
- }
+#if defined(KEYCACHE_DEBUG_LOG)
- found=changed=0;
- if ((pos=_my_used_first))
- {
- while (pos != _my_used_last && found < _my_blocks_used+2)
- {
- found++;
- if (pos->changed)
- changed++;
- if (pos->next_used->prev_used != pos)
- {
- DBUG_PRINT("error",("pos: %lx next_used: %lx next_used->prev: %lx",
- (ulong) pos,
- (ulong) pos->next_used,
- (ulong) pos->next_used->prev_hash));
- error=1;
- }
- pos=pos->next_used;
- }
- found++;
- if (pos->changed)
- changed++;
- }
- if (found != _my_blocks_used)
+static void keycache_debug_print(const char * fmt,...)
+{
+ va_list args;
+ va_start(args,fmt);
+ if (keycache_debug_log)
{
- DBUG_PRINT("error",("Found %lu of %lu keyblocks",found,_my_blocks_used));
- error=1;
+ VOID(vfprintf(keycache_debug_log, fmt, args));
+ VOID(fputc('\n',keycache_debug_log));
}
+ va_end(args);
+}
+#endif /* defined(KEYCACHE_DEBUG_LOG) */
- for (i= 0 ; i < CHANGED_BLOCKS_HASH ; i++)
- {
- found=0;
- prev= &changed_blocks[i];
- for (pos= *prev ; pos && found < _my_blocks_used+2; pos=pos->next_changed)
- {
- found++;
- if (pos->prev_changed != prev)
- {
- DBUG_PRINT("error",("changed_block list %d doesn't point backwards properly",i));
- error=1;
- }
- prev= &pos->next_changed;
- if (((uint) pos->file & CHANGED_BLOCKS_MASK) != i)
- {
- DBUG_PRINT("error",("Wrong file %d in changed blocks: %d",pos->file,i));
- error=1;
- }
- changed--;
- }
- if (pos)
- {
- DBUG_PRINT("error",("changed_blocks %d has recursive link",i));
- error=1;
- }
+#if defined(KEYCACHE_DEBUG_LOG)
+
+
+void keycache_debug_log_close(void)
+{
+ if (keycache_debug_log)
+ fclose(keycache_debug_log);
+}
+#endif /* defined(KEYCACHE_DEBUG_LOG) */
+
+#endif /* defined(KEYCACHE_DEBUG) */
- found=0;
- prev= &file_blocks[i];
- for (pos= *prev ; pos && found < _my_blocks_used+2; pos=pos->next_changed)
- {
- found++;
- if (pos->prev_changed != prev)
- {
- DBUG_PRINT("error",("file_block list %d doesn't point backwards properly",i));
- error=1;
- }
- prev= &pos->next_changed;
- if (((uint) pos->file & CHANGED_BLOCKS_MASK) != i)
- {
- DBUG_PRINT("error",("Wrong file %d in file_blocks: %d",pos->file,i));
- error=1;
- }
- }
- if (pos)
- {
- DBUG_PRINT("error",("File_blocks %d has recursive link",i));
- error=1;
- }
- }
- if (changed != 0)
- {
- DBUG_PRINT("error",("Found %lu blocks that wasn't in changed blocks",
- changed));
- error=1;
- }
- if (error)
- DBUG_PRINT("error",("Found error at %s",where));
- if (lock)
- pthread_mutex_unlock(&THR_LOCK_keycache);
- return;
-} /* test_key_cache */
-#endif
diff --git a/sql/field.cc b/sql/field.cc
index 00bb409e82e..0fa306fe718 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1652,11 +1652,10 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs)
long tmp;
int error= 0;
char *end;
- /* TODO: Make multi-byte-character safe */
- while (len && my_isspace(cs,*from))
- {
- len--; from++;
- }
+
+ tmp= cs->scan(cs, from, from+len, MY_SEQ_SPACES);
+ len-= tmp;
+ from+= tmp;
my_errno=0;
if (unsigned_flag)
{
@@ -1910,11 +1909,10 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
longlong tmp;
int error= 0;
char *end;
- /* TODO: Make multi byte safe */
- while (len && my_isspace(cs,*from))
- { // For easy error check
- len--; from++;
- }
+
+ tmp= cs->scan(cs, from, from+len, MY_SEQ_SPACES);
+ len-= tmp;
+ from+= tmp;
my_errno=0;
if (unsigned_flag)
{
@@ -3878,14 +3876,6 @@ void Field_datetime::sql_type(String &res) const
int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
{
int error= 0;
-#ifdef USE_TIS620
- if (!binary()) {
- ThNormalize((uchar *)ptr, field_length, (uchar *)from, length);
- if (length < field_length) {
- bfill(ptr + length, field_length - length, ' ');
- }
- }
-#else
if (length <= field_length)
{
memcpy(ptr,from,length);
@@ -3909,7 +3899,6 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
}
}
}
-#endif /* USE_TIS620 */
return error;
}
@@ -4062,12 +4051,6 @@ uint Field_string::max_packed_col_length(uint max_length)
int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
{
int error= 0;
-#ifdef USE_TIS620
- if (!binary())
- {
- ThNormalize((uchar *) ptr+2, field_length, (uchar *) from, length);
- }
-#else
if (length > field_length)
{
length=field_length;
@@ -4075,7 +4058,6 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
error= 1;
}
memcpy(ptr+2,from,length);
-#endif /* USE_TIS620 */
int2store(ptr, length);
return error;
}
@@ -4376,28 +4358,11 @@ int Field_blob::store(const char *from,uint len,CHARSET_INFO *cs)
}
else
{
-#ifdef USE_TIS620
- char *th_ptr=0;
-#endif
Field_blob::store_length(len);
if (table->copy_blobs || len <= MAX_FIELD_WIDTH)
{ // Must make a copy
-#ifdef USE_TIS620
- if (!binary())
- {
- /* If there isn't enough memory, use original string */
- if ((th_ptr=(char * ) my_malloc(sizeof(char) * len,MYF(0))))
- {
- ThNormalize((uchar *) th_ptr, len, (uchar *) from, len);
- from= (const char*) th_ptr;
- }
- }
-#endif /* USE_TIS620 */
value.copy(from,len,charset());
from=value.ptr();
-#ifdef USE_TIS620
- my_free(th_ptr,MYF(MY_ALLOW_ZERO_PTR));
-#endif
}
bmove(ptr+packlength,(char*) &from,sizeof(char*));
}
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index c639431c0fa..e4ffeef85c3 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -1672,18 +1672,31 @@ build_template(
field = table->field[i];
if (templ_type == ROW_MYSQL_REC_FIELDS
+ && prebuilt->read_just_key
+ && dict_index_get_nth_col_pos(index, i)
+ == ULINT_UNDEFINED) {
+ /* Skip a column which is not in the index */
+
+ goto skip_field;
+ }
+
+
+ /* TODO: we have removed temporarily the test of which columns
+ to fetch, until the new client/server protocol of 4.1
+ is fixed!!!!!!!!!!!!!!!!!
+
+ if (templ_type == ROW_MYSQL_REC_FIELDS
&& !(fetch_all_in_key &&
- ULINT_UNDEFINED != dict_index_get_nth_col_pos(
- index, i))
+ ULINT_UNDEFINED != dict_index_get_nth_col_pos(
+ index, i))
&& thd->query_id != field->query_id
&& thd->query_id != (field->query_id ^ MAX_ULONG_BIT)
&& thd->query_id !=
(field->query_id ^ (MAX_ULONG_BIT >> 1))) {
- /* This field is not needed in the query, skip it */
-
goto skip_field;
}
+ */
n_requested_fields++;
diff --git a/sql/item.cc b/sql/item.cc
index f1fee7e8ef1..575c5b4d89c 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -347,6 +347,25 @@ void Item_param::set_value(const char *str, uint length)
}
+void Item_param::set_time(TIME *tm, timestamp_type type)
+{
+ ltime.year= tm->year;
+ ltime.month= tm->month;
+ ltime.day= tm->day;
+
+ ltime.hour= tm->hour;
+ ltime.minute= tm->minute;
+ ltime.second= tm->second;
+
+ ltime.second_part= tm->second_part;
+
+ ltime.time_type= type;
+
+ item_is_time= true;
+ item_type= STRING_ITEM;
+}
+
+
void Item_param::set_longdata(const char *str, ulong length)
{
str_value.append(str,length);
@@ -369,11 +388,21 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
{
double nr=val();
return (field->store(nr)) ? -1 : 0;
+ }
+ if (item_is_time)
+ {
+ field->store_time(&ltime, ltime.time_type);
+ return 0;
}
String *result=val_str(&str_value);
return (field->store(result->ptr(),result->length(),field->charset())) ? -1 : 0;
}
+bool Item_param::get_time(TIME *res)
+{
+ *res=ltime;
+ return 0;
+}
double Item_param::val()
{
diff --git a/sql/item.h b/sql/item.h
index ee68563d5cf..7aa33b25db8 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -202,9 +202,11 @@ class Item_param :public Item
public:
longlong int_value;
double real_value;
+ TIME ltime;
enum Item_result item_result_type;
enum Type item_type;
enum enum_field_types buffer_type;
+ bool item_is_time;
my_bool long_data_supplied;
Item_param(char *name_par=0)
@@ -213,6 +215,7 @@ public:
long_data_supplied= false;
item_type= STRING_ITEM;
item_result_type = STRING_RESULT;
+ item_is_time= false;
}
enum Type type() const { return item_type; }
double val();
@@ -227,6 +230,8 @@ public:
void set_long_binary(const char *str, ulong length);
void set_longdata(const char *str, ulong length);
void set_long_end();
+ void set_time(TIME *tm, timestamp_type type);
+ bool get_time(TIME *tm);
void reset() {}
void (*setup_param_func)(Item_param *param, uchar **pos);
enum Item_result result_type () const
@@ -556,12 +561,12 @@ public:
#include "spatial.h"
#include "item_sum.h"
#include "item_func.h"
+#include "item_row.h"
#include "item_cmpfunc.h"
#include "item_strfunc.h"
#include "item_timefunc.h"
#include "item_uniq.h"
#include "item_subselect.h"
-#include "item_row.h"
class Item_copy_string :public Item
{
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 1c579708503..06b7dc451dc 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -392,51 +392,44 @@ longlong Item_func_strcmp::val_int()
return !value ? 0 : (value < 0 ? (longlong) -1 : (longlong) 1);
}
-
void Item_func_interval::fix_length_and_dec()
{
- bool nums=1;
- uint i;
- for (i=0 ; i < arg_count ; i++)
+ if (row->cols() > 8)
{
- if (!args[i])
- return; // End of memory
- if (args[i]->type() != Item::INT_ITEM &&
- args[i]->type() != Item::REAL_ITEM)
+ bool consts=1;
+
+ for (uint i=1 ; consts && i < row->cols() ; i++)
{
- nums=0;
- break;
+ consts&= row->el(i)->const_item();
}
- }
- if (nums && arg_count >= 8)
- {
- if ((intervals=(double*) sql_alloc(sizeof(double)*arg_count)))
+
+ if (consts &&
+ (intervals=(double*) sql_alloc(sizeof(double)*(row->cols()-1))))
{
- for (i=0 ; i < arg_count ; i++)
- intervals[i]=args[i]->val();
+ for (uint i=1 ; i < row->cols(); i++)
+ intervals[i-1]=row->el(i)->val();
}
}
maybe_null= 0;
max_length= 2;
- used_tables_cache|=item->used_tables();
}
/*
return -1 if null value,
0 if lower than lowest
- 1 - arg_count if between args[n] and args[n+1]
- arg_count+1 if higher than biggest argument
+ 1 - arg_count-1 if between args[n] and args[n+1]
+ arg_count if higher than biggest argument
*/
longlong Item_func_interval::val_int()
{
- double value=item->val();
- if (item->null_value)
- return -1; // -1 if null /* purecov: inspected */
+ double value=row->el(0)->val();
+ if (row->el(0)->null_value)
+ return -1; // -1 if null
if (intervals)
{ // Use binary search to find interval
uint start,end;
- start=0; end=arg_count-1;
+ start=1; end=row->cols()-2;
while (start != end)
{
uint mid=(start+end+1)/2;
@@ -447,31 +440,14 @@ longlong Item_func_interval::val_int()
}
return (value < intervals[start]) ? 0 : start+1;
}
- if (args[0]->val() > value)
- return 0;
- for (uint i=1 ; i < arg_count ; i++)
+
+ uint i;
+ for (i=1 ; i < row->cols() ; i++)
{
- if (args[i]->val() > value)
- return i;
+ if (row->el(i)->val() > value)
+ return i-1;
}
- return (longlong) arg_count;
-}
-
-
-void Item_func_interval::update_used_tables()
-{
- Item_func::update_used_tables();
- item->update_used_tables();
- used_tables_cache|=item->used_tables();
- const_item_cache&=item->const_item();
-}
-
-bool Item_func_interval::check_loop(uint id)
-{
- DBUG_ENTER("Item_func_interval::check_loop");
- if (Item_func::check_loop(id))
- DBUG_RETURN(1);
- DBUG_RETURN(item->check_loop(id));
+ return i-1;
}
void Item_func_between::fix_length_and_dec()
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 8641ab410b3..1bb85a9e233 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -267,27 +267,14 @@ public:
class Item_func_interval :public Item_int_func
{
- Item *item;
+ Item_row *row;
double *intervals;
public:
- Item_func_interval(Item *a,List<Item> &list)
- :Item_int_func(list),item(a),intervals(0) {}
+ Item_func_interval(Item_row *a)
+ :Item_int_func(a),row(a),intervals(0) { allowed_arg_cols= a->cols(); }
longlong val_int();
- bool fix_fields(THD *thd, struct st_table_list *tlist, Item **ref)
- {
- return (item->fix_fields(thd, tlist, &item) || item->check_cols(1) ||
- Item_func::fix_fields(thd, tlist, ref));
- }
void fix_length_and_dec();
- ~Item_func_interval() { delete item; }
const char *func_name() const { return "interval"; }
- void update_used_tables();
- bool check_loop(uint id);
- void set_outer_resolving()
- {
- item->set_outer_resolving();
- Item_func::set_outer_resolving();
- }
};
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 66f0510cc3d..03dd940db51 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -2014,9 +2014,8 @@ String *Item_func_conv_charset::val_str(String *str)
d0=d=(unsigned char*)str->ptr();
de=d+dmaxlen;
- while (s < se && d < de)
+ while (1)
{
-
cnvres=from->mb_wc(from,&wc,s,se);
if (cnvres>0)
{
@@ -2089,8 +2088,8 @@ String *Item_func_conv_charset3::val_str(String *str)
d0=d=(unsigned char*)str->ptr();
de=d+dmaxlen;
- while (s < se && d < de){
-
+ while (1)
+ {
cnvres=from_charset->mb_wc(from_charset,&wc,s,se);
if (cnvres>0)
{
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 3b34f111e8d..1bbd5914d36 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -670,6 +670,7 @@ static void close_connections(void)
}
#endif /*EMBEDDED_LIBRARY*/
+
static void close_server_sock()
{
#ifdef HAVE_CLOSE_SERVER_SOCK
@@ -760,8 +761,6 @@ void kill_mysql(void)
DBUG_VOID_RETURN;
}
-#ifndef EMBEDDED_LIBRARY
-
/* Force server down. kill all connections and threads and exit */
#if defined(OS2)
@@ -777,7 +776,7 @@ static void __cdecl kill_server(int sig_ptr)
{
int sig=(int) (long) sig_ptr; // This is passed a int
DBUG_ENTER("kill_server");
-
+#ifndef EMBEDDED_LIBRARY
// if there is a signal during the kill in progress, ignore the other
if (kill_in_progress) // Safety
RETURN_FROM_KILL_SERVER;
@@ -798,19 +797,17 @@ static void __cdecl kill_server(int sig_ptr)
else
unireg_end();
pthread_exit(0); /* purecov: deadcode */
+#endif /* EMBEDDED_LIBRARY */
RETURN_FROM_KILL_SERVER;
}
-#endif /* EMBEDDED_LIBRARY */
#ifdef USE_ONE_SIGNAL_HAND
extern "C" pthread_handler_decl(kill_server_thread,arg __attribute__((unused)))
{
SHUTDOWN_THD;
my_thread_init(); // Initialize new thread
-#ifndef EMBEDDED_LIBRARY
kill_server(0);
-#endif /* EMBEDDED_LIBRARY */
my_thread_end(); // Normally never reached
return 0;
}
@@ -1047,6 +1044,7 @@ static void set_root(const char *path)
#endif
}
+
static void server_init(void)
{
struct sockaddr_in IPaddr;
@@ -1158,7 +1156,7 @@ static void server_init(void)
{
DBUG_PRINT("general",("UNIX Socket is %s",mysql_unix_port));
- if ((unix_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ if ((unix_sock= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
sql_perror("Can't start server : UNIX Socket "); /* purecov: inspected */
unireg_abort(1); /* purecov: inspected */
@@ -1201,6 +1199,7 @@ void yyerror(const char *s)
thd->lex.yylineno);
}
+
#ifndef EMBEDDED_LIBRARY
void close_connection(NET *net,uint errcode,bool lock)
{
@@ -1221,7 +1220,8 @@ void close_connection(NET *net,uint errcode,bool lock)
(void) pthread_mutex_unlock(&LOCK_thread_count);
DBUG_VOID_RETURN;
}
-#endif
+#endif /* EMBEDDED_LIBRARY */
+
/* Called when a thread is aborted */
/* ARGSUSED */
@@ -1772,7 +1772,7 @@ extern "C" pthread_handler_decl(handle_shutdown,arg)
PeekMessage(&msg, NULL, 1, 65534,PM_NOREMOVE);
#if !defined(EMBEDDED_LIBRARY)
if (WaitForSingleObject(hEventShutdown,INFINITE)==WAIT_OBJECT_0)
-#endif
+#endif /* EMBEDDED_LIBRARY */
kill_server(MYSQL_KILL_SIGNAL);
return 0;
}
@@ -1789,6 +1789,7 @@ int __stdcall handle_kill(ulong ctrl_type)
}
#endif
+
#ifdef OS2
extern "C" pthread_handler_decl(handle_shutdown,arg)
{
@@ -1856,8 +1857,8 @@ bool open_log(MYSQL_LOG *log, const char *hostname,
}
-static int init_common_variables(const char *conf_file_name, int argc, char **argv,
- const char **groups)
+static int init_common_variables(const char *conf_file_name, int argc,
+ char **argv, const char **groups)
{
my_umask=0660; // Default umask for new files
my_umask_dir=0700; // Default umask for new directories
@@ -1943,12 +1944,12 @@ static int init_common_variables(const char *conf_file_name, int argc, char **ar
charsets_list= list_charsets(MYF(MY_CS_COMPILED | MY_CS_CONFIG));
if (use_temp_pool && bitmap_init(&temp_pool,1024,1))
- return 2;
-
+ return 1;
return 0;
}
-static int init_thread_environement()
+
+static int init_thread_environment()
{
(void) pthread_mutex_init(&LOCK_mysql_create_db,MY_MUTEX_INIT_SLOW);
(void) pthread_mutex_init(&LOCK_Acl,MY_MUTEX_INIT_SLOW);
@@ -1966,9 +1967,6 @@ static int init_thread_environement()
(void) pthread_mutex_init(&LOCK_bytes_received,MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_timezone,MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_user_conn, MY_MUTEX_INIT_FAST);
-#ifdef HAVE_REPLICATION
- (void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST);
-#endif
(void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
(void) my_rwlock_init(&LOCK_grant, NULL);
@@ -1978,14 +1976,16 @@ static int init_thread_environement()
(void) pthread_cond_init(&COND_flush_thread_cache,NULL);
(void) pthread_cond_init(&COND_manager,NULL);
#ifdef HAVE_REPLICATION
+ (void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&COND_rpl_status, NULL);
#endif
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
PTHREAD_CREATE_DETACHED);
- pthread_attr_setstacksize(&connection_attrib,thread_stack);
pthread_attr_setscope(&connection_attrib, PTHREAD_SCOPE_SYSTEM);
+ if (!(opt_specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_attr_setprio(&connection_attrib,WAIT_PRIOR);
if (pthread_key_create(&THR_THD,NULL) ||
pthread_key_create(&THR_MALLOC,NULL))
@@ -1993,12 +1993,10 @@ static int init_thread_environement()
sql_print_error("Can't create thread-keys");
return 1;
}
-
- (void) thr_setconcurrency(concurrency); // 10 by default
-
return 0;
}
+
static void init_ssl()
{
#ifdef HAVE_OPENSSL
@@ -2017,6 +2015,7 @@ static void init_ssl()
#endif /* HAVE_OPENSSL */
}
+
static int init_server_components()
{
table_cache_init();
@@ -2039,15 +2038,24 @@ static int init_server_components()
NullS, LOG_NEW);
using_update_log=1;
}
-
if (opt_slow_log)
open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log",
NullS, LOG_NORMAL);
+
+ if (opt_bin_log)
+ {
+ open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin",
+ opt_binlog_index_name,LOG_BIN);
+ using_update_log=1;
+ }
+
if (ha_init())
{
sql_print_error("Can't init databases");
return 1;
}
+ if (opt_myisam_log)
+ (void) mi_log(1);
ha_key_cache();
#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT)
@@ -2064,23 +2072,14 @@ static int init_server_components()
locked_in_memory=0;
#endif
- if (opt_myisam_log)
- (void) mi_log( 1 );
ft_init_stopwords(ft_precompiled_stopwords);
init_max_user_conn();
init_update_queries();
-
- if (opt_bin_log)
- {
- open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin",
- opt_binlog_index_name,LOG_BIN);
- using_update_log=1;
- }
-
return 0;
}
+
static void create_maintenance_thread()
{
if (
@@ -2095,74 +2094,88 @@ static void create_maintenance_thread()
}
}
+
static void create_shutdown_thread()
{
-
#ifdef __WIN__
- {
- hEventShutdown=CreateEvent(0, FALSE, FALSE, event_name);
- pthread_t hThread;
- if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0))
- sql_print_error("Warning: Can't create thread to handle shutdown requests");
+ hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name);
+ pthread_t hThread;
+ if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0))
+ sql_print_error("Warning: Can't create thread to handle shutdown requests");
- // On "Stop Service" we have to do regular shutdown
- Service.SetShutdownEvent(hEventShutdown);
- }
+ // On "Stop Service" we have to do regular shutdown
+ Service.SetShutdownEvent(hEventShutdown);
#endif
#ifdef OS2
- {
- pthread_cond_init( &eventShutdown, NULL);
- pthread_t hThread;
- if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0))
- sql_print_error("Warning: Can't create thread to handle shutdown requests");
- }
+ pthread_cond_init(&eventShutdown, NULL);
+ pthread_t hThread;
+ if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0))
+ sql_print_error("Warning: Can't create thread to handle shutdown requests");
#endif
}
-#ifdef __NT__
-void create_named_pipe_thread()
+
+#if defined(__NT__) || defined(HAVE_SMEM)
+static void handle_connections_methods()
{
+ pthread_t hThread;
+ DBUG_ENTER("handle_connections_methods");
+#ifdef __NT__
if (hPipe == INVALID_HANDLE_VALUE &&
- (!have_tcpip || opt_disable_networking))
+ (!have_tcpip || opt_disable_networking) &&
+ !opt_enable_shared_memory)
{
- sql_print_error("TCP/IP or --enable-named-pipe should be configured on NT OS");
- unireg_abort(1);
+ sql_print_error("TCP/IP,--shared-memory or --named-pipe should be configured on NT OS");
+ unireg_abort(1); // Will not return
}
- else
+#endif
+
+ pthread_mutex_lock(&LOCK_thread_count);
+ (void) pthread_cond_init(&COND_handler_count,NULL);
+ handler_count=0;
+#ifdef __NT__
+ if (hPipe != INVALID_HANDLE_VALUE)
{
- pthread_mutex_lock(&LOCK_thread_count);
- (void) pthread_cond_init(&COND_handler_count,NULL);
+ handler_count++;
+ if (pthread_create(&hThread,&connection_attrib,
+ handle_connections_namedpipes, 0))
{
- pthread_t hThread;
- handler_count=0;
- if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe)
- {
- handler_count++;
- if (pthread_create(&hThread,&connection_attrib,
- handle_connections_namedpipes, 0))
- {
- sql_print_error("Warning: Can't create thread to handle named pipes");
- handler_count--;
- }
- }
- if (have_tcpip && !opt_disable_networking)
- {
- handler_count++;
- if (pthread_create(&hThread,&connection_attrib,
- handle_connections_sockets, 0))
- {
- sql_print_error("Warning: Can't create thread to handle named pipes");
- handler_count--;
- }
- }
- while (handler_count > 0)
- pthread_cond_wait(&COND_handler_count,&LOCK_thread_count);
+ sql_print_error("Warning: Can't create thread to handle named pipes");
+ handler_count--;
+ }
+ }
+#endif /* __NT__ */
+ if (have_tcpip && !opt_disable_networking)
+ {
+ handler_count++;
+ if (pthread_create(&hThread,&connection_attrib,
+ handle_connections_sockets, 0))
+ {
+ sql_print_error("Warning: Can't create thread to handle TCP/IP");
+ handler_count--;
+ }
+ }
+#ifdef HAVE_SMEM
+ if (opt_enable_shared_memory)
+ {
+ handler_count++;
+ if (pthread_create(&hThread,&connection_attrib,
+ handle_connections_shared_memory, 0))
+ {
+ sql_print_error("Warning: Can't create thread to handle shared memory");
+ handler_count--;
}
- pthread_mutex_unlock(&LOCK_thread_count);
}
-}
#endif
+ while (handler_count > 0)
+ pthread_cond_wait(&COND_handler_count,&LOCK_thread_count);
+ pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_VOID_RETURN;
+}
+#endif /* defined(__NT__) || defined(HAVE_SMEM) */
+
+
#ifndef EMBEDDED_LIBRARY
#ifdef __WIN__
int win_main(int argc, char **argv)
@@ -2170,10 +2183,10 @@ int win_main(int argc, char **argv)
int main(int argc, char **argv)
#endif
{
- int init_error;
-
DEBUGGER_OFF;
+ MY_INIT(argv[0]); // init my_sys library & pthreads
+
#ifdef _CUSTOMSTARTUPCONFIG_
if (_cust_check_startup())
{
@@ -2182,26 +2195,20 @@ int main(int argc, char **argv)
}
#endif
- MY_INIT(argv[0]); // init my_sys library & pthreads
-
- if ((init_error=init_common_variables(MYSQL_CONFIG_NAME,
- argc, argv, load_default_groups)))
- if (init_error == 2)
- unireg_abort(1);
- else
- exit(1);
+ if (init_common_variables(MYSQL_CONFIG_NAME,
+ argc, argv, load_default_groups))
+ unireg_abort(1); // Will do exit
init_signals();
- if (init_thread_environement())
- exit(1);
- select_thread=pthread_self();
- select_thread_in_use=1;
if (!(opt_specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),CONNECT_PRIOR);
+ if (init_thread_environment())
+ unireg_abort(1);
+ pthread_attr_setstacksize(&connection_attrib,thread_stack);
+ (void) thr_setconcurrency(concurrency); // 10 by default
- if (!(opt_specialflag & SPECIAL_NO_PRIOR))
- my_pthread_attr_setprio(&connection_attrib,WAIT_PRIOR);
-
+ select_thread=pthread_self();
+ select_thread_in_use=1;
init_ssl();
#ifdef HAVE_LIBWRAP
@@ -2224,21 +2231,21 @@ int main(int argc, char **argv)
if (opt_bin_log && !server_id)
{
server_id= !master_host ? 1 : 2;
- switch (server_id) {
#ifdef EXTRA_DEBUG
+ switch (server_id) {
case 1:
sql_print_error("\
Warning: You have enabled the binary log, but you haven't set server-id:\n\
Updates will be logged to the binary log, but connections to slaves will\n\
not be accepted.");
break;
-#endif
case 2:
sql_print_error("\
Warning: You should set server-id to a non-0 value if master_host is set.\n\
The server will not act as a slave.");
break;
}
+#endif
}
if (init_server_components())
@@ -2250,12 +2257,8 @@ The server will not act as a slave.");
{
freopen(MYSQL_ERR_FILE,"a+",stdout);
freopen(MYSQL_ERR_FILE,"a+",stderr);
- }
-#endif
-
-#ifdef __WIN__
- if (!opt_console)
FreeConsole(); // Remove window
+ }
#endif
/*
@@ -2273,6 +2276,8 @@ The server will not act as a slave.");
if (!opt_bootstrap)
(void) my_delete(pidfile_name,MYF(MY_WME)); // Not needed anymore
#endif
+ if (unix_sock != INVALID_SOCKET)
+ unlink(mysql_unix_port);
exit(1);
}
if (!opt_noacl)
@@ -2306,13 +2311,10 @@ The server will not act as a slave.");
printf(ER(ER_READY),my_progname,server_version,"");
fflush(stdout);
-#ifdef __NT__
- create_named_pipe_thread();
+#if defined(__NT__) || defined(HAVE_SMEM)
+ handle_connections_methods();
#else
handle_connections_sockets(0);
-#ifdef EXTRA_DEBUG2
- sql_print_error("Exiting main thread");
-#endif
#endif /* __NT__ */
/* (void) pthread_attr_destroy(&connection_attrib); */
@@ -2494,13 +2496,14 @@ int main(int argc, char **argv)
Execute all commands from a file. Used by the mysql_install_db script to
create MySQL privilege tables without having to start a full MySQL server.
*/
-#ifndef EMBEDDED_LIBRARY
+
static int bootstrap(FILE *file)
{
- THD *thd= new THD;
- int error;
+ THD *thd;
+ int error= 0;
DBUG_ENTER("bootstrap");
-
+#ifndef EMBEDDED_LIBRARY // TODO: Enable this
+ thd= new THD;
thd->bootstrap=1;
thd->client_capabilities=0;
my_net_init(&thd->net,(st_vio*) 0);
@@ -2528,10 +2531,10 @@ static int bootstrap(FILE *file)
net_end(&thd->net);
thd->cleanup();
delete thd;
+#endif /* EMBEDDED_LIBRARY */
DBUG_RETURN(error);
}
-#endif
static bool read_init_file(char *file_name)
{
@@ -2540,15 +2543,13 @@ static bool read_init_file(char *file_name)
DBUG_PRINT("enter",("name: %s",file_name));
if (!(file=my_fopen(file_name,O_RDONLY,MYF(MY_WME))))
return(1);
-#ifndef EMBEDDED_LIBRARY
bootstrap(file); /* Ignore errors from this */
-#endif
(void) my_fclose(file,MYF(MY_WME));
return 0;
}
-#ifndef EMBEDDED_LIBRARY
+#ifndef EMBEDDED_LIBRARY
static void create_new_thread(THD *thd)
{
DBUG_ENTER("create_new_thread");
@@ -2629,7 +2630,8 @@ static void create_new_thread(THD *thd)
DBUG_PRINT("info",("Thread created"));
DBUG_VOID_RETURN;
}
-#endif
+#endif /* EMBEDDED_LIBRARY */
+
#ifdef SIGNALS_DONT_BREAK_READ
inline void kill_broken_server()
@@ -2647,10 +2649,11 @@ inline void kill_broken_server()
#define MAYBE_BROKEN_SYSCALL
#endif
-#ifndef EMBEDDED_LIBRARY
/* Handle new connections and spawn new process to handle them */
-extern "C" pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused)))
+#ifndef EMBEDDED_LIBRARY
+extern "C" pthread_handler_decl(handle_connections_sockets,
+ arg __attribute__((unused)))
{
my_socket sock,new_sock;
uint error_count=0;
@@ -2939,6 +2942,7 @@ extern "C" pthread_handler_decl(handle_connections_namedpipes,arg)
}
#endif /* __NT__ */
+
/*
Thread of shared memory's service
@@ -2947,6 +2951,7 @@ extern "C" pthread_handler_decl(handle_connections_namedpipes,arg)
handle_connections_shared_memory Thread handle
arg Arguments of thread
*/
+
#ifdef HAVE_SMEM
pthread_handler_decl(handle_connections_shared_memory,arg)
{
@@ -2979,13 +2984,13 @@ pthread_handler_decl(handle_connections_shared_memory,arg)
DBUG_PRINT("general",("Waiting for allocated shared memory."));
-/*
- The name of event and file-mapping events create agree next rule:
- shared_memory_base_name+unique_part
- Where:
- shared_memory_base_name is unique value for each server
- unique_part is unique value for each object (events and file-mapping)
-*/
+ /*
+ The name of event and file-mapping events create agree next rule:
+ shared_memory_base_name+unique_part
+ Where:
+ shared_memory_base_name is unique value for each server
+ unique_part is unique value for each object (events and file-mapping)
+ */
suffix_pos = strxmov(tmp,shared_memory_base_name,"_",NullS);
strmov(suffix_pos, "CONNECT_REQUEST");
if ((event_connect_request = CreateEvent(NULL,FALSE,FALSE,tmp)) == 0)
@@ -3151,14 +3156,15 @@ error:
DBUG_RETURN(0);
}
#endif /* HAVE_SMEM */
-
#endif /* EMBEDDED_LIBRARY */
-/******************************************************************************
-** handle start options
+
+/****************************************************************************
+ Handle start options
******************************************************************************/
-enum options {
+enum options
+{
OPT_ISAM_LOG=256, OPT_SKIP_NEW,
OPT_SKIP_GRANT, OPT_SKIP_LOCK,
OPT_ENABLE_LOCK, OPT_USE_LOCKING,
@@ -4039,7 +4045,7 @@ struct my_option my_long_options[] =
"Number of seconds to wait for more data from a master/slave connection before aborting the read.",
(gptr*) &slave_net_timeout, (gptr*) &slave_net_timeout, 0,
GET_ULONG, REQUIRED_ARG, SLAVE_NET_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0},
-#endif
+#endif /* HAVE_REPLICATION */
{"slow_launch_time", OPT_SLOW_LAUNCH_TIME,
"If creating the thread takes longer than this value (in seconds), the Slow_launch_threads counter will be incremented.",
(gptr*) &slow_launch_time, (gptr*) &slow_launch_time, 0, GET_ULONG,
@@ -4941,7 +4947,7 @@ static void fix_paths(void)
if (!(slave_load_tmpdir = (char*) my_strdup(mysql_tmpdir, MYF(MY_FAE))))
exit(1);
}
-#endif
+#endif /* HAVE_REPLICATION */
}
diff --git a/sql/protocol.cc b/sql/protocol.cc
index da9f5712e3a..b81aa54af99 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -923,6 +923,7 @@ bool Protocol_prep::store_long(longlong from)
{
#ifndef DEBUG_OFF
DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_INT24 ||
field_types[field_pos] == MYSQL_TYPE_LONG);
#endif
field_pos++;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index d5deabf606b..ac79f4ebcf6 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1208,23 +1208,26 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
while (!thd->fatal_error && thd->lex.found_colon)
{
+ char *packet= thd->lex.found_colon;
/*
Multiple queries exits, execute them individually
*/
if (thd->lock || thd->open_tables || thd->derived_tables)
close_thread_tables(thd);
- uint length= thd->query_length-(uint)(thd->lex.found_colon-thd->query);
+ ulong length= thd->query_length-(ulong)(thd->lex.found_colon-thd->query);
/* Remove garbage at start of query */
- char *packet= thd->lex.found_colon;
- while (my_isspace(system_charset_info,packet[0]) && length > 0)
+ while (my_isspace(system_charset_info, *packet) && length > 0)
{
packet++;
length--;
}
- thd->query= packet;
thd->query_length= length;
+ thd->query= packet;
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ thd->query_id= query_id++;
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
mysql_parse(thd, packet, length);
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index e8ba7e91c7b..758054a6e48 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -256,11 +256,87 @@ static void setup_param_double(Item_param *param, uchar **pos)
*pos+= 8;
}
+static void setup_param_time(Item_param *param, uchar **pos)
+{
+ ulong length;
+
+ if ((length= get_param_length(pos)))
+ {
+ uchar *to= *pos;
+ TIME tm;
+
+ tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0;
+
+ tm.day= (ulong) sint4korr(to+1);
+ tm.hour= (uint) to[5];
+ tm.minute= (uint) to[6];
+ tm.second= (uint) to[7];
+
+ tm.year= tm.month= 0;
+ tm.neg= (bool)to[0];
+
+ param->set_time(&tm, TIMESTAMP_TIME);
+ }
+ *pos+= length;
+}
+
+static void setup_param_datetime(Item_param *param, uchar **pos)
+{
+ uint length= get_param_length(pos);
+
+ if (length)
+ {
+ uchar *to= *pos;
+ TIME tm;
+
+ tm.second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0;
+
+ if (length > 4)
+ {
+ tm.hour= (uint) to[4];
+ tm.minute= (uint) to[5];
+ tm.second= (uint) to[6];
+ }
+ else
+ tm.hour= tm.minute= tm.second= 0;
+
+ tm.year= (uint) sint2korr(to);
+ tm.month= (uint) to[2];
+ tm.day= (uint) to[3];
+ tm.neg= 0;
+
+ param->set_time(&tm, TIMESTAMP_FULL);
+ }
+ *pos+= length;
+}
+
+static void setup_param_date(Item_param *param, uchar **pos)
+{
+ ulong length;
+
+ if ((length= get_param_length(pos)))
+ {
+ uchar *to= *pos;
+ TIME tm;
+
+ tm.year = (uint) sint2korr(to);
+ tm.month= (uint) to[2];
+ tm.day= (uint) to[3];
+
+ tm.hour= tm.minute= tm.second= 0;
+ tm.second_part= 0;
+ tm.neg= 0;
+
+ param->set_time(&tm, TIMESTAMP_DATE);
+ }
+ *pos+= length;
+}
+
static void setup_param_str(Item_param *param, uchar **pos)
{
- ulong len=get_param_length(pos);
+ ulong len= get_param_length(pos);
param->set_value((const char *)*pos, len);
- *pos+=len;
+ *pos+= len;
}
static void setup_param_functions(Item_param *param, uchar param_type)
@@ -290,6 +366,19 @@ static void setup_param_functions(Item_param *param, uchar param_type)
param->setup_param_func= setup_param_double;
param->item_result_type = REAL_RESULT;
break;
+ case FIELD_TYPE_TIME:
+ param->setup_param_func= setup_param_time;
+ param->item_result_type = STRING_RESULT;
+ break;
+ case FIELD_TYPE_DATE:
+ param->setup_param_func= setup_param_date;
+ param->item_result_type = STRING_RESULT;
+ break;
+ case FIELD_TYPE_DATETIME:
+ case FIELD_TYPE_TIMESTAMP:
+ param->setup_param_func= setup_param_datetime;
+ param->item_result_type = STRING_RESULT;
+ break;
default:
param->setup_param_func= setup_param_str;
param->item_result_type = STRING_RESULT;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index f758fef4fc9..e554ac1d560 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -732,7 +732,9 @@ JOIN::exec()
result->send_fields(fields_list,1);
if (!having || having->val_int())
{
- if (do_send_rows && result->send_data(fields_list))
+ if (do_send_rows && (procedure ? (procedure->send_row(fields_list) ||
+ procedure->end_of_records())
+ : result->send_data(fields_list)))
error= 1;
else
{
@@ -743,7 +745,6 @@ JOIN::exec()
else
error=(int) result->send_eof();
}
- delete procedure;
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 0d604d043cc..654076c3f41 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -706,7 +706,7 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
char *to_start= to;
uchar *to_end= (uchar*) to+to_length;
- while ((uchar*) from < from_end)
+ while (1)
{
if ((cnvres=from_cs->mb_wc(from_cs, &wc, (uchar*) from, from_end)) > 0)
from+= cnvres;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index f6834f71bd9..239e8728d48 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -587,7 +587,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
literal text_literal insert_ident order_ident
simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr
table_wild opt_pad no_in_expr expr_expr simple_expr no_and_expr
- using_list expr_or_default set_expr_or_default
+ using_list expr_or_default set_expr_or_default interval_expr
param_marker singlerow_subselect singlerow_subselect_init
exists_subselect exists_subselect_init
@@ -1930,10 +1930,10 @@ expr_expr:
| expr '^' expr { $$= new Item_func_bit_xor($1,$3); }
| expr '&' expr { $$= new Item_func_bit_and($1,$3); }
| expr '%' expr { $$= new Item_func_mod($1,$3); }
- | expr '+' INTERVAL_SYM expr interval
- { $$= new Item_date_add_interval($1,$4,$5,0); }
- | expr '-' INTERVAL_SYM expr interval
- { $$= new Item_date_add_interval($1,$4,$5,1); }
+ | expr '+' interval_expr interval
+ { $$= new Item_date_add_interval($1,$3,$4,0); }
+ | expr '-' interval_expr interval
+ { $$= new Item_date_add_interval($1,$3,$4,1); }
| expr COLLATE_SYM collation_name
{ $$= new Item_func_set_collation($1,$3); };
@@ -1977,10 +1977,10 @@ no_in_expr:
| no_in_expr '&' expr { $$= new Item_func_bit_and($1,$3); }
| no_in_expr '%' expr { $$= new Item_func_mod($1,$3); }
| no_in_expr MOD_SYM expr { $$= new Item_func_mod($1,$3); }
- | no_in_expr '+' INTERVAL_SYM expr interval
- { $$= new Item_date_add_interval($1,$4,$5,0); }
- | no_in_expr '-' INTERVAL_SYM expr interval
- { $$= new Item_date_add_interval($1,$4,$5,1); }
+ | no_in_expr '+' interval_expr interval
+ { $$= new Item_date_add_interval($1,$3,$4,0); }
+ | no_in_expr '-' interval_expr interval
+ { $$= new Item_date_add_interval($1,$3,$4,1); }
| simple_expr;
/* expressions that begin with 'expr' that does NOT follow AND */
@@ -2032,12 +2032,16 @@ no_and_expr:
| no_and_expr '&' expr { $$= new Item_func_bit_and($1,$3); }
| no_and_expr '%' expr { $$= new Item_func_mod($1,$3); }
| no_and_expr MOD_SYM expr { $$= new Item_func_mod($1,$3); }
- | no_and_expr '+' INTERVAL_SYM expr interval
- { $$= new Item_date_add_interval($1,$4,$5,0); }
- | no_and_expr '-' INTERVAL_SYM expr interval
- { $$= new Item_date_add_interval($1,$4,$5,1); }
+ | no_and_expr '+' interval_expr interval
+ { $$= new Item_date_add_interval($1,$3,$4,0); }
+ | no_and_expr '-' interval_expr interval
+ { $$= new Item_date_add_interval($1,$3,$4,1); }
| simple_expr;
+interval_expr:
+ INTERVAL_SYM expr { $$=$2; }
+ ;
+
simple_expr:
simple_ident
| literal
@@ -2063,8 +2067,11 @@ simple_expr:
| NOT expr %prec NEG { $$= new Item_func_not($2); }
| '!' expr %prec NEG { $$= new Item_func_not($2); }
| '(' expr ')' { $$= $2; }
- /* Note: In SQL-99 "ROW" is optional, but not having it mandatory
- causes conflicts with the INTERVAL syntax. */
+ | '(' expr ',' expr_list ')'
+ {
+ $4->push_front($2);
+ $$= new Item_row(*$4);
+ }
| ROW_SYM '(' expr ',' expr_list ')'
{
$5->push_front($3);
@@ -2122,10 +2129,10 @@ simple_expr:
$$= new Item_func_curtime($3);
Lex->safe_to_cache_query=0;
}
- | DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
- { $$= new Item_date_add_interval($3,$6,$7,0); }
- | DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
- { $$= new Item_date_add_interval($3,$6,$7,1); }
+ | DATE_ADD_INTERVAL '(' expr ',' interval_expr interval ')'
+ { $$= new Item_date_add_interval($3,$5,$6,0); }
+ | DATE_SUB_INTERVAL '(' expr ',' interval_expr interval ')'
+ { $$= new Item_date_add_interval($3,$5,$6,1); }
| DATABASE '(' ')'
{
$$= new Item_func_database();
@@ -2185,14 +2192,21 @@ simple_expr:
{ $$= new Item_func_if($3,$5,$7); }
| INSERT '(' expr ',' expr ',' expr ',' expr ')'
{ $$= new Item_func_insert($3,$5,$7,$9); }
- | INTERVAL_SYM expr interval '+' expr
+ | interval_expr interval '+' expr
/* we cannot put interval before - */
- { $$= new Item_date_add_interval($5,$2,$3,0); }
- | INTERVAL_SYM '(' expr ',' expr_list ')'
- { $$= new Item_func_interval($3,* $5); }
+ { $$= new Item_date_add_interval($4,$1,$2,0); }
+ | interval_expr
+ {
+ if ($1->type() != Item::ROW_ITEM)
+ {
+ send_error(Lex->thd, ER_SYNTAX_ERROR);
+ YYABORT;
+ }
+ $$= new Item_func_interval((Item_row *)$1);
+ }
| LAST_INSERT_ID '(' ')'
{
- $$= get_system_var(OPT_SESSION, "last_insert_id", 14,
+ $$= get_system_var(OPT_SESSION, "last_insert_id", 14,
"last_insert_id()");
}
| LAST_INSERT_ID '(' expr ')'
@@ -2247,10 +2261,10 @@ simple_expr:
| MPOLYFROMTEXT '(' expr ',' expr ')'
{ $$= new Item_func_geometry_from_text($3); }
| MULTIPOINT '(' expr_list ')'
- { $$= new Item_func_spatial_collection(* $3,
+ { $$= new Item_func_spatial_collection(* $3,
Geometry::wkbMultiPoint, Geometry::wkbPoint); }
| MULTIPOLYGON '(' expr_list ')'
- { $$= new Item_func_spatial_collection(* $3,
+ { $$= new Item_func_spatial_collection(* $3,
Geometry::wkbMultiPolygon, Geometry::wkbPolygon ); }
| NOW_SYM optional_braces
{ $$= new Item_func_now(); Lex->safe_to_cache_query=0;}
@@ -2269,7 +2283,7 @@ simple_expr:
| POLYFROMTEXT '(' expr ',' expr ')'
{ $$= new Item_func_geometry_from_text($3); }
| POLYGON '(' expr_list ')'
- { $$= new Item_func_spatial_collection(* $3,
+ { $$= new Item_func_spatial_collection(* $3,
Geometry::wkbPolygon, Geometry::wkbLineString); }
| POSITION_SYM '(' no_in_expr IN_SYM expr ')'
{ $$ = new Item_func_locate($5,$3); }
@@ -2429,7 +2443,7 @@ in_sum_expr:
};
cast_type:
- BINARY { $$=ITEM_CAST_BINARY; }
+ BINARY { $$=ITEM_CAST_BINARY; }
| CHAR_SYM { $$=ITEM_CAST_CHAR; }
| SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; }
| SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; }
@@ -4651,7 +4665,7 @@ subselect_start:
'(' SELECT_SYM
{
LEX *lex=Lex;
- if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN &&
+ if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN &&
lex->sql_command <= (int)SQLCOM_HA_READ) || lex->sql_command == (int)SQLCOM_KILL) {
send_error(lex->thd, ER_SYNTAX_ERROR);
YYABORT;
diff --git a/strings/ctype-big5.c b/strings/ctype-big5.c
index a475a36d049..37bf2aba509 100644
--- a/strings/ctype-big5.c
+++ b/strings/ctype-big5.c
@@ -6175,6 +6175,9 @@ my_wc_mb_big5(CHARSET_INFO *cs __attribute__((unused)),
int code;
+ if (s >= e)
+ return MY_CS_TOOSMALL;
+
if(wc<0x80)
{
s[0]=wc;
@@ -6200,6 +6203,9 @@ my_mb_wc_big5(CHARSET_INFO *cs __attribute__((unused)),
int hi=s[0];
+ if (s >= e)
+ return MY_CS_TOOFEW(0);
+
if(hi<0x80)
{
pwc[0]=hi;
diff --git a/strings/ctype-bin.c b/strings/ctype-bin.c
index de137377c65..48d5536a2cb 100644
--- a/strings/ctype-bin.c
+++ b/strings/ctype-bin.c
@@ -96,6 +96,9 @@ static int my_mb_wc_bin(CHARSET_INFO *cs __attribute__((unused)),
const unsigned char *str,
const unsigned char *end __attribute__((unused)))
{
+ if (str >= end)
+ return MY_CS_TOOFEW(0);
+
*wc=str[0];
return 1;
}
@@ -105,6 +108,9 @@ static int my_wc_mb_bin(CHARSET_INFO *cs __attribute__((unused)),
unsigned char *s,
unsigned char *e __attribute__((unused)))
{
+ if (s >= e)
+ return MY_CS_TOOSMALL;
+
if (wc < 256)
{
s[0]= (char) wc;
diff --git a/strings/ctype-euc_kr.c b/strings/ctype-euc_kr.c
index ee75673e1c5..d4eebdcb4a9 100644
--- a/strings/ctype-euc_kr.c
+++ b/strings/ctype-euc_kr.c
@@ -8593,6 +8593,9 @@ my_wc_mb_euc_kr(CHARSET_INFO *cs __attribute__((unused)),
{
int code;
+ if (s >= e)
+ return MY_CS_TOOSMALL;
+
if (wc<0x80)
{
s[0]=wc;
@@ -8618,6 +8621,9 @@ my_mb_wc_euc_kr(CHARSET_INFO *cs __attribute__((unused)),
int hi=s[0];
+ if (s >= e)
+ return MY_CS_TOOFEW(0);
+
if (hi<0x80)
{
pwc[0]=hi;
diff --git a/strings/ctype-gb2312.c b/strings/ctype-gb2312.c
index 0820d03b2d0..b01053866be 100644
--- a/strings/ctype-gb2312.c
+++ b/strings/ctype-gb2312.c
@@ -5643,6 +5643,9 @@ my_wc_mb_gb2312(CHARSET_INFO *cs __attribute__((unused)),
{
int code;
+ if (s >= e)
+ return MY_CS_TOOSMALL;
+
if (wc<0x80)
{
s[0]=wc;
@@ -5668,6 +5671,9 @@ my_mb_wc_gb2312(CHARSET_INFO *cs __attribute__((unused)),
hi=s[0];
+ if (s >= e)
+ return MY_CS_TOOFEW(0);
+
if(hi<0x80)
{
pwc[0]=hi;
diff --git a/strings/ctype-gbk.c b/strings/ctype-gbk.c
index 3aff098cd14..c65c9f2123e 100644
--- a/strings/ctype-gbk.c
+++ b/strings/ctype-gbk.c
@@ -9829,6 +9829,9 @@ my_wc_mb_gbk(CHARSET_INFO *cs __attribute__((unused)),
{
int code;
+ if (s >= e)
+ return MY_CS_TOOSMALL;
+
if (wc<0x80)
{
s[0]=wc;
@@ -9852,6 +9855,9 @@ my_mb_wc_gbk(CHARSET_INFO *cs __attribute__((unused)),
{
int hi;
+ if (s >= e)
+ return MY_CS_TOOFEW(0);
+
hi=s[0];
if (hi<0x80)
diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c
index 99d03e187b1..aaa98fd4df2 100644
--- a/strings/ctype-simple.c
+++ b/strings/ctype-simple.c
@@ -106,6 +106,9 @@ int my_mb_wc_8bit(CHARSET_INFO *cs,my_wc_t *wc,
const unsigned char *str,
const unsigned char *end __attribute__((unused)))
{
+ if (str >= end)
+ return MY_CS_TOOFEW(0);
+
*wc=cs->tab_to_uni[*str];
return (!wc[0] && str[0]) ? MY_CS_ILSEQ : 1;
}
@@ -116,6 +119,9 @@ int my_wc_mb_8bit(CHARSET_INFO *cs,my_wc_t wc,
{
MY_UNI_IDX *idx;
+ if (str >= end)
+ return MY_CS_TOOSMALL;
+
for (idx=cs->tab_from_uni; idx->tab ; idx++)
{
if (idx->from <= wc && idx->to >= wc)
@@ -994,7 +1000,7 @@ ulong my_scan_8bit(CHARSET_INFO *cs, const char *str, const char *end, int sq)
return 0;
case MY_SEQ_SPACES:
- for (str++ ; str != end ; str++)
+ for (; str != end ; str++)
{
if (!my_isspace(cs,*str))
break;
diff --git a/strings/ctype-sjis.c b/strings/ctype-sjis.c
index b19bcf1a45a..0408332ee30 100644
--- a/strings/ctype-sjis.c
+++ b/strings/ctype-sjis.c
@@ -4420,6 +4420,9 @@ my_wc_mb_sjis(CHARSET_INFO *cs __attribute__((unused)),
{
int code;
+ if (s >= e)
+ return MY_CS_TOOSMALL;
+
if(wc<0x80)
{
s[0]=wc;
@@ -4442,6 +4445,9 @@ my_mb_wc_sjis(CHARSET_INFO *cs __attribute__((unused)),
my_wc_t *pwc, const uchar *s, const uchar *e){
int hi=s[0];
+ if (s >= e)
+ return MY_CS_TOOFEW(0);
+
if(hi<0x80)
{
pwc[0]=hi;
diff --git a/strings/ctype-ujis.c b/strings/ctype-ujis.c
index 12e177a89be..211b42db470 100644
--- a/strings/ctype-ujis.c
+++ b/strings/ctype-ujis.c
@@ -8350,6 +8350,9 @@ my_wc_mb_euc_jp(CHARSET_INFO *c,my_wc_t wc, unsigned char *s, unsigned char *e)
unsigned char buf[2];
unsigned char c1;
int ret,jp;
+
+ if (s >= e)
+ return MY_CS_TOOSMALL;
if (wc<0x80)
{
diff --git a/strings/ctype-utf8.c b/strings/ctype-utf8.c
index a237b6f14a0..3f859223a15 100644
--- a/strings/ctype-utf8.c
+++ b/strings/ctype-utf8.c
@@ -1586,6 +1586,9 @@ static int my_utf8_uni (CHARSET_INFO *cs __attribute__((unused)) ,
{
unsigned char c = s[0];
+ if (s >= e)
+ return MY_CS_TOOFEW(0);
+
if (c < 0x80)
{
*pwc = c;
@@ -1688,6 +1691,9 @@ static int my_uni_utf8 (CHARSET_INFO *cs __attribute__((unused)) ,
{
int count;
+ if (r >= e)
+ return MY_CS_TOOSMALL;
+
if (wc < 0x80)
count = 1;
else if (wc < 0x800)
diff --git a/tests/client_test.c b/tests/client_test.c
index 12a0275e0e0..964690353fa 100644
--- a/tests/client_test.c
+++ b/tests/client_test.c
@@ -46,12 +46,28 @@ static my_bool tty_password=0;
static MYSQL *mysql=0;
static char query[255];
static char current_db[]= "client_test_db";
+static unsigned int test_count= 0;
+static unsigned int opt_count= 0;
+static unsigned int iter_count= 0;
-#define myheader(str) { fprintf(stdout,"\n\n#######################\n"); \
- fprintf(stdout,"%s",str); \
- fprintf(stdout,"\n#######################\n"); \
- }
+#define myheader(str) \
+{ \
+ fprintf(stdout,"\n\n#####################################\n"); \
+ fprintf(stdout,"%d of (%d/%d): %s",test_count++, iter_count,\
+ opt_count, str); \
+ fprintf(stdout," \n#####################################\n"); \
+}
+#define myheader_r(str) \
+{ \
+ fprintf(stdout,"\n\n#####################################\n"); \
+ fprintf(stdout,"%s", str); \
+ fprintf(stdout," \n#####################################\n"); \
+}
+/*
+ TODO: un comment this in 4.1.1 when it supports mysql_param_result
+ and also enable all meta_param_result tests
+*/
#ifndef mysql_param_result
#define mysql_param_result mysql_prepare_result
#endif
@@ -143,8 +159,9 @@ static void client_connect()
{
int rc;
char buff[255];
- myheader("client_connect");
+ myheader_r("client_connect");
+ fprintf(stdout, "\n Establishig a connection ...");
if (!(mysql = mysql_init(NULL)))
{
myerror("mysql_init() failed");
@@ -158,15 +175,19 @@ static void client_connect()
mysql_close(mysql);
exit(0);
}
+ fprintf(stdout," OK");
/* set AUTOCOMMIT to ON*/
mysql_autocommit(mysql, TRUE);
+ fprintf(stdout, "\n Creating a test database '%s' ...", current_db);
sprintf(buff,"CREATE DATABASE IF NOT EXISTS %s", current_db);
rc = mysql_query(mysql, buff);
myquery(rc);
sprintf(buff,"USE %s", current_db);
rc = mysql_query(mysql, buff);
myquery(rc);
+
+ fprintf(stdout," OK");
}
/********************************************************
@@ -174,16 +195,18 @@ static void client_connect()
*********************************************************/
static void client_disconnect()
{
- myheader("client_disconnect");
+ myheader_r("client_disconnect");
if (mysql)
{
char buff[255];
- fprintf(stdout, "\n droping the test database '%s'", current_db);
+ fprintf(stdout, "\n droping the test database '%s' ...", current_db);
sprintf(buff,"DROP DATABASE IF EXISTS %s", current_db);
mysql_query(mysql, buff);
- fprintf(stdout, "\n closing the connection\n");
+ fprintf(stdout, " OK");
+ fprintf(stdout, "\n closing the connection ...");
mysql_close(mysql);
+ fprintf(stdout, " OK\n");
}
}
@@ -349,8 +372,9 @@ uint my_process_stmt_result(MYSQL_STMT *stmt)
if (!(result= mysql_prepare_result(stmt)))
{
- while (!mysql_fetch(stmt));
- return 0;
+ while (!mysql_fetch(stmt))
+ row_count++;
+ return row_count;
}
field_count= mysql_num_fields(result);
@@ -384,7 +408,10 @@ uint my_process_stmt_result(MYSQL_STMT *stmt)
if (is_null[i])
fprintf(stdout, " %-*s |", (int) field->max_length, "NULL");
else if (length[i] == 0)
+ {
data[i][0]='\0'; /* unmodified buffer */
+ fprintf(stdout, " %*s |", (int) field->max_length, data[i]);
+ }
else if (IS_NUM(field->type))
fprintf(stdout, " %*s |", (int) field->max_length, data[i]);
else
@@ -1570,10 +1597,10 @@ static void test_select()
/* string data */
nData=10;
- strmov(szData,(char *)"venu");
+ strcpy(szData,(char *)"venu");
bind[1].buffer_type=FIELD_TYPE_STRING;
- bind[1].buffer=szData;
- bind[1].buffer_length= sizeof(szData); /* Max string lenth */
+ bind[1].buffer=(char *)szData;
+ bind[1].buffer_length= 4;
bind[1].length= &length[1];
length[1]= 4;
bind[1].is_null=0;
@@ -2106,6 +2133,7 @@ static void test_simple_delete()
bind[1].buffer= szData; /* string data */
bind[1].buffer_length=sizeof(szData);
bind[1].length= &length[1];
+ bind[1].is_null= 0;
length[1]= 5;
bind[0].buffer=(char *)&nData;
@@ -2459,7 +2487,7 @@ static void test_bind_result_ext()
bind[5].buffer=(char *)&d_data;
bind[6].buffer_type=MYSQL_TYPE_STRING;
- bind[6].buffer= (char *)&szData;
+ bind[6].buffer= (char *)szData;
bind[6].buffer_length= sizeof(szData);
bind[6].length= &szLength;
@@ -2633,6 +2661,139 @@ static void test_bind_result_ext1()
mysql_stmt_close(stmt);
}
+/*
+ Generalized fetch conversion routine for all basic types
+*/
+static void bind_fetch(int row_count)
+{
+ MYSQL_STMT *stmt;
+ int rc, i, count= row_count;
+ ulong bit;
+ long data[10];
+ float f_data;
+ double d_data;
+ char s_data[10];
+ ulong length[10];
+ MYSQL_BIND bind[7];
+ my_bool is_null[7];
+
+ stmt = mysql_prepare(mysql,"INSERT INTO test_bind_fetch VALUES(?,?,?,?,?,?,?)",100);
+ mystmt_init(stmt);
+
+ verify_param_count(stmt, 7);
+
+ for (i= 0; i < (int) array_elements(bind); i++)
+ {
+ bind[i].buffer_type= MYSQL_TYPE_LONG;
+ bind[i].buffer= (char *) &data[i];
+ bind[i].is_null= 0;
+ }
+ rc = mysql_bind_param(stmt, bind);
+ mystmt(stmt,rc);
+
+ while (count--)
+ {
+ rc= 10+count;
+ for (i= 0; i < (int) array_elements(bind); i++)
+ {
+ data[i]= rc+i;
+ rc+= 12;
+ }
+ rc = mysql_execute(stmt);
+ mystmt(stmt, rc);
+ }
+
+ rc = mysql_commit(mysql);
+ myquery(rc);
+
+ mysql_stmt_close(stmt);
+
+ myassert(row_count == (int)
+ my_stmt_result("SELECT * FROM test_bind_fetch",50));
+
+ stmt = mysql_prepare(mysql,"SELECT * FROM test_bind_fetch",50);
+ myquery(rc);
+
+ for (i= 0; i < (int) array_elements(bind); i++)
+ {
+ bind[i].buffer= (char *) &data[i];
+ bind[i].length= &length[i];
+ bind[i].is_null= &is_null[i];
+ }
+
+ bind[0].buffer_type= MYSQL_TYPE_TINY;
+ bind[1].buffer_type= MYSQL_TYPE_SHORT;
+ bind[2].buffer_type= MYSQL_TYPE_LONG;
+ bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
+
+ bind[4].buffer_type= MYSQL_TYPE_FLOAT;
+ bind[4].buffer= (char *)&f_data;
+
+ bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
+ bind[5].buffer= (char *)&d_data;
+
+ bind[6].buffer_type= MYSQL_TYPE_STRING;
+ bind[6].buffer= (char *)&s_data;
+ bind[6].buffer_length= sizeof(s_data);
+
+ rc = mysql_bind_result(stmt, bind);
+ mystmt(stmt, rc);
+
+ rc = mysql_execute(stmt);
+ mystmt(stmt, rc);
+
+ rc = mysql_stmt_store_result(stmt);
+ mystmt(stmt, rc);
+
+ while (row_count--)
+ {
+ rc = mysql_fetch(stmt);
+ mystmt(stmt,rc);
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "\n tiny : %ld(%lu)", data[0], length[0]);
+ fprintf(stdout, "\n short : %ld(%lu)", data[1], length[1]);
+ fprintf(stdout, "\n int : %ld(%lu)", data[2], length[2]);
+ fprintf(stdout, "\n longlong : %ld(%lu)", data[3], length[3]);
+ fprintf(stdout, "\n float : %f(%lu)", f_data, length[4]);
+ fprintf(stdout, "\n double : %g(%lu)", d_data, length[5]);
+ fprintf(stdout, "\n char : %s(%lu)", s_data, length[6]);
+
+ bit= 1;
+ rc= 10+row_count;
+ for (i=0; i < 4; i++)
+ {
+ myassert(data[i] == rc+i);
+ myassert(length[i] == bit);
+ bit<<= 1;
+ rc+= 12;
+ }
+
+ /* FLOAT */
+ rc+= i;
+ myassert((int)f_data == rc);
+ myassert(length[4] == 4);
+
+ /* DOUBLE */
+ rc+= 13;
+ myassert((int)d_data == rc);
+ myassert(length[5] == 8);
+
+ /* CHAR */
+ rc+= 13;
+ {
+ char buff[20];
+ long len= my_sprintf(buff, (buff, "%d", rc));
+ myassert(strcmp(s_data,buff)==0);
+ myassert(length[6] == (ulong) len);
+ }
+ }
+ rc = mysql_fetch(stmt);
+ myassert(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
/********************************************************
* to test fetching of date, time and ts *
*********************************************************/
@@ -2753,14 +2914,14 @@ static void test_fetch_date()
myassert(year == 2010);
myassert(y_length == 4);
- myassert(strcmp(dt,"2010-07-10")==0);
- myassert(dt_length == 10);
+ myassert(strcmp(dt,"2010-07-10 00:00:00")==0);
+ myassert(dt_length == 19);
myassert(ts_4[0] == '\0');
myassert(ts4_length == 0);
- myassert(strcmp(ts_6,"1999-12-29")==0);
- myassert(ts6_length == 10);
+ myassert(strcmp(ts_6,"1999-12-29 00:00:00")==0);
+ myassert(ts6_length == 19);
rc = mysql_fetch(stmt);
myassert(rc == MYSQL_NO_DATA);
@@ -2773,26 +2934,17 @@ static void test_fetch_date()
*********************************************************/
static void test_fetch_str()
{
- MYSQL_STMT *stmt;
- int rc, i, round;
- ulong bit;
- long data[10];
- float f_data;
- double d_data;
- char s_data[10];
- ulong length[10];
- MYSQL_BIND bind[7];
- my_bool is_null[7];
+ int rc;
myheader("test_fetch_str");
- rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_str");
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_fetch");
myquery(rc);
rc = mysql_commit(mysql);
myquery(rc);
- rc = mysql_query(mysql,"CREATE TABLE test_bind_str(c1 char(10),\
+ rc = mysql_query(mysql,"CREATE TABLE test_bind_fetch(c1 char(10),\
c2 char(10),\
c3 char(20),\
c4 char(20),\
@@ -2804,106 +2956,7 @@ static void test_fetch_str()
rc = mysql_commit(mysql);
myquery(rc);
- stmt = mysql_prepare(mysql,"INSERT INTO test_bind_str VALUES(?,?,?,?,?,?,?)",100);
- myquery(rc);
-
- verify_param_count(stmt, 7);
-
- round= 0;
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer_type= MYSQL_TYPE_LONG;
- bind[i].buffer= &data[i];
- bind[i].is_null= 0;
- data[i]= round+i+1;
- round= (round +10)*10;
- }
- rc = mysql_bind_param(stmt, bind);
- mystmt(stmt,rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_commit(mysql);
- myquery(rc);
-
- mysql_stmt_close(stmt);
-
- myassert(1 == my_stmt_result("SELECT * FROM test_bind_str",50));
-
- stmt = mysql_prepare(mysql,"SELECT * FROM test_bind_str",50);
- myquery(rc);
-
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer= (char *) &data[i];
- bind[i].length= &length[i];
- bind[i].is_null= &is_null[i];
- }
- bind[0].buffer_type= MYSQL_TYPE_TINY;
- bind[1].buffer_type= MYSQL_TYPE_SHORT;
- bind[2].buffer_type= MYSQL_TYPE_LONG;
- bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
-
- bind[4].buffer_type= MYSQL_TYPE_FLOAT;
- bind[4].buffer= (char *)&f_data;
-
- bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
- bind[5].buffer= (char *)&d_data;
-
- bind[6].buffer_type= MYSQL_TYPE_STRING;
- bind[6].buffer= (char *)&s_data;
-
- rc = mysql_bind_result(stmt, bind);
- mystmt(stmt, rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_fetch(stmt);
- mystmt(stmt,rc);
-
- fprintf(stdout, "\n tiny : %ld(%lu)", data[0], length[0]);
- fprintf(stdout, "\n short : %ld(%lu)", data[1], length[1]);
- fprintf(stdout, "\n int : %ld(%lu)", data[2], length[2]);
- fprintf(stdout, "\n longlong : %ld(%lu)", data[3], length[3]);
- fprintf(stdout, "\n float : %f(%lu)", f_data, length[4]);
- fprintf(stdout, "\n double : %g(%lu)", d_data, length[5]);
- fprintf(stdout, "\n char : %s(%lu)", s_data, length[6]);
-
- round= 0;
- bit= 1;
-
- for (i=0; i < 4; i++)
- {
- myassert(data[i] == round+i+1);
- myassert(length[i] == bit);
- round= (round+10)*10;
- bit<<= 1;
- }
-
- /* FLOAT */
- myassert((int)f_data == round+1+i);
- myassert(length[4] == 4);
-
- /* DOUBLE */
- round= (round+10)*10;
- myassert((int)d_data == round+2+i);
- myassert(length[5] == 8);
-
- /* CHAR */
- round= (round+10)*10;
- {
- char buff[20];
- long len= my_sprintf(buff, (buff, "%d", round+3+i));
- myassert(strcmp(s_data,buff)==0);
- myassert(length[6] == (ulong) len);
- }
-
- rc = mysql_fetch(stmt);
- myassert(rc == MYSQL_NO_DATA);
-
- mysql_stmt_close(stmt);
+ bind_fetch(3);
}
/********************************************************
@@ -2911,26 +2964,17 @@ static void test_fetch_str()
*********************************************************/
static void test_fetch_long()
{
- MYSQL_STMT *stmt;
- int rc, i, round;
- ulong bit;
- long data[10];
- float f_data;
- double d_data;
- char s_data[10];
- ulong length[10];
- MYSQL_BIND bind[7];
- my_bool is_null[7];
+ int rc;
myheader("test_fetch_long");
- rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_long");
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_fetch");
myquery(rc);
rc = mysql_commit(mysql);
myquery(rc);
- rc = mysql_query(mysql,"CREATE TABLE test_bind_long(c1 int unsigned,\
+ rc = mysql_query(mysql,"CREATE TABLE test_bind_fetch(c1 int unsigned,\
c2 int unsigned,\
c3 int,\
c4 int,\
@@ -2942,108 +2986,7 @@ static void test_fetch_long()
rc = mysql_commit(mysql);
myquery(rc);
- stmt = mysql_prepare(mysql,"INSERT INTO test_bind_long VALUES(?,?,?,?,?,?,?)",100);
- myquery(rc);
-
- verify_param_count(stmt, 7);
-
- round= 0;
- for (i=0; i < 7; i++)
- {
- bind[i].buffer_type= MYSQL_TYPE_LONG;
- bind[i].buffer= (char *) &data[i];
- bind[i].is_null=0;
- data[i]= round+i+1;
- round= (round +10)*10;
- }
- rc = mysql_bind_param(stmt, bind);
- mystmt(stmt,rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_commit(mysql);
- myquery(rc);
-
- mysql_stmt_close(stmt);
-
- myassert(1 == my_stmt_result("SELECT * FROM test_bind_long",50));
-
- stmt = mysql_prepare(mysql,"SELECT * FROM test_bind_long",50);
- myquery(rc);
-
- bind[0].buffer_type= MYSQL_TYPE_TINY;
- bind[1].buffer_type= MYSQL_TYPE_SHORT;
- bind[2].buffer_type= MYSQL_TYPE_LONG;
- bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
-
- bind[4].buffer_type= MYSQL_TYPE_FLOAT;
- bind[4].buffer= (char *)&f_data;
-
- bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
- bind[5].buffer= (char *)&d_data;
-
- bind[6].buffer_type= MYSQL_TYPE_STRING;
- bind[6].buffer= (char *)&s_data;
- bind[6].buffer_length= sizeof(s_data);
-
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer= (char*) &data[i];
- bind[i].length= &length[i];
- bind[i].is_null= &is_null[i];
- }
-
- rc = mysql_bind_result(stmt, bind);
- mystmt(stmt, rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_fetch(stmt);
- mystmt(stmt,rc);
-
- fprintf(stdout, "\n tiny : %ld(%lu)", data[0], length[0]);
- fprintf(stdout, "\n short : %ld(%lu)", data[1], length[1]);
- fprintf(stdout, "\n int : %ld(%lu)", data[2], length[2]);
- fprintf(stdout, "\n longlong : %ld(%lu)", data[3], length[3]);
- fprintf(stdout, "\n float : %f(%lu)", f_data, length[4]);
- fprintf(stdout, "\n double : %g(%lu)", d_data, length[5]);
- fprintf(stdout, "\n char : %s(%lu)", s_data, length[6]);
-
- round= 0;
- bit= 1;
-
- for (i=0; i < 4; i++)
- {
- myassert(data[i] == round+i+1);
- myassert(length[i] == bit);
- round= (round+10)*10;
- bit<<= 1;
- }
-
- /* FLOAT */
- myassert((int)f_data == round+1+i);
- myassert(length[4] == 4);
-
- /* DOUBLE */
- round= (round+10)*10;
- myassert((int)d_data == round+2+i);
- myassert(length[5] == 8);
-
- /* CHAR */
- round= (round+10)*10;
- {
- char buff[20];
- long len= my_sprintf(buff, (buff, "%d", round+3+i));
- myassert(strcmp(s_data,buff)==0);
- myassert(length[6] == (ulong) len);
- }
-
- rc = mysql_fetch(stmt);
- myassert(rc == MYSQL_NO_DATA);
-
- mysql_stmt_close(stmt);
+ bind_fetch(4);
}
@@ -3052,30 +2995,17 @@ static void test_fetch_long()
*********************************************************/
static void test_fetch_short()
{
- MYSQL_STMT *stmt;
- int rc, i, round;
- ulong bit;
- long data[10];
- char tiny_data;
- short int short_data;
- long long_data;
- longlong longlong_data;
- float f_data;
- double d_data;
- char s_data[10];
- MYSQL_BIND bind[7];
- ulong length[10];
- my_bool is_null[7];
+ int rc;
myheader("test_fetch_short");
- rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_long");
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_fetch");
myquery(rc);
rc = mysql_commit(mysql);
myquery(rc);
- rc = mysql_query(mysql,"CREATE TABLE test_bind_long(c1 smallint unsigned,\
+ rc = mysql_query(mysql,"CREATE TABLE test_bind_fetch(c1 smallint unsigned,\
c2 smallint,\
c3 smallint unsigned,\
c4 smallint,\
@@ -3086,112 +3016,8 @@ static void test_fetch_short()
rc = mysql_commit(mysql);
myquery(rc);
-
- stmt = mysql_prepare(mysql,"INSERT INTO test_bind_long VALUES(?,?,?,?,?,?,?)",100);
- myquery(rc);
-
- verify_param_count(stmt, 7);
-
- round= 0;
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer_type= MYSQL_TYPE_LONG;
- bind[i].buffer= (char *) &data[i];
- bind[i].length= &length[i];
- bind[i].is_null= 0;
- data[i]= round+i+1;
- round= (round +10)*2;
- }
- rc = mysql_bind_param(stmt, bind);
- mystmt(stmt,rc);
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_commit(mysql);
- myquery(rc);
-
- mysql_stmt_close(stmt);
-
- myassert(1 == my_stmt_result("SELECT * FROM test_bind_long",50));
-
- stmt = mysql_prepare(mysql,"SELECT * FROM test_bind_long",50);
- myquery(rc);
-
- for (i=0; i < 7; i++)
- {
- bind[i].length= &length[i];
- bind[i].is_null= &is_null[i];
- }
- bind[0].buffer_type= MYSQL_TYPE_TINY;
- bind[0].buffer= (char*) &tiny_data;
- bind[1].buffer_type= MYSQL_TYPE_SHORT;
- bind[1].buffer= (char*) &short_data;
- bind[2].buffer_type= MYSQL_TYPE_LONG;
- bind[1].buffer= (char*) &long_data;
- bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
- bind[1].buffer= (char*) &longlong_data;
-
- bind[4].buffer_type= MYSQL_TYPE_FLOAT;
- bind[4].buffer= (char *)&f_data;
-
- bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
- bind[5].buffer= (char *)&d_data;
-
- bind[6].buffer_type= MYSQL_TYPE_STRING;
- bind[6].buffer= (char *)&s_data;
- bind[6].buffer_length= sizeof(s_data);
-
- rc = mysql_bind_result(stmt, bind);
- mystmt(stmt, rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_fetch(stmt);
- mystmt(stmt,rc);
-
- fprintf(stdout, "\n tiny : %d(%lu)", tiny_data, length[0]);
- fprintf(stdout, "\n short : %d(%lu)", short_data, length[1]);
- fprintf(stdout, "\n int : %ld(%lu)", long_data, length[2]);
- fprintf(stdout, "\n longlong : %ld(%lu)", (long) longlong_data, length[3]);
- fprintf(stdout, "\n float : %f(%lu)", f_data, length[4]);
- fprintf(stdout, "\n double : %g(%lu)", d_data, length[5]);
- fprintf(stdout, "\n char : %s(%lu)", s_data, length[6]);
-
- round= 0;
- bit= 1;
-
- for (i=0; i < 4; i++)
- {
- myassert(data[i] == round+i+1);
- myassert(length[i] == bit);
- round= (round+10)*2;
- bit<<= 1;
- }
-
- /* FLOAT */
- myassert((int)f_data == round+1+i);
- myassert(length[4] == 4);
-
- /* DOUBLE */
- round= (round+10)*2;
- myassert((int)d_data == round+2+i);
- myassert(length[5] == 8);
-
- /* CHAR */
- round= (round+10)*2;
- {
- char buff[20];
- long len= my_sprintf(buff, (buff, "%d", round+3+i));
- myassert(strcmp(s_data,buff)==0);
- myassert(length[6] == (ulong) len);
- }
-
- rc = mysql_fetch(stmt);
- myassert(rc == MYSQL_NO_DATA);
-
- mysql_stmt_close(stmt);
+ bind_fetch(5);
}
@@ -3200,26 +3026,17 @@ static void test_fetch_short()
*********************************************************/
static void test_fetch_tiny()
{
- MYSQL_STMT *stmt;
- int rc, i;
- ulong bit;
- long data[10];
- ulong length[10];
- float f_data;
- double d_data;
- char s_data[10];
- MYSQL_BIND bind[7];
- my_bool is_null[7];
+ int rc;
myheader("test_fetch_tiny");
- rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_long");
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_fetch");
myquery(rc);
rc = mysql_commit(mysql);
myquery(rc);
- rc = mysql_query(mysql,"CREATE TABLE test_bind_long(c1 tinyint unsigned,\
+ rc = mysql_query(mysql,"CREATE TABLE test_bind_fetch(c1 tinyint unsigned,\
c2 tinyint,\
c3 tinyint unsigned,\
c4 tinyint,\
@@ -3230,109 +3047,9 @@ static void test_fetch_tiny()
rc = mysql_commit(mysql);
myquery(rc);
-
- stmt = mysql_prepare(mysql,"INSERT INTO test_bind_long VALUES(?,?,?,?,?,?,?)",100);
- myquery(rc);
-
- verify_param_count(stmt, 7);
-
- rc= 10;
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer_type= MYSQL_TYPE_LONG;
- bind[i].buffer= (char *) &data[i];
- bind[i].is_null= 0;
- data[i]= rc+i;
- rc+= 10;
- }
- rc = mysql_bind_param(stmt, bind);
- mystmt(stmt,rc);
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
+ bind_fetch(3);
- rc = mysql_commit(mysql);
- myquery(rc);
-
- mysql_stmt_close(stmt);
-
- myassert(1 == my_stmt_result("SELECT * FROM test_bind_long",50));
-
- stmt = mysql_prepare(mysql,"SELECT * FROM test_bind_long",50);
- myquery(rc);
-
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer= (char *) &data[i];
- bind[i].length= &length[i];
- bind[i].is_null= &is_null[i];
- }
-
- bind[0].buffer_type= MYSQL_TYPE_TINY;
- bind[1].buffer_type= MYSQL_TYPE_SHORT;
- bind[2].buffer_type= MYSQL_TYPE_LONG;
- bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
-
- bind[4].buffer_type= MYSQL_TYPE_FLOAT;
- bind[4].buffer= (char *)&f_data;
-
- bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
- bind[5].buffer= (char *)&d_data;
-
- bind[6].buffer_type= MYSQL_TYPE_STRING;
- bind[6].buffer= (char *)&s_data;
- bind[6].buffer_length= sizeof(s_data);
-
- rc = mysql_bind_result(stmt, bind);
- mystmt(stmt, rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_fetch(stmt);
- mystmt(stmt,rc);
-
- fprintf(stdout, "\n tiny : %ld(%lu)", data[0], length[0]);
- fprintf(stdout, "\n short : %ld(%lu)", data[1], length[1]);
- fprintf(stdout, "\n int : %ld(%lu)", data[2], length[2]);
- fprintf(stdout, "\n longlong : %ld(%lu)", data[3], length[3]);
- fprintf(stdout, "\n float : %f(%lu)", f_data, length[4]);
- fprintf(stdout, "\n double : %g(%lu)", d_data, length[5]);
- fprintf(stdout, "\n char : %s(%lu)", s_data, length[6]);
-
- bit= 1;
- rc= 10;
- for (i=0; i < 4; i++)
- {
- myassert(data[i] == rc+i);
- myassert(length[i] == bit);
- bit<<= 1;
- rc+= 10;
- }
-
- /* FLOAT */
- rc+= i;
- myassert((int)f_data == rc);
- myassert(length[4] == 4);
-
- /* DOUBLE */
- rc+= 11;
- myassert((int)d_data == rc);
- myassert(length[5] == 8);
-
- /* CHAR */
- rc+= 11;
- {
- char buff[20];
- long len= my_sprintf(buff, (buff, "%d", rc));
- myassert(strcmp(s_data,buff)==0);
- myassert(length[6] == (ulong) len);
- }
-
- rc = mysql_fetch(stmt);
- myassert(rc == MYSQL_NO_DATA);
-
- mysql_stmt_close(stmt);
}
@@ -3341,26 +3058,17 @@ static void test_fetch_tiny()
*********************************************************/
static void test_fetch_bigint()
{
- MYSQL_STMT *stmt;
- int rc, i, round;
- ulong bit;
- long data[10];
- float f_data;
- double d_data;
- char s_data[10];
- MYSQL_BIND bind[7];
- ulong length[7];
- my_bool is_null[7];
+ int rc;
myheader("test_fetch_bigint");
- rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_long");
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_fetch");
myquery(rc);
rc = mysql_commit(mysql);
myquery(rc);
- rc = mysql_query(mysql,"CREATE TABLE test_bind_long(c1 bigint,\
+ rc = mysql_query(mysql,"CREATE TABLE test_bind_fetch(c1 bigint,\
c2 bigint,\
c3 bigint unsigned,\
c4 bigint unsigned,\
@@ -3371,110 +3079,9 @@ static void test_fetch_bigint()
rc = mysql_commit(mysql);
myquery(rc);
-
- stmt = mysql_prepare(mysql,"INSERT INTO test_bind_long VALUES(?,?,?,?,?,?,?)",100);
- myquery(rc);
-
- verify_param_count(stmt, 7);
-
- round= 0;
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer_type= MYSQL_TYPE_LONG;
- bind[i].buffer= (char *)&data[i];
- bind[i].is_null = &is_null[i];
- bind[i].length= &length[i];
- length[i]= 0;
- data[i]= round+i+1;
- round= (round +10)*10;
- }
- rc = mysql_bind_param(stmt, bind);
- mystmt(stmt,rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_commit(mysql);
- myquery(rc);
-
- mysql_stmt_close(stmt);
-
- myassert(1 == my_stmt_result("SELECT * FROM test_bind_long",50));
-
- stmt = mysql_prepare(mysql,"SELECT * FROM test_bind_long",50);
- myquery(rc);
-
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer= (char *)&data[i];
- bind[i].length= &length[i];
- bind[i].is_null= 0;
- }
- bind[0].buffer_type= MYSQL_TYPE_TINY;
- bind[1].buffer_type= MYSQL_TYPE_SHORT;
- bind[2].buffer_type= MYSQL_TYPE_LONG;
- bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
-
- bind[4].buffer_type= MYSQL_TYPE_FLOAT;
- bind[4].buffer= (char *)&f_data;
-
- bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
- bind[5].buffer= (char *)&d_data;
-
- bind[6].buffer_type= MYSQL_TYPE_STRING;
- bind[6].buffer= (char *)&s_data;
- bind[6].buffer_length= sizeof(s_data);
-
- rc = mysql_bind_result(stmt, bind);
- mystmt(stmt, rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_fetch(stmt);
- mystmt(stmt,rc);
-
- fprintf(stdout, "\n tiny : %ld(%lu)", data[0], length[0]);
- fprintf(stdout, "\n short : %ld(%lu)", data[1], length[1]);
- fprintf(stdout, "\n int : %ld(%lu)", data[2], length[2]);
- fprintf(stdout, "\n longlong : %ld(%lu)", data[3], length[3]);
- fprintf(stdout, "\n float : %f(%lu)", f_data, length[4]);
- fprintf(stdout, "\n double : %g(%lu)", d_data, length[5]);
- fprintf(stdout, "\n char : %s(%lu)", s_data, length[6]);
-
- round= 0;
- bit= 1;
- for (i=0; i < 4; i++)
- {
- myassert(data[i] == round+i+1);
- myassert(length[i] == bit);
- round= (round+10)*10;
- bit<<= 1;
- }
+ bind_fetch(2);
- /* FLOAT */
- myassert((int)f_data == round+1+i);
- myassert(length[4] == 4);
-
- /* DOUBLE */
- round= (round+10)*10;
- myassert((int)d_data == round+2+i);
- myassert(length[5] == 8);
-
- /* CHAR */
- round= (round+10)*10;
- {
- char buff[20];
- long len= my_sprintf(buff, (buff, "%d", round+3+i));
- myassert(strcmp(s_data,buff)==0);
- myassert(length[6] == (ulong) len);
- }
-
- rc = mysql_fetch(stmt);
- myassert(rc == MYSQL_NO_DATA);
-
- mysql_stmt_close(stmt);
}
@@ -3483,26 +3090,17 @@ static void test_fetch_bigint()
*********************************************************/
static void test_fetch_float()
{
- MYSQL_STMT *stmt;
- int rc, i, round;
- ulong bit;
- long data[10];
- float f_data;
- double d_data;
- char s_data[10];
- MYSQL_BIND bind[7];
- ulong length[10];
- my_bool is_null[7];
+ int rc;
myheader("test_fetch_float");
- rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_long");
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_fetch");
myquery(rc);
rc = mysql_commit(mysql);
myquery(rc);
- rc = mysql_query(mysql,"CREATE TABLE test_bind_long(c1 float(3),\
+ rc = mysql_query(mysql,"CREATE TABLE test_bind_fetch(c1 float(3),\
c2 float,\
c3 float unsigned,\
c4 float,\
@@ -3513,109 +3111,9 @@ static void test_fetch_float()
rc = mysql_commit(mysql);
myquery(rc);
-
- stmt = mysql_prepare(mysql,"INSERT INTO test_bind_long VALUES(?,?,?,?,?,?,?)",100);
- myquery(rc);
-
- verify_param_count(stmt, 7);
-
- round= 0;
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer_type= MYSQL_TYPE_LONG;
- bind[i].buffer= (char *)&data[i];
- bind[i].is_null= 0;
- data[i]= round+i+1;
- round= (round +10)*2;
- }
- rc = mysql_bind_param(stmt, bind);
- mystmt(stmt,rc);
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_commit(mysql);
- myquery(rc);
-
- mysql_stmt_close(stmt);
-
- myassert(1 == my_stmt_result("SELECT * FROM test_bind_long",50));
+ bind_fetch(2);
- stmt = mysql_prepare(mysql,"SELECT * FROM test_bind_long",50);
- myquery(rc);
-
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer= (char *)&data[i];
- bind[i].length= &length[i];
- bind[i].is_null= &is_null[i];
- length[i]=0;
- }
- bind[0].buffer_type= MYSQL_TYPE_TINY;
- bind[1].buffer_type= MYSQL_TYPE_SHORT;
- bind[2].buffer_type= MYSQL_TYPE_LONG;
- bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
-
- bind[4].buffer_type= MYSQL_TYPE_FLOAT;
- bind[4].buffer= (char *)&f_data;
-
- bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
- bind[5].buffer= (char *)&d_data;
-
- bind[6].buffer_type= MYSQL_TYPE_STRING;
- bind[6].buffer= (char *) &s_data;
- bind[6].buffer_length= sizeof(s_data);
-
- rc = mysql_bind_result(stmt, bind);
- mystmt(stmt, rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_fetch(stmt);
- mystmt(stmt,rc);
-
- fprintf(stdout, "\n tiny : %ld(%lu)", data[0], length[0]);
- fprintf(stdout, "\n short : %ld(%lu)", data[1], length[1]);
- fprintf(stdout, "\n int : %ld(%lu)", data[2], length[2]);
- fprintf(stdout, "\n longlong : %ld(%lu)", data[3], length[3]);
- fprintf(stdout, "\n float : %f(%lu)", f_data, length[4]);
- fprintf(stdout, "\n double : %g(%lu)", d_data, length[5]);
- fprintf(stdout, "\n char : %s(%lu)", s_data, length[6]);
-
- round= 0;
- bit= 1;
-
- for (i=0; i < 4; i++)
- {
- myassert(data[i] == round+i+1);
- myassert(length[i] == bit);
- round= (round+10)*2;
- bit<<= 1;
- }
-
- /* FLOAT */
- myassert((int)f_data == round+1+i);
- myassert(length[4] == 4);
-
- /* DOUBLE */
- round= (round+10)*2;
- myassert((int)d_data == round+2+i);
- myassert(length[5] == 8);
-
- /* CHAR */
- round= (round+10)*2;
- {
- char buff[20];
- long len= my_sprintf(buff, (buff, "%d", round+3+i));
- myassert(strcmp(s_data,buff)==0);
- myassert(length[6] == (ulong) len);
- }
-
- rc = mysql_fetch(stmt);
- myassert(rc == MYSQL_NO_DATA);
-
- mysql_stmt_close(stmt);
}
/********************************************************
@@ -3623,26 +3121,17 @@ static void test_fetch_float()
*********************************************************/
static void test_fetch_double()
{
- MYSQL_STMT *stmt;
- int rc, i, round;
- ulong bit;
- long data[10];
- float f_data;
- double d_data;
- char s_data[10];
- MYSQL_BIND bind[7];
- my_bool is_null[7];
- ulong length[7];
+ int rc;
myheader("test_fetch_double");
- rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_long");
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_bind_fetch");
myquery(rc);
rc = mysql_commit(mysql);
myquery(rc);
- rc = mysql_query(mysql,"CREATE TABLE test_bind_long(c1 double(5,2),\
+ rc = mysql_query(mysql,"CREATE TABLE test_bind_fetch(c1 double(5,2),\
c2 double unsigned,\
c3 double unsigned,\
c4 double unsigned,\
@@ -3653,107 +3142,9 @@ static void test_fetch_double()
rc = mysql_commit(mysql);
myquery(rc);
-
- stmt = mysql_prepare(mysql,"INSERT INTO test_bind_long VALUES(?,?,?,?,?,?,?)",100);
- myquery(rc);
-
- verify_param_count(stmt, 7);
-
- round= 0;
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer_type= MYSQL_TYPE_LONG;
- bind[i].buffer= (char *)&data[i];
- bind[i].is_null= 0;
- data[i]= round+i+1;
- round= (round +10)*10;
- }
- rc = mysql_bind_param(stmt, bind);
- mystmt(stmt,rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_commit(mysql);
- myquery(rc);
-
- mysql_stmt_close(stmt);
-
- myassert(1 == my_stmt_result("SELECT * FROM test_bind_long",50));
-
- stmt = mysql_prepare(mysql,"SELECT * FROM test_bind_long",50);
- myquery(rc);
-
- for (i= 0; i < (int) array_elements(bind); i++)
- {
- bind[i].buffer= (char *)&data[i];
- bind[i].length= &length[i];
- bind[i].is_null= &is_null[i];
- }
- bind[0].buffer_type= MYSQL_TYPE_TINY;
- bind[1].buffer_type= MYSQL_TYPE_SHORT;
- bind[2].buffer_type= MYSQL_TYPE_LONG;
- bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
- bind[4].buffer_type= MYSQL_TYPE_STRING;
- bind[4].buffer= (char *)&s_data;
- bind[4].buffer_length= sizeof(s_data);
-
- bind[5].buffer_type= MYSQL_TYPE_FLOAT;
- bind[5].buffer= (char *)&f_data;
+ bind_fetch(3);
- bind[6].buffer_type= MYSQL_TYPE_DOUBLE;
- bind[6].buffer= (char *)&d_data;
-
- rc = mysql_bind_result(stmt, bind);
- mystmt(stmt, rc);
-
- rc = mysql_execute(stmt);
- mystmt(stmt, rc);
-
- rc = mysql_fetch(stmt);
- mystmt(stmt,rc);
-
- fprintf(stdout, "\n tiny : %ld(%lu)", data[0], length[0]);
- fprintf(stdout, "\n short : %ld(%lu)", data[1], length[1]);
- fprintf(stdout, "\n int : %ld(%lu)", data[2], length[2]);
- fprintf(stdout, "\n longlong : %ld(%lu)", data[3], length[3]);
- fprintf(stdout, "\n float : %f(%lu)", f_data, length[5]);
- fprintf(stdout, "\n double : %g(%lu)", d_data, length[6]);
- fprintf(stdout, "\n char : %s(%lu)", s_data, length[4]);
-
- round= 0;
- bit= 1;
-
- for (i=0; i < 4; i++)
- {
- myassert(data[i] == round+i+1);
- myassert(length[i] == bit);
- round= (round+10)*10;
- bit<<= 1;
- }
- /* CHAR */
- {
- char buff[20];
- long len= my_sprintf(buff, (buff, "%d", round+1+i));
- myassert(strcmp(s_data,buff)==0);
- myassert(length[4] == (ulong) len);
- }
-
- /* FLOAT */
- round= (round+10)*10;
- myassert((int)f_data == round+2+i);
- myassert(length[5] == 4);
-
- /* DOUBLE */
- round= (round+10)*10;
- myassert((int)d_data == round+3+i);
- myassert(length[6] == 8);
-
- rc = mysql_fetch(stmt);
- myassert(rc == MYSQL_NO_DATA);
-
- mysql_stmt_close(stmt);
}
/********************************************************
@@ -3766,7 +3157,6 @@ static void test_prepare_ext()
int rc;
char *sql;
int nData=1;
- MYSQL_RES *result;
char tData=1;
short sData=10;
longlong bData=20;
@@ -3820,7 +3210,7 @@ static void test_prepare_ext()
/* insert by prepare - all integers */
strmov(query,(char *)"INSERT INTO test_prepare_ext(c1,c2,c3,c4,c5,c6) VALUES(?,?,?,?,?,?)");
stmt = mysql_prepare(mysql,query, strlen(query));
- myquery(rc);
+ mystmt_init(stmt);
verify_param_count(stmt,6);
@@ -3868,16 +3258,16 @@ static void test_prepare_ext()
rc = mysql_commit(mysql);
myquery(rc);
- /* test the results now, only one row should exists */
- rc = mysql_query(mysql,"SELECT c1,c2,c3,c4,c5,c6 FROM test_prepare_ext");
- myquery(rc);
+ stmt = mysql_prepare(mysql,"SELECT c1,c2,c3,c4,c5,c6 FROM test_prepare_ext",100);
+ mystmt_init(stmt);
/* get the result */
- result = mysql_store_result(mysql);
- mytest(result);
+ rc = mysql_execute(stmt);
+ mystmt(stmt, rc);
- myassert(nData == my_process_result_set(result));
- mysql_free_result(result);
+ myassert(nData == (int)my_process_stmt_result(stmt));
+
+ mysql_stmt_close(stmt);
}
@@ -4165,63 +3555,68 @@ static void test_stmt_close()
myheader("test_stmt_close");
+ fprintf(stdout, "\n Establishing a test connection ...");
if (!(lmysql = mysql_init(NULL)))
{
myerror("mysql_init() failed");
exit(0);
}
if (!(mysql_real_connect(lmysql,opt_host,opt_user,
- opt_password, opt_db ? opt_db:"inter_client_test_db", opt_port,
+ opt_password, current_db, opt_port,
opt_unix_socket, 0)))
{
myerror("connection failed");
exit(0);
}
- if (opt_db)
- strmov(current_db,opt_db);
+ fprintf(stdout," OK");
+
/* set AUTOCOMMIT to ON*/
mysql_autocommit(lmysql, TRUE);
- mysql_query(lmysql,"DROP TABLE IF EXISTS test_stmt_close");
- mysql_query(lmysql,"CREATE TABLE test_stmt_close(id int)");
+
+ rc = mysql_query(lmysql,"DROP TABLE IF EXISTS test_stmt_close");
+ myquery(rc);
+
+ rc = mysql_query(lmysql,"CREATE TABLE test_stmt_close(id int)");
+ myquery(rc);
strmov(query,"ALTER TABLE test_stmt_close ADD name varchar(20)");
stmt1= PREPARE(lmysql, query);
mystmt_init(stmt1);
- count= mysql_param_count(stmt1);
- fprintf(stdout,"\n total params in alter: %d", count);
- myassert(count == 0);
+
+ verify_param_count(stmt1, 0);
+
strmov(query,"INSERT INTO test_stmt_close(id) VALUES(?)");
stmt_x= PREPARE(mysql, query);
mystmt_init(stmt_x);
- count= mysql_param_count(stmt_x);
- fprintf(stdout,"\n total params in insert: %d", count);
- myassert(count == 1);
+
+ verify_param_count(stmt_x, 1);
+
strmov(query,"UPDATE test_stmt_close SET id=? WHERE id=?");
stmt3= PREPARE(lmysql, query);
mystmt_init(stmt3);
- count= mysql_param_count(stmt3);
- fprintf(stdout,"\n total params in update: %d", count);
- myassert(count == 2);
+
+ verify_param_count(stmt3, 2);
+
strmov(query,"SELECT * FROM test_stmt_close WHERE id=?");
stmt2= PREPARE(lmysql, query);
mystmt_init(stmt2);
- count= mysql_param_count(stmt2);
- fprintf(stdout,"\n total params in select: %d", count);
- myassert(count == 1);
+
+ verify_param_count(stmt2, 1);
rc= mysql_stmt_close(stmt1);
fprintf(stdout,"\n mysql_close_stmt(1) returned: %d", rc);
myassert(rc == 0);
- mysql_close(lmysql); /* it should free all stmts */
-#if NOT_VALID
+
+ mysql_close(lmysql); /* it should free all open stmts(stmt3, stmt2) */
+
rc= mysql_stmt_close(stmt3);
fprintf(stdout,"\n mysql_close_stmt(3) returned: %d", rc);
myassert( rc == 1);
+
rc= mysql_stmt_close(stmt2);
fprintf(stdout,"\n mysql_close_stmt(2) returned: %d", rc);
myassert( rc == 1);
-#endif
count= 100;
bind[0].buffer=(char *)&count;
@@ -4233,7 +3628,7 @@ static void test_stmt_close()
rc = mysql_execute(stmt_x);
mystmt(stmt_x, rc);
- rc= (ulong)mysql_affected_rows(stmt_x->mysql);
+ rc= (ulong)mysql_stmt_affected_rows(stmt_x);
fprintf(stdout,"\n total rows affected: %d", rc);
myassert (rc == 1);
@@ -4241,7 +3636,6 @@ static void test_stmt_close()
fprintf(stdout,"\n mysql_close_stmt(x) returned: %d", rc);
myassert( rc == 0);
- /*verify_col_data("test_stmt_close", "id", "100");*/
rc = mysql_query(mysql,"SELECT id FROM test_stmt_close");
myquery(rc);
@@ -4992,7 +4386,7 @@ static void test_store_result()
bind[0].buffer_type=FIELD_TYPE_LONG;
bind[0].buffer= (char*) &nData; /* integer data */
bind[0].length= &length;
- bind[1].is_null= &is_null[1];
+ bind[0].is_null= &is_null[0];
length= 0;
bind[1].buffer_type=FIELD_TYPE_STRING;
@@ -5035,11 +4429,11 @@ static void test_store_result()
rc = mysql_fetch(stmt);
mystmt(stmt,rc);
- if (is_null[1])
+ if (is_null[0])
fprintf(stdout,"\n row 3: NULL,%s(%lu)", szData, length1);
else
fprintf(stdout,"\n row 3: %ld,%s(%lu)", nData, szData, length1);
- myassert(is_null[1]);
+ myassert(is_null[0]);
myassert(strcmp(szData,"monty")==0);
myassert(length1 == 5);
@@ -5072,11 +4466,11 @@ static void test_store_result()
rc = mysql_fetch(stmt);
mystmt(stmt,rc);
- if (is_null[1])
+ if (is_null[0])
fprintf(stdout,"\n row 3: NULL,%s(%lu)", szData, length1);
else
fprintf(stdout,"\n row 3: %ld,%s(%lu)", nData, szData, length1);
- myassert(is_null[1]);
+ myassert(is_null[0]);
myassert(strcmp(szData,"monty")==0);
myassert(length1 == 5);
@@ -5183,8 +4577,6 @@ static void test_store_result2()
rc = mysql_commit(mysql);
myquery(rc);
- /* fetch */
-
bind[0].buffer_type=FIELD_TYPE_LONG;
bind[0].buffer= (char *) &nData; /* integer data */
bind[0].length= &length;
@@ -5317,6 +4709,424 @@ static void test_subselect()
#endif
}
+/*
+ Generalized conversion routine to handle DATE, TIME and DATETIME
+ conversion using MYSQL_TIME structure
+*/
+static void test_bind_date_conv(uint row_count)
+{
+ MYSQL_STMT *stmt;
+ uint rc, i, count= row_count;
+ ulong length[4];
+ MYSQL_BIND bind[4];
+ my_bool is_null[4]={0};
+ MYSQL_TIME tm[4];
+ ulong second_part;
+ uint year, month, day, hour, minute, sec;
+
+ stmt = mysql_prepare(mysql,"INSERT INTO test_date VALUES(?,?,?,?)", 100);
+ mystmt_init(stmt);
+
+ verify_param_count(stmt, 4);
+
+ bind[0].buffer_type= MYSQL_TYPE_TIMESTAMP;
+ bind[1].buffer_type= MYSQL_TYPE_TIME;
+ bind[2].buffer_type= MYSQL_TYPE_DATETIME;
+ bind[3].buffer_type= MYSQL_TYPE_DATE;
+
+ second_part= 0;
+
+ year=2000;
+ month=01;
+ day=10;
+
+ hour=11;
+ minute=16;
+ sec= 20;
+
+ for (i= 0; i < (int) array_elements(bind); i++)
+ {
+ bind[i].buffer= (char *) &tm[i];
+ bind[i].is_null= &is_null[i];
+ bind[i].length= &length[i];
+ bind[i].buffer_length= 30;
+ length[i]=20;
+ }
+
+ rc = mysql_bind_param(stmt, bind);
+ mystmt(stmt,rc);
+
+ for (count= 0; count < row_count; count++)
+ {
+ for (i= 0; i < (int) array_elements(bind); i++)
+ {
+ tm[i].neg= 0;
+ tm[i].second_part= second_part+count;
+ tm[i].year= year+count;
+ tm[i].month= month+count;
+ tm[i].day= day+count;
+ tm[i].hour= hour+count;
+ tm[i].minute= minute+count;
+ tm[i].second= sec+count;
+ }
+ rc = mysql_execute(stmt);
+ mystmt(stmt, rc);
+ }
+
+ rc = mysql_commit(mysql);
+ myquery(rc);
+
+ mysql_stmt_close(stmt);
+
+ myassert(row_count == my_stmt_result("SELECT * FROM test_date",50));
+
+ stmt = mysql_prepare(mysql,"SELECT * FROM test_date",50);
+ myquery(rc);
+
+ rc = mysql_bind_result(stmt, bind);
+ mystmt(stmt, rc);
+
+ rc = mysql_execute(stmt);
+ mystmt(stmt, rc);
+
+ rc = mysql_stmt_store_result(stmt);
+ mystmt(stmt, rc);
+
+ for (count=0; count < row_count; count++)
+ {
+ rc = mysql_fetch(stmt);
+ mystmt(stmt,rc);
+
+ fprintf(stdout, "\n");
+ for (i= 0; i < array_elements(bind); i++)
+ {
+ fprintf(stdout, "\n");
+ fprintf(stdout,"time[%d]: %02d-%02d-%02d %02d:%02d:%02d.%02lu",
+ i, tm[i].year, tm[i].month, tm[i].day,
+ tm[i].hour, tm[i].minute, tm[i].second,
+ tm[i].second_part);
+
+ myassert(tm[i].year == 0 || tm[i].year == year+count);
+ myassert(tm[i].month == 0 || tm[i].month == month+count);
+ myassert(tm[i].day == 0 || tm[i].day == day+count);
+
+ myassert(tm[i].hour == 0 || tm[i].hour == hour+count);
+ /*
+ minute causes problems from date<->time, don't assert, instead
+ validate separatly in another routine
+ */
+ /*myassert(tm[i].minute == 0 || tm[i].minute == minute+count);
+ myassert(tm[i].second == 0 || tm[i].second == sec+count);*/
+
+ myassert(tm[i].second_part == 0 || tm[i].second_part == second_part+count);
+ }
+ }
+ rc = mysql_fetch(stmt);
+ myassert(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+/*
+ Test DATE, TIME, DATETIME and TS with MYSQL_TIME conversion
+*/
+
+static void test_date()
+{
+ int rc;
+
+ myheader("test_date");
+
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql,"CREATE TABLE test_date(c1 TIMESTAMP(14), \
+ c2 TIME,\
+ c3 DATETIME,\
+ c4 DATE)");
+
+ myquery(rc);
+
+ rc = mysql_commit(mysql);
+ myquery(rc);
+
+ test_bind_date_conv(5);
+}
+
+/*
+ Test all time types to DATE and DATE to all types
+*/
+
+static void test_date_date()
+{
+ int rc;
+
+ myheader("test_date_date");
+
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql,"CREATE TABLE test_date(c1 DATE, \
+ c2 DATE,\
+ c3 DATE,\
+ c4 DATE)");
+
+ myquery(rc);
+
+ rc = mysql_commit(mysql);
+ myquery(rc);
+
+ test_bind_date_conv(3);
+}
+
+/*
+ Test all time types to TIME and TIME to all types
+*/
+
+static void test_date_time()
+{
+ int rc;
+
+ myheader("test_date_time");
+
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql,"CREATE TABLE test_date(c1 TIME, \
+ c2 TIME,\
+ c3 TIME,\
+ c4 TIME)");
+
+ myquery(rc);
+
+ rc = mysql_commit(mysql);
+ myquery(rc);
+
+ test_bind_date_conv(3);
+}
+
+/*
+ Test all time types to TIMESTAMP and TIMESTAMP to all types
+*/
+
+static void test_date_ts()
+{
+ int rc;
+
+ myheader("test_date_ts");
+
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql,"CREATE TABLE test_date(c1 TIMESTAMP(10), \
+ c2 TIMESTAMP(14),\
+ c3 TIMESTAMP,\
+ c4 TIMESTAMP(6))");
+
+ myquery(rc);
+
+ rc = mysql_commit(mysql);
+ myquery(rc);
+
+ test_bind_date_conv(2);
+}
+
+/*
+ Test all time types to DATETIME and DATETIME to all types
+*/
+
+static void test_date_dt()
+{
+ int rc;
+
+ myheader("test_date_dt");
+
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql,"CREATE TABLE test_date(c1 datetime, \
+ c2 datetime,\
+ c3 datetime,\
+ c4 date)");
+
+ myquery(rc);
+
+ rc = mysql_commit(mysql);
+ myquery(rc);
+
+ test_bind_date_conv(2);
+}
+
+/*
+ Misc tests to keep pure coverage happy
+*/
+static void test_pure_coverage()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[1];
+ int rc;
+ ulong length;
+
+ myheader("test_pure_coverage");
+
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_pure");
+ myquery(rc);
+
+ rc = mysql_query(mysql,"CREATE TABLE test_pure(c1 int, c2 varchar(20))");
+ myquery(rc);
+
+ stmt = mysql_prepare(mysql,"insert into test_pure(c67788) values(10)",100);
+ mystmt_init_r(stmt);
+
+#ifndef DBUG_OFF
+ stmt = mysql_prepare(mysql,(const char *)0,0);
+ mystmt_init_r(stmt);
+
+ stmt = mysql_prepare(mysql,"insert into test_pure(c2) values(10)",100);
+ mystmt_init(stmt);
+
+ verify_param_count(stmt, 0);
+
+ rc = mysql_bind_param(stmt, bind);
+ mystmt_r(stmt, rc);
+
+ mysql_stmt_close(stmt);
+#endif
+
+ stmt = mysql_prepare(mysql,"insert into test_pure(c2) values(?)",100);
+ mystmt_init(stmt);
+
+#ifndef DBUG_OFF
+ rc = mysql_execute(stmt);
+ mystmt_r(stmt, rc);/* No parameters supplied */
+#endif
+
+ bind[0].length= &length;
+ bind[0].is_null= 0;
+ bind[0].buffer_length= 0;
+
+ bind[0].buffer_type= MYSQL_TYPE_GEOMETRY;
+ rc = mysql_bind_param(stmt, bind);
+ mystmt_r(stmt, rc); /* unsupported buffer type */
+
+ bind[0].buffer_type= MYSQL_TYPE_STRING;
+ rc = mysql_bind_param(stmt, bind);
+ mystmt(stmt, rc);
+
+ rc = mysql_send_long_data(stmt, 20, (char *)"venu", 4);
+ mystmt_r(stmt, rc); /* wrong param number */
+
+ rc = mysql_stmt_store_result(stmt);
+ mystmt(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ stmt = mysql_prepare(mysql,"select * from test_pure",100);
+ mystmt(stmt, rc);
+
+ rc = mysql_execute(stmt);
+ mystmt(stmt, rc);
+
+#ifndef DBUG_OFF
+ rc = mysql_bind_result(stmt, (MYSQL_BIND *)0);
+ mystmt_r(stmt, rc);
+#endif
+
+ rc = mysql_stmt_store_result(stmt);
+ mystmt(stmt, rc);
+
+ rc = mysql_stmt_store_result(stmt);
+ mystmt_r(stmt, rc); /* commands out of sync */
+
+ mysql_stmt_close(stmt);
+
+ mysql_query(mysql,"DROP TABLE test_pure");
+ mysql_commit(mysql);
+}
+
+/*
+ test for string buffer fetch
+*/
+
+static void test_buffers()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[1];
+ int rc;
+ ulong length;
+ my_bool is_null;
+ char buffer[20];
+
+ myheader("test_buffers");
+
+ rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_buffer");
+ myquery(rc);
+
+ rc = mysql_query(mysql,"CREATE TABLE test_buffer(str varchar(20))");
+ myquery(rc);
+
+ rc = mysql_query(mysql,"insert into test_buffer values('MySQL')\
+ ,('Database'),('Open-Source'),('Popular')");
+ myquery(rc);
+
+ stmt = mysql_prepare(mysql,"select str from test_buffer",100);
+ mystmt_init(stmt);
+
+ rc = mysql_execute(stmt);
+ mystmt(stmt, rc);
+
+ bind[0].length= &length;
+ bind[0].is_null= &is_null;
+ bind[0].buffer_length= 1;
+ bind[0].buffer_type= MYSQL_TYPE_STRING;
+ bind[0].buffer= (char *)buffer;
+
+ rc = mysql_bind_result(stmt, bind);
+ mystmt(stmt, rc);
+
+ rc = mysql_stmt_store_result(stmt);
+ mystmt(stmt, rc);
+
+ buffer[1]='X';
+ rc = mysql_fetch(stmt);
+ mystmt(stmt, rc);
+ fprintf(stdout, "\n data: %s (%lu)", buffer, length);
+ myassert(buffer[0] == 'M');
+ myassert(buffer[1] == 'X');
+ myassert(length == 5);
+
+ bind[0].buffer_length=8;
+ rc = mysql_bind_result(stmt, bind);/* re-bind */
+ mystmt(stmt, rc);
+
+ rc = mysql_fetch(stmt);
+ mystmt(stmt, rc);
+ fprintf(stdout, "\n data: %s (%lu)", buffer, length);
+ myassert(strncmp(buffer,"Database",8) == 0);
+ myassert(length == 8);
+
+ bind[0].buffer_length=12;
+ rc = mysql_bind_result(stmt, bind);/* re-bind */
+ mystmt(stmt, rc);
+
+ rc = mysql_fetch(stmt);
+ mystmt(stmt, rc);
+ fprintf(stdout, "\n data: %s (%lu)", buffer, length);
+ myassert(strcmp(buffer,"Open-Source") == 0);
+ myassert(length == 11);
+
+ bind[0].buffer_length=6;
+ rc = mysql_bind_result(stmt, bind);/* re-bind */
+ mystmt(stmt, rc);
+
+ rc = mysql_fetch(stmt);
+ mystmt(stmt, rc);
+ fprintf(stdout, "\n data: %s (%lu)", buffer, length);
+ myassert(strncmp(buffer,"Popula",6) == 0);
+ myassert(length == 7);
+
+ mysql_stmt_close(stmt);
+}
static struct my_option myctest_long_options[] =
{
@@ -5337,6 +5147,8 @@ static struct my_option myctest_long_options[] =
(char **) &opt_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"socket", 'S', "Socket file to use for connection", (char **) &opt_unix_socket,
(char **) &opt_unix_socket, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"count", 't', "Number of times test to be executed", (char **) &opt_count,
+ (char **) &opt_count, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
@@ -5369,6 +5181,8 @@ static void usage(void)
fprintf(stdout,"\
-u, --user=# User for login if not current user.\n");
#endif
+ fprintf(stdout,"\
+ -t, --count=... Execute the test count times.\n");
fprintf(stdout,"*********************************************************************\n");
}
@@ -5404,7 +5218,6 @@ static const char *load_default_groups[]= { "client",0 };
static void get_options(int argc, char **argv)
{
int ho_error;
-
load_defaults("my",load_default_groups,&argc,&argv);
if ((ho_error=handle_options(&argc,&argv, myctest_long_options,
@@ -5414,6 +5227,8 @@ static void get_options(int argc, char **argv)
/*free_defaults(argv);*/
if (tty_password)
opt_password=get_tty_password(NullS);
+ if (!opt_count)
+ opt_count= 1;
return;
}
@@ -5427,90 +5242,91 @@ int main(int argc, char **argv)
client_connect(); /* connect to server */
- /* Start of tests */
- test_multi_query();
- test_select_show(); /* test show syntax */
- test_prepare_alter(); /* change table schema in middle of prepare */
- test_manual_sample(); /* sample in the manual */
- test_bind_result(); /* result bind test */
- test_fetch_null(); /* to fetch null data */
- test_fetch_date(); /* to fetch date,time and timestamp */
- test_fetch_str(); /* to fetch string to all types */
- test_fetch_long(); /* to fetch long to all types */
- test_fetch_short(); /* to fetch short to all types */
- test_fetch_tiny(); /* to fetch tiny to all types */
- test_fetch_bigint(); /* to fetch bigint to all types */
- test_fetch_float(); /* to fetch float to all types */
- test_fetch_double(); /* to fetch double to all types */
- test_bind_result_ext(); /* result bind test - extension */
- test_bind_result_ext1(); /* result bind test - extension */
- test_select_direct(); /* direct select - protocol_simple debug */
- test_select_prepare(); /* prepare select - protocol_prep debug */
- test_select_direct(); /* direct select - protocol_simple debug */
- test_select(); /* simple select test */
- test_select_version(); /* select with variables */
- test_set_variable(); /* set variable prepare */
+ for (iter_count=1; iter_count <= opt_count; iter_count++)
+ {
+ /* Start of tests */
+ test_count= 0;
+
+ test_fetch_null(); /* to fetch null data */
+ test_fetch_date(); /* to fetch date,time and timestamp */
+ test_fetch_str(); /* to fetch string to all types */
+ test_fetch_long(); /* to fetch long to all types */
+ test_fetch_short(); /* to fetch short to all types */
+ test_fetch_tiny(); /* to fetch tiny to all types */
+ test_fetch_bigint(); /* to fetch bigint to all types */
+ test_fetch_float(); /* to fetch float to all types */
+ test_fetch_double(); /* to fetch double to all types */
+ test_bind_result_ext(); /* result bind test - extension */
+ test_bind_result_ext1(); /* result bind test - extension */
+ test_select_direct(); /* direct select - protocol_simple debug */
+ test_select_prepare(); /* prepare select - protocol_prep debug */
+ test_select(); /* simple select test */
+ test_select_version(); /* select with variables */
+ test_select_simple(); /* simple select prepare */
#if NOT_USED
- test_select_meta(); /* select param meta information */
- test_update_meta(); /* update param meta information */
- test_insert_meta(); /* insert param meta information */
+ /*
+ Enable this tests from 4.1.1 when mysql_param_result() is
+ supported
+ */
+ test_select_meta(); /* select param meta information */
+ test_update_meta(); /* update param meta information */
+ test_insert_meta(); /* insert param meta information */
#endif
- test_simple_update(); /* simple update test */
- test_func_fields(); /* test for new 4.1 MYSQL_FIELD members */
- test_long_data(); /* test for sending text data in chunks */
- test_insert(); /* simple insert test - prepare */
- test_set_variable(); /* prepare with set variables */
- test_tran_innodb(); /* test for mysql_commit(), rollback() and autocommit() */
- test_select_show(); /* prepare - show test */
- test_null(); /* test null data handling */
- test_simple_update(); /* simple prepare - update */
- test_prepare_noparam(); /* prepare without parameters */
- test_select(); /* simple prepare-select */
- test_insert(); /* prepare with insert */
- test_bind_result(); /* result bind test */
- test_long_data(); /* long data handling in pieces */
- test_prepare_simple(); /* simple prepare */
- test_prepare(); /* prepare test */
- test_null(); /* test null data handling */
- test_debug_example(); /* some debugging case */
- test_update(); /* prepare-update test */
- test_simple_update(); /* simple prepare with update */
- test_long_data(); /* long data handling in pieces */
- test_simple_delete(); /* prepare with delete */
- test_field_names(); /* test for field names */
- test_double_compare(); /* float comparision */
- client_query(); /* simple client query test */
- client_store_result(); /* usage of mysql_store_result() */
- client_use_result(); /* usage of mysql_use_result() */
- test_tran_bdb(); /* transaction test on BDB table type */
- test_tran_innodb(); /* transaction test on InnoDB table type */
- test_prepare_ext(); /* test prepare with all types conversion -- TODO */
- test_prepare_syntax(); /* syntax check for prepares */
- test_prepare_field_result(); /* prepare meta info */
- test_prepare_resultset(); /* prepare meta info test */
- test_field_names(); /* test for field names */
- test_field_flags(); /* test to help .NET provider team */
- test_long_data_str(); /* long data handling */
- test_long_data_str1(); /* yet another long data handling */
- test_long_data_bin(); /* long binary insertion */
- test_warnings(); /* show warnings test */
- test_errors(); /* show errors test */
- test_select_simple(); /* simple select prepare */
- test_prepare_resultset();/* prepare meta info test */
- test_func_fields(); /* FUNCTION field info */
- /*test_stmt_close(); */ /* mysql_stmt_close() test -- hangs */
- test_prepare_field_result(); /* prepare meta info */
- test_multi_stmt(); /* multi stmt test */
- test_store_result(); /* test the store_result */
- test_store_result1(); /* test store result without buffers */
- test_store_result2(); /* test store result for misc case */
- test_multi_stmt(); /* test multi stmt */
- test_subselect(); /* test subselect prepare */
- test_multi_query(); /* test multi query exec */
- /* End of tests */
+ test_func_fields(); /* test for new 4.1 MYSQL_FIELD members */
+ test_long_data(); /* test for sending text data in chunks */
+ test_insert(); /* simple insert test - prepare */
+ test_set_variable(); /* prepare with set variables */
+ test_select_show(); /* prepare - show test */
+ test_prepare_noparam(); /* prepare without parameters */
+ test_bind_result(); /* result bind test */
+ test_prepare_simple(); /* simple prepare */
+ test_prepare(); /* prepare test */
+ test_null(); /* test null data handling */
+ test_debug_example(); /* some debugging case */
+ test_update(); /* prepare-update test */
+ test_simple_update(); /* simple prepare with update */
+ test_simple_delete(); /* prepare with delete */
+ test_double_compare(); /* float comparision */
+ client_query(); /* simple client query test */
+ client_store_result(); /* usage of mysql_store_result() */
+ client_use_result(); /* usage of mysql_use_result() */
+ test_tran_bdb(); /* transaction test on BDB table type */
+ test_tran_innodb(); /* transaction test on InnoDB table type */
+ test_prepare_ext(); /* test prepare with all types conversion -- TODO */
+ test_prepare_syntax(); /* syntax check for prepares */
+ test_field_names(); /* test for field names */
+ test_field_flags(); /* test to help .NET provider team */
+ test_long_data_str(); /* long data handling */
+ test_long_data_str1(); /* yet another long data handling */
+ test_long_data_bin(); /* long binary insertion */
+ test_warnings(); /* show warnings test */
+ test_errors(); /* show errors test */
+ test_prepare_resultset();/* prepare meta info test */
+ test_stmt_close(); /* mysql_stmt_close() test -- hangs */
+ test_prepare_field_result(); /* prepare meta info */
+ test_multi_stmt(); /* multi stmt test -TODO*/
+ test_multi_query(); /* test multi query execution */
+ test_store_result(); /* test the store_result */
+ test_store_result1(); /* test store result without buffers */
+ test_store_result2(); /* test store result for misc case */
+ test_subselect(); /* test subselect prepare -TODO*/
+ test_date(); /* test the MYSQL_TIME conversion */
+ test_date_date(); /* test conversion from DATE to all */
+ test_date_time(); /* test conversion from TIME to all */
+ test_date_ts() ; /* test conversion from TIMESTAMP to all */
+ test_date_dt() ; /* test conversion from DATETIME to all */
+ test_prepare_alter(); /* change table schema in middle of prepare */
+ test_manual_sample(); /* sample in the manual */
+ test_pure_coverage(); /* keep pure coverage happy */
+ test_buffers(); /* misc buffer handling */
+ /* End of tests */
+ }
client_disconnect(); /* disconnect from server */
- fprintf(stdout,"\n\nSUCCESS !!!\n");
+ fprintf(stdout,"\n\nAll '%d' tests were successful (in '%d' iterations)",
+ test_count-1, opt_count);
+
+ fprintf(stdout,"\nSUCCESS !!!\n");
return(0);
}