diff options
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= ¶m->bind_length; + param->length= ¶m->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= ¶m->bind_length; - param->bind_length= param->buffer_length= 1; + param->length= ¶m->buffer_length; + param->buffer_length= 1; param->store_param_func= store_param_tinyint; break; case MYSQL_TYPE_SHORT: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 2; + param->length= ¶m->buffer_length; + param->buffer_length= 2; param->store_param_func= store_param_short; break; case MYSQL_TYPE_LONG: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 4; + param->length= ¶m->buffer_length; + param->buffer_length= 4; param->store_param_func= store_param_int32; break; case MYSQL_TYPE_LONGLONG: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 8; + param->length= ¶m->buffer_length; + param->buffer_length= 8; param->store_param_func= store_param_int64; break; case MYSQL_TYPE_FLOAT: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 4; + param->length= ¶m->buffer_length; + param->buffer_length= 4; param->store_param_func= store_param_float; break; case MYSQL_TYPE_DOUBLE: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 8; + param->length= ¶m->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= ¶m->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= ¶m->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(<ime, 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); } |