From 1220069c537a733b233c446e27aec9f9247aef31 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 4 Mar 2005 21:14:35 +0000 Subject: [PATCH 1/3] Bug#3788 Crashes with stored procedure return non-string values Also fixes Bug#2773 mysql-test/r/information_schema.result: Bug#3788 Fix results for bugfix mysql-test/r/sp.result: Bug#3788 Tests for Bug Fix results for bugfix mysql-test/t/sp.test: Bug#3788 New tests for bug sql/item.cc: Fix unrelated crash in view test with --ps-protocol. sql/item_func.cc: Bug#3788 Alter how SP function result types are handled. sql/item_func.h: Bug#3788 Alter how SP function result types are handled. sql/mysql_priv.h: Bug#3788 Prototypes for new global functions sql/sp.cc: Bug#3788 Alter how function return type is reported sql/sp_head.cc: Bug#3788 Change how function return types are stored for SPs sql/sp_head.h: Bug#3788 Change how function return types are stored for SPs sql/sql_parse.cc: Bug#3788 Split out field construction into its own function sql/sql_table.cc: Bug#3788 Split out field preparation code into its own function sql/sql_yacc.yy: Bug#3788 Change how function return types are stored for SPs sql/unireg.cc: Bug#3788 Add assertion check --- mysql-test/r/information_schema.result | 2 +- mysql-test/r/sp.result | 32 ++- mysql-test/t/sp.test | 29 +++ sql/item.cc | 2 +- sql/item_func.cc | 84 ++++++- sql/item_func.h | 9 + sql/mysql_priv.h | 11 + sql/sp.cc | 24 +- sql/sp_head.cc | 77 +++--- sql/sp_head.h | 15 +- sql/sql_parse.cc | 54 +++-- sql/sql_table.cc | 314 ++++++++++++++----------- sql/sql_yacc.yy | 32 ++- sql/unireg.cc | 1 + 14 files changed, 469 insertions(+), 217 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 121de940560..3b2d86f84bf 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -202,7 +202,7 @@ select parameter_style, sql_data_access, dtd_identifier from information_schema.routines; parameter_style sql_data_access dtd_identifier SQL CONTAINS SQL NULL -SQL CONTAINS SQL int +SQL CONTAINS SQL int(11) show procedure status; Db Name Type Definer Modified Created Security_type Comment test sel2 PROCEDURE root@localhost # # DEFINER diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index f2b99b7074a..b267331ff6d 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -951,7 +951,7 @@ comment 'Characteristics procedure test' return 42| show create function chistics| Function sql_mode Create Function -chistics CREATE FUNCTION `test`.`chistics`() RETURNS int +chistics CREATE FUNCTION `test`.`chistics`() RETURNS int(11) DETERMINISTIC SQL SECURITY INVOKER COMMENT 'Characteristics procedure test' @@ -964,7 +964,7 @@ no sql comment 'Characteristics function test'| show create function chistics| Function sql_mode Create Function -chistics CREATE FUNCTION `test`.`chistics`() RETURNS int +chistics CREATE FUNCTION `test`.`chistics`() RETURNS int(11) NO SQL DETERMINISTIC SQL SECURITY INVOKER @@ -1214,7 +1214,7 @@ show procedure status; end call bug2267_4()| Function sql_mode Create Function -fac CREATE FUNCTION `test`.`fac`(n int unsigned) RETURNS bigint unsigned +fac CREATE FUNCTION `test`.`fac`(n int unsigned) RETURNS bigint(20) unsigned begin declare f bigint unsigned default 1; while n > 1 do @@ -1576,11 +1576,11 @@ bug2564_2 ANSI_QUOTES CREATE PROCEDURE "test"."bug2564_2"() insert into "t1" values ('foo', 1) show create function bug2564_3| Function sql_mode Create Function -bug2564_3 CREATE FUNCTION `test`.`bug2564_3`(x int, y int) RETURNS int +bug2564_3 CREATE FUNCTION `test`.`bug2564_3`(x int, y int) RETURNS int(11) return x || y show create function bug2564_4| Function sql_mode Create Function -bug2564_4 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI CREATE FUNCTION "test"."bug2564_4"(x int, y int) RETURNS int +bug2564_4 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI CREATE FUNCTION "test"."bug2564_4"(x int, y int) RETURNS int(11) return x || y drop procedure bug2564_1| drop procedure bug2564_2| @@ -1645,6 +1645,28 @@ drop procedure bug4579_1| drop procedure bug4579_2| drop table t3| drop table if exists t3| +drop procedure if exists bug2773| +create function bug2773() returns int return null| +create table t3 as select bug2773()| +show create table t3| +Table Create Table +t3 CREATE TABLE `t3` ( + `bug2773()` int(11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t3| +drop function bug2773| +drop procedure if exists bug3788| +create function bug3788() returns date return cast("2005-03-04" as date)| +select bug3788()| +bug3788() +2005-03-04 +drop function bug3788| +create function bug3788() returns binary(5) return 5| +select bug3788()| +bug3788() +5 +drop function bug3788| +drop table if exists t3| create table t3 (f1 int, f2 int, f3 int)| insert into t3 values (1,1,1)| drop procedure if exists bug4726| diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 2cfd27134ad..267fcaf6bdf 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -2063,6 +2063,35 @@ drop procedure bug4579_1| drop procedure bug4579_2| drop table t3| +# +# BUG#2773: Function's data type ignored in stored procedures +# +--disable_warnings +drop table if exists t3| +drop procedure if exists bug2773| +--enable_warnings + +create function bug2773() returns int return null| +create table t3 as select bug2773()| +show create table t3| +drop table t3| +drop function bug2773| + +# +# BUG#3788: Stored procedure packet error +# +--disable_warnings +drop procedure if exists bug3788| +--enable_warnings + +create function bug3788() returns date return cast("2005-03-04" as date)| +select bug3788()| +drop function bug3788| + +create function bug3788() returns binary(5) return 5| +select bug3788()| +drop function bug3788| + # # BUG#4726 diff --git a/sql/item.cc b/sql/item.cc index 3b920bd218d..ac69fd33b1f 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3524,7 +3524,7 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) enum_parsing_place place= NO_MATTER; SELECT_LEX *current_sel= thd->lex->current_select; - if (!ref) + if (!ref || ref == not_found_item) { SELECT_LEX_UNIT *prev_unit= current_sel->master_unit(); SELECT_LEX *outer_sel= prev_unit->outer_select(); diff --git a/sql/item_func.cc b/sql/item_func.cc index 3742a13e0bc..d52b0a184da 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4318,13 +4318,33 @@ longlong Item_func_row_count::val_int() Item_func_sp::Item_func_sp(sp_name *name) :Item_func(), m_name(name), m_sp(NULL) { + char *empty_name= (char *) ""; + maybe_null= 1; m_name->init_qname(current_thd); + bzero(&dummy_table, sizeof(dummy_table)); + dummy_table.share.table_cache_key = empty_name; + dummy_table.share.table_name = empty_name; + dummy_table.table.alias = empty_name; + dummy_table.share.table_name = empty_name; + dummy_table.table.maybe_null = maybe_null; + dummy_table.table.in_use= current_thd; + dummy_table.table.s = &dummy_table.share; } Item_func_sp::Item_func_sp(sp_name *name, List &list) :Item_func(list), m_name(name), m_sp(NULL) { + char *empty_name= (char *) ""; + maybe_null= 1; m_name->init_qname(current_thd); + bzero(&dummy_table, sizeof(dummy_table)); + dummy_table.share.table_cache_key = empty_name; + dummy_table.share.table_name = empty_name; + dummy_table.table.alias = empty_name; + dummy_table.share.table_name = empty_name; + dummy_table.table.maybe_null = maybe_null; + dummy_table.table.in_use= current_thd; + dummy_table.table.s = &dummy_table.share; } const char * @@ -4349,6 +4369,18 @@ Item_func_sp::func_name() const } +Field * +Item_func_sp::sp_result_field(void) const +{ + Field *field= 0; + THD *thd= current_thd; + DBUG_ENTER("Item_func_sp::sp_result_field"); + if (m_sp) + field= m_sp->make_field(max_length, name, &dummy_table.table); + DBUG_RETURN(field); +} + + int Item_func_sp::execute(Item **itp) { @@ -4404,17 +4436,38 @@ Item_func_sp::execute(Item **itp) } +void +Item_func_sp::make_field(Send_field *tmp_field) +{ + Field *field; + DBUG_ENTER("Item_func_sp::make_field"); + if (! m_sp) + m_sp= sp_find_function(current_thd, m_name, TRUE); // cache only + if ((field= sp_result_field())) + { + field->make_field(tmp_field); + delete field; + DBUG_VOID_RETURN; + } + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); + init_make_field(tmp_field, MYSQL_TYPE_VARCHAR); + DBUG_VOID_RETURN; +} + + enum enum_field_types Item_func_sp::field_type() const { + Field *field= 0; DBUG_ENTER("Item_func_sp::field_type"); if (! m_sp) m_sp= sp_find_function(current_thd, m_name, TRUE); // cache only - if (m_sp) + if ((field= sp_result_field())) { - DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns)); - DBUG_RETURN(m_sp->m_returns); + enum_field_types result= field->type(); + delete field; + DBUG_RETURN(result); } my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); DBUG_RETURN(MYSQL_TYPE_VARCHAR); @@ -4424,14 +4477,17 @@ Item_func_sp::field_type() const Item_result Item_func_sp::result_type() const { + Field *field= 0; DBUG_ENTER("Item_func_sp::result_type"); DBUG_PRINT("info", ("m_sp = %p", m_sp)); if (! m_sp) m_sp= sp_find_function(current_thd, m_name, TRUE); // cache only - if (m_sp) + if ((field= sp_result_field())) { - DBUG_RETURN(m_sp->result()); + Item_result result= field->result_type(); + delete field; + DBUG_RETURN(result); } my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); DBUG_RETURN(STRING_RESULT); @@ -4450,7 +4506,7 @@ Item_func_sp::fix_length_and_dec() } else { - switch (m_sp->result()) { + switch (result_type()) { case STRING_RESULT: maybe_null= 1; max_length= MAX_BLOB_WIDTH; @@ -4485,3 +4541,19 @@ longlong Item_func_found_rows::val_int() return thd->found_rows(); } + +Field * +Item_func_sp::tmp_table_field(TABLE *t_arg) +{ + Field *res= 0; + enum_field_types ftype; + DBUG_ENTER("Item_func_sp::tmp_table_field"); + + if (m_sp) + res= m_sp->make_field(max_length, (const char *)name, t_arg); + + if (!res) + res= Item_func::tmp_table_field(t_arg); + + DBUG_RETURN(res); +} diff --git a/sql/item_func.h b/sql/item_func.h index 48fc278ccde..cb096732b85 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1250,8 +1250,13 @@ class Item_func_sp :public Item_func private: sp_name *m_name; mutable sp_head *m_sp; + mutable struct { + TABLE table; + TABLE_SHARE share; + } dummy_table; int execute(Item **itp); + Field *sp_result_field(void) const; public: @@ -1266,6 +1271,10 @@ public: enum enum_field_types field_type() const; + Field *tmp_table_field(TABLE *t_arg); + + void make_field(Send_field *tmp_field); + Item_result result_type() const; longlong val_int() diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2d4a1c3a0ad..40372abbeb1 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -629,6 +629,10 @@ int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t); 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); +int prepare_create_field(create_field *sql_field, + uint &blob_columns, + int ×tamps, int ×tamps_with_niladic, + uint table_flags); int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, List &fields, List &keys, uint &db_options, @@ -837,6 +841,13 @@ bool add_field_to_list(THD *thd, char *field_name, enum enum_field_types type, char *change, List *interval_list, CHARSET_INFO *cs, uint uint_geom_type); +create_field * new_create_field(THD *thd, char *field_name, enum_field_types type, + char *length, char *decimals, + uint type_modifier, + Item *default_value, Item *on_update_value, + LEX_STRING *comment, char *change, + List *interval_list, CHARSET_INFO *cs, + uint uint_geom_type); void store_position_for_column(const char *name); bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc=0); void add_join_on(TABLE_LIST *b,Item *expr); diff --git a/sql/sp.cc b/sql/sp.cc index 46b08c3e847..af6441f96f4 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -334,6 +334,22 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) } +static void +sp_returns_type(THD *thd, String &result, sp_head *sp) +{ + struct { + TABLE table; + TABLE_SHARE share; + } dummy; + Field *field; + bzero(&dummy, sizeof(dummy)); + dummy.table.in_use= thd; + dummy.table.s = &dummy.share; + field= sp->make_field(0, 0, &dummy.table); + field->sql_type(result); + delete field; +} + static int db_create_routine(THD *thd, int type, sp_head *sp) { @@ -388,9 +404,13 @@ db_create_routine(THD *thd, int type, sp_head *sp) store((longlong)sp->m_chistics->suid); table->field[MYSQL_PROC_FIELD_PARAM_LIST]-> store(sp->m_params.str, sp->m_params.length, system_charset_info); - if (sp->m_retstr.str) + if (sp->m_type == TYPE_ENUM_FUNCTION) + { + String retstr(64); + sp_returns_type(thd, retstr, sp); table->field[MYSQL_PROC_FIELD_RETURNS]-> - store(sp->m_retstr.str, sp->m_retstr.length, system_charset_info); + store(retstr.ptr(), retstr.length(), system_charset_info); + } table->field[MYSQL_PROC_FIELD_BODY]-> store(sp->m_body.str, sp->m_body.length, system_charset_info); table->field[MYSQL_PROC_FIELD_DEFINER]-> diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 72e2204f1b4..a4ca92da60a 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -300,11 +300,11 @@ sp_head::init(LEX *lex) */ lex->trg_table_fields.empty(); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); - m_param_begin= m_param_end= m_returns_begin= m_returns_end= m_body_begin= 0; - m_qname.str= m_db.str= m_name.str= m_params.str= m_retstr.str= + m_param_begin= m_param_end= m_body_begin= 0; + m_qname.str= m_db.str= m_name.str= m_params.str= m_body.str= m_defstr.str= 0; m_qname.length= m_db.length= m_name.length= m_params.length= - m_retstr.length= m_body.length= m_defstr.length= 0; + m_body.length= m_defstr.length= 0; m_returns_cs= NULL; DBUG_VOID_RETURN; } @@ -346,41 +346,6 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) (char *)m_param_begin, m_params.length); } - if (m_returns_begin && m_returns_end) - { - /* QQ KLUDGE: We can't seem to cut out just the type in the parser - (without the RETURNS), so we'll have to do it here. :-( - Furthermore, if there's a character type as well, it's not include - (beyond the m_returns_end pointer), in which case we need - m_returns_cs. */ - char *p= (char *)m_returns_begin+strspn((char *)m_returns_begin,"\t\n\r "); - p+= strcspn(p, "\t\n\r "); - p+= strspn(p, "\t\n\r "); - if (p < (char *)m_returns_end) - m_returns_begin= (uchar *)p; - /* While we're at it, trim the end too. */ - p= (char *)m_returns_end-1; - while (p > (char *)m_returns_begin && - (*p == '\t' || *p == '\n' || *p == '\r' || *p == ' ')) - p-= 1; - m_returns_end= (uchar *)p+1; - if (m_returns_cs) - { - String s((char *)m_returns_begin, m_returns_end - m_returns_begin, - system_charset_info); - - s.append(' '); - s.append(m_returns_cs->csname); - m_retstr.length= s.length(); - m_retstr.str= strmake_root(root, s.ptr(), m_retstr.length); - } - else - { - m_retstr.length= m_returns_end - m_returns_begin; - m_retstr.str= strmake_root(root, - (char *)m_returns_begin, m_retstr.length); - } - } m_body.length= lex->ptr - m_body_begin; /* Trim nuls at the end */ n= 0; @@ -396,6 +361,27 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) DBUG_VOID_RETURN; } +TYPELIB * +sp_head::create_typelib(List *src) +{ + TYPELIB *result= NULL; + DBUG_ENTER("sp_head::clone_typelib"); + if (src->elements) + { + result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB)); + result->count= src->elements; + result->name= ""; + if (!(result->type_names=(const char **) + alloc_root(mem_root,sizeof(char *)*(result->count+1)))) + return 0; + List_iterator it(*src); + for (uint i=0; icount; i++) + result->type_names[i]= strdup_root(mem_root, (it++)->c_ptr()); + result->type_names[result->count]= 0; + } + return result; +} + int sp_head::create(THD *thd) { @@ -464,6 +450,21 @@ sp_head::destroy() DBUG_VOID_RETURN; } + +Field * +sp_head::make_field(uint max_length, const char *name, TABLE *dummy) +{ + Field *field; + DBUG_ENTER("sp_head::make_field"); + field= ::make_field((char *)0, + !m_returns_len ? max_length : m_returns_len, + (uchar *)"", 0, m_returns_pack, m_returns, m_returns_cs, + (enum Field::geometry_type)0, Field::NONE, + m_returns_typelib, + name ? name : (const char *)m_name.str, dummy); + DBUG_RETURN(field); +} + int sp_head::execute(THD *thd) { diff --git a/sql/sp_head.h b/sql/sp_head.h index 5df9c753048..a4a7e2be8de 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -84,6 +84,9 @@ public: int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE enum enum_field_types m_returns; // For FUNCTIONs only CHARSET_INFO *m_returns_cs; // For FUNCTIONs only + TYPELIB *m_returns_typelib; // For FUNCTIONs only + uint m_returns_len; // For FUNCTIONs only + uint m_returns_pack; // For FUNCTIONs only my_bool m_has_return; // For FUNCTIONs only my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise my_bool m_multi_results; // TRUE if a procedure with SELECT(s) @@ -96,7 +99,6 @@ public: LEX_STRING m_db; LEX_STRING m_name; LEX_STRING m_params; - LEX_STRING m_retstr; // For FUNCTIONs only LEX_STRING m_body; LEX_STRING m_defstr; LEX_STRING m_definer_user; @@ -105,8 +107,7 @@ public: longlong m_modified; HASH m_sptabs; /* Merged table lists */ // Pointers set during parsing - uchar *m_param_begin, *m_param_end, *m_returns_begin, *m_returns_end, - *m_body_begin; + uchar *m_param_begin, *m_param_end, *m_body_begin; static void * operator new(size_t size); @@ -124,6 +125,9 @@ public: void init_strings(THD *thd, LEX *lex, sp_name *name); + TYPELIB * + create_typelib(List *src); + int create(THD *thd); @@ -197,10 +201,7 @@ public: char *create_string(THD *thd, ulong *lenp); - inline Item_result result() - { - return sp_map_result_type(m_returns); - } + Field *make_field(uint max_length, const char *name, TABLE *dummy); void set_info(char *definer, uint definerlen, longlong created, longlong modified, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9a78daa1479..85225c94d03 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5291,9 +5291,6 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, { register create_field *new_field; LEX *lex= thd->lex; - uint allowed_type_modifier=0; - uint sign_len; - ulong max_field_charlength= MAX_FIELD_CHARLENGTH; DBUG_ENTER("add_field_to_list"); if (strlen(field_name) > NAME_LEN) @@ -5354,9 +5351,38 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name); DBUG_RETURN(1); } - - if (!(new_field=new create_field())) + + if (!(new_field= new_create_field(thd, field_name, type, length, decimals, + type_modifier, default_value, on_update_value, + comment, change, interval_list, cs, uint_geom_type))) DBUG_RETURN(1); + + lex->create_list.push_back(new_field); + lex->last_field=new_field; + DBUG_RETURN(0); +} + +/***************************************************************************** +** Create field definition for create +** Return 0 on failure, otherwise return create_field instance +******************************************************************************/ + +create_field * +new_create_field(THD *thd, char *field_name, enum_field_types type, + char *length, char *decimals, + uint type_modifier, + Item *default_value, Item *on_update_value, + LEX_STRING *comment, + char *change, List *interval_list, CHARSET_INFO *cs, + uint uint_geom_type) +{ + register create_field *new_field; + uint sign_len, allowed_type_modifier=0; + ulong max_field_charlength= MAX_FIELD_CHARLENGTH; + DBUG_ENTER("new_create_field"); + + if (!(new_field=new create_field())) + DBUG_RETURN(NULL); new_field->field=0; new_field->field_name=field_name; new_field->def= default_value; @@ -5428,7 +5454,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, new_field->length >= new_field->decimals) break; my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); - DBUG_RETURN(1); + DBUG_RETURN(NULL); case MYSQL_TYPE_VARCHAR: /* Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table @@ -5451,7 +5477,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, { my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), field_name); /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + DBUG_RETURN(NULL); } new_field->def=0; } @@ -5471,7 +5497,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, if (tmp_length > PRECISION_FOR_DOUBLE) { my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); - DBUG_RETURN(1); + DBUG_RETURN(NULL); } else if (tmp_length > PRECISION_FOR_FLOAT) { @@ -5568,7 +5594,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, if (interval_list->elements > sizeof(longlong)*8) { my_error(ER_TOO_BIG_SET, MYF(0), field_name); /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + DBUG_RETURN(NULL); } new_field->pack_length= (interval_list->elements + 7) / 8; if (new_field->pack_length > 4) @@ -5609,7 +5635,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, { my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name, MAX_BIT_FIELD_LENGTH); - DBUG_RETURN(1); + DBUG_RETURN(NULL); } new_field->pack_length= (new_field->length + 7) / 8; break; @@ -5628,17 +5654,15 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, { my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name, max_field_charlength); /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + DBUG_RETURN(NULL); } type_modifier&= AUTO_INCREMENT_FLAG; if ((~allowed_type_modifier) & type_modifier) { my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); - DBUG_RETURN(1); + DBUG_RETURN(NULL); } - lex->create_list.push_back(new_field); - lex->last_field=new_field; - DBUG_RETURN(0); + DBUG_RETURN(new_field); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b38014eb4ea..93c1b7040a9 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -424,6 +424,173 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval, } +/* + Prepare a create_table instance for packing + + SYNOPSIS + prepare_create_field() + sql_field field to prepare for packing + blob_columns count for BLOBs + timestamps count for timestamps + table_flags table flags + + DESCRIPTION + This function prepares a create_field instance. + Fields such as pack_flag are valid after this call. + + RETURN VALUES + 0 ok + 1 Error +*/ + +int prepare_create_field(create_field *sql_field, + uint &blob_columns, + int ×tamps, int ×tamps_with_niladic, + uint table_flags) +{ + DBUG_ENTER("prepare_field"); + { + /* This code came from mysql_prepare_table. + Indent preserved to make patching easier */ + DBUG_ASSERT(sql_field->charset); + + switch (sql_field->sql_type) { + case FIELD_TYPE_BLOB: + case FIELD_TYPE_MEDIUM_BLOB: + case FIELD_TYPE_TINY_BLOB: + case FIELD_TYPE_LONG_BLOB: + sql_field->pack_flag=FIELDFLAG_BLOB | + pack_length_to_packflag(sql_field->pack_length - + portable_sizeof_char_ptr); + if (sql_field->charset->state & MY_CS_BINSORT) + sql_field->pack_flag|=FIELDFLAG_BINARY; + sql_field->length=8; // Unireg field length + sql_field->unireg_check=Field::BLOB_FIELD; + blob_columns++; + break; + case FIELD_TYPE_GEOMETRY: +#ifdef HAVE_SPATIAL + if (!(table_flags & HA_CAN_GEOMETRY)) + { + my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED), + MYF(0), "GEOMETRY"); + DBUG_RETURN(1); + } + sql_field->pack_flag=FIELDFLAG_GEOM | + pack_length_to_packflag(sql_field->pack_length - + portable_sizeof_char_ptr); + if (sql_field->charset->state & MY_CS_BINSORT) + sql_field->pack_flag|=FIELDFLAG_BINARY; + sql_field->length=8; // Unireg field length + sql_field->unireg_check=Field::BLOB_FIELD; + blob_columns++; + break; +#else + my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED), MYF(0), + sym_group_geom.name, sym_group_geom.needed_define); + DBUG_RETURN(1); +#endif /*HAVE_SPATIAL*/ + case MYSQL_TYPE_VARCHAR: +#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR + if (table_flags & HA_NO_VARCHAR) + { + /* convert VARCHAR to CHAR because handler is not yet up to date */ + sql_field->sql_type= MYSQL_TYPE_VAR_STRING; + sql_field->pack_length= calc_pack_length(sql_field->sql_type, + (uint) sql_field->length); + if ((sql_field->length / sql_field->charset->mbmaxlen) > + MAX_FIELD_CHARLENGTH) + { + my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH), + MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH); + DBUG_RETURN(1); + } + } +#endif + /* fall through */ + case FIELD_TYPE_STRING: + sql_field->pack_flag=0; + if (sql_field->charset->state & MY_CS_BINSORT) + sql_field->pack_flag|=FIELDFLAG_BINARY; + break; + case FIELD_TYPE_ENUM: + sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | + FIELDFLAG_INTERVAL; + if (sql_field->charset->state & MY_CS_BINSORT) + sql_field->pack_flag|=FIELDFLAG_BINARY; + sql_field->unireg_check=Field::INTERVAL_FIELD; + check_duplicates_in_interval("ENUM",sql_field->field_name, + sql_field->interval, + sql_field->charset); + break; + case FIELD_TYPE_SET: + sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | + FIELDFLAG_BITFIELD; + if (sql_field->charset->state & MY_CS_BINSORT) + sql_field->pack_flag|=FIELDFLAG_BINARY; + sql_field->unireg_check=Field::BIT_FIELD; + check_duplicates_in_interval("SET",sql_field->field_name, + sql_field->interval, + sql_field->charset); + break; + case FIELD_TYPE_DATE: // Rest of string types + case FIELD_TYPE_NEWDATE: + case FIELD_TYPE_TIME: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_NULL: + sql_field->pack_flag=f_settype((uint) sql_field->sql_type); + break; + case FIELD_TYPE_BIT: + if (!(table_flags & HA_CAN_BIT_FIELD)) + { + my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "BIT FIELD"); + DBUG_RETURN(1); + } + sql_field->pack_flag= FIELDFLAG_NUMBER; + break; + case FIELD_TYPE_NEWDECIMAL: + sql_field->pack_flag=(FIELDFLAG_NUMBER | + (sql_field->flags & UNSIGNED_FLAG ? 0 : + FIELDFLAG_DECIMAL) | + (sql_field->flags & ZEROFILL_FLAG ? + FIELDFLAG_ZEROFILL : 0) | + (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); + break; + case FIELD_TYPE_TIMESTAMP: + /* We should replace old TIMESTAMP fields with their newer analogs */ + if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD) + { + if (!timestamps) + { + sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD; + timestamps_with_niladic++; + } + else + sql_field->unireg_check= Field::NONE; + } + else if (sql_field->unireg_check != Field::NONE) + timestamps_with_niladic++; + + timestamps++; + /* fall-through */ + default: + sql_field->pack_flag=(FIELDFLAG_NUMBER | + (sql_field->flags & UNSIGNED_FLAG ? 0 : + FIELDFLAG_DECIMAL) | + (sql_field->flags & ZEROFILL_FLAG ? + FIELDFLAG_ZEROFILL : 0) | + f_settype((uint) sql_field->sql_type) | + (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); + break; + } + if (!(sql_field->flags & NOT_NULL_FLAG)) + sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL; + if (sql_field->flags & NO_DEFAULT_VALUE_FLAG) + sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT; + } + DBUG_RETURN(0); +} + /* Preparation for table creation @@ -683,142 +850,17 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, { DBUG_ASSERT(sql_field->charset); - switch (sql_field->sql_type) { - case FIELD_TYPE_BLOB: - case FIELD_TYPE_MEDIUM_BLOB: - case FIELD_TYPE_TINY_BLOB: - case FIELD_TYPE_LONG_BLOB: - sql_field->pack_flag=FIELDFLAG_BLOB | - pack_length_to_packflag(sql_field->pack_length - - portable_sizeof_char_ptr); - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|=FIELDFLAG_BINARY; - sql_field->length=8; // Unireg field length - sql_field->unireg_check=Field::BLOB_FIELD; - blob_columns++; - create_info->varchar= 1; - break; - case FIELD_TYPE_GEOMETRY: -#ifdef HAVE_SPATIAL - if (!(file->table_flags() & HA_CAN_GEOMETRY)) - { - my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "GEOMETRY"); - DBUG_RETURN(-1); - } - sql_field->pack_flag=FIELDFLAG_GEOM | - pack_length_to_packflag(sql_field->pack_length - - portable_sizeof_char_ptr); - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|=FIELDFLAG_BINARY; - sql_field->length=8; // Unireg field length - sql_field->unireg_check=Field::BLOB_FIELD; - blob_columns++; - create_info->varchar= 1; - break; -#else - my_error(ER_FEATURE_DISABLED, MYF(0), - sym_group_geom.name, sym_group_geom.needed_define); + if (prepare_create_field(sql_field, blob_columns, + timestamps, timestamps_with_niladic, + file->table_flags())) DBUG_RETURN(-1); -#endif /*HAVE_SPATIAL*/ - case MYSQL_TYPE_VARCHAR: -#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR - if (file->table_flags() & HA_NO_VARCHAR) - { - /* convert VARCHAR to CHAR because handler is not yet up to date */ - sql_field->sql_type= MYSQL_TYPE_VAR_STRING; - sql_field->pack_length= calc_pack_length(sql_field->sql_type, - (uint) sql_field->length); - if ((sql_field->length / sql_field->charset->mbmaxlen) > - MAX_FIELD_CHARLENGTH) - { - my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH), - MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH); - DBUG_RETURN(-1); - } - } - else -#endif - create_info->varchar= 1; - /* fall through */ - case MYSQL_TYPE_STRING: - sql_field->pack_flag=0; - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|= FIELDFLAG_BINARY; - break; - case FIELD_TYPE_ENUM: - sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | - FIELDFLAG_INTERVAL; - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|=FIELDFLAG_BINARY; - sql_field->unireg_check=Field::INTERVAL_FIELD; - check_duplicates_in_interval("ENUM",sql_field->field_name, - sql_field->interval, - sql_field->charset); - break; - case FIELD_TYPE_SET: - sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | - FIELDFLAG_BITFIELD; - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|=FIELDFLAG_BINARY; - sql_field->unireg_check=Field::BIT_FIELD; - check_duplicates_in_interval("SET",sql_field->field_name, - sql_field->interval, - sql_field->charset); - break; - case FIELD_TYPE_DATE: // Rest of string types - case FIELD_TYPE_NEWDATE: - case FIELD_TYPE_TIME: - case FIELD_TYPE_DATETIME: - case FIELD_TYPE_NULL: - sql_field->pack_flag=f_settype((uint) sql_field->sql_type); - break; - case FIELD_TYPE_BIT: - if (!(file->table_flags() & HA_CAN_BIT_FIELD)) - { - my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "BIT FIELD"); - DBUG_RETURN(-1); - } - sql_field->pack_flag= FIELDFLAG_NUMBER; - break; - case FIELD_TYPE_NEWDECIMAL: - sql_field->pack_flag=(FIELDFLAG_NUMBER | - (sql_field->flags & UNSIGNED_FLAG ? 0 : - FIELDFLAG_DECIMAL) | - (sql_field->flags & ZEROFILL_FLAG ? - FIELDFLAG_ZEROFILL : 0) | - (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); - break; - case FIELD_TYPE_TIMESTAMP: - /* We should replace old TIMESTAMP fields with their newer analogs */ - if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD) - { - if (!timestamps) - { - sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD; - timestamps_with_niladic++; - } - else - sql_field->unireg_check= Field::NONE; - } - else if (sql_field->unireg_check != Field::NONE) - timestamps_with_niladic++; - - timestamps++; - /* fall-through */ - default: - sql_field->pack_flag=(FIELDFLAG_NUMBER | - (sql_field->flags & UNSIGNED_FLAG ? 0 : - FIELDFLAG_DECIMAL) | - (sql_field->flags & ZEROFILL_FLAG ? - FIELDFLAG_ZEROFILL : 0) | - f_settype((uint) sql_field->sql_type) | - (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); - break; - } - if (!(sql_field->flags & NOT_NULL_FLAG)) - sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL; - if (sql_field->flags & NO_DEFAULT_VALUE_FLAG) - sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT; + if (sql_field->sql_type == FIELD_TYPE_BLOB || + sql_field->sql_type == FIELD_TYPE_MEDIUM_BLOB || + sql_field->sql_type == FIELD_TYPE_TINY_BLOB || + sql_field->sql_type == FIELD_TYPE_LONG_BLOB || + sql_field->sql_type == FIELD_TYPE_GEOMETRY || + sql_field->sql_type == MYSQL_TYPE_VARCHAR) + create_info->varchar= 1; sql_field->offset= pos; if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) auto_increment++; @@ -1585,6 +1627,8 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, ((Item_field *)item)->field : (Field*) 0)))) DBUG_RETURN(0); + if (item->maybe_null) + cr_field->flags &= ~NOT_NULL_FLAG; extra_fields->push_back(cr_field); } /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ba1b999b240..a57ec24def5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1370,19 +1370,37 @@ create_function_tail: RETURNS_SYM { LEX *lex= Lex; - sp_head *sp= lex->sphead; - - sp->m_returns_begin= lex->tok_start; - sp->m_returns_cs= lex->charset= NULL; + lex->charset= NULL; + lex->length= lex->dec= NULL; + lex->interval_list.empty(); + lex->type= 0; } type { LEX *lex= Lex; sp_head *sp= lex->sphead; + LEX_STRING cmt = { 0, 0 }; + create_field *new_field; + uint unused1= 0; + int unused2= 0; + + if (!(new_field= new_create_field(YYTHD, "", (enum enum_field_types)$8, + lex->length, lex->dec, lex->type, + (Item *)0, (Item *) 0, &cmt, 0, &lex->interval_list, + (lex->charset ? lex->charset : default_charset_info), + lex->uint_geom_type))) + YYABORT; + + if (prepare_create_field(new_field, unused1, unused2, unused2, 0)) + YYABORT; + + sp->m_returns= new_field->sql_type; + 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_end= lex->tok_start; - sp->m_returns= (enum enum_field_types)$8; - sp->m_returns_cs= lex->charset; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } sp_c_chistics diff --git a/sql/unireg.cc b/sql/unireg.cc index dd94098fbf3..3e85767dc86 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -695,6 +695,7 @@ static bool make_empty_rec(File file,enum db_type table_type, field->interval, field->field_name, &table); + DBUG_ASSERT(regfield); if (!(field->flags & NOT_NULL_FLAG)) null_count++; From b0e1389b82e1f05de98e436377e08c3c6ff9eed2 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 9 Mar 2005 00:32:58 +0100 Subject: [PATCH 2/3] This code change has 0 effects as it's about the case where innobase_very_fast_shutdown!=0, which is always false. In a very fast InnoDB shutdown, we just ensure that no more transactions are running, flush InnoDB log, signal InnoDB threads to die, and then return from InnoDB (from innobase_end()) without waiting for those threads to actually die. I have tested on a 4CPU machine that even with --innodb_flush_log_at_trx_commit=0, this optimized InnoDB very fast shutdown loses no committed transactions. Patch pre-approved by Heikki. innobase/log/log0log.c: In an InnoDB very fast shutdown, we just need to wait for no more transactions to be happening and then we can flush the InnoDB log and don't need to wait for the signaled-to-die InnoDB threads to finish (saves seconds). innobase/srv/srv0start.c: In an InnoDB very fast shutdown, once we have forced a flush of the InnoDB log to disk, and signalled InnoDB threads to die, we needn't wait for these threads to die. --- innobase/log/log0log.c | 49 ++++++++++++++++++++-------------------- innobase/srv/srv0start.c | 7 ++++++ 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/innobase/log/log0log.c b/innobase/log/log0log.c index 5915146b466..e8a720e8a88 100644 --- a/innobase/log/log0log.c +++ b/innobase/log/log0log.c @@ -3047,7 +3047,10 @@ loop: mutex_enter(&kernel_mutex); - /* Check that there are no longer transactions */ + /* Check that there are no longer transactions. We need this wait even + for the 'very fast' shutdown, because the InnoDB layer may have + committed or prepared transactions and we don't want to lose them. */ + if (trx_n_mysql_transactions > 0 || UT_LIST_GET_LEN(trx_sys->trx_list) > 0) { @@ -3056,6 +3059,23 @@ loop: goto loop; } + if (srv_very_fast_shutdown) { + /* In a 'very fast' shutdown we do not flush the buffer pool: + it is essentially a 'crash' of the InnoDB server. + Make sure that the log is all flushed to disk, so that + we can recover all committed transactions in a crash + recovery. + In a 'very fast' shutdown we do not flush the buffer pool: + it is essentially a 'crash' of the InnoDB server. Then we must + not write the lsn stamps to the data files, since at a + startup InnoDB deduces from the stamps if the previous + shutdown was clean. */ + + log_buffer_flush_to_disk(); + return; /* We SKIP ALL THE REST !! */ + } + + /* Check that the master thread is suspended */ if (srv_n_threads_active[SRV_MASTER] != 0) { @@ -3092,24 +3112,13 @@ loop: log_archive_all(); #endif /* UNIV_LOG_ARCHIVE */ - if (!srv_very_fast_shutdown) { - /* In a 'very fast' shutdown we do not flush the buffer pool: - it is essentially a 'crash' of the InnoDB server. */ - log_make_checkpoint_at(ut_dulint_max, TRUE); - } else { - /* Make sure that the log is all flushed to disk, so that - we can recover all committed transactions in a crash - recovery */ - log_buffer_flush_to_disk(); - } mutex_enter(&(log_sys->mutex)); lsn = log_sys->lsn; - if ((ut_dulint_cmp(lsn, log_sys->last_checkpoint_lsn) != 0 - && !srv_very_fast_shutdown) + if ((ut_dulint_cmp(lsn, log_sys->last_checkpoint_lsn) != 0) #ifdef UNIV_LOG_ARCHIVE || (srv_log_archive_on && ut_dulint_cmp(lsn, @@ -3158,7 +3167,7 @@ loop: completely flushed to disk! (We do not call fil_write... if the 'very fast' shutdown is enabled.) */ - if (!srv_very_fast_shutdown && !buf_all_freed()) { + if (!buf_all_freed()) { goto loop; } @@ -3181,7 +3190,7 @@ loop: /* Make some checks that the server really is quiet */ ut_a(srv_n_threads_active[SRV_MASTER] == 0); - ut_a(srv_very_fast_shutdown || buf_all_freed()); + ut_a(buf_all_freed()); ut_a(0 == ut_dulint_cmp(lsn, log_sys->lsn)); if (ut_dulint_cmp(lsn, srv_start_lsn) < 0) { @@ -3196,15 +3205,7 @@ loop: srv_shutdown_lsn = lsn; - if (!srv_very_fast_shutdown) { - /* In a 'very fast' shutdown we do not flush the buffer pool: - it is essentially a 'crash' of the InnoDB server. Then we must - not write the lsn stamps to the data files, since at a - startup InnoDB deduces from the stamps if the previous - shutdown was clean. */ - fil_write_flushed_lsn_to_data_files(lsn, arch_log_no); - } fil_flush_file_spaces(FIL_TABLESPACE); @@ -3212,7 +3213,7 @@ loop: /* Make some checks that the server really is quiet */ ut_a(srv_n_threads_active[SRV_MASTER] == 0); - ut_a(srv_very_fast_shutdown || buf_all_freed()); + ut_a(buf_all_freed()); ut_a(0 == ut_dulint_cmp(lsn, log_sys->lsn)); } diff --git a/innobase/srv/srv0start.c b/innobase/srv/srv0start.c index 210739b26fd..c65b7d3e141 100644 --- a/innobase/srv/srv0start.c +++ b/innobase/srv/srv0start.c @@ -1740,6 +1740,13 @@ innobase_shutdown_for_mysql(void) srv_shutdown_state = SRV_SHUTDOWN_EXIT_THREADS; + /* In a 'very fast' shutdown, we do not need to wait for these threads + to die; all which counts is that we flushed the log; a 'very fast' + shutdown is essentially a crash. */ + + if (srv_fast_shutdown) + return((int) DB_SUCCESS); + /* All threads end up waiting for certain events. Put those events to the signaled state. Then the threads will exit themselves in os_thread_event_wait(). */ From 1ee822cefc9d417fc920c90054bca197a6400a82 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 9 Mar 2005 01:13:11 +0000 Subject: [PATCH 3/3] Fix test after merge --- mysql-test/r/information_schema.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 47b696db200..e1270168493 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -258,7 +258,7 @@ Function sql_mode Create Function sub1 show create function sub2; Function sql_mode Create Function -sub2 CREATE FUNCTION `test`.`sub2`(i int) RETURNS int +sub2 CREATE FUNCTION `test`.`sub2`(i int) RETURNS int(11) return i+1 drop function sub2; show create procedure sel2;