Commit 5a33842a authored by venu@myvenu.com's avatar venu@myvenu.com

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
parent 75959e6e
......@@ -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() {}
......
......@@ -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;
......
......@@ -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)
......
......@@ -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
......
/* 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;
......
......@@ -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;
}
......
/* 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> &params);
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> &params);
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> &params)
{
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> &params)
{
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)
{
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment