From bacc6ee071516817881de32923e503548191b8cd Mon Sep 17 00:00:00 2001 From: "anozdrin/alik@ibm." <> Date: Thu, 7 Jun 2007 13:50:22 +0400 Subject: [PATCH 01/26] Fix for BUG#27592: stack overrun when storing datetime value using prepared statements. --- sql/field.cc | 24 ++++++++++++++---------- sql/item_timefunc.cc | 32 +++++++++++++++++++++----------- sql/unireg.h | 1 + 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 8186659ae89..d69324141ce 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4285,7 +4285,7 @@ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg, const char *field_name_arg, struct st_table *table_arg, CHARSET_INFO *cs) - :Field_str(ptr_arg, 19, null_ptr_arg, null_bit_arg, + :Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, cs) { /* For 4.0 MYD and 4.0 InnoDB compatibility */ @@ -4303,7 +4303,8 @@ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg, Field_timestamp::Field_timestamp(bool maybe_null_arg, const char *field_name_arg, struct st_table *table_arg, CHARSET_INFO *cs) - :Field_str((char*) 0, 19, maybe_null_arg ? (uchar*) "": 0, 0, + :Field_str((char*) 0, MAX_DATETIME_WIDTH, + maybe_null_arg ? (uchar*) "": 0, 0, NONE, field_name_arg, table_arg, cs) { /* For 4.0 MYD and 4.0 InnoDB compatibility */ @@ -4834,7 +4835,7 @@ String *Field_time::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { MYSQL_TIME ltime; - val_buffer->alloc(19); + val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); long tmp=(long) sint3korr(ptr); ltime.neg= 0; if (tmp < 0) @@ -5370,7 +5371,7 @@ int Field_newdate::store_time(MYSQL_TIME *ltime, timestamp_type time_type) (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error)) { - char buff[12]; + char buff[MAX_DATE_STRING_REP_LENGTH]; String str(buff, sizeof(buff), &my_charset_latin1); make_date((DATE_TIME_FORMAT *) 0, ltime, &str); set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, @@ -5595,7 +5596,7 @@ int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type) (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error)) { - char buff[19]; + char buff[MAX_DATE_STRING_REP_LENGTH]; String str(buff, sizeof(buff), &my_charset_latin1); make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str); set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, @@ -5669,7 +5670,7 @@ String *Field_datetime::val_str(String *val_buffer, part1=(long) (tmp/LL(1000000)); part2=(long) (tmp - (ulonglong) part1*LL(1000000)); - pos=(char*) val_buffer->ptr()+19; + pos= (char*) val_buffer->ptr() + MAX_DATETIME_WIDTH; *pos--=0; *pos--= (char) ('0'+(char) (part2%10)); part2/=10; *pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10); @@ -8565,15 +8566,18 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, break; case FIELD_TYPE_TIMESTAMP: if (!fld_length) - length= 14; /* Full date YYYYMMDDHHMMSS */ - else if (length != 19) + { + /* Compressed date YYYYMMDDHHMMSS */ + length= MAX_DATETIME_COMPRESSED_WIDTH; + } + else if (length != MAX_DATETIME_WIDTH) { /* We support only even TIMESTAMP lengths less or equal than 14 and 19 as length of 4.1 compatible representation. */ length= ((length+1)/2)*2; /* purecov: inspected */ - length= min(length,14); /* purecov: inspected */ + length= min(length, MAX_DATETIME_COMPRESSED_WIDTH); /* purecov: inspected */ } flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; if (fld_default_value) @@ -8626,7 +8630,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, length= 10; break; case FIELD_TYPE_DATETIME: - length= 19; + length= MAX_DATETIME_WIDTH; break; case FIELD_TYPE_SET: { diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index f5895369a55..5ac0a90c28a 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -51,7 +51,7 @@ static bool make_datetime(date_time_format_types format, MYSQL_TIME *ltime, { char *buff; CHARSET_INFO *cs= &my_charset_bin; - uint length= 30; + uint length= MAX_DATE_STRING_REP_LENGTH; if (str->alloc(length)) return 1; @@ -1400,7 +1400,7 @@ String *Item_date::val_str(String *str) MYSQL_TIME ltime; if (get_date(<ime, TIME_FUZZY_DATE)) return (String *) 0; - if (str->alloc(11)) + if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) { null_value= 1; return (String *) 0; @@ -1449,7 +1449,7 @@ void Item_func_curdate::fix_length_and_dec() String *Item_func_curdate::val_str(String *str) { DBUG_ASSERT(fixed == 1); - if (str->alloc(11)) + if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) { null_value= 1; return (String *) 0; @@ -1678,7 +1678,8 @@ String *Item_func_sec_to_time::val_str(String *str) MYSQL_TIME ltime; longlong arg_val= args[0]->val_int(); - if ((null_value=args[0]->null_value) || str->alloc(19)) + if ((null_value=args[0]->null_value) || + str->alloc(MAX_DATE_STRING_REP_LENGTH)) { null_value= 1; return (String*) 0; @@ -1863,6 +1864,10 @@ String *Item_func_date_format::val_str(String *str) size=max_length; else size=format_length(format); + + if (size < MAX_DATE_STRING_REP_LENGTH) + size= MAX_DATE_STRING_REP_LENGTH; + if (format == str) str= &value; // Save result here if (str->alloc(size)) @@ -1906,13 +1911,14 @@ String *Item_func_from_unixtime::val_str(String *str) if (get_date(&time_tmp, 0)) return 0; - if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN)) + if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) { null_value= 1; return 0; } make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str); + return str; } @@ -1974,14 +1980,15 @@ String *Item_func_convert_tz::val_str(String *str) if (get_date(&time_tmp, 0)) return 0; - - if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN)) + + if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) { null_value= 1; return 0; } - + make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str); + return str; } @@ -2582,6 +2589,7 @@ String *Item_datetime_typecast::val_str(String *str) { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; + if (!get_arg0_date(<ime, TIME_FUZZY_DATE) && !make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME, <ime, str)) @@ -2660,7 +2668,8 @@ String *Item_date_typecast::val_str(String *str) DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (!get_arg0_date(<ime, TIME_FUZZY_DATE) && !str->alloc(11)) + if (!get_arg0_date(<ime, TIME_FUZZY_DATE) && + !str->alloc(MAX_DATE_STRING_REP_LENGTH)) { make_date((DATE_TIME_FORMAT *) 0, <ime, str); return str; @@ -2713,7 +2722,7 @@ String *Item_func_makedate::val_str(String *str) { null_value=0; get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day); - if (str->alloc(11)) + if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) goto err; make_date((DATE_TIME_FORMAT *) 0, &l_time, str); return str; @@ -2849,6 +2858,7 @@ String *Item_func_add_time::val_str(String *str) days= (long)(seconds/86400L); calc_time_from_sec(&l_time3, (long)(seconds%86400L), microseconds); + if (!is_time) { get_date_from_daynr(days,&l_time3.year,&l_time3.month,&l_time3.day); @@ -2964,7 +2974,7 @@ String *Item_func_maketime::val_str(String *str) args[2]->null_value || minute < 0 || minute > 59 || second < 0 || second > 59 || - str->alloc(19)))) + str->alloc(MAX_DATE_STRING_REP_LENGTH)))) return 0; bzero((char *)<ime, sizeof(ltime)); diff --git a/sql/unireg.h b/sql/unireg.h index 886b3d99212..0f7c1709c6c 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -71,6 +71,7 @@ #define MAX_TIME_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */ #define MAX_DATETIME_FULL_WIDTH 29 /* YYYY-MM-DD HH:MM:SS.###### AM */ #define MAX_DATETIME_WIDTH 19 /* YYYY-MM-DD HH:MM:SS */ +#define MAX_DATETIME_COMPRESSED_WIDTH 14 /* YYYYMMDDHHMMSS */ #define MAX_TABLES (sizeof(table_map)*8-3) /* Max tables in join */ #define PARAM_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-3)) From 6dce2955f1b0996152269fe53ee549a7096ba17e Mon Sep 17 00:00:00 2001 From: "anozdrin/alik@ibm." <> Date: Thu, 7 Jun 2007 19:35:29 +0400 Subject: [PATCH 02/26] Add test case for BUG#27592: stack overrun when storing datetime value using prepared statements. --- tests/mysql_client_test.c | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index e56dd693287..d3aa31a9400 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15623,6 +15623,59 @@ static void test_bug27876() } +/* + Bug#27592 (stack overrun when storing datetime value using prepared statements) +*/ + +static void test_bug27592() +{ + const int NUM_ITERATIONS= 40; + int i; + int rc; + MYSQL_STMT *stmt= NULL; + MYSQL_BIND bind[1]; + MYSQL_TIME time_val; + + DBUG_ENTER("test_bug27592"); + myheader("test_bug27592"); + + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + mysql_query(mysql, "CREATE TABLE t1(c2 DATETIME)"); + + stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 VALUES (?)"); + DIE_UNLESS(stmt); + + memset(bind, 0, sizeof(bind)); + + bind[0].buffer_type= MYSQL_TYPE_DATETIME; + bind[0].buffer= (char *) &time_val; + bind[0].length= NULL; + + for (i= 0; i < NUM_ITERATIONS; i++) + { + time_val.year=1000+lrand48()%1000; + time_val.month=1+lrand48()%12; + time_val.day=1+lrand48()%31; + time_val.hour=lrand48()%23; + time_val.minute=lrand48()%59; + time_val.second=lrand48()%59; + + time_val.second_part=0; + time_val.neg=0; + + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt, rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + } + + mysql_stmt_close(stmt); + + DBUG_VOID_RETURN; +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -15904,6 +15957,7 @@ static struct my_tests_st my_tests[]= { { "test_bug21635", test_bug21635 }, { "test_bug24179", test_bug24179 }, { "test_bug27876", test_bug27876 }, + { "test_bug27592", test_bug27592 }, { 0, 0 } }; From 8725f45724c2323b8a160e2e2c0398f8fd25ff45 Mon Sep 17 00:00:00 2001 From: "anozdrin/alik@ibm." <> Date: Thu, 7 Jun 2007 20:46:35 +0400 Subject: [PATCH 03/26] Fix Windows failure. --- tests/mysql_client_test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index d3aa31a9400..bc429f371c8 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15653,12 +15653,12 @@ static void test_bug27592() for (i= 0; i < NUM_ITERATIONS; i++) { - time_val.year=1000+lrand48()%1000; - time_val.month=1+lrand48()%12; - time_val.day=1+lrand48()%31; - time_val.hour=lrand48()%23; - time_val.minute=lrand48()%59; - time_val.second=lrand48()%59; + time_val.year= 2007; + time_val.month= 6; + time_val.day= 7; + time_val.hour= 18; + time_val.minute= 41; + time_val.second= 3; time_val.second_part=0; time_val.neg=0; From 5005ae36569d14692df18f1408fc370519fe9e34 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Sun, 10 Jun 2007 13:16:02 +0400 Subject: [PATCH 04/26] Fix for Bug#28963 "Missing DBUG_RETURN message when stopping event scheduler". Add missing DBUG_VOID_RETURN --- sql/event_scheduler.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 478ae0098da..c552b22e942 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -326,6 +326,8 @@ end: delete event; deinit_event_thread(thd); + + DBUG_VOID_RETURN; } From 6c352d16d933777293d86f9b9069ebdf7b792389 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Sun, 10 Jun 2007 14:43:57 +0400 Subject: [PATCH 05/26] Follow up after work on Bug 4968 Coding style: classes start with a capital letter. Rename some classes related to parsing: create_field -> Create_field foreign_key -> Foreign_key key_part_spec -> Key_part_spec --- sql/field.cc | 36 ++++++++++++++-------------- sql/field.h | 30 +++++++++++------------ sql/item.h | 2 +- sql/item_sum.cc | 4 ++-- sql/mysql_priv.h | 12 +++++----- sql/sp_head.cc | 8 +++---- sql/sp_head.h | 4 ++-- sql/sp_pcontext.cc | 2 +- sql/sp_pcontext.h | 4 ++-- sql/sp_rcontext.cc | 2 +- sql/sql_class.cc | 14 +++++------ sql/sql_class.h | 36 ++++++++++++++-------------- sql/sql_insert.cc | 6 ++--- sql/sql_lex.cc | 2 +- sql/sql_lex.h | 8 +++---- sql/sql_parse.cc | 8 +++---- sql/sql_select.cc | 6 ++--- sql/sql_table.cc | 60 +++++++++++++++++++++++----------------------- sql/sql_yacc.yy | 28 +++++++++++----------- sql/unireg.cc | 54 ++++++++++++++++++++--------------------- 20 files changed, 163 insertions(+), 163 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 6fd88e4e1ac..20bc2e18cf0 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -38,8 +38,8 @@ *****************************************************************************/ #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List; -template class List_iterator; +template class List; +template class List_iterator; #endif uchar Field_null::null[1]={1}; @@ -2631,7 +2631,7 @@ void Field_new_decimal::sql_type(String &str) const } -uint Field_new_decimal::is_equal(create_field *new_field) +uint Field_new_decimal::is_equal(Create_field *new_field) { return ((new_field->sql_type == real_type()) && ((new_field->flags & UNSIGNED_FLAG) == @@ -6100,7 +6100,7 @@ int Field_str::store(double nr) } -uint Field::is_equal(create_field *new_field) +uint Field::is_equal(Create_field *new_field) { return (new_field->sql_type == real_type()); } @@ -6108,7 +6108,7 @@ uint Field::is_equal(create_field *new_field) /* If one of the fields is binary and the other one isn't return 1 else 0 */ -bool Field_str::compare_str_field_flags(create_field *new_field, uint32 flags) +bool Field_str::compare_str_field_flags(Create_field *new_field, uint32 flags) { return (((new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) && !(flags & (BINCMP_FLAG | BINARY_FLAG))) || @@ -6117,7 +6117,7 @@ bool Field_str::compare_str_field_flags(create_field *new_field, uint32 flags) } -uint Field_str::is_equal(create_field *new_field) +uint Field_str::is_equal(Create_field *new_field) { if (compare_str_field_flags(new_field, flags)) return 0; @@ -6969,7 +6969,7 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root, } -uint Field_varstring::is_equal(create_field *new_field) +uint Field_varstring::is_equal(Create_field *new_field) { if (new_field->sql_type == real_type() && new_field->charset == field_charset) @@ -7651,7 +7651,7 @@ uint Field_blob::max_packed_col_length(uint max_length) } -uint Field_blob::is_equal(create_field *new_field) +uint Field_blob::is_equal(Create_field *new_field) { if (compare_str_field_flags(new_field, flags)) return 0; @@ -8187,7 +8187,7 @@ bool Field_num::eq_def(Field *field) } -uint Field_num::is_equal(create_field *new_field) +uint Field_num::is_equal(Create_field *new_field) { return ((new_field->sql_type == real_type()) && ((new_field->flags & UNSIGNED_FLAG) == (uint) (flags & @@ -8616,20 +8616,20 @@ void Field_bit_as_char::sql_type(String &res) const /***************************************************************************** - Handling of field and create_field + Handling of field and Create_field *****************************************************************************/ /* - Convert create_field::length from number of characters to number of bytes + Convert Create_field::length from number of characters to number of bytes SYNOPSIS - create_field::create_length_to_internal_length() + Create_field::create_length_to_internal_length() DESCRIPTION - Convert create_field::length from number of characters to number of bytes. + Convert Create_field::length from number of characters to number of bytes. */ -void create_field::create_length_to_internal_length(void) +void Create_field::create_length_to_internal_length(void) { switch (sql_type) { case MYSQL_TYPE_TINY_BLOB: @@ -8676,7 +8676,7 @@ void create_field::create_length_to_internal_length(void) } -void create_field::init_for_tmp_table(enum_field_types sql_type_arg, +void Create_field::init_for_tmp_table(enum_field_types sql_type_arg, uint32 length_arg, uint32 decimals_arg, bool maybe_null, bool is_unsigned) { @@ -8717,7 +8717,7 @@ void create_field::init_for_tmp_table(enum_field_types sql_type_arg, TRUE on error */ -bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, +bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, char *fld_length, char *fld_decimals, uint fld_type_modifier, Item *fld_default_value, Item *fld_on_update_value, LEX_STRING *fld_comment, @@ -8727,7 +8727,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, uint sign_len, allowed_type_modifier= 0; ulong max_field_charlength= MAX_FIELD_CHARLENGTH; - DBUG_ENTER("create_field::init()"); + DBUG_ENTER("Create_field::init()"); field= 0; field_name= fld_name; @@ -9300,7 +9300,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, /* Create a field suitable for create of table */ -create_field::create_field(Field *old_field,Field *orig_field) +Create_field::Create_field(Field *old_field,Field *orig_field) { field= old_field; field_name=change=old_field->field_name; diff --git a/sql/field.h b/sql/field.h index 54a061276b6..92f6612f9b3 100644 --- a/sql/field.h +++ b/sql/field.h @@ -28,7 +28,7 @@ class Send_field; class Protocol; -class create_field; +class Create_field; struct st_cache_field; int field_conv(Field *to,Field *from); @@ -413,7 +413,7 @@ public: /* maximum possible display length */ virtual uint32 max_display_length()= 0; - virtual uint is_equal(create_field *new_field); + virtual uint is_equal(Create_field *new_field); /* convert decimal to longlong with overflow check */ longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag, int *err); @@ -468,14 +468,14 @@ public: Item_result result_type () const { return REAL_RESULT; } void prepend_zeros(String *value); void add_zerofill_and_unsigned(String &res) const; - friend class create_field; + friend class Create_field; void make_field(Send_field *); uint decimals() const { return (uint) dec; } uint size_of() const { return sizeof(*this); } bool eq_def(Field *field); int store_decimal(const my_decimal *); my_decimal *val_decimal(my_decimal *); - uint is_equal(create_field *new_field); + uint is_equal(Create_field *new_field); int check_int(CHARSET_INFO *cs, const char *str, int length, const char *int_end, int error); bool get_int(CHARSET_INFO *cs, const char *from, uint len, @@ -506,11 +506,11 @@ public: { field_derivation= derivation_arg; } bool binary() const { return field_charset == &my_charset_bin; } uint32 max_display_length() { return field_length; } - friend class create_field; + friend class Create_field; my_decimal *val_decimal(my_decimal *); virtual bool str_needs_quotes() { return TRUE; } - bool compare_str_field_flags(create_field *new_field, uint32 flags); - uint is_equal(create_field *new_field); + bool compare_str_field_flags(Create_field *new_field, uint32 flags); + uint is_equal(Create_field *new_field); }; @@ -614,7 +614,7 @@ public: uint32 max_display_length() { return field_length; } uint size_of() const { return sizeof(*this); } uint32 pack_length() const { return (uint32) bin_size; } - uint is_equal(create_field *new_field); + uint is_equal(Create_field *new_field); }; @@ -1246,7 +1246,7 @@ public: Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit); - uint is_equal(create_field *new_field); + uint is_equal(Create_field *new_field); void hash(ulong *nr, ulong *nr2); }; @@ -1380,7 +1380,7 @@ public: bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } uint32 max_display_length(); - uint is_equal(create_field *new_field); + uint is_equal(Create_field *new_field); }; @@ -1595,7 +1595,7 @@ public: Create field class for CREATE TABLE */ -class create_field :public Sql_alloc +class Create_field :public Sql_alloc { public: const char *field_name; @@ -1626,11 +1626,11 @@ public: uint8 row,col,sc_length,interval_id; // For rea_create_table uint offset,pack_flag; - create_field() :after(0) {} - create_field(Field *field, Field *orig_field); + Create_field() :after(0) {} + Create_field(Field *field, Field *orig_field); /* Used to make a clone of this object for ALTER/CREATE TABLE */ - create_field *clone(MEM_ROOT *mem_root) const - { return new (mem_root) create_field(*this); } + Create_field *clone(MEM_ROOT *mem_root) const + { return new (mem_root) Create_field(*this); } void create_length_to_internal_length(void); /* Init for a tmp table field. To be extended if need be. */ diff --git a/sql/item.h b/sql/item.h index 1c8b46fe0c7..f42977e4e59 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1756,7 +1756,7 @@ public: We have to have a different max_length than 'length' here to ensure that we get the right length if we do use the item to create a new table. In this case max_length must be the maximum - number of chars for a string of this type because we in create_field:: + number of chars for a string of this type because we in Create_field:: divide the max_length with mbmaxlen). */ max_length= str_value.numchars()*cs->mbmaxlen; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 69148d81b9a..1210d00f210 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -912,8 +912,8 @@ void Item_sum_distinct::fix_length_and_dec() bool Item_sum_distinct::setup(THD *thd) { - List field_list; - create_field field_def; /* field definition */ + List field_list; + Create_field field_def; /* field definition */ DBUG_ENTER("Item_sum_distinct::setup"); DBUG_ASSERT(tree == 0); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index f5d66696cab..6d14325c7d9 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -911,7 +911,7 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list, bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list); int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache, KEY_CACHE *dst_cache); -TABLE *create_virtual_tmp_table(THD *thd, List &field_list); +TABLE *create_virtual_tmp_table(THD *thd, List &field_list); bool mysql_xa_recover(THD *thd); @@ -955,8 +955,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, bool table_cant_handle_bit_fields, bool make_copy_field, uint convert_blob_length); -void sp_prepare_create_field(THD *thd, create_field *sql_field); -int prepare_create_field(create_field *sql_field, +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, longlong table_flags); @@ -1181,7 +1181,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum enum_field_types t 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, +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, @@ -1808,12 +1808,12 @@ void unireg_end(void) __attribute__((noreturn)); bool mysql_create_frm(THD *thd, const char *file_name, const char *db, const char *table, HA_CREATE_INFO *create_info, - List &create_field, + List &create_field, uint key_count,KEY *key_info,handler *db_type); int rea_create_table(THD *thd, const char *path, const char *db, const char *table_name, HA_CREATE_INFO *create_info, - List &create_field, + List &create_field, uint key_count,KEY *key_info, handler *file); int format_number(uint inputflag,uint max_length,char * pos,uint length, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 3d5f2f0f50d..1bbf2732081 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -581,7 +581,7 @@ sp_head::init_strings(THD *thd, LEX *lex) static TYPELIB * -create_typelib(MEM_ROOT *mem_root, create_field *field_def, List *src) +create_typelib(MEM_ROOT *mem_root, Create_field *field_def, List *src) { TYPELIB *result= NULL; CHARSET_INFO *cs= field_def->charset; @@ -1905,7 +1905,7 @@ sp_head::backpatch(sp_label_t *lab) } /* - Prepare an instance of create_field for field creation (fill all necessary + Prepare an instance of Create_field for field creation (fill all necessary attributes). SYNOPSIS @@ -1913,7 +1913,7 @@ sp_head::backpatch(sp_label_t *lab) thd [IN] Thread handle lex [IN] Yacc parsing context field_type [IN] Field type - field_def [OUT] An instance of create_field to be filled + field_def [OUT] An instance of Create_field to be filled RETURN FALSE on success @@ -1923,7 +1923,7 @@ sp_head::backpatch(sp_label_t *lab) bool sp_head::fill_field_definition(THD *thd, LEX *lex, enum enum_field_types field_type, - create_field *field_def) + Create_field *field_def) { HA_CREATE_INFO sp_db_info; LEX_STRING cmt = { 0, 0 }; diff --git a/sql/sp_head.h b/sql/sp_head.h index eacc2ccdcf6..89af0259706 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -126,7 +126,7 @@ public: int m_type; uint m_flags; // Boolean attributes of a stored routine - create_field m_return_field_def; /* This is used for FUNCTIONs only. */ + Create_field m_return_field_def; /* This is used for FUNCTIONs only. */ const char *m_tmp_query; // Temporary pointer to sub query string st_sp_chistics *m_chistics; @@ -290,7 +290,7 @@ public: bool fill_field_definition(THD *thd, LEX *lex, enum enum_field_types field_type, - create_field *field_def); + Create_field *field_def); void set_info(longlong created, longlong modified, st_sp_chistics *chistics, ulong sql_mode); diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 9c96485b8ec..8babbd52ff6 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -422,7 +422,7 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped) void -sp_pcontext::retrieve_field_definitions(List *field_def_lst) +sp_pcontext::retrieve_field_definitions(List *field_def_lst) { /* Put local/context fields in the result list. */ diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 3b8b165a6e0..aefc501b3b0 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -43,7 +43,7 @@ typedef struct sp_variable uint offset; Item *dflt; - create_field field_def; + Create_field field_def; } sp_variable_t; @@ -234,7 +234,7 @@ public: children. */ void - retrieve_field_definitions(List *field_def_lst); + retrieve_field_definitions(List *field_def_lst); // Find by name sp_variable_t * diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index e49c4eb1240..b94c733cb0a 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -102,7 +102,7 @@ bool sp_rcontext::init(THD *thd) bool sp_rcontext::init_var_table(THD *thd) { - List field_def_lst; + List field_def_lst; if (!m_root_parsing_ctx->max_var_index()) return FALSE; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 9dbc79344a9..66959940d90 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -59,8 +59,8 @@ const char * const THD::DEFAULT_WHERE= "field list"; /* Used templates */ template class List; template class List_iterator; -template class List; -template class List_iterator; +template class List; +template class List_iterator; template class List; template class List_iterator; template class List; @@ -86,7 +86,7 @@ extern "C" void free_user_var(user_var_entry *entry) my_free((char*) entry,MYF(0)); } -bool key_part_spec::operator==(const key_part_spec& other) const +bool Key_part_spec::operator==(const Key_part_spec& other) const { return length == other.length && !strcmp(field_name, other.field_name); } @@ -115,7 +115,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root) in THD. */ -foreign_key::foreign_key(const foreign_key &rhs, MEM_ROOT *mem_root) +Foreign_key::Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root) :Key(rhs), ref_table(rhs.ref_table), ref_columns(rhs.ref_columns), @@ -160,9 +160,9 @@ bool foreign_key_prefix(Key *a, Key *b) if (a->columns.elements > b->columns.elements) return TRUE; // Can't be prefix - List_iterator col_it1(a->columns); - List_iterator col_it2(b->columns); - const key_part_spec *col1, *col2; + List_iterator col_it1(a->columns); + List_iterator col_it2(b->columns); + const Key_part_spec *col1, *col2; #ifdef ENABLE_WHEN_INNODB_CAN_HANDLE_SWAPED_FOREIGN_KEY_COLUMNS while ((col1= col_it1++)) diff --git a/sql/sql_class.h b/sql/sql_class.h index 7729b597098..38408fb8a0c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -84,14 +84,14 @@ typedef struct st_copy_info { } COPY_INFO; -class key_part_spec :public Sql_alloc { +class Key_part_spec :public Sql_alloc { public: const char *field_name; uint length; - key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {} - bool operator==(const key_part_spec& other) const; + Key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {} + bool operator==(const Key_part_spec& other) const; /** - Construct a copy of this key_part_spec. field_name is copied + Construct a copy of this Key_part_spec. field_name is copied by-pointer as it is known to never change. At the same time 'length' may be reset in mysql_prepare_create_table, and this is why we supply it with a copy. @@ -99,8 +99,8 @@ public: @return If out of memory, 0 is returned and an error is set in THD. */ - key_part_spec *clone(MEM_ROOT *mem_root) const - { return new (mem_root) key_part_spec(*this); } + Key_part_spec *clone(MEM_ROOT *mem_root) const + { return new (mem_root) Key_part_spec(*this); } }; @@ -113,7 +113,7 @@ public: :name(par_name), type(par_type) {} /** Used to make a clone of this object for ALTER/CREATE TABLE - @sa comment for key_part_spec::clone + @sa comment for Key_part_spec::clone */ Alter_drop *clone(MEM_ROOT *mem_root) const { return new (mem_root) Alter_drop(*this); } @@ -128,7 +128,7 @@ public: :name(par_name), def(literal) {} /** Used to make a clone of this object for ALTER/CREATE TABLE - @sa comment for key_part_spec::clone + @sa comment for Key_part_spec::clone */ Alter_column *clone(MEM_ROOT *mem_root) const { return new (mem_root) Alter_column(*this); } @@ -140,13 +140,13 @@ public: enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL, FOREIGN_KEY}; enum Keytype type; KEY_CREATE_INFO key_create_info; - List columns; + List columns; const char *name; bool generated; Key(enum Keytype type_par, const char *name_arg, KEY_CREATE_INFO *key_info_arg, - bool generated_arg, List &cols) + bool generated_arg, List &cols) :type(type_par), key_create_info(*key_info_arg), columns(cols), name(name_arg), generated(generated_arg) {} @@ -156,7 +156,7 @@ public: friend bool foreign_key_prefix(Key *a, Key *b); /** Used to make a clone of this object for ALTER/CREATE TABLE - @sa comment for key_part_spec::clone + @sa comment for Key_part_spec::clone */ virtual Key *clone(MEM_ROOT *mem_root) const { return new (mem_root) Key(*this, mem_root); } @@ -164,7 +164,7 @@ public: class Table_ident; -class foreign_key: public Key { +class Foreign_key: public Key { public: enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL, FK_MATCH_PARTIAL, FK_MATCH_SIMPLE}; @@ -172,23 +172,23 @@ public: FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_DEFAULT}; Table_ident *ref_table; - List ref_columns; + List ref_columns; uint delete_opt, update_opt, match_opt; - foreign_key(const char *name_arg, List &cols, - Table_ident *table, List &ref_cols, + Foreign_key(const char *name_arg, List &cols, + Table_ident *table, List &ref_cols, uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg) :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols), ref_table(table), ref_columns(cols), delete_opt(delete_opt_arg), update_opt(update_opt_arg), match_opt(match_opt_arg) {} - foreign_key(const foreign_key &rhs, MEM_ROOT *mem_root); + Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root); /** Used to make a clone of this object for ALTER/CREATE TABLE - @sa comment for key_part_spec::clone + @sa comment for Key_part_spec::clone */ virtual Key *clone(MEM_ROOT *mem_root) const - { return new (mem_root) foreign_key(*this, mem_root); } + { return new (mem_root) Foreign_key(*this, mem_root); } }; typedef struct st_mysql_lock diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 9067df2f014..8fa444bfd59 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3201,7 +3201,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, MYSQL_LOCK **lock, TABLEOP_HOOKS *hooks) { - TABLE tmp_table; // Used during 'create_field()' + TABLE tmp_table; // Used during 'Create_field()' TABLE_SHARE share; TABLE *table= 0; uint select_field_count= items->elements; @@ -3245,7 +3245,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, while ((item=it++)) { - create_field *cr_field; + Create_field *cr_field; Field *field, *def_field; if (item->type() == Item::FUNC_ITEM) field= item->tmp_table_field(&tmp_table); @@ -3254,7 +3254,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, (Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0, 0); if (!field || - !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? + !(cr_field=new Create_field(field,(item->type() == Item::FIELD_ITEM ? ((Item_field *)item)->field : (Field*) 0)))) DBUG_RETURN(0); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d8846d3fc9d..ab0dad07900 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1132,7 +1132,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) /* Make deep copies of used objects. This is not a fully deep copy - clone() implementations - of Alter_drop, Alter_column, Key, foreign_key, key_part_spec + of Alter_drop, Alter_column, Key, foreign_key, Key_part_spec do not copy string constants. At the same length the only reason we make a copy currently is that ALTER/CREATE TABLE code changes input Alter_info definitions, but string diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6d96dded088..4d056643e51 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -843,7 +843,7 @@ public: List drop_list; List alter_list; List key_list; - List create_list; + List create_list; uint flags; enum enum_enable_or_disable keys_onoff; enum tablespace_op_type tablespace_op; @@ -1132,8 +1132,8 @@ typedef struct st_lex : public Query_tables_list */ LEX_USER *definer; - List col_list; - List ref_list; + List col_list; + List ref_list; List interval_list; List users_list; List columns; @@ -1159,7 +1159,7 @@ typedef struct st_lex : public Query_tables_list List db_list; SQL_LIST proc_list, auxiliary_table_list, save_list; - create_field *last_field; + Create_field *last_field; Item_sum *in_sum_func; udf_func udf; HA_CHECK_OPT check_opt; // check/repair options diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8e55610df36..9cc1f6a3919 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5459,7 +5459,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, List *interval_list, CHARSET_INFO *cs, uint uint_geom_type) { - register create_field *new_field; + register Create_field *new_field; LEX *lex= thd->lex; DBUG_ENTER("add_field_to_list"); @@ -5472,7 +5472,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, if (type_modifier & PRI_KEY_FLAG) { Key *key; - lex->col_list.push_back(new key_part_spec(field_name->str, 0)); + lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); key= new Key(Key::PRIMARY, NullS, &default_key_create_info, 0, lex->col_list); @@ -5482,7 +5482,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG)) { Key *key; - lex->col_list.push_back(new key_part_spec(field_name->str, 0)); + lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); key= new Key(Key::UNIQUE, NullS, &default_key_create_info, 0, lex->col_list); @@ -5540,7 +5540,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, WARN_DEPRECATED(thd, "5.2", buf, "'TIMESTAMP'"); } - if (!(new_field= new create_field()) || + if (!(new_field= new Create_field()) || new_field->init(thd, field_name->str, type, length, decimals, type_modifier, default_value, on_update_value, comment, change, interval_list, cs, uint_geom_type)) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c6ef16d46a3..77bf168f400 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10024,12 +10024,12 @@ err: 0 if out of memory, TABLE object in case of success */ -TABLE *create_virtual_tmp_table(THD *thd, List &field_list) +TABLE *create_virtual_tmp_table(THD *thd, List &field_list) { uint field_count= field_list.elements; uint blob_count= 0; Field **field; - create_field *cdef; /* column definition */ + Create_field *cdef; /* column definition */ uint record_length= 0; uint null_count= 0; /* number of columns which may be null */ uint null_pack_length; /* NULL representation array length */ @@ -10057,7 +10057,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List &field_list) setup_tmp_table_column_bitmaps(table, bitmaps); /* Create all fields and calculate the total length of record */ - List_iterator_fast it(field_list); + List_iterator_fast it(field_list); while ((cdef= it++)) { *field= make_field(share, 0, cdef->length, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index bb0f7649272..503c2cc50a6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -34,12 +34,12 @@ const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end); static int copy_data_between_tables(TABLE *from,TABLE *to, - List &create, bool ignore, + List &create, bool ignore, uint order_num, ORDER *order, ha_rows *copied,ha_rows *deleted, enum enum_enable_or_disable keys_onoff); -static bool prepare_blob_field(THD *thd, create_field *sql_field); +static bool prepare_blob_field(THD *thd, Create_field *sql_field); static bool check_engine(THD *, const char *, HA_CREATE_INFO *); static bool mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, @@ -1920,7 +1920,7 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval, table_flags table flags DESCRIPTION - This function prepares a create_field instance. + This function prepares a Create_field instance. Fields such as pack_flag are valid after this call. RETURN VALUES @@ -1928,7 +1928,7 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval, 1 Error */ -int prepare_create_field(create_field *sql_field, +int prepare_create_field(Create_field *sql_field, uint *blob_columns, int *timestamps, int *timestamps_with_niladic, longlong table_flags) @@ -2119,7 +2119,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, uint *key_count, int select_field_count) { const char *key_name; - create_field *sql_field,*dup_field; + Create_field *sql_field,*dup_field; uint field,null_fields,blob_columns,max_key_length; ulong record_offset= 0; KEY *key_info; @@ -2127,8 +2127,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, int timestamps= 0, timestamps_with_niladic= 0; int field_no,dup_no; int select_field_pos,auto_increment=0; - List_iterator it(alter_info->create_list); - List_iterator it2(alter_info->create_list); + List_iterator it(alter_info->create_list); + List_iterator it2(alter_info->create_list); uint total_uneven_bit_length= 0; DBUG_ENTER("mysql_prepare_create_table"); @@ -2182,7 +2182,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->sql_type == MYSQL_TYPE_ENUM)) { /* - Starting from 5.1 we work here with a copy of create_field + Starting from 5.1 we work here with a copy of Create_field created by the caller, not with the instance that was originally created during parsing. It's OK to create a temporary item and initialize with it a member of the @@ -2473,7 +2473,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (key->type == Key::FOREIGN_KEY) { fk_key_count++; - foreign_key *fk_key= (foreign_key*) key; + Foreign_key *fk_key= (Foreign_key*) key; if (fk_key->ref_columns.elements && fk_key->ref_columns.elements != fk_key->columns.elements) { @@ -2557,7 +2557,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, for (; (key=key_iterator++) ; key_number++) { uint key_length=0; - key_part_spec *column; + Key_part_spec *column; if (key->name == ignore_key) { @@ -2666,12 +2666,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (key_info->block_size) key_info->flags|= HA_USES_BLOCK_SIZE; - List_iterator cols(key->columns), cols2(key->columns); + List_iterator cols(key->columns), cols2(key->columns); CHARSET_INFO *ft_key_charset=0; // for FULLTEXT for (uint column_nr=0 ; (column=cols++) ; column_nr++) { uint length; - key_part_spec *dup_column; + Key_part_spec *dup_column; it.rewind(); field=0; @@ -2981,7 +2981,7 @@ static void set_table_default_charset(THD *thd, In this case the error is given */ -static bool prepare_blob_field(THD *thd, create_field *sql_field) +static bool prepare_blob_field(THD *thd, Create_field *sql_field) { DBUG_ENTER("prepare_blob_field"); @@ -3022,7 +3022,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field) /* - Preparation of create_field for SP function return values. + Preparation of Create_field for SP function return values. Based on code used in the inner loop of mysql_prepare_create_table() above. @@ -3036,7 +3036,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field) */ -void sp_prepare_create_field(THD *thd, create_field *sql_field) +void sp_prepare_create_field(THD *thd, Create_field *sql_field) { if (sql_field->sql_type == MYSQL_TYPE_SET || sql_field->sql_type == MYSQL_TYPE_ENUM) @@ -4928,8 +4928,8 @@ compare_tables(TABLE *table, Field **f_ptr, *field; uint changes= 0, tmp; uint key_count; - List_iterator_fast new_field_it(alter_info->create_list); - create_field *new_field; + List_iterator_fast new_field_it(alter_info->create_list); + Create_field *new_field; KEY_PART_INFO *key_part; KEY_PART_INFO *end; /* @@ -5276,16 +5276,16 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Alter_info *alter_info) { /* New column definitions are added here */ - List new_create_list; + List new_create_list; /* New key definitions are added here */ List new_key_list; List_iterator drop_it(alter_info->drop_list); - List_iterator def_it(alter_info->create_list); + List_iterator def_it(alter_info->create_list); List_iterator alter_it(alter_info->alter_list); List_iterator key_it(alter_info->key_list); - List_iterator find_it(new_create_list); - List_iterator field_it(new_create_list); - List key_parts; + List_iterator find_it(new_create_list); + List_iterator field_it(new_create_list); + List key_parts; uint db_create_options= (table->s->db_create_options & ~(HA_OPTION_PACK_RECORD)); uint used_fields= create_info->used_fields; @@ -5325,7 +5325,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, create_info->tablespace= tablespace; } restore_record(table, s->default_values); // Empty record for DEFAULT - create_field *def; + Create_field *def; /* First collect all fields from table which isn't in drop_list @@ -5381,7 +5381,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, This field was not dropped and not changed, add it to the list for the new table. */ - def= new create_field(field, field); + def= new Create_field(field, field); new_create_list.push_back(def); alter_it.rewind(); // Change default if ALTER Alter_column *alter; @@ -5419,7 +5419,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, new_create_list.push_front(def); else { - create_field *find; + Create_field *find; find_it.rewind(); while ((find=find_it++)) // Add new columns { @@ -5476,7 +5476,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (!key_part->field) continue; // Wrong field (from UNIREG) const char *key_part_name=key_part->field->field_name; - create_field *cfield; + Create_field *cfield; field_it.rewind(); while ((cfield=field_it++)) { @@ -5518,7 +5518,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, key_part_length= 0; // Use whole field } key_part_length /= key_part->field->charset()->mbmaxlen; - key_parts.push_back(new key_part_spec(cfield->field_name, + key_parts.push_back(new Key_part_spec(cfield->field_name, key_part_length)); } if (key_parts.elements) @@ -6687,7 +6687,7 @@ err_with_placeholders: static int copy_data_between_tables(TABLE *from,TABLE *to, - List &create, + List &create, bool ignore, uint order_num, ORDER *order, ha_rows *copied, @@ -6740,8 +6740,8 @@ copy_data_between_tables(TABLE *from,TABLE *to, save_sql_mode= thd->variables.sql_mode; - List_iterator it(create); - create_field *def; + List_iterator it(create); + Create_field *def; copy_end=copy; for (Field **ptr=to->field ; *ptr ; ptr++) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7875b4244a8..c9297e37802 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -456,7 +456,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, List *item_list; List *string_list; String *string; - key_part_spec *key_part; + Key_part_spec *key_part; TABLE_LIST *table_list; udf_func *udf; LEX_USER *lex_user; @@ -4498,7 +4498,7 @@ key_def: { LEX *lex=Lex; const char *key_name= $4 ? $4 : $1; - Key *key= new foreign_key(key_name, lex->col_list, + Key *key= new Foreign_key(key_name, lex->col_list, $8, lex->ref_list, lex->fk_delete_opt, @@ -4925,8 +4925,8 @@ opt_ref_list: | '(' ref_list ')' opt_on_delete {}; ref_list: - ref_list ',' ident { Lex->ref_list.push_back(new key_part_spec($3.str)); } - | ident { Lex->ref_list.push_back(new key_part_spec($1.str)); }; + ref_list ',' ident { Lex->ref_list.push_back(new Key_part_spec($3.str)); } + | ident { Lex->ref_list.push_back(new Key_part_spec($1.str)); }; opt_on_delete: @@ -4940,16 +4940,16 @@ opt_on_delete_list: opt_on_delete_item: ON DELETE_SYM delete_option { Lex->fk_delete_opt= $3; } | ON UPDATE_SYM delete_option { Lex->fk_update_opt= $3; } - | MATCH FULL { Lex->fk_match_option= foreign_key::FK_MATCH_FULL; } - | MATCH PARTIAL { Lex->fk_match_option= foreign_key::FK_MATCH_PARTIAL; } - | MATCH SIMPLE_SYM { Lex->fk_match_option= foreign_key::FK_MATCH_SIMPLE; }; + | MATCH FULL { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; } + | MATCH PARTIAL { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; } + | MATCH SIMPLE_SYM { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; }; delete_option: - RESTRICT { $$= (int) foreign_key::FK_OPTION_RESTRICT; } - | CASCADE { $$= (int) foreign_key::FK_OPTION_CASCADE; } - | SET NULL_SYM { $$= (int) foreign_key::FK_OPTION_SET_NULL; } - | NO_SYM ACTION { $$= (int) foreign_key::FK_OPTION_NO_ACTION; } - | SET DEFAULT { $$= (int) foreign_key::FK_OPTION_DEFAULT; }; + RESTRICT { $$= (int) Foreign_key::FK_OPTION_RESTRICT; } + | CASCADE { $$= (int) Foreign_key::FK_OPTION_CASCADE; } + | SET NULL_SYM { $$= (int) Foreign_key::FK_OPTION_SET_NULL; } + | NO_SYM ACTION { $$= (int) Foreign_key::FK_OPTION_NO_ACTION; } + | SET DEFAULT { $$= (int) Foreign_key::FK_OPTION_DEFAULT; }; key_type: key_or_index { $$= Key::MULTIPLE; } @@ -5061,7 +5061,7 @@ key_list: | key_part order_dir { Lex->col_list.push_back($1); }; key_part: - ident { $$=new key_part_spec($1.str); } + ident { $$=new Key_part_spec($1.str); } | ident '(' NUM ')' { int key_part_len= atoi($3.str); @@ -5069,7 +5069,7 @@ key_part: { my_error(ER_KEY_PART_0, MYF(0), $1.str); } - $$=new key_part_spec($1.str,(uint) key_part_len); + $$=new Key_part_spec($1.str,(uint) key_part_len); }; opt_ident: diff --git a/sql/unireg.cc b/sql/unireg.cc index a02d24d8ae5..57847bc70c6 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -29,21 +29,21 @@ #define FCOMP 17 /* Bytes for a packed field */ -static uchar * pack_screens(List &create_fields, +static uchar * pack_screens(List &create_fields, uint *info_length, uint *screens, bool small_file); static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info, ulong data_offset); static bool pack_header(uchar *forminfo,enum legacy_db_type table_type, - List &create_fields, + List &create_fields, uint info_length, uint screens, uint table_options, ulong data_offset, handler *file); -static uint get_interval_id(uint *int_count,List &create_fields, - create_field *last_field); -static bool pack_fields(File file, List &create_fields, +static uint get_interval_id(uint *int_count,List &create_fields, + Create_field *last_field); +static bool pack_fields(File file, List &create_fields, ulong data_offset); static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type, uint table_options, - List &create_fields, + List &create_fields, uint reclength, ulong data_offset, handler *handler); @@ -70,7 +70,7 @@ static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type, bool mysql_create_frm(THD *thd, const char *file_name, const char *db, const char *table, HA_CREATE_INFO *create_info, - List &create_fields, + List &create_fields, uint keys, KEY *key_info, handler *db_file) { @@ -294,8 +294,8 @@ bool mysql_create_frm(THD *thd, const char *file_name, Restore all UCS2 intervals. HEX representation of them is not needed anymore. */ - List_iterator it(create_fields); - create_field *field; + List_iterator it(create_fields); + Create_field *field; while ((field=it++)) { if (field->save_interval) @@ -341,7 +341,7 @@ err3: int rea_create_table(THD *thd, const char *path, const char *db, const char *table_name, HA_CREATE_INFO *create_info, - List &create_fields, + List &create_fields, uint keys, KEY *key_info, handler *file) { DBUG_ENTER("rea_create_table"); @@ -371,7 +371,7 @@ err_handler: /* Pack screens to a screen for save in a form-file */ -static uchar *pack_screens(List &create_fields, +static uchar *pack_screens(List &create_fields, uint *info_length, uint *screens, bool small_file) { @@ -380,7 +380,7 @@ static uchar *pack_screens(List &create_fields, uint length,cols; uchar *info,*pos,*start_screen; uint fields=create_fields.elements; - List_iterator it(create_fields); + List_iterator it(create_fields); DBUG_ENTER("pack_screens"); start_row=4; end_row=22; cols=80; fields_on_screen=end_row+1-start_row; @@ -388,7 +388,7 @@ static uchar *pack_screens(List &create_fields, *screens=(fields-1)/fields_on_screen+1; length= (*screens) * (SC_INFO_LENGTH+ (cols>> 1)+4); - create_field *field; + Create_field *field; while ((field=it++)) length+=(uint) strlen(field->field_name)+1+TE_INFO_LENGTH+cols/2; @@ -401,7 +401,7 @@ static uchar *pack_screens(List &create_fields, it.rewind(); for (i=0 ; i < fields ; i++) { - create_field *cfield=it++; + Create_field *cfield=it++; if (row++ == end_row) { if (i) @@ -521,7 +521,7 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo, /* Make formheader */ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, - List &create_fields, + List &create_fields, uint info_length, uint screens, uint table_options, ulong data_offset, handler *file) { @@ -544,8 +544,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, /* Check fields */ - List_iterator it(create_fields); - create_field *field; + List_iterator it(create_fields); + Create_field *field; while ((field=it++)) { uint tmp_len= system_charset_info->cset->charpos(system_charset_info, @@ -687,11 +687,11 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, /* get each unique interval each own id */ -static uint get_interval_id(uint *int_count,List &create_fields, - create_field *last_field) +static uint get_interval_id(uint *int_count,List &create_fields, + Create_field *last_field) { - List_iterator it(create_fields); - create_field *field; + List_iterator it(create_fields); + Create_field *field; TYPELIB *interval=last_field->interval; while ((field=it++) != last_field) @@ -715,18 +715,18 @@ static uint get_interval_id(uint *int_count,List &create_fields, /* Save fields, fieldnames and intervals */ -static bool pack_fields(File file, List &create_fields, +static bool pack_fields(File file, List &create_fields, ulong data_offset) { reg2 uint i; uint int_count, comment_length=0; uchar buff[MAX_FIELD_WIDTH]; - create_field *field; + Create_field *field; DBUG_ENTER("pack_fields"); /* Write field info */ - List_iterator it(create_fields); + List_iterator it(create_fields); int_count=0; while ((field=it++)) @@ -856,7 +856,7 @@ static bool pack_fields(File file, List &create_fields, static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type, uint table_options, - List &create_fields, + List &create_fields, uint reclength, ulong data_offset, handler *handler) @@ -867,7 +867,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type, uchar *buff,*null_pos; TABLE table; TABLE_SHARE share; - create_field *field; + Create_field *field; enum_check_fields old_count_cuted_fields= thd->count_cuted_fields; DBUG_ENTER("make_empty_rec"); @@ -893,7 +893,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type, } null_pos= buff; - List_iterator it(create_fields); + List_iterator it(create_fields); thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values while ((field=it++)) { From 041c7797a9e2ba7d17ff6da15bfc452fd8ab5324 Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Mon, 11 Jun 2007 15:20:46 -0600 Subject: [PATCH 06/26] Manual merge of Bug 27592 (5.0-runtime to 5.1-runtime) --- sql/field.cc | 24 +++++++++-------- tests/mysql_client_test.c | 54 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 20bc2e18cf0..ae259b7a91b 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4397,7 +4397,7 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, const char *field_name_arg, TABLE_SHARE *share, CHARSET_INFO *cs) - :Field_str(ptr_arg, 19, null_ptr_arg, null_bit_arg, + :Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, cs) { /* For 4.0 MYD and 4.0 InnoDB compatibility */ @@ -4414,7 +4414,8 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, Field_timestamp::Field_timestamp(bool maybe_null_arg, const char *field_name_arg, CHARSET_INFO *cs) - :Field_str((uchar*) 0, 19, maybe_null_arg ? (uchar*) "": 0, 0, + :Field_str((uchar*) 0, MAX_DATETIME_WIDTH, + maybe_null_arg ? (uchar*) "": 0, 0, NONE, field_name_arg, cs) { /* For 4.0 MYD and 4.0 InnoDB compatibility */ @@ -4947,7 +4948,7 @@ String *Field_time::val_str(String *val_buffer, { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; - val_buffer->alloc(19); + val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); long tmp=(long) sint3korr(ptr); ltime.neg= 0; if (tmp < 0) @@ -5495,7 +5496,7 @@ int Field_newdate::store_time(MYSQL_TIME *ltime,timestamp_type time_type) (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error)) { - char buff[12]; + char buff[MAX_DATE_STRING_REP_LENGTH]; String str(buff, sizeof(buff), &my_charset_latin1); make_date((DATE_TIME_FORMAT *) 0, ltime, &str); set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, @@ -5726,7 +5727,7 @@ int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type) (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error)) { - char buff[19]; + char buff[MAX_DATE_STRING_REP_LENGTH]; String str(buff, sizeof(buff), &my_charset_latin1); make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str); set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, @@ -5802,7 +5803,7 @@ String *Field_datetime::val_str(String *val_buffer, part1=(long) (tmp/LL(1000000)); part2=(long) (tmp - (ulonglong) part1*LL(1000000)); - pos=(char*) val_buffer->ptr()+19; + pos=(char*) val_buffer->ptr() + MAX_DATETIME_WIDTH; *pos--=0; *pos--= (char) ('0'+(char) (part2%10)); part2/=10; *pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10); @@ -8911,15 +8912,18 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, break; case MYSQL_TYPE_TIMESTAMP: if (!fld_length) - length= 14; /* Full date YYYYMMDDHHMMSS */ - else if (length != 19) + { + /* Compressed date YYYYMMDDHHMMSS */ + length= MAX_DATETIME_COMPRESSED_WIDTH; + } + else if (length != MAX_DATETIME_WIDTH) { /* We support only even TIMESTAMP lengths less or equal than 14 and 19 as length of 4.1 compatible representation. */ length= ((length+1)/2)*2; /* purecov: inspected */ - length= min(length,14); /* purecov: inspected */ + length= min(length, MAX_DATETIME_COMPRESSED_WIDTH); /* purecov: inspected */ } flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; if (fld_default_value) @@ -8972,7 +8976,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, length= 10; break; case MYSQL_TYPE_DATETIME: - length= 19; + length= MAX_DATETIME_WIDTH; break; case MYSQL_TYPE_SET: { diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index a093456427f..ef80c2fd05d 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -16161,6 +16161,59 @@ static void test_bug27876() } +/* + Bug#27592 (stack overrun when storing datetime value using prepared statements) +*/ + +static void test_bug27592() +{ + const int NUM_ITERATIONS= 40; + int i; + int rc; + MYSQL_STMT *stmt= NULL; + MYSQL_BIND bind[1]; + MYSQL_TIME time_val; + + DBUG_ENTER("test_bug27592"); + myheader("test_bug27592"); + + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + mysql_query(mysql, "CREATE TABLE t1(c2 DATETIME)"); + + stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 VALUES (?)"); + DIE_UNLESS(stmt); + + memset(bind, 0, sizeof(bind)); + + bind[0].buffer_type= MYSQL_TYPE_DATETIME; + bind[0].buffer= (char *) &time_val; + bind[0].length= NULL; + + for (i= 0; i < NUM_ITERATIONS; i++) + { + time_val.year= 2007; + time_val.month= 6; + time_val.day= 7; + time_val.hour= 18; + time_val.minute= 41; + time_val.second= 3; + + time_val.second_part=0; + time_val.neg=0; + + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt, rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + } + + mysql_stmt_close(stmt); + + DBUG_VOID_RETURN; +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -16449,6 +16502,7 @@ static struct my_tests_st my_tests[]= { { "test_bug28075", test_bug28075 }, #endif { "test_bug27876", test_bug27876 }, + { "test_bug27592", test_bug27592 }, { 0, 0 } }; From 586ef7052af79cc44a8305a5a4f1346dd9af4535 Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Mon, 11 Jun 2007 18:08:51 -0600 Subject: [PATCH 07/26] Resolved merge conflicts --- sql/field.cc | 2 +- sql/field.h | 2 +- sql/sql_lex.h | 37 +++++++++++++++++++++++++++---------- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 9f412e99223..3e4d27b2e38 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8264,7 +8264,7 @@ Field *Field_bit::new_key_field(MEM_ROOT *root, } -uint Field_bit::is_equal(create_field *new_field) +uint Field_bit::is_equal(Create_field *new_field) { return (new_field->sql_type == real_type() && new_field->length == max_display_length()); diff --git a/sql/field.h b/sql/field.h index 34ba540d2ea..7a64b04d5e5 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1563,7 +1563,7 @@ public: bit_ptr == ((Field_bit *)field)->bit_ptr && bit_ofs == ((Field_bit *)field)->bit_ofs); } - uint is_equal(create_field *new_field); + uint is_equal(Create_field *new_field); void move_field_offset(my_ptrdiff_t ptr_diff) { Field::move_field_offset(ptr_diff); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 4d056643e51..2cecf282e7f 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -830,6 +830,13 @@ inline bool st_select_lex_unit::is_union () #define ALTER_REMOVE_PARTITIONING (1L << 25) #define ALTER_FOREIGN_KEY (1L << 26) +enum enum_alter_table_change_level +{ + ALTER_TABLE_METADATA_ONLY= 0, + ALTER_TABLE_DATA_CHANGED= 1, + ALTER_TABLE_INDEX_CHANGED= 2 +}; + /** @brief Parsing data for CREATE or ALTER TABLE. @@ -840,21 +847,28 @@ inline bool st_select_lex_unit::is_union () class Alter_info { public: - List drop_list; - List alter_list; - List key_list; - List create_list; - uint flags; - enum enum_enable_or_disable keys_onoff; - enum tablespace_op_type tablespace_op; - List partition_names; - uint no_parts; + List drop_list; + List alter_list; + List key_list; + List create_list; + uint flags; + enum enum_enable_or_disable keys_onoff; + enum tablespace_op_type tablespace_op; + List partition_names; + uint no_parts; + enum_alter_table_change_level change_level; + Create_field *datetime_field; + bool error_if_not_empty; + Alter_info() : flags(0), keys_onoff(LEAVE_AS_IS), tablespace_op(NO_TABLESPACE_OP), - no_parts(0) + no_parts(0), + change_level(ALTER_TABLE_METADATA_ONLY), + datetime_field(NULL), + error_if_not_empty(FALSE) {} void reset() @@ -868,6 +882,9 @@ public: tablespace_op= NO_TABLESPACE_OP; no_parts= 0; partition_names.empty(); + change_level= ALTER_TABLE_METADATA_ONLY; + datetime_field= 0; + error_if_not_empty= FALSE; } /** Construct a copy of this object to be used for mysql_alter_table From a508260b85382f06dd9d97507839030ff9bae8d3 Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Tue, 12 Jun 2007 15:23:58 -0600 Subject: [PATCH 08/26] Bug#25411 (trigger code truncated), PART II Bug 28127 (Some valid identifiers names are not parsed correctly) Bug 26302 (MySQL server cuts off trailing "*/" from comments in SP/func) This patch is the second part of a major cleanup, required to fix Bug 25411 (trigger code truncated). The root cause of the issue stems from the function skip_rear_comments, which was a work around to remove "extra" "*/" characters from the query text, when parsing a query and reusing the text fragments to represent a view, trigger, function or stored procedure. The reason for this work around is that "special comments", like /*!50002 XXX */, were not parsed properly, so that a query like: AAA /*!50002 BBB */ CCC would be seen by the parser as "AAA BBB */ CCC" when the current version is greater or equal to 5.0.2 The root cause of this stems from how special comments are parsed. Special comments are really out-of-bound text that appear inside a query, that affects how the parser behave. In nature, /*!50002 XXX */ in MySQL is similar to the C concept of preprocessing : #if VERSION >= 50002 XXX #endif Depending on the current VERSION of the server, either the special comment should be expanded or it should be ignored, but in all cases the "text" of the query should be re-written to strip the "/*!50002" and "*/" markers, which does not belong to the SQL language itself. Prior to this fix, these markers would leak into : - the storage format for VIEW, - the storage format for FUNCTION, - the storage format for FUNCTION parameters, in mysql.proc (param_list), - the storage format for PROCEDURE, - the storage format for PROCEDURE parameters, in mysql.proc (param_list), - the storage format for TRIGGER, - the binary log used for replication. In all cases, not only this cause format corruption, but also provide a vector for dormant security issues, by allowing to tunnel code that will be activated after an upgrade. The proper solution is to deal with special comments strictly during parsing, when accepting a query from the outside world. Once a query is parsed and an object is created with a persistant representation, this object should not arbitrarily mutate after an upgrade. In short, special comments are a useful but limited feature for MYSQLdump, when used at an *interface* level to facilitate import/export, but bloating the server *internal* storage format is *not* the proper way to deal with configuration management of the user logic. With this fix: - the Lex_input_stream class now acts as a comment pre-processor, and either expands or ignore special comments on the fly. - MYSQLlex and sql_yacc.yy have been cleaned up to strictly use the public interface of Lex_input_stream. In particular, how the input stream accepts or rejects a character is private to Lex_input_stream, and the internal buffer pointers of that class are strictly private, and should not be tempered with during parsing. This caused many changes mostly in sql_lex.cc. During the code cleanup in case MY_LEX_NUMBER_IDENT, Bug 28127 (Some valid identifiers names are not parsed correctly) was found and fixed. By parsing special comments properly, and removing the function 'skip_rear_comments' [sic], Bug 26302 (MySQL server cuts off trailing "*/" from comments in SP/func) has been fixed as well. --- mysql-test/r/comments.result | 12 +- mysql-test/r/sp.result | 128 +++++++- mysql-test/r/trigger.result | 16 + mysql-test/r/varbinary.result | 16 + mysql-test/t/comments.test | 15 + mysql-test/t/sp.test | 81 ++++- mysql-test/t/trigger.test | 27 ++ mysql-test/t/varbinary.test | 19 ++ sql/event_data_objects.cc | 56 +--- sql/sp.cc | 12 +- sql/sp_head.cc | 19 +- sql/sql_lex.cc | 560 ++++++++++++++++++++-------------- sql/sql_lex.h | 334 ++++++++++++++++++-- sql/sql_trigger.cc | 22 +- sql/sql_view.cc | 11 +- sql/sql_yacc.yy | 125 ++++---- 16 files changed, 1063 insertions(+), 390 deletions(-) diff --git a/mysql-test/r/comments.result b/mysql-test/r/comments.result index a9106ce0538..98921c561d1 100644 --- a/mysql-test/r/comments.result +++ b/mysql-test/r/comments.result @@ -8,7 +8,7 @@ multi line comment */; ; ERROR 42000: Query was empty select 1 /*!32301 +1 */; -1 /*!32301 +1 +1 +1 2 select 1 /*!52301 +1 */; 1 @@ -26,3 +26,13 @@ select 1 # The rest of the row will be ignored 1 1 /* line with only comment */; +select 1/*!2*/; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '2*/' at line 1 +select 1/*!000002*/; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '2*/' at line 1 +select 1/*!999992*/; +1 +1 +select 1 + /*!00000 2 */ + 3 /*!99999 noise*/ + 4; +1 + 2 + 3 + 4 +10 diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 86ede7a8f00..0d1e18174ab 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6282,4 +6282,130 @@ v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VI DROP VIEW v1; DROP FUNCTION metered; DROP TABLE t1; -End of 5.0 tests +drop procedure if exists proc_25411_a; +drop procedure if exists proc_25411_b; +drop procedure if exists proc_25411_c; +create procedure proc_25411_a() +begin +/* real comment */ +select 1; +/*! select 2; */ +select 3; +/*!00000 select 4; */ +/*!99999 select 5; */ +end +$$ +create procedure proc_25411_b( +/* real comment */ +/*! p1 int, */ +/*!00000 p2 int */ +/*!99999 ,p3 int */ +) +begin +select p1, p2; +end +$$ +create procedure proc_25411_c() +begin +select 1/*!,2*//*!00000,3*//*!99999,4*/; +select 1/*! ,2*//*!00000 ,3*//*!99999 ,4*/; +select 1/*!,2 *//*!00000,3 *//*!99999,4 */; +select 1/*! ,2 *//*!00000 ,3 *//*!99999 ,4 */; +select 1 /*!,2*/ /*!00000,3*/ /*!99999,4*/ ; +end +$$ +show create procedure proc_25411_a; +Procedure sql_mode Create Procedure +proc_25411_a CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_25411_a`() +begin +/* real comment */ +select 1; + select 2; +select 3; + select 4; + +end +call proc_25411_a(); +1 +1 +2 +2 +3 +3 +4 +4 +show create procedure proc_25411_b; +Procedure sql_mode Create Procedure +proc_25411_b CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_25411_b`( +/* real comment */ + p1 int, + p2 int + +) +begin +select p1, p2; +end +select name, param_list, body from mysql.proc where name like "%25411%"; +name param_list body +proc_25411_a begin +/* real comment */ +select 1; + select 2; +select 3; + select 4; + +end +proc_25411_b +/* real comment */ + p1 int, + p2 int + + begin +select p1, p2; +end +proc_25411_c begin +select 1,2,3; +select 1 ,2 ,3; +select 1,2 ,3 ; +select 1 ,2 ,3 ; +select 1 ,2 ,3 ; +end +call proc_25411_b(10, 20); +p1 p2 +10 20 +show create procedure proc_25411_c; +Procedure sql_mode Create Procedure +proc_25411_c CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_25411_c`() +begin +select 1,2,3; +select 1 ,2 ,3; +select 1,2 ,3 ; +select 1 ,2 ,3 ; +select 1 ,2 ,3 ; +end +call proc_25411_c(); +1 2 3 +1 2 3 +1 2 3 +1 2 3 +1 2 3 +1 2 3 +1 2 3 +1 2 3 +1 2 3 +1 2 3 +drop procedure proc_25411_a; +drop procedure proc_25411_b; +drop procedure proc_25411_c; +drop procedure if exists proc_26302; +create procedure proc_26302() +select 1 /* testing */; +show create procedure proc_26302; +Procedure sql_mode Create Procedure +proc_26302 CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_26302`() +select 1 /* testing */ +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES +where ROUTINE_NAME = "proc_26302"; +ROUTINE_NAME ROUTINE_DEFINITION +proc_26302 select 1 /* testing */ +drop procedure proc_26302; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 1b4e0decb62..89194ca485a 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -1474,3 +1474,19 @@ DROP TABLE t1,t2; SET SESSION LOW_PRIORITY_UPDATES=DEFAULT; SET GLOBAL LOW_PRIORITY_UPDATES=DEFAULT; End of 5.0 tests +drop table if exists table_25411_a; +drop table if exists table_25411_b; +create table table_25411_a(a int); +create table table_25411_b(b int); +create trigger trg_25411a_ai after insert on table_25411_a +for each row +insert into table_25411_b select new.*; +select * from table_25411_a; +a +insert into table_25411_a values (1); +ERROR 42S02: Unknown table 'new' +select * from table_25411_a; +a +1 +drop table table_25411_a; +drop table table_25411_b; diff --git a/mysql-test/r/varbinary.result b/mysql-test/r/varbinary.result index f30af2ea1a3..6d39d8301c5 100644 --- a/mysql-test/r/varbinary.result +++ b/mysql-test/r/varbinary.result @@ -79,3 +79,19 @@ select length(a) from t1; length(a) 6 drop table t1; +drop table if exists table_28127_a; +drop table if exists table_28127_b; +create table table_28127_a(0b02 int); +show create table table_28127_a; +Table Create Table +table_28127_a CREATE TABLE `table_28127_a` ( + `0b02` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +create table table_28127_b(0b2 int); +show create table table_28127_b; +Table Create Table +table_28127_b CREATE TABLE `table_28127_b` ( + `0b2` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table table_28127_a; +drop table table_28127_b; diff --git a/mysql-test/t/comments.test b/mysql-test/t/comments.test index 52273ec9523..8ae6ba5779e 100644 --- a/mysql-test/t/comments.test +++ b/mysql-test/t/comments.test @@ -19,3 +19,18 @@ select 1 # The rest of the row will be ignored /* line with only comment */; # End of 4.1 tests + +# +# Bug#25411 (trigger code truncated) +# + +--error ER_PARSE_ERROR +select 1/*!2*/; + +--error ER_PARSE_ERROR +select 1/*!000002*/; + +select 1/*!999992*/; + +select 1 + /*!00000 2 */ + 3 /*!99999 noise*/ + 4; + diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 08a01be0fd2..8a56d5550ec 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -7251,4 +7251,83 @@ DROP FUNCTION metered; DROP TABLE t1; ---echo End of 5.0 tests +# +# Bug#25411 (trigger code truncated) +# + +--disable_warnings +drop procedure if exists proc_25411_a; +drop procedure if exists proc_25411_b; +drop procedure if exists proc_25411_c; +--enable_warnings + +delimiter $$; + +create procedure proc_25411_a() +begin + /* real comment */ + select 1; + /*! select 2; */ + select 3; + /*!00000 select 4; */ + /*!99999 select 5; */ +end +$$ + +create procedure proc_25411_b( +/* real comment */ +/*! p1 int, */ +/*!00000 p2 int */ +/*!99999 ,p3 int */ +) +begin + select p1, p2; +end +$$ + +create procedure proc_25411_c() +begin + select 1/*!,2*//*!00000,3*//*!99999,4*/; + select 1/*! ,2*//*!00000 ,3*//*!99999 ,4*/; + select 1/*!,2 *//*!00000,3 *//*!99999,4 */; + select 1/*! ,2 *//*!00000 ,3 *//*!99999 ,4 */; + select 1 /*!,2*/ /*!00000,3*/ /*!99999,4*/ ; +end +$$ + +delimiter ;$$ + +show create procedure proc_25411_a; +call proc_25411_a(); + +show create procedure proc_25411_b; +select name, param_list, body from mysql.proc where name like "%25411%"; +call proc_25411_b(10, 20); + +show create procedure proc_25411_c; +call proc_25411_c(); + +drop procedure proc_25411_a; +drop procedure proc_25411_b; +drop procedure proc_25411_c; + + +# +# Bug#26302 (MySQL server cuts off trailing "*/" from comments in SP/func) +# + +--disable_warnings +drop procedure if exists proc_26302; +--enable_warnings + +create procedure proc_26302() +select 1 /* testing */; + +show create procedure proc_26302; + +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES +where ROUTINE_NAME = "proc_26302"; + +drop procedure proc_26302; + + diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 31739f6c41b..89a680b87ce 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -1819,3 +1819,30 @@ SET SESSION LOW_PRIORITY_UPDATES=DEFAULT; SET GLOBAL LOW_PRIORITY_UPDATES=DEFAULT; --echo End of 5.0 tests + +# +# Bug#25411 (trigger code truncated) +# + +--disable_warnings +drop table if exists table_25411_a; +drop table if exists table_25411_b; +--enable_warnings + +create table table_25411_a(a int); +create table table_25411_b(b int); + +create trigger trg_25411a_ai after insert on table_25411_a +for each row + insert into table_25411_b select new.*; + +select * from table_25411_a; + +--error ER_BAD_TABLE_ERROR +insert into table_25411_a values (1); + +select * from table_25411_a; + +drop table table_25411_a; +drop table table_25411_b; + diff --git a/mysql-test/t/varbinary.test b/mysql-test/t/varbinary.test index 2d055920c22..9ccbac7cdda 100644 --- a/mysql-test/t/varbinary.test +++ b/mysql-test/t/varbinary.test @@ -85,3 +85,22 @@ alter table t1 modify a varchar(255); select length(a) from t1; drop table t1; + +# +# Bug#28127 (Some valid identifiers names are not parsed correctly) +# + +--disable_warnings +drop table if exists table_28127_a; +drop table if exists table_28127_b; +--enable_warnings + +create table table_28127_a(0b02 int); +show create table table_28127_a; + +create table table_28127_b(0b2 int); +show create table table_28127_b; + +drop table table_28127_a; +drop table table_28127_b; + diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 138752d5ce5..c95a6c5dd3c 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -148,8 +148,7 @@ Event_parse_data::init_name(THD *thd, sp_name *spn) The body is extracted by copying all data between the start of the body set by another method and the current pointer in Lex. - Some questionable removal of characters is done in here, and that part - should be refactored when the parser is smarter. + See related code in sp_head::init_strings(). */ void @@ -160,58 +159,9 @@ Event_parse_data::init_body(THD *thd) /* This method is called from within the parser, from sql_yacc.yy */ DBUG_ASSERT(thd->m_lip != NULL); - DBUG_PRINT("info", ("body: '%s' body_begin: 0x%lx end: 0x%lx", body_begin, - (long) body_begin, (long) thd->m_lip->ptr)); - - body.length= thd->m_lip->ptr - body_begin; - const char *body_end= body_begin + body.length - 1; - - /* Trim nuls or close-comments ('*'+'/') or spaces at the end */ - while (body_begin < body_end) - { - - if ((*body_end == '\0') || - (my_isspace(thd->variables.character_set_client, *body_end))) - { /* consume NULs and meaningless whitespace */ - --body.length; - --body_end; - continue; - } - - /* - consume closing comments - - This is arguably wrong, but it's the best we have until the parser is - changed to be smarter. FIXME PARSER - - See also the sp_head code, where something like this is done also. - - One idea is to keep in the lexer structure the count of the number of - open-comments we've entered, and scan left-to-right looking for a - closing comment IFF the count is greater than zero. - - Another idea is to remove the closing comment-characters wholly in the - parser, since that's where it "removes" the opening characters. - */ - if ((*(body_end - 1) == '*') && (*body_end == '/')) - { - DBUG_PRINT("info", ("consumend one '*" "/' comment in the query '%s'", - body_begin)); - body.length-= 2; - body_end-= 2; - continue; - } - - break; /* none were found, so we have excised all we can. */ - } - - /* the first is always whitespace which I cannot skip in the parser */ - while (my_isspace(thd->variables.character_set_client, *body_begin)) - { - ++body_begin; - --body.length; - } + body.length= thd->m_lip->get_cpp_ptr() - body_begin; body.str= thd->strmake(body_begin, body.length); + trim_whitespace(thd->charset(), & body); DBUG_VOID_RETURN; } diff --git a/sql/sp.cc b/sql/sp.cc index 49e86f9d07e..8fcc80a1504 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -588,10 +588,14 @@ sp_create_routine(THD *thd, int type, sp_head *sp) log_query.append(STRING_WITH_LEN("CREATE ")); append_definer(thd, &log_query, &thd->lex->definer->user, &thd->lex->definer->host); - log_query.append(thd->lex->stmt_definition_begin, - (char *)sp->m_body_begin - - thd->lex->stmt_definition_begin + - sp->m_body.length); + + LEX_STRING stmt_definition; + stmt_definition.str= (char*) thd->lex->stmt_definition_begin; + stmt_definition.length= thd->lex->stmt_definition_end + - thd->lex->stmt_definition_begin; + trim_whitespace(thd->charset(), & stmt_definition); + + log_query.append(stmt_definition.str, stmt_definition.length); /* Such a statement can always go directly to binlog, no trans cache */ thd->binlog_query(THD::MYSQL_QUERY_TYPE, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 912176191cf..bd38de2dd42 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -564,18 +564,17 @@ sp_head::init_strings(THD *thd, LEX *lex) m_params.str= strmake_root(root, m_param_begin, m_params.length); } - /* If ptr has overrun end_of_query then end_of_query is the end */ - endp= (lip->ptr > lip->end_of_query ? lip->end_of_query : lip->ptr); - /* - Trim "garbage" at the end. This is sometimes needed with the - "/ * ! VERSION... * /" wrapper in dump files. - */ - endp= skip_rear_comments(thd->charset(), m_body_begin, endp); + endp= lip->get_cpp_ptr(); + lex->stmt_definition_end= endp; m_body.length= endp - m_body_begin; m_body.str= strmake_root(root, m_body_begin, m_body.length); - m_defstr.length= endp - lip->buf; - m_defstr.str= strmake_root(root, lip->buf, m_defstr.length); + trim_whitespace(thd->charset(), & m_body); + + m_defstr.length= endp - lip->get_cpp_buf(); + m_defstr.str= strmake_root(root, lip->get_cpp_buf(), m_defstr.length); + trim_whitespace(thd->charset(), & m_defstr); + DBUG_VOID_RETURN; } @@ -1827,8 +1826,6 @@ sp_head::reset_lex(THD *thd) sublex->trg_table_fields.empty(); sublex->sp_lex_in_use= FALSE; - sublex->in_comment= oldlex->in_comment; - /* Reset type info. */ sublex->charset= NULL; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f169f2d3c4a..1b8d90d51b6 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -31,16 +31,6 @@ sys_var *trg_new_row_fake_var= (sys_var*) 0x01; -/* Macros to look like lex */ - -#define yyGet() ((uchar) *(lip->ptr++)) -#define yyGetLast() ((uchar) lip->ptr[-1]) -#define yyPeek() ((uchar) lip->ptr[0]) -#define yyPeek2() ((uchar) lip->ptr[1]) -#define yyUnget() lip->ptr-- -#define yySkip() lip->ptr++ -#define yyLength() ((uint) (lip->ptr - lip->tok_start)-1) - /* Longest standard keyword name */ #define TOCK_NAME_LENGTH 24 @@ -127,17 +117,24 @@ Lex_input_stream::Lex_input_stream(THD *thd, yylineno(1), yytoklen(0), yylval(NULL), - ptr(buffer), - tok_start(NULL), - tok_end(NULL), - end_of_query(buffer + length), - tok_start_prev(NULL), - buf(buffer), + m_ptr(buffer), + m_tok_start(NULL), + m_tok_end(NULL), + m_end_of_query(buffer + length), + m_tok_start_prev(NULL), + m_buf(buffer), + m_echo(true), + m_cpp_tok_start(NULL), + m_cpp_tok_start_prev(NULL), + m_cpp_tok_end(NULL), next_state(MY_LEX_START), found_semicolon(NULL), ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)), - stmt_prepare_mode(FALSE) + stmt_prepare_mode(FALSE), + in_comment(NO_COMMENT) { + m_cpp_buf= (char*) thd->alloc(length + 1); + m_cpp_ptr= m_cpp_buf; } Lex_input_stream::~Lex_input_stream() @@ -192,7 +189,6 @@ void lex_start(THD *thd) lex->parsing_options.reset(); lex->empty_field_list_on_rset= 0; lex->select_lex.select_number= 1; - lex->in_comment=0; lex->length=0; lex->part_info= 0; lex->select_lex.in_sum_expr=0; @@ -261,7 +257,7 @@ void lex_end(LEX *lex) static int find_keyword(Lex_input_stream *lip, uint len, bool function) { - const char *tok= lip->tok_start; + const char *tok= lip->get_tok_start(); SYMBOL *symbol= get_hash_symbol(tok, len, function); if (symbol) @@ -312,9 +308,9 @@ bool is_lex_native_function(const LEX_STRING *name) static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length) { LEX_STRING tmp; - yyUnget(); // ptr points now after last token char + lip->yyUnget(); // ptr points now after last token char tmp.length=lip->yytoklen=length; - tmp.str= lip->m_thd->strmake(lip->tok_start + skip, tmp.length); + tmp.str= lip->m_thd->strmake(lip->get_tok_start() + skip, tmp.length); return tmp; } @@ -332,10 +328,10 @@ static LEX_STRING get_quoted_token(Lex_input_stream *lip, LEX_STRING tmp; const char *from, *end; char *to; - yyUnget(); // ptr points now after last token char + lip->yyUnget(); // ptr points now after last token char tmp.length= lip->yytoklen=length; tmp.str=(char*) lip->m_thd->alloc(tmp.length+1); - from= lip->tok_start + skip; + from= lip->get_tok_start() + skip; to= tmp.str; end= to+length; for ( ; to != end; ) @@ -353,23 +349,25 @@ static LEX_STRING get_quoted_token(Lex_input_stream *lip, Fix sometimes to do only one scan of the string */ -static char *get_text(Lex_input_stream *lip) +static char *get_text(Lex_input_stream *lip, int pre_skip, int post_skip) { reg1 uchar c,sep; uint found_escape=0; CHARSET_INFO *cs= lip->m_thd->charset(); - sep= yyGetLast(); // String should end with this - while (lip->ptr != lip->end_of_query) + sep= lip->yyGetLast(); // String should end with this + while (! lip->eof()) { - c = yyGet(); + c= lip->yyGet(); #ifdef USE_MB { int l; if (use_mb(cs) && - (l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query))) { - lip->ptr += l-1; - continue; + (l = my_ismbchar(cs, + lip->get_ptr() -1, + lip->get_end_of_query()))) { + lip->skip_binary(l-1); + continue; } } #endif @@ -377,26 +375,31 @@ static char *get_text(Lex_input_stream *lip) !(lip->m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)) { // Escaped character found_escape=1; - if (lip->ptr == lip->end_of_query) + if (lip->eof()) return 0; - yySkip(); + lip->yySkip(); } else if (c == sep) { - if (c == yyGet()) // Check if two separators in a row + if (c == lip->yyGet()) // Check if two separators in a row { - found_escape=1; // dupplicate. Remember for delete + found_escape=1; // duplicate. Remember for delete continue; } else - yyUnget(); + lip->yyUnget(); /* Found end. Unescape and return string */ const char *str, *end; char *start; - str=lip->tok_start+1; - end=lip->ptr-1; + str= lip->get_tok_start(); + end= lip->get_ptr(); + /* Extract the text from the token */ + str += pre_skip; + end -= post_skip; + DBUG_ASSERT(end >= str); + if (!(start= (char*) lip->m_thd->alloc((uint) (end-str)+1))) return (char*) ""; // Sql_alloc has set error flag if (!found_escape) @@ -581,9 +584,7 @@ int MYSQLlex(void *arg, void *yythd) lip->yylval=yylval; // The global state - lip->tok_start_prev= lip->tok_start; - - lip->tok_start=lip->tok_end=lip->ptr; + lip->start_token(); state=lip->next_state; lip->next_state=MY_LEX_OPERATOR_OR_IDENT; LINT_INIT(c); @@ -592,17 +593,22 @@ int MYSQLlex(void *arg, void *yythd) switch (state) { case MY_LEX_OPERATOR_OR_IDENT: // Next is operator or keyword case MY_LEX_START: // Start of token - // Skip startspace - for (c=yyGet() ; (state_map[c] == MY_LEX_SKIP) ; c= yyGet()) + // Skip starting whitespace + while(state_map[c= lip->yyPeek()] == MY_LEX_SKIP) { if (c == '\n') lip->yylineno++; + + lip->yySkip(); } - lip->tok_start=lip->ptr-1; // Start of real token + + /* Start of real token */ + lip->restart_token(); + c= lip->yyGet(); state= (enum my_lex_states) state_map[c]; break; case MY_LEX_ESCAPE: - if (yyGet() == 'N') + if (lip->yyGet() == 'N') { // Allow \N as shortcut for NULL yylval->lex_str.str=(char*) "\\N"; yylval->lex_str.length=2; @@ -610,40 +616,53 @@ int MYSQLlex(void *arg, void *yythd) } case MY_LEX_CHAR: // Unknown or single char token case MY_LEX_SKIP: // This should not happen - if (c == '-' && yyPeek() == '-' && - (my_isspace(cs,yyPeek2()) || - my_iscntrl(cs,yyPeek2()))) + if (c == '-' && lip->yyPeek() == '-' && + (my_isspace(cs,lip->yyPeekn(1)) || + my_iscntrl(cs,lip->yyPeekn(1)))) { state=MY_LEX_COMMENT; break; } - yylval->lex_str.str=(char*) (lip->ptr=lip->tok_start);// Set to first chr - yylval->lex_str.length=1; - c=yyGet(); + if (c != ')') lip->next_state= MY_LEX_START; // Allow signed numbers + if (c == ',') - lip->tok_start=lip->ptr; // Let tok_start point at next item - /* - Check for a placeholder: it should not precede a possible identifier - because of binlogging: when a placeholder is replaced with - its value in a query for the binlog, the query must stay - grammatically correct. - */ - else if (c == '?' && lip->stmt_prepare_mode && !ident_map[yyPeek()]) + { + /* + Warning: + This is a work around, to make the "remember_name" rule in + sql/sql_yacc.yy work properly. + The problem is that, when parsing "select expr1, expr2", + the code generated by bison executes the *pre* action + remember_name (see select_item) *before* actually parsing the + first token of expr2. + */ + lip->restart_token(); + } + else + { + /* + Check for a placeholder: it should not precede a possible identifier + because of binlogging: when a placeholder is replaced with + its value in a query for the binlog, the query must stay + grammatically correct. + */ + if (c == '?' && lip->stmt_prepare_mode && !ident_map[lip->yyPeek()]) return(PARAM_MARKER); + } + return((int) c); case MY_LEX_IDENT_OR_NCHAR: - if (yyPeek() != '\'') - { + if (lip->yyPeek() != '\'') + { state= MY_LEX_IDENT; break; } /* Found N'string' */ - lip->tok_start++; // Skip N - yySkip(); // Skip ' - if (!(yylval->lex_str.str = get_text(lip))) + lip->yySkip(); // Skip ' + if (!(yylval->lex_str.str = get_text(lip, 2, 1))) { state= MY_LEX_CHAR; // Read char by char break; @@ -652,13 +671,13 @@ int MYSQLlex(void *arg, void *yythd) return(NCHAR_STRING); case MY_LEX_IDENT_OR_HEX: - if (yyPeek() == '\'') + if (lip->yyPeek() == '\'') { // Found x'hex-number' state= MY_LEX_HEX_NUMBER; break; } case MY_LEX_IDENT_OR_BIN: - if (yyPeek() == '\'') + if (lip->yyPeek() == '\'') { // Found b'bin-number' state= MY_LEX_BIN_NUMBER; break; @@ -669,54 +688,58 @@ int MYSQLlex(void *arg, void *yythd) if (use_mb(cs)) { result_state= IDENT_QUOTED; - if (my_mbcharlen(cs, yyGetLast()) > 1) + if (my_mbcharlen(cs, lip->yyGetLast()) > 1) { - int l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query); + int l = my_ismbchar(cs, + lip->get_ptr() -1, + lip->get_end_of_query()); if (l == 0) { state = MY_LEX_CHAR; continue; } - lip->ptr += l - 1; + lip->skip_binary(l - 1); } - while (ident_map[c=yyGet()]) + while (ident_map[c=lip->yyGet()]) { if (my_mbcharlen(cs, c) > 1) { int l; - if ((l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query)) == 0) + if ((l = my_ismbchar(cs, + lip->get_ptr() -1, + lip->get_end_of_query())) == 0) break; - lip->ptr += l-1; + lip->skip_binary(l-1); } } } else #endif { - for (result_state= c; ident_map[c= yyGet()]; result_state|= c); + for (result_state= c; ident_map[c= lip->yyGet()]; result_state|= c); /* If there were non-ASCII characters, mark that we must convert */ result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT; } - length= (uint) (lip->ptr - lip->tok_start)-1; - start= lip->ptr; + length= lip->yyLength(); + start= lip->get_ptr(); if (lip->ignore_space) { /* If we find a space then this can't be an identifier. We notice this below by checking start != lex->ptr. */ - for (; state_map[c] == MY_LEX_SKIP ; c= yyGet()); + for (; state_map[c] == MY_LEX_SKIP ; c= lip->yyGet()); } - if (start == lip->ptr && c == '.' && ident_map[yyPeek()]) + if (start == lip->get_ptr() && c == '.' && ident_map[lip->yyPeek()]) lip->next_state=MY_LEX_IDENT_SEP; else { // '(' must follow directly if function - yyUnget(); - if ((tokval = find_keyword(lip, length,c == '('))) + lip->yyUnget(); + if ((tokval = find_keyword(lip, length, c == '('))) { lip->next_state= MY_LEX_START; // Allow signed numbers return(tokval); // Was keyword } - yySkip(); // next state does a unget + lip->yySkip(); // next state does a unget } yylval->lex_str=get_token(lip, 0, length); @@ -735,16 +758,48 @@ int MYSQLlex(void *arg, void *yythd) return(result_state); // IDENT or IDENT_QUOTED case MY_LEX_IDENT_SEP: // Found ident and now '.' - yylval->lex_str.str=(char*) lip->ptr; - yylval->lex_str.length=1; - c=yyGet(); // should be '.' + yylval->lex_str.str= (char*) lip->get_ptr(); + yylval->lex_str.length= 1; + c= lip->yyGet(); // should be '.' lip->next_state= MY_LEX_IDENT_START;// Next is an ident (not a keyword) - if (!ident_map[yyPeek()]) // Probably ` or " + if (!ident_map[lip->yyPeek()]) // Probably ` or " lip->next_state= MY_LEX_START; return((int) c); case MY_LEX_NUMBER_IDENT: // number or ident which num-start - while (my_isdigit(cs,(c = yyGet()))) ; + if (lip->yyGetLast() == '0') + { + c= lip->yyGet(); + if (c == 'x') + { + while (my_isxdigit(cs,(c = lip->yyGet()))) ; + if ((lip->yyLength() >= 3) && !ident_map[c]) + { + /* skip '0x' */ + yylval->lex_str=get_token(lip, 2, lip->yyLength()-2); + return (HEX_NUM); + } + lip->yyUnget(); + state= MY_LEX_IDENT_START; + break; + } + else if (c == 'b') + { + while ((c= lip->yyGet()) == '0' || c == '1'); + if ((lip->yyLength() >= 3) && !ident_map[c]) + { + /* Skip '0b' */ + yylval->lex_str= get_token(lip, 2, lip->yyLength()-2); + return (BIN_NUM); + } + lip->yyUnget(); + state= MY_LEX_IDENT_START; + break; + } + lip->yyUnget(); + } + + while (my_isdigit(cs, (c = lip->yyGet()))) ; if (!ident_map[c]) { // Can't be identifier state=MY_LEX_INT_OR_REAL; @@ -753,42 +808,18 @@ int MYSQLlex(void *arg, void *yythd) if (c == 'e' || c == 'E') { // The following test is written this way to allow numbers of type 1e1 - if (my_isdigit(cs,yyPeek()) || - (c=(yyGet())) == '+' || c == '-') + if (my_isdigit(cs,lip->yyPeek()) || + (c=(lip->yyGet())) == '+' || c == '-') { // Allow 1E+10 - if (my_isdigit(cs,yyPeek())) // Number must have digit after sign + if (my_isdigit(cs,lip->yyPeek())) // Number must have digit after sign { - yySkip(); - while (my_isdigit(cs,yyGet())) ; - yylval->lex_str=get_token(lip, 0, yyLength()); + lip->yySkip(); + while (my_isdigit(cs,lip->yyGet())) ; + yylval->lex_str=get_token(lip, 0, lip->yyLength()); return(FLOAT_NUM); } } - yyUnget(); /* purecov: inspected */ - } - else if (c == 'x' && (lip->ptr - lip->tok_start) == 2 && - lip->tok_start[0] == '0' ) - { // Varbinary - while (my_isxdigit(cs,(c = yyGet()))) ; - if ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c]) - { - /* skip '0x' */ - yylval->lex_str=get_token(lip, 2, yyLength()-2); - return (HEX_NUM); - } - yyUnget(); - } - else if (c == 'b' && (lip->ptr - lip->tok_start) == 2 && - lip->tok_start[0] == '0' ) - { // b'bin-number' - while (my_isxdigit(cs,(c = yyGet()))) ; - if ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c]) - { - /* Skip '0b' */ - yylval->lex_str= get_token(lip, 2, yyLength()-2); - return (BIN_NUM); - } - yyUnget(); + lip->yyUnget(); } // fall through case MY_LEX_IDENT_START: // We come here after '.' @@ -797,44 +828,46 @@ int MYSQLlex(void *arg, void *yythd) if (use_mb(cs)) { result_state= IDENT_QUOTED; - while (ident_map[c=yyGet()]) + while (ident_map[c=lip->yyGet()]) { if (my_mbcharlen(cs, c) > 1) { int l; - if ((l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query)) == 0) + if ((l = my_ismbchar(cs, + lip->get_ptr() -1, + lip->get_end_of_query())) == 0) break; - lip->ptr += l-1; + lip->skip_binary(l-1); } } } else #endif { - for (result_state=0; ident_map[c= yyGet()]; result_state|= c); + for (result_state=0; ident_map[c= lip->yyGet()]; result_state|= c); /* If there were non-ASCII characters, mark that we must convert */ result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT; } - if (c == '.' && ident_map[yyPeek()]) + if (c == '.' && ident_map[lip->yyPeek()]) lip->next_state=MY_LEX_IDENT_SEP;// Next is '.' - yylval->lex_str= get_token(lip, 0, yyLength()); + yylval->lex_str= get_token(lip, 0, lip->yyLength()); return(result_state); case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char { uint double_quotes= 0; char quote_char= c; // Used char - while ((c=yyGet())) + while ((c=lip->yyGet())) { int var_length; if ((var_length= my_mbcharlen(cs, c)) == 1) { if (c == quote_char) { - if (yyPeek() != quote_char) + if (lip->yyPeek() != quote_char) break; - c=yyGet(); + c=lip->yyGet(); double_quotes++; continue; } @@ -842,78 +875,78 @@ int MYSQLlex(void *arg, void *yythd) #ifdef USE_MB else if (var_length < 1) break; // Error - lip->ptr+= var_length-1; + lip->skip_binary(var_length-1); #endif } if (double_quotes) yylval->lex_str=get_quoted_token(lip, 1, - yyLength() - double_quotes -1, + lip->yyLength() - double_quotes -1, quote_char); else - yylval->lex_str=get_token(lip, 1, yyLength() -1); + yylval->lex_str=get_token(lip, 1, lip->yyLength() -1); if (c == quote_char) - yySkip(); // Skip end ` + lip->yySkip(); // Skip end ` lip->next_state= MY_LEX_START; return(IDENT_QUOTED); } case MY_LEX_INT_OR_REAL: // Complete int or incomplete real if (c != '.') { // Found complete integer number. - yylval->lex_str=get_token(lip, 0, yyLength()); + yylval->lex_str=get_token(lip, 0, lip->yyLength()); return int_token(yylval->lex_str.str,yylval->lex_str.length); } // fall through case MY_LEX_REAL: // Incomplete real number - while (my_isdigit(cs,c = yyGet())) ; + while (my_isdigit(cs,c = lip->yyGet())) ; if (c == 'e' || c == 'E') { - c = yyGet(); + c = lip->yyGet(); if (c == '-' || c == '+') - c = yyGet(); // Skip sign + c = lip->yyGet(); // Skip sign if (!my_isdigit(cs,c)) { // No digit after sign state= MY_LEX_CHAR; break; } - while (my_isdigit(cs,yyGet())) ; - yylval->lex_str=get_token(lip, 0, yyLength()); + while (my_isdigit(cs,lip->yyGet())) ; + yylval->lex_str=get_token(lip, 0, lip->yyLength()); return(FLOAT_NUM); } - yylval->lex_str=get_token(lip, 0, yyLength()); + yylval->lex_str=get_token(lip, 0, lip->yyLength()); return(DECIMAL_NUM); case MY_LEX_HEX_NUMBER: // Found x'hexstring' - yyGet(); // Skip ' - while (my_isxdigit(cs,(c = yyGet()))) ; - length=(lip->ptr - lip->tok_start); // Length of hexnum+3 - if (!(length & 1) || c != '\'') - { - return(ABORT_SYM); // Illegal hex constant - } - yyGet(); // get_token makes an unget + lip->yySkip(); // Accept opening ' + while (my_isxdigit(cs, (c= lip->yyGet()))) ; + if (c != '\'') + return(ABORT_SYM); // Illegal hex constant + lip->yySkip(); // Accept closing ' + length= lip->yyLength(); // Length of hexnum+3 + if ((length % 2) == 0) + return(ABORT_SYM); // odd number of hex digits yylval->lex_str=get_token(lip, 2, // skip x' length-3); // don't count x' and last ' return (HEX_NUM); case MY_LEX_BIN_NUMBER: // Found b'bin-string' - yyGet(); // Skip ' - while ((c= yyGet()) == '0' || c == '1'); - length= (lip->ptr - lip->tok_start); // Length of bin-num + 3 + lip->yySkip(); // Accept opening ' + while ((c= lip->yyGet()) == '0' || c == '1'); if (c != '\'') - return(ABORT_SYM); // Illegal hex constant - yyGet(); // get_token makes an unget + return(ABORT_SYM); // Illegal hex constant + lip->yySkip(); // Accept closing ' + length= lip->yyLength(); // Length of bin-num + 3 yylval->lex_str= get_token(lip, 2, // skip b' length-3); // don't count b' and last ' return (BIN_NUM); case MY_LEX_CMP_OP: // Incomplete comparison operator - if (state_map[yyPeek()] == MY_LEX_CMP_OP || - state_map[yyPeek()] == MY_LEX_LONG_CMP_OP) - yySkip(); - if ((tokval = find_keyword(lip, (uint) (lip->ptr - lip->tok_start),0))) + if (state_map[lip->yyPeek()] == MY_LEX_CMP_OP || + state_map[lip->yyPeek()] == MY_LEX_LONG_CMP_OP) + lip->yySkip(); + if ((tokval = find_keyword(lip, lip->yyLength() + 1, 0))) { lip->next_state= MY_LEX_START; // Allow signed numbers return(tokval); @@ -922,14 +955,14 @@ int MYSQLlex(void *arg, void *yythd) break; case MY_LEX_LONG_CMP_OP: // Incomplete comparison operator - if (state_map[yyPeek()] == MY_LEX_CMP_OP || - state_map[yyPeek()] == MY_LEX_LONG_CMP_OP) + if (state_map[lip->yyPeek()] == MY_LEX_CMP_OP || + state_map[lip->yyPeek()] == MY_LEX_LONG_CMP_OP) { - yySkip(); - if (state_map[yyPeek()] == MY_LEX_CMP_OP) - yySkip(); + lip->yySkip(); + if (state_map[lip->yyPeek()] == MY_LEX_CMP_OP) + lip->yySkip(); } - if ((tokval = find_keyword(lip, (uint) (lip->ptr - lip->tok_start),0))) + if ((tokval = find_keyword(lip, lip->yyLength() + 1, 0))) { lip->next_state= MY_LEX_START; // Found long op return(tokval); @@ -938,12 +971,12 @@ int MYSQLlex(void *arg, void *yythd) break; case MY_LEX_BOOL: - if (c != yyPeek()) + if (c != lip->yyPeek()) { state=MY_LEX_CHAR; break; } - yySkip(); + lip->yySkip(); tokval = find_keyword(lip,2,0); // Is a bool operator lip->next_state= MY_LEX_START; // Allow signed numbers return(tokval); @@ -956,7 +989,7 @@ int MYSQLlex(void *arg, void *yythd) } /* " used for strings */ case MY_LEX_STRING: // Incomplete text string - if (!(yylval->lex_str.str = get_text(lip))) + if (!(yylval->lex_str.str = get_text(lip, 1, 1))) { state= MY_LEX_CHAR; // Read char by char break; @@ -966,82 +999,138 @@ int MYSQLlex(void *arg, void *yythd) case MY_LEX_COMMENT: // Comment lex->select_lex.options|= OPTION_FOUND_COMMENT; - while ((c = yyGet()) != '\n' && c) ; - yyUnget(); // Safety against eof + while ((c = lip->yyGet()) != '\n' && c) ; + lip->yyUnget(); // Safety against eof state = MY_LEX_START; // Try again break; case MY_LEX_LONG_COMMENT: /* Long C comment? */ - if (yyPeek() != '*') + if (lip->yyPeek() != '*') { state=MY_LEX_CHAR; // Probable division break; } - yySkip(); // Skip '*' lex->select_lex.options|= OPTION_FOUND_COMMENT; - if (yyPeek() == '!') // MySQL command in comment + /* Reject '/' '*', since we might need to turn off the echo */ + lip->yyUnget(); + + if (lip->yyPeekn(2) == '!') { - ulong version=MYSQL_VERSION_ID; - yySkip(); - state=MY_LEX_START; - if (my_isdigit(cs,yyPeek())) - { // Version number - version=strtol((char*) lip->ptr,(char**) &lip->ptr,10); - } - if (version <= MYSQL_VERSION_ID) - { - lex->in_comment=1; - break; - } + lip->in_comment= DISCARD_COMMENT; + /* Accept '/' '*' '!', but do not keep this marker. */ + lip->set_echo(false); + lip->yySkip(); + lip->yySkip(); + lip->yySkip(); + + /* + The special comment format is very strict: + '/' '*' '!', followed by exactly + 2 digits (major), then 3 digits (minor). + */ + char version_str[6]; + version_str[0]= lip->yyPeekn(0); + version_str[1]= lip->yyPeekn(1); + version_str[2]= lip->yyPeekn(2); + version_str[3]= lip->yyPeekn(3); + version_str[4]= lip->yyPeekn(4); + version_str[5]= 0; + if ( my_isdigit(cs, version_str[0]) + && my_isdigit(cs, version_str[1]) + && my_isdigit(cs, version_str[2]) + && my_isdigit(cs, version_str[3]) + && my_isdigit(cs, version_str[4]) + ) + { + ulong version; + version=strtol(version_str, NULL, 10); + + /* Accept 'M' 'M' 'm' 'm' 'm' */ + lip->yySkipn(5); + + if (version <= MYSQL_VERSION_ID) + { + /* Expand the content of the special comment as real code */ + lip->set_echo(true); + state=MY_LEX_START; + break; + } + } + else + { + state=MY_LEX_START; + lip->set_echo(true); + break; + } } - while (lip->ptr != lip->end_of_query && - ((c=yyGet()) != '*' || yyPeek() != '/')) + else { - if (c == '\n') - lip->yylineno++; + lip->in_comment= PRESERVE_COMMENT; + lip->yySkip(); // Accept / + lip->yySkip(); // Accept * } - if (lip->ptr != lip->end_of_query) - yySkip(); // remove last '/' - state = MY_LEX_START; // Try again + + while (! lip->eof() && + ((c=lip->yyGet()) != '*' || lip->yyPeek() != '/')) + { + if (c == '\n') + lip->yylineno++; + } + if (! lip->eof()) + lip->yySkip(); // remove last '/' + state = MY_LEX_START; // Try again + lip->set_echo(true); break; case MY_LEX_END_LONG_COMMENT: - if (lex->in_comment && yyPeek() == '/') + if ((lip->in_comment != NO_COMMENT) && lip->yyPeek() == '/') { - yySkip(); - lex->in_comment=0; - state=MY_LEX_START; + /* Reject '*' '/' */ + lip->yyUnget(); + /* Accept '*' '/', with the proper echo */ + lip->set_echo(lip->in_comment == PRESERVE_COMMENT); + lip->yySkipn(2); + /* And start recording the tokens again */ + lip->set_echo(true); + lip->in_comment=NO_COMMENT; + state=MY_LEX_START; } else state=MY_LEX_CHAR; // Return '*' break; case MY_LEX_SET_VAR: // Check if ':=' - if (yyPeek() != '=') + if (lip->yyPeek() != '=') { state=MY_LEX_CHAR; // Return ':' break; } - yySkip(); + lip->yySkip(); return (SET_VAR); case MY_LEX_SEMICOLON: // optional line terminator - if (yyPeek()) + if (lip->yyPeek()) { if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) && !lip->stmt_prepare_mode) { lex->safe_to_cache_query= 0; - lip->found_semicolon= lip->ptr; + lip->found_semicolon= lip->get_ptr(); thd->server_status|= SERVER_MORE_RESULTS_EXISTS; lip->next_state= MY_LEX_END; + lip->set_echo(true); return (END_OF_INPUT); } state= MY_LEX_CHAR; // Return ';' break; } - /* fall true */ + lip->next_state=MY_LEX_END; // Mark for next loop + return(END_OF_INPUT); case MY_LEX_EOL: - if (lip->ptr >= lip->end_of_query) + if (lip->eof()) { - lip->next_state=MY_LEX_END; // Mark for next loop - return(END_OF_INPUT); + lip->yyUnget(); // Reject the last '\0' + lip->set_echo(false); + lip->yySkip(); + lip->set_echo(true); + lip->next_state=MY_LEX_END; // Mark for next loop + return(END_OF_INPUT); } state=MY_LEX_CHAR; break; @@ -1051,16 +1140,16 @@ int MYSQLlex(void *arg, void *yythd) /* Actually real shouldn't start with . but allow them anyhow */ case MY_LEX_REAL_OR_POINT: - if (my_isdigit(cs,yyPeek())) + if (my_isdigit(cs,lip->yyPeek())) state = MY_LEX_REAL; // Real else { state= MY_LEX_IDENT_SEP; // return '.' - yyUnget(); // Put back '.' + lip->yyUnget(); // Put back '.' } break; case MY_LEX_USER_END: // end '@' of user@hostname - switch (state_map[yyPeek()]) { + switch (state_map[lip->yyPeek()]) { case MY_LEX_STRING: case MY_LEX_USER_VARIABLE_DELIMITER: case MY_LEX_STRING_OR_DELIMITER: @@ -1072,20 +1161,20 @@ int MYSQLlex(void *arg, void *yythd) lip->next_state=MY_LEX_HOSTNAME; break; } - yylval->lex_str.str=(char*) lip->ptr; + yylval->lex_str.str=(char*) lip->get_ptr(); yylval->lex_str.length=1; return((int) '@'); case MY_LEX_HOSTNAME: // end '@' of user@hostname - for (c=yyGet() ; + for (c=lip->yyGet() ; my_isalnum(cs,c) || c == '.' || c == '_' || c == '$'; - c= yyGet()) ; - yylval->lex_str=get_token(lip, 0, yyLength()); + c= lip->yyGet()) ; + yylval->lex_str=get_token(lip, 0, lip->yyLength()); return(LEX_HOSTNAME); case MY_LEX_SYSTEM_VAR: - yylval->lex_str.str=(char*) lip->ptr; + yylval->lex_str.str=(char*) lip->get_ptr(); yylval->lex_str.length=1; - yySkip(); // Skip '@' - lip->next_state= (state_map[yyPeek()] == + lip->yySkip(); // Skip '@' + lip->next_state= (state_map[lip->yyPeek()] == MY_LEX_USER_VARIABLE_DELIMITER ? MY_LEX_OPERATOR_OR_IDENT : MY_LEX_IDENT_OR_KEYWORD); @@ -1096,19 +1185,19 @@ int MYSQLlex(void *arg, void *yythd) We should now be able to handle: [(global | local | session) .]variable_name */ - - for (result_state= 0; ident_map[c= yyGet()]; result_state|= c); + + for (result_state= 0; ident_map[c= lip->yyGet()]; result_state|= c); /* If there were non-ASCII characters, mark that we must convert */ result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT; - + if (c == '.') lip->next_state=MY_LEX_IDENT_SEP; - length= (uint) (lip->ptr - lip->tok_start)-1; - if (length == 0) + length= lip->yyLength(); + if (length == 0) return(ABORT_SYM); // Names must be nonempty. if ((tokval= find_keyword(lip, length,0))) { - yyUnget(); // Put back 'c' + lip->yyUnget(); // Put back 'c' return(tokval); // Was keyword } yylval->lex_str=get_token(lip, 0, length); @@ -1149,32 +1238,31 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) } -/* - Skip comment in the end of statement. - - SYNOPSIS - skip_rear_comments() - cs character set - begin pointer to the beginning of statement - end pointer to the end of statement - - DESCRIPTION - The function is intended to trim comments at the end of the statement. - - RETURN - Pointer to the last non-comment symbol of the statement. -*/ - -const char *skip_rear_comments(CHARSET_INFO *cs, const char *begin, - const char *end) +void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str) { - while (begin < end && (end[-1] == '*' || - end[-1] == '/' || end[-1] == ';' || - my_isspace(cs, end[-1]))) - end-= 1; - return end; + /* + TODO: + This code assumes that there are no multi-bytes characters + that can be considered white-space. + */ + + while ((str->length > 0) && (my_isspace(cs, str->str[0]))) + { + str->length --; + str->str ++; + } + + /* + FIXME: + Also, parsing backward is not safe with multi bytes characters + */ + while ((str->length > 0) && (my_isspace(cs, str->str[str->length-1]))) + { + str->length --; + } } + /* st_select_lex structures initialisations */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 2cecf282e7f..7057bf4b5ef 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1048,9 +1048,41 @@ struct st_parsing_options }; +/** + The state of the lexical parser, when parsing comments. +*/ +enum enum_comment_state +{ + /** + Not parsing comments. + */ + NO_COMMENT, + /** + Parsing comments that need to be preserved. + Typically, these are user comments '/' '*' ... '*' '/'. + */ + PRESERVE_COMMENT, + /** + Parsing comments that need to be discarded. + Typically, these are special comments '/' '*' '!' ... '*' '/', + or '/' '*' '!' 'M' 'M' 'm' 'm' 'm' ... '*' '/', where the comment + markers should not be expanded. + */ + DISCARD_COMMENT +}; + + /** This class represents the character input stream consumed during lexical analysis. + In addition to consuming the input stream, this class performs some + comment pre processing, by filtering out out of bound special text + from the query input stream. + Two buffers, with pointers inside each buffers, are maintained in + parallel. The 'raw' buffer is the original query text, which may + contain out-of-bound comments. The 'cpp' (for comments pre processor) + is the pre-processed buffer that contains only the query text that + should be seen once out-of-bound data is removed. */ class Lex_input_stream { @@ -1058,6 +1090,218 @@ public: Lex_input_stream(THD *thd, const char* buff, unsigned int length); ~Lex_input_stream(); + /** + Set the echo mode. + When echo is true, characters parsed from the raw input stream are + preserved. When false, characters parsed are silently ignored. + @param echo the echo mode. + */ + void set_echo(bool echo) + { + m_echo= echo; + } + + /** + Skip binary from the input stream. + @param n number of bytes to accept. + */ + void skip_binary(int n) + { + if (m_echo) + { + memcpy(m_cpp_ptr, m_ptr, n); + m_cpp_ptr += n; + } + m_ptr += n; + } + + /** + Get a character, and advance in the stream. + @return the next character to parse. + */ + char yyGet() + { + char c= *m_ptr++; + if (m_echo) + *m_cpp_ptr++ = c; + return c; + } + + /** + Get the last character accepted. + @return the last character accepted. + */ + char yyGetLast() + { + return m_ptr[-1]; + } + + /** + Look at the next character to parse, but do not accept it. + */ + char yyPeek() + { + return m_ptr[0]; + } + + /** + Look ahead at some character to parse. + @param n offset of the character to look up + */ + char yyPeekn(int n) + { + return m_ptr[n]; + } + + /** + Cancel the effect of the last yyGet() or yySkip(). + Note that the echo mode should not change between calls to yyGet / yySkip + and yyUnget. The caller is responsible for ensuring that. + */ + void yyUnget() + { + m_ptr--; + if (m_echo) + m_cpp_ptr--; + } + + /** + Accept a character, by advancing the input stream. + */ + void yySkip() + { + if (m_echo) + *m_cpp_ptr++ = *m_ptr++; + else + m_ptr++; + } + + /** + Accept multiple characters at once. + @param n the number of characters to accept. + */ + void yySkipn(int n) + { + if (m_echo) + { + memcpy(m_cpp_ptr, m_ptr, n); + m_cpp_ptr += n; + } + m_ptr += n; + } + + /** + End of file indicator for the query text to parse. + @return true if there are no more characters to parse + */ + bool eof() + { + return (m_ptr >= m_end_of_query); + } + + /** + End of file indicator for the query text to parse. + @param n number of characters expected + @return true if there are less than n characters to parse + */ + bool eof(int n) + { + return ((m_ptr + n) >= m_end_of_query); + } + + /** Get the raw query buffer. */ + const char* get_buf() + { + return m_buf; + } + + /** Get the pre-processed query buffer. */ + const char* get_cpp_buf() + { + return m_cpp_buf; + } + + /** Get the end of the raw query buffer. */ + const char* get_end_of_query() + { + return m_end_of_query; + } + + /** Mark the stream position as the start of a new token. */ + void start_token() + { + m_tok_start_prev= m_tok_start; + m_tok_start= m_ptr; + m_tok_end= m_ptr; + + m_cpp_tok_start_prev= m_cpp_tok_start; + m_cpp_tok_start= m_cpp_ptr; + m_cpp_tok_end= m_cpp_ptr; + } + + /** + Adjust the starting position of the current token. + This is used to compensate for starting whitespace. + */ + void restart_token() + { + m_tok_start= m_ptr; + m_cpp_tok_start= m_cpp_ptr; + } + + /** Get the token start position, in the raw buffer. */ + const char* get_tok_start() + { + return m_tok_start; + } + + /** Get the token start position, in the pre-processed buffer. */ + const char* get_cpp_tok_start() + { + return m_cpp_tok_start; + } + + /** Get the token end position, in the raw buffer. */ + const char* get_tok_end() + { + return m_tok_end; + } + + /** Get the token end position, in the pre-processed buffer. */ + const char* get_cpp_tok_end() + { + return m_cpp_tok_end; + } + + /** Get the previous token start position, in the raw buffer. */ + const char* get_tok_start_prev() + { + return m_tok_start_prev; + } + + /** Get the current stream pointer, in the raw buffer. */ + const char* get_ptr() + { + return m_ptr; + } + + /** Get the current stream pointer, in the pre-processed buffer. */ + const char* get_cpp_ptr() + { + return m_cpp_ptr; + } + + /** Get the length of the current token, in the raw buffer. */ + uint yyLength() + { + /* + The assumption is that the lexical analyser is always 1 character ahead, + which the -1 account for. + */ + DBUG_ASSERT(m_ptr > m_tok_start); + return (uint) ((m_ptr - m_tok_start) - 1); + } + /** Current thread. */ THD *m_thd; @@ -1070,37 +1314,74 @@ public: /** Interface with bison, value of the last token parsed. */ LEX_YYSTYPE yylval; - /** Pointer to the current position in the input stream. */ - const char* ptr; +private: + /** Pointer to the current position in the raw input stream. */ + const char* m_ptr; - /** Starting position of the last token parsed. */ - const char* tok_start; + /** Starting position of the last token parsed, in the raw buffer. */ + const char* m_tok_start; - /** Ending position of the last token parsed. */ - const char* tok_end; + /** Ending position of the previous token parsed, in the raw buffer. */ + const char* m_tok_end; - /** End of the query text in the input stream. */ - const char* end_of_query; + /** End of the query text in the input stream, in the raw buffer. */ + const char* m_end_of_query; - /** Starting position of the previous token parsed. */ - const char* tok_start_prev; + /** Starting position of the previous token parsed, in the raw buffer. */ + const char* m_tok_start_prev; - /** Begining of the query text in the input stream. */ - const char* buf; + /** Begining of the query text in the input stream, in the raw buffer. */ + const char* m_buf; + + /** Echo the parsed stream to the pre-processed buffer. */ + bool m_echo; + + /** Pre-processed buffer. */ + char* m_cpp_buf; + + /** Pointer to the current position in the pre-processed input stream. */ + char* m_cpp_ptr; + + /** + Starting position of the last token parsed, + in the pre-processed buffer. + */ + const char* m_cpp_tok_start; + + /** + Starting position of the previous token parsed, + in the pre-procedded buffer. + */ + const char* m_cpp_tok_start_prev; + + /** + Ending position of the previous token parsed, + in the pre-processed buffer. + */ + const char* m_cpp_tok_end; + +public: /** Current state of the lexical analyser. */ enum my_lex_states next_state; - /** Position of ';' in the stream, to delimit multiple queries. */ + /** + Position of ';' in the stream, to delimit multiple queries. + This delimiter is in the raw buffer. + */ const char* found_semicolon; /** SQL_MODE = IGNORE_SPACE. */ bool ignore_space; - /* + + /** TRUE if we're parsing a prepared statement: in this mode we should allow placeholders and disallow multi-statements. */ bool stmt_prepare_mode; + + /** State of the lexical analyser for comments. */ + enum_comment_state in_comment; }; @@ -1138,8 +1419,17 @@ typedef struct st_lex : public Query_tables_list CHARSET_INFO *charset, *underscore_charset; /* store original leaf_tables for INSERT SELECT and PS/SP */ TABLE_LIST *leaf_tables_insert; - /* Position (first character index) of SELECT of CREATE VIEW statement */ - uint create_view_select_start; + + /** Start of SELECT of CREATE VIEW statement */ + const char* create_view_select_start; + /** End of SELECT of CREATE VIEW statement */ + const char* create_view_select_end; + + /** Start of 'ON ', in trigger statements. */ + const char* raw_trg_on_table_name_begin; + /** End of 'ON
', in trigger statements. */ + const char* raw_trg_on_table_name_end; + /* Partition info structure filled in by PARTITION BY parse part */ partition_info *part_info; @@ -1238,7 +1528,9 @@ typedef struct st_lex : public Query_tables_list uint8 create_view_algorithm; uint8 create_view_check; bool drop_if_exists, drop_temporary, local_file, one_shot_set; - bool in_comment, verbose, no_write_to_binlog; + + bool verbose, no_write_to_binlog; + bool tx_chain, tx_release; /* Special JOIN::prepare mode: changing of query is prohibited. @@ -1302,10 +1594,12 @@ typedef struct st_lex : public Query_tables_list - CREATE FUNCTION (points to "FUNCTION" or "AGGREGATE"); This pointer is required to add possibly omitted DEFINER-clause to the - DDL-statement before dumping it to the binlog. + DDL-statement before dumping it to the binlog. */ const char *stmt_definition_begin; + const char *stmt_definition_end; + /* Pointers to part of LOAD DATA statement that should be rewritten during replication ("LOCAL 'filename' REPLACE INTO" part). @@ -1434,8 +1728,8 @@ extern void lex_free(void); extern void lex_start(THD *thd); extern void lex_end(LEX *lex); extern int MYSQLlex(void *arg, void *yythd); -extern const char *skip_rear_comments(CHARSET_INFO *cs, const char *ubegin, - const char *uend); + +extern void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str); extern bool is_lex_native_function(const LEX_STRING *name); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index e15003ab243..f63fcab04bb 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -563,10 +563,13 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, append_definer(thd, stmt_query, &definer_user, &definer_host); } - stmt_query->append(thd->lex->stmt_definition_begin, - (char *) thd->lex->sphead->m_body_begin - - thd->lex->stmt_definition_begin + - thd->lex->sphead->m_body.length); + LEX_STRING stmt_definition; + stmt_definition.str= (char*) thd->lex->stmt_definition_begin; + stmt_definition.length= thd->lex->stmt_definition_end + - thd->lex->stmt_definition_begin; + trim_whitespace(thd->charset(), & stmt_definition); + + stmt_query->append(stmt_definition.str, stmt_definition.length); trg_def->str= stmt_query->c_ptr(); trg_def->length= stmt_query->length(); @@ -1032,7 +1035,11 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (!(on_table_name= (LEX_STRING*) alloc_root(&table->mem_root, sizeof(LEX_STRING)))) goto err_with_lex_cleanup; - *on_table_name= lex.ident; + + on_table_name->str= (char*) lex.raw_trg_on_table_name_begin; + on_table_name->length= lex.raw_trg_on_table_name_end + - lex.raw_trg_on_table_name_begin; + if (triggers->on_table_names_list.push_back(on_table_name, &table->mem_root)) goto err_with_lex_cleanup; @@ -1348,7 +1355,12 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd, /* Construct CREATE TRIGGER statement with new table name. */ buff.length(0); + + /* WARNING: 'on_table_name' is supposed to point inside 'def' */ + DBUG_ASSERT(on_table_name->str > def->str); + DBUG_ASSERT(on_table_name->str < (def->str + def->length)); before_on_len= on_table_name->str - def->str; + buff.append(def->str, before_on_len); buff.append(STRING_WITH_LEN("ON ")); append_identifier(thd, &buff, new_table_name->str, new_table_name->length); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c1e4006555f..a2d6e080763 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -690,7 +690,6 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, char md5[MD5_BUFF_LENGTH]; bool can_be_merged; char dir_buff[FN_REFLEN], path_buff[FN_REFLEN]; - const char *endp; LEX_STRING dir, file, path; int error= 0; DBUG_ENTER("mysql_register_view"); @@ -708,10 +707,12 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, /* fill structure */ view->query.str= str.c_ptr_safe(); view->query.length= str.length(); - view->source.str= thd->query + thd->lex->create_view_select_start; - endp= view->source.str; - endp= skip_rear_comments(thd->charset(), endp, thd->query + thd->query_length); - view->source.length= endp - view->source.str; + + view->source.str= (char*) thd->lex->create_view_select_start; + view->source.length= (thd->lex->create_view_select_end + - thd->lex->create_view_select_start); + trim_whitespace(thd->charset(), & view->source); + view->file_version= 1; view->calc_md5(md5); view->md5.str= md5; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4ffe8647513..837c1699249 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -106,7 +106,7 @@ void my_parse_error(const char *s) THD *thd= current_thd; Lex_input_stream *lip= thd->m_lip; - const char *yytext= lip->tok_start; + const char *yytext= lip->get_tok_start(); /* Push an error into the error stack */ my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s, (yytext ? yytext : ""), @@ -1872,9 +1872,9 @@ ev_sql_stmt: bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lip->ptr; + lex->sphead->m_body_begin= lip->get_cpp_ptr(); - lex->event_parse_data->body_begin= lip->ptr; + lex->event_parse_data->body_begin= lip->get_cpp_ptr(); } ev_sql_stmt_inner @@ -1986,6 +1986,7 @@ create_function_tail: LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; sp_head *sp; + const char* tmp_param_begin; /* First check if AGGREGATE was used, in that case it's a @@ -2017,7 +2018,10 @@ create_function_tail: */ $$= thd->client_capabilities & CLIENT_MULTI_QUERIES; thd->client_capabilities &= ~CLIENT_MULTI_QUERIES; - lex->sphead->m_param_begin= lip->tok_start+1; + + tmp_param_begin= lip->get_cpp_tok_start(); + tmp_param_begin++; + lex->sphead->m_param_begin= tmp_param_begin; } sp_fdparam_list ')' { @@ -2025,7 +2029,7 @@ create_function_tail: LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; - lex->sphead->m_param_end= lip->tok_start; + lex->sphead->m_param_end= lip->get_cpp_tok_start(); } RETURNS_SYM { @@ -2065,7 +2069,7 @@ create_function_tail: Lex_input_stream *lip= thd->m_lip; lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lip->tok_start; + lex->sphead->m_body_begin= lip->get_cpp_tok_start(); } sp_proc_stmt { @@ -2676,7 +2680,7 @@ sp_proc_stmt_statement: Lex_input_stream *lip= thd->m_lip; lex->sphead->reset_lex(thd); - lex->sphead->m_tmp_query= lip->tok_start; + lex->sphead->m_tmp_query= lip->get_tok_start(); } statement { @@ -2709,9 +2713,9 @@ sp_proc_stmt_statement: lex->tok_end otherwise. */ if (yychar == YYEMPTY) - i->m_query.length= lip->ptr - sp->m_tmp_query; + i->m_query.length= lip->get_ptr() - sp->m_tmp_query; else - i->m_query.length= lip->tok_end - sp->m_tmp_query; + i->m_query.length= lip->get_tok_end() - sp->m_tmp_query; i->m_query.str= strmake_root(thd->mem_root, sp->m_tmp_query, i->m_query.length); @@ -6229,14 +6233,14 @@ remember_name: { THD *thd= YYTHD; Lex_input_stream *lip= thd->m_lip; - $$= (char*) lip->tok_start; + $$= (char*) lip->get_cpp_tok_start(); }; remember_end: { THD *thd= YYTHD; Lex_input_stream *lip= thd->m_lip; - $$=(char*) lip->tok_end; + $$= (char*) lip->get_cpp_tok_end(); }; select_item2: @@ -8010,7 +8014,7 @@ procedure_item: if (add_proc_to_list(thd, $2)) MYSQL_YYABORT; if (!$2->name) - $2->set_name($1,(uint) ((char*) lip->tok_end - $1), + $2->set_name($1,(uint) ((char*) lip->get_tok_end() - $1), thd->charset()); } ; @@ -9111,7 +9115,7 @@ load: LOAD DATA_SYM my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA"); MYSQL_YYABORT; } - lex->fname_start= lip->ptr; + lex->fname_start= lip->get_ptr(); } load_data {} @@ -9148,7 +9152,7 @@ load_data: THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; - lex->fname_end= lip->ptr; + lex->fname_end= lip->get_ptr(); } TABLE_SYM table_ident { @@ -9337,7 +9341,7 @@ param_marker: my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); MYSQL_YYABORT; } - item= new Item_param((uint) (lip->tok_start - thd->query)); + item= new Item_param((uint) (lip->get_tok_start() - thd->query)); if (!($$= item) || lex->param_list.push_back(item)) { my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); @@ -9470,7 +9474,7 @@ simple_ident: Item_splocal *splocal; splocal= new Item_splocal($1, spv->offset, spv->type, - lip->tok_start_prev - + lip->get_tok_start_prev() - lex->sphead->m_tmp_query); #ifndef DBUG_OFF if (splocal) @@ -10146,7 +10150,7 @@ option_type_value: lex->option_type=OPT_SESSION; lex->var_list.empty(); lex->one_shot_set= 0; - lex->sphead->m_tmp_query= lip->tok_start; + lex->sphead->m_tmp_query= lip->get_tok_start(); } } ext_option_value @@ -10179,9 +10183,9 @@ option_type_value: lip->tok_end otherwise. */ if (yychar == YYEMPTY) - qbuff.length= lip->ptr - sp->m_tmp_query; + qbuff.length= lip->get_ptr() - sp->m_tmp_query; else - qbuff.length= lip->tok_end - sp->m_tmp_query; + qbuff.length= lip->get_tok_end() - sp->m_tmp_query; if (!(qbuff.str= (char*) alloc_root(thd->mem_root, qbuff.length + 5))) @@ -11362,7 +11366,7 @@ view_tail: if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING)) MYSQL_YYABORT; } - view_list_opt AS view_select view_check_option + view_list_opt AS view_select {} ; @@ -11387,40 +11391,32 @@ view_list: view_select: { + THD *thd= YYTHD; LEX *lex= Lex; + Lex_input_stream *lip= thd->m_lip; lex->parsing_options.allows_variable= FALSE; lex->parsing_options.allows_select_into= FALSE; lex->parsing_options.allows_select_procedure= FALSE; lex->parsing_options.allows_derived= FALSE; - } - view_select_aux + lex->create_view_select_start= lip->get_cpp_ptr(); + } + view_select_aux view_check_option { + THD *thd= YYTHD; LEX *lex= Lex; + Lex_input_stream *lip= thd->m_lip; lex->parsing_options.allows_variable= TRUE; lex->parsing_options.allows_select_into= TRUE; lex->parsing_options.allows_select_procedure= TRUE; lex->parsing_options.allows_derived= TRUE; + lex->create_view_select_end= lip->get_cpp_ptr(); } ; view_select_aux: - SELECT_SYM remember_name select_init2 - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - const char *stmt_beg= (lex->sphead ? - lex->sphead->m_tmp_query : thd->query); - lex->create_view_select_start= $2 - stmt_beg; - } - | '(' remember_name select_paren ')' union_opt - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - const char *stmt_beg= (lex->sphead ? - lex->sphead->m_tmp_query : thd->query); - lex->create_view_select_start= $2 - stmt_beg; - } - ; + SELECT_SYM select_init2 + | '(' select_paren ')' union_opt + ; view_check_option: /* empty */ @@ -11440,9 +11436,31 @@ view_check_option: **************************************************************************/ trigger_tail: - TRIGGER_SYM remember_name sp_name trg_action_time trg_event - ON remember_name table_ident FOR_SYM remember_name EACH_SYM ROW_SYM - { + TRIGGER_SYM + remember_name + sp_name + trg_action_time + trg_event + ON + remember_name /* $7 */ + { /* $8 */ + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + lex->raw_trg_on_table_name_begin= lip->get_tok_start(); + } + table_ident /* $9 */ + FOR_SYM + remember_name /* $11 */ + { /* $12 */ + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + lex->raw_trg_on_table_name_end= lip->get_tok_start(); + } + EACH_SYM + ROW_SYM + { /* $15 */ THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; @@ -11461,7 +11479,7 @@ trigger_tail: sp->init_sp_name(thd, $3); lex->stmt_definition_begin= $2; lex->ident.str= $7; - lex->ident.length= $10 - $7; + lex->ident.length= $11 - $7; sp->m_type= TYPE_ENUM_TRIGGER; lex->sphead= sp; @@ -11476,12 +11494,10 @@ trigger_tail: bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lip->ptr; - while (my_isspace(system_charset_info, lex->sphead->m_body_begin[0])) - ++lex->sphead->m_body_begin; + lex->sphead->m_body_begin= lip->get_cpp_ptr(); } - sp_proc_stmt - { + sp_proc_stmt /* $16 */ + { /* $17 */ LEX *lex= Lex; sp_head *sp= lex->sphead; @@ -11489,7 +11505,7 @@ trigger_tail: sp->init_strings(YYTHD, lex); /* Restore flag if it was cleared above */ - YYTHD->client_capabilities |= $13; + YYTHD->client_capabilities |= $15; sp->restore_thd_mem_root(YYTHD); if (sp->is_not_allowed_in_function("trigger")) @@ -11500,7 +11516,7 @@ trigger_tail: sp_proc_stmt alternatives are not saving/restoring LEX, so lex->query_tables can be wiped out. */ - if (!lex->select_lex.add_table_to_list(YYTHD, $8, + if (!lex->select_lex.add_table_to_list(YYTHD, $9, (LEX_STRING*) 0, TL_OPTION_UPDATING, TL_IGNORE)) @@ -11558,8 +11574,11 @@ sp_tail: THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; + const char* tmp_param_begin; - lex->sphead->m_param_begin= lip->tok_start+1; + tmp_param_begin= lip->get_cpp_tok_start(); + tmp_param_begin++; + lex->sphead->m_param_begin= tmp_param_begin; } sp_pdparam_list ')' @@ -11568,7 +11587,7 @@ sp_tail: LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; - lex->sphead->m_param_end= lip->tok_start; + lex->sphead->m_param_end= lip->get_cpp_tok_start(); bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } sp_c_chistics @@ -11578,7 +11597,7 @@ sp_tail: Lex_input_stream *lip= thd->m_lip; lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lip->tok_start; + lex->sphead->m_body_begin= lip->get_cpp_tok_start(); } sp_proc_stmt { From daf0b9274f05a4576c15db7ff3fcab0e20704e8c Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Wed, 13 Jun 2007 07:31:41 -0600 Subject: [PATCH 09/26] Fixed valgrind error, caused by incorrect pointer arithetic --- sql/sql_yacc.yy | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 837c1699249..a17a0475ac5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8006,16 +8006,14 @@ procedure_list2: | procedure_item; procedure_item: - remember_name expr + remember_name expr remember_end { THD *thd= YYTHD; - Lex_input_stream *lip= thd->m_lip; if (add_proc_to_list(thd, $2)) MYSQL_YYABORT; if (!$2->name) - $2->set_name($1,(uint) ((char*) lip->get_tok_end() - $1), - thd->charset()); + $2->set_name($1, (uint) ($3 - $1), thd->charset()); } ; From dd2bdfda8d32cd9c8c89b6f7c19aee2f15ff35d1 Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Wed, 13 Jun 2007 22:05:22 -0600 Subject: [PATCH 10/26] Bug#27857 (Log tables supplies the wrong value for generating AUTO_INCREMENT numbers) Before this patch, the code in the class Log_to_csv_event_handler, which is used by the global LOGGER object to write to the tables mysql.slow_log and mysql_general_log, was supporting only records of the format defined for these tables in the database creation scripts. Also before this patch, the server would allow, with certain limitations, to perform ALTER TABLE on the LOG TABLES. As implemented, the behavior of the server, with regards to LOG TABLES, is inconsistent: - either ALTER TABLES on LOG TABLES should be prohibited, and the code writing to these tables can make assumptions on the record format, - or ALTER TABLE on LOG TABLES is permitted, in which case the code writing a record to these tables should be more flexible and honor new fields. In particular, adding an AUTO_INCREMENT column to the logs, does not work as expected (per the bug report). Given that the ALTER TABLE on log tables statement has been explicitly implemented to check that the log should be off to perform the operation, and that current test cases already cover this, the user expectation is already set that this is a "feature" and should be supported. With this patch, the server will: - populate AUTO INCREMENT columns if present, - populate any additional column with it's default value when writing a record to the LOG TABLES. Tests are provided, that detail the precise sequence of statements a SUPER user might want to perform to add more columns to the log tables. --- mysql-test/r/log_tables.result | 68 +++++++++++++++++++++++++++++++++ mysql-test/t/log_tables.test | 70 ++++++++++++++++++++++++++++++++++ sql/log.cc | 11 ++++++ 3 files changed, 149 insertions(+) diff --git a/mysql-test/r/log_tables.result b/mysql-test/r/log_tables.result index 5a90c22fa06..ce3dabe3a56 100644 --- a/mysql-test/r/log_tables.result +++ b/mysql-test/r/log_tables.result @@ -287,3 +287,71 @@ slow_log slow_log_new drop table slow_log_new, general_log_new; use test; +SET GLOBAL LOG_OUTPUT = 'TABLE'; +SET GLOBAL general_log = 0; +FLUSH LOGS; +TRUNCATE TABLE mysql.general_log; +ALTER TABLE mysql.general_log ENGINE = MyISAM; +ALTER TABLE mysql.general_log +ADD COLUMN seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY; +SET GLOBAL general_log = 1; +FLUSH LOGS; +SELECT * FROM mysql.general_log; +event_time user_host thread_id server_id command_type argument seq +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query FLUSH LOGS 1 +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 2 +SELECT * FROM mysql.general_log; +event_time user_host thread_id server_id command_type argument seq +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query FLUSH LOGS 1 +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 2 +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 3 +SELECT "My own query 1"; +My own query 1 +My own query 1 +SELECT "My own query 2"; +My own query 2 +My own query 2 +SELECT * FROM mysql.general_log; +event_time user_host thread_id server_id command_type argument seq +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query FLUSH LOGS 1 +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 2 +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 3 +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT "My own query 1" 4 +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT "My own query 2" 5 +EVENT_TIME USER_HOST THREAD_ID SERVER_ID Query SELECT * FROM mysql.general_log 6 +SET GLOBAL general_log = 0; +FLUSH LOGS; +ALTER TABLE mysql.general_log DROP COLUMN seq; +ALTER TABLE mysql.general_log ENGINE = CSV; +SET @old_long_query_time:=@@long_query_time; +SET GLOBAL slow_query_log = 0; +FLUSH LOGS; +TRUNCATE TABLE mysql.slow_log; +ALTER TABLE mysql.slow_log ENGINE = MyISAM; +ALTER TABLE mysql.slow_log +ADD COLUMN seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY; +SET SESSION long_query_time = 1; +SET GLOBAL slow_query_log = 1; +FLUSH LOGS; +SELECT "My own slow query", sleep(2); +My own slow query sleep(2) +My own slow query 0 +SELECT "My own slow query", sleep(2); +My own slow query sleep(2) +My own slow query 0 +SELECT "My own slow query", sleep(2); +My own slow query sleep(2) +My own slow query 0 +SELECT "My own slow query", sleep(2); +My own slow query sleep(2) +My own slow query 0 +SELECT * FROM mysql.slow_log WHERE seq >= 2 LIMIT 3; +start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text seq +START_TIME USER_HOST QUERY_TIME 00:00:00 1 0 test NULL NULL 1 SELECT "My own slow query", sleep(2) 2 +START_TIME USER_HOST QUERY_TIME 00:00:00 1 0 test NULL NULL 1 SELECT "My own slow query", sleep(2) 3 +START_TIME USER_HOST QUERY_TIME 00:00:00 1 0 test NULL NULL 1 SELECT "My own slow query", sleep(2) 4 +SET GLOBAL slow_query_log = 0; +SET SESSION long_query_time =@old_long_query_time; +FLUSH LOGS; +ALTER TABLE mysql.slow_log DROP COLUMN seq; +ALTER TABLE mysql.slow_log ENGINE = CSV; diff --git a/mysql-test/t/log_tables.test b/mysql-test/t/log_tables.test index b02a47dde6b..e8eff7cba0a 100644 --- a/mysql-test/t/log_tables.test +++ b/mysql-test/t/log_tables.test @@ -400,6 +400,76 @@ show tables like "%log%"; drop table slow_log_new, general_log_new; use test; +# +# Bug#27857 (Log tables supplies the wrong value for generating +# AUTO_INCREMENT numbers) +# + +SET GLOBAL LOG_OUTPUT = 'TABLE'; + +## test the general log + +SET GLOBAL general_log = 0; +FLUSH LOGS; + +TRUNCATE TABLE mysql.general_log; +ALTER TABLE mysql.general_log ENGINE = MyISAM; +ALTER TABLE mysql.general_log + ADD COLUMN seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY; + +SET GLOBAL general_log = 1; +FLUSH LOGS; + +--replace_column 1 EVENT_TIME 2 USER_HOST 3 THREAD_ID 4 SERVER_ID +SELECT * FROM mysql.general_log; +--replace_column 1 EVENT_TIME 2 USER_HOST 3 THREAD_ID 4 SERVER_ID +SELECT * FROM mysql.general_log; +SELECT "My own query 1"; +SELECT "My own query 2"; +--replace_column 1 EVENT_TIME 2 USER_HOST 3 THREAD_ID 4 SERVER_ID +SELECT * FROM mysql.general_log; + +SET GLOBAL general_log = 0; +FLUSH LOGS; + +ALTER TABLE mysql.general_log DROP COLUMN seq; +ALTER TABLE mysql.general_log ENGINE = CSV; + +## test the slow query log + +SET @old_long_query_time:=@@long_query_time; + +SET GLOBAL slow_query_log = 0; +FLUSH LOGS; + +TRUNCATE TABLE mysql.slow_log; +ALTER TABLE mysql.slow_log ENGINE = MyISAM; + +ALTER TABLE mysql.slow_log + ADD COLUMN seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY; + +SET SESSION long_query_time = 1; +SET GLOBAL slow_query_log = 1; +FLUSH LOGS; + +## FLUSH LOGS above might be slow, so the following is +## logged as either seq 1-4 or seq 2-5 +SELECT "My own slow query", sleep(2); +SELECT "My own slow query", sleep(2); +SELECT "My own slow query", sleep(2); +SELECT "My own slow query", sleep(2); + +## So we look for seq 2-4 +--replace_column 1 START_TIME 2 USER_HOST 3 QUERY_TIME +SELECT * FROM mysql.slow_log WHERE seq >= 2 LIMIT 3; + +SET GLOBAL slow_query_log = 0; +SET SESSION long_query_time =@old_long_query_time; +FLUSH LOGS; + +ALTER TABLE mysql.slow_log DROP COLUMN seq; +ALTER TABLE mysql.slow_log ENGINE = CSV; + # kill all connections disconnect con1; disconnect con2; diff --git a/sql/log.cc b/sql/log.cc index 6ef1c1ea912..02de7084222 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -305,6 +305,9 @@ bool Log_to_csv_event_handler::open_log_table(uint log_table_type) table->table->use_all_columns(); table->table->locked_by_logger= TRUE; table->table->no_replicate= TRUE; + + /* Honor next number columns if present */ + table->table->next_number_field= table->table->found_next_number_field; } /* restore thread settings */ if (curr) @@ -438,6 +441,7 @@ bool Log_to_csv_event_handler:: CHARSET_INFO *client_cs) { TABLE *table= general_log.table; + int field_index; /* "INSERT INTO general_log" can generate warning sometimes. @@ -488,6 +492,12 @@ bool Log_to_csv_event_handler:: table->field[4]->set_notnull(); table->field[5]->set_notnull(); + /* Set any extra columns to their default values */ + for (field_index= 6 ; field_index < table->s->fields ; field_index++) + { + table->field[field_index]->set_default(); + } + /* log table entries are not replicated at the moment */ tmp_disable_binlog(current_thd); @@ -1329,6 +1339,7 @@ void Log_to_csv_event_handler:: /* close the table */ log_thd->store_globals(); table->table->file->ha_rnd_end(); + table->table->file->ha_release_auto_increment(); /* discard logger mark before unlock*/ table->table->locked_by_logger= FALSE; close_thread_tables(log_thd, lock_in_use); From 96f57c13dfa1a53c2cfcc3c4925175d072ccd6dd Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Thu, 14 Jun 2007 13:51:35 +0400 Subject: [PATCH 11/26] Add a missing wait_condition call. --- mysql-test/t/events_restart_phase3.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/t/events_restart_phase3.test b/mysql-test/t/events_restart_phase3.test index f5eeb1af2d9..04d879e50ec 100644 --- a/mysql-test/t/events_restart_phase3.test +++ b/mysql-test/t/events_restart_phase3.test @@ -18,3 +18,4 @@ drop database events_test; let $wait_condition= select count(*) = 0 from information_schema.processlist where db='events_test' and command = 'Connect' and user=current_user(); +--source include/wait_condition.inc From 1ff91214c6103b8c4cefd6e47f01a5c7e83a64ba Mon Sep 17 00:00:00 2001 From: "anozdrin/alik@ibm." <> Date: Thu, 14 Jun 2007 18:35:59 +0400 Subject: [PATCH 12/26] The second cleanup patch in scope of BUG#11986. 1. Introduce parse_sql() as a high-level replacement for MYSQLparse(). parse_sql() is responsible to switch and restore "parser context" (THD::m_lip for now). 2. Fix typo in sp.cc: THD::spcont should be reset *before* calling the parser. --- sql/event_data_objects.cc | 4 +--- sql/mysql_priv.h | 3 ++- sql/sp.cc | 42 ++++++++++++++++++------------------- sql/sql_class.cc | 3 ++- sql/sql_parse.cc | 44 ++++++++++++++++++++++++++++++++------- sql/sql_partition.cc | 3 +-- sql/sql_prepare.cc | 7 +++---- sql/sql_trigger.cc | 4 +--- sql/sql_view.cc | 7 +++---- 9 files changed, 70 insertions(+), 47 deletions(-) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index c95a6c5dd3c..727aecfa7bb 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -1875,11 +1875,9 @@ Event_job_data::execute(THD *thd, bool drop) { Lex_input_stream lip(thd, thd->query, thd->query_length); - thd->m_lip= &lip; lex_start(thd); - int err= MYSQLparse(thd); - if (err || thd->is_fatal_error) + if (parse_sql(thd, &lip)) { sql_print_error("Event Scheduler: " "%serror during compilation of %s.%s", diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1c961cf917f..9b9f9340c3f 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -618,6 +618,8 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg, uint max_char_length, CHARSET_INFO *cs, bool no_error); +bool parse_sql(THD *thd, class Lex_input_stream *lip); + enum enum_mysql_completiontype { ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7, COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6 @@ -1953,7 +1955,6 @@ void free_list(I_List *list); void free_list(I_List *list); /* sql_yacc.cc */ -extern int MYSQLparse(void *thd); #ifndef DBUG_OFF extern void turn_parser_debug_on(); #endif diff --git a/sql/sp.cc b/sql/sp.cc index 8fcc80a1504..a8e6c2844a2 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -384,32 +384,32 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged))) goto end; + thd->spcont= NULL; + { Lex_input_stream lip(thd, defstr.c_ptr(), defstr.length()); - thd->m_lip= &lip; lex_start(thd); - ret= MYSQLparse(thd); + + if (parse_sql(thd, &lip) || newlex.sphead == NULL) + { + sp_head *sp= newlex.sphead; + + if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) + goto end; + delete sp; + ret= SP_PARSE_ERROR; + } + else + { + if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) + goto end; + *sphp= newlex.sphead; + (*sphp)->set_definer(&definer_user_name, &definer_host_name); + (*sphp)->set_info(created, modified, &chistics, sql_mode); + (*sphp)->optimize(); + } } - thd->spcont= 0; - if (ret || thd->is_fatal_error || newlex.sphead == NULL) - { - sp_head *sp= newlex.sphead; - - if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) - goto end; - delete sp; - ret= SP_PARSE_ERROR; - } - else - { - if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) - goto end; - *sphp= newlex.sphead; - (*sphp)->set_definer(&definer_user_name, &definer_host_name); - (*sphp)->set_info(created, modified, &chistics, sql_mode); - (*sphp)->optimize(); - } end: lex_end(thd->lex); thd->spcont= old_spcont; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 66959940d90..62f6706d717 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -342,7 +342,8 @@ THD::THD() in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE), - spcont(NULL) + spcont(NULL), + m_lip(NULL) { ulong tmp; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f904404171d..cbe3c4363cf 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5343,12 +5343,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, sp_cache_flush_obsolete(&thd->sp_func_cache); Lex_input_stream lip(thd, inBuf, length); - thd->m_lip= &lip; - int err= MYSQLparse(thd); + bool err= parse_sql(thd, &lip); *found_semicolon= lip.found_semicolon; - if (!err && ! thd->is_fatal_error) + if (!err) { #ifndef NO_EMBEDDED_ACCESS_CHECKS if (mqh_used && thd->user_connect && @@ -5371,8 +5370,8 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, PROCESSLIST. Note that we don't need LOCK_thread_count to modify query_length. */ - if (lip.found_semicolon && - (thd->query_length= (ulong)(lip.found_semicolon - thd->query))) + if (*found_semicolon && + (thd->query_length= (ulong)(*found_semicolon - thd->query))) thd->query_length--; /* Actually execute the query */ mysql_execute_command(thd); @@ -5426,12 +5425,10 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) DBUG_ENTER("mysql_test_parse_for_slave"); Lex_input_stream lip(thd, inBuf, length); - thd->m_lip= &lip; lex_start(thd); mysql_reset_thd_for_next_command(thd); - int err= MYSQLparse((void*) thd); - if (!err && ! thd->is_fatal_error && + if (!parse_sql(thd, &lip) && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ thd->end_statement(); @@ -7123,3 +7120,34 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg, my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_char_length); return TRUE; } + + +extern int MYSQLparse(void *thd); // from sql_yacc.cc + + +/** + This is a wrapper of MYSQLparse(). All the code should call parse_sql() + instead of MYSQLparse(). + + @param thd Thread context. + @param lip Lexer context. + + @return Error status. + @retval FALSE on success. + @retval TRUE on parsing error. +*/ + +bool parse_sql(THD *thd, Lex_input_stream *lip) +{ + bool err_status; + + DBUG_ASSERT(thd->m_lip == NULL); + + thd->m_lip= lip; + + err_status= MYSQLparse(thd) != 0 || thd->is_fatal_error; + + thd->m_lip= NULL; + + return err_status; +} diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index d47aacee924..05e85b34a9c 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3696,7 +3696,6 @@ bool mysql_unpack_partition(THD *thd, thd->variables.character_set_client= system_charset_info; Lex_input_stream lip(thd, part_buf, part_info_len); - thd->m_lip= &lip; lex_start(thd); /* @@ -3725,7 +3724,7 @@ bool mysql_unpack_partition(THD *thd, lex.part_info->part_state= part_state; lex.part_info->part_state_len= part_state_len; DBUG_PRINT("info", ("Parse: %s", part_buf)); - if (MYSQLparse((void*)thd) || thd->is_fatal_error) + if (parse_sql(thd, &lip)) { thd->free_items(); goto end; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 716ca08beb1..2031f4b2448 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2858,12 +2858,11 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) Lex_input_stream lip(thd, thd->query, thd->query_length); lip.stmt_prepare_mode= TRUE; - thd->m_lip= &lip; lex_start(thd); - int err= MYSQLparse((void *)thd); - error= err || thd->is_fatal_error || - thd->net.report_error || init_param_array(this); + error= parse_sql(thd, &lip) || + thd->net.report_error || + init_param_array(this); /* While doing context analysis of the query (in check_prepared_statement) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index f63fcab04bb..ded3a0a6e24 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -981,12 +981,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, thd->variables.sql_mode= (ulong)*trg_sql_mode; Lex_input_stream lip(thd, trg_create_str->str, trg_create_str->length); - thd->m_lip= &lip; lex_start(thd); thd->spcont= 0; - int err= MYSQLparse((void *)thd); - if (err || thd->is_fatal_error) + if (parse_sql(thd, &lip)) { /* Currently sphead is always deleted in case of a parse error */ DBUG_ASSERT(lex.sphead == 0); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index a2d6e080763..dd17024aee0 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -893,7 +893,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, LEX *old_lex, *lex; Query_arena *arena, backup; TABLE_LIST *top_view= table->top_table(); - int res; + bool res; bool result, view_is_mergeable; TABLE_LIST *view_main_select_tables; DBUG_ENTER("mysql_make_view"); @@ -1005,7 +1005,6 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, { Lex_input_stream lip(thd, table->query.str, table->query.length); - thd->m_lip= &lip; lex_start(thd); view_select= &lex->select_lex; view_select->select_number= ++thd->select_number; @@ -1039,7 +1038,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); CHARSET_INFO *save_cs= thd->variables.character_set_client; thd->variables.character_set_client= system_charset_info; - res= MYSQLparse((void *)thd); + res= parse_sql(thd, &lip); if ((old_lex->sql_command == SQLCOM_SHOW_FIELDS) || (old_lex->sql_command == SQLCOM_SHOW_CREATE)) @@ -1048,7 +1047,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, thd->variables.character_set_client= save_cs; thd->variables.sql_mode= save_mode; } - if (!res && !thd->is_fatal_error) + if (!res) { TABLE_LIST *view_tables= lex->query_tables; TABLE_LIST *view_tables_tail= 0; From f8be48bb4f27e4472346987dde341f393dc7d944 Mon Sep 17 00:00:00 2001 From: "anozdrin/alik@ibm." <> Date: Thu, 14 Jun 2007 18:49:17 +0400 Subject: [PATCH 13/26] This is the 3-rd part of patch for BUG#11986: remove redundant "body" from Event_parse_data (use sp_head::m_body). --- sql/event_data_objects.cc | 155 +++++++++++++------------------------ sql/event_data_objects.h | 14 ++-- sql/event_db_repository.cc | 28 +++++-- sql/sql_yacc.yy | 5 +- 4 files changed, 81 insertions(+), 121 deletions(-) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 727aecfa7bb..757cf7f93fb 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -94,17 +94,18 @@ Event_parse_data::Event_parse_data() :on_completion(Event_basic::ON_COMPLETION_DROP), status(Event_basic::ENABLED), do_not_create(FALSE), - item_starts(NULL), item_ends(NULL), item_execute_at(NULL), - starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE), - item_expression(NULL), expression(0) + body_changed(FALSE), + item_starts(NULL), item_ends(NULL), item_execute_at(NULL), + starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE), + item_expression(NULL), expression(0) { DBUG_ENTER("Event_parse_data::Event_parse_data"); /* Actually in the parser STARTS is always set */ starts= ends= execute_at= 0; - body.str= comment.str= NULL; - body.length= comment.length= 0; + comment.str= NULL; + comment.length= 0; DBUG_VOID_RETURN; } @@ -137,36 +138,6 @@ Event_parse_data::init_name(THD *thd, sp_name *spn) } -/* - Set body of the event - what should be executed. - - SYNOPSIS - Event_parse_data::init_body() - thd THD - - NOTE - The body is extracted by copying all data between the - start of the body set by another method and the current pointer in Lex. - - See related code in sp_head::init_strings(). -*/ - -void -Event_parse_data::init_body(THD *thd) -{ - DBUG_ENTER("Event_parse_data::init_body"); - - /* This method is called from within the parser, from sql_yacc.yy */ - DBUG_ASSERT(thd->m_lip != NULL); - - body.length= thd->m_lip->get_cpp_ptr() - body_begin; - body.str= thd->strmake(body_begin, body.length); - trim_whitespace(thd->charset(), & body); - - DBUG_VOID_RETURN; -} - - /* This function is called on CREATE EVENT or ALTER EVENT. When either ENDS or AT is in the past, we are trying to create an event that @@ -788,36 +759,32 @@ Event_timed::init() } -/* - Loads an event's body from a row from mysql.event +/** + Load an event's body from a row from mysql.event. + @details This method is silent on errors and should behave like that. + Callers should handle throwing of error messages. The reason is that the + class should not know about how to deal with communication. - SYNOPSIS - Event_job_data::load_from_row(THD *thd, TABLE *table) - - RETURN VALUE - 0 OK - EVEX_GET_FIELD_FAILED Error - - NOTES - This method is silent on errors and should behave like that. Callers - should handle throwing of error messages. The reason is that the class - should not know about how to deal with communication. + @return Operation status + @retval FALSE OK + @retval TRUE Error */ -int +bool Event_job_data::load_from_row(THD *thd, TABLE *table) { char *ptr; uint len; + LEX_STRING tz_name; + DBUG_ENTER("Event_job_data::load_from_row"); if (!table) - goto error; + DBUG_RETURN(TRUE); if (table->s->fields < ET_FIELD_COUNT) - goto error; + DBUG_RETURN(TRUE); - LEX_STRING tz_name; if (load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name, @@ -825,10 +792,10 @@ Event_job_data::load_from_row(THD *thd, TABLE *table) ET_FIELD_DEFINER, &definer, ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT)) - goto error; + DBUG_RETURN(TRUE); if (load_time_zone(thd, tz_name)) - goto error; + DBUG_RETURN(TRUE); ptr= strchr(definer.str, '@'); @@ -845,29 +812,23 @@ Event_job_data::load_from_row(THD *thd, TABLE *table) sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int(); - DBUG_RETURN(0); -error: - DBUG_RETURN(EVEX_GET_FIELD_FAILED); + DBUG_RETURN(FALSE); } -/* - Loads an event from a row from mysql.event +/** + Load an event's body from a row from mysql.event. - SYNOPSIS - Event_queue_element::load_from_row(THD *thd, TABLE *table) + @details This method is silent on errors and should behave like that. + Callers should handle throwing of error messages. The reason is that the + class should not know about how to deal with communication. - RETURN VALUE - 0 OK - EVEX_GET_FIELD_FAILED Error - - NOTES - This method is silent on errors and should behave like that. Callers - should handle throwing of error messages. The reason is that the class - should not know about how to deal with communication. + @return Operation status + @retval FALSE OK + @retval TRUE Error */ -int +bool Event_queue_element::load_from_row(THD *thd, TABLE *table) { char *ptr; @@ -877,10 +838,10 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) DBUG_ENTER("Event_queue_element::load_from_row"); if (!table) - goto error; + DBUG_RETURN(TRUE); if (table->s->fields < ET_FIELD_COUNT) - goto error; + DBUG_RETURN(TRUE); if (load_string_fields(table->field, ET_FIELD_DB, &dbname, @@ -888,10 +849,10 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ET_FIELD_DEFINER, &definer, ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT)) - goto error; + DBUG_RETURN(TRUE); if (load_time_zone(thd, tz_name)) - goto error; + DBUG_RETURN(TRUE); starts_null= table->field[ET_FIELD_STARTS]->is_null(); if (!starts_null) @@ -921,7 +882,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) { if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time, TIME_NO_ZERO_DATE)) - goto error; + DBUG_RETURN(TRUE); execute_at= sec_since_epoch_TIME(&time); } @@ -940,13 +901,13 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str); if (!(tmp.length= str.length())) - goto error; + DBUG_RETURN(TRUE); tmp.str= str.c_ptr_safe(); i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info); if (i < 0) - goto error; + DBUG_RETURN(TRUE); interval= (interval_type) i; } @@ -959,7 +920,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) last_executed_changed= FALSE; if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS) - goto error; + DBUG_RETURN(TRUE); DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr)); @@ -978,40 +939,34 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) break; } if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ORIGINATOR])) == NullS) - goto error; + DBUG_RETURN(TRUE); originator = table->field[ET_FIELD_ORIGINATOR]->val_int(); /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ON_COMPLETION])) == NullS) - goto error; + DBUG_RETURN(TRUE); on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP: Event_queue_element::ON_COMPLETION_PRESERVE); - DBUG_RETURN(0); -error: - DBUG_RETURN(EVEX_GET_FIELD_FAILED); + DBUG_RETURN(FALSE); } -/* - Loads an event from a row from mysql.event +/** + Load an event's body from a row from mysql.event. - SYNOPSIS - Event_timed::load_from_row(THD *thd, TABLE *table) + @details This method is silent on errors and should behave like that. + Callers should handle throwing of error messages. The reason is that the + class should not know about how to deal with communication. - RETURN VALUE - 0 OK - EVEX_GET_FIELD_FAILED Error - - NOTES - This method is silent on errors and should behave like that. Callers - should handle throwing of error messages. The reason is that the class - should not know about how to deal with communication. + @return Operation status + @retval FALSE OK + @retval TRUE Error */ -int +bool Event_timed::load_from_row(THD *thd, TABLE *table) { char *ptr; @@ -1020,12 +975,12 @@ Event_timed::load_from_row(THD *thd, TABLE *table) DBUG_ENTER("Event_timed::load_from_row"); if (Event_queue_element::load_from_row(thd, table)) - goto error; + DBUG_RETURN(TRUE); if (load_string_fields(table->field, ET_FIELD_BODY, &body, ET_FIELD_COUNT)) - goto error; + DBUG_RETURN(TRUE); ptr= strchr(definer.str, '@'); @@ -1052,9 +1007,7 @@ Event_timed::load_from_row(THD *thd, TABLE *table) sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int(); - DBUG_RETURN(0); -error: - DBUG_RETURN(EVEX_GET_FIELD_FAILED); + DBUG_RETURN(FALSE); } diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index 8e03ab19602..d79732780ff 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -77,7 +77,7 @@ public: Event_basic(); virtual ~Event_basic(); - virtual int + virtual bool load_from_row(THD *thd, TABLE *table) = 0; protected: @@ -119,7 +119,7 @@ public: Event_queue_element(); virtual ~Event_queue_element(); - virtual int + virtual bool load_from_row(THD *thd, TABLE *table); bool @@ -157,7 +157,7 @@ public: void init(); - virtual int + virtual bool load_from_row(THD *thd, TABLE *table); int @@ -176,7 +176,7 @@ public: Event_job_data(); - virtual int + virtual bool load_from_row(THD *thd, TABLE *table); bool @@ -205,12 +205,11 @@ public: */ bool do_not_create; - const char *body_begin; + bool body_changed; LEX_STRING dbname; LEX_STRING name; LEX_STRING definer;// combination of user and host - LEX_STRING body; LEX_STRING comment; Item* item_starts; @@ -235,9 +234,6 @@ public: bool check_parse_data(THD *thd); - void - init_body(THD *thd); - private: void diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index fa17ee8a380..703c4160216 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -15,6 +15,7 @@ #include "mysql_priv.h" #include "event_db_repository.h" +#include "sp_head.h" #include "event_data_objects.h" #include "events.h" #include "sql_show.h" @@ -141,7 +142,10 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = */ static bool -mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, +mysql_event_fill_row(THD *thd, + TABLE *table, + Event_parse_data *et, + sp_head *sp, my_bool is_update) { CHARSET_INFO *scs= system_charset_info; @@ -152,7 +156,6 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str)); DBUG_PRINT("info", ("name =[%s]", et->name.str)); - DBUG_PRINT("info", ("body =[%s]", et->body.str)); if (table->s->fields < ET_FIELD_COUNT) { @@ -187,11 +190,18 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, Change the SQL_MODE only if body was present in an ALTER EVENT and of course always during CREATE EVENT. */ - if (et->body.str) + if (et->body_changed) { + DBUG_ASSERT(sp->m_body.str); + fields[ET_FIELD_SQL_MODE]->store((longlong)thd->variables.sql_mode, TRUE); - if (fields[f_num= ET_FIELD_BODY]->store(et->body.str, et->body.length, scs)) + + if (fields[f_num= ET_FIELD_BODY]->store(sp->m_body.str, + sp->m_body.length, + scs)) + { goto err_truncate; + } } if (et->expression) @@ -513,10 +523,12 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, { int ret= 1; TABLE *table= NULL; + sp_head *sp= thd->lex->sphead; DBUG_ENTER("Event_db_repository::create_event"); DBUG_PRINT("info", ("open mysql.event for update")); + DBUG_ASSERT(sp); if (open_event_table(thd, TL_WRITE, &table)) goto end; @@ -561,7 +573,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, goto end; } - if (parse_data->body.length > table->field[ET_FIELD_BODY]->field_length) + if (sp->m_body.length > table->field[ET_FIELD_BODY]->field_length) { my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str); goto end; @@ -573,7 +585,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, mysql_event_fill_row() calls my_error() in case of error so no need to handle it here */ - if (mysql_event_fill_row(thd, table, parse_data, FALSE)) + if (mysql_event_fill_row(thd, table, parse_data, sp, FALSE)) goto end; table->field[ET_FIELD_STATUS]->store((longlong)parse_data->status, TRUE); @@ -617,7 +629,9 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, { CHARSET_INFO *scs= system_charset_info; TABLE *table= NULL; + sp_head *sp= thd->lex->sphead; int ret= 1; + DBUG_ENTER("Event_db_repository::update_event"); /* None or both must be set */ @@ -661,7 +675,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, mysql_event_fill_row() calls my_error() in case of error so no need to handle it here */ - if (mysql_event_fill_row(thd, table, parse_data, TRUE)) + if (mysql_event_fill_row(thd, table, parse_data, sp, TRUE)) goto end; if (new_dbname) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a17a0475ac5..8a4c99b6d00 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1873,9 +1873,6 @@ ev_sql_stmt: lex->sphead->m_chistics= &lex->sp_chistics; lex->sphead->m_body_begin= lip->get_cpp_ptr(); - - lex->event_parse_data->body_begin= lip->get_cpp_ptr(); - } ev_sql_stmt_inner { @@ -1888,7 +1885,7 @@ ev_sql_stmt: lex->sp_chistics.suid= SP_IS_SUID; //always the definer! - lex->event_parse_data->init_body(thd); + lex->event_parse_data->body_changed= TRUE; } ; From 62e3e462757af6dd2e0d8dab0ecefb77aa461768 Mon Sep 17 00:00:00 2001 From: "anozdrin/alik@ibm." <> Date: Thu, 14 Jun 2007 19:23:55 +0400 Subject: [PATCH 14/26] This the 4-th patch in scope of CS patch (BUG#11986). The patch contains the following changes: - Introduce auxilary functions to convenient work with character sets: - resolve_charset(); - resolve_collation(); - get_default_db_collation(); - Introduce lex_string_set(); - Refactor Table_trigger_list::process_triggers() & sp_head::execute_trigger() to be consistent with other code; - Move reusable code from add_table_for_trigger() into build_trn_path(), check_trn_exists() and load_table_name_for_trigger() to be used in the following patch. - Rename triggers_file_ext and trigname_file_ext into TRN_EXT and TRG_EXT respectively. --- include/my_sys.h | 8 ++ mysys/charset.c | 64 ++++++++++ sql/handler.cc | 4 +- sql/mysql_priv.h | 13 +- sql/sp_head.cc | 86 ++++++++++--- sql/sp_head.h | 6 +- sql/sql_db.cc | 49 +++++-- sql/sql_trigger.cc | 310 ++++++++++++++++++++++++++------------------- sql/sql_trigger.h | 19 ++- 9 files changed, 386 insertions(+), 173 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index a8633982f84..d1a253e4a7f 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -904,6 +904,14 @@ extern CHARSET_INFO *get_charset(uint cs_number, myf flags); extern CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags); extern CHARSET_INFO *get_charset_by_csname(const char *cs_name, uint cs_flags, myf my_flags); + +extern bool resolve_charset(CHARSET_INFO **cs, + const char *cs_name, + CHARSET_INFO *default_cs); +extern bool resolve_collation(CHARSET_INFO **cl, + const char *cl_name, + CHARSET_INFO *default_cl); + extern void free_charsets(void); extern char *get_charsets_dir(char *buf); extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2); diff --git a/mysys/charset.c b/mysys/charset.c index c6065f87df3..5f9521eeab8 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -573,6 +573,70 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name, } +/** + Resolve character set by the character set name (utf8, latin1, ...). + + The function tries to resolve character set by the specified name. If + there is character set with the given name, it is assigned to the "cs" + parameter and FALSE is returned. If there is no such character set, + "default_cs" is assigned to the "cs" and TRUE is returned. + + @param[out] cs Variable to store character set. + @param[in] cs_name Character set name. + @param[in] default_cs Default character set. + + @return FALSE if character set was resolved successfully; TRUE if there + is no character set with given name. +*/ + +bool resolve_charset(CHARSET_INFO **cs, + const char *cs_name, + CHARSET_INFO *default_cs) +{ + *cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0)); + + if (*cs == NULL) + { + *cs= default_cs; + return TRUE; + } + + return FALSE; +} + + +/** + Resolve collation by the collation name (utf8_general_ci, ...). + + The function tries to resolve collation by the specified name. If there + is collation with the given name, it is assigned to the "cl" parameter + and FALSE is returned. If there is no such collation, "default_cl" is + assigned to the "cl" and TRUE is returned. + + @param[out] cl Variable to store collation. + @param[in] cl_name Collation name. + @param[in] default_cl Default collation. + + @return FALSE if collation was resolved successfully; TRUE if there is no + collation with given name. +*/ + +bool resolve_collation(CHARSET_INFO **cl, + const char *cl_name, + CHARSET_INFO *default_cl) +{ + *cl= get_charset_by_name(cl_name, MYF(0)); + + if (*cl == NULL) + { + *cl= default_cl; + return TRUE; + } + + return FALSE; +} + + /* Escape string with backslashes (\) diff --git a/sql/handler.cc b/sql/handler.cc index afb88dc962d..79719745119 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3376,8 +3376,8 @@ TYPELIB *ha_known_exts(void) const char **ext, *old_ext; known_extensions_id= mysys_usage_id; - found_exts.push_back((char*) triggers_file_ext); - found_exts.push_back((char*) trigname_file_ext); + found_exts.push_back((char*) TRG_EXT); + found_exts.push_back((char*) TRN_EXT); plugin_foreach(NULL, exts_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9b9f9340c3f..762fafdaaa7 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -101,7 +101,7 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ (Old), (Ver), (New)); \ else \ - sql_print_warning("The syntax %s is deprecated and will be removed " \ + sql_print_warning("The syntax '%s' is deprecated and will be removed " \ "in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \ } while(0) @@ -1590,6 +1590,7 @@ bool check_db_dir_existence(const char *db_name); bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); bool load_db_opt_by_name(THD *thd, const char *db_name, HA_CREATE_INFO *db_create_info); +CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name); bool my_dbopt_init(void); void my_dbopt_cleanup(void); extern int creating_database; // How many database locks are made @@ -1610,8 +1611,8 @@ extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword; extern const char **errmesg; /* Error messages */ extern const char *myisam_recover_options_str; extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond; -extern const char * const triggers_file_ext; -extern const char * const trigname_file_ext; +extern const char * const TRG_EXT; +extern const char * const TRN_EXT; extern Eq_creator eq_creator; extern Ne_creator ne_creator; extern Gt_creator gt_creator; @@ -2146,6 +2147,12 @@ bool schema_table_store_record(THD *thd, TABLE *table); int item_create_init(); void item_create_cleanup(); +inline void lex_string_set(LEX_STRING *lex_str, const char *c_str) +{ + lex_str->str= (char *) c_str; + lex_str->length= strlen(c_str); +} + #endif /* MYSQL_SERVER */ #endif /* MYSQL_CLIENT */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index bd38de2dd42..edfa493da9f 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1268,30 +1268,31 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, #endif // ! NO_EMBEDDED_ACCESS_CHECKS -/* - Execute a trigger: - - changes security context for triggers - - switch to new memroot - - call sp_head::execute - - restore old memroot - - restores security context +/** + Execute trigger stored program. - SYNOPSIS - sp_head::execute_trigger() - thd Thread handle - db database name - table table name - grant_info GRANT_INFO structure to be filled with - information about definer's privileges - on subject table - - RETURN - FALSE on success - TRUE on error + Execute a trigger: + - changes security context for triggers; + - switch to new memroot; + - call sp_head::execute; + - restore old memroot; + - restores security context. + + @param thd Thread context. + @param db_name Database name. + @param table_name Table name. + @param grant_info GRANT_INFO structure to be filled with information + about definer's privileges on subject table. + + @return Error status. + @retval FALSE on success. + @retval TRUE on error. */ bool -sp_head::execute_trigger(THD *thd, const char *db, const char *table, +sp_head::execute_trigger(THD *thd, + const LEX_STRING *db_name, + const LEX_STRING *table_name, GRANT_INFO *grant_info) { sp_rcontext *octx = thd->spcont; @@ -1304,6 +1305,46 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table, DBUG_ENTER("sp_head::execute_trigger"); DBUG_PRINT("info", ("trigger %s", m_name.str)); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_ctx= NULL; + + + if (m_chistics->suid != SP_IS_NOT_SUID && + m_security_ctx.change_security_context(thd, + &m_definer_user, + &m_definer_host, + &m_db, + &save_ctx)) + DBUG_RETURN(TRUE); + + /* + Fetch information about table-level privileges for subject table into + GRANT_INFO instance. The access check itself will happen in + Item_trigger_field, where this information will be used along with + information about column-level privileges. + */ + + fill_effective_table_privileges(thd, + grant_info, + db_name->str, + table_name->str); + + /* Check that the definer has TRIGGER privilege on the subject table. */ + + if (!(grant_info->privilege & TRIGGER_ACL)) + { + char priv_desc[128]; + get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL); + + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc, + thd->security_ctx->priv_user, thd->security_ctx->host_or_ip, + table_name->str); + + m_security_ctx.restore_security_context(thd, save_ctx); + DBUG_RETURN(TRUE); + } +#endif // NO_EMBEDDED_ACCESS_CHECKS + /* Prepare arena and memroot for objects which lifetime is whole duration of trigger call (sp_rcontext, it's tables and items, @@ -1336,6 +1377,11 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table, err_with_cleanup: thd->restore_active_arena(&call_arena, &backup_arena); + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + m_security_ctx.restore_security_context(thd, save_ctx); +#endif // NO_EMBEDDED_ACCESS_CHECKS + delete nctx; call_arena.free_items(); free_root(&call_mem_root, MYF(0)); diff --git a/sql/sp_head.h b/sql/sp_head.h index 89af0259706..f3c3ebfe6e8 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -216,8 +216,10 @@ public: destroy(); bool - execute_trigger(THD *thd, const char *db, const char *table, - GRANT_INFO *grant_onfo); + execute_trigger(THD *thd, + const LEX_STRING *db_name, + const LEX_STRING *table_name, + GRANT_INFO *grant_info); bool execute_function(THD *thd, Item **args, uint argcount, Field *return_fld); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 82938184245..43d84740f0b 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -538,6 +538,37 @@ bool load_db_opt_by_name(THD *thd, const char *db_name, } +/** + Return default database collation. + + @param thd Thread context. + @param db_name Database name. + + @return CHARSET_INFO object. The operation always return valid character + set, even if the database does not exist. +*/ + +CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name) +{ + HA_CREATE_INFO db_info; + + if (thd->db != NULL && strcmp(db_name, thd->db) == 0) + return thd->db_charset; + + load_db_opt_by_name(thd, db_name, &db_info); + + /* + NOTE: even if load_db_opt_by_name() fails, + db_info.default_table_charset contains valid character set + (collation_server). We should not fail if load_db_opt_by_name() fails, + because it is valid case. If a database has been created just by + "mkdir", it does not contain db.opt file, but it is valid database. + */ + + return db_info.default_table_charset; +} + + /* Create a database @@ -751,10 +782,8 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) if ((error=write_db_opt(thd, path, create_info))) goto exit; - /* - Change options if current database is being altered - TODO: Delete this code - */ + /* Change options if current database is being altered. */ + if (thd->db && !strcmp(thd->db,db)) { thd->db_charset= create_info->default_table_charset ? @@ -1358,6 +1387,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) Security_context *sctx= thd->security_ctx; ulong db_access= sctx->db_access; + CHARSET_INFO *db_default_cl; DBUG_ENTER("mysql_change_db"); DBUG_PRINT("enter",("name: '%s'", new_db_name->str)); @@ -1487,16 +1517,9 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) attributes and will be freed in THD::~THD(). */ - { - HA_CREATE_INFO db_options; + db_default_cl= get_default_db_collation(thd, new_db_file_name.str); - load_db_opt_by_name(thd, new_db_name->str, &db_options); - - mysql_change_db_impl(thd, &new_db_file_name, db_access, - db_options.default_table_charset ? - db_options.default_table_charset : - thd->variables.collation_server); - } + mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl); DBUG_RETURN(FALSE); } diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index ded3a0a6e24..7433a3f45cd 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -23,7 +23,7 @@ static const LEX_STRING triggers_file_type= { C_STRING_WITH_LEN("TRIGGERS") }; -const char * const triggers_file_ext= ".TRG"; +const char * const TRG_EXT= ".TRG"; /* Table of .TRG file field descriptors. @@ -79,7 +79,7 @@ struct st_trigname static const LEX_STRING trigname_file_type= { C_STRING_WITH_LEN("TRIGGERNAME") }; -const char * const trigname_file_ext= ".TRN"; +const char * const TRN_EXT= ".TRN"; static File_option trigname_file_parameters[]= { @@ -132,6 +132,7 @@ private: LEX_STRING *trigger_table_value; }; + /* Create or drop trigger for table. @@ -463,14 +464,14 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, sql_create_definition_file() files handles renaming and backup of older versions */ - file.length= build_table_filename(file_buff, FN_REFLEN-1, + file.length= build_table_filename(file_buff, FN_REFLEN - 1, tables->db, tables->table_name, - triggers_file_ext, 0); + TRG_EXT, 0); file.str= file_buff; trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1, tables->db, lex->spname->m_name.str, - trigname_file_ext, 0); + TRN_EXT, 0); trigname_file.str= trigname_buff; /* Use the filesystem to enforce trigger namespace constraints. */ @@ -604,7 +605,7 @@ err_with_cleanup: static bool rm_trigger_file(char *path, const char *db, const char *table_name) { - build_table_filename(path, FN_REFLEN-1, db, table_name, triggers_file_ext, 0); + build_table_filename(path, FN_REFLEN-1, db, table_name, TRG_EXT, 0); return my_delete(path, MYF(MY_WME)); } @@ -627,8 +628,7 @@ static bool rm_trigger_file(char *path, const char *db, static bool rm_trigname_file(char *path, const char *db, const char *trigger_name) { - build_table_filename(path, FN_REFLEN-1, - db, trigger_name, trigname_file_ext, 0); + build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0); return my_delete(path, MYF(MY_WME)); } @@ -653,8 +653,8 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db, char file_buff[FN_REFLEN]; LEX_STRING file; - file.length= build_table_filename(file_buff, FN_REFLEN-1, db, table_name, - triggers_file_ext, 0); + file.length= build_table_filename(file_buff, FN_REFLEN - 1, db, table_name, + TRG_EXT, 0); file.str= file_buff; return sql_create_definition_file(NULL, &file, &triggers_file_type, (uchar*)triggers, triggers_file_parameters, @@ -834,8 +834,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, DBUG_ENTER("Table_triggers_list::check_n_load"); - path.length= build_table_filename(path_buff, FN_REFLEN-1, - db, table_name, triggers_file_ext, 0); + path.length= build_table_filename(path_buff, FN_REFLEN - 1, + db, table_name, TRG_EXT, 0); path.str= path_buff; // QQ: should we analyze errno somehow ? @@ -1106,7 +1106,7 @@ err_with_lex_cleanup: be merged into .FRM anyway. */ my_error(ER_WRONG_OBJECT, MYF(0), - table_name, triggers_file_ext+1, "TRIGGER"); + table_name, TRG_EXT + 1, "TRIGGER"); DBUG_RETURN(1); } @@ -1166,83 +1166,66 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, } -/* +/** Find trigger's table from trigger identifier and add it to the statement table list. - SYNOPSIS - mysql_table_for_trigger() - thd - current thread context - trig - identifier for trigger - if_exists - treat a not existing trigger as a warning if TRUE - table - pointer to TABLE_LIST object for the table trigger (output) + @param[in] thd Thread context. + @param[in] trg_name Trigger name. + @param[in] if_exists TRUE if SQL statement contains "IF EXISTS" clause. + That means a warning instead of error should be + thrown if trigger with given name does not exist. + @param[out] table Pointer to TABLE_LIST object for the + table trigger. - RETURN VALUE - 0 Success - 1 Error + @return Operation status + @retval FALSE On success. + @retval TRUE Otherwise. */ -int -add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, - TABLE_LIST **table) +bool add_table_for_trigger(THD *thd, + sp_name *trg_name, + bool if_exists, + TABLE_LIST **table) { LEX *lex= thd->lex; - char path_buff[FN_REFLEN]; - LEX_STRING path; - File_parser *parser; - struct st_trigname trigname; - Handle_old_incorrect_trigger_table_hook trigger_table_hook( - path_buff, &trigname.trigger_table); - + char trn_path_buff[FN_REFLEN]; + LEX_STRING trn_path= { trn_path_buff, 0 }; + LEX_STRING tbl_name; + DBUG_ENTER("add_table_for_trigger"); - DBUG_ASSERT(table != NULL); - path.length= build_table_filename(path_buff, FN_REFLEN-1, - trig->m_db.str, trig->m_name.str, - trigname_file_ext, 0); - path.str= path_buff; + build_trn_path(thd, trg_name, &trn_path); - if (access(path_buff, F_OK)) + if (check_trn_exists(&trn_path)) { if (if_exists) { push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_TRG_DOES_NOT_EXIST, - ER(ER_TRG_DOES_NOT_EXIST)); + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_TRG_DOES_NOT_EXIST, + ER(ER_TRG_DOES_NOT_EXIST)); + *table= NULL; - DBUG_RETURN(0); + + DBUG_RETURN(FALSE); } my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } - if (!(parser= sql_parse_prepare(&path, thd->mem_root, 1))) - DBUG_RETURN(1); - - if (!is_equal(&trigname_file_type, parser->type())) - { - my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext+1, - "TRIGGERNAME"); - DBUG_RETURN(1); - } - - if (parser->parse((uchar*)&trigname, thd->mem_root, - trigname_file_parameters, 1, - &trigger_table_hook)) - DBUG_RETURN(1); + if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name)) + DBUG_RETURN(TRUE); /* We need to reset statement table list to be PS/SP friendly. */ lex->query_tables= 0; lex->query_tables_last= &lex->query_tables; - *table= sp_add_to_query_tables(thd, lex, trig->m_db.str, - trigname.trigger_table.str, TL_IGNORE); - if (! *table) - DBUG_RETURN(1); + *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str, + tbl_name.str, TL_IGNORE); - DBUG_RETURN(0); + DBUG_RETURN(*table ? FALSE : TRUE); } @@ -1426,7 +1409,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name, { trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1, db_name, trigger->str, - trigname_file_ext, 0); + TRN_EXT, 0); trigname_file.str= trigname_buff; trigname.trigger_table= *new_table_name; @@ -1535,77 +1518,54 @@ end: } -bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, +/** + Execute trigger for given (event, time) pair. + + The operation executes trigger for the specified event (insert, update, + delete) and time (after, before) if it is set. + + @param thd + @param event + @param time_type, + @param old_row_is_record1 + + @return Error status. + @retval FALSE on success. + @retval TRUE on error. +*/ + +bool Table_triggers_list::process_triggers(THD *thd, + trg_event_type event, trg_action_time_type time_type, bool old_row_is_record1) { - bool err_status= FALSE; - sp_head *sp_trigger= bodies[event][time_type]; + bool err_status; + Sub_statement_state statement_state; - if (sp_trigger) + if (!bodies[event][time_type]) + return FALSE; + + if (old_row_is_record1) { - Sub_statement_state statement_state; - - if (old_row_is_record1) - { - old_field= record1_field; - new_field= trigger_table->field; - } - else - { - new_field= record1_field; - old_field= trigger_table->field; - } -#ifndef NO_EMBEDDED_ACCESS_CHECKS - Security_context *sctx= &sp_trigger->m_security_ctx; - Security_context *save_ctx= NULL; - - - if (sp_trigger->m_chistics->suid != SP_IS_NOT_SUID && - sctx->change_security_context(thd, - &sp_trigger->m_definer_user, - &sp_trigger->m_definer_host, - &sp_trigger->m_db, - &save_ctx)) - return TRUE; - - /* - Fetch information about table-level privileges to GRANT_INFO structure for - subject table. Check of privileges that will use it and information about - column-level privileges will happen in Item_trigger_field::fix_fields(). - */ - - fill_effective_table_privileges(thd, - &subject_table_grants[event][time_type], - trigger_table->s->db.str, - trigger_table->s->table_name.str); - - /* Check that the definer has TRIGGER privilege on the subject table. */ - - if (!(subject_table_grants[event][time_type].privilege & TRIGGER_ACL)) - { - char priv_desc[128]; - get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL); - - my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc, - thd->security_ctx->priv_user, thd->security_ctx->host_or_ip, - trigger_table->s->table_name.str); - - sctx->restore_security_context(thd, save_ctx); - return TRUE; - } -#endif // NO_EMBEDDED_ACCESS_CHECKS - - thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER); - err_status= sp_trigger->execute_trigger - (thd, trigger_table->s->db.str, trigger_table->s->table_name.str, - &subject_table_grants[event][time_type]); - thd->restore_sub_statement_state(&statement_state); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - sctx->restore_security_context(thd, save_ctx); -#endif // NO_EMBEDDED_ACCESS_CHECKS + old_field= record1_field; + new_field= trigger_table->field; } + else + { + new_field= record1_field; + old_field= trigger_table->field; + } + + thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER); + + err_status= + bodies[event][time_type]->execute_trigger( + thd, + &trigger_table->s->db, + &trigger_table->s->table_name, + &subject_table_grants[event][time_type]); + + thd->restore_sub_statement_state(&statement_state); return err_status; } @@ -1747,3 +1707,95 @@ process_unknown_string(char *&unknown_key, uchar* base, MEM_ROOT *mem_root, } DBUG_RETURN(FALSE); } + + +/** + Contruct path to TRN-file. + + @param thd[in] Thread context. + @param trg_name[in] Trigger name. + @param trn_path[out] Variable to store constructed path +*/ + +void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path) +{ + /* Construct path to the TRN-file. */ + + trn_path->length= build_table_filename(trn_path->str, + FN_REFLEN - 1, + trg_name->m_db.str, + trg_name->m_name.str, + TRN_EXT, + 0); +} + + +/** + Check if TRN-file exists. + + @return + @retval TRUE if TRN-file does not exist. + @retval FALSE if TRN-file exists. +*/ + +bool check_trn_exists(const LEX_STRING *trn_path) +{ + return access(trn_path->str, F_OK) != 0; +} + + +/** + Retrieve table name for given trigger. + + @param thd[in] Thread context. + @param trg_name[in] Trigger name. + @param trn_path[in] Path to the corresponding TRN-file. + @param tbl_name[out] Variable to store retrieved table name. + + @return Error status. + @retval FALSE on success. + @retval TRUE if table name could not be retrieved. +*/ + +bool load_table_name_for_trigger(THD *thd, + const sp_name *trg_name, + const LEX_STRING *trn_path, + LEX_STRING *tbl_name) +{ + File_parser *parser; + struct st_trigname trn_data; + + Handle_old_incorrect_trigger_table_hook trigger_table_hook( + trn_path->str, + &trn_data.trigger_table); + + DBUG_ENTER("load_table_name_for_trigger"); + + /* Parse the TRN-file. */ + + if (!(parser= sql_parse_prepare(trn_path, thd->mem_root, TRUE))) + DBUG_RETURN(TRUE); + + if (!is_equal(&trigname_file_type, parser->type())) + { + my_error(ER_WRONG_OBJECT, MYF(0), + trg_name->m_name.str, + TRN_EXT + 1, + "TRIGGERNAME"); + + DBUG_RETURN(TRUE); + } + + if (parser->parse((uchar*) &trn_data, thd->mem_root, + trigname_file_parameters, 1, + &trigger_table_hook)) + DBUG_RETURN(TRUE); + + /* Copy trigger table name. */ + + *tbl_name= trn_data.trigger_table; + + /* That's all. */ + + DBUG_RETURN(FALSE); +} diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 7d99dd811cd..c305efb5029 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -17,7 +17,7 @@ /* This class holds all information about triggers of table. - QQ: Will it be merged into TABLE in future ? + QQ: Will it be merged into TABLE in the future ? */ class Table_triggers_list: public Sql_alloc @@ -143,6 +143,17 @@ private: extern const LEX_STRING trg_action_time_type_names[]; extern const LEX_STRING trg_event_type_names[]; -int -add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, - TABLE_LIST **table); +bool add_table_for_trigger(THD *thd, + sp_name *trg_name, + bool continue_if_not_exist, + TABLE_LIST **table); + +void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path); + +bool check_trn_exists(const LEX_STRING *trn_path); + +bool load_table_name_for_trigger(THD *thd, + const sp_name *trg_name, + const LEX_STRING *trn_path, + LEX_STRING *tbl_name); + From 754e9788b8775f5135227f0917640dd79b178ebb Mon Sep 17 00:00:00 2001 From: "anozdrin/alik@ibm." <> Date: Thu, 14 Jun 2007 22:14:52 +0400 Subject: [PATCH 15/26] Part of patch for BUG#11986: make sp_head::m_body_begin pointer private and provide a setter for it. The setter will be used to construct UTF-query in the following patches. --- sql/sp_head.cc | 7 +++++++ sql/sp_head.h | 5 +++++ sql/sql_yacc.yy | 8 ++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index edfa493da9f..feceb2fb960 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2053,6 +2053,13 @@ sp_head::set_info(longlong created, longlong modified, } +void +sp_head::set_body_begin_ptr(Lex_input_stream *lip, const char *begin_ptr) +{ + m_body_begin= begin_ptr; +} + + void sp_head::set_definer(const char *definer, uint definerlen) { diff --git a/sql/sp_head.h b/sql/sp_head.h index f3c3ebfe6e8..2d3bc1307d9 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -178,8 +178,11 @@ public: // Pointers set during parsing const char *m_param_begin; const char *m_param_end; + +private: const char *m_body_begin; +public: /* Security context for stored routine which should be run under definer privileges. @@ -297,6 +300,8 @@ public: void set_info(longlong created, longlong modified, st_sp_chistics *chistics, ulong sql_mode); + void set_body_begin_ptr(Lex_input_stream *lip, const char *begin_ptr); + void set_definer(const char *definer, uint definerlen); void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 8a4c99b6d00..f3b8e7fe640 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1872,7 +1872,7 @@ ev_sql_stmt: bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lip->get_cpp_ptr(); + lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_ptr()); } ev_sql_stmt_inner { @@ -2066,7 +2066,7 @@ create_function_tail: Lex_input_stream *lip= thd->m_lip; lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lip->get_cpp_tok_start(); + lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_tok_start()); } sp_proc_stmt { @@ -11489,7 +11489,7 @@ trigger_tail: bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lip->get_cpp_ptr(); + lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_ptr()); } sp_proc_stmt /* $16 */ { /* $17 */ @@ -11592,7 +11592,7 @@ sp_tail: Lex_input_stream *lip= thd->m_lip; lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lip->get_cpp_tok_start(); + lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_tok_start()); } sp_proc_stmt { From b27172eb705cfcbaae3f7d909541bf769c93a302 Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Thu, 14 Jun 2007 17:43:19 -0600 Subject: [PATCH 16/26] build warning --- sql/log.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/log.cc b/sql/log.cc index 02de7084222..e5cebee3469 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -441,7 +441,7 @@ bool Log_to_csv_event_handler:: CHARSET_INFO *client_cs) { TABLE *table= general_log.table; - int field_index; + uint field_index; /* "INSERT INTO general_log" can generate warning sometimes. From 9e6685dc74180ebbe7344a5403c498f1f9a22153 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Fri, 15 Jun 2007 12:40:30 +0400 Subject: [PATCH 17/26] Another attempt to fix the valgrind warning in SHOW PROCESSLIST (event_bugs.test) --- sql/event_data_objects.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 757cf7f93fb..77dc33e6265 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -1900,6 +1900,9 @@ end: thd->lex->unit.cleanup(); thd->end_statement(); thd->cleanup_after_query(); + /* Avoid races with SHOW PROCESSLIST */ + thd->query_length= 0; + thd->query= NULL; DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret)); From e20232cf633a8232a3de2c56e8eb53b2c446f55a Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Fri, 15 Jun 2007 10:59:40 -0600 Subject: [PATCH 18/26] manual merge --- tests/mysql_client_test.c | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 0985b4d25ec..9bf062f7df8 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15680,6 +15680,59 @@ static void test_bug28934() } +/* + Bug#27592 (stack overrun when storing datetime value using prepared statements) +*/ + +static void test_bug27592() +{ + const int NUM_ITERATIONS= 40; + int i; + int rc; + MYSQL_STMT *stmt= NULL; + MYSQL_BIND bind[1]; + MYSQL_TIME time_val; + + DBUG_ENTER("test_bug27592"); + myheader("test_bug27592"); + + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + mysql_query(mysql, "CREATE TABLE t1(c2 DATETIME)"); + + stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 VALUES (?)"); + DIE_UNLESS(stmt); + + memset(bind, 0, sizeof(bind)); + + bind[0].buffer_type= MYSQL_TYPE_DATETIME; + bind[0].buffer= (char *) &time_val; + bind[0].length= NULL; + + for (i= 0; i < NUM_ITERATIONS; i++) + { + time_val.year= 2007; + time_val.month= 6; + time_val.day= 7; + time_val.hour= 18; + time_val.minute= 41; + time_val.second= 3; + + time_val.second_part=0; + time_val.neg=0; + + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt, rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + } + + mysql_stmt_close(stmt); + + DBUG_VOID_RETURN; +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -15963,6 +16016,7 @@ static struct my_tests_st my_tests[]= { { "test_bug27876", test_bug27876 }, { "test_bug28505", test_bug28505 }, { "test_bug28934", test_bug28934 }, + { "test_bug27592", test_bug27592 }, { 0, 0 } }; From 4b427bde8226427066c7f2213a53815f3ba0d39c Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Fri, 15 Jun 2007 11:15:22 -0600 Subject: [PATCH 19/26] Fixed warnings raised from mysqltest (unknown -- commands) --- mysql-test/t/greedy_optimizer.test | 4 ++-- mysql-test/t/join.test | 6 ++++-- mysql-test/t/ndb_index_ordered.test | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/mysql-test/t/greedy_optimizer.test b/mysql-test/t/greedy_optimizer.test index 049d0ab09f7..b73f70c6a3e 100644 --- a/mysql-test/t/greedy_optimizer.test +++ b/mysql-test/t/greedy_optimizer.test @@ -145,11 +145,11 @@ select @@optimizer_prune_level; # # These are the values for the parameters that control the greedy optimizer # (total 6 combinations - 3 for optimizer_search_depth, 2 for optimizer_prune_level): --- +# 3: # set optimizer_search_depth=0; - automatic # set optimizer_search_depth=1; - min # set optimizer_search_depth=62; - max (default) --- +# 2: # set optimizer_prune_level=0 - exhaustive; # set optimizer_prune_level=1 - heuristic; # default diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test index 68b97854c3b..5b599c3dad7 100644 --- a/mysql-test/t/join.test +++ b/mysql-test/t/join.test @@ -512,10 +512,12 @@ select * from v1a join (t3 natural join t4) on a = y; #-------------------------------------------------------------------- # Negative tests (tests for errors) #-------------------------------------------------------------------- +# works in Oracle - bug -- error 1052 -select * from t1 natural join (t3 cross join t4); -- works in Oracle - bug +select * from t1 natural join (t3 cross join t4); +# works in Oracle - bug -- error 1052 -select * from (t3 cross join t4) natural join t1; -- works in Oracle - bug +select * from (t3 cross join t4) natural join t1; -- error 1052 select * from t1 join (t2, t3) using (b); -- error 1052 diff --git a/mysql-test/t/ndb_index_ordered.test b/mysql-test/t/ndb_index_ordered.test index 19b024a043f..31385fd56b2 100644 --- a/mysql-test/t/ndb_index_ordered.test +++ b/mysql-test/t/ndb_index_ordered.test @@ -50,7 +50,7 @@ update t1 set c = 13 where b <= 3; select * from t1 order by a; update t1 set b = b + 1 where b > 4 and b < 7; select * from t1 order by a; --- Update primary key +# Update primary key update t1 set a = a + 10 where b > 1 and b < 7; select * from t1 order by a; From 381deba3878b3e82dbd77d9f908a670f940e47bf Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Fri, 15 Jun 2007 15:38:25 -0600 Subject: [PATCH 20/26] Manual merge --- tests/mysql_client_test.c | 58 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 3f0cb1bf4fe..0fff1bb71b5 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -16217,6 +16217,59 @@ static void test_bug28934() } +/* + Bug#27592 (stack overrun when storing datetime value using prepared statements) +*/ + +static void test_bug27592() +{ + const int NUM_ITERATIONS= 40; + int i; + int rc; + MYSQL_STMT *stmt= NULL; + MYSQL_BIND bind[1]; + MYSQL_TIME time_val; + + DBUG_ENTER("test_bug27592"); + myheader("test_bug27592"); + + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + mysql_query(mysql, "CREATE TABLE t1(c2 DATETIME)"); + + stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 VALUES (?)"); + DIE_UNLESS(stmt); + + memset(bind, 0, sizeof(bind)); + + bind[0].buffer_type= MYSQL_TYPE_DATETIME; + bind[0].buffer= (char *) &time_val; + bind[0].length= NULL; + + for (i= 0; i < NUM_ITERATIONS; i++) + { + time_val.year= 2007; + time_val.month= 6; + time_val.day= 7; + time_val.hour= 18; + time_val.minute= 41; + time_val.second= 3; + + time_val.second_part=0; + time_val.neg=0; + + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt, rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + } + + mysql_stmt_close(stmt); + + DBUG_VOID_RETURN; +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -16498,15 +16551,16 @@ static struct my_tests_st my_tests[]= { { "test_bug15518", test_bug15518 }, { "test_bug23383", test_bug23383 }, { "test_bug21635", test_bug21635 }, - { "test_bug28505", test_bug28505 }, { "test_status", test_status }, { "test_bug24179", test_bug24179 }, { "test_ps_query_cache", test_ps_query_cache }, #ifdef fix_bug_in_pb_first { "test_bug28075", test_bug28075 }, #endif - { "test_bug28934", test_bug28934 }, { "test_bug27876", test_bug27876 }, + { "test_bug28505", test_bug28505 }, + { "test_bug28934", test_bug28934 }, + { "test_bug27592", test_bug27592 }, { 0, 0 } }; From 2da91b2316650578b6a990f04a1b6141383d4cf6 Mon Sep 17 00:00:00 2001 From: "thek@adventure.(none)" <> Date: Mon, 18 Jun 2007 17:46:29 +0200 Subject: [PATCH 21/26] Bug#28211 RENAME DATABASE and query cache don't play nicely together When all table blocks were removed from the query cache the client session hung in a tight loop waiting on an impossible condition while consuming a lot of CPU. This patch also corrects an error which caused valid tables to sometimes be removed from the query cache. --- mysql-test/r/query_cache.result | 48 ++++++++++++++++++++ mysql-test/t/query_cache.test | 45 +++++++++++++++++++ sql/sql_cache.cc | 79 ++++++++++++++++++++++----------- 3 files changed, 145 insertions(+), 27 deletions(-) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index 64d32ba334c..287ccd3e048 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -1437,3 +1437,51 @@ set GLOBAL query_cache_type=default; set GLOBAL query_cache_limit=default; set GLOBAL query_cache_min_res_unit=default; set GLOBAL query_cache_size= default; +drop database if exists db1; +drop database if exists db2; +set GLOBAL query_cache_size=15*1024*1024; +create database db1; +use db1; +create table t1(c1 int)engine=myisam; +insert into t1(c1) values (1); +select * from db1.t1 f; +c1 +1 +show status like 'Qcache_queries_in_cache'; +Variable_name Value +Qcache_queries_in_cache 1 +rename schema db1 to db2; +show status like 'Qcache_queries_in_cache'; +Variable_name Value +Qcache_queries_in_cache 0 +drop database db2; +set global query_cache_size=default; +drop database if exists db1; +drop database if exists db3; +set GLOBAL query_cache_size=15*1024*1024; +create database db1; +create database db3; +use db1; +create table t1(c1 int) engine=myisam; +use db3; +create table t1(c1 int) engine=myisam; +use db1; +insert into t1(c1) values (1); +use mysql; +select * from db1.t1; +c1 +1 +select c1+1 from db1.t1; +c1+1 +2 +select * from db3.t1; +c1 +show status like 'Qcache_queries_in_cache'; +Variable_name Value +Qcache_queries_in_cache 3 +rename schema db1 to db2; +show status like 'Qcache_queries_in_cache'; +Variable_name Value +Qcache_queries_in_cache 1 +drop database db2; +drop database db3; diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index b878c141546..b00fb0fe035 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -1000,3 +1000,48 @@ set GLOBAL query_cache_min_res_unit=default; set GLOBAL query_cache_size= default; # End of 5.0 tests + + +# +# Bug #28211 RENAME DATABASE and query cache don't play nicely together +# +--disable_warnings +drop database if exists db1; +drop database if exists db2; +--enable_warnings +set GLOBAL query_cache_size=15*1024*1024; +create database db1; +use db1; +create table t1(c1 int)engine=myisam; +insert into t1(c1) values (1); +select * from db1.t1 f; +show status like 'Qcache_queries_in_cache'; +rename schema db1 to db2; +show status like 'Qcache_queries_in_cache'; +drop database db2; +set global query_cache_size=default; + +--disable_warnings +drop database if exists db1; +drop database if exists db3; +--enable_wearnings +set GLOBAL query_cache_size=15*1024*1024; +create database db1; +create database db3; +use db1; +create table t1(c1 int) engine=myisam; +use db3; +create table t1(c1 int) engine=myisam; +use db1; +insert into t1(c1) values (1); +use mysql; +select * from db1.t1; +select c1+1 from db1.t1; +select * from db3.t1; +show status like 'Qcache_queries_in_cache'; +rename schema db1 to db2; +show status like 'Qcache_queries_in_cache'; +drop database db2; +drop database db3; + +# End of 5.1 tests diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index bcc4f6a71d3..068dc6e0cd9 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1445,40 +1445,62 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, DBUG_VOID_RETURN; } -/* - Remove all cached queries that uses the given database +/** + @brief Remove all cached queries that uses the given database */ - void Query_cache::invalidate(char *db) { + bool restart= FALSE; DBUG_ENTER("Query_cache::invalidate (db)"); STRUCT_LOCK(&structure_guard_mutex); if (query_cache_size > 0 && !flush_in_progress) { - DUMP(this); - restart_search: if (tables_blocks) { - Query_cache_block *curr= tables_blocks; - Query_cache_block *next; - do - { - next= curr->next; - if (strcmp(db, (char*)(curr->table()->db())) == 0) - invalidate_table(curr); + Query_cache_block *table_block = tables_blocks; + do { + restart= FALSE; + do + { + Query_cache_block *next= table_block->next; + Query_cache_table *table = table_block->table(); + if (strcmp(table->db(),db) == 0) + invalidate_table(table_block); + + table_block= next; + + /* + If our root node to used tables became null then the last element + in the table list was removed when a query was invalidated; + Terminate the search. + */ + if (tables_blocks == 0) + { + table_block= tables_blocks; + } + /* + If the iterated list has changed underlying structure; + we need to restart the search. + */ + else if (table_block->type == Query_cache_block::FREE) + { + restart= TRUE; + table_block= tables_blocks; + } + /* + The used tables are linked in a circular list; + loop until we return to the begining. + */ + } while (table_block != tables_blocks); /* - invalidate_table can freed block on which point 'next' (if - table of this block used only in queries which was deleted - by invalidate_table). As far as we do not allocate new blocks - and mark all headers of freed blocks as 'FREE' (even if they are - merged with other blocks) we can just test type of block - to be sure that block is not deleted + Invalidating a table will also mean that all cached queries using + this table also will be invalidated. This will in turn change the + list of tables associated with these queries and the linked list of + used table will be changed. Because of this we might need to restart + the search when a table has been invalidated. */ - if (next->type == Query_cache_block::FREE) - goto restart_search; - curr= next; - } while (curr != tables_blocks); - } + } while (restart); + } // end if( tables_blocks ) } STRUCT_UNLOCK(&structure_guard_mutex); @@ -2412,6 +2434,7 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used, (ulong) tables_used->table, tables_used->table->s->table_cache_key.length, (ulong) tables_used->table->s->table_cache_key.str)); + if (!insert_table(tables_used->table->s->table_cache_key.length, tables_used->table->s->table_cache_key.str, block_table, @@ -2478,9 +2501,8 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, n= register_tables_from_list(tables_used, 0, block_table); - if (n) + if (n==0) { - DBUG_PRINT("qcache", ("failed at table %d", (int) n)); /* Unlink the tables we allocated above */ for (Query_cache_block_table *tmp = block->table(0) ; tmp != block_table; @@ -2970,8 +2992,11 @@ Query_cache::double_linked_list_exclude(Query_cache_block *point, { point->next->prev = point->prev; point->prev->next = point->next; + /* + If the root is removed; select a new root + */ if (point == *list_pointer) - *list_pointer = point->next; + *list_pointer= point->next; } DBUG_VOID_RETURN; } @@ -3769,7 +3794,7 @@ void Query_cache::tables_dump() Query_cache_table *table = table_block->table(); DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table())); table_block = table_block->next; - } while ( table_block != tables_blocks); + } while (table_block != tables_blocks); } else DBUG_PRINT("qcache", ("no tables in list")); From f927d50509ef0e25ece6449d3e263cf264ac5e6f Mon Sep 17 00:00:00 2001 From: "df@pippilotta.erinye.com" <> Date: Tue, 19 Jun 2007 12:03:10 +0200 Subject: [PATCH 22/26] try again. --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index e7e9aa58c9b..5f02b5eae63 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.44) +AM_INIT_AUTOMAKE(mysql, 5.0.46) AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 @@ -23,7 +23,7 @@ NDB_SHARED_LIB_VERSION=$NDB_SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=44 +NDB_VERSION_BUILD=46 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? From 4fd8158456f855274f88692f721ae36dbdfce9ef Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Tue, 19 Jun 2007 18:04:42 +0400 Subject: [PATCH 23/26] Fix a merge mistake. --- mysql-test/r/query_cache.result | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index cf53ada9c20..de257e37288 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -1438,6 +1438,12 @@ set GLOBAL query_cache_type=default; set GLOBAL query_cache_limit=default; set GLOBAL query_cache_min_res_unit=default; set GLOBAL query_cache_size= default; +set GLOBAL query_cache_size=1000000; +create table t1 (a char); +insert into t1 values ('c'); +a +drop table t1; +set GLOBAL query_cache_size= default; drop database if exists db1; drop database if exists db2; set GLOBAL query_cache_size=15*1024*1024; @@ -1486,9 +1492,3 @@ Variable_name Value Qcache_queries_in_cache 1 drop database db2; drop database db3; -set GLOBAL query_cache_size=1000000; -create table t1 (a char); -insert into t1 values ('c'); -a -drop table t1; -set GLOBAL query_cache_size= default; From 9ff8e6d7b58ae570340b8197721a45d112f1c82e Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Wed, 20 Jun 2007 14:04:53 +0400 Subject: [PATCH 24/26] Fix a typo. --- mysql-test/t/query_cache.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index 42fa681caea..821cdf2df38 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -1048,7 +1048,7 @@ set global query_cache_size=default; --disable_warnings drop database if exists db1; drop database if exists db3; ---enable_wearnings +--enable_warnings set GLOBAL query_cache_size=15*1024*1024; create database db1; create database db3; From a1fefd4e8de0afc3a6fac6e0e5b82746f4a080ac Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Wed, 20 Jun 2007 16:05:57 +0400 Subject: [PATCH 25/26] Disable randomly failing tests: rpl_udf -- Bug#28993 rpl_udf test causes server crash and valgrind warning in pushbuild rpl_ndb_circular -- Bug#29233 rpl_ndb_circular fails randomly ndb_dd_sql_features -- Bug#29102 ndb_dd_sql_features fails in pushbuild --- mysql-test/t/disabled.def | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 27e3b9bc614..29336e1050d 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -48,3 +48,6 @@ ndb_partition_error2 : HF is not sure if the test can work as internded on all im_options_set : Bug#20294: Instance manager tests fail randomly im_options_unset : Bug#20294: Instance manager tests fail randomly mysql_upgrade : Bug#28560 test links to /usr/local/mysql/lib libraries, causes non-determinism and failures on ABI breakage +rpl_udf : Bug#28993 rpl_udf test causes server crash and valgrind warning in pushbuild +rpl_ndb_circular : Bug#29233 rpl_ndb_circular fails randomly +ndb_dd_sql_features : Bug#29102 ndb_dd_sql_features fails in pushbuild From 7758a5de33727ce71f29903e62d3826570b3b842 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Wed, 20 Jun 2007 19:14:59 +0400 Subject: [PATCH 26/26] Enable the disabled test case for Bug#28075 (pushbuild by now should be fixed). --- tests/mysql_client_test.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index e9943e8dca8..eefaa6ce071 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -16079,7 +16079,6 @@ static void test_bug24179() Bug#28075 "COM_DEBUG crashes mysqld" Note: Test disabled because of failure in PushBuild. */ -#ifdef FIX_BUG_IN_PB_FIRST static void test_bug28075() { @@ -16096,7 +16095,6 @@ static void test_bug28075() DBUG_VOID_RETURN; } -#endif /* @@ -16557,9 +16555,7 @@ static struct my_tests_st my_tests[]= { { "test_status", test_status }, { "test_bug24179", test_bug24179 }, { "test_ps_query_cache", test_ps_query_cache }, -#ifdef FIX_BUG_IN_PB_FIRST { "test_bug28075", test_bug28075 }, -#endif { "test_bug27876", test_bug27876 }, { "test_bug28505", test_bug28505 }, { "test_bug28934", test_bug28934 },