diff options
author | unknown <venu@myvenu.com> | 2002-06-12 14:13:12 -0700 |
---|---|---|
committer | unknown <venu@myvenu.com> | 2002-06-12 14:13:12 -0700 |
commit | 6cdebb33d7849adb90015ecd4a7312de81200de0 (patch) | |
tree | c259cc4fb9ea2857ea92e18b183d51a0289b2270 | |
parent | 049a8386f3ce99e689f1dc3e562c29e7d8113e71 (diff) | |
download | mariadb-git-6cdebb33d7849adb90015ecd4a7312de81200de0.tar.gz |
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
-rw-r--r-- | sql/Makefile.am | 1 | ||||
-rw-r--r-- | sql/field.cc | 8 | ||||
-rw-r--r-- | sql/field.h | 4 | ||||
-rw-r--r-- | sql/item.cc | 139 | ||||
-rw-r--r-- | sql/item.h | 34 | ||||
-rw-r--r-- | sql/item_sum.cc | 5 | ||||
-rw-r--r-- | sql/lex.h | 4 | ||||
-rw-r--r-- | sql/mysql_priv.h | 28 | ||||
-rw-r--r-- | sql/net_pkg.cc | 11 | ||||
-rw-r--r-- | sql/sql_base.cc | 188 | ||||
-rw-r--r-- | sql/sql_class.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.h | 36 | ||||
-rw-r--r-- | sql/sql_error.cc | 219 | ||||
-rw-r--r-- | sql/sql_insert.cc | 2 | ||||
-rw-r--r-- | sql/sql_lex.h | 7 | ||||
-rw-r--r-- | sql/sql_list.h | 121 | ||||
-rw-r--r-- | sql/sql_parse.cc | 54 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 709 | ||||
-rw-r--r-- | sql/sql_select.cc | 4 | ||||
-rw-r--r-- | sql/sql_show.cc | 195 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 57 | ||||
-rw-r--r-- | sql/structs.h | 23 |
22 files changed, 1794 insertions, 58 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index bd626ea10b7..81fe927ce5a 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -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 \ diff --git a/sql/field.cc b/sql/field.cc index 1561f1831de..8b1073d32f3 100644 --- a/sql/field.cc +++ b/sql/field.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; diff --git a/sql/field.h b/sql/field.h index f84b54271ce..5b9723654d9 100644 --- a/sql/field.h +++ b/sql/field.h @@ -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() {} diff --git a/sql/item.cc b/sql/item.cc index 45564bcd98e..d01a06e5eba 100644 --- a/sql/item.cc +++ b/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; diff --git a/sql/item.h b/sql/item.h index 8e8b44a5006..e6debdf2afc 100644 --- a/sql/item.h +++ b/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 { diff --git a/sql/item_sum.cc b/sql/item_sum.cc index a1ffae2ed82..6ef968c33f7 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -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) diff --git a/sql/lex.h b/sql/lex.h index dd1a69f9c83..da0a71a135e 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -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}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 268a452095e..011b7823dd0 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -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]; diff --git a/sql/net_pkg.cc b/sql/net_pkg.cc index a201651fc2f..fa3abc68bfa 100644 --- a/sql/net_pkg.cc +++ b/sql/net_pkg.cc @@ -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) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index c9ef64b6da9..92606716c63 100644 --- a/sql/sql_base.cc +++ b/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 */ } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 86e4e6896e6..f91c508e8af 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -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 diff --git a/sql/sql_class.h b/sql/sql_class.h index 8a1a299ceee..016df485245 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -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 diff --git a/sql/sql_error.cc b/sql/sql_error.cc new file mode 100644 index 00000000000..13466f454c5 --- /dev/null +++ b/sql/sql_error.cc @@ -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); +} + diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index c181e299d05..c3aeca1fff8 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -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) { diff --git a/sql/sql_lex.h b/sql/sql_lex.h index ee0209f9329..ca824b3eab8 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -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; diff --git a/sql/sql_list.h b/sql/sql_list.h index 542eef623f0..541db4ec166 100644 --- a/sql/sql_list.h +++ b/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(); } +}; + diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a5d8aa3185e..70f50483114 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -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; } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc new file mode 100644 index 00000000000..d5896901935 --- /dev/null +++ b/sql/sql_prepare.cc @@ -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; +} + diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c10174a7b71..4737e068d3e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -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) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 1421eac168f..48d6bc7471f 100644 --- a/sql/sql_show.cc +++ b/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, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 25d8dbbab16..d140cd4dcdd 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -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); diff --git a/sql/structs.h b/sql/structs.h index 75280b34715..c4cb6c82209 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -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; |