From f638e5cb8f9956b92c0f72fc48d6a297d7fc393e Mon Sep 17 00:00:00 2001 From: "acurtis@xiphis.org" <> Date: Tue, 19 Apr 2005 09:09:25 +0100 Subject: [PATCH 1/4] Bug#9102 - Stored proccedures: function which returns blob causes crash Initialization of fields for sp return type was not complete. --- mysql-test/r/sp.result | 6 ++++ mysql-test/t/sp.test | 9 ++++++ sql/mysql_priv.h | 1 + sql/sp_head.cc | 26 +++++++++++++++- sql/sql_table.cc | 71 ++++++++++++++++++++++++++++++++++++++++++ sql/sql_yacc.yy | 14 +++++++-- 6 files changed, 124 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 4bb1640f0eb..4cc59679ce5 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -2958,4 +2958,10 @@ select @x| set global query_cache_size = @qcs1| delete from t1| drop function bug9902| +drop function if exists bug9102| +create function bug9102() returns blob return 'a'| +select bug9102(); +drop function bug9102| +bug9102() +a drop table t1,t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 4101a7a4bfa..3934e8ad17a 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -3627,6 +3627,15 @@ set global query_cache_size = @qcs1| delete from t1| drop function bug9902| +# +# BUG#9102: New bug synopsis +# +--disable_warnings +drop function if exists bug9102| +--enable_warnings +create function bug9102() returns blob return 'a'| +select bug9102(); +drop function bug9102| # # BUG#NNNN: New bug synopsis diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index a854f8c45d3..56fbd993aed 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -647,6 +647,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item ***copy_func, Field **from_field, bool group, bool modify_item, uint convert_blob_length); +void sp_prepare_create_field(THD *thd, create_field *sql_field); int prepare_create_field(create_field *sql_field, uint *blob_columns, int *timestamps, int *timestamps_with_niladic, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 0fe9c449540..d1486cb234e 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -370,6 +370,7 @@ TYPELIB * sp_head::create_typelib(List *src) { TYPELIB *result= NULL; + CHARSET_INFO *cs= m_returns_cs; DBUG_ENTER("sp_head::clone_typelib"); if (src->elements) { @@ -380,8 +381,31 @@ sp_head::create_typelib(List *src) alloc_root(mem_root,sizeof(char *)*(result->count+1)))) return 0; List_iterator it(*src); + String conv, *tmp; + uint32 dummy; for (uint i=0; icount; i++) - result->type_names[i]= strdup_root(mem_root, (it++)->c_ptr()); + { + tmp = it++; + if (String::needs_conversion(tmp->length(), tmp->charset(), + cs, &dummy)) + { + uint cnv_errs; + conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs); + char *buf= (char*) alloc_root(mem_root,conv.length()+1); + memcpy(buf, conv.ptr(), conv.length()); + buf[conv.length()]= '\0'; + result->type_names[i]= buf; + result->type_lengths[i]= conv.length(); + } + else + result->type_names[i]= strdup_root(mem_root, tmp->c_ptr()); + + // Strip trailing spaces. + uint lengthsp= cs->cset->lengthsp(cs, result->type_names[i], + result->type_lengths[i]); + result->type_lengths[i]= lengthsp; + ((uchar *)result->type_names[i])[lengthsp]= '\0'; + } result->type_names[result->count]= 0; } return result; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8753f62ab89..18c90d549ec 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1351,6 +1351,77 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, } +/* + Preparation of create_field for SP function return values. + Based on code used in the inner loop of mysql_prepare_table() above + + SYNOPSIS + sp_prepare_create_field() + thd Thread object + sql_field Field to prepare + + DESCRIPTION + Prepares the field structures for field creation. + +*/ + +void sp_prepare_create_field(THD *thd, create_field *sql_field) +{ + if (sql_field->sql_type == FIELD_TYPE_SET || + sql_field->sql_type == FIELD_TYPE_ENUM) + { + uint32 field_length, dummy; + if (sql_field->sql_type == FIELD_TYPE_SET) + { + calculate_interval_lengths(sql_field->charset, + sql_field->interval, &dummy, + &field_length); + sql_field->length= field_length + + (sql_field->interval->count - 1); + } + else /* FIELD_TYPE_ENUM */ + { + calculate_interval_lengths(sql_field->charset, + sql_field->interval, + &field_length, &dummy); + sql_field->length= field_length; + } + set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1); + } + + if (sql_field->sql_type == FIELD_TYPE_BIT) + { + sql_field->pack_flag= FIELDFLAG_NUMBER | + FIELDFLAG_TREAT_BIT_AS_CHAR; + } + sql_field->create_length_to_internal_length(); + + if (sql_field->length > MAX_FIELD_VARCHARLENGTH && + !(sql_field->flags & BLOB_FLAG)) + { + /* Convert long VARCHAR columns to TEXT or BLOB */ + char warn_buff[MYSQL_ERRMSG_SIZE]; + + sql_field->sql_type= FIELD_TYPE_BLOB; + sql_field->flags|= BLOB_FLAG; + sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name, + "VARCHAR", + (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT"); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT, + warn_buff); + } + + if ((sql_field->flags & BLOB_FLAG) && sql_field->length) + { + if (sql_field->sql_type == FIELD_TYPE_BLOB) + { + /* The user has given a length to the blob column */ + sql_field->sql_type= get_blob_type_from_length(sql_field->length); + sql_field->pack_length= calc_pack_length(sql_field->sql_type, 0); + } + sql_field->length= 0; // Probably from an item + } +} /* Create a table diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9aa5d7fb4fc..40529312493 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1407,6 +1407,16 @@ create_function_tail: lex->uint_geom_type))) YYABORT; + sp->m_returns_cs= new_field->charset; + + if (new_field->sql_type == FIELD_TYPE_SET || + new_field->sql_type == FIELD_TYPE_ENUM) + { + new_field->interval= + sp->create_typelib(&new_field->interval_list); + } + sp_prepare_create_field(YYTHD, new_field); + if (prepare_create_field(new_field, &unused1, &unused2, &unused2, 0)) YYABORT; @@ -1415,8 +1425,8 @@ create_function_tail: sp->m_returns_cs= new_field->charset; sp->m_returns_len= new_field->length; sp->m_returns_pack= new_field->pack_flag; - sp->m_returns_typelib= - sp->create_typelib(&new_field->interval_list); + sp->m_returns_typelib= new_field->interval; + new_field->interval= NULL; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } From 77a9429c134ddf6eaf3936286280e216b2be03af Mon Sep 17 00:00:00 2001 From: "acurtis@xiphis.org" <> Date: Tue, 19 Apr 2005 10:51:11 +0100 Subject: [PATCH 2/4] Bug#7648 - Stored procedure crash when invoking a function that returns a bit bugfix 9102 corrected the crashing, this corrects the result. --- mysql-test/r/sp.result | 6 ++++++ mysql-test/t/sp.test | 17 +++++++++++++++-- sql/item_func.h | 12 +++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 4cc59679ce5..a4a3968672f 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -2964,4 +2964,10 @@ select bug9102(); drop function bug9102| bug9102() a +drop procedure if exists bug7648| +create function bug7648() returns bit(8) return 'a'| +select bug7648()| +bug7648() +a +drop function bug7648| drop table t1,t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 3934e8ad17a..6c833b14482 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -3627,16 +3627,29 @@ set global query_cache_size = @qcs1| delete from t1| drop function bug9902| + # -# BUG#9102: New bug synopsis +# BUG#9102: Stored proccedures: function which returns blob causes crash # --disable_warnings drop function if exists bug9102| --enable_warnings create function bug9102() returns blob return 'a'| -select bug9102(); +select bug9102()| drop function bug9102| + +# +# BUG#7648: Stored procedure crash when invoking a function that returns a bit +# +--disable_warnings +drop procedure if exists bug7648| +--enable_warnings +create function bug7648() returns bit(8) return 'a'| +select bug7648()| +drop function bug7648| + + # # BUG#NNNN: New bug synopsis # diff --git a/sql/item_func.h b/sql/item_func.h index 9bf21fa1aa3..b39786e5544 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1308,7 +1308,17 @@ public: longlong val_int() { - return (longlong)Item_func_sp::val_real(); + Item *it; + longlong l; + + if (execute(&it)) + { + null_value= 1; + return 0LL; + } + l= it->val_int(); + null_value= it->null_value; + return l; } double val_real() From 8689083aca0c97457701551b776b1a9c9743c10d Mon Sep 17 00:00:00 2001 From: "acurtis@xiphis.org" <> Date: Wed, 20 Apr 2005 18:08:42 +0100 Subject: [PATCH 3/4] Bug#9775 - Stored procedures: crash if create function that returns enum or set Fix bug and implement return type casting. --- mysql-test/r/sp.result | 27 ++++++++++++++++-- mysql-test/t/sp.test | 23 +++++++++++++++- sql/item_func.cc | 62 ++++++++++++++++++++++++++++++++---------- sql/item_func.h | 58 ++++++++++++--------------------------- sql/sp_head.cc | 8 ++++-- 5 files changed, 117 insertions(+), 61 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index a4a3968672f..5d93f252716 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -2960,14 +2960,35 @@ delete from t1| drop function bug9902| drop function if exists bug9102| create function bug9102() returns blob return 'a'| -select bug9102(); -drop function bug9102| +select bug9102()| bug9102() a -drop procedure if exists bug7648| +drop function bug9102| +drop function if exists bug7648| create function bug7648() returns bit(8) return 'a'| select bug7648()| bug7648() a drop function bug7648| +drop function if exists bug9775| +create function bug9775(v1 char(1)) returns enum('a','b') return v1| +select bug9775('a'),bug9775('b'),bug9775('c')| +bug9775('a') bug9775('b') bug9775('c') +a b +drop function bug9775| +create function bug9775(v1 int) returns enum('a','b') return v1| +select bug9775(1),bug9775(2),bug9775(3)| +bug9775(1) bug9775(2) bug9775(3) +a b +drop function bug9775| +create function bug9775(v1 char(1)) returns set('a','b') return v1| +select bug9775('a'),bug9775('b'),bug9775('a,b'),bug9775('c')| +bug9775('a') bug9775('b') bug9775('a,b') bug9775('c') +a b a,b +drop function bug9775| +create function bug9775(v1 int) returns set('a','b') return v1| +select bug9775(1),bug9775(2),bug9775(3),bug9775(4)| +bug9775(1) bug9775(2) bug9775(3) bug9775(4) +a b a,b +drop function bug9775| drop table t1,t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 6c833b14482..9f76f7ec0e4 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -3643,13 +3643,34 @@ drop function bug9102| # BUG#7648: Stored procedure crash when invoking a function that returns a bit # --disable_warnings -drop procedure if exists bug7648| +drop function if exists bug7648| --enable_warnings create function bug7648() returns bit(8) return 'a'| select bug7648()| drop function bug7648| +# +# BUG#9775: crash if create function that returns enum or set +# +--disable_warnings +drop function if exists bug9775| +--enable_warnings +create function bug9775(v1 char(1)) returns enum('a','b') return v1| +select bug9775('a'),bug9775('b'),bug9775('c')| +drop function bug9775| +create function bug9775(v1 int) returns enum('a','b') return v1| +select bug9775(1),bug9775(2),bug9775(3)| +drop function bug9775| + +create function bug9775(v1 char(1)) returns set('a','b') return v1| +select bug9775('a'),bug9775('b'),bug9775('a,b'),bug9775('c')| +drop function bug9775| +create function bug9775(v1 int) returns set('a','b') return v1| +select bug9775(1),bug9775(2),bug9775(3),bug9775(4)| +drop function bug9775| + + # # BUG#NNNN: New bug synopsis # diff --git a/sql/item_func.cc b/sql/item_func.cc index c607efa0797..fb21551e22f 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4461,7 +4461,7 @@ longlong Item_func_row_count::val_int() Item_func_sp::Item_func_sp(sp_name *name) - :Item_func(), m_name(name), m_sp(NULL) + :Item_func(), m_name(name), m_sp(NULL), result_field(NULL) { maybe_null= 1; m_name->init_qname(current_thd); @@ -4470,7 +4470,7 @@ Item_func_sp::Item_func_sp(sp_name *name) Item_func_sp::Item_func_sp(sp_name *name, List &list) - :Item_func(list), m_name(name), m_sp(NULL) + :Item_func(list), m_name(name), m_sp(NULL), result_field(NULL) { maybe_null= 1; m_name->init_qname(current_thd); @@ -4526,6 +4526,29 @@ Item_func_sp::sp_result_field(void) const } +int +Item_func_sp::execute(Field **flp) +{ + Item *it; + Field *f; + if (execute(&it)) + { + null_value= 1; + return 1; + } + if (!(f= *flp)) + { + *flp= f= sp_result_field(); + f->move_field((f->pack_length() > sizeof(result_buf)) ? + sql_alloc(f->pack_length()) : result_buf); + f->null_ptr= (uchar *)&null_value; + f->null_bit= 1; + } + it->save_in_field(f, 1); + return f->is_null(); +} + + int Item_func_sp::execute(Item **itp) { @@ -4601,6 +4624,8 @@ Item_func_sp::field_type() const Field *field= 0; DBUG_ENTER("Item_func_sp::field_type"); + if (result_field) + DBUG_RETURN(result_field->type()); if (! m_sp) m_sp= sp_find_function(current_thd, m_name, TRUE); // cache only if ((field= sp_result_field())) @@ -4621,6 +4646,8 @@ Item_func_sp::result_type() const DBUG_ENTER("Item_func_sp::result_type"); DBUG_PRINT("info", ("m_sp = %p", m_sp)); + if (result_field) + DBUG_RETURN(result_field->result_type()); if (! m_sp) m_sp= sp_find_function(current_thd, m_name, TRUE); // cache only if ((field= sp_result_field())) @@ -4636,8 +4663,16 @@ Item_func_sp::result_type() const void Item_func_sp::fix_length_and_dec() { + Field *field= result_field; DBUG_ENTER("Item_func_sp::fix_length_and_dec"); + if (result_field) + { + decimals= result_field->decimals(); + max_length= result_field->representation_length(); + DBUG_VOID_RETURN; + } + if (! m_sp) m_sp= sp_find_function(current_thd, m_name, TRUE); // cache only if (! m_sp) @@ -4646,29 +4681,28 @@ Item_func_sp::fix_length_and_dec() } else { - switch (result_type()) { + if (!field) + field= sp_result_field(); + + decimals= field->decimals(); + max_length= field->representation_length(); + + switch (field->result_type()) { case STRING_RESULT: maybe_null= 1; - max_length= MAX_BLOB_WIDTH; - break; case REAL_RESULT: - decimals= NOT_FIXED_DEC; - max_length= float_length(decimals); - break; case INT_RESULT: - decimals= 0; - max_length= 21; - break; case DECIMAL_RESULT: - // TODO: where to find real precision and scale? - decimals= min(DECIMAL_MAX_LENGTH / 2, NOT_FIXED_DEC - 1); - max_length= DECIMAL_MAX_LENGTH; + break; case ROW_RESULT: default: // This case should never be chosen DBUG_ASSERT(0); break; } + + if (field != result_field) + delete field; } DBUG_VOID_RETURN; } diff --git a/sql/item_func.h b/sql/item_func.h index b39786e5544..ba5a6101e4c 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1283,8 +1283,11 @@ private: sp_name *m_name; mutable sp_head *m_sp; TABLE *dummy_table; + Field *result_field; + char result_buf[64]; int execute(Item **itp); + int execute(Field **flp); Field *sp_result_field(void) const; public: @@ -1296,6 +1299,12 @@ public: virtual ~Item_func_sp() {} + void cleanup() + { + Item_func::cleanup(); + result_field= NULL; + } + const char *func_name() const; enum enum_field_types field_type() const; @@ -1308,63 +1317,30 @@ public: longlong val_int() { - Item *it; - longlong l; - - if (execute(&it)) - { - null_value= 1; + if (execute(&result_field)) return 0LL; - } - l= it->val_int(); - null_value= it->null_value; - return l; + return result_field->val_int(); } double val_real() { - Item *it; - double d; - - if (execute(&it)) - { - null_value= 1; + if (execute(&result_field)) return 0.0; - } - d= it->val_real(); - null_value= it->null_value; - return d; + return result_field->val_real(); } my_decimal *val_decimal(my_decimal *dec_buf) { - Item *it; - my_decimal *result; - - if (execute(&it)) - { - null_value= 1; + if (execute(&result_field)) return NULL; - } - result= it->val_decimal(dec_buf); - null_value= it->null_value; - return result; + return result_field->val_decimal(dec_buf); } - String *val_str(String *str) { - Item *it; - String *s; - - if (execute(&it)) - { - null_value= 1; + if (execute(&result_field)) return NULL; - } - s= it->val_str(str); - null_value= it->null_value; - return s; + return result_field->val_str(str); } void fix_length_and_dec(); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index d1486cb234e..4ae76e4c6a5 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -378,8 +378,9 @@ sp_head::create_typelib(List *src) result->count= src->elements; result->name= ""; if (!(result->type_names=(const char **) - alloc_root(mem_root,sizeof(char *)*(result->count+1)))) + alloc_root(mem_root,(sizeof(char *)+sizeof(int))*(result->count+1)))) return 0; + result->type_lengths= (unsigned int *)(result->type_names + result->count+1); List_iterator it(*src); String conv, *tmp; uint32 dummy; @@ -397,8 +398,10 @@ sp_head::create_typelib(List *src) result->type_names[i]= buf; result->type_lengths[i]= conv.length(); } - else + else { result->type_names[i]= strdup_root(mem_root, tmp->c_ptr()); + result->type_lengths[i]= tmp->length(); + } // Strip trailing spaces. uint lengthsp= cs->cset->lengthsp(cs, result->type_names[i], @@ -407,6 +410,7 @@ sp_head::create_typelib(List *src) ((uchar *)result->type_names[i])[lengthsp]= '\0'; } result->type_names[result->count]= 0; + result->type_lengths[result->count]= 0; } return result; } From f67507588fc58b86a05ea494f669e2909b08bc72 Mon Sep 17 00:00:00 2001 From: "acurtis@xiphis.org" <> Date: Thu, 21 Apr 2005 13:22:47 +0100 Subject: [PATCH 4/4] Bug#8861 - If Return is a YEAR data type, value is not shown in year format Bug partially fixed by 9775/9102 work on SP function return. --- mysql-test/r/sp.result | 10 ++++++++++ mysql-test/t/sp.test | 13 +++++++++++++ sql/protocol.cc | 1 + 3 files changed, 24 insertions(+) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 5d93f252716..23c3548b9bf 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -2991,4 +2991,14 @@ select bug9775(1),bug9775(2),bug9775(3),bug9775(4)| bug9775(1) bug9775(2) bug9775(3) bug9775(4) a b a,b drop function bug9775| +drop function if exists bug8861| +create function bug8861(v1 int) returns year return v1| +select bug8861(05)| +bug8861(05) +2005 +set @x = bug8861(05)| +select @x| +@x +2005 +drop function bug8861| drop table t1,t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 9f76f7ec0e4..66c36a394f7 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -3671,6 +3671,19 @@ select bug9775(1),bug9775(2),bug9775(3),bug9775(4)| drop function bug9775| +# +# BUG#8861: If Return is a YEAR data type, value is not shown in year format +# +--disable_warnings +drop function if exists bug8861| +--enable_warnings +create function bug8861(v1 int) returns year return v1| +select bug8861(05)| +set @x = bug8861(05)| +select @x| +drop function bug8861| + + # # BUG#NNNN: New bug synopsis # diff --git a/sql/protocol.cc b/sql/protocol.cc index dc9ab7bf795..edeb78cc00b 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -774,6 +774,7 @@ bool Protocol_simple::store(const char *from, uint length, #ifndef DEBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || + field_types[field_pos] == MYSQL_TYPE_YEAR || field_types[field_pos] == MYSQL_TYPE_BIT || field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL || (field_types[field_pos] >= MYSQL_TYPE_ENUM &&