sql_error.cc, sql_prepare.cc:
new file Client-server protocol 4.1 changes - Server side: * Enhanced metadata information: - SHOW [COUNT(*)] ERRORS [LIMIT [offset,] rows] - SHOW [COUNT(*)] WARNING [LIMIT [offset,] rows] - SHOW TABLE TYPES - SHOW PRIVILEGES - SHOW COLUMN TYPES (Not fully implemented) * Prepared execution * Long data handling in pieces * And other misc changes
This commit is contained in:
parent
049a8386f3
commit
6cdebb33d7
@ -67,6 +67,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
|
||||
mysqld.cc password.c hash_filo.cc hostname.cc \
|
||||
convert.cc sql_parse.cc sql_yacc.yy \
|
||||
sql_base.cc table.cc sql_select.cc sql_insert.cc \
|
||||
sql_prepare.cc sql_error.cc \
|
||||
sql_update.cc sql_delete.cc uniques.cc sql_do.cc \
|
||||
procedure.cc item_uniq.cc sql_test.cc \
|
||||
log.cc log_event.cc init.cc derror.cc sql_acl.cc \
|
||||
|
@ -279,8 +279,10 @@ void Field_num::add_zerofill_and_unsigned(String &res) const
|
||||
|
||||
void Field_num::make_field(Send_field *field)
|
||||
{
|
||||
field->db_name=table->table_cache_key ? table->table_cache_key : "";
|
||||
field->org_table_name=table->real_name;
|
||||
field->table_name=table_name;
|
||||
field->col_name=field_name;
|
||||
field->col_name=field->org_col_name=field_name;
|
||||
field->length=field_length;
|
||||
field->type=type();
|
||||
field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
|
||||
@ -290,8 +292,10 @@ void Field_num::make_field(Send_field *field)
|
||||
|
||||
void Field_str::make_field(Send_field *field)
|
||||
{
|
||||
field->db_name=table->table_cache_key ? table->table_cache_key : "";
|
||||
field->org_table_name=table->real_name;
|
||||
field->table_name=table_name;
|
||||
field->col_name=field_name;
|
||||
field->col_name=field->org_col_name=field_name;
|
||||
field->length=field_length;
|
||||
field->type=type();
|
||||
field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
|
||||
|
@ -1038,7 +1038,9 @@ public:
|
||||
|
||||
class Send_field {
|
||||
public:
|
||||
const char *table_name,*col_name;
|
||||
const char *db_name;
|
||||
const char *table_name,*org_table_name;
|
||||
const char *col_name,*org_col_name;
|
||||
uint length,flags,decimals;
|
||||
enum_field_types type;
|
||||
Send_field() {}
|
||||
|
139
sql/item.cc
139
sql/item.cc
@ -287,6 +287,140 @@ String *Item_null::val_str(String *str)
|
||||
{ null_value=1; return 0;}
|
||||
|
||||
|
||||
/* Item_param related */
|
||||
void Item_param::set_null()
|
||||
{
|
||||
maybe_null=null_value=1;
|
||||
}
|
||||
|
||||
void Item_param::set_int(longlong i)
|
||||
{
|
||||
int_value=(longlong)i;
|
||||
item_result_type = INT_RESULT;
|
||||
item_type = INT_ITEM;
|
||||
}
|
||||
|
||||
void Item_param::set_double(double i)
|
||||
{
|
||||
double value = (double)i;
|
||||
real_value=value;
|
||||
item_result_type = REAL_RESULT;
|
||||
item_type = REAL_ITEM;
|
||||
}
|
||||
|
||||
void Item_param::set_double(float i)
|
||||
{
|
||||
float value = (float)i;
|
||||
real_value=(double)value;
|
||||
item_result_type = REAL_RESULT;
|
||||
item_type = REAL_ITEM;
|
||||
}
|
||||
|
||||
void Item_param::set_value(const char *str, uint length)
|
||||
{
|
||||
str_value.set(str,length,default_charset_info);
|
||||
item_result_type = STRING_RESULT;
|
||||
item_type = STRING_ITEM;
|
||||
}
|
||||
|
||||
void Item_param::set_longdata(const char *str, ulong length)
|
||||
{
|
||||
/* TODO: Fix this for binary handling by making use of
|
||||
buffer_type..
|
||||
*/
|
||||
str_value.append(str,length);
|
||||
}
|
||||
|
||||
void Item_param::set_long_end()
|
||||
{
|
||||
long_data_supplied = true;
|
||||
item_result_type = STRING_RESULT;
|
||||
};
|
||||
|
||||
bool Item_param::save_in_field(Field *field)
|
||||
{
|
||||
if (null_value)
|
||||
return set_field_to_null(field);
|
||||
|
||||
field->set_notnull();
|
||||
if (item_result_type == INT_RESULT)
|
||||
{
|
||||
longlong nr=val_int();
|
||||
field->store(nr);
|
||||
return 0;
|
||||
}
|
||||
if (item_result_type == REAL_RESULT)
|
||||
{
|
||||
double nr=val();
|
||||
field->store(nr);
|
||||
return 0;
|
||||
}
|
||||
String *result;
|
||||
CHARSET_INFO *cs=default_charset_info;//fix this
|
||||
result=val_str(&str_value);
|
||||
field->store(result->ptr(),result->length(),cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Item_param::make_field(Send_field *tmp_field)
|
||||
{
|
||||
init_make_field(tmp_field,FIELD_TYPE_STRING);
|
||||
}
|
||||
|
||||
double Item_param::val()
|
||||
{
|
||||
/* Cross check whether we need need this conversions ? or direct
|
||||
return(real_value) is enough ?
|
||||
*/
|
||||
|
||||
switch(item_result_type) {
|
||||
|
||||
case STRING_RESULT:
|
||||
return (double)atof(str_value.ptr());
|
||||
case INT_RESULT:
|
||||
return (double)int_value;
|
||||
default:
|
||||
return real_value;
|
||||
}
|
||||
}
|
||||
|
||||
longlong Item_param::val_int()
|
||||
{
|
||||
/* Cross check whether we need need this conversions ? or direct
|
||||
return(int_value) is enough ?
|
||||
*/
|
||||
|
||||
switch(item_result_type) {
|
||||
|
||||
case STRING_RESULT:
|
||||
return (longlong)strtoll(str_value.ptr(),(char**) 0,10);
|
||||
case REAL_RESULT:
|
||||
return (longlong) (real_value+(real_value > 0 ? 0.5 : -0.5));
|
||||
default:
|
||||
return int_value;
|
||||
}
|
||||
}
|
||||
|
||||
String *Item_param::val_str(String* str)
|
||||
{
|
||||
/* Cross check whether we need need this conversions ? or direct
|
||||
return(&str_value) is enough ?
|
||||
*/
|
||||
|
||||
switch(item_result_type) {
|
||||
|
||||
case INT_RESULT:
|
||||
str->set(int_value);
|
||||
return str;
|
||||
case REAL_RESULT:
|
||||
str->set(real_value);
|
||||
return str;
|
||||
default:
|
||||
return (String*) &str_value;
|
||||
}
|
||||
}
|
||||
/* End of Item_param related */
|
||||
|
||||
void Item_copy_string::copy()
|
||||
{
|
||||
String *res=item->val_str(&str_value);
|
||||
@ -373,7 +507,10 @@ bool Item_field::fix_fields(THD *thd,TABLE_LIST *tables)
|
||||
|
||||
void Item::init_make_field(Send_field *tmp_field,
|
||||
enum enum_field_types field_type)
|
||||
{
|
||||
{
|
||||
tmp_field->db_name=(char*) "";
|
||||
tmp_field->org_table_name=(char*) "";
|
||||
tmp_field->org_col_name=(char*) "";
|
||||
tmp_field->table_name=(char*) "";
|
||||
tmp_field->col_name=name;
|
||||
tmp_field->flags=maybe_null ? 0 : NOT_NULL_FLAG;
|
||||
|
34
sql/item.h
34
sql/item.h
@ -155,6 +155,40 @@ public:
|
||||
bool is_null() { return 1; }
|
||||
};
|
||||
|
||||
class Item_param :public Item
|
||||
{
|
||||
public:
|
||||
longlong int_value;
|
||||
double real_value;
|
||||
enum Item_result item_result_type;
|
||||
enum Type item_type;
|
||||
enum enum_field_types buffer_type;
|
||||
my_bool long_data_supplied;
|
||||
|
||||
Item_param(char *name_par=0){
|
||||
name= name_par ? name_par : (char*) "?";
|
||||
long_data_supplied = false;
|
||||
item_type = STRING_ITEM; item_result_type = STRING_RESULT;
|
||||
}
|
||||
enum Type type() const { return item_type; }
|
||||
double val();
|
||||
longlong val_int();
|
||||
String *val_str(String*);
|
||||
void make_field(Send_field *field);
|
||||
bool save_in_field(Field *field);
|
||||
void set_null();
|
||||
void set_int(longlong i);
|
||||
void set_double(float i);
|
||||
void set_double(double i);
|
||||
void set_value(const char *str, uint length);
|
||||
void set_long_str(const char *str, ulong length);
|
||||
void set_long_binary(const char *str, ulong length);
|
||||
void set_longdata(const char *str, ulong length);
|
||||
void set_long_end();
|
||||
enum Item_result result_type () const
|
||||
{ return item_result_type; }
|
||||
Item *new_item() { return new Item_param(name); }
|
||||
};
|
||||
|
||||
class Item_int :public Item
|
||||
{
|
||||
|
@ -60,8 +60,9 @@ void Item_sum::make_field(Send_field *tmp_field)
|
||||
result_type() == REAL_RESULT ? FIELD_TYPE_DOUBLE :
|
||||
FIELD_TYPE_VAR_STRING);
|
||||
}
|
||||
tmp_field->table_name=(char*)"";
|
||||
tmp_field->col_name=name;
|
||||
tmp_field->db_name=(char*)"";
|
||||
tmp_field->org_table_name=tmp_field->table_name=(char*)"";
|
||||
tmp_field->org_col_name=tmp_field->col_name=name;
|
||||
}
|
||||
|
||||
void Item_sum::print(String *str)
|
||||
|
@ -129,6 +129,7 @@ static SYMBOL symbols[] = {
|
||||
{ "DROP", SYM(DROP),0,0},
|
||||
{ "DUMPFILE", SYM(DUMPFILE),0,0},
|
||||
{ "DYNAMIC", SYM(DYNAMIC_SYM),0,0},
|
||||
{ "ERRORS", SYM(ERRORS),0,0},
|
||||
{ "END", SYM(END),0,0},
|
||||
{ "ELSE", SYM(ELSE),0,0},
|
||||
{ "ESCAPE", SYM(ESCAPE_SYM),0,0},
|
||||
@ -327,6 +328,7 @@ static SYMBOL symbols[] = {
|
||||
{ "SQL_BUFFER_RESULT", SYM(SQL_BUFFER_RESULT),0,0},
|
||||
{ "SQL_CACHE", SYM(SQL_CACHE_SYM), 0, 0},
|
||||
{ "SQL_CALC_FOUND_ROWS", SYM(SQL_CALC_FOUND_ROWS),0,0},
|
||||
{ "SQL_ERROR_COUNT", SYM(SQL_ERROR_COUNT),0,0},
|
||||
{ "SQL_LOG_BIN", SYM(SQL_LOG_BIN),0,0},
|
||||
{ "SQL_LOG_OFF", SYM(SQL_LOG_OFF),0,0},
|
||||
{ "SQL_LOG_UPDATE", SYM(SQL_LOG_UPDATE),0,0},
|
||||
@ -366,6 +368,7 @@ static SYMBOL symbols[] = {
|
||||
{ "TRUNCATE", SYM(TRUNCATE_SYM),0,0},
|
||||
{ "TO", SYM(TO_SYM),0,0},
|
||||
{ "TYPE", SYM(TYPE_SYM),0,0},
|
||||
{ "TYPES", SYM(TYPES_SYM),0,0},
|
||||
{ "UNCOMMITTED", SYM(UNCOMMITTED_SYM),0,0},
|
||||
{ "UNION", SYM(UNION_SYM),0,0},
|
||||
{ "UNIQUE", SYM(UNIQUE_SYM),0,0},
|
||||
@ -381,6 +384,7 @@ static SYMBOL symbols[] = {
|
||||
{ "VARIABLES", SYM(VARIABLES),0,0},
|
||||
{ "VARYING", SYM(VARYING),0,0},
|
||||
{ "VARBINARY", SYM(VARBINARY),0,0},
|
||||
{ "WARNINGS", SYM(WARNINGS),0,0},
|
||||
{ "WITH", SYM(WITH),0,0},
|
||||
{ "WORK", SYM(WORK_SYM),0,0},
|
||||
{ "WRITE", SYM(WRITE_SYM),0,0},
|
||||
|
@ -287,6 +287,8 @@ inline THD *_current_thd(void)
|
||||
#define query_cache_invalidate_by_MyISAM_filename_ref NULL
|
||||
#endif /*HAVE_QUERY_CACHE*/
|
||||
|
||||
#define prepare_execute(A) ((A)->command == COM_EXECUTE)
|
||||
|
||||
int mysql_create_db(THD *thd, char *db, uint create_info, bool silent);
|
||||
int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent);
|
||||
void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags);
|
||||
@ -363,6 +365,8 @@ bool net_store_data(String *packet, CONVERT *convert, const char *from);
|
||||
SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length);
|
||||
int setup_order(THD *thd,TABLE_LIST *tables, List<Item> &fields,
|
||||
List <Item> &all_fields, ORDER *order);
|
||||
int setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields,
|
||||
List<Item> &all_fields, ORDER *order, bool *hidden_group_fields);
|
||||
|
||||
int handle_select(THD *thd, LEX *lex, select_result *result);
|
||||
int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds,
|
||||
@ -455,7 +459,7 @@ bool load_des_key_file(const char *file_name);
|
||||
/* sql_do.cc */
|
||||
int mysql_do(THD *thd, List<Item> &values);
|
||||
|
||||
/* sql_list.c */
|
||||
/* sql_show.c */
|
||||
int mysqld_show_dbs(THD *thd,const char *wild);
|
||||
int mysqld_show_open_tables(THD *thd,const char *wild);
|
||||
int mysqld_show_tables(THD *thd,const char *db,const char *wild);
|
||||
@ -473,6 +477,24 @@ int mysqld_show_status(THD *thd);
|
||||
int mysqld_show_variables(THD *thd,const char *wild);
|
||||
int mysqld_show(THD *thd, const char *wild, show_var_st *variables);
|
||||
int mysqld_show_charsets(THD *thd,const char *wild);
|
||||
int mysqld_show_privileges(THD *thd);
|
||||
int mysqld_show_column_types(THD *thd);
|
||||
|
||||
/* sql_prepare.cc */
|
||||
void mysql_com_prepare(THD *thd,char*packet,uint packet_length);
|
||||
void mysql_init_query(THD *thd);/* sql_parse. cc */
|
||||
void mysql_com_execute(THD *thd);
|
||||
void mysql_com_longdata(THD *thd);
|
||||
int check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
|
||||
List<Item> &values, ulong counter);
|
||||
|
||||
/* sql_error.cc */
|
||||
void push_error(uint code, const char *msg);
|
||||
void push_warning(uint code, const char *msg);
|
||||
int mysqld_show_warnings(THD *thd);
|
||||
int mysqld_show_errors(THD *thd);
|
||||
int mysqld_show_warnings_count(THD *thd);
|
||||
int mysqld_show_errors_count(THD *);
|
||||
|
||||
/* sql_handler.cc */
|
||||
int mysql_ha_open(THD *thd, TABLE_LIST *tables);
|
||||
@ -654,6 +676,10 @@ extern const char *default_tx_isolation_name;
|
||||
extern String empty_string;
|
||||
extern struct show_var_st init_vars[];
|
||||
extern struct show_var_st status_vars[];
|
||||
extern struct show_table_type_st table_type_vars[];
|
||||
extern SHOW_COMP_OPTION have_isam;
|
||||
extern SHOW_COMP_OPTION have_innodb;
|
||||
extern SHOW_COMP_OPTION have_berkeley_db;
|
||||
extern enum db_type default_table_type;
|
||||
extern enum enum_tx_isolation default_tx_isolation;
|
||||
extern char glob_hostname[FN_REFLEN];
|
||||
|
@ -48,6 +48,7 @@ void send_error(NET *net, uint sql_errno, const char *err)
|
||||
}
|
||||
}
|
||||
}
|
||||
push_error(sql_errno, err);
|
||||
if (net->vio == 0)
|
||||
{
|
||||
if (thd && thd->bootstrap)
|
||||
@ -82,7 +83,14 @@ void send_error(NET *net, uint sql_errno, const char *err)
|
||||
|
||||
void send_warning(NET *net, uint sql_errno, const char *err)
|
||||
{
|
||||
DBUG_ENTER("send_warning");
|
||||
DBUG_ENTER("send_warning");
|
||||
push_warning(sql_errno, err ? err : ER(sql_errno));
|
||||
|
||||
/*
|
||||
TODO :
|
||||
Try to return ok with warning status to client, instead
|
||||
of returning error ..
|
||||
*/
|
||||
send_error(net,sql_errno,err);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
@ -123,6 +131,7 @@ net_printf(NET *net, uint errcode, ...)
|
||||
length=sizeof(net->last_error)-1; /* purecov: inspected */
|
||||
va_end(args);
|
||||
|
||||
push_error(errcode, text_pos);
|
||||
if (net->vio == 0)
|
||||
{
|
||||
if (thd && thd->bootstrap)
|
||||
|
188
sql/sql_base.cc
188
sql/sql_base.cc
@ -179,56 +179,58 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild)
|
||||
DBUG_RETURN(open_list);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
** Send name and type of result to client.
|
||||
** Sum fields has table name empty and field_name.
|
||||
** flag is a bit mask with the following functions:
|
||||
** 1 send number of rows
|
||||
** 2 send default values
|
||||
** 4 Don't convert field names
|
||||
******************************************************************************/
|
||||
/*
|
||||
Send name and type of result to client.
|
||||
Sum fields has table name empty and field_name.
|
||||
flag is a bit mask with the following functions:
|
||||
1 send number of rows
|
||||
2 send default values
|
||||
4 Don't convert field names
|
||||
*/
|
||||
|
||||
bool
|
||||
send_fields(THD *thd,List<Item> &list,uint flag)
|
||||
send_convert_fields(THD *thd,List<Item> &list,CONVERT *convert,uint flag)
|
||||
{
|
||||
List_iterator_fast<Item> it(list);
|
||||
Item *item;
|
||||
char buff[80];
|
||||
CONVERT *convert= (flag & 4) ? (CONVERT*) 0 : thd->convert_set;
|
||||
DBUG_ENTER("send_fields");
|
||||
|
||||
|
||||
String tmp((char*) buff,sizeof(buff),default_charset_info);
|
||||
String *res,*packet= &thd->packet;
|
||||
|
||||
if (thd->fatal_error) // We have got an error
|
||||
goto err;
|
||||
|
||||
if (flag & 1)
|
||||
{ // Packet with number of elements
|
||||
char *pos=net_store_length(buff,(uint) list.elements);
|
||||
(void) my_net_write(&thd->net, buff,(uint) (pos-buff));
|
||||
}
|
||||
|
||||
while ((item=it++))
|
||||
{
|
||||
char *pos;
|
||||
Send_field field;
|
||||
item->make_field(&field);
|
||||
|
||||
packet->length(0);
|
||||
|
||||
if (convert)
|
||||
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
|
||||
{
|
||||
if (convert->store(packet,field.table_name,
|
||||
if (convert->store(packet,field.db_name,
|
||||
(uint) strlen(field.db_name)) ||
|
||||
convert->store(packet,field.table_name,
|
||||
(uint) strlen(field.table_name)) ||
|
||||
convert->store(packet,field.org_table_name,
|
||||
(uint) strlen(field.org_table_name)) ||
|
||||
convert->store(packet,field.col_name,
|
||||
(uint) strlen(field.col_name)) ||
|
||||
convert->store(packet,field.org_col_name,
|
||||
(uint) strlen(field.org_col_name)) ||
|
||||
packet->realloc(packet->length()+10))
|
||||
goto err;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (convert->store(packet,field.table_name,
|
||||
(uint) strlen(field.table_name)) ||
|
||||
convert->store(packet,field.col_name,
|
||||
(uint) strlen(field.col_name)) ||
|
||||
packet->realloc(packet->length()+10))
|
||||
return 1;
|
||||
}
|
||||
else if (net_store_data(packet,field.table_name) ||
|
||||
net_store_data(packet,field.col_name) ||
|
||||
packet->realloc(packet->length()+10))
|
||||
goto err; /* purecov: inspected */
|
||||
|
||||
pos= (char*) packet->ptr()+packet->length();
|
||||
|
||||
if (!(thd->client_capabilities & CLIENT_LONG_FLAG))
|
||||
@ -250,19 +252,135 @@ send_fields(THD *thd,List<Item> &list,uint flag)
|
||||
if (!(res=item->val_str(&tmp)))
|
||||
{
|
||||
if (net_store_null(packet))
|
||||
goto err;
|
||||
return 1;
|
||||
}
|
||||
else if (net_store_data(packet,res->ptr(),res->length()))
|
||||
goto err;
|
||||
return 1;
|
||||
}
|
||||
if (my_net_write(&thd->net, (char*) packet->ptr(),packet->length()))
|
||||
break; /* purecov: inspected */
|
||||
}
|
||||
send_eof(&thd->net,1);
|
||||
DBUG_RETURN(0);
|
||||
err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Send name and type of result to client.
|
||||
Sum fields has table name empty and field_name
|
||||
flag is a bit mask with the following functios:
|
||||
1 send number of rows
|
||||
2 send default values
|
||||
4 Don't convert field names
|
||||
*/
|
||||
|
||||
bool
|
||||
send_non_convert_fields(THD *thd,List<Item> &list,uint flag)
|
||||
{
|
||||
List_iterator_fast<Item> it(list);
|
||||
Item *item;
|
||||
char buff[80];
|
||||
|
||||
String tmp((char*) buff,sizeof(buff),default_charset_info);
|
||||
String *res,*packet= &thd->packet;
|
||||
|
||||
while ((item=it++))
|
||||
{
|
||||
char *pos;
|
||||
Send_field field;
|
||||
item->make_field(&field);
|
||||
|
||||
packet->length(0);
|
||||
|
||||
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
|
||||
{
|
||||
if (net_store_data(packet,field.db_name) ||
|
||||
net_store_data(packet,field.table_name) ||
|
||||
net_store_data(packet,field.org_table_name) ||
|
||||
net_store_data(packet,field.col_name) ||
|
||||
net_store_data(packet,field.org_col_name) ||
|
||||
packet->realloc(packet->length()+10))
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (net_store_data(packet,field.table_name) ||
|
||||
net_store_data(packet,field.col_name) ||
|
||||
packet->realloc(packet->length()+10))
|
||||
return 1;
|
||||
}
|
||||
|
||||
pos= (char*) packet->ptr()+packet->length();
|
||||
|
||||
if (!(thd->client_capabilities & CLIENT_LONG_FLAG))
|
||||
{
|
||||
packet->length(packet->length()+9);
|
||||
pos[0]=3; int3store(pos+1,field.length);
|
||||
pos[4]=1; pos[5]=field.type;
|
||||
pos[6]=2; pos[7]=(char) field.flags; pos[8]= (char) field.decimals;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet->length(packet->length()+10);
|
||||
pos[0]=3; int3store(pos+1,field.length);
|
||||
pos[4]=1; pos[5]=field.type;
|
||||
pos[6]=3; int2store(pos+7,field.flags); pos[9]= (char) field.decimals;
|
||||
}
|
||||
if (flag & 2)
|
||||
{ // Send default value
|
||||
if (!(res=item->val_str(&tmp)))
|
||||
{
|
||||
if (net_store_null(packet))
|
||||
return 1;
|
||||
}
|
||||
else if (net_store_data(packet,res->ptr(),res->length()))
|
||||
return 1;
|
||||
}
|
||||
if (my_net_write(&thd->net, (char*) packet->ptr(),packet->length()))
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
** Send name and type of result to client.
|
||||
** Sum fields has table name empty and field_name.
|
||||
** flag is a bit mask with the following functions:
|
||||
** 1 send number of rows
|
||||
** 2 send default values
|
||||
** 4 Don't convert field names
|
||||
******************************************************************************/
|
||||
|
||||
bool
|
||||
send_fields(THD *thd,List<Item> &list,uint flag)
|
||||
{
|
||||
List_iterator_fast<Item> it(list);
|
||||
char buff[80];
|
||||
CONVERT *convert= (flag & 4) ? (CONVERT*) 0 : thd->convert_set;
|
||||
|
||||
String tmp((char*) buff,sizeof(buff),default_charset_info);
|
||||
|
||||
if (thd->fatal_error) // We have got an error
|
||||
goto err;
|
||||
|
||||
if (flag & 1)
|
||||
{ // Packet with number of elements
|
||||
char *pos=net_store_length(buff,(uint) list.elements);
|
||||
(void) my_net_write(&thd->net, buff,(uint) (pos-buff));
|
||||
}
|
||||
|
||||
/* Avoid check conditions on convert() for each field
|
||||
by having two diffrent functions
|
||||
*/
|
||||
if (convert && send_convert_fields(thd,list,convert,flag))
|
||||
goto err;
|
||||
|
||||
else if(send_non_convert_fields(thd,list,flag))
|
||||
goto err;
|
||||
|
||||
send_eof(&thd->net);
|
||||
return 0;
|
||||
err:
|
||||
send_error(&thd->net,ER_OUT_OF_RESOURCES); /* purecov: inspected */
|
||||
DBUG_RETURN(1); /* purecov: inspected */
|
||||
return 1; /* purecov: inspected */
|
||||
}
|
||||
|
||||
|
||||
|
@ -140,6 +140,7 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0),
|
||||
command=COM_CONNECT;
|
||||
set_query_id=1;
|
||||
default_select_limit= HA_POS_ERROR;
|
||||
max_error_count=max_warning_count=MYSQL_DEFAULT_ERROR_COUNT;
|
||||
max_join_size= ((::max_join_size != ~ (ulong) 0L) ? ::max_join_size :
|
||||
HA_POS_ERROR);
|
||||
db_access=NO_ACCESS;
|
||||
@ -147,6 +148,7 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0),
|
||||
/* Initialize sub structures */
|
||||
bzero((char*) &mem_root,sizeof(mem_root));
|
||||
bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root));
|
||||
bzero((char*) &con_root,sizeof(con_root));
|
||||
user_connect=(USER_CONN *)0;
|
||||
hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
|
||||
(hash_get_key) get_var_key,
|
||||
@ -223,6 +225,7 @@ THD::~THD()
|
||||
safeFree(db);
|
||||
safeFree(ip);
|
||||
free_root(&mem_root,MYF(0));
|
||||
free_root(&con_root,MYF(0));
|
||||
free_root(&transaction.mem_root,MYF(0));
|
||||
mysys_var=0; // Safety (shouldn't be needed)
|
||||
#ifdef SIGNAL_WITH_VIO_CLOSE
|
||||
|
@ -289,7 +289,30 @@ public:
|
||||
i_string_pair():key(0),val(0) { }
|
||||
i_string_pair(char* key_arg, char* val_arg) : key(key_arg),val(val_arg) {}
|
||||
};
|
||||
#define MYSQL_DEFAULT_ERROR_COUNT 500
|
||||
|
||||
class mysql_st_error
|
||||
{
|
||||
public:
|
||||
uint code;
|
||||
char msg[MYSQL_ERRMSG_SIZE+1];
|
||||
char query[NAME_LEN+1];
|
||||
|
||||
static void *operator new(size_t size)
|
||||
{
|
||||
return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE));
|
||||
}
|
||||
static void operator delete(void* ptr_arg, size_t size)
|
||||
{
|
||||
my_free((gptr)ptr_arg, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
|
||||
}
|
||||
mysql_st_error(uint ecode, const char *emsg, const char *equery)
|
||||
{
|
||||
code = ecode;
|
||||
strmov(msg, emsg);
|
||||
strnmov(query, equery ? equery : "", NAME_LEN);
|
||||
}
|
||||
};
|
||||
|
||||
class delayed_insert;
|
||||
|
||||
@ -308,6 +331,7 @@ public:
|
||||
NET net; // client connection descriptor
|
||||
LEX lex; // parse tree descriptor
|
||||
MEM_ROOT mem_root; // 1 command-life memory
|
||||
MEM_ROOT con_root; // connection-life memory
|
||||
HASH user_vars; // hash for user variables
|
||||
String packet; // buffer used for network I/O
|
||||
struct sockaddr_in remote; // client socket address
|
||||
@ -410,7 +434,8 @@ public:
|
||||
max_join_size, sent_row_count, examined_row_count;
|
||||
table_map used_tables;
|
||||
USER_CONN *user_connect;
|
||||
ulong query_id,version, inactive_timeout,options,thread_id;
|
||||
ulong query_id,version, inactive_timeout,options,thread_id,
|
||||
max_error_count, max_warning_count;
|
||||
long dbug_thread_id;
|
||||
pthread_t real_id;
|
||||
uint current_tablenr,tmp_table,cond_count,col_access;
|
||||
@ -428,8 +453,13 @@ public:
|
||||
bool query_error, bootstrap, cleanup_done;
|
||||
bool safe_to_cache_query;
|
||||
bool volatile killed;
|
||||
// TRUE when having fix field called
|
||||
bool having_fix_field;
|
||||
bool having_fix_field; //TRUE when having fix field called
|
||||
bool prepare_command;
|
||||
ulong param_count,current_param_number;
|
||||
Error<mysql_st_error> err_list;
|
||||
Error<mysql_st_error> warn_list;
|
||||
Item_param *current_param;
|
||||
|
||||
/*
|
||||
If we do a purge of binary logs, log index info of the threads
|
||||
that are currently reading it needs to be adjusted. To do that
|
||||
|
219
sql/sql_error.cc
Normal file
219
sql/sql_error.cc
Normal file
@ -0,0 +1,219 @@
|
||||
/* Copyright (C) 1995-2002 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/**********************************************************************
|
||||
This file contains the implementation of error and warnings related
|
||||
|
||||
- Whenever an error or warning occured, it pushes the same to
|
||||
the respective list along with sending it to client.
|
||||
|
||||
- When client requests the information using SHOW command, then
|
||||
server processes from this list and returns back in the form of
|
||||
resultset.
|
||||
|
||||
syntax : SHOW [COUNT(*)] ERRORS [LIMIT [offset,] rows]
|
||||
SHOW [COUNT(*)] WARNINGS [LIMIT [offset,] rows]
|
||||
|
||||
***********************************************************************/
|
||||
/* Handles errors and warnings .. */
|
||||
|
||||
#include "mysql_priv.h"
|
||||
|
||||
/*
|
||||
Push the error to error list
|
||||
*/
|
||||
|
||||
void push_error(uint code, const char *msg)
|
||||
{
|
||||
THD *thd=current_thd;
|
||||
|
||||
mysql_st_error *err = new mysql_st_error(code,msg,(const char*)thd->query);
|
||||
|
||||
if (thd->err_list.elements >= thd->max_error_count)
|
||||
{
|
||||
/* Remove the old elements and always maintain the max size
|
||||
equal to sql_error_count.
|
||||
|
||||
If one max_error_count using sets sql_error_count less than
|
||||
the current list size, then make sure it always grows upto
|
||||
sql_error_count size only
|
||||
|
||||
** BUG ** : Doesn't work in removing the old one
|
||||
from the list, and thus SET SQL_ERROR_COUNT=x doesn't work
|
||||
*/
|
||||
for (uint count=thd->err_list.elements-1;
|
||||
count <= thd->max_error_count-1; count++)
|
||||
{
|
||||
thd->err_list.remove_last();
|
||||
}
|
||||
}
|
||||
thd->err_list.push_front(err);
|
||||
}
|
||||
|
||||
/*
|
||||
Push the warning to warning list
|
||||
*/
|
||||
|
||||
void push_warning(uint code, const char *msg)
|
||||
{
|
||||
THD *thd=current_thd;
|
||||
|
||||
mysql_st_error *warn = new mysql_st_error(code,msg,(const char *)thd->query);
|
||||
|
||||
if (thd->warn_list.elements >= thd->max_warning_count)
|
||||
{
|
||||
/* Remove the old elements and always maintian the max size
|
||||
equal to sql_error_count
|
||||
*/
|
||||
for (uint count=thd->warn_list.elements;
|
||||
count <= thd->max_warning_count-1; count++)
|
||||
{
|
||||
thd->warn_list.remove_last();
|
||||
}
|
||||
}
|
||||
thd->warn_list.push_front(warn);
|
||||
}
|
||||
|
||||
/*
|
||||
List all errors
|
||||
*/
|
||||
|
||||
int mysqld_show_errors(THD *thd)
|
||||
{
|
||||
List<Item> field_list;
|
||||
DBUG_ENTER("mysqld_show_errors");
|
||||
|
||||
field_list.push_back(new Item_int("CODE",0,4));
|
||||
field_list.push_back(new Item_empty_string("MESSAGE",MYSQL_ERRMSG_SIZE));
|
||||
field_list.push_back(new Item_empty_string("QUERY",NAME_LEN));
|
||||
|
||||
if (send_fields(thd,field_list,1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
mysql_st_error *err;
|
||||
SELECT_LEX *sel=&thd->lex.select_lex;
|
||||
ha_rows offset = sel->offset_limit,limit = sel->select_limit;
|
||||
uint num_rows = 0;
|
||||
|
||||
Error_iterator<mysql_st_error> it(thd->err_list);
|
||||
|
||||
while(offset-- && (err = it++));/* Should be fixed with overloaded
|
||||
operator '+' or with new funtion
|
||||
goto() in list ?
|
||||
*/
|
||||
|
||||
while((num_rows++ < limit) && (err = it++))
|
||||
{
|
||||
thd->packet.length(0);
|
||||
net_store_data(&thd->packet,(uint32)err->code);
|
||||
net_store_data(&thd->packet,err->msg);
|
||||
net_store_data(&thd->packet,err->query);
|
||||
|
||||
if (my_net_write(&thd->net,(char*)thd->packet.ptr(),thd->packet.length()))
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
send_eof(&thd->net);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Return errors count
|
||||
*/
|
||||
|
||||
int mysqld_show_errors_count(THD *thd)
|
||||
{
|
||||
List<Item> field_list;
|
||||
DBUG_ENTER("mysqld_show_errors_count");
|
||||
|
||||
field_list.push_back(new Item_int("COUNT(*)",0,4));
|
||||
|
||||
if (send_fields(thd,field_list,1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
thd->packet.length(0);
|
||||
net_store_data(&thd->packet,(uint32)thd->err_list.elements);
|
||||
|
||||
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
send_eof(&thd->net);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
List all warnings
|
||||
*/
|
||||
|
||||
int mysqld_show_warnings(THD *thd)
|
||||
{
|
||||
List<Item> field_list;
|
||||
DBUG_ENTER("mysqld_show_warnings");
|
||||
|
||||
field_list.push_back(new Item_int("CODE",0,21));
|
||||
field_list.push_back(new Item_empty_string("MESSAGE",MYSQL_ERRMSG_SIZE));
|
||||
field_list.push_back(new Item_empty_string("QUERY",NAME_LEN));
|
||||
|
||||
if (send_fields(thd,field_list,1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
mysql_st_error *warn;
|
||||
|
||||
|
||||
SELECT_LEX *sel=&thd->lex.select_lex;
|
||||
ha_rows offset = sel->offset_limit,limit = sel->select_limit;
|
||||
uint num_rows = 0;
|
||||
|
||||
Error_iterator<mysql_st_error> it(thd->warn_list);
|
||||
while(offset-- && (warn = it++));
|
||||
while((num_rows++ < limit) && (warn = it++))
|
||||
{
|
||||
thd->packet.length(0);
|
||||
net_store_data(&thd->packet,(uint32)warn->code);
|
||||
net_store_data(&thd->packet,warn->msg);
|
||||
net_store_data(&thd->packet,warn->query);
|
||||
|
||||
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
send_eof(&thd->net);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Return warnings count
|
||||
*/
|
||||
|
||||
int mysqld_show_warnings_count(THD *thd)
|
||||
{
|
||||
List<Item> field_list;
|
||||
DBUG_ENTER("mysqld_show_warnings_count");
|
||||
|
||||
field_list.push_back(new Item_int("COUNT(*)",0,21));
|
||||
|
||||
if (send_fields(thd,field_list,1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
thd->packet.length(0);
|
||||
net_store_data(&thd->packet,(uint32)thd->warn_list.elements);
|
||||
|
||||
if (my_net_write(&thd->net,(char*)thd->packet.ptr(),thd->packet.length()))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
send_eof(&thd->net);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ static void unlink_blobs(register TABLE *table);
|
||||
Resets form->time_stamp if a timestamp value is set
|
||||
*/
|
||||
|
||||
static int
|
||||
int
|
||||
check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
|
||||
List<Item> &values, ulong counter)
|
||||
{
|
||||
|
@ -57,8 +57,10 @@ enum enum_sql_command {
|
||||
SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ,
|
||||
SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_MULTI_UPDATE,
|
||||
SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO,
|
||||
SQLCOM_EMPTY_QUERY,
|
||||
SQLCOM_END
|
||||
SQLCOM_END, SQLCOM_SHOW_WARNS, SQLCOM_SHOW_WARNS_COUNT,
|
||||
SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
|
||||
SQLCOM_SHOW_ERRORS_COUNT, SQLCOM_SHOW_COLUMN_TYPES,
|
||||
SQLCOM_SHOW_TABLE_TYPES, SQLCOM_SHOW_PRIVILEGES
|
||||
};
|
||||
|
||||
enum lex_states { STATE_START, STATE_CHAR, STATE_IDENT,
|
||||
@ -318,6 +320,7 @@ typedef struct st_lex {
|
||||
List<Item> *insert_list,field_list,value_list;
|
||||
List<List_item> many_values;
|
||||
List<Set_option> option_list;
|
||||
List<Item> param_list;
|
||||
SQL_LIST proc_list, auxilliary_table_list;
|
||||
TYPELIB *interval;
|
||||
create_field *last_field;
|
||||
|
121
sql/sql_list.h
121
sql/sql_list.h
@ -122,11 +122,14 @@ public:
|
||||
last= &first;
|
||||
return tmp->info;
|
||||
}
|
||||
inline list_node* last_node() { return *last; }
|
||||
inline list_node* first_node() { return first;}
|
||||
inline void *head() { return first->info; }
|
||||
inline void **head_ref() { return first != &end_of_list ? &first->info : 0; }
|
||||
inline bool is_empty() { return first == &end_of_list ; }
|
||||
inline list_node *last_ref() { return &end_of_list; }
|
||||
friend class base_list_iterator;
|
||||
friend class error_list;
|
||||
|
||||
protected:
|
||||
void after(void *info,list_node *node)
|
||||
@ -204,6 +207,7 @@ public:
|
||||
{
|
||||
return el == &list->last_ref()->next;
|
||||
}
|
||||
friend class error_list_iterator;
|
||||
};
|
||||
|
||||
|
||||
@ -356,3 +360,120 @@ public:
|
||||
I_List_iterator(I_List<T> &a) : base_ilist_iterator(a) {}
|
||||
inline T* operator++(int) { return (T*) base_ilist_iterator::next(); }
|
||||
};
|
||||
|
||||
/*
|
||||
New error list without mem_root from THD, to have the life of the
|
||||
allocation becomes connection level . by ovveriding new from Sql_alloc.
|
||||
*/
|
||||
class Error_alloc
|
||||
{
|
||||
public:
|
||||
static void *operator new(size_t size)
|
||||
{
|
||||
return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE));
|
||||
}
|
||||
#if 0
|
||||
static void operator delete(void* ptr_arg, size_t size)
|
||||
{
|
||||
my_free((gptr)ptr_arg, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
|
||||
}
|
||||
#endif
|
||||
friend class error_node;
|
||||
friend class error_list;
|
||||
};
|
||||
|
||||
class error_node :public Error_alloc, public list_node
|
||||
{
|
||||
public:
|
||||
static void *operator new(size_t size)
|
||||
{
|
||||
return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE));
|
||||
}
|
||||
#if 0
|
||||
static void operator delete(void* ptr_arg, size_t size)
|
||||
{
|
||||
my_free((gptr)ptr_arg, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
|
||||
}
|
||||
#endif
|
||||
error_node(void *info_par,list_node *next_par):list_node(info_par,next_par){};
|
||||
error_node() : list_node() {};
|
||||
friend class error_list;
|
||||
friend class error_list_iterator;
|
||||
};
|
||||
|
||||
class error_list: public Error_alloc, public base_list
|
||||
{
|
||||
public:
|
||||
inline error_list() : base_list() { };
|
||||
inline error_list(const error_list &tmp) : Error_alloc()
|
||||
{
|
||||
elements=tmp.elements;
|
||||
first=tmp.first;
|
||||
last=tmp.last;
|
||||
}
|
||||
inline bool push_front(void *info)
|
||||
{
|
||||
error_node *node=new error_node(info,first);
|
||||
if (node)
|
||||
{
|
||||
if (last == &first)
|
||||
last= &node->next;
|
||||
first=node;
|
||||
elements++;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
inline void remove_last(void)
|
||||
{
|
||||
remove(last);
|
||||
}
|
||||
protected:
|
||||
void after(void *info,list_node *node)
|
||||
{
|
||||
error_node *new_node=new error_node(info,node->next);
|
||||
node->next=new_node;
|
||||
elements++;
|
||||
if (last == &(node->next))
|
||||
last= &new_node->next;
|
||||
}
|
||||
};
|
||||
|
||||
class error_list_iterator : public base_list_iterator
|
||||
{
|
||||
inline error_list_iterator(base_list &base_ptr): base_list_iterator(base_ptr) {};
|
||||
};
|
||||
|
||||
template <class T> class Error :public error_list
|
||||
{
|
||||
public:
|
||||
inline Error() :error_list() {}
|
||||
inline Error(const Error<T> &tmp) :error_list(tmp) {}
|
||||
inline bool push_back(T *a) { return error_list::push_back(a); }
|
||||
inline bool push_front(T *a) { return error_list::push_front(a); }
|
||||
inline T* head() {return (T*) error_list::head(); }
|
||||
inline T** head_ref() {return (T**) error_list::head_ref(); }
|
||||
inline T* pop() {return (T*) error_list::pop(); }
|
||||
void delete_elements(void)
|
||||
{
|
||||
error_node *element,*next;
|
||||
for (element=first; element != &error_end_of_list; element=next)
|
||||
{
|
||||
next=element->next;
|
||||
delete (T*) element->info;
|
||||
}
|
||||
empty();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> class Error_iterator :public base_list_iterator
|
||||
{
|
||||
public:
|
||||
Error_iterator(Error<T> &a) : base_list_iterator(a) {}
|
||||
inline T* operator++(int) { return (T*) base_list_iterator::next(); }
|
||||
inline T *replace(T *a) { return (T*) base_list_iterator::replace(a); }
|
||||
inline T *replace(Error<T> &a) { return (T*) base_list_iterator::replace(a); }
|
||||
inline void after(T *a) { base_list_iterator::after(a); }
|
||||
inline T** ref(void) { return (T**) base_list_iterator::ref(); }
|
||||
};
|
||||
|
||||
|
@ -65,7 +65,6 @@ static void decrease_user_connections(USER_CONN *uc);
|
||||
static bool check_db_used(THD *thd,TABLE_LIST *tables);
|
||||
static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables);
|
||||
static bool check_dup(const char *db, const char *name, TABLE_LIST *tables);
|
||||
void mysql_init_query(THD *thd);
|
||||
static void remove_escape(char *name);
|
||||
static void refresh_status(void);
|
||||
static bool append_file_to_dir(THD *thd, char **filename_ptr,
|
||||
@ -77,7 +76,8 @@ const char *command_name[]={
|
||||
"Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB",
|
||||
"Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist",
|
||||
"Connect","Kill","Debug","Ping","Time","Delayed_insert","Change user",
|
||||
"Binlog Dump","Table Dump", "Connect Out", "Register Slave"
|
||||
"Binlog Dump","Table Dump", "Connect Out", "Register Slave",
|
||||
"Prepare", "Prepare Execute", "Long Data"
|
||||
};
|
||||
|
||||
bool volatile abort_slave = 0;
|
||||
@ -486,7 +486,7 @@ check_connections(THD *thd)
|
||||
{
|
||||
/* buff[] needs to big enough to hold the server_version variable */
|
||||
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+32],*end;
|
||||
int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB;
|
||||
int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | CLIENT_PROTOCOL_41;
|
||||
|
||||
if (opt_using_transactions)
|
||||
client_flags|=CLIENT_TRANSACTIONS;
|
||||
@ -957,7 +957,21 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
thd->password=test(passwd[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
case COM_EXECUTE:
|
||||
{
|
||||
mysql_com_execute(thd);
|
||||
break;
|
||||
}
|
||||
case COM_LONG_DATA:
|
||||
{
|
||||
mysql_com_longdata(thd);
|
||||
break;
|
||||
}
|
||||
case COM_PREPARE:
|
||||
{
|
||||
mysql_com_prepare(thd,packet,packet_length);
|
||||
break;
|
||||
}
|
||||
case COM_QUERY:
|
||||
{
|
||||
packet_length--; // Remove end null
|
||||
@ -1380,6 +1394,26 @@ mysql_execute_command(void)
|
||||
res = purge_master_logs(thd, lex->to_log);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_WARNS_COUNT:
|
||||
{
|
||||
res = mysqld_show_warnings_count(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_WARNS:
|
||||
{
|
||||
res = mysqld_show_warnings(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_ERRORS_COUNT:
|
||||
{
|
||||
res = mysqld_show_errors_count(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_ERRORS:
|
||||
{
|
||||
res = mysqld_show_errors(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_NEW_MASTER:
|
||||
{
|
||||
if (check_access(thd, FILE_ACL, any_db))
|
||||
@ -2048,6 +2082,15 @@ mysql_execute_command(void)
|
||||
mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS :
|
||||
thd->priv_user,lex->verbose);
|
||||
break;
|
||||
case SQLCOM_SHOW_TABLE_TYPES:
|
||||
res= mysqld_show_table_types(thd);
|
||||
break;
|
||||
case SQLCOM_SHOW_PRIVILEGES:
|
||||
res= mysqld_show_privileges(thd);
|
||||
break;
|
||||
case SQLCOM_SHOW_COLUMN_TYPES:
|
||||
res= mysqld_show_column_types(thd);
|
||||
break;
|
||||
case SQLCOM_SHOW_STATUS:
|
||||
res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars);
|
||||
break;
|
||||
@ -2732,6 +2775,9 @@ mysql_init_query(THD *thd)
|
||||
thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
|
||||
thd->sent_row_count= thd->examined_row_count= 0;
|
||||
thd->safe_to_cache_query= 1;
|
||||
thd->param_count=0;
|
||||
thd->prepare_command=false;
|
||||
thd->lex.param_list.empty();
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
709
sql/sql_prepare.cc
Normal file
709
sql/sql_prepare.cc
Normal file
@ -0,0 +1,709 @@
|
||||
/* Copyright (C) 1995-2002 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/**********************************************************************
|
||||
This file contains the implementation of prepare and executes.
|
||||
|
||||
Prepare:
|
||||
|
||||
- Server gets the query from client with command 'COM_PREPARE'
|
||||
- Parse the query and recognize any parameter markers '?' and
|
||||
store its information list lex->param_list
|
||||
- Without executing the query, return back to client the total
|
||||
number of parameters along with result-set metadata information
|
||||
(if any )
|
||||
|
||||
Prepare-execute:
|
||||
|
||||
- Server gets the command 'COM_EXECUTE' to execute the
|
||||
previously prepared query.
|
||||
- If there is are any parameters, then replace the markers with the
|
||||
data supplied by client with the following format:
|
||||
[types_specified(0/1)][type][length][data] .. [type][length]..
|
||||
- Execute the query without re-parsing and send back the results
|
||||
to client
|
||||
|
||||
Long data handling:
|
||||
|
||||
- Server gets the long data in pieces with command type 'COM_LONG_DATA'.
|
||||
- The packet recieved will have the format as:
|
||||
[type_spec_exists][type][length][data]
|
||||
- Checks if the type is specified by client, and if yes reads the type,
|
||||
and stores the data in that format.
|
||||
- If length == MYSQL_END_OF_DATA, then server sets up the data read ended.
|
||||
***********************************************************************/
|
||||
|
||||
#include "mysql_priv.h"
|
||||
#include "sql_acl.h"
|
||||
#include <assert.h> // for DEBUG_ASSERT()
|
||||
#include <ctype.h> // for isspace()
|
||||
|
||||
/**************************************************************************/
|
||||
extern int yyparse(void);
|
||||
static ulong get_param_length(uchar **packet);
|
||||
static uint get_buffer_type(uchar **packet);
|
||||
static bool param_is_null(uchar **packet);
|
||||
static bool setup_param_fields(THD *thd,List<Item> ¶ms);
|
||||
static uchar* setup_param_field(Item_param *item_param, uchar *pos, uint buffer_type);
|
||||
static void setup_longdata_field(Item_param *item_param, uchar *pos);
|
||||
static bool setup_longdata(THD *thd,List<Item> ¶ms);
|
||||
static void send_prepare_results(THD *thd);
|
||||
static void mysql_parse_prepare_query(THD *thd,char *packet,uint length);
|
||||
static bool mysql_send_insert_fields(THD *thd,TABLE_LIST *table_list,
|
||||
List<Item> &fields,
|
||||
List<List_item> &values_list,thr_lock_type lock_type);
|
||||
static bool mysql_test_insert_fields(THD *thd,TABLE_LIST *table_list,
|
||||
List<Item> &fields,
|
||||
List<List_item> &values_list,thr_lock_type lock_type);
|
||||
static bool mysql_test_upd_fields(THD *thd,TABLE_LIST *table_list,
|
||||
List<Item> &fields, List<Item> &values,
|
||||
COND *conds,thr_lock_type lock_type);
|
||||
static bool mysql_test_select_fields(THD *thd, TABLE_LIST *tables,
|
||||
List<Item> &fields, List<Item> &values,
|
||||
COND *conds, ORDER *order, ORDER *group,
|
||||
Item *having,thr_lock_type lock_type);
|
||||
extern const char *any_db;
|
||||
/**************************************************************************/
|
||||
|
||||
/*
|
||||
Read the buffer type, this happens only first time
|
||||
*/
|
||||
|
||||
static uint get_buffer_type(uchar **packet)
|
||||
{
|
||||
reg1 uchar *pos= *packet;
|
||||
(*packet)+= 2;
|
||||
return (uint) uint2korr(pos);
|
||||
}
|
||||
|
||||
/*
|
||||
Check for NULL param data
|
||||
*/
|
||||
|
||||
static bool param_is_null(uchar **packet)
|
||||
{
|
||||
reg1 uchar *pos= *packet;
|
||||
if (*pos == 251)
|
||||
{
|
||||
(*packet)++;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Read the length of the parameter data and retun back to
|
||||
caller by positing the pointer to param data
|
||||
*/
|
||||
|
||||
static ulong get_param_length(uchar **packet)
|
||||
{
|
||||
reg1 uchar *pos= *packet;
|
||||
if (*pos < 251)
|
||||
{
|
||||
(*packet)++;
|
||||
return (ulong) *pos;
|
||||
}
|
||||
if (*pos == 252)
|
||||
{
|
||||
(*packet)+=3;
|
||||
return (ulong) uint2korr(pos+1);
|
||||
}
|
||||
if (*pos == 253)
|
||||
{
|
||||
(*packet)+=4;
|
||||
return (ulong) uint3korr(pos+1);
|
||||
}
|
||||
(*packet)+=9; // Must be 254 when here
|
||||
return (ulong) uint4korr(pos+1);
|
||||
}
|
||||
|
||||
/*
|
||||
Read and return the data for parameters supplied by client
|
||||
*/
|
||||
|
||||
static uchar* setup_param_field(Item_param *item_param,
|
||||
uchar *pos, uint buffer_type)
|
||||
{
|
||||
if (param_is_null(&pos))
|
||||
{
|
||||
item_param->set_null();
|
||||
return(pos);
|
||||
}
|
||||
switch (buffer_type)
|
||||
{
|
||||
case FIELD_TYPE_TINY:
|
||||
item_param->set_int((longlong)(*pos));
|
||||
pos += 1;
|
||||
break;
|
||||
case FIELD_TYPE_SHORT:
|
||||
item_param->set_int((longlong)sint2korr(pos));
|
||||
pos += 2;
|
||||
break;
|
||||
case FIELD_TYPE_INT24:
|
||||
item_param->set_int((longlong)sint4korr(pos));
|
||||
pos += 3;
|
||||
break;
|
||||
case FIELD_TYPE_LONG:
|
||||
item_param->set_int((longlong)sint4korr(pos));
|
||||
pos += 4;
|
||||
break;
|
||||
case FIELD_TYPE_LONGLONG:
|
||||
item_param->set_int((longlong)sint8korr(pos));
|
||||
pos += 8;
|
||||
break;
|
||||
case FIELD_TYPE_FLOAT:
|
||||
float data;
|
||||
float4get(data,pos);
|
||||
item_param->set_double(data);
|
||||
pos += 4;
|
||||
break;
|
||||
case FIELD_TYPE_DOUBLE:
|
||||
double j;
|
||||
float8get(j,pos)
|
||||
item_param->set_double(j);
|
||||
pos += 8;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
ulong len=get_param_length(&pos);
|
||||
item_param->set_value((const char*)pos,len);
|
||||
pos+=len;
|
||||
}
|
||||
}
|
||||
return(pos);
|
||||
}
|
||||
|
||||
/*
|
||||
Update the parameter markers by reading the data
|
||||
from client ..
|
||||
*/
|
||||
|
||||
static bool setup_param_fields(THD *thd, List<Item> ¶ms)
|
||||
{
|
||||
reg2 Item_param *item_param;
|
||||
List_iterator<Item> it(params);
|
||||
NET *net = &thd->net;
|
||||
DBUG_ENTER("setup_param_fields");
|
||||
|
||||
ulong param_count=0;
|
||||
uchar *pos=(uchar*)net->read_pos+1;// skip command type
|
||||
|
||||
if(*pos++) // No types supplied, read only param data
|
||||
{
|
||||
while ((item_param=(Item_param *)it++) &&
|
||||
(param_count++ < thd->param_count))
|
||||
{
|
||||
if (item_param->long_data_supplied)
|
||||
continue;
|
||||
|
||||
if (!(pos=setup_param_field(item_param,pos,item_param->buffer_type)))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
else // Types supplied, read and store it along with param data
|
||||
{
|
||||
while ((item_param=(Item_param *)it++) &&
|
||||
(param_count++ < thd->param_count))
|
||||
{
|
||||
if (item_param->long_data_supplied)
|
||||
continue;
|
||||
|
||||
if (!(pos=setup_param_field(item_param,pos,
|
||||
item_param->buffer_type=(enum_field_types)get_buffer_type(&pos))))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Buffer the long data and update the flags
|
||||
*/
|
||||
|
||||
static void setup_longdata_field(Item_param *item_param, uchar *pos)
|
||||
{
|
||||
ulong len;
|
||||
|
||||
if (!*pos++)
|
||||
item_param->buffer_type=(enum_field_types)get_buffer_type(&pos);
|
||||
|
||||
if (*pos == MYSQL_LONG_DATA_END)
|
||||
item_param->set_long_end();
|
||||
|
||||
else
|
||||
{
|
||||
len = get_param_length(&pos);
|
||||
item_param->set_longdata((const char *)pos, len);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Store the long data from client in pieces
|
||||
*/
|
||||
|
||||
static bool setup_longdata(THD *thd, List<Item> ¶ms)
|
||||
{
|
||||
NET *net=&thd->net;
|
||||
List_iterator<Item> it(params);
|
||||
DBUG_ENTER("setup_longdata");
|
||||
|
||||
uchar *pos=(uchar*)net->read_pos+1;// skip command type at first position
|
||||
ulong param_number = get_param_length(&pos);
|
||||
Item_param *item_param = thd->current_param;
|
||||
|
||||
if (thd->current_param_number != param_number)
|
||||
{
|
||||
thd->current_param_number = param_number;
|
||||
while (param_number--) /* TODO:
|
||||
Change this loop by either having operator '+'
|
||||
overloaded to point to desired 'item' or
|
||||
add another memeber in list as 'goto' with
|
||||
location count as parameter number, but what
|
||||
is the best way to traverse ?
|
||||
*/
|
||||
{
|
||||
it++;
|
||||
}
|
||||
thd->current_param = item_param = (Item_param *)it++;
|
||||
}
|
||||
setup_longdata_field(item_param,pos);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Validates insert fields
|
||||
*/
|
||||
|
||||
static int check_prepare_fields(THD *thd,TABLE *table, List<Item> &fields,
|
||||
List<Item> &values, ulong counter)
|
||||
{
|
||||
if (fields.elements == 0 && values.elements != 0)
|
||||
{
|
||||
if (values.elements != table->fields)
|
||||
{
|
||||
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
|
||||
ER(ER_WRONG_VALUE_COUNT_ON_ROW),
|
||||
MYF(0),counter);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fields.elements != values.elements)
|
||||
{
|
||||
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
|
||||
ER(ER_WRONG_VALUE_COUNT_ON_ROW),
|
||||
MYF(0),counter);
|
||||
return -1;
|
||||
}
|
||||
TABLE_LIST table_list;
|
||||
bzero((char*) &table_list,sizeof(table_list));
|
||||
table_list.name=table->table_name;
|
||||
table_list.table=table;
|
||||
table_list.grant=table->grant;
|
||||
|
||||
thd->dupp_field=0;
|
||||
if (setup_tables(&table_list) ||
|
||||
setup_fields(thd,&table_list,fields,1,0,0))
|
||||
return -1;
|
||||
if (thd->dupp_field)
|
||||
{
|
||||
my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Validate the following information for INSERT statement:
|
||||
- field existance
|
||||
- fields count
|
||||
|
||||
If there is no column list spec exists, then update the field_list
|
||||
with all columns from the table, and send fields info back to client
|
||||
*/
|
||||
|
||||
static bool mysql_test_insert_fields(THD *thd, TABLE_LIST *table_list,
|
||||
List<Item> &fields,
|
||||
List<List_item> &values_list,
|
||||
thr_lock_type lock_type)
|
||||
{
|
||||
TABLE *table;
|
||||
List_iterator_fast<List_item> its(values_list);
|
||||
List_item *values;
|
||||
DBUG_ENTER("mysql_test_insert_fields");
|
||||
|
||||
if (!(table = open_ltable(thd,table_list,lock_type)))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if ((values= its++))
|
||||
{
|
||||
uint value_count;
|
||||
ulong counter=0;
|
||||
|
||||
if (check_insert_fields(thd,table,fields,*values,1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
value_count= values->elements;
|
||||
its.rewind();
|
||||
|
||||
while ((values = its++))
|
||||
{
|
||||
counter++;
|
||||
if (values->elements != value_count)
|
||||
{
|
||||
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
|
||||
ER(ER_WRONG_VALUE_COUNT_ON_ROW),
|
||||
MYF(0),counter);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
if (fields.elements == 0)
|
||||
{
|
||||
/* No field listing, so setup all fields */
|
||||
List<Item> all_fields;
|
||||
Field **ptr,*field;
|
||||
for (ptr=table->field; (field= *ptr) ; ptr++)
|
||||
{
|
||||
all_fields.push_back(new Item_field(table->table_cache_key,
|
||||
table->real_name,
|
||||
field->field_name));
|
||||
}
|
||||
if ((setup_fields(thd,table_list,all_fields,1,0,0) ||
|
||||
send_fields(thd,all_fields,1)))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
else if (send_fields(thd,fields,1))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Validate the following information
|
||||
UPDATE - set and where clause DELETE - where clause
|
||||
|
||||
And send update-set cluase column list fields info
|
||||
back to client. For DELETE, just validate where cluase
|
||||
and return no fields information back to client.
|
||||
*/
|
||||
|
||||
static bool mysql_test_upd_fields(THD *thd, TABLE_LIST *table_list,
|
||||
List<Item> &fields, List<Item> &values,
|
||||
COND *conds, thr_lock_type lock_type)
|
||||
{
|
||||
TABLE *table;
|
||||
DBUG_ENTER("mysql_test_upd_fields");
|
||||
|
||||
if (!(table = open_ltable(thd,table_list,lock_type)))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0,0) ||
|
||||
setup_conds(thd,table_list,&conds))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/*
|
||||
Currently return only column list info only, and we are not
|
||||
sending any info on where clause.
|
||||
*/
|
||||
if (fields.elements && send_fields(thd,fields,1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Validate the following information:
|
||||
|
||||
SELECT - column list
|
||||
- where clause
|
||||
- orderr clause
|
||||
- having clause
|
||||
- group by clause
|
||||
- if no column spec i.e. '*', then setup all fields
|
||||
|
||||
And send column list fields info back to client.
|
||||
*/
|
||||
|
||||
static bool mysql_test_select_fields(THD *thd, TABLE_LIST *tables,
|
||||
List<Item> &fields, List<Item> &values,
|
||||
COND *conds, ORDER *order, ORDER *group,
|
||||
Item *having,thr_lock_type lock_type)
|
||||
{
|
||||
TABLE *table;
|
||||
bool hidden_group_fields;
|
||||
List<Item> all_fields(fields);
|
||||
DBUG_ENTER("mysql_test_select_fields");
|
||||
|
||||
if (!(table = open_ltable(thd,tables,lock_type)))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
thd->used_tables=0; // Updated by setup_fields
|
||||
if (setup_tables(tables) ||
|
||||
setup_fields(thd,tables,fields,1,&all_fields,1) ||
|
||||
setup_conds(thd,tables,&conds) ||
|
||||
setup_order(thd,tables,fields,all_fields,order) ||
|
||||
setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (having)
|
||||
{
|
||||
thd->where="having clause";
|
||||
thd->allow_sum_func=1;
|
||||
if (having->fix_fields(thd,tables) || thd->fatal_error)
|
||||
DBUG_RETURN(1);
|
||||
if (having->with_sum_func)
|
||||
having->split_sum_func(all_fields);
|
||||
}
|
||||
if (setup_ftfuncs(thd))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/*
|
||||
Currently return only column list info only, and we are not
|
||||
sending any info on where clause.
|
||||
*/
|
||||
if (fields.elements && send_fields(thd,fields,1))
|
||||
DBUG_RETURN(1);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Check the access privileges
|
||||
*/
|
||||
|
||||
static bool check_prepare_access(THD *thd, TABLE_LIST *tables,
|
||||
uint type)
|
||||
{
|
||||
if (check_access(thd,type,tables->db,&tables->grant.privilege))
|
||||
return 1;
|
||||
if (grant_option && check_grant(thd,type,tables))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Send the prepare query results back to client
|
||||
*/
|
||||
|
||||
static void send_prepare_results(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("send_prepare_results");
|
||||
enum enum_sql_command sql_command = thd->lex.sql_command;
|
||||
|
||||
DBUG_PRINT("enter",("command :%d, param_count :%ld",
|
||||
sql_command,thd->param_count));
|
||||
|
||||
LEX *lex=&thd->lex;
|
||||
SELECT_LEX *select_lex = lex->select;
|
||||
TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first;
|
||||
|
||||
switch(sql_command) {
|
||||
|
||||
case SQLCOM_INSERT:
|
||||
if (mysql_test_insert_fields(thd,tables, lex->field_list,
|
||||
lex->many_values, lex->lock_option))
|
||||
goto abort;
|
||||
break;
|
||||
|
||||
case SQLCOM_UPDATE:
|
||||
if (mysql_test_upd_fields(thd,tables, select_lex->item_list,
|
||||
lex->value_list, select_lex->where,
|
||||
lex->lock_option))
|
||||
goto abort;
|
||||
break;
|
||||
|
||||
case SQLCOM_DELETE:
|
||||
if (mysql_test_upd_fields(thd,tables, select_lex->item_list,
|
||||
lex->value_list, select_lex->where,
|
||||
lex->lock_option))
|
||||
goto abort;
|
||||
break;
|
||||
|
||||
case SQLCOM_SELECT:
|
||||
if (mysql_test_select_fields(thd,tables, select_lex->item_list,
|
||||
lex->value_list, select_lex->where,
|
||||
(ORDER*) select_lex->order_list.first,
|
||||
(ORDER*) select_lex->group_list.first,
|
||||
select_lex->having, lex->lock_option))
|
||||
goto abort;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
/*
|
||||
Rest fall through to default category, no parsing
|
||||
for non-DML statements
|
||||
*/
|
||||
}
|
||||
}
|
||||
send_ok(&thd->net,thd->param_count,0);
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
abort:
|
||||
send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN : 0);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
Parse the prepare query
|
||||
*/
|
||||
|
||||
static void mysql_parse_prepare_query(THD *thd, char *packet, uint length)
|
||||
{
|
||||
DBUG_ENTER("mysql_parse_prepare_query");
|
||||
|
||||
mysql_log.write(thd,COM_PREPARE,"%s",packet);
|
||||
mysql_init_query(thd);
|
||||
thd->prepare_command=true;
|
||||
|
||||
if (query_cache.send_result_to_client(thd, packet, length) <= 0)
|
||||
{
|
||||
LEX *lex=lex_start(thd, (uchar*)packet, length);
|
||||
|
||||
if (!yyparse() && !thd->fatal_error)
|
||||
{
|
||||
send_prepare_results(thd);
|
||||
query_cache_end_of_result(&thd->net);
|
||||
}
|
||||
else
|
||||
query_cache_abort(&thd->net);
|
||||
lex_end(lex);
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
Parse the query and send the total number of parameters
|
||||
and resultset metadata information back to client (if any),
|
||||
without executing the query i.e. with out any log/disk
|
||||
writes. This will allow the queries to be re-executed
|
||||
without re-parsing during execute.
|
||||
|
||||
If parameter markers are found in the query, then store
|
||||
the information using Item_param along with maintaining a
|
||||
list in lex->param_list, so that a fast and direct
|
||||
retrieveal can be made without going through all field
|
||||
items.
|
||||
*/
|
||||
|
||||
void mysql_com_prepare(THD *thd, char *packet, uint packet_length)
|
||||
{
|
||||
MEM_ROOT thd_root = thd->mem_root;
|
||||
DBUG_ENTER("mysql_com_prepare");
|
||||
|
||||
packet_length--;
|
||||
|
||||
while (isspace(packet[0]) && packet_length > 0)
|
||||
{
|
||||
packet++;
|
||||
packet_length--;
|
||||
}
|
||||
char *pos=packet+packet_length;
|
||||
while (packet_length > 0 && (pos[-1] == ';' || isspace(pos[-1])))
|
||||
{
|
||||
pos--;
|
||||
packet_length--;
|
||||
}
|
||||
/*
|
||||
Have the prepare items to have a connection level scope or
|
||||
till next prepare statement by doing all allocations using
|
||||
connection level memory allocator 'con_root' from THD.
|
||||
*/
|
||||
free_root(&thd->con_root,MYF(0));
|
||||
init_sql_alloc(&thd->con_root,8192,8192);
|
||||
thd->mem_root = thd->con_root;
|
||||
|
||||
if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
|
||||
packet_length,
|
||||
thd->db_length+2)))
|
||||
DBUG_VOID_RETURN;
|
||||
thd->query[packet_length]=0;
|
||||
thd->packet.shrink(net_buffer_length);
|
||||
thd->query_length = packet_length;
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
|
||||
mysql_parse_prepare_query(thd,thd->query,packet_length);
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),WAIT_PRIOR);
|
||||
|
||||
thd->mem_root = thd_root; // restore main mem_root
|
||||
DBUG_PRINT("exit",("prepare query ready"));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Executes previously prepared query
|
||||
|
||||
If there is any parameters(thd->param_count), then replace
|
||||
markers with the data supplied from client, and then
|
||||
execute the query
|
||||
*/
|
||||
|
||||
void mysql_com_execute(THD *thd)
|
||||
{
|
||||
MEM_ROOT thd_root=thd->mem_root;
|
||||
DBUG_ENTER("mysql_com_execute");
|
||||
DBUG_PRINT("enter", ("parameters : %ld", thd->param_count));
|
||||
|
||||
thd->mem_root = thd->con_root;
|
||||
if (thd->param_count && setup_param_fields(thd, thd->lex.param_list))
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
|
||||
/* TODO:
|
||||
Also, have checks on basic executions such as mysql_insert(),
|
||||
mysql_delete(), mysql_update() and mysql_select() to not to
|
||||
have re-check on setup_* and other things ..
|
||||
*/
|
||||
mysql_execute_command();
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),WAIT_PRIOR);
|
||||
|
||||
thd->mem_root = (MEM_ROOT )thd_root;
|
||||
DBUG_PRINT("exit",("prepare-execute done!"));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
Long data in pieces from client
|
||||
*/
|
||||
|
||||
void mysql_com_longdata(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("mysql_com_execute");
|
||||
|
||||
if(thd->param_count && setup_longdata(thd,thd->lex.param_list))
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
send_ok(&thd->net,0,0);// ok status to client
|
||||
DBUG_PRINT("exit",("longdata-buffering done!"));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
@ -126,8 +126,6 @@ static bool store_record_in_cache(JOIN_CACHE *cache);
|
||||
static void reset_cache(JOIN_CACHE *cache);
|
||||
static void read_cached_record(JOIN_TAB *tab);
|
||||
static bool cmp_buffer_with_ref(JOIN_TAB *tab);
|
||||
static int setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields,
|
||||
List<Item> &all_fields, ORDER *order, bool *hidden);
|
||||
static bool setup_new_fields(THD *thd,TABLE_LIST *tables,List<Item> &fields,
|
||||
List<Item> &all_fields,ORDER *new_order);
|
||||
static ORDER *create_distinct_group(ORDER *order, List<Item> &fields);
|
||||
@ -6456,7 +6454,7 @@ int setup_order(THD *thd,TABLE_LIST *tables,List<Item> &fields,
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
int
|
||||
setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields,
|
||||
List<Item> &all_fields, ORDER *order, bool *hidden_group_fields)
|
||||
{
|
||||
|
195
sql/sql_show.cc
195
sql/sql_show.cc
@ -169,6 +169,201 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild)
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** List all table types supported
|
||||
***************************************************************************/
|
||||
|
||||
static struct show_table_type_st sys_table_types[]= {
|
||||
{"MyISAM", (char *)"YES", "Default type from 3.23 with great performance"},
|
||||
{"HEAP" , (char *)"YES", "Hash based, stored in memory, useful for temporary tables"},
|
||||
{"MERGE", (char *)"YES", "Collection of identical MyISAM tables"},
|
||||
{"ISAM", (char*) &have_isam,"Obsolete table type"},
|
||||
{"InnoDB", (char*) &have_innodb,"Supports transactions, row-level locking and foreign keys"},
|
||||
{"BDB", (char*) &have_berkeley_db, "Supports transactions and page-level locking"},
|
||||
};
|
||||
|
||||
int mysqld_show_table_types(THD *thd)
|
||||
{
|
||||
List<Item> field_list;
|
||||
DBUG_ENTER("mysqld_show_table_types");
|
||||
|
||||
field_list.push_back(new Item_empty_string("Type",10));
|
||||
field_list.push_back(new Item_empty_string("Support",10));
|
||||
field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
|
||||
|
||||
if (send_fields(thd,field_list,1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
const char *default_type_name=ha_table_typelib.type_names[default_table_type-1];
|
||||
show_table_type_st *types = sys_table_types;
|
||||
|
||||
uint i;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
thd->packet.length(0);
|
||||
net_store_data(&thd->packet,types[i].type);
|
||||
if (!strcasecmp(default_type_name,types[i].type))
|
||||
net_store_data(&thd->packet,"DEFAULT");
|
||||
else
|
||||
net_store_data(&thd->packet,types[i].value);
|
||||
net_store_data(&thd->packet,types[i].comment);
|
||||
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
for (; i < sizeof(sys_table_types)/sizeof(sys_table_types[0]); i++)
|
||||
{
|
||||
thd->packet.length(0);
|
||||
net_store_data(&thd->packet,types[i].type);
|
||||
SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) types[i].value;
|
||||
|
||||
if (tmp == SHOW_OPTION_NO)
|
||||
net_store_data(&thd->packet,"NO");
|
||||
else
|
||||
{
|
||||
if (tmp == SHOW_OPTION_YES)
|
||||
{
|
||||
if (!strcasecmp(default_type_name,types[i].type))
|
||||
net_store_data(&thd->packet,"DEFAULT");
|
||||
else
|
||||
net_store_data(&thd->packet,"YES");
|
||||
}
|
||||
else net_store_data(&thd->packet,"DISABLED");
|
||||
}
|
||||
net_store_data(&thd->packet,types[i].comment);
|
||||
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
send_eof(&thd->net);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** List all privileges supported
|
||||
***************************************************************************/
|
||||
|
||||
static struct show_table_type_st sys_privileges[]= {
|
||||
{"Select", (char *)"Tables", "To retrieve rows from table"},
|
||||
{"Insert", (char *)"Tables", "To insert data into tables"},
|
||||
{"Update", (char *)"Tables", "To update existing rows "},
|
||||
{"Delete", (char *)"Tables", "To delete existing rows"},
|
||||
{"Index", (char *)"Tables", "To create or drop indexes"},
|
||||
{"Alter", (char *)"Tables", "To alter the table"},
|
||||
{"Create", (char *)"Databases,Tables,Indexes", "To create new databases and tables"},
|
||||
{"Drop", (char *)"Databases,Tables", "To drop databases and tables"},
|
||||
{"Grant", (char *)"Databases,Tables", "To give to other users those privileges you possesed"},
|
||||
{"References", (char *)"Databases,Tables", "To have references on tables"},
|
||||
{"Reload", (char *)"Server Admin", "To reload or refresh tables, logs and privileges"},
|
||||
{"Shutdown",(char *)"Server Admin", "To shutdown the server"},
|
||||
{"Process", (char *)"Server Admin", "To view the plain text of currently executing queries"},
|
||||
{"File", (char *)"File access on server", "To read and write files on the server"},
|
||||
};
|
||||
|
||||
int mysqld_show_privileges(THD *thd)
|
||||
{
|
||||
List<Item> field_list;
|
||||
DBUG_ENTER("mysqld_show_privileges");
|
||||
|
||||
field_list.push_back(new Item_empty_string("Privilege",10));
|
||||
field_list.push_back(new Item_empty_string("Context",15));
|
||||
field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
|
||||
|
||||
if (send_fields(thd,field_list,1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
for (uint i=0; i < sizeof(sys_privileges)/sizeof(sys_privileges[0]); i++)
|
||||
{
|
||||
thd->packet.length(0);
|
||||
net_store_data(&thd->packet,sys_privileges[i].type);
|
||||
net_store_data(&thd->packet,sys_privileges[i].value);
|
||||
net_store_data(&thd->packet,sys_privileges[i].comment);
|
||||
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
send_eof(&thd->net);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** List all column types
|
||||
***************************************************************************/
|
||||
|
||||
#if 0
|
||||
struct show_column_type_st {
|
||||
const char *type;
|
||||
uint size;
|
||||
char *min_value;
|
||||
char *max_value;
|
||||
uint precision,
|
||||
uint scale,
|
||||
char *nullable;
|
||||
char *auto_increment;
|
||||
char *unsigned_attr;
|
||||
char *zerofill;
|
||||
char *searchable;
|
||||
char *case_sensitivity;
|
||||
char *default_value;
|
||||
char *comment;
|
||||
};
|
||||
#endif
|
||||
static struct show_column_type_st sys_column_types[]= {
|
||||
{"tinyint",
|
||||
1, "-128", "127", 0, 0, "YES", "YES",
|
||||
"NO", "YES", "YES", "NO", "NULL,0",
|
||||
"A very small integer"},
|
||||
{"tinyint unsigned",
|
||||
1, "0" , "255", 0, 0, "YES", "YES",
|
||||
"YES", "YES", "YES", "NO", "NULL,0",
|
||||
"A very small integer"},
|
||||
};
|
||||
|
||||
int mysqld_show_column_types(THD *thd)
|
||||
{
|
||||
List<Item> field_list;
|
||||
DBUG_ENTER("mysqld_show_column_types");
|
||||
|
||||
field_list.push_back(new Item_empty_string("Type",30));
|
||||
field_list.push_back(new Item_int("Size",(longlong) 1,21));
|
||||
field_list.push_back(new Item_empty_string("Min_Value",20));
|
||||
field_list.push_back(new Item_empty_string("Max_Value",20));
|
||||
field_list.push_back(new Item_int("Prec", 0,4));
|
||||
field_list.push_back(new Item_int("Scale", 0,4));
|
||||
field_list.push_back(new Item_empty_string("Nullable",4));
|
||||
field_list.push_back(new Item_empty_string("Auto_Increment",4));
|
||||
field_list.push_back(new Item_empty_string("Unsigned",4));
|
||||
field_list.push_back(new Item_empty_string("Zerofill",4));
|
||||
field_list.push_back(new Item_empty_string("Searchable",4));
|
||||
field_list.push_back(new Item_empty_string("Case_Sensitive",4));
|
||||
field_list.push_back(new Item_empty_string("Default",NAME_LEN));
|
||||
field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
|
||||
|
||||
if (send_fields(thd,field_list,1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
for (uint i=0; i < sizeof(sys_column_types)/sizeof(sys_column_types[0]); i++)
|
||||
{
|
||||
thd->packet.length(0);
|
||||
net_store_data(&thd->packet,sys_column_types[i].type);
|
||||
net_store_data(&thd->packet,(longlong)sys_column_types[i].size);
|
||||
net_store_data(&thd->packet,sys_column_types[i].min_value);
|
||||
net_store_data(&thd->packet,sys_column_types[i].max_value);
|
||||
net_store_data(&thd->packet,(uint32)sys_column_types[i].precision);
|
||||
net_store_data(&thd->packet,(uint32)sys_column_types[i].scale);
|
||||
net_store_data(&thd->packet,sys_column_types[i].nullable);
|
||||
net_store_data(&thd->packet,sys_column_types[i].auto_increment);
|
||||
net_store_data(&thd->packet,sys_column_types[i].unsigned_attr);
|
||||
net_store_data(&thd->packet,sys_column_types[i].zerofill);
|
||||
net_store_data(&thd->packet,sys_column_types[i].searchable);
|
||||
net_store_data(&thd->packet,sys_column_types[i].case_sensitivity);
|
||||
net_store_data(&thd->packet,sys_column_types[i].default_value);
|
||||
net_store_data(&thd->packet,sys_column_types[i].comment);
|
||||
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
send_eof(&thd->net);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
static int
|
||||
mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
|
||||
|
@ -324,6 +324,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
||||
%token TRAILING
|
||||
%token TRANSACTION_SYM
|
||||
%token TYPE_SYM
|
||||
%token TYPES_SYM
|
||||
%token FUNC_ARG0
|
||||
%token FUNC_ARG1
|
||||
%token FUNC_ARG2
|
||||
@ -346,6 +347,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
||||
%token X509_SYM
|
||||
%token COMPRESSED_SYM
|
||||
|
||||
%token ERRORS
|
||||
%token SQL_ERROR_COUNT
|
||||
%token WARNINGS
|
||||
%token SQL_WARNING_COUNT
|
||||
|
||||
%token BIGINT
|
||||
%token BLOB_SYM
|
||||
%token CHAR_SYM
|
||||
@ -548,7 +554,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 subselect subselect_init
|
||||
using_list param_marker subselect subselect_init
|
||||
|
||||
%type <item_list>
|
||||
expr_list udf_expr_list when_list ident_list ident_list_arg
|
||||
@ -1720,6 +1726,7 @@ no_and_expr:
|
||||
simple_expr:
|
||||
simple_ident
|
||||
| literal
|
||||
| param_marker
|
||||
| '@' ident_or_text SET_VAR expr
|
||||
{ $$= new Item_func_set_user_var($2,$4);
|
||||
current_thd->safe_to_cache_query=0;
|
||||
@ -2795,6 +2802,29 @@ show_param:
|
||||
if (!add_table_to_list($3,NULL,0))
|
||||
YYABORT;
|
||||
}
|
||||
| COLUMN_SYM TYPES_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->sql_command= SQLCOM_SHOW_COLUMN_TYPES;
|
||||
}
|
||||
| TABLE_SYM TYPES_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->sql_command= SQLCOM_SHOW_TABLE_TYPES;
|
||||
}
|
||||
| PRIVILEGES
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->sql_command= SQLCOM_SHOW_PRIVILEGES;
|
||||
}
|
||||
| COUNT_SYM '(' '*' ')' WARNINGS
|
||||
{ Lex->sql_command = SQLCOM_SHOW_WARNS_COUNT;}
|
||||
| COUNT_SYM '(' '*' ')' ERRORS
|
||||
{ Lex->sql_command = SQLCOM_SHOW_ERRORS_COUNT;}
|
||||
| WARNINGS {Select->offset_limit=0L;} limit_clause
|
||||
{ Lex->sql_command = SQLCOM_SHOW_WARNS;}
|
||||
| ERRORS {Select->offset_limit=0L;} limit_clause
|
||||
{ Lex->sql_command = SQLCOM_SHOW_ERRORS;}
|
||||
| STATUS_SYM wild
|
||||
{ Lex->sql_command= SQLCOM_SHOW_STATUS; }
|
||||
| opt_full PROCESSLIST_SYM
|
||||
@ -3055,7 +3085,20 @@ text_string:
|
||||
Item *tmp = new Item_varbinary($1.str,$1.length,default_charset_info);
|
||||
$$= tmp ? tmp->val_str((String*) 0) : (String*) 0;
|
||||
};
|
||||
|
||||
param_marker:
|
||||
'?'
|
||||
{
|
||||
if(current_thd->prepare_command)
|
||||
{
|
||||
Lex->param_list.push_back($$=new Item_param());
|
||||
current_thd->param_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
yyerror("You have an error in your SQL syntax");
|
||||
YYABORT;
|
||||
}
|
||||
}
|
||||
literal:
|
||||
text_literal { $$ = $1; }
|
||||
| NUM { $$ = new Item_int($1.str, (longlong) atol($1.str),$1.length); }
|
||||
@ -3411,6 +3454,16 @@ option_value:
|
||||
YYABORT;
|
||||
}
|
||||
| SQL_QUERY_CACHE_TYPE_SYM equal query_cache_type
|
||||
| SQL_ERROR_COUNT equal ULONG_NUM
|
||||
{
|
||||
LEX *lex = Lex;
|
||||
lex->thd->max_error_count = $3;
|
||||
}
|
||||
| SQL_WARNING_COUNT equal ULONG_NUM
|
||||
{
|
||||
LEX *lex = Lex;
|
||||
lex->thd->max_warning_count = $3;
|
||||
}
|
||||
| '@' ident_or_text equal expr
|
||||
{
|
||||
Item_func_set_user_var *item = new Item_func_set_user_var($2,$4);
|
||||
|
@ -152,6 +152,29 @@ struct show_var_st {
|
||||
SHOW_TYPE type;
|
||||
};
|
||||
|
||||
struct show_table_type_st {
|
||||
const char *type;
|
||||
char *value;
|
||||
const char *comment;
|
||||
};
|
||||
|
||||
struct show_column_type_st {
|
||||
const char *type;
|
||||
uint size;
|
||||
const char *min_value;
|
||||
const char *max_value;
|
||||
uint precision;
|
||||
uint scale;
|
||||
const char *nullable;
|
||||
const char *auto_increment;
|
||||
const char *unsigned_attr;
|
||||
const char *zerofill;
|
||||
const char *searchable;
|
||||
const char *case_sensitivity;
|
||||
const char *default_value;
|
||||
const char *comment;
|
||||
};
|
||||
|
||||
typedef struct lex_string {
|
||||
char *str;
|
||||
uint length;
|
||||
|
Loading…
x
Reference in New Issue
Block a user