diff options
Diffstat (limited to 'sql')
77 files changed, 2486 insertions, 1039 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 194b6ab29ef..3a6f4bcb7a2 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -19,6 +19,7 @@ MYSQLDATAdir = $(localstatedir) MYSQLSHAREdir = $(pkgdatadir) MYSQLBASEdir= $(prefix) MYSQLLIBdir= $(pkglibdir) +pkgplugindir = $(pkglibdir)/plugin INCLUDES = @ZLIB_INCLUDES@ \ -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_srcdir)/regex -I$(srcdir) $(openssl_includes) @@ -138,7 +139,7 @@ DEFS = -DMYSQL_SERVER \ -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \ - -DLIBDIR="\"$(MYSQLLIBdir)\"" \ + -DPLUGINDIR="\"$(pkgplugindir)\"" \ @DEFS@ BUILT_MAINT_SRC = sql_yacc.cc sql_yacc.h diff --git a/sql/field.cc b/sql/field.cc index 7c4f6c9ff5f..32bf5855d8b 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2261,13 +2261,11 @@ int Field_decimal::store(double nr) return 1; } -#ifdef HAVE_FINITE - if (!finite(nr)) // Handle infinity as special case + if (!isfinite(nr)) // Handle infinity as special case { overflow(nr < 0.0); return 1; } -#endif reg4 uint i; size_t length; @@ -6323,26 +6321,41 @@ check_string_copy_error(Field_str *field, } - /* - Send a truncation warning or a truncation error - after storing a too long character string info a field. + Check if we lost any important data and send a truncation error/warning SYNOPSIS - report_data_too_long() - field - Field + Field_longstr::report_if_important_data() + ptr - Truncated rest of string + end - End of truncated string - RETURN - N/A + RETURN VALUES + 0 - None was truncated (or we don't count cut fields) + 2 - Some bytes was truncated + + NOTE + Check if we lost any important data (anything in a binary string, + or any non-space in others). If only trailing spaces was lost, + send a truncation note, otherwise send a truncation error. */ -inline void -report_data_too_long(Field_str *field) +int +Field_longstr::report_if_important_data(const char *ptr, const char *end) { - if (field->table->in_use->abort_on_warning) - field->set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1); - else - field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + if ((ptr < end) && table->in_use->count_cuted_fields) + { + if (test_if_important_data(field_charset, ptr, end)) + { + if (table->in_use->abort_on_warning) + set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1); + else + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + } + else /* If we lost only spaces then produce a NOTE, not a WARNING */ + set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); + return 2; + } + return 0; } @@ -6377,19 +6390,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) cannot_convert_error_pos, from + length, cs)) return 2; - /* - Check if we lost any important data (anything in a binary string, - or any non-space in others). - */ - if ((from_end_pos < from + length) && table->in_use->count_cuted_fields) - { - if (test_if_important_data(field_charset, from_end_pos, from + length)) - { - report_data_too_long(this); - return 2; - } - } - return 0; + return report_if_important_data(from_end_pos, from + length); } @@ -6964,16 +6965,7 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) cannot_convert_error_pos, from + length, cs)) return 2; - // Check if we lost something other than just trailing spaces - if ((from_end_pos < from + length) && table->in_use->count_cuted_fields) - { - if (test_if_important_data(field_charset, from_end_pos, from + length)) - report_data_too_long(this); - else /* If we lost only spaces then produce a NOTE, not a WARNING */ - set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); - return 2; - } - return 0; + return report_if_important_data(from_end_pos, from + length); } @@ -7644,6 +7636,17 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) if (value.alloc(new_length)) goto oom_error; + + if (f_is_hex_escape(flags)) + { + copy_length= my_copy_with_hex_escaping(field_charset, + (char*) value.ptr(), new_length, + from, length); + Field_blob::store_length(copy_length); + tmp= value.ptr(); + bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*)); + return 0; + } /* "length" is OK as "nchars" argument to well_formed_copy_nchars as this is never used to limit the length of the data. The cut of long data @@ -7665,13 +7668,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) cannot_convert_error_pos, from + length, cs)) return 2; - if (from_end_pos < from + length) - { - report_data_too_long(this); - return 2; - } - - return 0; + return report_if_important_data(from_end_pos, from + length); oom_error: /* Fatal OOM error */ @@ -8619,10 +8616,10 @@ int Field_set::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; - if ((ulonglong) nr > (ulonglong) (((longlong) 1 << typelib->count) - - (longlong) 1)) + ulonglong max_nr= set_bits(ulonglong, typelib->count); + if ((ulonglong) nr > max_nr) { - nr&= (longlong) (((longlong) 1 << typelib->count) - (longlong) 1); + nr&= max_nr; set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); error=1; } @@ -8793,6 +8790,23 @@ Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, } +void Field_bit::hash(ulong *nr, ulong *nr2) +{ + if (is_null()) + { + *nr^= (*nr << 1) | 1; + } + else + { + CHARSET_INFO *cs= &my_charset_bin; + longlong value= Field_bit::val_int(); + uchar tmp[8]; + mi_int8store(tmp,value); + cs->coll->hash_sort(cs, tmp, 8, nr, nr2); + } +} + + size_t Field_bit::do_last_null_byte() const { diff --git a/sql/field.h b/sql/field.h index a6a1d8bfabc..5457b7284e9 100644 --- a/sql/field.h +++ b/sql/field.h @@ -593,6 +593,8 @@ public: class Field_longstr :public Field_str { +protected: + int report_if_important_data(const char *ptr, const char *end); public: Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -1904,6 +1906,7 @@ public: Field::move_field_offset(ptr_diff); bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*); } + void hash(ulong *nr, ulong *nr2); private: virtual size_t do_last_null_byte() const; @@ -2068,6 +2071,7 @@ int set_field_to_null_with_conversions(Field *field, bool no_conversions); #define FIELDFLAG_NO_DEFAULT 16384 /* sql */ #define FIELDFLAG_SUM ((uint) 32768)// predit: +#fieldflag #define FIELDFLAG_MAYBE_NULL ((uint) 32768)// sql +#define FIELDFLAG_HEX_ESCAPE ((uint) 0x10000) #define FIELDFLAG_PACK_SHIFT 3 #define FIELDFLAG_DEC_SHIFT 8 #define FIELDFLAG_MAX_DEC 31 @@ -2093,3 +2097,4 @@ int set_field_to_null_with_conversions(Field *field, bool no_conversions); #define f_maybe_null(x) (x & FIELDFLAG_MAYBE_NULL) #define f_no_default(x) (x & FIELDFLAG_NO_DEFAULT) #define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR) +#define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE) diff --git a/sql/filesort.cc b/sql/filesort.cc index ac731616218..90e0fe386d5 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -230,8 +230,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, sort_keys= table_sort.sort_keys; if (memavl < min_sort_memory) { - my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG), - thd->variables.sortbuff_size); + my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR+ME_WAITTANG)); goto err; } if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX, diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc index c9c1813a429..7b2395673eb 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -486,8 +486,8 @@ int main(int argc,char **argv) printf("\nstatic unsigned int symbols_max_len=%d;\n\n", max_len2); printf("\ -static inline SYMBOL *get_hash_symbol(const char *s,\n\ - unsigned int len,bool function)\n\ +static SYMBOL *get_hash_symbol(const char *s,\n\ + unsigned int len,bool function)\n\ {\n\ register uchar *hash_map;\n\ register const char *cur_str= s;\n\ diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc index d8dff3c7e2f..bb35211944b 100644 --- a/sql/ha_ndbcluster_cond.cc +++ b/sql/ha_ndbcluster_cond.cc @@ -117,7 +117,8 @@ void ndb_serialize_cond(const Item *item, void *arg) if (item->type() == Item::FUNC_ITEM) { Item_func *func_item= (Item_func *) item; - if (func_item->functype() == Item_func::UNKNOWN_FUNC && + if ((func_item->functype() == Item_func::UNKNOWN_FUNC || + func_item->functype() == Item_func::NEG_FUNC) && func_item->const_item()) { // Skip any arguments since we will evaluate function instead @@ -369,8 +370,9 @@ void ndb_serialize_cond(const Item *item, void *arg) { Item_func *func_item= (Item_func *) item; // Check that we expect a function or functional expression here - if (context->expecting(Item::FUNC_ITEM) || - func_item->functype() == Item_func::UNKNOWN_FUNC) + if (context->expecting(Item::FUNC_ITEM) || + func_item->functype() == Item_func::UNKNOWN_FUNC || + func_item->functype() == Item_func::NEG_FUNC) context->expect_nothing(); else { @@ -584,6 +586,7 @@ void ndb_serialize_cond(const Item *item, void *arg) context->expect(Item::FUNC_ITEM); break; } + case Item_func::NEG_FUNC: case Item_func::UNKNOWN_FUNC: { DBUG_PRINT("info", ("UNKNOWN_FUNC %s", diff --git a/sql/ha_ndbcluster_cond.h b/sql/ha_ndbcluster_cond.h index 4d1d2ef9f92..4401a93c9e1 100644 --- a/sql/ha_ndbcluster_cond.h +++ b/sql/ha_ndbcluster_cond.h @@ -233,6 +233,7 @@ public: case (Item_func::ISNOTNULL_FUNC): { return NDB_ISNOTNULL_FUNC; } case (Item_func::LIKE_FUNC): { return NDB_LIKE_FUNC; } case (Item_func::NOT_FUNC): { return NDB_NOT_FUNC; } + case (Item_func::NEG_FUNC): { return NDB_UNKNOWN_FUNC; } case (Item_func::UNKNOWN_FUNC): { return NDB_UNKNOWN_FUNC; } case (Item_func::COND_AND_FUNC): { return NDB_COND_AND_FUNC; } case (Item_func::COND_OR_FUNC): { return NDB_COND_OR_FUNC; } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 51070a525c5..2e0bb090ad2 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1028,6 +1028,7 @@ int ha_partition::repair_partitions(THD *thd) 0 Success */ +#ifdef WL4176_IS_DONE static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, handler *file, uint flag) { @@ -1035,6 +1036,12 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, DBUG_ENTER("handle_opt_part"); DBUG_PRINT("enter", ("flag = %u", flag)); + /* + TODO: + Rewrite the code for ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION WL4176 + */ + DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED); + if (flag == OPTIMIZE_PARTS) error= file->ha_optimize(thd, check_opt); else if (flag == ANALYZE_PARTS) @@ -1052,6 +1059,7 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, error= 0; DBUG_RETURN(error); } +#endif /* @@ -1072,14 +1080,22 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flag, bool all_parts) { +#ifdef WL4176_IS_DONE List_iterator<partition_element> part_it(m_part_info->partitions); uint no_parts= m_part_info->no_parts; uint no_subparts= m_part_info->no_subparts; uint i= 0; int error; +#endif DBUG_ENTER("ha_partition::handle_opt_partitions"); DBUG_PRINT("enter", ("all_parts %u, flag= %u", all_parts, flag)); + /* + TODO: + Rewrite the code for ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION WL4176 + */ + DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED); +#ifdef WL4176_IS_DONE do { partition_element *part_elem= part_it++; @@ -1110,6 +1126,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, } } while (++i < no_parts); DBUG_RETURN(FALSE); +#endif } /* @@ -1629,6 +1646,15 @@ void ha_partition::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share) { (*file_array)->change_table_ptr(table_arg, share); } while (*(++file_array)); + if (m_added_file && m_added_file[0]) + { + /* if in middle of a drop/rename etc */ + file_array= m_added_file; + do + { + (*file_array)->change_table_ptr(table_arg, share); + } while (*(++file_array)); + } } /* @@ -4779,11 +4805,14 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, about this call). We pass this along to all underlying MyISAM handlers and ignore it for the rest. - HA_EXTRA_PREPARE_FOR_DELETE: + HA_EXTRA_PREPARE_FOR_DROP: Only used by MyISAM, called in preparation for a DROP TABLE. It's used mostly by Windows that cannot handle dropping an open file. On other platforms it has the same effect as HA_EXTRA_FORCE_REOPEN. + HA_EXTRA_PREPARE_FOR_RENAME: + Informs the handler we are about to attempt a rename of the table. + HA_EXTRA_READCHECK: HA_EXTRA_NO_READCHECK: Only one call to HA_EXTRA_NO_READCHECK from ha_open where it says that @@ -4918,14 +4947,15 @@ int ha_partition::extra(enum ha_extra_function operation) } /* Category 3), used by MyISAM handlers */ - case HA_EXTRA_PREPARE_FOR_DELETE: - DBUG_RETURN(prepare_for_delete()); + case HA_EXTRA_PREPARE_FOR_RENAME: + DBUG_RETURN(prepare_for_rename()); break; case HA_EXTRA_NORMAL: case HA_EXTRA_QUICK: case HA_EXTRA_NO_READCHECK: case HA_EXTRA_PREPARE_FOR_UPDATE: case HA_EXTRA_FORCE_REOPEN: + case HA_EXTRA_PREPARE_FOR_DROP: case HA_EXTRA_FLUSH_CACHE: { if (m_myisam) @@ -5084,24 +5114,24 @@ void ha_partition::prepare_extra_cache(uint cachesize) 0 Success */ -int ha_partition::prepare_for_delete() +int ha_partition::prepare_for_rename() { int result= 0, tmp; handler **file; - DBUG_ENTER("ha_partition::prepare_for_delete()"); + DBUG_ENTER("ha_partition::prepare_for_rename()"); if (m_new_file != NULL) { for (file= m_new_file; *file; file++) - if ((tmp= (*file)->extra(HA_EXTRA_PREPARE_FOR_DELETE))) + if ((tmp= (*file)->extra(HA_EXTRA_PREPARE_FOR_RENAME))) result= tmp; for (file= m_reorged_file; *file; file++) - if ((tmp= (*file)->extra(HA_EXTRA_PREPARE_FOR_DELETE))) + if ((tmp= (*file)->extra(HA_EXTRA_PREPARE_FOR_RENAME))) result= tmp; DBUG_RETURN(result); } - DBUG_RETURN(loop_extra(HA_EXTRA_PREPARE_FOR_DELETE)); + DBUG_RETURN(loop_extra(HA_EXTRA_PREPARE_FOR_RENAME)); } /* diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 9d10aea2b6f..ac00581fae0 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -211,7 +211,7 @@ public: } virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share); private: - int prepare_for_delete(); + int prepare_for_rename(); int copy_partitions(ulonglong *copied, ulonglong *deleted); void cleanup_new_partition(uint part_count); int prepare_new_partition(TABLE *table, HA_CREATE_INFO *create_info, diff --git a/sql/handler.cc b/sql/handler.cc index 4e6b09c069e..b334e003851 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -288,7 +288,8 @@ handler *get_ha_partition(partition_info *part_info) @retval !=0 Error */ -static int ha_init_errors(void) + +int ha_init_errors(void) { #define SETMSG(nr, msg) errmsgs[(nr) - HA_ERR_FIRST]= (msg) const char **errmsgs; @@ -501,9 +502,6 @@ int ha_init() int error= 0; DBUG_ENTER("ha_init"); - if (ha_init_errors()) - DBUG_RETURN(1); - DBUG_ASSERT(total_ha < MAX_HA); /* Check if there is a transaction-capable storage engine besides the @@ -1924,6 +1922,13 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, handler *handler::clone(MEM_ROOT *mem_root) { handler *new_handler= get_new_handler(table->s, mem_root, table->s->db_type()); + /* + Allocate handler->ref here because otherwise ha_open will allocate it + on this->table->mem_root and we will not be able to reclaim that memory + when the clone handler object is destroyed. + */ + if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2))) + return NULL; if (new_handler && !new_handler->ha_open(table, table->s->normalized_path.str, table->db_stat, @@ -1992,7 +1997,9 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, table->db_stat|=HA_READ_ONLY; (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL - if (!(ref= (uchar*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2))) + /* ref is already allocated for us if we're called from handler::clone() */ + if (!ref && !(ref= (uchar*) alloc_root(&table->mem_root, + ALIGN_SIZE(ref_length)*2))) { close(); error=HA_ERR_OUT_OF_MEM; @@ -3338,10 +3345,10 @@ handler::ha_repair_partitions(THD *thd) int ha_enable_transaction(THD *thd, bool on) { int error=0; - DBUG_ENTER("ha_enable_transaction"); - thd->transaction.on= on; - if (on) + DBUG_PRINT("enter", ("on: %d", (int) on)); + + if ((thd->transaction.on= on)) { /* Now all storage engines should have transaction handling enabled. diff --git a/sql/handler.h b/sql/handler.h index 9800f4974c3..d674c07a57d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -20,6 +20,7 @@ #pragma interface /* gcc class implementation */ #endif +#include <my_handler.h> #include <ft_global.h> #include <keycache.h> @@ -272,6 +273,7 @@ enum legacy_db_type DB_TYPE_TABLE_FUNCTION, DB_TYPE_MEMCACHE, DB_TYPE_FALCON, + DB_TYPE_MARIA, DB_TYPE_FIRST_DYNAMIC=42, DB_TYPE_DEFAULT=127 // Must be last }; @@ -322,6 +324,7 @@ enum enum_binlog_command { #define HA_CREATE_USED_CONNECTION (1L << 18) #define HA_CREATE_USED_KEY_BLOCK_SIZE (1L << 19) #define HA_CREATE_USED_TRANSACTIONAL (1L << 20) +#define HA_CREATE_USED_PAGE_CHECKSUM (1L << 21) typedef ulonglong my_xid; // this line is the same as in log_event.h #define MYSQL_XID_PREFIX "MySQLXid" @@ -917,6 +920,7 @@ typedef struct st_ha_create_information bool frm_only; /* 1 if no ha_create_table() */ bool varchar; /* 1 if table has a VARCHAR */ enum ha_storage_media storage_media; /* DEFAULT, DISK or MEMORY */ + enum ha_choice page_checksum; /* If we have page_checksums */ } HA_CREATE_INFO; @@ -1883,7 +1887,7 @@ private: { return HA_ADMIN_NOT_IMPLEMENTED; } virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt) { return HA_ADMIN_NOT_IMPLEMENTED; } - virtual bool check_and_repair(THD *thd) { return HA_ERR_WRONG_COMMAND; } + virtual bool check_and_repair(THD *thd) { return TRUE; } virtual int disable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; } virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; } virtual int discard_or_import_tablespace(my_bool discard) @@ -1964,6 +1968,7 @@ static inline bool ha_storage_engine_is_enabled(const handlerton *db_type) } /* basic stuff */ +int ha_init_errors(void); int ha_init(void); int ha_end(void); int ha_initialize_handlerton(st_plugin_int *plugin); diff --git a/sql/item.cc b/sql/item.cc index d2b071a1dfd..fb8b0c1de2b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -18,6 +18,7 @@ #pragma implementation // gcc: Class implementation #endif #include "mysql_priv.h" +#include <mysql.h> #include <m_ctype.h> #include "my_dir.h" #include "sp_rcontext.h" @@ -1237,6 +1238,22 @@ bool Item_name_const::is_null() return value_item->is_null(); } + +Item_name_const::Item_name_const(Item *name_arg, Item *val): + value_item(val), name_item(name_arg) +{ + if (!(valid_args= name_item->basic_const_item() && + (value_item->basic_const_item() || + ((value_item->type() == FUNC_ITEM) && + (((Item_func *) value_item)->functype() == + Item_func::NEG_FUNC) && + (((Item_func *) value_item)->key_item()->type() != + FUNC_ITEM))))) + my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST"); + Item::maybe_null= TRUE; +} + + Item::Type Item_name_const::type() const { /* @@ -1248,8 +1265,17 @@ Item::Type Item_name_const::type() const if (item->type() == FIELD_ITEM) ((Item_field *) item)->... we return NULL_ITEM in the case to avoid wrong casting. + + valid_args guarantees value_item->basic_const_item(); if type is + FUNC_ITEM, then we have a fudged item_func_neg() on our hands + and return the underlying type. */ - return valid_args ? value_item->type() : NULL_ITEM; + return valid_args ? + (((value_item->type() == FUNC_ITEM) && + (((Item_func *) value_item)->functype() == Item_func::NEG_FUNC)) ? + ((Item_func *) value_item)->key_item()->type() : + value_item->type()) : + NULL_ITEM; } @@ -1270,6 +1296,7 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref) return TRUE; } set_name(item_name->ptr(), (uint) item_name->length(), system_charset_info); + collation.set(value_item->collation.collation, DERIVATION_IMPLICIT); max_length= value_item->max_length; decimals= value_item->decimals; fixed= 1; @@ -3976,13 +4003,25 @@ bool Item_field::fix_fields(THD *thd, Item **reference) } if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) goto error; - else if (!ret) - return FALSE; outer_fixed= TRUE; + if (!ret) + goto mark_non_agg_field; } else if (!from_field) goto error; + if (!outer_fixed && cached_table && cached_table->select_lex && + context->select_lex && + cached_table->select_lex != context->select_lex) + { + int ret; + if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) + goto error; + outer_fixed= 1; + if (!ret) + goto mark_non_agg_field; + } + /* if it is not expression from merged VIEW we will set this field. @@ -3998,18 +4037,6 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (from_field == view_ref_found) return FALSE; - if (!outer_fixed && cached_table && cached_table->select_lex && - context->select_lex && - cached_table->select_lex != context->select_lex) - { - int ret; - if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) - goto error; - if (!ret) - return FALSE; - outer_fixed= 1; - } - set_field(from_field); if (thd->lex->in_sum_func && thd->lex->in_sum_func->nest_level == @@ -4076,6 +4103,26 @@ bool Item_field::fix_fields(THD *thd, Item **reference) thd->lex->current_select->non_agg_fields.push_back(this); marker= thd->lex->current_select->cur_pos_in_select_list; } +mark_non_agg_field: + if (fixed && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) + { + /* + Mark selects according to presence of non aggregated fields. + Fields from outer selects added to the aggregate function + outer_fields list as its unknown at the moment whether it's + aggregated or not. + */ + if (!thd->lex->in_sum_func) + cached_table->select_lex->full_group_by_flag|= NON_AGG_FIELD_USED; + else + { + if (outer_fixed) + thd->lex->in_sum_func->outer_fields.push_back(this); + else if (thd->lex->in_sum_func->nest_level != + thd->lex->current_select->nest_level) + cached_table->select_lex->full_group_by_flag|= NON_AGG_FIELD_USED; + } + } return FALSE; error: @@ -4181,6 +4228,30 @@ bool Item_field::subst_argument_checker(uchar **arg) /** + Convert a numeric value to a zero-filled string + + @param[in,out] item the item to operate on + @param field The field that this value is equated to + + This function converts a numeric value to a string. In this conversion + the zero-fill flag of the field is taken into account. + This is required so the resulting string value can be used instead of + the field reference when propagating equalities. +*/ + +static void convert_zerofill_number_to_string(Item **item, Field_num *field) +{ + char buff[MAX_FIELD_WIDTH],*pos; + String tmp(buff,sizeof(buff), field->charset()), *res; + + res= (*item)->val_str(&tmp); + field->prepend_zeros(res); + pos= (char *) sql_strmake (res->ptr(), res->length()); + *item= new Item_string(pos, res->length(), field->charset()); +} + + +/** Set a pointer to the multiple equality the field reference belongs to (if any). @@ -4226,6 +4297,13 @@ Item *Item_field::equal_fields_propagator(uchar *arg) if (!item || (cmp_context != (Item_result)-1 && item->cmp_context != cmp_context)) item= this; + else if (field && (field->flags & ZEROFILL_FLAG) && IS_NUM(field->type())) + { + if (item && cmp_context != INT_RESULT) + convert_zerofill_number_to_string(&item, (Field_num *)field); + else + item= this; + } return item; } @@ -4402,6 +4480,49 @@ String *Item::check_well_formed_result(String *str, bool send_error) return str; } +/* + Compare two items using a given collation + + SYNOPSIS + eq_by_collation() + item item to compare with + binary_cmp TRUE <-> compare as binaries + cs collation to use when comparing strings + + DESCRIPTION + This method works exactly as Item::eq if the collation cs coincides with + the collation of the compared objects. Otherwise, first the collations that + differ from cs are replaced for cs and then the items are compared by + Item::eq. After the comparison the original collations of items are + restored. + + RETURN + 1 compared items has been detected as equal + 0 otherwise +*/ + +bool Item::eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs) +{ + CHARSET_INFO *save_cs= 0; + CHARSET_INFO *save_item_cs= 0; + if (collation.collation != cs) + { + save_cs= collation.collation; + collation.collation= cs; + } + if (item->collation.collation != cs) + { + save_item_cs= item->collation.collation; + item->collation.collation= cs; + } + bool res= eq(item, binary_cmp); + if (save_cs) + collation.collation= save_cs; + if (save_item_cs) + item->collation.collation= save_item_cs; + return res; +} + /** Create a field to hold a string value from an item. @@ -5504,13 +5625,16 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) DBUG_ASSERT(*ref); /* Check if this is an incorrect reference in a group function or forward - reference. Do not issue an error if this is an unnamed reference inside an - aggregate function. + reference. Do not issue an error if this is: + 1. outer reference (will be fixed later by the fix_inner_refs function); + 2. an unnamed reference inside an aggregate function. */ - if (((*ref)->with_sum_func && name && - !(current_sel->linkage != GLOBAL_OPTIONS_TYPE && - current_sel->having_fix_field)) || - !(*ref)->fixed) + if (!((*ref)->type() == REF_ITEM && + ((Item_ref *)(*ref))->ref_type() == OUTER_REF) && + (((*ref)->with_sum_func && name && + !(current_sel->linkage != GLOBAL_OPTIONS_TYPE && + current_sel->having_fix_field)) || + !(*ref)->fixed)) { my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, ((*ref)->with_sum_func? diff --git a/sql/item.h b/sql/item.h index 7cf0bafa9d9..cd8bb4faccb 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1007,6 +1007,7 @@ public: virtual Field::geometry_type get_geometry_type() const { return Field::GEOM_GEOMETRY; }; String *check_well_formed_result(String *str, bool send_error= 0); + bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); }; @@ -1264,14 +1265,7 @@ class Item_name_const : public Item Item *name_item; bool valid_args; public: - Item_name_const(Item *name_arg, Item *val): - value_item(val), name_item(name_arg) - { - if (!(valid_args= name_item->basic_const_item() & - value_item->basic_const_item())) - my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST"); - Item::maybe_null= TRUE; - } + Item_name_const(Item *name_arg, Item *val); bool fix_fields(THD *, Item **); @@ -2219,6 +2213,35 @@ public: Item_field *filed_for_view_update() { return (*ref)->filed_for_view_update(); } virtual Ref_Type ref_type() { return REF; } + + // Row emulation: forwarding of ROW-related calls to ref + uint cols() + { + return ref && result_type() == ROW_RESULT ? (*ref)->cols() : 1; + } + Item* element_index(uint i) + { + return ref && result_type() == ROW_RESULT ? (*ref)->element_index(i) : this; + } + Item** addr(uint i) + { + return ref && result_type() == ROW_RESULT ? (*ref)->addr(i) : 0; + } + bool check_cols(uint c) + { + return ref && result_type() == ROW_RESULT ? (*ref)->check_cols(c) + : Item::check_cols(c); + } + bool null_inside() + { + return ref && result_type() == ROW_RESULT ? (*ref)->null_inside() : 0; + } + void bring_value() + { + if (ref && result_type() == ROW_RESULT) + (*ref)->bring_value(); + } + }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index ae7ea95707c..f267ad39984 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -3059,7 +3059,10 @@ void in_string::set(uint pos,Item *item) { if (res->uses_buffer_owned_by(str)) res->copy(); - *str= *res; + if (item->type() == Item::FUNC_ITEM) + str->copy(*res); + else + *str= *res; } if (!str->charset()) { diff --git a/sql/item_create.cc b/sql/item_create.cc index 40578bef5f8..49cc33b95a7 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -4354,12 +4354,12 @@ Create_func_space::create(THD *thd, Item *arg1) if (cs->mbminlen > 1) { uint dummy_errors; - sp= new (thd->mem_root) Item_string("", 0, cs); + sp= new (thd->mem_root) Item_string("", 0, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); sp->str_value.copy(" ", 1, &my_charset_latin1, cs, &dummy_errors); } else { - sp= new (thd->mem_root) Item_string(" ", 1, cs); + sp= new (thd->mem_root) Item_string(" ", 1, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); } return new (thd->mem_root) Item_func_repeat(sp, arg1); diff --git a/sql/item_func.cc b/sql/item_func.cc index 12bb0999ffc..a17c1eff5dd 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -32,6 +32,7 @@ #include <hash.h> #include <time.h> #include <ft_global.h> +#include <my_bit.h> #include "sp_head.h" #include "sp_rcontext.h" @@ -1093,7 +1094,7 @@ double Item_func_plus::real_op() double value= args[0]->val_real() + args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; - return value; + return fix_result(value); } @@ -1171,7 +1172,7 @@ double Item_func_minus::real_op() double value= args[0]->val_real() - args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; - return value; + return fix_result(value); } @@ -1211,7 +1212,7 @@ double Item_func_mul::real_op() double value= args[0]->val_real() * args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; - return value; + return fix_result(value); } @@ -1269,7 +1270,7 @@ double Item_func_div::real_op() signal_divide_by_null(); return 0.0; } - return value/val2; + return fix_result(value/val2); } @@ -1643,7 +1644,7 @@ double Item_func_exp::val_real() double value= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0.0; /* purecov: inspected */ - return exp(value); + return fix_result(exp(value)); } double Item_func_sqrt::val_real() @@ -1662,7 +1663,7 @@ double Item_func_pow::val_real() double val2= args[1]->val_real(); if ((null_value=(args[0]->null_value || args[1]->null_value))) return 0.0; /* purecov: inspected */ - return pow(value,val2); + return fix_result(pow(value,val2)); } // Trigonometric functions @@ -1674,7 +1675,7 @@ double Item_func_acos::val_real() volatile double value= args[0]->val_real(); if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0)))) return 0.0; - return fix_result(acos(value)); + return acos(value); } double Item_func_asin::val_real() @@ -1684,7 +1685,7 @@ double Item_func_asin::val_real() volatile double value= args[0]->val_real(); if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0)))) return 0.0; - return fix_result(asin(value)); + return asin(value); } double Item_func_atan::val_real() @@ -1700,7 +1701,7 @@ double Item_func_atan::val_real() return 0.0; return fix_result(atan2(value,val2)); } - return fix_result(atan(value)); + return atan(value); } double Item_func_cos::val_real() @@ -1709,7 +1710,7 @@ double Item_func_cos::val_real() double value= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0.0; - return fix_result(cos(value)); + return cos(value); } double Item_func_sin::val_real() @@ -1718,7 +1719,7 @@ double Item_func_sin::val_real() double value= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0.0; - return fix_result(sin(value)); + return sin(value); } double Item_func_tan::val_real() @@ -3646,18 +3647,28 @@ longlong Item_func_benchmark::val_int() String tmp(buff,sizeof(buff), &my_charset_bin); my_decimal tmp_decimal; THD *thd=current_thd; - ulong loop_count; + ulonglong loop_count; - loop_count= (ulong) args[0]->val_int(); + loop_count= (ulonglong) args[0]->val_int(); - if (args[0]->null_value) + if (args[0]->null_value || + (!args[0]->unsigned_flag && (((longlong) loop_count) < 0))) { + if (!args[0]->null_value) + { + char buff[22]; + llstr(((longlong) loop_count), buff); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE), + "count", buff, "benchmark"); + } + null_value= 1; return 0; } null_value=0; - for (ulong loop=0 ; loop < loop_count && !thd->killed; loop++) + for (ulonglong loop=0 ; loop < loop_count && !thd->killed; loop++) { switch (args[1]->result_type()) { case REAL_RESULT: @@ -3705,11 +3716,24 @@ longlong Item_func_sleep::val_int() DBUG_ASSERT(fixed == 1); double time= args[0]->val_real(); + /* + On 64-bit OSX pthread_cond_timedwait() waits forever + if passed abstime time has already been exceeded by + the system time. + When given a very short timeout (< 10 mcs) just return + immediately. + We assume that the lines between this test and the call + to pthread_cond_timedwait() will be executed in less than 0.00001 sec. + */ + if (time < 0.00001) + return 0; + set_timespec_nsec(abstime, (ulonglong)(time * ULL(1000000000))); pthread_cond_init(&cond, NULL); pthread_mutex_lock(&LOCK_user_locks); + thd_proc_info(thd, "User sleep"); thd->mysys_var->current_mutex= &LOCK_user_locks; thd->mysys_var->current_cond= &cond; @@ -3721,6 +3745,7 @@ longlong Item_func_sleep::val_int() break; error= 0; } + thd_proc_info(thd, 0); pthread_mutex_unlock(&LOCK_user_locks); pthread_mutex_lock(&thd->mysys_var->mutex); thd->mysys_var->current_mutex= 0; @@ -5496,6 +5521,8 @@ Item_func_sp::make_field(Send_field *tmp_field) DBUG_ENTER("Item_func_sp::make_field"); DBUG_ASSERT(sp_result_field); sp_result_field->make_field(tmp_field); + if (name) + tmp_field->col_name= name; DBUG_VOID_RETURN; } diff --git a/sql/item_func.h b/sql/item_func.h index 9ab30a2cf93..02631d7643d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -54,7 +54,8 @@ public: NOT_FUNC, NOT_ALL_FUNC, NOW_FUNC, TRIG_COND_FUNC, SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC, - EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC }; + EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC, + NEG_FUNC }; enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL, OPTIMIZE_EQUAL }; enum Type type() const { return FUNC_ITEM; } @@ -192,6 +193,13 @@ public: void * arg, traverse_order order); bool is_expensive_processor(uchar *arg); virtual bool is_expensive() { return 0; } + inline double fix_result(double value) + { + if (isfinite(value)) + return value; + null_value=1; + return 0.0; + } }; @@ -301,7 +309,6 @@ class Item_num_op :public Item_func_numhybrid void find_num_type(); String *str_op(String *str) { DBUG_ASSERT(0); return 0; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -387,6 +394,7 @@ class Item_func_additive_op :public Item_num_op public: Item_func_additive_op(Item *a,Item *b) :Item_num_op(a,b) {} void result_precision(); + bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -421,6 +429,7 @@ public: double real_op(); my_decimal *decimal_op(my_decimal *); void result_precision(); + bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -466,6 +475,7 @@ public: const char *func_name() const { return "%"; } void result_precision(); void fix_length_and_dec(); + bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -477,7 +487,7 @@ public: longlong int_op(); my_decimal *decimal_op(my_decimal *); const char *func_name() const { return "-"; } - virtual bool basic_const_item() const { return args[0]->basic_const_item(); } + enum Functype functype() const { return NEG_FUNC; } void fix_length_and_dec(); void fix_num_length_and_dec(); uint decimal_precision() const { return args[0]->decimal_precision(); } @@ -509,18 +519,6 @@ class Item_dec_func :public Item_real_func decimals=NOT_FIXED_DEC; max_length=float_length(decimals); maybe_null=1; } - inline double fix_result(double value) - { -#ifndef HAVE_FINITE - return value; -#else - /* The following should be safe, even if we compare doubles */ - if (finite(value) && value != POSTFIX_ERROR) - return value; - null_value=1; - return 0.0; -#endif - } }; class Item_func_exp :public Item_dec_func diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 7b68d258d29..ea16f3c3518 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1288,7 +1288,11 @@ Item_in_subselect::row_value_transformer(JOIN *join) Item *item_having_part2= 0; for (uint i= 0; i < cols_num; i++) { - DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed); + DBUG_ASSERT(left_expr->fixed && + select_lex->ref_pointer_array[i]->fixed || + (select_lex->ref_pointer_array[i]->type() == REF_ITEM && + ((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() == + Item_ref::OUTER_REF)); if (select_lex->ref_pointer_array[i]-> check_cols(left_expr->element_index(i)->cols())) DBUG_RETURN(RES_ERROR); @@ -1362,7 +1366,11 @@ Item_in_subselect::row_value_transformer(JOIN *join) for (uint i= 0; i < cols_num; i++) { Item *item, *item_isnull; - DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed); + DBUG_ASSERT(left_expr->fixed && + select_lex->ref_pointer_array[i]->fixed || + (select_lex->ref_pointer_array[i]->type() == REF_ITEM && + ((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() == + Item_ref::OUTER_REF)); if (select_lex->ref_pointer_array[i]-> check_cols(left_expr->element_index(i)->cols())) DBUG_RETURN(RES_ERROR); @@ -1490,6 +1498,19 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) DBUG_ENTER("Item_in_subselect::select_in_like_transformer"); + { + /* + IN/SOME/ALL/ANY subqueries aren't support LIMIT clause. Without it + ORDER BY clause becomes meaningless thus we drop it here. + */ + SELECT_LEX *sl= current->master_unit()->first_select(); + for (; sl; sl= sl->next_select()) + { + if (sl->join) + sl->join->order= 0; + } + } + if (changed) { DBUG_RETURN(RES_OK); @@ -1524,6 +1545,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) transformed= 1; arena= thd->activate_stmt_arena_if_needed(&backup); + /* Both transformers call fix_fields() only for Items created inside them, and all that items do not make permanent changes in current item arena diff --git a/sql/item_sum.cc b/sql/item_sum.cc index f5a3956c1e4..264b53c780a 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -68,6 +68,7 @@ bool Item_sum::init_sum_func_check(THD *thd) aggr_sel= NULL; max_arg_level= -1; max_sum_func_level= -1; + outer_fields.empty(); return FALSE; } @@ -176,6 +177,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) MYF(0)); return TRUE; } + if (in_sum_func) { /* @@ -196,6 +198,68 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) set_if_bigger(in_sum_func->max_sum_func_level, aggr_level); set_if_bigger(in_sum_func->max_sum_func_level, max_sum_func_level); } + + /* + Check that non-aggregated fields and sum functions aren't mixed in the + same select in the ONLY_FULL_GROUP_BY mode. + */ + if (outer_fields.elements) + { + Item_field *field; + /* + Here we compare the nesting level of the select to which an outer field + belongs to with the aggregation level of the sum function. All fields in + the outer_fields list are checked. + + If the nesting level is equal to the aggregation level then the field is + aggregated by this sum function. + If the nesting level is less than the aggregation level then the field + belongs to an outer select. In this case if there is an embedding sum + function add current field to functions outer_fields list. If there is + no embedding function then the current field treated as non aggregated + and the select it belongs to is marked accordingly. + If the nesting level is greater than the aggregation level then it means + that this field was added by an inner sum function. + Consider an example: + + select avg ( <-- we are here, checking outer.f1 + select ( + select sum(outer.f1 + inner.f1) from inner + ) from outer) + from most_outer; + + In this case we check that no aggregate functions are used in the + select the field belongs to. If there are some then an error is + raised. + */ + List_iterator<Item_field> of(outer_fields); + while ((field= of++)) + { + SELECT_LEX *sel= field->cached_table->select_lex; + if (sel->nest_level < aggr_level) + { + if (in_sum_func) + { + /* + Let upper function decide whether this field is a non + aggregated one. + */ + in_sum_func->outer_fields.push_back(field); + } + else + sel->full_group_by_flag|= NON_AGG_FIELD_USED; + } + if (sel->nest_level > aggr_level && + (sel->full_group_by_flag & SUM_FUNC_USED) && + !sel->group_list.elements) + { + my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, + ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); + return TRUE; + } + } + } + aggr_sel->full_group_by_flag|= SUM_FUNC_USED; update_used_tables(); thd->lex->in_sum_func= in_sum_func; return FALSE; @@ -600,6 +664,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) result_field=0; null_value=1; fix_length_and_dec(); + item= item->real_item(); if (item->type() == Item::FIELD_ITEM) hybrid_field_type= ((Item_field*) item)->field->type(); else @@ -1227,7 +1292,15 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val) null_value=1; return NULL; } - sum_dec= Item_sum_sum::val_decimal(&sum_buff); + + /* + For non-DECIMAL hybrid_type the division will be done in + Item_sum_avg::val_real(). + */ + if (hybrid_type != DECIMAL_RESULT) + return val_decimal_from_real(val); + + sum_dec= dec_buffs + curr_dec_buff; int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &cnt); my_decimal_div(E_DEC_FATAL_ERROR, val, sum_dec, &cnt, prec_increment); return val; @@ -3493,6 +3566,6 @@ void Item_func_group_concat::print(String *str, enum_query_type query_type) Item_func_group_concat::~Item_func_group_concat() { - if (unique_filter) + if (!original && unique_filter) delete unique_filter; } diff --git a/sql/item_sum.h b/sql/item_sum.h index d1e6a74e85e..bee8792fbfa 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -239,6 +239,13 @@ public: int8 max_arg_level; /* max level of unbound column references */ int8 max_sum_func_level;/* max level of aggregation for embedded functions */ bool quick_group; /* If incremental update of fields */ + /* + This list is used by the check for mixing non aggregated fields and + sum functions in the ONLY_FULL_GROUP_BY_MODE. We save all outer fields + directly or indirectly used under this function it as it's unclear + at the moment of fixing outer field whether it's aggregated or not. + */ + List<Item_field> outer_fields; protected: table_map used_tables_cache; diff --git a/sql/key.cc b/sql/key.cc index 7f075674ab6..47e5c5ebdd7 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -168,6 +168,7 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info, } for (key_part= key_info->key_part ; (int) key_length > 0 ; key_part++) { + uchar used_uneven_bits= 0; if (key_part->null_bit) { if (*from_key++) @@ -186,6 +187,8 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info, set_rec_bits(bits, to_record + key_part->null_offset + (key_part->null_bit == 128), field->bit_ofs, field->bit_len); + /* we have now used the byte with 'uneven' bits */ + used_uneven_bits= 1; } } if (key_part->key_part_flag & HA_BLOB_PART) @@ -222,7 +225,9 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info, else { length= min(key_length, key_part->length); - memcpy(to_record + key_part->offset, from_key, (size_t) length); + /* skip the byte with 'uneven' bits, if used */ + memcpy(to_record + key_part->offset, from_key + used_uneven_bits + , (size_t) length - used_uneven_bits); } from_key+= length; key_length-= length; diff --git a/sql/lex.h b/sql/lex.h index 0b601de772a..acb81dcf717 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -386,8 +386,9 @@ static SYMBOL symbols[] = { { "OWNER", SYM(OWNER_SYM)}, { "PACK_KEYS", SYM(PACK_KEYS_SYM)}, { "PARSER", SYM(PARSER_SYM)}, + { "PAGE", SYM(PAGE_SYM)}, + { "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)}, { "PARTIAL", SYM(PARTIAL)}, - { "PAGE", SYM(PAGE_SYM)}, { "PARTITION", SYM(PARTITION_SYM)}, { "PARTITIONING", SYM(PARTITIONING_SYM)}, { "PARTITIONS", SYM(PARTITIONS_SYM)}, @@ -524,7 +525,8 @@ static SYMBOL symbols[] = { { "SWITCHES", SYM(SWITCHES_SYM)}, { "TABLE", SYM(TABLE_SYM)}, { "TABLES", SYM(TABLES)}, - { "TABLESPACE", SYM(TABLESPACE)}, + { "TABLESPACE", SYM(TABLESPACE)}, + { "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)}, { "TEMPORARY", SYM(TEMPORARY)}, { "TEMPTABLE", SYM(TEMPTABLE_SYM)}, { "TERMINATED", SYM(TERMINATED)}, @@ -541,6 +543,7 @@ static SYMBOL symbols[] = { { "TO", SYM(TO_SYM)}, { "TRAILING", SYM(TRAILING)}, { "TRANSACTION", SYM(TRANSACTION_SYM)}, + { "TRANSACTIONAL", SYM(TRANSACTIONAL_SYM)}, { "TRIGGER", SYM(TRIGGER_SYM)}, { "TRIGGERS", SYM(TRIGGERS_SYM)}, { "TRUE", SYM(TRUE_SYM)}, diff --git a/sql/log.cc b/sql/log.cc index 6a9673989ea..46efc68fc38 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -52,8 +52,6 @@ LOGGER logger; MYSQL_BIN_LOG mysql_bin_log; ulong sync_binlog_counter= 0; -static Muted_query_log_event invisible_commit; - static bool test_if_number(const char *str, long *res, bool allow_wildcards); static int binlog_init(void *p); @@ -155,7 +153,7 @@ private: class binlog_trx_data { public: binlog_trx_data() - : m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF) + : at_least_one_stmt(0), m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF) { trans_log.end_of_file= max_binlog_cache_size; } @@ -188,6 +186,16 @@ public: reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0); if (pos < before_stmt_pos) before_stmt_pos= MY_OFF_T_UNDEF; + + /* + The only valid positions that can be truncated to are at the + beginning of a statement. We are relying on this fact to be able + to set the at_least_one_stmt flag correctly. In other word, if + we are truncating to the beginning of the transaction cache, + there will be no statements in the cache, otherwhise, we will + have at least one statement in the transaction cache. + */ + at_least_one_stmt= (pos > 0); } /* @@ -213,6 +221,12 @@ public: IO_CACHE trans_log; // The transaction cache + /** + Boolean that is true if there is at least one statement in the + transaction cache. + */ + bool at_least_one_stmt; + private: /* Pending binrows event. This event is the event where the rows are @@ -418,6 +432,7 @@ bool Log_to_csv_event_handler:: A positive return value in store() means truncation. Still logging a message in the log in this case. */ + table->field[5]->flags|= FIELDFLAG_HEX_ESCAPE; if (table->field[5]->store(sql_text, sql_text_len, client_cs) < 0) goto err; @@ -1005,7 +1020,7 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, current_time= my_time(0); while (*current_handler) - error+= (*current_handler++)-> + error|= (*current_handler++)-> log_general(thd, current_time, user_host_buff, user_host_len, id, command_name[(uint) command].str, @@ -1373,26 +1388,20 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, inside a stored function. */ thd->binlog_flush_pending_rows_event(TRUE); + + error= mysql_bin_log.write(thd, &trx_data->trans_log, end_ev); + trx_data->reset(); + /* - We write the transaction cache to the binary log if either we're - committing the entire transaction, or if we are doing an - autocommit outside a transaction. - */ - if (all || !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) + We need to step the table map version after writing the + transaction cache to disk. + */ + mysql_bin_log.update_table_map_version(); + statistic_increment(binlog_cache_use, &LOCK_status); + if (trans_log->disk_writes != 0) { - error= mysql_bin_log.write(thd, &trx_data->trans_log, end_ev); - trx_data->reset(); - /* - We need to step the table map version after writing the - transaction cache to disk. - */ - mysql_bin_log.update_table_map_version(); - statistic_increment(binlog_cache_use, &LOCK_status); - if (trans_log->disk_writes != 0) - { - statistic_increment(binlog_cache_disk_use, &LOCK_status); - trans_log->disk_writes= 0; - } + statistic_increment(binlog_cache_disk_use, &LOCK_status); + trans_log->disk_writes= 0; } } else @@ -1431,6 +1440,8 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all) return 0; } +#define YESNO(X) ((X) ? "yes" : "no") + /** This function is called once after each statement. @@ -1439,10 +1450,8 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all) @param hton The binlog handlerton. @param thd The client thread that executes the transaction. - @param all true if this is the last statement before a COMMIT - statement; false if either this is a statement in a - transaction but not the last, or if this is a statement - not inside a BEGIN block and autocommit is on. + @param all This is @c true if this is a real transaction commit, and + @false otherwise. @see handlerton::commit */ @@ -1458,26 +1467,86 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) trx_data->reset(); DBUG_RETURN(0); } + /* - Write commit event if at least one of the following holds: - - the user sends an explicit COMMIT; or - - the autocommit flag is on, and we are not inside a BEGIN. - However, if the user has not sent an explicit COMMIT, and we are - either inside a BEGIN or run with autocommit off, then this is not - the end of a transaction and we should not write a commit event. + Decision table for committing a transaction. The top part, the + *conditions* represent different cases that can occur, and hte + bottom part, the *actions*, represent what should be done in that + particular case. + + Real transaction 'all' was true + + Statement in cache There were at least one statement in the + transaction cache + + In transaction We are inside a transaction + + Stmt modified non-trans The statement being committed modified a + non-transactional table + + All modified non-trans Some statement before this one in the + transaction modified a non-transactional + table + + + ============================= = = = = = = = = = = = = = = = = + Real transaction N N N N N N N N N N N N N N N N + Statement in cache N N N N N N N N Y Y Y Y Y Y Y Y + In transaction N N N N Y Y Y Y N N N N Y Y Y Y + Stmt modified non-trans N N Y Y N N Y Y N N Y Y N N Y Y + All modified non-trans N Y N Y N Y N Y N Y N Y N Y N Y + + Action: (C)ommit/(A)ccumulate C C - C A C - C - - - - A A - A + ============================= = = = = = = = = = = = = = = = = + + + ============================= = = = = = = = = = = = = = = = = + Real transaction Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y + Statement in cache N N N N N N N N Y Y Y Y Y Y Y Y + In transaction N N N N Y Y Y Y N N N N Y Y Y Y + Stmt modified non-trans N N Y Y N N Y Y N N Y Y N N Y Y + All modified non-trans N Y N Y N Y N Y N Y N Y N Y N Y + + (C)ommit/(A)ccumulate/(-) - - - - C C - C - - - - C C - C + ============================= = = = = = = = = = = = = = = = = + + In other words, we commit the transaction if and only if both of + the following are true: + - We are not in a transaction and committing a statement + + - We are in a transaction and one (or more) of the following are + true: + + - A full transaction is committed + + OR + + - A non-transactional statement is committed and there is + no statement cached + + Otherwise, we accumulate the statement */ - if (all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) + ulonglong const in_transaction= + thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN); + DBUG_PRINT("debug", + ("all: %d, empty: %s, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s", + all, + YESNO(trx_data->empty()), + YESNO(in_transaction), + YESNO(thd->transaction.all.modified_non_trans_table), + YESNO(thd->transaction.stmt.modified_non_trans_table))); + if (in_transaction && + (all || + (!trx_data->at_least_one_stmt && + thd->transaction.stmt.modified_non_trans_table)) || + !in_transaction && !all) { Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE); qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) int error= binlog_end_trans(thd, trx_data, &qev, all); DBUG_RETURN(error); } - else - { - int error= binlog_end_trans(thd, trx_data, &invisible_commit, all); - DBUG_RETURN(error); - } + DBUG_RETURN(0); } /** @@ -1490,10 +1559,8 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) @param hton The binlog handlerton. @param thd The client thread that executes the transaction. - @param all true if this is the last statement before a COMMIT - statement; false if either this is a statement in a - transaction but not the last, or if this is a statement - not inside a BEGIN block and autocommit is on. + @param all This is @c true if this is a real transaction rollback, and + @false otherwise. @see handlerton::rollback */ @@ -1509,21 +1576,36 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) DBUG_RETURN(0); } - /* - Update the binary log with a BEGIN/ROLLBACK block if we have - cached some queries and we updated some non-transactional - table. Such cases should be rare (updating a - non-transactional table inside a transaction...) - */ - if (unlikely(thd->transaction.all.modified_non_trans_table || - (thd->options & OPTION_KEEP_LOG))) + DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s", + YESNO(all), + YESNO(thd->transaction.all.modified_non_trans_table), + YESNO(thd->transaction.stmt.modified_non_trans_table))); + if (all && thd->transaction.all.modified_non_trans_table || + !all && thd->transaction.stmt.modified_non_trans_table || + (thd->options & OPTION_KEEP_LOG)) { + /* + We write the transaction cache with a rollback last if we have + modified any non-transactional table. We do this even if we are + committing a single statement that has modified a + non-transactional table since it can have modified a + transactional table in that statement as well, which needs to be + rolled back on the slave. + */ Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE); qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) error= binlog_end_trans(thd, trx_data, &qev, all); } - else + else if (all && !thd->transaction.all.modified_non_trans_table || + !all && !thd->transaction.stmt.modified_non_trans_table) + { + /* + If we have modified only transactional tables, we can truncate + the transaction cache without writing anything to the binary + log. + */ error= binlog_end_trans(thd, trx_data, 0, all); + } DBUG_RETURN(error); } @@ -2338,6 +2420,11 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg, my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)), 0, MYF(MY_WME | MY_WAIT_IF_FULL))) { + /* + TODO: all operations creating/deleting the index file or a log, should + call my_sync_dir() or my_sync_dir_by_file() to be durable. + TODO: file creation should be done with my_create() not my_open(). + */ if (index_file_nr >= 0) my_close(index_file_nr,MYF(0)); return TRUE; @@ -2745,14 +2832,62 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd) for (;;) { - my_delete_allow_opened(linfo.log_file_name, MYF(MY_WME)); + if ((error= my_delete_allow_opened(linfo.log_file_name, MYF(0))) != 0) + { + if (my_errno == ENOENT) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), + linfo.log_file_name); + sql_print_information("Failed to delete file '%s'", + linfo.log_file_name); + my_errno= 0; + error= 0; + } + else + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_BINLOG_PURGE_FATAL_ERR, + "a problem with deleting %s; " + "consider examining correspondence " + "of your binlog index file " + "to the actual binlog files", + linfo.log_file_name); + error= 1; + goto err; + } + } if (find_next_log(&linfo, 0)) break; } /* Start logging with a new file */ close(LOG_CLOSE_INDEX); - my_delete_allow_opened(index_file_name, MYF(MY_WME)); // Reset (open will update) + if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will update) + { + if (my_errno == ENOENT) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), + index_file_name); + sql_print_information("Failed to delete file '%s'", + index_file_name); + my_errno= 0; + error= 0; + } + else + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_BINLOG_PURGE_FATAL_ERR, + "a problem with deleting %s; " + "consider examining correspondence " + "of your binlog index file " + "to the actual binlog files", + index_file_name); + error= 1; + goto err; + } + } if (!thd->slave_thread) need_start_event=1; if (!open_index_file(index_file_name, 0)) @@ -2912,6 +3047,9 @@ int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads 0 ok @retval LOG_INFO_EOF to_log not found + LOG_INFO_EMFILE too many files opened + LOG_INFO_FATAL if any other than ENOENT error from + my_stat() or my_delete() */ int MYSQL_BIN_LOG::purge_logs(const char *to_log, @@ -2941,44 +3079,84 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log, while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) && !log_in_use(log_info.log_file_name)) { - ulong file_size= 0; - if (decrease_log_space) //stat the file we want to delete + MY_STAT s; + if (!my_stat(log_info.log_file_name, &s, MYF(0))) { - MY_STAT s; - - /* - If we could not stat, we can't know the amount - of space that deletion will free. In most cases, - deletion won't work either, so it's not a problem. - */ - if (my_stat(log_info.log_file_name,&s,MYF(0))) - file_size= s.st_size; - else - sql_print_information("Failed to execute my_stat on file '%s'", + if (my_errno == ENOENT) + { + /* + It's not fatal if we can't stat a log file that does not exist; + If we could not stat, we won't delete. + */ + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), + log_info.log_file_name); + sql_print_information("Failed to execute my_stat on file '%s'", log_info.log_file_name); + my_errno= 0; + } + else + { + /* + Other than ENOENT are fatal + */ + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_BINLOG_PURGE_FATAL_ERR, + "a problem with getting info on being purged %s; " + "consider examining correspondence " + "of your binlog index file " + "to the actual binlog files", + log_info.log_file_name); + error= LOG_INFO_FATAL; + goto err; + } } - /* - It's not fatal if we can't delete a log file ; - if we could delete it, take its size into account - */ - DBUG_PRINT("info",("purging %s",log_info.log_file_name)); - if (!my_delete(log_info.log_file_name, MYF(0)) && decrease_log_space) - *decrease_log_space-= file_size; - - ha_binlog_index_purge_file(current_thd, log_info.log_file_name); - if (current_thd->is_slave_error) { - DBUG_PRINT("info",("slave error: %d", current_thd->is_slave_error)); - if (my_errno == EMFILE) { - DBUG_PRINT("info",("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno)); - ret = LOG_INFO_EMFILE; - break; + else + { + DBUG_PRINT("info",("purging %s",log_info.log_file_name)); + if (!my_delete(log_info.log_file_name, MYF(0))) + { + if (decrease_log_space) + *decrease_log_space-= s.st_size; + } + else + { + if (my_errno == ENOENT) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), + log_info.log_file_name); + sql_print_information("Failed to delete file '%s'", + log_info.log_file_name); + my_errno= 0; + } + else + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_BINLOG_PURGE_FATAL_ERR, + "a problem with deleting %s; " + "consider examining correspondence " + "of your binlog index file " + "to the actual binlog files", + log_info.log_file_name); + if (my_errno == EMFILE) + { + DBUG_PRINT("info", + ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno)); + error= LOG_INFO_EMFILE; + } + error= LOG_INFO_FATAL; + goto err; + } } } + ha_binlog_index_purge_file(current_thd, log_info.log_file_name); + if (find_next_log(&log_info, 0) || exit_loop) break; } - + /* If we get killed -9 here, the sysadmin would have to edit the log index file after restart - otherwise, this should be safe @@ -3009,6 +3187,8 @@ err: 0 ok @retval LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated + LOG_INFO_FATAL if any other than ENOENT error from + my_stat() or my_delete() */ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time) @@ -3032,14 +3212,67 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time) while (strcmp(log_file_name, log_info.log_file_name) && !log_in_use(log_info.log_file_name)) { - /* It's not fatal even if we can't delete a log file */ - if (!my_stat(log_info.log_file_name, &stat_area, MYF(0)) || - stat_area.st_mtime >= purge_time) - break; - my_delete(log_info.log_file_name, MYF(0)); - - ha_binlog_index_purge_file(current_thd, log_info.log_file_name); - + if (!my_stat(log_info.log_file_name, &stat_area, MYF(0))) + { + if (my_errno == ENOENT) + { + /* + It's not fatal if we can't stat a log file that does not exist. + */ + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), + log_info.log_file_name); + sql_print_information("Failed to execute my_stat on file '%s'", + log_info.log_file_name); + my_errno= 0; + } + else + { + /* + Other than ENOENT are fatal + */ + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_BINLOG_PURGE_FATAL_ERR, + "a problem with getting info on being purged %s; " + "consider examining correspondence " + "of your binlog index file " + "to the actual binlog files", + log_info.log_file_name); + error= LOG_INFO_FATAL; + goto err; + } + } + else + { + if (stat_area.st_mtime >= purge_time) + break; + if (my_delete(log_info.log_file_name, MYF(0))) + { + if (my_errno == ENOENT) + { + /* It's not fatal even if we can't delete a log file */ + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), + log_info.log_file_name); + sql_print_information("Failed to delete file '%s'", + log_info.log_file_name); + my_errno= 0; + } + else + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_BINLOG_PURGE_FATAL_ERR, + "a problem with deleting %s; " + "consider examining correspondence " + "of your binlog index file " + "to the actual binlog files", + log_info.log_file_name); + error= LOG_INFO_FATAL; + goto err; + } + } + ha_binlog_index_purge_file(current_thd, log_info.log_file_name); + } if (find_next_log(&log_info, 0)) break; } @@ -4419,15 +4652,7 @@ static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff, return an error (e.g. logging to the log tables) */ -#ifdef EMBEDDED_LIBRARY -int vprint_msg_to_log(enum loglevel level __attribute__((unused)), - const char *format __attribute__((unused)), - va_list argsi __attribute__((unused))) -{ - DBUG_ENTER("vprint_msg_to_log"); - DBUG_RETURN(0); -} -#else /*!EMBEDDED_LIBRARY*/ +#ifndef EMBEDDED_LIBRARY static void print_buffer_to_file(enum loglevel level, const char *buffer) { time_t skr; diff --git a/sql/log_event.cc b/sql/log_event.cc index 15f1a957149..35cffbbf38c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -609,6 +609,7 @@ Log_event::Log_event(const char* buf, #endif when = uint4korr(buf); server_id = uint4korr(buf + SERVER_ID_OFFSET); + data_written= uint4korr(buf + EVENT_LEN_OFFSET); if (description_event->binlog_version==1) { log_pos= 0; @@ -641,7 +642,7 @@ Log_event::Log_event(const char* buf, binlog, so which will cause problems if the user uses this value in CHANGE MASTER). */ - log_pos+= uint4korr(buf + EVENT_LEN_OFFSET); + log_pos+= data_written; /* purecov: inspected */ } DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos)); @@ -2142,11 +2143,13 @@ void Query_log_event::print_query_header(IO_CACHE* file, bool need_comma= 0; my_b_printf(file, "SET "); print_set_option(file, tmp, OPTION_NO_FOREIGN_KEY_CHECKS, ~flags2, - "@@session.foreign_key_checks", &need_comma); + "@@session.foreign_key_checks", &need_comma); print_set_option(file, tmp, OPTION_AUTO_IS_NULL, flags2, - "@@session.sql_auto_is_null", &need_comma); + "@@session.sql_auto_is_null", &need_comma); print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2, - "@@session.unique_checks", &need_comma); + "@@session.unique_checks", &need_comma); + print_set_option(file, tmp, OPTION_NOT_AUTOCOMMIT, ~flags2, + "@@session.autocommit", &need_comma); my_b_printf(file,"%s\n", print_event_info->delimiter); print_event_info->flags2= flags2; } @@ -2606,21 +2609,6 @@ Query_log_event::do_shall_skip(Relay_log_info *rli) /************************************************************************** - Muted_query_log_event methods -**************************************************************************/ - -#ifndef MYSQL_CLIENT -/* - Muted_query_log_event::Muted_query_log_event() -*/ -Muted_query_log_event::Muted_query_log_event() - :Query_log_event() -{ -} -#endif - - -/************************************************************************** Start_log_event_v3 methods **************************************************************************/ @@ -7057,7 +7045,7 @@ int Table_map_log_event::save_field_metadata() #if !defined(MYSQL_CLIENT) Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, bool is_transactional, uint16 flags) - : Log_event(thd, 0, is_transactional), + : Log_event(thd, 0, true), m_table(tbl), m_dbnam(tbl->s->db.str), m_dblen(m_dbnam ? tbl->s->db.length : 0), diff --git a/sql/log_event.h b/sql/log_event.h index 2cf69e975f4..76d92b23189 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1599,31 +1599,6 @@ public: /* !!! Public in this patch to allow old usage */ }; -/** - @class Muted_query_log_event - - Pretends to log SQL queries, but doesn't actually do so. This is - used internally only and never written to any binlog. - - @section Muted_query_log_event_binary_format Binary Format - - This log event is not stored, and thus the binary format is 0 bytes - long. Note that not even the Common-Header is stored. -*/ -class Muted_query_log_event: public Query_log_event -{ -public: -#ifndef MYSQL_CLIENT - Muted_query_log_event(); - - bool write(IO_CACHE* file) { return(false); }; - virtual bool write_post_header_for_derived(IO_CACHE* file) { return FALSE; } -#else - Muted_query_log_event() {} -#endif -}; - - #ifdef HAVE_REPLICATION /** diff --git a/sql/matherr.c b/sql/matherr.c deleted file mode 100644 index 4998d8b4961..00000000000 --- a/sql/matherr.c +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright (C) 2000-2001 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* Fix that we got POSTFIX_ERROR when doing unreasonable math (not core) */ - -#include <my_global.h> -#include <errno.h> - - /* Fix that we gets POSTFIX_ERROR when error in math */ - -#if defined(HAVE_MATHERR) -int matherr(struct exception *x) -{ - if (x->type != PLOSS) - x->retval=POSTFIX_ERROR; - switch (x->type) { - case DOMAIN: - case SING: - my_errno=EDOM; - break; - case OVERFLOW: - case UNDERFLOW: - my_errno=ERANGE; - break; - default: - break; - } - return(1); /* Take no other action */ -} -#endif diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b52e5aa745c..69f214d95f3 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -741,8 +741,8 @@ inline bool check_some_routine_access(THD *thd, const char *db, bool multi_update_precheck(THD *thd, TABLE_LIST *tables); bool multi_delete_precheck(THD *thd, TABLE_LIST *tables); -bool mysql_multi_update_prepare(THD *thd); -bool mysql_multi_delete_prepare(THD *thd); +int mysql_multi_update_prepare(THD *thd); +int mysql_multi_delete_prepare(THD *thd); bool mysql_insert_select_prepare(THD *thd); bool update_precheck(THD *thd, TABLE_LIST *tables); bool delete_precheck(THD *thd, TABLE_LIST *tables); @@ -761,6 +761,7 @@ bool check_string_byte_length(LEX_STRING *str, const char *err_msg, bool check_string_char_length(LEX_STRING *str, const char *err_msg, uint max_char_length, CHARSET_INFO *cs, bool no_error); +bool test_if_data_home_dir(const char *dir); bool parse_sql(THD *thd, class Lex_input_stream *lip, @@ -974,10 +975,8 @@ void time_out_user_resource_limits(THD *thd, USER_CONN *uc); void decrease_user_connections(USER_CONN *uc); void thd_init_client_charset(THD *thd, uint cs_number); bool setup_connection_thread_globals(THD *thd); -bool login_connection(THD *thd); -void end_connection(THD *thd); -bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent); +int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent); bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create); bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent); bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db); @@ -1021,7 +1020,7 @@ void init_max_user_conn(void); void init_update_queries(void); void free_max_user_conn(void); pthread_handler_t handle_bootstrap(void *arg); -bool mysql_execute_command(THD *thd); +int mysql_execute_command(THD *thd); bool do_command(THD *thd); bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length); @@ -1197,7 +1196,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields, int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *table_list); void prepare_triggers_for_insert_stmt(TABLE *table); -bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds); +int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds); bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ha_rows rows, ulonglong options, bool reset_auto_increment); @@ -1408,6 +1407,13 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables, extern Item **not_found_item; /* + A set of constants used for checking non aggregated fields and sum + functions mixture in the ONLY_FULL_GROUP_BY_MODE. +*/ +#define NON_AGG_FIELD_USED 1 +#define SUM_FUNC_USED 2 + +/* This enumeration type is used only by the function find_item_in_list to return the info on how an item has been resolved against a list of possibly aliased items. @@ -1471,14 +1477,14 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond); int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags); /* open_and_lock_tables with optional derived handling */ -bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived); +int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived); /* simple open_and_lock_tables without derived handling */ -inline bool simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) +inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) { return open_and_lock_tables_derived(thd, tables, FALSE); } /* open_and_lock_tables with derived handling */ -inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) +inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables) { return open_and_lock_tables_derived(thd, tables, TRUE); } @@ -1651,6 +1657,7 @@ extern pthread_mutex_t LOCK_gdl; #define WFRM_WRITE_SHADOW 1 #define WFRM_INSTALL_SHADOW 2 #define WFRM_PACK_FRM 4 +#define WFRM_KEEP_SHARE 8 bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags); int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt); void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt); @@ -1707,7 +1714,7 @@ inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table, bool eval_const_cond(COND *cond); /* sql_load.cc */ -bool mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list, +int mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list, List<Item> &fields_vars, List<Item> &set_fields, List<Item> &set_values_list, enum enum_duplicates handle_duplicates, bool ignore, @@ -1805,7 +1812,8 @@ extern time_t server_start_time, flush_status_time; #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS extern uint mysql_data_home_len; extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH], - mysql_real_data_home[]; + mysql_real_data_home[], mysql_unpacked_real_data_home[]; +extern CHARSET_INFO *character_set_filesystem; #endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ #ifdef MYSQL_SERVER extern char *opt_mysql_tmpdir, mysql_charsets_dir[], @@ -1853,7 +1861,7 @@ extern ulong max_connections,max_connect_errors, connect_timeout; extern ulong slave_net_timeout, slave_trans_retries; extern uint max_user_connections; extern ulong what_to_log,flush_time; -extern ulong query_buff_size, thread_stack; +extern ulong query_buff_size; extern ulong max_prepared_stmt_count, prepared_stmt_count; extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit; extern ulong max_binlog_size, max_relay_log_size; @@ -1895,6 +1903,7 @@ extern bool opt_disable_networking, opt_skip_show_db; extern my_bool opt_character_set_client_handshake; extern bool volatile abort_loop, shutdown_in_progress; extern uint volatile thread_count, thread_running, global_read_lock; +extern uint connection_count; extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types; extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap; extern my_bool opt_slave_compressed_protocol, use_temp_pool; @@ -1933,7 +1942,7 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_lock_db, LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock, LOCK_global_system_variables, LOCK_user_conn, LOCK_prepared_stmt_count, - LOCK_bytes_sent, LOCK_bytes_received; + LOCK_bytes_sent, LOCK_bytes_received, LOCK_connection_count; #ifdef HAVE_OPENSSL extern pthread_mutex_t LOCK_des_key_file; #endif diff --git a/sql/mysqld.cc b/sql/mysqld.cc index cdd08be6573..8bafb15eeaa 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -16,6 +16,7 @@ #include "mysql_priv.h" #include <m_ctype.h> #include <my_dir.h> +#include <my_bit.h> #include "slave.h" #include "rpl_mi.h" #include "sql_repl.h" @@ -468,7 +469,7 @@ uint volatile thread_count, thread_running; ulonglong thd_startup_options; ulong back_log, connect_timeout, concurrency, server_id; ulong table_cache_size, table_def_size; -ulong thread_stack, what_to_log; +ulong what_to_log; ulong query_buff_size, slow_launch_time, slave_open_temp_tables; ulong open_files_limit, max_binlog_size, max_relay_log_size; ulong slave_net_timeout, slave_trans_retries; @@ -518,6 +519,7 @@ char mysql_real_data_home[FN_REFLEN], language[FN_REFLEN], reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN], *opt_init_file, *opt_tc_log_file, def_ft_boolean_syntax[sizeof(ft_boolean_syntax)]; +char mysql_unpacked_real_data_home[FN_REFLEN]; uint reg_ext_length; const key_map key_map_empty(0); key_map key_map_full(0); // Will be initialized later @@ -584,7 +586,8 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received, LOCK_global_system_variables, - LOCK_user_conn, LOCK_slave_list, LOCK_active_mi; + LOCK_user_conn, LOCK_slave_list, LOCK_active_mi, + LOCK_connection_count; /** The below lock protects access to two global server variables: max_prepared_stmt_count and prepared_stmt_count. These variables @@ -720,6 +723,11 @@ char *des_key_file; struct st_VioSSLFd *ssl_acceptor_fd; #endif /* HAVE_OPENSSL */ +/** + Number of currently active user connections. The variable is protected by + LOCK_connection_count. +*/ +uint connection_count= 0; /* Function declarations */ @@ -1341,6 +1349,7 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_bytes_sent); (void) pthread_mutex_destroy(&LOCK_bytes_received); (void) pthread_mutex_destroy(&LOCK_user_conn); + (void) pthread_mutex_destroy(&LOCK_connection_count); Events::destroy_mutexes(); #ifdef HAVE_OPENSSL (void) pthread_mutex_destroy(&LOCK_des_key_file); @@ -1783,6 +1792,11 @@ void unlink_thd(THD *thd) DBUG_ENTER("unlink_thd"); DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); thd->cleanup(); + + pthread_mutex_lock(&LOCK_connection_count); + --connection_count; + pthread_mutex_unlock(&LOCK_connection_count); + (void) pthread_mutex_lock(&LOCK_thread_count); thread_count--; delete thd; @@ -2379,7 +2393,7 @@ or misconfigured. This error can also be caused by malfunctioning hardware.\n", We will try our best to scrape up some info that will hopefully help diagnose\n\ the problem, but since we have already crashed, something is definitely wrong\n\ and this may fail.\n\n"); - fprintf(stderr, "key_buffer_size=%lu\n", + fprintf(stderr, "key_buffer_size=%lu\n", (ulong) dflt_key_cache->key_cache_mem_size); fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size); fprintf(stderr, "max_used_connections=%lu\n", max_used_connections); @@ -2415,7 +2429,7 @@ Attempting backtrace. You can use the following information to find out\n\ where mysqld died. If you see no messages after this, something went\n\ terribly wrong...\n"); print_stacktrace(thd ? (uchar*) thd->thread_stack : (uchar*) 0, - thread_stack); + my_thread_stack_size); } if (thd) { @@ -2512,10 +2526,6 @@ static void init_signals(void) struct sigaction sa; DBUG_ENTER("init_signals"); - if (test_flags & TEST_SIGINT) - { - my_sigset(thr_kill_signal, end_thread_signal); - } my_sigset(THR_SERVER_ALARM,print_signal_warning); // Should never be called! if (!(test_flags & TEST_NO_STACKTRACE) || (test_flags & TEST_CORE_ON_SIGNAL)) @@ -2552,7 +2562,6 @@ static void init_signals(void) (void) sigemptyset(&set); my_sigset(SIGPIPE,SIG_IGN); sigaddset(&set,SIGPIPE); - sigaddset(&set,SIGINT); #ifndef IGNORE_SIGHUP_SIGQUIT sigaddset(&set,SIGQUIT); sigaddset(&set,SIGHUP); @@ -2574,9 +2583,12 @@ static void init_signals(void) sigaddset(&set,THR_SERVER_ALARM); if (test_flags & TEST_SIGINT) { + my_sigset(thr_kill_signal, end_thread_signal); // May be SIGINT sigdelset(&set, thr_kill_signal); } + else + sigaddset(&set,SIGINT); sigprocmask(SIG_SETMASK,&set,NULL); pthread_sigmask(SIG_SETMASK,&set,NULL); DBUG_VOID_RETURN; @@ -2600,9 +2612,9 @@ static void start_signal_handler(void) Peculiar things with ia64 platforms - it seems we only have half the stack size in reality, so we have to double it here */ - pthread_attr_setstacksize(&thr_attr,thread_stack*2); + pthread_attr_setstacksize(&thr_attr,my_thread_stack_size*2); #else - pthread_attr_setstacksize(&thr_attr,thread_stack); + pthread_attr_setstacksize(&thr_attr,my_thread_stack_size); #endif #endif @@ -3453,6 +3465,7 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_global_read_lock, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_connection_count, MY_MUTEX_INIT_FAST); #ifdef HAVE_OPENSSL (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST); #ifndef HAVE_YASSL @@ -3797,6 +3810,13 @@ server."); using_update_log=1; } + /* call ha_init_key_cache() on all key caches to init them */ + process_key_caches(&ha_init_key_cache); + + /* Allow storage engine to give real error messages */ + if (ha_init_errors()) + DBUG_RETURN(1); + if (plugin_init(&defaults_argc, defaults_argv, (opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) | (opt_help ? PLUGIN_INIT_SKIP_INITIALIZATION : 0))) @@ -3963,9 +3983,6 @@ server."); if (opt_myisam_log) (void) mi_log(1); - /* call ha_init_key_cache() on all key caches to init them */ - process_key_caches(&ha_init_key_cache); - #if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && !defined(EMBEDDED_LIBRARY) if (locked_in_memory && !getuid()) { @@ -4155,9 +4172,9 @@ int main(int argc, char **argv) Peculiar things with ia64 platforms - it seems we only have half the stack size in reality, so we have to double it here */ - pthread_attr_setstacksize(&connection_attrib,thread_stack*2); + pthread_attr_setstacksize(&connection_attrib,my_thread_stack_size*2); #else - pthread_attr_setstacksize(&connection_attrib,thread_stack); + pthread_attr_setstacksize(&connection_attrib,my_thread_stack_size); #endif #ifdef HAVE_PTHREAD_ATTR_GETSTACKSIZE { @@ -4168,15 +4185,15 @@ int main(int argc, char **argv) stack_size/= 2; #endif /* We must check if stack_size = 0 as Solaris 2.9 can return 0 here */ - if (stack_size && stack_size < thread_stack) + if (stack_size && stack_size < my_thread_stack_size) { if (global_system_variables.log_warnings) sql_print_warning("Asked for %lu thread stack, but got %ld", - thread_stack, (long) stack_size); + my_thread_stack_size, (long) stack_size); #if defined(__ia64__) || defined(__ia64) - thread_stack= stack_size*2; + my_thread_stack_size= stack_size*2; #else - thread_stack= stack_size; + my_thread_stack_size= stack_size; #endif } } @@ -4700,6 +4717,11 @@ void create_thread_to_handle_connection(THD *thd) thread_count--; thd->killed= THD::KILL_CONNECTION; // Safety (void) pthread_mutex_unlock(&LOCK_thread_count); + + pthread_mutex_lock(&LOCK_connection_count); + --connection_count; + pthread_mutex_unlock(&LOCK_connection_count); + statistic_increment(aborted_connects,&LOCK_status); /* Can't use my_error() since store_globals has not been called. */ my_snprintf(error_message_buff, sizeof(error_message_buff), @@ -4739,15 +4761,34 @@ static void create_new_thread(THD *thd) if (protocol_version > 9) net->return_errno=1; - /* don't allow too many connections */ - if (thread_count - delayed_insert_threads >= max_connections+1 || abort_loop) + /* + Don't allow too many connections. We roughly check here that we allow + only (max_connections + 1) connections. + */ + + pthread_mutex_lock(&LOCK_connection_count); + + if (connection_count >= max_connections + 1 || abort_loop) { + pthread_mutex_unlock(&LOCK_connection_count); + DBUG_PRINT("error",("Too many connections")); close_connection(thd, ER_CON_COUNT_ERROR, 1); delete thd; DBUG_VOID_RETURN; } + + ++connection_count; + + if (connection_count > max_used_connections) + max_used_connections= connection_count; + + pthread_mutex_unlock(&LOCK_connection_count); + + /* Start a new thread to handle connection. */ + pthread_mutex_lock(&LOCK_thread_count); + /* The initialization of thread_id is done in create_embedded_thd() for the embedded library. @@ -4755,13 +4796,10 @@ static void create_new_thread(THD *thd) */ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; - /* Start a new thread to handle connection */ thread_count++; - if (thread_count - delayed_insert_threads > max_used_connections) - max_used_connections= thread_count - delayed_insert_threads; - thread_scheduler.add_connection(thd); + DBUG_VOID_RETURN; } #endif /* EMBEDDED_LIBRARY */ @@ -5416,7 +5454,7 @@ enum options_mysqld OPT_MAX_ERROR_COUNT, OPT_MULTI_RANGE_COUNT, OPT_MYISAM_DATA_POINTER_SIZE, OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, OPT_MYISAM_MAX_SORT_FILE_SIZE, OPT_MYISAM_SORT_BUFFER_SIZE, - OPT_MYISAM_USE_MMAP, + OPT_MYISAM_USE_MMAP, OPT_MYISAM_REPAIR_THREADS, OPT_MYISAM_STATS_METHOD, OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT, OPT_NET_READ_TIMEOUT, OPT_NET_WRITE_TIMEOUT, @@ -5431,7 +5469,7 @@ enum options_mysqld OPT_SORT_BUFFER, OPT_TABLE_OPEN_CACHE, OPT_TABLE_DEF_CACHE, OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE, OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK, - OPT_WAIT_TIMEOUT, OPT_MYISAM_REPAIR_THREADS, + OPT_WAIT_TIMEOUT, OPT_ERROR_LOG_FILE, OPT_DEFAULT_WEEK_FORMAT, OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS, @@ -6602,10 +6640,10 @@ The minimum value for this variable is 4096.", (uchar**) &opt_plugin_load, (uchar**) &opt_plugin_load, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"preload_buffer_size", OPT_PRELOAD_BUFFER_SIZE, - "The size of the buffer that is allocated when preloading indexes", - (uchar**) &global_system_variables.preload_buff_size, - (uchar**) &max_system_variables.preload_buff_size, 0, GET_ULONG, - REQUIRED_ARG, 32*1024L, 1024, 1024*1024*1024L, 0, 1, 0}, + "The size of the buffer that is allocated when preloading indexes", + (uchar**) &global_system_variables.preload_buff_size, + (uchar**) &max_system_variables.preload_buff_size, 0, GET_ULONG, + REQUIRED_ARG, 32*1024L, 1024, 1024*1024*1024L, 0, 1, 0}, {"query_alloc_block_size", OPT_QUERY_ALLOC_BLOCK_SIZE, "Allocation block size for query parsing and execution", (uchar**) &global_system_variables.query_alloc_block_size, @@ -6749,8 +6787,8 @@ The minimum value for this variable is 4096.", REQUIRED_ARG, 20, 1, 16384, 0, 1, 0}, #endif {"thread_stack", OPT_THREAD_STACK, - "The stack size for each thread.", (uchar**) &thread_stack, - (uchar**) &thread_stack, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK, + "The stack size for each thread.", (uchar**) &my_thread_stack_size, + (uchar**) &my_thread_stack_size, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK, 1024L*128L, ULONG_MAX, 0, 1024, 0}, { "time_format", OPT_TIME_FORMAT, "The TIME format (for future).", @@ -6764,12 +6802,12 @@ The minimum value for this variable is 4096.", (uchar**) &max_system_variables.tmp_table_size, 0, GET_ULL, REQUIRED_ARG, 16*1024*1024L, 1024, MAX_MEM_TABLE_SIZE, 0, 1, 0}, {"transaction_alloc_block_size", OPT_TRANS_ALLOC_BLOCK_SIZE, - "Allocation block size for various transaction-related structures", + "Allocation block size for transactions to be stored in binary log", (uchar**) &global_system_variables.trans_alloc_block_size, (uchar**) &max_system_variables.trans_alloc_block_size, 0, GET_ULONG, REQUIRED_ARG, QUERY_ALLOC_BLOCK_SIZE, 1024, ULONG_MAX, 0, 1024, 0}, {"transaction_prealloc_size", OPT_TRANS_PREALLOC_SIZE, - "Persistent buffer for various transaction-related structures", + "Persistent buffer for transactions to be stored in binary log", (uchar**) &global_system_variables.trans_prealloc_size, (uchar**) &max_system_variables.trans_prealloc_size, 0, GET_ULONG, REQUIRED_ARG, TRANS_ALLOC_PREALLOC_SIZE, 1024, ULONG_MAX, 0, 1024, 0}, @@ -7201,6 +7239,7 @@ SHOW_VAR status_vars[]= { {"Open_tables", (char*) &show_open_tables, SHOW_FUNC}, {"Opened_files", (char*) &my_file_total_opened, SHOW_LONG_NOFLUSH}, {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS}, + {"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS}, {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC}, #ifdef HAVE_QUERY_CACHE {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH}, @@ -7430,9 +7469,10 @@ static void mysql_init_variables(void) thread_cache.empty(); key_caches.empty(); if (!(dflt_key_cache= get_or_create_key_cache(default_key_cache_base.str, - default_key_cache_base.length))) + default_key_cache_base.length))) exit(1); - multi_keycache_init(); /* set key_cache_hash.default_value = dflt_key_cache */ + /* set key_cache_hash.default_value = dflt_key_cache */ + multi_keycache_init(); /* Set directory paths */ strmake(language, LANGUAGE, sizeof(language)-1); @@ -7447,7 +7487,7 @@ static void mysql_init_variables(void) master_password= master_host= 0; master_info_file= (char*) "master.info", relay_log_info_file= (char*) "relay-log.info"; - master_ssl_key= master_ssl_cert= master_ssl_ca= + master_ssl_key= master_ssl_cert= master_ssl_ca= master_ssl_capath= master_ssl_cipher= 0; report_user= report_password = report_host= 0; /* TO BE DELETED */ opt_relay_logname= opt_relaylog_index_name= 0; @@ -8108,7 +8148,7 @@ mysql_getopt_value(const char *keyname, uint key_length, } } } - return option->value; + return option->value; } @@ -8303,12 +8343,15 @@ static void fix_paths(void) pos[1]= 0; } convert_dirname(mysql_real_data_home,mysql_real_data_home,NullS); + (void) fn_format(buff, mysql_real_data_home, "", "", + (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS)); + (void) unpack_dirname(mysql_unpacked_real_data_home, buff); convert_dirname(language,language,NullS); (void) my_load_path(mysql_home,mysql_home,""); // Resolve current dir (void) my_load_path(mysql_real_data_home,mysql_real_data_home,mysql_home); (void) my_load_path(pidfile_name,pidfile_name,mysql_real_data_home); (void) my_load_path(opt_plugin_dir, opt_plugin_dir_ptr ? opt_plugin_dir_ptr : - get_relative_path(LIBDIR), mysql_home); + get_relative_path(PLUGINDIR), mysql_home); opt_plugin_dir_ptr= opt_plugin_dir; char *sharedir=get_relative_path(SHAREDIR); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index b8bdb604eea..e5709f418f7 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2142,9 +2142,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, quick=0; needed_reg.clear_all(); quick_keys.clear_all(); - if ((specialflag & SPECIAL_SAFE_MODE) && ! force_quick_range || - !limit) - DBUG_RETURN(0); /* purecov: inspected */ if (keys_to_use.is_clear_all()) DBUG_RETURN(0); records= head->file->stats.records; @@ -4315,7 +4312,6 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info, } info->out_rows *= selectivity_mult; - DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost)); if (is_cpk_scan) { @@ -5726,52 +5722,70 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, field->type() == MYSQL_TYPE_DATETIME)) field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; err= value->save_in_field_no_warnings(field, 1); - if (err > 0 && field->cmp_type() != value->result_type()) + if (err > 0) { - if ((type == Item_func::EQ_FUNC || type == Item_func::EQUAL_FUNC) && - value->result_type() == item_cmp_type(field->result_type(), - value->result_type())) - + if (field->cmp_type() != value->result_type()) { - tree= new (alloc) SEL_ARG(field, 0, 0); - tree->type= SEL_ARG::IMPOSSIBLE; - goto end; - } - else - { - /* - TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE - for the cases like int_field > 999999999999999999999999 as well. - */ - tree= 0; - if (err == 3 && field->type() == FIELD_TYPE_DATE && - (type == Item_func::GT_FUNC || type == Item_func::GE_FUNC || - type == Item_func::LT_FUNC || type == Item_func::LE_FUNC) ) + if ((type == Item_func::EQ_FUNC || type == Item_func::EQUAL_FUNC) && + value->result_type() == item_cmp_type(field->result_type(), + value->result_type())) + { + tree= new (alloc) SEL_ARG(field, 0, 0); + tree->type= SEL_ARG::IMPOSSIBLE; + goto end; + } + else { /* - We were saving DATETIME into a DATE column, the conversion went ok - but a non-zero time part was cut off. + TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE + for the cases like int_field > 999999999999999999999999 as well. + */ + tree= 0; + if (err == 3 && field->type() == FIELD_TYPE_DATE && + (type == Item_func::GT_FUNC || type == Item_func::GE_FUNC || + type == Item_func::LT_FUNC || type == Item_func::LE_FUNC) ) + { + /* + We were saving DATETIME into a DATE column, the conversion went ok + but a non-zero time part was cut off. - In MySQL's SQL dialect, DATE and DATETIME are compared as datetime - values. Index over a DATE column uses DATE comparison. Changing - from one comparison to the other is possible: + In MySQL's SQL dialect, DATE and DATETIME are compared as datetime + values. Index over a DATE column uses DATE comparison. Changing + from one comparison to the other is possible: - datetime(date_col)< '2007-12-10 12:34:55' -> date_col<='2007-12-10' - datetime(date_col)<='2007-12-10 12:34:55' -> date_col<='2007-12-10' + datetime(date_col)< '2007-12-10 12:34:55' -> date_col<='2007-12-10' + datetime(date_col)<='2007-12-10 12:34:55' -> date_col<='2007-12-10' - datetime(date_col)> '2007-12-10 12:34:55' -> date_col>='2007-12-10' - datetime(date_col)>='2007-12-10 12:34:55' -> date_col>='2007-12-10' + datetime(date_col)> '2007-12-10 12:34:55' -> date_col>='2007-12-10' + datetime(date_col)>='2007-12-10 12:34:55' -> date_col>='2007-12-10' - but we'll need to convert '>' to '>=' and '<' to '<='. This will - be done together with other types at the end of this function - (grep for field_is_equal_to_item) - */ + but we'll need to convert '>' to '>=' and '<' to '<='. This will + be done together with other types at the end of this function + (grep for field_is_equal_to_item) + */ + } + else + goto end; } - else - goto end; } - } - if (err < 0) + + /* + guaranteed at this point: err > 0; field and const of same type + If an integer got bounded (e.g. to within 0..255 / -128..127) + for < or >, set flags as for <= or >= (no NEAR_MAX / NEAR_MIN) + */ + else if (err == 1 && field->result_type() == INT_RESULT) + { + if (type == Item_func::LT_FUNC && (value->val_int() > 0)) + type = Item_func::LE_FUNC; + else if (type == Item_func::GT_FUNC && + !((Field_num*)field)->unsigned_flag && + !((Item_int*)value)->unsigned_flag && + (value->val_int() < 0)) + type = Item_func::GE_FUNC; + } + } + else if (err < 0) { field->table->in_use->variables.sql_mode= orig_sql_mode; /* This happens when we try to insert a NULL field in a not null column */ diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 3b580422da1..8feac884c77 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -419,41 +419,167 @@ char *partition_info::has_unique_names() /* - Check that all partitions use the same storage engine. - This is currently a limitation in this version. + Check that the partition/subpartition is setup to use the correct + storage engine + SYNOPSIS + check_engine_condition() + p_elem Partition element + table_engine_set Have user specified engine on table level + inout::engine_type Current engine used + inout::first Is it first partition + RETURN VALUE + TRUE Failed check + FALSE Ok + DESCRIPTION + Specified engine for table and partitions p0 and pn + Must be correct both on CREATE and ALTER commands + table p0 pn res (0 - OK, 1 - FAIL) + - - - 0 + - - x 1 + - x - 1 + - x x 0 + x - - 0 + x - x 0 + x x - 0 + x x x 0 + i.e: + - All subpartitions must use the same engine + AND it must be the same as the partition. + - All partitions must use the same engine + AND it must be the same as the table. + - if one does NOT specify an engine on the table level + then one must either NOT specify any engine on any + partition/subpartition OR for ALL partitions/subpartitions + Note: + When ALTER a table, the engines are already set for all levels + (table, all partitions and subpartitions). So if one want to + change the storage engine, one must specify it on the table level + +*/ + +static bool check_engine_condition(partition_element *p_elem, + bool table_engine_set, + handlerton **engine_type, + bool *first) +{ + DBUG_ENTER("check_engine_condition"); + + DBUG_PRINT("enter", ("p_eng %s t_eng %s t_eng_set %u first %u state %u", + ha_resolve_storage_engine_name(p_elem->engine_type), + ha_resolve_storage_engine_name(*engine_type), + table_engine_set, *first, p_elem->part_state)); + if (*first && !table_engine_set) + { + *engine_type= p_elem->engine_type; + DBUG_PRINT("info", ("setting table_engine = %s", + ha_resolve_storage_engine_name(*engine_type))); + } + *first= FALSE; + if ((table_engine_set && + (p_elem->engine_type != (*engine_type) && + p_elem->engine_type)) || + (!table_engine_set && + p_elem->engine_type != (*engine_type))) + { + DBUG_RETURN(TRUE); + } + else + { + DBUG_RETURN(FALSE); + } +} + +/* + Check engine mix that it is correct + Current limitation is that all partitions and subpartitions + must use the same storage engine. SYNOPSIS check_engine_mix() - engine_array An array of engine identifiers - no_parts Total number of partitions - + inout::engine_type Current engine used + table_engine_set Have user specified engine on table level RETURN VALUE - TRUE Error, mixed engines - FALSE Ok, no mixed engines + TRUE Error, mixed engines + FALSE Ok, no mixed engines DESCRIPTION Current check verifies only that all handlers are the same. Later this check will be more sophisticated. + (specified partition handler ) specified table handler + (NDB, NDB) NDB OK + (MYISAM, MYISAM) - OK + (MYISAM, -) - NOT OK + (MYISAM, -) MYISAM OK + (- , MYISAM) - NOT OK + (- , -) MYISAM OK + (-,-) - OK + (NDB, MYISAM) * NOT OK */ -bool partition_info::check_engine_mix(handlerton **engine_array, uint no_parts) +bool partition_info::check_engine_mix(handlerton *engine_type, + bool table_engine_set) { - uint i= 0; + handlerton *old_engine_type= engine_type; + bool first= TRUE; + uint no_parts= partitions.elements; DBUG_ENTER("partition_info::check_engine_mix"); - - do + DBUG_PRINT("info", ("in: engine_type = %s, table_engine_set = %u", + ha_resolve_storage_engine_name(engine_type), + table_engine_set)); + if (no_parts) { - if (engine_array[i] != engine_array[0]) + List_iterator<partition_element> part_it(partitions); + uint i= 0; + do { - my_error(ER_MIX_HANDLER_ERROR, MYF(0)); - DBUG_RETURN(TRUE); - } - } while (++i < no_parts); - if (engine_array[0]->flags & HTON_NO_PARTITION) + partition_element *part_elem= part_it++; + DBUG_PRINT("info", ("part = %d engine = %s table_engine_set %u", + i, ha_resolve_storage_engine_name(part_elem->engine_type), + table_engine_set)); + if (is_sub_partitioned() && + part_elem->subpartitions.elements) + { + uint no_subparts= part_elem->subpartitions.elements; + uint j= 0; + List_iterator<partition_element> sub_it(part_elem->subpartitions); + do + { + partition_element *sub_elem= sub_it++; + DBUG_PRINT("info", ("sub = %d engine = %s table_engie_set %u", + j, ha_resolve_storage_engine_name(sub_elem->engine_type), + table_engine_set)); + if (check_engine_condition(sub_elem, table_engine_set, + &engine_type, &first)) + goto error; + } while (++j < no_subparts); + /* ensure that the partition also has correct engine */ + if (check_engine_condition(part_elem, table_engine_set, + &engine_type, &first)) + goto error; + } + else if (check_engine_condition(part_elem, table_engine_set, + &engine_type, &first)) + goto error; + } while (++i < no_parts); + } + DBUG_PRINT("info", ("engine_type = %s", + ha_resolve_storage_engine_name(engine_type))); + if (!engine_type) + engine_type= old_engine_type; + if (engine_type->flags & HTON_NO_PARTITION) { my_error(ER_PARTITION_MERGE_ERROR, MYF(0)); DBUG_RETURN(TRUE); } + DBUG_PRINT("info", ("out: engine_type = %s", + ha_resolve_storage_engine_name(engine_type))); + DBUG_ASSERT(engine_type != partition_hton); DBUG_RETURN(FALSE); +error: + /* + Mixed engines not yet supported but when supported it will need + the partition handler + */ + DBUG_RETURN(TRUE); } @@ -726,13 +852,15 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, handler *file, HA_CREATE_INFO *info, bool check_partition_function) { - handlerton **engine_array= NULL; - uint part_count= 0; + handlerton *table_engine= default_engine_type; uint i, tot_partitions; - bool result= TRUE; + bool result= TRUE, table_engine_set; char *same_name; DBUG_ENTER("partition_info::check_partition_info"); + DBUG_ASSERT(default_engine_type != partition_hton); + DBUG_PRINT("info", ("default table_engine = %s", + ha_resolve_storage_engine_name(table_engine))); if (check_partition_function) { int err= 0; @@ -777,44 +905,89 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); goto end; } + /* + if NOT specified ENGINE = <engine>: + If Create, always use create_info->db_type + else, use previous tables db_type + either ALL or NONE partition should be set to + default_engine_type when not table_engine_set + Note: after a table is created its storage engines for + the table and all partitions/subpartitions are set. + So when ALTER it is already set on table level + */ + if (info && info->used_fields & HA_CREATE_USED_ENGINE) + { + table_engine_set= TRUE; + table_engine= info->db_type; + /* if partition_hton, use thd->lex->create_info */ + if (table_engine == partition_hton) + table_engine= thd->lex->create_info.db_type; + DBUG_ASSERT(table_engine != partition_hton); + DBUG_PRINT("info", ("Using table_engine = %s", + ha_resolve_storage_engine_name(table_engine))); + } + else + { + table_engine_set= FALSE; + if (thd->lex->sql_command != SQLCOM_CREATE_TABLE) + { + table_engine_set= TRUE; + DBUG_PRINT("info", ("No create, table_engine = %s", + ha_resolve_storage_engine_name(table_engine))); + DBUG_ASSERT(table_engine && table_engine != partition_hton); + } + } + if ((same_name= has_unique_names())) { my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name); goto end; } - engine_array= (handlerton**)my_malloc(tot_partitions * sizeof(handlerton *), - MYF(MY_WME)); - if (unlikely(!engine_array)) - goto end; i= 0; { List_iterator<partition_element> part_it(partitions); + uint no_parts_not_set= 0; + uint prev_no_subparts_not_set= no_subparts + 1; do { partition_element *part_elem= part_it++; - if (part_elem->engine_type == NULL) - part_elem->engine_type= default_engine_type; - if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) - part_elem->data_file_name= part_elem->index_file_name= 0; +#ifdef HAVE_READLINK + if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)) +#endif + { + if (part_elem->data_file_name) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "DATA DIRECTORY option ignored"); + if (part_elem->index_file_name) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "INDEX DIRECTORY option ignored"); + part_elem->data_file_name= part_elem->index_file_name= NULL; + } if (!is_sub_partitioned()) { + if (part_elem->engine_type == NULL) + { + no_parts_not_set++; + part_elem->engine_type= default_engine_type; + } if (check_table_name(part_elem->partition_name, strlen(part_elem->partition_name))) { my_error(ER_WRONG_PARTITION_NAME, MYF(0)); goto end; } - DBUG_PRINT("info", ("engine = %d", - ha_legacy_type(part_elem->engine_type))); - engine_array[part_count++]= part_elem->engine_type; + DBUG_PRINT("info", ("part = %d engine = %s", + i, ha_resolve_storage_engine_name(part_elem->engine_type))); } else { uint j= 0; + uint no_subparts_not_set= 0; List_iterator<partition_element> sub_it(part_elem->subpartitions); + partition_element *sub_elem; do { - partition_element *sub_elem= sub_it++; + sub_elem= sub_it++; if (check_table_name(sub_elem->partition_name, strlen(sub_elem->partition_name))) { @@ -822,19 +995,65 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, goto end; } if (sub_elem->engine_type == NULL) - sub_elem->engine_type= default_engine_type; - DBUG_PRINT("info", ("engine = %u", - ha_legacy_type(sub_elem->engine_type))); - engine_array[part_count++]= sub_elem->engine_type; + { + if (part_elem->engine_type != NULL) + sub_elem->engine_type= part_elem->engine_type; + else + { + sub_elem->engine_type= default_engine_type; + no_subparts_not_set++; + } + } + DBUG_PRINT("info", ("part = %d sub = %d engine = %s", i, j, + ha_resolve_storage_engine_name(sub_elem->engine_type))); } while (++j < no_subparts); + + if (prev_no_subparts_not_set == (no_subparts + 1) && + (no_subparts_not_set == 0 || no_subparts_not_set == no_subparts)) + prev_no_subparts_not_set= no_subparts_not_set; + + if (!table_engine_set && + prev_no_subparts_not_set != no_subparts_not_set) + { + DBUG_PRINT("info", ("no_subparts_not_set = %u no_subparts = %u", + no_subparts_not_set, no_subparts)); + my_error(ER_MIX_HANDLER_ERROR, MYF(0)); + goto end; + } + + if (part_elem->engine_type == NULL) + { + if (no_subparts_not_set == 0) + part_elem->engine_type= sub_elem->engine_type; + else + { + no_parts_not_set++; + part_elem->engine_type= default_engine_type; + } + } } } while (++i < no_parts); + if (!table_engine_set && + no_parts_not_set != 0 && + no_parts_not_set != no_parts) + { + DBUG_PRINT("info", ("no_parts_not_set = %u no_parts = %u", + no_parts_not_set, no_subparts)); + my_error(ER_MIX_HANDLER_ERROR, MYF(0)); + goto end; + } } - if (unlikely(partition_info::check_engine_mix(engine_array, part_count))) + if (unlikely(check_engine_mix(table_engine, table_engine_set))) + { + my_error(ER_MIX_HANDLER_ERROR, MYF(0)); goto end; + } + DBUG_ASSERT(table_engine != partition_hton && + default_engine_type == table_engine); if (eng_type) - *eng_type= (handlerton*)engine_array[0]; + *eng_type= table_engine; + /* We need to check all constant expressions that they are of the correct @@ -850,7 +1069,6 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } result= FALSE; end: - my_free((char*)engine_array,MYF(MY_ALLOW_ZERO_PTR)); DBUG_RETURN(result); } @@ -1047,4 +1265,60 @@ error: mem_alloc_error(size); DBUG_RETURN(TRUE); } + + +/* + Check if path does not contain mysql data home directory + for partition elements with data directory and index directory + + SYNOPSIS + check_partition_dirs() + part_info partition_info struct + + RETURN VALUES + 0 ok + 1 error +*/ + +bool check_partition_dirs(partition_info *part_info) +{ + if (!part_info) + return 0; + + partition_element *part_elem; + List_iterator<partition_element> part_it(part_info->partitions); + while ((part_elem= part_it++)) + { + if (part_elem->subpartitions.elements) + { + List_iterator<partition_element> sub_it(part_elem->subpartitions); + partition_element *subpart_elem; + while ((subpart_elem= sub_it++)) + { + if (test_if_data_home_dir(subpart_elem->data_file_name)) + goto dd_err; + if (test_if_data_home_dir(subpart_elem->index_file_name)) + goto id_err; + } + } + else + { + if (test_if_data_home_dir(part_elem->data_file_name)) + goto dd_err; + if (test_if_data_home_dir(part_elem->index_file_name)) + goto id_err; + } + } + return 0; + +dd_err: + my_error(ER_WRONG_ARGUMENTS,MYF(0),"DATA DIRECTORY"); + return 1; + +id_err: + my_error(ER_WRONG_ARGUMENTS,MYF(0),"INDEX DIRECTORY"); + return 1; +} + + #endif /* WITH_PARTITION_STORAGE_ENGINE */ diff --git a/sql/partition_info.h b/sql/partition_info.h index b7d13e188f3..2af7fa1717c 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -269,7 +269,7 @@ public: bool set_up_defaults_for_partitioning(handler *file, HA_CREATE_INFO *info, uint start_no); char *has_unique_names(); - static bool check_engine_mix(handlerton **engine_array, uint no_parts); + bool check_engine_mix(handlerton *engine_type, bool default_engine); bool check_range_constants(); bool check_list_constants(); bool check_partition_info(THD *thd, handlerton **eng_type, @@ -290,6 +290,7 @@ private: }; uint32 get_next_partition_id_range(struct st_partition_iter* part_iter); +bool check_partition_dirs(partition_info *part_info); /* Initialize the iterator to return a single partition with given part_id */ diff --git a/sql/procedure.h b/sql/procedure.h index 4578fcb87f1..ceb586766b1 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -144,7 +144,7 @@ public: virtual int send_row(List<Item> &fields)=0; virtual bool change_columns(List<Item> &fields)=0; virtual void update_refs(void) {} - virtual bool end_of_records() { return 0; } + virtual int end_of_records() { return 0; } }; Procedure *setup_procedure(THD *thd,ORDER *proc_param,select_result *result, diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index a8953217ce1..d7e783f534f 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -118,11 +118,14 @@ void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status) } -#define get_object(p, obj) \ +#define get_object(p, obj, msg) \ {\ uint len = (uint)*p++; \ if (p + len > p_end || len >= sizeof(obj)) \ + {\ + errmsg= msg;\ goto err; \ + }\ strmake(obj,(char*) p,len); \ p+= len; \ }\ @@ -168,6 +171,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length) int res; SLAVE_INFO *si; uchar *p= packet, *p_end= packet + packet_length; + const char *errmsg= "Wrong parameters to function register_slave"; if (check_access(thd, REPL_SLAVE_ACL, any_db,0,0,0,0)) return 1; @@ -176,9 +180,9 @@ int register_slave(THD* thd, uchar* packet, uint packet_length) thd->server_id= si->server_id= uint4korr(p); p+= 4; - get_object(p,si->host); - get_object(p,si->user); - get_object(p,si->password); + get_object(p,si->host, "Failed to register slave: too long 'report-host'"); + get_object(p,si->user, "Failed to register slave: too long 'report-user'"); + get_object(p,si->password, "Failed to register slave; too long 'report-password'"); if (p+10 > p_end) goto err; si->port= uint2korr(p); @@ -197,8 +201,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length) err: my_free(si, MYF(MY_WME)); - my_message(ER_UNKNOWN_ERROR, "Wrong parameters to function register_slave", - MYF(0)); + my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */ err2: return 1; } diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 1165164fd88..5e46837e948 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -29,9 +29,9 @@ int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, Master_info::Master_info() :Slave_reporting_capability("I/O"), - ssl(0), fd(-1), io_thd(0), inited(0), + ssl(0), ssl_verify_server_cert(0), fd(-1), io_thd(0), inited(0), abort_slave(0),slave_running(0), - ssl_verify_server_cert(0), slave_run_id(0) + slave_run_id(0) { host[0] = 0; user[0] = 0; password[0] = 0; ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index bd8246b066f..b6dfd1a1dc8 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -33,12 +33,12 @@ Relay_log_info::Relay_log_info() :Slave_reporting_capability("SQL"), no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id), info_fd(-1), cur_log_fd(-1), save_temporary_tables(0), - group_relay_log_pos(0), event_relay_log_pos(0), #if HAVE_purify is_fake(FALSE), #endif - cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0), - ignore_log_space_limit(0), last_master_timestamp(0), slave_skip_counter(0), + cur_log_old_open_count(0), group_relay_log_pos(0), event_relay_log_pos(0), + group_master_log_pos(0), log_space_total(0), ignore_log_space_limit(0), + last_master_timestamp(0), slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0), sql_thd(0), inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE), until_log_pos(0), retried_trans(0), @@ -955,6 +955,11 @@ err: Check if condition stated in UNTIL clause of START SLAVE is reached. SYNOPSYS Relay_log_info::is_until_satisfied() + master_beg_pos position of the beginning of to be executed event + (not log_pos member of the event that points to the + beginning of the following event) + + DESCRIPTION Checks if UNTIL condition is reached. Uses caching result of last comparison of current log file name and target log file name. So cached @@ -979,7 +984,7 @@ err: false - condition not met */ -bool Relay_log_info::is_until_satisfied() +bool Relay_log_info::is_until_satisfied(my_off_t master_beg_pos) { const char *log_name; ulonglong log_pos; @@ -990,7 +995,7 @@ bool Relay_log_info::is_until_satisfied() if (until_condition == UNTIL_MASTER_POS) { log_name= group_master_log_name; - log_pos= group_master_log_pos; + log_pos= master_beg_pos; } else { /* until_condition == UNTIL_RELAY_POS */ diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 36daffae1af..e13ea93842c 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -296,7 +296,7 @@ public: void close_temporary_tables(); /* Check if UNTIL condition is satisfied. See slave.cc for more. */ - bool is_until_satisfied(); + bool is_until_satisfied(my_off_t master_beg_pos); inline ulonglong until_pos() { return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos : diff --git a/sql/set_var.cc b/sql/set_var.cc index 0d1d8b92d3b..99931d7bafc 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -137,7 +137,8 @@ static void fix_trans_mem_root(THD *thd, enum_var_type type); static void fix_server_id(THD *thd, enum_var_type type); static ulonglong fix_unsigned(THD *, ulonglong, const struct my_option *); static bool get_unsigned(THD *thd, set_var *var); -static void throw_bounds_warning(THD *thd, const char *name, ulonglong num); +bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd, + const char *name, longlong val); static KEY_CACHE *create_key_cache(const char *name, uint length); void fix_sql_mode_var(THD *thd, enum_var_type type); static uchar *get_error_count(THD *thd); @@ -809,7 +810,7 @@ static SHOW_VAR fixed_vars[]= { #ifdef HAVE_THR_SETCONCURRENCY {"thread_concurrency", (char*) &concurrency, SHOW_LONG}, #endif - {"thread_stack", (char*) &thread_stack, SHOW_LONG}, + {"thread_stack", (char*) &my_thread_stack_size, SHOW_LONG}, }; @@ -1274,13 +1275,29 @@ static void fix_server_id(THD *thd, enum_var_type type) } -static void throw_bounds_warning(THD *thd, const char *name, ulonglong num) +bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd, + const char *name, longlong val) { - char buf[22]; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), name, - ullstr(num, buf)); + if (fixed) + { + char buf[22]; + + if (unsignd) + ullstr((ulonglong) val, buf); + else + llstr(val, buf); + + if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) + { + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf); + return TRUE; + } + + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER(ER_TRUNCATED_WRONG_VALUE), name, buf); + } + return FALSE; } static ulonglong fix_unsigned(THD *thd, ulonglong num, @@ -1289,8 +1306,7 @@ static ulonglong fix_unsigned(THD *thd, ulonglong num, my_bool fixed= FALSE; ulonglong out= getopt_ull_limit_value(num, option_limits, &fixed); - if (fixed) - throw_bounds_warning(thd, option_limits->name, num); + throw_bounds_warning(thd, fixed, TRUE, option_limits->name, (longlong) num); return out; } @@ -1333,7 +1349,8 @@ bool sys_var_long_ptr_global::update(THD *thd, set_var *var) if (tmp > ULONG_MAX) { tmp= ULONG_MAX; - throw_bounds_warning(thd, name, var->save_result.ulonglong_value); + throw_bounds_warning(thd, TRUE, TRUE, name, + (longlong) var->save_result.ulonglong_value); } #endif *value= (ulong) tmp; @@ -1422,7 +1439,7 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var) /* Don't use bigger value than given with --maximum-variable-name=.. */ if ((ulong) tmp > max_system_variables.*offset) { - throw_bounds_warning(thd, name, tmp); + throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) tmp); tmp= max_system_variables.*offset; } @@ -1432,7 +1449,7 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var) else if (tmp > ULONG_MAX) { tmp= ULONG_MAX; - throw_bounds_warning(thd, name, var->save_result.ulonglong_value); + throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) var->save_result.ulonglong_value); } #endif diff --git a/sql/set_var.h b/sql/set_var.h index 46fe7755baf..b33a3a968bb 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -73,8 +73,8 @@ public: bool no_support_one_shot; sys_var(const char *name_arg, sys_after_update_func func= NULL, Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG) - :name(name_arg), after_update(func), binlog_status(binlog_status_arg) - , no_support_one_shot(1) + :name(name_arg), after_update(func), no_support_one_shot(1), + binlog_status(binlog_status_arg) {} virtual ~sys_var() {} void chain_sys_var(sys_var_chain *chain_arg) diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index ee3a7e6080a..894e2094968 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -2146,14 +2146,14 @@ ER_WRONG_SUB_KEY cze "Chybn-Bá podèást klíèe -- není to øetìzec nebo je del¹í ne¾ délka èásti klíèe" dan "Forkert indeksdel. Den anvendte nøgledel er ikke en streng eller længden er større end nøglelængden" nla "Foutief sub-gedeelte van de zoeksleutel. De gebruikte zoeksleutel is geen onderdeel van een string of of de gebruikte lengte is langer dan de zoeksleutel" - eng "Incorrect sub part key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique sub keys" + eng "Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys" est "Vigane võtme osa. Kasutatud võtmeosa ei ole string tüüpi, määratud pikkus on pikem kui võtmeosa või tabelihandler ei toeta seda tüüpi võtmeid" fre "Mauvaise sous-clef. Ce n'est pas un 'string' ou la longueur dépasse celle définie dans la clef" ger "Falscher Unterteilschlüssel. Der verwendete Schlüsselteil ist entweder kein String, die verwendete Länge ist länger als der Teilschlüssel oder die Speicher-Engine unterstützt keine Unterteilschlüssel" greek "ÅóöáëìÝíï sub part key. Ôï ÷ñçóéìïðïéïýìåíï key part äåí åßíáé string Þ ôï ìÞêïò ôïõ åßíáé ìåãáëýôåñï" hun "Rossz alkulcs. A hasznalt kulcsresz nem karaktersorozat vagy hosszabb, mint a kulcsresz" ita "Sotto-parte della chiave errata. La parte di chiave utilizzata non e` una stringa o la lunghezza e` maggiore della parte di chiave." - jpn "Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part" + jpn "Incorrect prefix key; the used key part isn't a string or the used length is longer than the key part" kor "ºÎÁ¤È®ÇÑ ¼¹ö ÆÄÆ® Ű. »ç¿ëµÈ Ű ÆÄÆ®°¡ ½ºÆ®¸µÀÌ ¾Æ´Ï°Å³ª Ű ÆÄÆ®ÀÇ ±æÀ̰¡ ³Ê¹« ±é´Ï´Ù." nor "Feil delnøkkel. Den brukte delnøkkelen er ikke en streng eller den oppgitte lengde er lengre enn nøkkel lengden" norwegian-ny "Feil delnykkel. Den brukte delnykkelen er ikkje ein streng eller den oppgitte lengda er lengre enn nykkellengden" @@ -2162,7 +2162,7 @@ ER_WRONG_SUB_KEY rum "Componentul cheii este incorrect. Componentul folosit al cheii nu este un sir sau lungimea folosita este mai lunga decit lungimea cheii" rus "îÅËÏÒÒÅËÔÎÁÑ ÞÁÓÔØ ËÌÀÞÁ. éÓÐÏÌØÚÕÅÍÁÑ ÞÁÓÔØ ËÌÀÞÁ ÎÅ Ñ×ÌÑÅÔÓÑ ÓÔÒÏËÏÊ, ÕËÁÚÁÎÎÁÑ ÄÌÉÎÁ ÂÏÌØÛÅ, ÞÅÍ ÄÌÉÎÁ ÞÁÓÔÉ ËÌÀÞÁ, ÉÌÉ ÏÂÒÁÂÏÔÞÉË ÔÁÂÌÉÃÙ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÕÎÉËÁÌØÎÙÅ ÞÁÓÔÉ ËÌÀÞÁ" serbian "Pogrešan pod-kljuè dela kljuèa. Upotrebljeni deo kljuèa nije string, upotrebljena dužina je veæa od dela kljuèa ili handler tabela ne podržava jedinstvene pod-kljuèeve" - slo "Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part" + slo "Incorrect prefix key; the used key part isn't a string or the used length is longer than the key part" spa "Parte de la clave es erronea. Una parte de la clave no es una cadena o la longitud usada es tan grande como la parte de la clave" swe "Felaktig delnyckel. Nyckeldelen är inte en sträng eller den angivna längden är längre än kolumnlängden" ukr "îÅצÒÎÁ ÞÁÓÔÉÎÁ ËÌÀÞÁ. ÷ÉËÏÒÉÓÔÁÎÁ ÞÁÓÔÉÎÁ ËÌÀÞÁ ÎÅ ¤ ÓÔÒÏËÏÀ, ÚÁÄÏ×ÇÁ ÁÂÏ ×ËÁÚ¦×ÎÉË ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ ÕΦËÁÌØÎÉÈ ÞÁÓÔÉÎ ËÌÀÞÅÊ" @@ -5523,6 +5523,7 @@ ER_M_BIGGER_THAN_D 42000 S1009 ger "Für FLOAT(M,D), DOUBLE(M,D) oder DECIMAL(M,D) muss M >= D sein (Feld '%-.192s')" ER_WRONG_LOCK_OF_SYSTEM_TABLE eng "You can't combine write-locking of system tables with other tables or lock types" + ger "Sie können Schreibsperren auf der Systemtabelle nicht mit anderen Tabellen kombinieren" ER_CONNECT_TO_FOREIGN_DATA_SOURCE eng "Unable to connect to foreign data source: %.64s" ger "Kann nicht mit Fremddatenquelle verbinden: %.64s" @@ -5584,8 +5585,7 @@ ER_VIEW_OTHER_USER eng "You need the SUPER privilege for creation view with '%-.192s'@'%-.192s' definer" ger "Sie brauchen die SUPER-Berechtigung, um einen View mit dem Definierer '%-.192s'@'%-.192s' zu erzeugen" ER_NO_SUCH_USER - eng "There is no '%-.64s'@'%-.64s' registered" - ger "'%-.64s'@'%-.64s' ist nicht registriert" + eng "The user specified as a definer ('%-.64s'@'%-.64s') does not exist" ER_FORBID_SCHEMA_CHANGE eng "Changing schema from '%-.192s' to '%-.192s' is not allowed." ger "Wechsel des Schemas von '%-.192s' auf '%-.192s' ist nicht erlaubt" @@ -6121,3 +6121,8 @@ ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT eng "The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement." ER_SLAVE_CORRUPT_EVENT eng "Corrupted replication event was detected" +ER_LOAD_DATA_INVALID_COLUMN + eng "Invalid column reference (%-.64s) in LOAD DATA" + +ER_LOG_PURGE_NO_FILE + eng "Being purged log %s was not found" diff --git a/sql/slave.cc b/sql/slave.cc index b333b336a25..dcc808625c0 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -415,7 +415,20 @@ terminate_slave_thread(THD *thd, while (*slave_running) // Should always be true { DBUG_PRINT("loop", ("killing slave thread")); - KICK_SLAVE(thd); + + pthread_mutex_lock(&thd->LOCK_delete); +#ifndef DONT_USE_THR_ALARM + /* + Error codes from pthread_kill are: + EINVAL: invalid signal number (can't happen) + ESRCH: thread already killed (can happen, should be ignored) + */ + IF_DBUG(int err= ) pthread_kill(thd->real_id, thr_client_alarm); + DBUG_ASSERT(err != EINVAL); +#endif + thd->awake(THD::NOT_KILLED); + pthread_mutex_unlock(&thd->LOCK_delete); + /* There is a small chance that slave thread might miss the first alarm. To protect againts it, resend the signal until it reacts @@ -2006,28 +2019,6 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) wait for something for example inside of next_event(). */ pthread_mutex_lock(&rli->data_lock); - /* - This tests if the position of the end of the last previous executed event - hits the UNTIL barrier. - We would prefer to test if the position of the start (or possibly) end of - the to-be-read event hits the UNTIL barrier, this is different if there - was an event ignored by the I/O thread just before (BUG#13861 to be - fixed). - */ - if (rli->until_condition!=Relay_log_info::UNTIL_NONE && - rli->is_until_satisfied()) - { - char buf[22]; - sql_print_information("Slave SQL thread stopped because it reached its" - " UNTIL position %s", llstr(rli->until_pos(), buf)); - /* - Setting abort_slave flag because we do not want additional message about - error in query execution to be printed. - */ - rli->abort_slave= 1; - pthread_mutex_unlock(&rli->data_lock); - DBUG_RETURN(1); - } Log_event * ev = next_event(rli); @@ -2041,7 +2032,30 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) } if (ev) { - int exec_res= apply_event_and_update_pos(ev, thd, rli, TRUE); + int exec_res; + + /* + This tests if the position of the beginning of the current event + hits the UNTIL barrier. + */ + if (rli->until_condition != Relay_log_info::UNTIL_NONE && + rli->is_until_satisfied((rli->is_in_group() || !ev->log_pos) ? + rli->group_master_log_pos : + ev->log_pos - ev->data_written)) + { + char buf[22]; + sql_print_information("Slave SQL thread stopped because it reached its" + " UNTIL position %s", llstr(rli->until_pos(), buf)); + /* + Setting abort_slave flag because we do not want additional message about + error in query execution to be printed. + */ + rli->abort_slave= 1; + pthread_mutex_unlock(&rli->data_lock); + delete ev; + DBUG_RETURN(1); + } + exec_res= apply_event_and_update_pos(ev, thd, rli, TRUE); /* Format_description_log_event should not be deleted because it will be @@ -2677,6 +2691,22 @@ Slave SQL thread aborted. Can't execute init_slave query"); } } + /* + First check until condition - probably there is nothing to execute. We + do not want to wait for next event in this case. + */ + pthread_mutex_lock(&rli->data_lock); + if (rli->until_condition != Relay_log_info::UNTIL_NONE && + rli->is_until_satisfied(rli->group_master_log_pos)) + { + char buf[22]; + sql_print_information("Slave SQL thread stopped because it reached its" + " UNTIL position %s", llstr(rli->until_pos(), buf)); + pthread_mutex_unlock(&rli->data_lock); + goto err; + } + pthread_mutex_unlock(&rli->data_lock); + /* Read queries from the IO/THREAD until this thread is killed */ while (!sql_slave_killed(thd,rli)) @@ -4026,9 +4056,10 @@ end: has a certain bug. @param rli Relay_log_info which tells the master's version @param bug_id Number of the bug as found in bugs.mysql.com + @param report bool report error message, default TRUE @return TRUE if master has the bug, FALSE if it does not. */ -bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id) +bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id, bool report) { struct st_version_range_for_one_bug { uint bug_id; @@ -4038,7 +4069,9 @@ bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id) static struct st_version_range_for_one_bug versions_for_all_bugs[]= { {24432, { 5, 0, 24 }, { 5, 0, 38 } }, - {24432, { 5, 1, 12 }, { 5, 1, 17 } } + {24432, { 5, 1, 12 }, { 5, 1, 17 } }, + {33029, { 5, 0, 0 }, { 5, 0, 58 } }, + {33029, { 5, 1, 0 }, { 5, 1, 12 } }, }; const uchar *master_ver= rli->relay_log.description_event_for_exec->server_version_split; @@ -4054,6 +4087,9 @@ bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id) (memcmp(introduced_in, master_ver, 3) <= 0) && (memcmp(fixed_in, master_ver, 3) > 0)) { + if (!report) + return TRUE; + // a short message for SHOW SLAVE STATUS (message length constraints) my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from" " http://bugs.mysql.com/bug.php?id=%u" @@ -4085,6 +4121,26 @@ bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id) return FALSE; } +/** + BUG#33029, For all 5.0 up to 5.0.58 exclusive, and 5.1 up to 5.1.12 + exclusive, if one statement in a SP generated AUTO_INCREMENT value + by the top statement, all statements after it would be considered + generated AUTO_INCREMENT value by the top statement, and a + erroneous INSERT_ID value might be associated with these statement, + which could cause duplicate entry error and stop the slave. + + Detect buggy master to work around. + */ +bool rpl_master_erroneous_autoinc(THD *thd) +{ + if (active_mi && active_mi->rli.sql_thd == thd) + { + Relay_log_info *rli= &active_mi->rli; + return rpl_master_has_bug(rli, 33029, FALSE); + } + return FALSE; +} + #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION template class I_List_iterator<i_string>; template class I_List_iterator<i_string_pair>; diff --git a/sql/slave.h b/sql/slave.h index b4f1b7f7467..80d267e5b27 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -165,7 +165,8 @@ int fetch_master_table(THD* thd, const char* db_name, const char* table_name, bool show_master_info(THD* thd, Master_info* mi); bool show_binlog_info(THD* thd); -bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id); +bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id, bool report=TRUE); +bool rpl_master_erroneous_autoinc(THD* thd); const char *print_slave_db_safe(const char *db); int check_expected_error(THD* thd, Relay_log_info const *rli, int error_code); diff --git a/sql/sp.cc b/sql/sp.cc index b486e58883a..69eae8de207 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -388,6 +388,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) uint length; char buff[65]; String str(buff, sizeof(buff), &my_charset_bin); + bool saved_time_zone_used= thd->time_zone_used; ulong sql_mode, saved_mode= thd->variables.sql_mode; Open_tables_state open_tables_state_backup; Stored_program_creation_ctx *creation_ctx; @@ -504,6 +505,11 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) sql_mode, params, returns, body, chistics, definer, created, modified, creation_ctx); done: + /* + Restore the time zone flag as the timezone usage in proc table + does not affect replication. + */ + thd->time_zone_used= saved_time_zone_used; if (table) close_system_tables(thd, &open_tables_state_backup); thd->variables.sql_mode= saved_mode; diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 8babbd52ff6..414ea12cd7a 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -51,6 +51,8 @@ sp_cond_check(LEX_STRING *sqlstate) (c < 'A' || 'Z' < c)) return FALSE; } + if (strcmp(sqlstate->str, "00000") == 0) + return FALSE; return TRUE; } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 3805d9fa8b6..92eb68dc2fa 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -192,7 +192,7 @@ static void update_hostname(acl_host_and_ip *host, const char *hostname); static bool compare_hostname(const acl_host_and_ip *host,const char *hostname, const char *ip); static my_bool acl_load(THD *thd, TABLE_LIST *tables); -static my_bool grant_load(TABLE_LIST *tables); +static my_bool grant_load(THD *thd, TABLE_LIST *tables); /* Convert scrambled password to binary form, according to scramble type, @@ -314,8 +314,11 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; char tmp_name[NAME_LEN+1]; int password_length; + ulong old_sql_mode= thd->variables.sql_mode; DBUG_ENTER("acl_load"); + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + grant_version++; /* Privileges updated */ acl_cache->clear(1); // Clear locked hostname cache @@ -622,6 +625,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) return_val=0; end: + thd->variables.sql_mode= old_sql_mode; DBUG_RETURN(return_val); } @@ -801,7 +805,9 @@ static ulong get_sort(uint count,...) { for (; *str ; str++) { - if (*str == wild_many || *str == wild_one || *str == wild_prefix) + if (*str == wild_prefix && str[1]) + str++; + else if (*str == wild_many || *str == wild_one) { wild_pos= (uint) (str - start) + 1; break; @@ -1515,7 +1521,7 @@ bool acl_check_host(const char *host, const char *ip) 1 ERROR ; In this case the error is sent to the client. */ -bool check_change_password(THD *thd, const char *host, const char *user, +int check_change_password(THD *thd, const char *host, const char *user, char *new_password, uint new_password_len) { if (!initialized) @@ -2281,7 +2287,7 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, const char *t, ulong p, ulong c) :GRANT_NAME(h,d,u,t,p), cols(c) { - (void) hash_init(&hash_columns,system_charset_info, + (void) hash_init2(&hash_columns,4,system_charset_info, 0,0,0, (hash_get_key) get_key_column,0,0); } @@ -2329,7 +2335,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) cols= (ulong) form->field[7]->val_int(); cols = fix_rights_for_column(cols); - (void) hash_init(&hash_columns,system_charset_info, + (void) hash_init2(&hash_columns,4,system_charset_info, 0,0,0, (hash_get_key) get_key_column,0,0); if (cols) { @@ -2779,6 +2785,10 @@ table_error: } +/** + @retval 0 success + @retval -1 error +*/ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, TABLE *table, const LEX_USER &combo, const char *db, const char *routine_name, @@ -2800,14 +2810,11 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, thd->security_ctx->host_or_ip, NullS); /* - The following should always succeed as new users are created before - this function is called! + New users are created before this function is called. + + There may be some cases where a routine's definer is removed but the + routine remains. */ - if (!find_acl_user(combo.host.str, combo.user.str, FALSE)) - { - my_error(ER_PASSWORD_NO_MATCH,MYF(0)); - DBUG_RETURN(-1); - } table->use_all_columns(); restore_record(table, s->default_values); // Get empty record @@ -2916,7 +2923,7 @@ table_error: TRUE error */ -bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, +int mysql_table_grant(THD *thd, TABLE_LIST *table_list, List <LEX_USER> &user_list, List <LEX_COLUMN> &columns, ulong rights, bool revoke_grant) @@ -3328,7 +3335,8 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } if (replace_routine_table(thd, grant_name, tables[1].table, *Str, - db_name, table_name, is_proc, rights, revoke_grant)) + db_name, table_name, is_proc, rights, + revoke_grant) != 0) { result= TRUE; continue; @@ -3618,7 +3626,7 @@ end_unlock: @retval TRUE Error */ -static my_bool grant_load(TABLE_LIST *tables) +static my_bool grant_load(THD *thd, TABLE_LIST *tables) { MEM_ROOT *memex_ptr; my_bool return_val= 1; @@ -3626,7 +3634,11 @@ static my_bool grant_load(TABLE_LIST *tables) bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); + ulong old_sql_mode= thd->variables.sql_mode; DBUG_ENTER("grant_load"); + + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + (void) hash_init(&column_priv_hash,system_charset_info, 0,0,0, (hash_get_key) get_grant_table, (hash_free_key) free_grant_table,0); @@ -3678,6 +3690,7 @@ static my_bool grant_load(TABLE_LIST *tables) return_val=0; // Return ok end_unlock: + thd->variables.sql_mode= old_sql_mode; t_table->file->ha_index_end(); my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr); DBUG_RETURN(return_val); @@ -3791,7 +3804,7 @@ my_bool grant_reload(THD *thd) old_mem= memex; init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0); - if ((return_val= grant_load(tables))) + if ((return_val= grant_load(thd, tables))) { // Error. Revert to old hash DBUG_PRINT("error",("Reverting to old privileges")); grant_free(); /* purecov: deadcode */ @@ -5956,11 +5969,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) if (!strcmp(lex_user->user.str,user) && !strcmp(lex_user->host.str, host)) { - if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user, + if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user, grant_proc->db, grant_proc->tname, is_proc, - ~(ulong)0, 1)) + ~(ulong)0, 1) == 0) { revoked= 1; continue; @@ -5986,17 +5999,73 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) } -/* - Revoke privileges for all users on a stored procedure - SYNOPSIS - sp_revoke_privileges() + +/** + If the defining user for a routine does not exist, then the ACL lookup + code should raise two errors which we should intercept. We convert the more + descriptive error into a warning, and consume the other. + + If any other errors are raised, then we set a flag that should indicate + that there was some failure we should complain at a higher level. +*/ +class Silence_routine_definer_errors : public Internal_error_handler +{ +public: + Silence_routine_definer_errors() + : is_grave(FALSE) + {} + + virtual ~Silence_routine_definer_errors() + {} + + virtual bool handle_error(uint sql_errno, const char *message, + MYSQL_ERROR::enum_warning_level level, + THD *thd); + + bool has_errors() { return is_grave; } + +private: + bool is_grave; +}; + +bool +Silence_routine_definer_errors::handle_error(uint sql_errno, + const char *message, + MYSQL_ERROR::enum_warning_level level, + THD *thd) +{ + if (level == MYSQL_ERROR::WARN_LEVEL_ERROR) + { + switch (sql_errno) + { + case ER_NONEXISTING_PROC_GRANT: + /* Convert the error into a warning. */ + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno, message); + return TRUE; + default: + is_grave= TRUE; + } + } + + return FALSE; +} + + +/** + Revoke privileges for all users on a stored procedure. Use an error handler + that converts errors about missing grants into warnings. + + @param thd The current thread. + @param db DB of the stored procedure + @param name Name of the stored procedure - RETURN + @retval 0 OK. + @retval < 0 Error. Error message not yet sent. */ @@ -6007,11 +6076,15 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, int result; TABLE_LIST tables[GRANT_TABLES]; HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; + Silence_routine_definer_errors error_handler; DBUG_ENTER("sp_revoke_privileges"); if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); + /* Be sure to pop this before exiting this scope! */ + thd->push_internal_handler(&error_handler); + rw_wrlock(&LOCK_grant); VOID(pthread_mutex_lock(&acl_cache->lock)); @@ -6038,14 +6111,14 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, grant_proc->host.hostname : (char*)""; lex_user.host.length= grant_proc->host.hostname ? strlen(grant_proc->host.hostname) : 0; - if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user, - grant_proc->db, grant_proc->tname, - is_proc, ~(ulong)0, 1)) + + if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user, + grant_proc->db, grant_proc->tname, + is_proc, ~(ulong)0, 1) == 0) { revoked= 1; continue; } - result= -1; // Something went wrong } counter++; } @@ -6055,10 +6128,9 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, rw_unlock(&LOCK_grant); close_thread_tables(thd); - if (result) - my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0)); + thd->pop_internal_handler(); - DBUG_RETURN(result); + DBUG_RETURN(error_handler.has_errors()); } @@ -6076,7 +6148,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, < 0 Error. Error message not yet sent. */ -bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, +int sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, bool is_proc) { Security_context *sctx= thd->security_ctx; diff --git a/sql/sql_acl.h b/sql/sql_acl.h index a279c26c2e4..9ae17a4bf02 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -222,13 +222,13 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd, bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, char *ip, char *db); bool acl_check_host(const char *host, const char *ip); -bool check_change_password(THD *thd, const char *host, const char *user, +int check_change_password(THD *thd, const char *host, const char *user, char *password, uint password_len); bool change_password(THD *thd, const char *host, const char *user, char *password); bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list, ulong rights, bool revoke); -bool mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list, +int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list, List <LEX_COLUMN> &column_list, ulong rights, bool revoke); bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc, @@ -264,7 +264,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table); bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, bool is_proc); -bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, +int sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, bool is_proc); bool check_routine_level_acl(THD *thd, const char *db, const char *name, bool is_proc); diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 490cc5e28c1..9ca6e0a0a2b 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -682,7 +682,7 @@ int analyse::send_row(List<Item> & /* field_list */) } // analyse::send_row -bool analyse::end_of_records() +int analyse::end_of_records() { field_info **f = f_info; char buff[MAX_FIELD_WIDTH]; diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index 827b6f4b217..8807b40857e 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -350,7 +350,7 @@ public: virtual bool change_columns(List<Item> &fields); virtual int send_row(List<Item> &field_list); virtual void end_group(void) {} - virtual bool end_of_records(void); + virtual int end_of_records(void); friend Procedure *proc_analyse_init(THD *thd, ORDER *param, select_result *result, List<Item> &field_list); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4a417dc05b1..3f0c1ee8a80 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -747,6 +747,7 @@ void close_handle_and_leave_table_as_lock(TABLE *table) table->db_stat= 0; // Mark file closed release_table_share(table->s, RELEASE_NORMAL); table->s= share; + table->file->change_table_ptr(table, table->s); DBUG_VOID_RETURN; } @@ -4599,8 +4600,12 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) } if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables) - tables->table->reginfo.lock_type= tables->lock_type == TL_WRITE_DEFAULT ? - thd->update_lock_default : tables->lock_type; + { + if (tables->lock_type == TL_WRITE_DEFAULT) + tables->table->reginfo.lock_type= thd->update_lock_default; + else if (tables->table->s->tmp_table == NO_TMP_TABLE) + tables->table->reginfo.lock_type= tables->lock_type; + } tables->table->grant= tables->grant; /* Attach MERGE children if not locked already. */ @@ -4861,7 +4866,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, the third argument set appropriately. */ -bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) +int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) { uint counter; bool need_reopen; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index ae6765c7934..388700f0efa 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1998,6 +1998,7 @@ void Query_cache::make_disabled() query_cache_size= 0; queries_blocks= 0; free_memory= 0; + free_memory_blocks= 0; bins= 0; steps= 0; cache= 0; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 376102c8bf9..db2bbcad788 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -28,6 +28,7 @@ #include "mysql_priv.h" #include "rpl_rli.h" #include "rpl_record.h" +#include "slave.h" #include <my_bitmap.h> #include "log_event.h" #include <m_ctype.h> @@ -253,7 +254,8 @@ const char *set_thd_proc_info(THD *thd, const char *info, const unsigned int calling_line) { const char *old_info= thd->proc_info; - DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, info)); + DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, + (info != NULL) ? info : "(null)")); #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) thd->profiling.status_change(info, calling_function, calling_file, calling_line); #endif @@ -395,8 +397,11 @@ Diagnostics_area::set_ok_status(THD *thd, ha_rows affected_rows_arg, { DBUG_ASSERT(! is_set()); #ifdef DBUG_OFF - /* In production, refuse to overwrite an error with an OK packet. */ - if (is_error()) + /* + In production, refuse to overwrite an error or a custom response + with an OK packet. + */ + if (is_error() || is_disabled()) return; #endif /** Only allowed to report success if has not yet reported an error */ @@ -424,8 +429,11 @@ Diagnostics_area::set_eof_status(THD *thd) DBUG_ASSERT(! is_set()); #ifdef DBUG_OFF - /* In production, refuse to overwrite an error with an EOF packet. */ - if (is_error()) + /* + In production, refuse to overwrite an error or a custom response + with an EOF packet. + */ + if (is_error() || is_disabled()) return; #endif @@ -454,6 +462,14 @@ Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg, an error can happen during the flush. */ DBUG_ASSERT(! is_set() || can_overwrite_status); +#ifdef DBUG_OFF + /* + In production, refuse to overwrite a custom response with an + ERROR packet. + */ + if (is_disabled()) + return; +#endif m_sql_errno= sql_errno_arg; strmake(m_message, message_arg, sizeof(m_message) - 1); @@ -564,6 +580,7 @@ THD::THD() cleanup_done= abort_on_warning= no_warnings_for_error= 0; peer_port= 0; // For SHOW PROCESSLIST transaction.m_pending_rows_event= 0; + transaction.on= 1; #ifdef SIGNAL_WITH_VIO_CLOSE active_vio = 0; #endif @@ -2843,6 +2860,18 @@ extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all) void THD::reset_sub_statement_state(Sub_statement_state *backup, uint new_state) { +#ifndef EMBEDDED_LIBRARY + /* BUG#33029, if we are replicating from a buggy master, reset + auto_inc_intervals_forced to prevent substatement + (triggers/functions) from using erroneous INSERT_ID value + */ + if (rpl_master_erroneous_autoinc(this)) + { + backup->auto_inc_intervals_forced= auto_inc_intervals_forced; + auto_inc_intervals_forced.empty(); + } +#endif + backup->options= options; backup->in_sub_stmt= in_sub_stmt; backup->enable_slow_log= enable_slow_log; @@ -2880,6 +2909,18 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, void THD::restore_sub_statement_state(Sub_statement_state *backup) { +#ifndef EMBEDDED_LIBRARY + /* BUG#33029, if we are replicating from a buggy master, restore + auto_inc_intervals_forced so that the top statement can use the + INSERT_ID value set before this statement. + */ + if (rpl_master_erroneous_autoinc(this)) + { + auto_inc_intervals_forced= backup->auto_inc_intervals_forced; + backup->auto_inc_intervals_forced.empty(); + } +#endif + /* To save resources we want to release savepoints which were created during execution of function or trigger before leaving their savepoint @@ -3585,17 +3626,24 @@ bool Discrete_intervals_list::append(ulonglong start, ulonglong val, { /* it cannot, so need to add a new interval */ Discrete_interval *new_interval= new Discrete_interval(start, val, incr); - if (unlikely(new_interval == NULL)) // out of memory - DBUG_RETURN(1); - DBUG_PRINT("info",("adding new auto_increment interval")); - if (head == NULL) - head= current= new_interval; - else - tail->next= new_interval; - tail= new_interval; - elements++; + DBUG_RETURN(append(new_interval)); } DBUG_RETURN(0); } +bool Discrete_intervals_list::append(Discrete_interval *new_interval) +{ + DBUG_ENTER("Discrete_intervals_list::append"); + if (unlikely(new_interval == NULL)) + DBUG_RETURN(1); + DBUG_PRINT("info",("adding new auto_increment interval")); + if (head == NULL) + head= current= new_interval; + else + tail->next= new_interval; + tail= new_interval; + elements++; + DBUG_RETURN(0); +} + #endif /* !defined(MYSQL_CLIENT) */ diff --git a/sql/sql_class.h b/sql/sql_class.h index 2bfcc43a454..5b9ef7f381d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -912,6 +912,7 @@ public: ulonglong first_successful_insert_id_in_prev_stmt; ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row; Discrete_interval auto_inc_interval_for_cur_row; + Discrete_intervals_list auto_inc_intervals_forced; ulonglong limit_found_rows; ha_rows cuted_fields, sent_row_count, examined_row_count; ulong client_capabilities; diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index b22a33e3e92..d9fb586eb77 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -402,10 +402,11 @@ check_user(THD *thd, enum enum_server_command command, if (check_count) { - VOID(pthread_mutex_lock(&LOCK_thread_count)); - bool count_ok= thread_count <= max_connections + delayed_insert_threads - || (thd->main_security_ctx.master_access & SUPER_ACL); - VOID(pthread_mutex_unlock(&LOCK_thread_count)); + pthread_mutex_lock(&LOCK_connection_count); + bool count_ok= connection_count <= max_connections || + (thd->main_security_ctx.master_access & SUPER_ACL); + VOID(pthread_mutex_unlock(&LOCK_connection_count)); + if (!count_ok) { // too many connections my_error(ER_CON_COUNT_ERROR, MYF(0)); @@ -700,20 +701,24 @@ static int check_connection(THD *thd) bzero((char*) &thd->remote, sizeof(thd->remote)); } vio_keepalive(net->vio, TRUE); + + ulong server_capabilites; { /* buff[] needs to big enough to hold the server_version variable */ char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64]; - ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | - CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION); + server_capabilites= CLIENT_BASIC_FLAGS; if (opt_using_transactions) - client_flags|=CLIENT_TRANSACTIONS; + server_capabilites|= CLIENT_TRANSACTIONS; #ifdef HAVE_COMPRESS - client_flags |= CLIENT_COMPRESS; + server_capabilites|= CLIENT_COMPRESS; #endif /* HAVE_COMPRESS */ #ifdef HAVE_OPENSSL if (ssl_acceptor_fd) - client_flags |= CLIENT_SSL; /* Wow, SSL is available! */ + { + server_capabilites |= CLIENT_SSL; /* Wow, SSL is available! */ + server_capabilites |= CLIENT_SSL_VERIFY_SERVER_CERT; + } #endif /* HAVE_OPENSSL */ end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1; @@ -732,7 +737,7 @@ static int check_connection(THD *thd) */ end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1; - int2store(end, client_flags); + int2store(end, server_capabilites); /* write server characteristics: up to 16 bytes allowed */ end[2]=(char) default_charset_info->number; int2store(end+3, thd->server_status); @@ -762,7 +767,7 @@ static int check_connection(THD *thd) if (thd->packet.alloc(thd->variables.net_buffer_length)) return 1; /* The error is set by alloc(). */ - thd->client_capabilities=uint2korr(net->read_pos); + thd->client_capabilities= uint2korr(net->read_pos); if (thd->client_capabilities & CLIENT_PROTOCOL_41) { thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; @@ -777,6 +782,11 @@ static int check_connection(THD *thd) thd->max_client_packet_length= uint3korr(net->read_pos+2); end= (char*) net->read_pos+5; } + /* + Disable those bits which are not supported by the server. + This is a precautionary measure, if the client lies. See Bug#27944. + */ + thd->client_capabilities&= server_capabilites; if (thd->client_capabilities & CLIENT_IGNORE_SPACE) thd->variables.sql_mode|= MODE_IGNORE_SPACE; @@ -930,7 +940,7 @@ bool setup_connection_thread_globals(THD *thd) */ -bool login_connection(THD *thd) +static bool login_connection(THD *thd) { NET *net= &thd->net; int error; @@ -968,7 +978,7 @@ bool login_connection(THD *thd) This mainly updates status variables */ -void end_connection(THD *thd) +static void end_connection(THD *thd) { NET *net= &thd->net; plugin_thdvar_cleanup(thd); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index d03ac7921ac..2c7e0e82b3c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -606,7 +606,7 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name) */ -bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, +int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, bool silent) { char path[FN_REFLEN+16]; @@ -1111,12 +1111,17 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, /* Drop the table nicely */ *extension= 0; // Remove extension TABLE_LIST *table_list=(TABLE_LIST*) - thd->calloc(sizeof(*table_list)+ strlen(db)+strlen(file->name)+2); + thd->calloc(sizeof(*table_list) + + strlen(db) + 1 + + MYSQL50_TABLE_NAME_PREFIX_LENGTH + + strlen(file->name) + 1); + if (!table_list) - goto err; + goto err; table_list->db= (char*) (table_list+1); table_list->table_name= strmov(table_list->db, db) + 1; VOID(filename_to_tablename(file->name, table_list->table_name, + MYSQL50_TABLE_NAME_PREFIX_LENGTH + strlen(file->name) + 1)); table_list->alias= table_list->table_name; // If lower_case_table_names=2 table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 4e0853dbbc4..38a71ab1e32 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -45,6 +45,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool transactional_table, safe_update, const_cond; bool const_cond_result; ha_rows deleted= 0; + bool triggers_applicable; uint usable_index= MAX_KEY; SELECT_LEX *select_lex= &thd->lex->select_lex; THD::killed_state killed_status= THD::NOT_KILLED; @@ -102,6 +103,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, /* Error evaluating val_int(). */ DBUG_RETURN(TRUE); } + /* Test if the user wants to delete all rows and deletion doesn't have any side-effects (because of triggers), so we can use optimized @@ -249,7 +251,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, init_ftfuncs(thd, select_lex, 1); thd_proc_info(thd, "updating"); - if (table->triggers && + + /* NOTE: TRUNCATE must not invoke triggers. */ + + triggers_applicable= table->triggers && + thd->lex->sql_command != SQLCOM_TRUNCATE; + + if (triggers_applicable && table->triggers->has_triggers(TRG_EVENT_DELETE, TRG_ACTION_AFTER)) { @@ -274,7 +282,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!(select && select->skip_record())&& ! thd->is_error() ) { - if (table->triggers && + if (triggers_applicable && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)) { @@ -285,7 +293,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!(error= table->file->ha_delete_row(table->record[0]))) { deleted++; - if (table->triggers && + if (triggers_applicable && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_AFTER, FALSE)) { @@ -411,13 +419,26 @@ cleanup: FALSE OK TRUE error */ -bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) +int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) { Item *fake_conds= 0; SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_prepare_delete"); List<Item> all_fields; + /* + Statement-based replication of DELETE ... LIMIT is not safe as order of + rows is not defined, so in mixed mode we go to row-based. + + Note that we may consider a statement as safe if ORDER BY primary_key + is present. However it may confuse users to see very similiar statements + replicated differently. + */ + if (thd->lex->current_select->select_limit) + { + thd->lex->set_stmt_unsafe(); + thd->set_current_stmt_binlog_row_based_if_mixed(); + } thd->lex->allow_sum_func= 0; if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, @@ -474,7 +495,7 @@ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b) TRUE Error */ -bool mysql_multi_delete_prepare(THD *thd) +int mysql_multi_delete_prepare(THD *thd) { LEX *lex= thd->lex; TABLE_LIST *aux_tables= (TABLE_LIST *)lex->auxiliary_table_list.first; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 2be932a6040..89038bacda3 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -701,8 +701,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, error=0; thd_proc_info(thd, "update"); - if (duplic != DUP_ERROR || ignore) - table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); if (duplic == DUP_REPLACE && (!table->triggers || !table->triggers->has_delete_triggers())) table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); @@ -720,8 +718,15 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, values_list.elements, and - if nothing else - to initialize the code to make the call of end_bulk_insert() below safe. */ - if (lock_type != TL_WRITE_DELAYED && !thd->prelocked_mode) - table->file->ha_start_bulk_insert(values_list.elements); +#ifndef EMBEDDED_LIBRARY + if (lock_type != TL_WRITE_DELAYED) +#endif /* EMBEDDED_LIBRARY */ + { + if (duplic != DUP_ERROR || ignore) + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + if (!thd->prelocked_mode) + table->file->ha_start_bulk_insert(values_list.elements); + } thd->abort_on_warning= (!ignore && (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | @@ -840,6 +845,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, table->file->print_error(my_errno,MYF(0)); error=1; } + if (duplic != DUP_ERROR || ignore) + table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); + transactional_table= table->file->has_transactions(); if ((changed= (info.copied || info.deleted || info.updated))) @@ -932,8 +940,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, table->next_number_field=0; thd->count_cuted_fields= CHECK_FIELD_IGNORE; table->auto_increment_field_not_null= FALSE; - if (duplic != DUP_ERROR || ignore) - table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); if (duplic == DUP_REPLACE && (!table->triggers || !table->triggers->has_delete_triggers())) table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); @@ -1267,7 +1273,12 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, select_lex->fix_prepare_information(thd, &fake_conds, &fake_conds); select_lex->first_execution= 0; } - if (duplic == DUP_UPDATE || duplic == DUP_REPLACE) + /* + Only call prepare_for_posistion() if we are not performing a DELAYED + operation. It will instead be executed by delayed insert thread. + */ + if ((duplic == DUP_UPDATE || duplic == DUP_REPLACE) && + (table->reginfo.lock_type != TL_WRITE_DELAYED)) table->prepare_for_position(); DBUG_RETURN(FALSE); } @@ -2426,6 +2437,7 @@ pthread_handler_t handle_delayed_insert(void *arg) */ di->table->file->ha_release_auto_increment(); mysql_unlock_tables(thd, lock); + ha_autocommit_or_rollback(thd, 0); di->group_count=0; pthread_mutex_lock(&di->mutex); } @@ -2764,6 +2776,19 @@ bool mysql_insert_select_prepare(THD *thd) DBUG_ENTER("mysql_insert_select_prepare"); /* + Statement-based replication of INSERT ... SELECT ... LIMIT is not safe + as order of rows is not defined, so in mixed mode we go to row-based. + + Note that we may consider a statement as safe if ORDER BY primary_key + is present or we SELECT a constant. However it may confuse users to + see very similiar statements replicated differently. + */ + if (lex->current_select->select_limit) + { + lex->set_stmt_unsafe(); + thd->set_current_stmt_binlog_row_based_if_mixed(); + } + /* SELECT_LEX do not belong to INSERT statement, so we can't add WHERE clause if table is VIEW */ @@ -3677,13 +3702,10 @@ void select_create::abort() DBUG_ENTER("select_create::abort"); /* - Disable binlog, because we "roll back" partial inserts in ::abort - by removing the table, even for non-transactional tables. - */ - tmp_disable_binlog(thd); - /* In select_insert::abort() we roll back the statement, including - truncating the transaction cache of the binary log. + truncating the transaction cache of the binary log. To do this, we + pretend that the statement is transactional, even though it might + be the case that it was not. We roll back the statement prior to deleting the table and prior to releasing the lock on the table, since there might be potential @@ -3694,7 +3716,9 @@ void select_create::abort() of the table succeeded or not, since we need to reset the binary log state. */ + tmp_disable_binlog(thd); select_insert::abort(); + thd->transaction.stmt.modified_non_trans_table= FALSE; reenable_binlog(thd); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index b07efc62a00..449c2fccb0b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1601,6 +1601,7 @@ void st_select_lex::init_select() non_agg_fields.empty(); cond_value= having_value= Item::COND_UNDEF; inner_refs_list.empty(); + full_group_by_flag= 0; } /* @@ -1836,8 +1837,6 @@ bool st_select_lex::test_limit() "LIMIT & IN/ALL/ANY/SOME subquery"); return(1); } - // no sense in ORDER BY without LIMIT - order_list.empty(); return(0); } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index b1a0d506382..443c85b4854 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -692,6 +692,16 @@ public: joins on the right. */ List<String> *prev_join_using; + /* + Bitmap used in the ONLY_FULL_GROUP_BY_MODE to prevent mixture of aggregate + functions and non aggregated fields when GROUP BY list is absent. + Bits: + 0 - non aggregated fields are used in this select, + defined as NON_AGG_FIELD_USED. + 1 - aggregate functions are used in this select, + defined as SUM_FUNC_USED. + */ + uint8 full_group_by_flag; void init_query(); void init_select(); st_select_lex_unit* master_unit(); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 4d5dc8e6f06..255d8e5813d 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -110,7 +110,7 @@ static bool write_execute_load_query_log_event(THD *thd, TRUE - error / FALSE - success */ -bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, +int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, List<Item> &fields_vars, List<Item> &set_fields, List<Item> &set_values, enum enum_duplicates handle_duplicates, bool ignore, @@ -237,9 +237,11 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, while ((item= it++)) { - if (item->type() == Item::FIELD_ITEM) + Item *real_item= item->real_item(); + + if (real_item->type() == Item::FIELD_ITEM) { - Field *field= ((Item_field*)item)->field; + Field *field= ((Item_field*)real_item)->field; if (field->flags & BLOB_FLAG) { use_blobs= 1; @@ -248,7 +250,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, else tot_length+= field->field_length; } - else + else if (item->type() == Item::STRING_ITEM) use_vars= 1; } if (use_blobs && !ex->line_term->length() && !field_term->length()) @@ -706,6 +708,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, { uint length; uchar *pos; + Item *real_item; if (read_info.read_field()) break; @@ -717,14 +720,17 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, pos=read_info.row_start; length=(uint) (read_info.row_end-pos); + real_item= item->real_item(); + if (!read_info.enclosed && (enclosed_length && length == 4 && !memcmp(pos, STRING_WITH_LEN("NULL"))) || (length == 1 && read_info.found_null)) { - if (item->type() == Item::FIELD_ITEM) + + if (real_item->type() == Item::FIELD_ITEM) { - Field *field= ((Item_field *)item)->field; + Field *field= ((Item_field *)real_item)->field; if (field->reset()) { my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name, @@ -741,25 +747,39 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ER_WARN_NULL_TO_NOTNULL, 1); } } - else + else if (item->type() == Item::STRING_ITEM) + { ((Item_user_var_as_out_param *)item)->set_null_value( read_info.read_charset); + } + else + { + my_error(ER_LOAD_DATA_INVALID_COLUMN, MYF(0), item->full_name()); + DBUG_RETURN(1); + } + continue; } - if (item->type() == Item::FIELD_ITEM) + if (real_item->type() == Item::FIELD_ITEM) { - - Field *field= ((Item_field *)item)->field; + Field *field= ((Item_field *)real_item)->field; field->set_notnull(); read_info.row_end[0]=0; // Safe to change end marker if (field == table->next_number_field) table->auto_increment_field_not_null= TRUE; field->store((char*) pos, length, read_info.read_charset); } - else + else if (item->type() == Item::STRING_ITEM) + { ((Item_user_var_as_out_param *)item)->set_value((char*) pos, length, - read_info.read_charset); + read_info.read_charset); + } + else + { + my_error(ER_LOAD_DATA_INVALID_COLUMN, MYF(0), item->full_name()); + DBUG_RETURN(1); + } } if (read_info.error) break; @@ -775,9 +795,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, break; for (; item ; item= it++) { - if (item->type() == Item::FIELD_ITEM) + Item *real_item= item->real_item(); + if (real_item->type() == Item::FIELD_ITEM) { - Field *field= ((Item_field *)item)->field; + Field *field= ((Item_field *)real_item)->field; if (field->reset()) { my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name, @@ -797,9 +818,16 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ER_WARN_TOO_FEW_RECORDS, ER(ER_WARN_TOO_FEW_RECORDS), thd->row_count); } - else + else if (item->type() == Item::STRING_ITEM) + { ((Item_user_var_as_out_param *)item)->set_null_value( read_info.read_charset); + } + else + { + my_error(ER_LOAD_DATA_INVALID_COLUMN, MYF(0), item->full_name()); + DBUG_RETURN(1); + } } } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index bc97e783b77..2b57071d497 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1856,10 +1856,10 @@ bool sp_process_definer(THD *thd) TRUE Error */ -bool +int mysql_execute_command(THD *thd) { - bool res= FALSE; + int res= FALSE; bool need_start_waiting= FALSE; // have protection against global read lock int up_result= 0; LEX *lex= thd->lex; @@ -2351,15 +2351,7 @@ mysql_execute_command(THD *thd) /* Might have been updated in create_table_precheck */ create_info.alias= create_table->alias; -#ifndef HAVE_READLINK - if (create_info.data_file_name) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, - "DATA DIRECTORY option ignored"); - if (create_info.index_file_name) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, - "INDEX DIRECTORY option ignored"); - create_info.data_file_name= create_info.index_file_name= NULL; -#else +#ifdef HAVE_READLINK /* Fix names if symlinked tables */ if (append_file_to_dir(thd, &create_info.data_file_name, create_table->table_name) || @@ -3249,6 +3241,18 @@ end_with_restore_list: thd->one_shot_set|= lex->one_shot_set; my_ok(thd); } + else + { + /* + We encountered some sort of error, but no message was sent. + Send something semi-generic here since we don't know which + assignment in the list caused the error. + */ + if (!thd->is_error()) + my_error(ER_WRONG_ARGUMENTS,MYF(0),"SET"); + goto error; + } + break; } @@ -5286,10 +5290,10 @@ bool check_stack_overrun(THD *thd, long margin, long stack_used; DBUG_ASSERT(thd == current_thd); if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >= - (long) (thread_stack - margin)) + (long) (my_thread_stack_size - margin)) { sprintf(errbuff[0],ER(ER_STACK_OVERRUN_NEED_MORE), - stack_used,thread_stack,margin); + stack_used,my_thread_stack_size,margin); my_message(ER_STACK_OVERRUN_NEED_MORE,errbuff[0],MYF(0)); thd->fatal_error(); return 1; @@ -6460,6 +6464,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, result= 1; if (grant_reload(thd)) result= 1; + if (servers_reload(thd)) + result= 1; /* purecov: inspected */ } if (tmp_thd) { @@ -7340,6 +7346,49 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg, } +/* + Check if path does not contain mysql data home directory + SYNOPSIS + test_if_data_home_dir() + dir directory + conv_home_dir converted data home directory + home_dir_len converted data home directory length + + RETURN VALUES + 0 ok + 1 error +*/ + +bool test_if_data_home_dir(const char *dir) +{ + char path[FN_REFLEN], conv_path[FN_REFLEN]; + uint dir_len, home_dir_len= strlen(mysql_unpacked_real_data_home); + DBUG_ENTER("test_if_data_home_dir"); + + if (!dir) + DBUG_RETURN(0); + + (void) fn_format(path, dir, "", "", + (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS)); + dir_len= unpack_dirname(conv_path, dir); + + if (home_dir_len < dir_len) + { + if (lower_case_file_system) + { + if (!my_strnncoll(character_set_filesystem, + (const uchar*) conv_path, home_dir_len, + (const uchar*) mysql_unpacked_real_data_home, + home_dir_len)) + DBUG_RETURN(1); + } + else if (!memcmp(conv_path, mysql_unpacked_real_data_home, home_dir_len)) + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + extern int MYSQLparse(void *thd); // from sql_yacc.cc diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 45a82782de0..037da87be7f 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3819,9 +3819,9 @@ bool mysql_unpack_partition(THD *thd, DBUG_PRINT("info", ("Successful parse")); part_info= lex.part_info; - DBUG_PRINT("info", ("default engine = %d, default_db_type = %d", - ha_legacy_type(part_info->default_engine_type), - ha_legacy_type(default_db_type))); + DBUG_PRINT("info", ("default engine = %s, default_db_type = %s", + ha_resolve_storage_engine_name(part_info->default_engine_type), + ha_resolve_storage_engine_name(default_db_type))); if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE) { if (old_lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) @@ -3863,6 +3863,8 @@ bool mysql_unpack_partition(THD *thd, if (!part_info->default_engine_type) part_info->default_engine_type= default_db_type; DBUG_ASSERT(part_info->default_engine_type == default_db_type); + DBUG_ASSERT(part_info->default_engine_type->db_type != DB_TYPE_UNKNOWN); + DBUG_ASSERT(part_info->default_engine_type != partition_hton); { /* @@ -4001,56 +4003,6 @@ static int fast_end_partition(THD *thd, ulonglong copied, /* - Check engine mix that it is correct - SYNOPSIS - check_engine_condition() - p_elem Partition element - default_engine Have user specified engine on table level - inout::engine_type Current engine used - inout::first Is it first partition - RETURN VALUE - TRUE Failed check - FALSE Ok - DESCRIPTION - (specified partition handler ) specified table handler - (NDB, NDB) NDB OK - (MYISAM, MYISAM) - OK - (MYISAM, -) - NOT OK - (MYISAM, -) MYISAM OK - (- , MYISAM) - NOT OK - (- , -) MYISAM OK - (-,-) - OK - (NDB, MYISAM) * NOT OK -*/ - -static bool check_engine_condition(partition_element *p_elem, - bool default_engine, - handlerton **engine_type, - bool *first) -{ - DBUG_ENTER("check_engine_condition"); - - DBUG_PRINT("enter", ("def_eng = %u, first = %u", default_engine, *first)); - if (*first && default_engine) - { - *engine_type= p_elem->engine_type; - } - *first= FALSE; - if ((!default_engine && - (p_elem->engine_type != (*engine_type) && - p_elem->engine_type)) || - (default_engine && - p_elem->engine_type != (*engine_type))) - { - DBUG_RETURN(TRUE); - } - else - { - DBUG_RETURN(FALSE); - } -} - -/* We need to check if engine used by all partitions can handle partitioning natively. @@ -4074,52 +4026,30 @@ static bool check_engine_condition(partition_element *p_elem, static bool check_native_partitioned(HA_CREATE_INFO *create_info,bool *ret_val, partition_info *part_info, THD *thd) { - List_iterator<partition_element> part_it(part_info->partitions); - bool first= TRUE; - bool default_engine; - handlerton *engine_type= create_info->db_type; + bool table_engine_set; + handlerton *engine_type= part_info->default_engine_type; handlerton *old_engine_type= engine_type; - uint i= 0; - uint no_parts= part_info->partitions.elements; DBUG_ENTER("check_native_partitioned"); - default_engine= (create_info->used_fields & HA_CREATE_USED_ENGINE) ? - FALSE : TRUE; - DBUG_PRINT("info", ("engine_type = %u, default = %u", - ha_legacy_type(engine_type), - default_engine)); - if (no_parts) + if (create_info->used_fields & HA_CREATE_USED_ENGINE) { - do + table_engine_set= TRUE; + engine_type= create_info->db_type; + } + else + { + table_engine_set= FALSE; + if (thd->lex->sql_command != SQLCOM_CREATE_TABLE) { - partition_element *part_elem= part_it++; - if (part_info->is_sub_partitioned() && - part_elem->subpartitions.elements) - { - uint no_subparts= part_elem->subpartitions.elements; - uint j= 0; - List_iterator<partition_element> sub_it(part_elem->subpartitions); - do - { - partition_element *sub_elem= sub_it++; - if (check_engine_condition(sub_elem, default_engine, - &engine_type, &first)) - goto error; - } while (++j < no_subparts); - /* - In case of subpartitioning and defaults we allow that only - subparts have specified engines, as long as the parts haven't - specified the wrong engine it's ok. - */ - if (check_engine_condition(part_elem, FALSE, - &engine_type, &first)) - goto error; - } - else if (check_engine_condition(part_elem, default_engine, - &engine_type, &first)) - goto error; - } while (++i < no_parts); + table_engine_set= TRUE; + DBUG_ASSERT(engine_type && engine_type != partition_hton); + } } + DBUG_PRINT("info", ("engine_type = %s, table_engine_set = %u", + ha_resolve_storage_engine_name(engine_type), + table_engine_set)); + if (part_info->check_engine_mix(engine_type, table_engine_set)) + goto error; /* All engines are of the same type. Check if this engine supports @@ -4216,7 +4146,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0)); DBUG_RETURN(TRUE); } - if (alter_info->flags == ALTER_TABLE_REORG) + if (alter_info->flags & ALTER_TABLE_REORG) { uint new_part_no, curr_part_no; if (tab_part_info->part_type != HASH_PARTITION || @@ -4317,7 +4247,12 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, { my_error(ER_NO_BINLOG_ERROR, MYF(0)); DBUG_RETURN(TRUE); - } + } + if (tab_part_info->defined_max_value) + { + my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } if (no_new_partitions == 0) { my_error(ER_ADD_PARTITION_NO_NEW_PARTITION, MYF(0)); @@ -4542,7 +4477,7 @@ that are reorganised. tab_part_info->is_auto_partitioned= FALSE; } } - else if (alter_info->flags == ALTER_DROP_PARTITION) + else if (alter_info->flags & ALTER_DROP_PARTITION) { /* Drop a partition from a range partition and list partitioning is @@ -4746,7 +4681,7 @@ state of p1. tab_part_info->is_auto_partitioned= FALSE; } } - else if (alter_info->flags == ALTER_REORGANIZE_PARTITION) + else if (alter_info->flags & ALTER_REORGANIZE_PARTITION) { /* Reorganise partitions takes a number of partitions that are next @@ -4927,8 +4862,8 @@ the generated partition syntax in a correct manner. } *partition_changed= TRUE; thd->work_part_info= tab_part_info; - if (alter_info->flags == ALTER_ADD_PARTITION || - alter_info->flags == ALTER_REORGANIZE_PARTITION) + if (alter_info->flags & ALTER_ADD_PARTITION || + alter_info->flags & ALTER_REORGANIZE_PARTITION) { if (tab_part_info->use_default_subpartitions && !alt_part_info->use_default_subpartitions) @@ -5055,13 +4990,21 @@ the generated partition syntax in a correct manner. DBUG_PRINT("info", ("partition changed")); *partition_changed= TRUE; } - if (create_info->db_type == partition_hton) + /* + Set up partition default_engine_type either from the create_info + or from the previus table + */ + if (create_info->used_fields & HA_CREATE_USED_ENGINE) + part_info->default_engine_type= create_info->db_type; + else { - if (!part_info->default_engine_type) + if (table->part_info) part_info->default_engine_type= table->part_info->default_engine_type; + else + part_info->default_engine_type= create_info->db_type; } - else - part_info->default_engine_type= create_info->db_type; + DBUG_ASSERT(part_info->default_engine_type && + part_info->default_engine_type != partition_hton); if (check_native_partitioned(create_info, &is_native_partitioned, part_info, thd)) { @@ -5842,32 +5785,42 @@ static void release_log_entries(partition_info *part_info) /* - Get a lock on table name to avoid that anyone can open the table in - a critical part of the ALTER TABLE. - SYNOPSIS - get_name_lock() + Final part of partition changes to handle things when under + LOCK TABLES. + SYNPOSIS + alter_partition_lock_handling() lpt Struct carrying parameters RETURN VALUES - FALSE Success - TRUE Failure + NONE */ - -static int get_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt) +static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) { - int error= 0; - DBUG_ENTER("get_name_lock"); - - bzero(&lpt->table_list, sizeof(lpt->table_list)); - lpt->table_list.db= (char*)lpt->db; - lpt->table_list.table= lpt->table; - lpt->table_list.table_name= (char*)lpt->table_name; - pthread_mutex_lock(&LOCK_open); - error= lock_table_name(lpt->thd, &lpt->table_list, FALSE); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(error); + int err; + if (lpt->thd->locked_tables) + { + /* + When we have the table locked, it is necessary to reopen the table + since all table objects were closed and removed as part of the + ALTER TABLE of partitioning structure. + */ + pthread_mutex_lock(&LOCK_open); + lpt->thd->in_lock_tables= 1; + err= reopen_tables(lpt->thd, 1, 1); + lpt->thd->in_lock_tables= 0; + if (err) + { + /* + Issue a warning since we weren't able to regain the lock again. + We also need to unlink table from thread's open list and from + table_cache + */ + unlink_open_table(lpt->thd, lpt->table, FALSE); + sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); + } + pthread_mutex_unlock(&LOCK_open); + } } - /* Unlock and close table before renaming and dropping partitions SYNOPSIS @@ -5880,35 +5833,16 @@ static int get_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt) static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt) { THD *thd= lpt->thd; - TABLE *table= lpt->table; + const char *db= lpt->db; + const char *table_name= lpt->table_name; DBUG_ENTER("alter_close_tables"); /* We need to also unlock tables and close all handlers. We set lock to zero to ensure we don't do this twice and we set db_stat to zero to ensure we don't close twice. */ - mysql_unlock_tables(thd, thd->lock); - thd->lock= 0; - table->file->close(); - table->db_stat= 0; - DBUG_RETURN(0); -} - - -/* - Release a lock name - SYNOPSIS - release_name_lock() - lpt - RETURN VALUES - 0 -*/ - -static int release_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - DBUG_ENTER("release_name_lock"); pthread_mutex_lock(&LOCK_open); - unlock_table_name(lpt->thd, &lpt->table_list); + close_data_files_and_morph_locks(thd, db, table_name); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } @@ -6117,7 +6051,19 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ((alter_info->flags & ALTER_REPAIR_PARTITION) && (error= table->file->ha_repair_partitions(thd)))) { - table->file->print_error(error, MYF(0)); + if (error == HA_ADMIN_NOT_IMPLEMENTED) { + if (alter_info->flags & ALTER_OPTIMIZE_PARTITION) + my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "optimize partition"); + else if (alter_info->flags & ALTER_ANALYZE_PARTITION) + my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "analyze partition"); + else if (alter_info->flags & ALTER_CHECK_PARTITION) + my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "check partition"); + else if (alter_info->flags & ALTER_REPAIR_PARTITION) + my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "repair partition"); + else + table->file->print_error(error, MYF(0)); + } else + table->file->print_error(error, MYF(0)); goto err; } } @@ -6168,7 +6114,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, goto err; } } - else if (alter_info->flags == ALTER_DROP_PARTITION) + else if (alter_info->flags & ALTER_DROP_PARTITION) { /* Now after all checks and setting state on dropped partitions we can @@ -6206,7 +6152,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, name lock. 5) Close all tables that have already been opened but didn't stumble on the abort locked previously. This is done as part of the - get_name_lock call. + close_data_files_and_morph_locks call. 6) We are now ready to release all locks we got in this thread. 7) Write the bin log Unfortunately the writing of the binlog is not synchronised with @@ -6223,8 +6169,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 9) Prepare handlers for drop of partitions 10) Drop the partitions 11) Remove entries from ddl log - 12) Release name lock so that all other threads can access the table - again. + 12) Reopen table if under lock tables 13) Complete query We insert Error injections at all places where it could be interesting @@ -6239,23 +6184,21 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, (not_completed= FALSE) || abort_and_upgrade_lock(lpt) || /* Always returns 0 */ ERROR_INJECT_CRASH("crash_drop_partition_4") || - get_name_lock(lpt) || - ERROR_INJECT_CRASH("crash_drop_partition_5") || alter_close_tables(lpt) || - ERROR_INJECT_CRASH("crash_drop_partition_6") || + ERROR_INJECT_CRASH("crash_drop_partition_5") || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query, thd->query_length), FALSE)) || - ERROR_INJECT_CRASH("crash_drop_partition_7") || + ERROR_INJECT_CRASH("crash_drop_partition_6") || ((frm_install= TRUE), FALSE) || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || ((frm_install= FALSE), FALSE) || - ERROR_INJECT_CRASH("crash_drop_partition_8") || + ERROR_INJECT_CRASH("crash_drop_partition_7") || mysql_drop_partitions(lpt) || - ERROR_INJECT_CRASH("crash_drop_partition_9") || + ERROR_INJECT_CRASH("crash_drop_partition_8") || (write_log_completed(lpt, FALSE), FALSE) || - ERROR_INJECT_CRASH("crash_drop_partition_10") || - (release_name_lock(lpt), FALSE)) + ERROR_INJECT_CRASH("crash_drop_partition_9") || + (alter_partition_lock_handling(lpt), FALSE)) { handle_alter_part_error(lpt, not_completed, TRUE, frm_install); goto err; @@ -6287,7 +6230,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, name lock. 5) Close all tables that have already been opened but didn't stumble on the abort locked previously. This is done as part of the - get_name_lock call. + close_data_files_and_morph_locks call. 6) Close all table handlers and unlock all handlers but retain name lock 7) Write binlog 8) Now the change is completed except for the installation of the @@ -6297,7 +6240,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, added to the table. 10)Wait until all accesses using the old frm file has completed 11)Remove entries from ddl log - 12)Release name lock + 12)Reopen tables if under lock tables 13)Complete query */ if (write_log_add_change_partition(lpt) || @@ -6307,8 +6250,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, mysql_change_partitions(lpt) || ERROR_INJECT_CRASH("crash_add_partition_3") || abort_and_upgrade_lock(lpt) || /* Always returns 0 */ - ERROR_INJECT_CRASH("crash_add_partition_3") || - get_name_lock(lpt) || ERROR_INJECT_CRASH("crash_add_partition_4") || alter_close_tables(lpt) || ERROR_INJECT_CRASH("crash_add_partition_5") || @@ -6324,7 +6265,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ERROR_INJECT_CRASH("crash_add_partition_8") || (write_log_completed(lpt, FALSE), FALSE) || ERROR_INJECT_CRASH("crash_add_partition_9") || - (release_name_lock(lpt), FALSE)) + (alter_partition_lock_handling(lpt), FALSE)) { handle_alter_part_error(lpt, not_completed, FALSE, frm_install); goto err; @@ -6378,15 +6319,15 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 7) Close all tables opened but not yet locked, after this call we are certain that no other thread is in the lock wait queue or has opened the table. The name lock will ensure that they are blocked - on the open call. This is achieved also by get_name_lock call. + on the open call. + This is achieved also by close_data_files_and_morph_locks call. 8) Close all partitions opened by this thread, but retain name lock. 9) Write bin log 10) Prepare handlers for rename and delete of partitions 11) Rename and drop the reorged partitions such that they are no longer used and rename those added to their real new names. 12) Install the shadow frm file - 13) Release the name lock to enable other threads to start using the - table again. + 13) Reopen the table if under lock tables 14) Complete query */ if (write_log_add_change_partition(lpt) || @@ -6400,24 +6341,22 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, (not_completed= FALSE) || abort_and_upgrade_lock(lpt) || /* Always returns 0 */ ERROR_INJECT_CRASH("crash_change_partition_5") || - get_name_lock(lpt) || - ERROR_INJECT_CRASH("crash_change_partition_6") || alter_close_tables(lpt) || - ERROR_INJECT_CRASH("crash_change_partition_7") || + ERROR_INJECT_CRASH("crash_change_partition_6") || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query, thd->query_length), FALSE)) || - ERROR_INJECT_CRASH("crash_change_partition_8") || + ERROR_INJECT_CRASH("crash_change_partition_7") || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || - ERROR_INJECT_CRASH("crash_change_partition_9") || + ERROR_INJECT_CRASH("crash_change_partition_8") || mysql_drop_partitions(lpt) || - ERROR_INJECT_CRASH("crash_change_partition_10") || + ERROR_INJECT_CRASH("crash_change_partition_9") || mysql_rename_partitions(lpt) || ((frm_install= TRUE), FALSE) || - ERROR_INJECT_CRASH("crash_change_partition_11") || + ERROR_INJECT_CRASH("crash_change_partition_10") || (write_log_completed(lpt, FALSE), FALSE) || - ERROR_INJECT_CRASH("crash_change_partition_12") || - (release_name_lock(lpt), FALSE)) + ERROR_INJECT_CRASH("crash_change_partition_11") || + (alter_partition_lock_handling(lpt), FALSE)) { handle_alter_part_error(lpt, not_completed, FALSE, frm_install); goto err; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 10176239711..811b82ebe05 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -210,6 +210,8 @@ static void reap_plugins(void); /* declared in set_var.cc */ extern sys_var *intern_find_sys_var(const char *str, uint length, bool no_error); +extern bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd, + const char *name, longlong val); #ifdef EMBEDDED_LIBRARY /* declared in sql_base.cc */ @@ -1181,9 +1183,8 @@ int plugin_init(int *argc, char **argv, int flags) /* Register all dynamic plugins */ if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING)) { - if (opt_plugin_load && - plugin_load_list(&tmp_root, argc, argv, opt_plugin_load)) - goto err; + if (opt_plugin_load) + plugin_load_list(&tmp_root, argc, argv, opt_plugin_load); if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE)) plugin_load(&tmp_root, argc, argv); } @@ -1412,7 +1413,11 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, while (list) { if (p == buffer + sizeof(buffer) - 1) - break; + { + sql_print_error("plugin-load parameter too long"); + DBUG_RETURN(TRUE); + } + switch ((*(p++)= *(list++))) { case '\0': list= NULL; /* terminate the loop */ @@ -1421,10 +1426,17 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, case ':': /* can't use this as delimiter as it may be drive letter */ #endif case ';': - name.str[name.length]= '\0'; - if (str != &dl) // load all plugins in named module + str->str[str->length]= '\0'; + if (str == &name) // load all plugins in named module { + if (!name.length) + { + p--; /* reset pointer */ + continue; + } + dl= name; + pthread_mutex_lock(&LOCK_plugin); if ((plugin_dl= plugin_dl_add(&dl, REPORT_TO_LOG))) { for (plugin= plugin_dl->plugins; plugin->info; plugin++) @@ -1442,9 +1454,11 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, else { free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); + pthread_mutex_lock(&LOCK_plugin); if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG)) goto error; } + pthread_mutex_unlock(&LOCK_plugin); name.length= dl.length= 0; dl.str= NULL; name.str= p= buffer; str= &name; @@ -1453,6 +1467,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, case '#': if (str == &name) { + name.str[name.length]= '\0'; str= &dl; str->str= p; continue; @@ -1464,6 +1479,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, } DBUG_RETURN(FALSE); error: + pthread_mutex_unlock(&LOCK_plugin); sql_print_error("Couldn't load plugin named '%s' with soname '%s'.", name.str, dl.str); DBUG_RETURN(TRUE); @@ -1888,16 +1904,8 @@ static int check_func_int(THD *thd, struct st_mysql_sys_var *var, else *(int *)save= (int) getopt_ll_limit_value(tmp, &options, &fixed); - if (fixed) - { - char buf[22]; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), var->name, - ullstr(tmp, buf)); - } - return (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) && - (*(int *)save != (int) tmp); + return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED, + var->name, (longlong) tmp); } @@ -1916,16 +1924,8 @@ static int check_func_long(THD *thd, struct st_mysql_sys_var *var, else *(long *)save= (long) getopt_ll_limit_value(tmp, &options, &fixed); - if (fixed) - { - char buf[22]; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), var->name, - ullstr(tmp, buf)); - } - return (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) && - (*(long *)save != (long) tmp); + return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED, + var->name, (longlong) tmp); } @@ -1944,16 +1944,8 @@ static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var, else *(longlong *)save= getopt_ll_limit_value(tmp, &options, &fixed); - if (fixed) - { - char buf[22]; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), var->name, - ullstr(tmp, buf)); - } - return (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) && - (*(long long *)save != tmp); + return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED, + var->name, (longlong) tmp); } static int check_func_str(THD *thd, struct st_mysql_sys_var *var, @@ -2068,35 +2060,35 @@ err: static void update_func_bool(THD *thd, struct st_mysql_sys_var *var, - void *tgt, void *save) + void *tgt, const void *save) { *(my_bool *) tgt= *(int *) save ? 1 : 0; } static void update_func_int(THD *thd, struct st_mysql_sys_var *var, - void *tgt, void *save) + void *tgt, const void *save) { *(int *)tgt= *(int *) save; } static void update_func_long(THD *thd, struct st_mysql_sys_var *var, - void *tgt, void *save) + void *tgt, const void *save) { *(long *)tgt= *(long *) save; } static void update_func_longlong(THD *thd, struct st_mysql_sys_var *var, - void *tgt, void *save) + void *tgt, const void *save) { *(longlong *)tgt= *(ulonglong *) save; } static void update_func_str(THD *thd, struct st_mysql_sys_var *var, - void *tgt, void *save) + void *tgt, const void *save) { char *old= *(char **) tgt; *(char **)tgt= *(char **) save; @@ -2653,7 +2645,8 @@ bool sys_var_pluginvar::check(THD *thd, set_var *var) void sys_var_pluginvar::set_default(THD *thd, enum_var_type type) { - void *tgt, *src; + const void *src; + void *tgt; DBUG_ASSERT(is_readonly() || plugin_var->update); @@ -2666,9 +2659,34 @@ void sys_var_pluginvar::set_default(THD *thd, enum_var_type type) if (plugin_var->flags & PLUGIN_VAR_THDLOCAL) { - src= ((int*) (plugin_var + 1) + 1); if (type != OPT_GLOBAL) src= real_value_ptr(thd, OPT_GLOBAL); + else + switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) { + case PLUGIN_VAR_INT: + src= &((thdvar_uint_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_LONG: + src= &((thdvar_ulong_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_LONGLONG: + src= &((thdvar_ulonglong_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_ENUM: + src= &((thdvar_enum_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_SET: + src= &((thdvar_set_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_BOOL: + src= &((thdvar_bool_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_STR: + src= &((thdvar_str_t*) plugin_var)->def_val; + break; + default: + DBUG_ASSERT(0); + } } /* thd must equal current_thd if PLUGIN_VAR_THDLOCAL flag is set */ @@ -2756,25 +2774,25 @@ static void plugin_opt_set_limits(struct my_option *options, case PLUGIN_VAR_ENUM: options->var_type= GET_ENUM; options->typelib= ((sysvar_enum_t*) opt)->typelib; - options->def_value= *(ulong*) ((int*) (opt + 1) + 1); + options->def_value= ((sysvar_enum_t*) opt)->def_val; options->min_value= options->block_size= 0; options->max_value= options->typelib->count - 1; break; case PLUGIN_VAR_SET: options->var_type= GET_SET; options->typelib= ((sysvar_set_t*) opt)->typelib; - options->def_value= *(ulonglong*) ((int*) (opt + 1) + 1); + options->def_value= ((sysvar_set_t*) opt)->def_val; options->min_value= options->block_size= 0; options->max_value= (ULL(1) << options->typelib->count) - 1; break; case PLUGIN_VAR_BOOL: options->var_type= GET_BOOL; - options->def_value= *(my_bool*) ((void**)(opt + 1) + 1); + options->def_value= ((sysvar_bool_t*) opt)->def_val; break; case PLUGIN_VAR_STR: options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ? GET_STR_ALLOC : GET_STR); - options->def_value= (ulonglong)(intptr) *((char**) ((void**) (opt + 1) + 1)); + options->def_value= (intptr) ((sysvar_str_t*) opt)->def_val; break; /* threadlocal variables */ case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL: @@ -2798,25 +2816,25 @@ static void plugin_opt_set_limits(struct my_option *options, case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL: options->var_type= GET_ENUM; options->typelib= ((thdvar_enum_t*) opt)->typelib; - options->def_value= *(ulong*) ((int*) (opt + 1) + 1); + options->def_value= ((thdvar_enum_t*) opt)->def_val; options->min_value= options->block_size= 0; options->max_value= options->typelib->count - 1; break; case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL: options->var_type= GET_SET; options->typelib= ((thdvar_set_t*) opt)->typelib; - options->def_value= *(ulonglong*) ((int*) (opt + 1) + 1); + options->def_value= ((thdvar_set_t*) opt)->def_val; options->min_value= options->block_size= 0; options->max_value= (ULL(1) << options->typelib->count) - 1; break; case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL: options->var_type= GET_BOOL; - options->def_value= *(my_bool*) ((int*) (opt + 1) + 1); + options->def_value= ((thdvar_bool_t*) opt)->def_val; break; case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL: options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ? GET_STR_ALLOC : GET_STR); - options->def_value= (intptr) *((char**) ((void**) (opt + 1) + 1)); + options->def_value= (intptr) ((thdvar_str_t*) opt)->def_val; break; default: DBUG_ASSERT(0); @@ -2998,7 +3016,8 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, DBUG_RETURN(-1); } - if (opt->flags & PLUGIN_VAR_NOCMDOPT) + if ((opt->flags & (PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_THDLOCAL)) + == PLUGIN_VAR_NOCMDOPT) continue; if (!opt->name) @@ -3008,7 +3027,7 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, DBUG_RETURN(-1); } - if (!(v= find_bookmark(name, opt->name, opt->flags))) + if (!(opt->flags & PLUGIN_VAR_THDLOCAL)) { optnamelen= strlen(opt->name); optname= (char*) alloc_root(mem_root, namelen + optnamelen + 2); @@ -3016,7 +3035,23 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, optnamelen= namelen + optnamelen + 1; } else - optname= (char*) memdup_root(mem_root, v->key + 1, (optnamelen= v->name_len) + 1); + { + /* this should not fail because register_var should create entry */ + if (!(v= find_bookmark(name, opt->name, opt->flags))) + { + sql_print_error("Thread local variable '%s' not allocated " + "in plugin '%s'.", opt->name, plugin_name); + DBUG_RETURN(-1); + } + + *(int*)(opt + 1)= offset= v->offset; + + if (opt->flags & PLUGIN_VAR_NOCMDOPT) + continue; + + optname= (char*) memdup_root(mem_root, v->key + 1, + (optnamelen= v->name_len) + 1); + } /* convert '_' to '-' */ for (p= optname; *p; p++) @@ -3028,20 +3063,13 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, options->app_type= opt; options->id= (options-1)->id + 1; - if (opt->flags & PLUGIN_VAR_THDLOCAL) - *(int*)(opt + 1)= offset= v->offset; - plugin_opt_set_limits(options, opt); - if ((opt->flags & PLUGIN_VAR_TYPEMASK) != PLUGIN_VAR_ENUM && - (opt->flags & PLUGIN_VAR_TYPEMASK) != PLUGIN_VAR_SET) - { - if (opt->flags & PLUGIN_VAR_THDLOCAL) - options->value= options->u_max_value= (uchar**) - (global_system_variables.dynamic_variables_ptr + offset); - else - options->value= options->u_max_value= *(uchar***) (opt + 1); - } + if (opt->flags & PLUGIN_VAR_THDLOCAL) + options->value= options->u_max_value= (uchar**) + (global_system_variables.dynamic_variables_ptr + offset); + else + options->value= options->u_max_value= *(uchar***) (opt + 1); options[1]= options[0]; options[1].name= p= (char*) alloc_root(mem_root, optnamelen + 8); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index a027ffe9daa..091cc18114b 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1415,7 +1415,7 @@ error: */ static bool select_like_stmt_test(Prepared_statement *stmt, - bool (*specific_prepare)(THD *thd), + int (*specific_prepare)(THD *thd), ulong setup_tables_done_option) { DBUG_ENTER("select_like_stmt_test"); @@ -1452,7 +1452,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt, static bool select_like_stmt_test_with_open(Prepared_statement *stmt, TABLE_LIST *tables, - bool (*specific_prepare)(THD *thd), + int (*specific_prepare)(THD *thd), ulong setup_tables_done_option) { DBUG_ENTER("select_like_stmt_test_with_open"); @@ -1638,7 +1638,7 @@ error: uses local tables lists. */ -static bool mysql_insert_select_prepare_tester(THD *thd) +static int mysql_insert_select_prepare_tester(THD *thd) { SELECT_LEX *first_select= &thd->lex->select_lex; TABLE_LIST *second_table= ((TABLE_LIST*)first_select->table_list.first)-> @@ -2555,6 +2555,8 @@ void mysql_stmt_close(THD *thd, char *packet) Prepared_statement *stmt; DBUG_ENTER("mysql_stmt_close"); + thd->main_da.disable_status(); + if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close"))) DBUG_VOID_RETURN; @@ -2566,8 +2568,6 @@ void mysql_stmt_close(THD *thd, char *packet) (void) stmt->deallocate(); general_log_print(thd, thd->command, NullS); - thd->main_da.disable_status(); - DBUG_VOID_RETURN; } diff --git a/sql/sql_repl.h b/sql/sql_repl.h index cf5201f17b1..d5c9040f8dc 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -36,12 +36,6 @@ extern bool server_id_supplied; extern int max_binlog_dump_events; extern my_bool opt_sporadic_binlog_dump_fail; -#define KICK_SLAVE(thd) do { \ - pthread_mutex_lock(&(thd)->LOCK_delete); \ - (thd)->awake(THD::NOT_KILLED); \ - pthread_mutex_unlock(&(thd)->LOCK_delete); \ - } while(0) - int start_slave(THD* thd, Master_info* mi, bool net_report); int stop_slave(THD* thd, Master_info* mi, bool net_report); bool change_master(THD* thd, Master_info* mi); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a520f9c57cf..164edacc932 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -33,6 +33,7 @@ #include "sql_cursor.h" #include <m_ctype.h> +#include <my_bit.h> #include <hash.h> #include <ft_global.h> @@ -371,10 +372,10 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select, } } new_ref= direct_ref ? - new Item_direct_ref(ref->context, item_ref, ref->field_name, - ref->table_name, ref->alias_name_used) : - new Item_ref(ref->context, item_ref, ref->field_name, - ref->table_name, ref->alias_name_used); + new Item_direct_ref(ref->context, item_ref, ref->table_name, + ref->field_name, ref->alias_name_used) : + new Item_ref(ref->context, item_ref, ref->table_name, + ref->field_name, ref->alias_name_used); if (!new_ref) return TRUE; ref->outer_ref= new_ref; @@ -585,37 +586,13 @@ JOIN::prepare(Item ***rref_pointer_array, /* Check if there are references to un-aggregated columns when computing aggregate functions with implicit grouping (there is no GROUP BY). - TODO: Add check of calculation of GROUP functions and fields: - SELECT COUNT(*)+table.col1 from table1; */ - if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !group_list && + select_lex->full_group_by_flag == (NON_AGG_FIELD_USED | SUM_FUNC_USED)) { - if (!group_list) - { - uint flag=0; - List_iterator_fast<Item> it(fields_list); - Item *item; - while ((item= it++)) - { - if (item->with_sum_func) - flag|=1; - else if (!(flag & 2) && !item->const_during_execution()) - flag|=2; - } - if (having) - { - if (having->with_sum_func) - flag |= 1; - else if (!having->const_during_execution()) - flag |= 2; - } - if (flag == 3) - { - my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, - ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); - DBUG_RETURN(-1); - } - } + my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, + ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); + DBUG_RETURN(-1); } { /* Caclulate the number of groups */ @@ -2989,7 +2966,9 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, } } else if (old->eq_func && new_fields->eq_func && - old->val->eq(new_fields->val, old->field->binary())) + old->val->eq_by_collation(new_fields->val, + old->field->binary(), + old->field->charset())) { old->level= and_level; @@ -9754,7 +9733,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, table->s= share; init_tmp_table_share(thd, share, "", 0, tmpname, tmpname); share->blob_field= blob_field; - share->blob_ptr_size= mi_portable_sizeof_char_ptr; + share->blob_ptr_size= portable_sizeof_char_ptr; share->db_low_byte_first=1; // True for HEAP and MyISAM share->table_charset= param->table_charset; share->primary_key= MAX_KEY; // Indicate no primary key @@ -10316,7 +10295,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list) table->s= share; share->blob_field= blob_field; share->fields= field_count; - share->blob_ptr_size= mi_portable_sizeof_char_ptr; + share->blob_ptr_size= portable_sizeof_char_ptr; setup_tmp_table_column_bitmaps(table, bitmaps); /* Create all fields and calculate the total length of record */ @@ -11183,7 +11162,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, we found a row, as no new rows can be added to the result. */ if (not_used_in_distinct && found_records != join->found_records) - return NESTED_LOOP_OK; + return NESTED_LOOP_NO_MORE_ROWS; } else join_tab->read_record.file->unlock_row(); @@ -11568,21 +11547,42 @@ join_read_key(JOIN_TAB *tab) } +/* + ref access method implementation: "read_first" function + + SYNOPSIS + join_read_always_key() + tab JOIN_TAB of the accessed table + + DESCRIPTION + This is "read_fist" function for the "ref" access method. + + The functon must leave the index initialized when it returns. + ref_or_null access implementation depends on that. + + RETURN + 0 - Ok + -1 - Row not found + 1 - Error +*/ + static int join_read_always_key(JOIN_TAB *tab) { int error; TABLE *table= tab->table; + /* Initialize the index first */ + if (!table->file->inited) + table->file->ha_index_init(tab->ref.key, tab->sorted); + + /* Perform "Late NULLs Filtering" (see internals manual for explanations) */ for (uint i= 0 ; i < tab->ref.key_parts ; i++) { if ((tab->ref.null_rejecting & 1 << i) && tab->ref.items[i]->is_null()) return -1; - } - if (!table->file->inited) - { - table->file->ha_index_init(tab->ref.key, tab->sorted); } + if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) return -1; if ((error=table->file->index_read_map(table->record[0], @@ -13135,6 +13135,11 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, tab->read_first_record= best_key_direction > 0 ? join_read_first:join_read_last; tab->type=JT_NEXT; // Read with index_first(), index_next() + if (select && select->quick) + { + delete select->quick; + select->quick= 0; + } if (table->covering_keys.is_set(best_key)) { table->key_read=1; @@ -13145,15 +13150,27 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, { tab->ref.key= -1; tab->ref.key_parts= 0; - if (select && select->quick) - { - delete select->quick; - select->quick= 0; - } if (select_limit < table_records) tab->limit= select_limit; } } + else if (tab->type != JT_ALL) + { + /* + We're about to use a quick access to the table. + We need to change the access method so as the quick access + method is actually used. + */ + DBUG_ASSERT(tab->select->quick); + tab->type=JT_ALL; + tab->use_quick=1; + tab->ref.key= -1; + tab->ref.key_parts=0; // Don't use ref key. + tab->read_first_record= join_init_read_record; + /* + TODO: update the number of records in join->best_positions[tablenr] + */ + } } used_key_parts= best_key_parts; order_direction= best_key_direction; @@ -16479,6 +16496,14 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) /* go through join tree */ print_join(thd, str, &top_join_list, query_type); } + else if (where) + { + /* + "SELECT 1 FROM DUAL WHERE 2" should not be printed as + "SELECT 1 WHERE 2": the 1st syntax is valid, but the 2nd is not. + */ + str->append(STRING_WITH_LEN(" from DUAL ")); + } // Where Item *cur_where= where; diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 8203ca92eed..4390919f8c7 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -55,8 +55,8 @@ static bool get_server_from_table_to_cache(TABLE *table); static int insert_server(THD *thd, FOREIGN_SERVER *server_options); static int insert_server_record(TABLE *table, FOREIGN_SERVER *server); static int insert_server_record_into_cache(FOREIGN_SERVER *server); -static void prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options, - FOREIGN_SERVER *server); +static FOREIGN_SERVER * +prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options); /* drop functions */ static int delete_server_record(TABLE *table, char *server_name, @@ -166,6 +166,9 @@ end: RETURN VALUES FALSE Success TRUE Error + + TODO + Revert back to old list if we failed to load new one. */ static bool servers_load(THD *thd, TABLE_LIST *tables) @@ -175,10 +178,9 @@ static bool servers_load(THD *thd, TABLE_LIST *tables) bool return_val= TRUE; DBUG_ENTER("servers_load"); - /* first, send all cached rows to sleep with the fishes, oblivion! - I expect this crappy comment replaced */ - free_root(&mem, MYF(MY_MARK_BLOCKS_FREE)); my_hash_reset(&servers_cache); + free_root(&mem, MYF(0)); + init_alloc_root(&mem, ACL_ALLOC_BLOCK_SIZE, 0); init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0); while (!(read_record_info.read_record(&read_record_info))) @@ -966,10 +968,14 @@ int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options) server_options->server_name_length)) goto end; - server= (FOREIGN_SERVER *)alloc_root(&mem, - sizeof(FOREIGN_SERVER)); - prepare_server_struct_for_insert(server_options, server); + if (!(server= prepare_server_struct_for_insert(server_options))) + { + /* purecov: begin inspected */ + error= ER_OUT_OF_RESOURCES; + goto end; + /* purecov: end */ + } error= insert_server(thd, server); @@ -1040,52 +1046,64 @@ end: SYNOPSIS prepare_server_struct_for_insert() LEX_SERVER_OPTIONS *server_options - FOREIGN_SERVER *server NOTES + As FOREIGN_SERVER members are allocated on mem_root, we do not need to + free them in case of error. RETURN VALUE - none + On success filled FOREIGN_SERVER, or NULL in case out of memory. */ -static void -prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options, - FOREIGN_SERVER *server) +static FOREIGN_SERVER * +prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options) { char *unset_ptr= (char*)""; + FOREIGN_SERVER *server; DBUG_ENTER("prepare_server_struct"); + if (!(server= (FOREIGN_SERVER *)alloc_root(&mem, sizeof(FOREIGN_SERVER)))) + DBUG_RETURN(NULL); /* purecov: inspected */ + /* these two MUST be set */ - server->server_name= strdup_root(&mem, server_options->server_name); + if (!(server->server_name= strdup_root(&mem, server_options->server_name))) + DBUG_RETURN(NULL); /* purecov: inspected */ server->server_name_length= server_options->server_name_length; - server->host= server_options->host ? - strdup_root(&mem, server_options->host) : unset_ptr; + if (!(server->host= server_options->host ? + strdup_root(&mem, server_options->host) : unset_ptr)) + DBUG_RETURN(NULL); /* purecov: inspected */ - server->db= server_options->db ? - strdup_root(&mem, server_options->db) : unset_ptr; + if (!(server->db= server_options->db ? + strdup_root(&mem, server_options->db) : unset_ptr)) + DBUG_RETURN(NULL); /* purecov: inspected */ - server->username= server_options->username ? - strdup_root(&mem, server_options->username) : unset_ptr; + if (!(server->username= server_options->username ? + strdup_root(&mem, server_options->username) : unset_ptr)) + DBUG_RETURN(NULL); /* purecov: inspected */ - server->password= server_options->password ? - strdup_root(&mem, server_options->password) : unset_ptr; + if (!(server->password= server_options->password ? + strdup_root(&mem, server_options->password) : unset_ptr)) + DBUG_RETURN(NULL); /* purecov: inspected */ /* set to 0 if not specified */ server->port= server_options->port > -1 ? server_options->port : 0; - server->socket= server_options->socket ? - strdup_root(&mem, server_options->socket) : unset_ptr; + if (!(server->socket= server_options->socket ? + strdup_root(&mem, server_options->socket) : unset_ptr)) + DBUG_RETURN(NULL); /* purecov: inspected */ - server->scheme= server_options->scheme ? - strdup_root(&mem, server_options->scheme) : unset_ptr; + if (!(server->scheme= server_options->scheme ? + strdup_root(&mem, server_options->scheme) : unset_ptr)) + DBUG_RETURN(NULL); /* purecov: inspected */ - server->owner= server_options->owner ? - strdup_root(&mem, server_options->owner) : unset_ptr; + if (!(server->owner= server_options->owner ? + strdup_root(&mem, server_options->owner) : unset_ptr)) + DBUG_RETURN(NULL); /* purecov: inspected */ - DBUG_VOID_RETURN; + DBUG_RETURN(server); } /* diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2c4251f0fde..b005615ce7c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -72,6 +72,9 @@ static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **), grant_names, NULL}; #endif +/* Match the values of enum ha_choice */ +static const char *ha_choice_values[] = {"", "0", "1"}; + static void store_key_options(THD *thd, String *packet, TABLE *table, KEY *key_info); @@ -1184,6 +1187,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, key_info= table->key_info; bzero((char*) &create_info, sizeof(create_info)); + /* Allow update_create_info to update row type */ + create_info.row_type= share->row_type; file->update_create_info(&create_info); primary_key= share->primary_key; @@ -1368,19 +1373,25 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" PACK_KEYS=1")); if (share->db_create_options & HA_OPTION_NO_PACK_KEYS) packet->append(STRING_WITH_LEN(" PACK_KEYS=0")); + /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ if (share->db_create_options & HA_OPTION_CHECKSUM) packet->append(STRING_WITH_LEN(" CHECKSUM=1")); + if (share->page_checksum != HA_CHOICE_UNDEF) + { + packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM=")); + packet->append(ha_choice_values[(uint) share->page_checksum], 1); + } if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE) packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1")); - if (share->row_type != ROW_TYPE_DEFAULT) + if (create_info.row_type != ROW_TYPE_DEFAULT) { packet->append(STRING_WITH_LEN(" ROW_FORMAT=")); - packet->append(ha_row_type[(uint) share->row_type]); + packet->append(ha_row_type[(uint) create_info.row_type]); } if (share->transactional != HA_CHOICE_UNDEF) { packet->append(STRING_WITH_LEN(" TRANSACTIONAL=")); - packet->append(share->transactional == HA_CHOICE_YES ? "1" : "0", 1); + packet->append(ha_choice_values[(uint) share->transactional], 1); } if (table->s->key_block_size) { @@ -3430,7 +3441,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, /* there was errors during opening tables */ - const char *error= thd->main_da.message(); + const char *error= thd->is_error() ? thd->main_da.message() : ""; if (tables->view) table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); else if (tables->schema_table) @@ -3494,8 +3505,12 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, ptr=strmov(ptr," pack_keys=1"); if (share->db_create_options & HA_OPTION_NO_PACK_KEYS) ptr=strmov(ptr," pack_keys=0"); + /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ if (share->db_create_options & HA_OPTION_CHECKSUM) ptr=strmov(ptr," checksum=1"); + if (share->page_checksum != HA_CHOICE_UNDEF) + ptr= strxmov(ptr, " page_checksum=", + ha_choice_values[(uint) share->page_checksum], NullS); if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE) ptr=strmov(ptr," delay_key_write=1"); if (share->row_type != ROW_TYPE_DEFAULT) @@ -3514,6 +3529,9 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, show_table->part_info->no_parts > 0) ptr= strmov(ptr, " partitioned"); #endif + if (share->transactional != HA_CHOICE_UNDEF) + ptr= strxmov(ptr, " transactional=", + ha_choice_values[(uint) share->transactional], NullS); table->field[19]->store(option_buff+1, (ptr == option_buff ? 0 : (uint) (ptr-option_buff)-1), cs); @@ -3631,8 +3649,9 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, I.e. we are in SELECT FROM INFORMATION_SCHEMA.COLUMS rather than in SHOW COLUMNS */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->main_da.sql_errno(), thd->main_da.message()); + if (thd->is_error()) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + thd->main_da.sql_errno(), thd->main_da.message()); thd->clear_error(); res= 0; } @@ -5276,8 +5295,14 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables, f_key_info->referenced_db->length, cs); table->field[10]->store(f_key_info->referenced_table->str, f_key_info->referenced_table->length, cs); - table->field[5]->store(f_key_info->referenced_key_name->str, - f_key_info->referenced_key_name->length, cs); + if (f_key_info->referenced_key_name) + { + table->field[5]->store(f_key_info->referenced_key_name->str, + f_key_info->referenced_key_name->length, cs); + table->field[5]->set_notnull(); + } + else + table->field[5]->set_null(); table->field[6]->store(STRING_WITH_LEN("NONE"), cs); table->field[7]->store(f_key_info->update_method->str, f_key_info->update_method->length, cs); @@ -5384,8 +5409,9 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx) @param thd thread handler - @param - schema_table pointer to 'shema_tables' element + + @param table_list Used to pass I_S table information(fields info, tables + parameters etc) and table name. @retval \# Pointer to created table @retval NULL Can't create table @@ -5436,6 +5462,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) DBUG_RETURN(NULL); break; case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: if (!(item= new Item_decimal((longlong) fields_info->value, false))) { DBUG_RETURN(0); @@ -5785,7 +5812,7 @@ int make_schema_select(THD *thd, SELECT_LEX *sel, { ST_SCHEMA_TABLE *schema_table= get_schema_table(schema_table_idx); LEX_STRING db, table; - DBUG_ENTER("mysql_schema_select"); + DBUG_ENTER("make_schema_select"); DBUG_PRINT("enter", ("mysql_schema_select: %s", schema_table->table_name)); /* We have to make non const db_name & table_name @@ -5880,9 +5907,11 @@ bool get_schema_tables_result(JOIN *join, { result= 1; join->error= 1; + tab->read_record.file= table_list->table->file; table_list->schema_table_state= executed_place; break; } + tab->read_record.file= table_list->table->file; table_list->schema_table_state= executed_place; } } @@ -6478,8 +6507,8 @@ ST_FIELD_INFO referential_constraints_fields_info[]= OPEN_FULL_TABLE}, {"UNIQUE_CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, - {"UNIQUE_CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, - OPEN_FULL_TABLE}, + {"UNIQUE_CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, + MY_I_S_MAYBE_NULL, 0, OPEN_FULL_TABLE}, {"MATCH_OPTION", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"UPDATE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"DELETE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 7fa3786c382..34b310931d6 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -840,6 +840,68 @@ outp: } +/** + Copy string with HEX-encoding of "bad" characters. + + @details This functions copies the string pointed by "src" + to the string pointed by "dst". Not more than "srclen" bytes + are read from "src". Any sequences of bytes representing + a not-well-formed substring (according to cs) are hex-encoded, + and all well-formed substrings (according to cs) are copied as is. + Not more than "dstlen" bytes are written to "dst". The number + of bytes written to "dst" is returned. + + @param cs character set pointer of the destination string + @param[out] dst destination string + @param dstlen size of dst + @param src source string + @param srclen length of src + + @retval result length +*/ + +size_t +my_copy_with_hex_escaping(CHARSET_INFO *cs, + char *dst, size_t dstlen, + const char *src, size_t srclen) +{ + const char *srcend= src + srclen; + char *dst0= dst; + + for ( ; src < srcend ; ) + { + size_t chlen; + if ((chlen= my_ismbchar(cs, src, srcend))) + { + if (dstlen < chlen) + break; /* purecov: inspected */ + memcpy(dst, src, chlen); + src+= chlen; + dst+= chlen; + dstlen-= chlen; + } + else if (*src & 0x80) + { + if (dstlen < 4) + break; /* purecov: inspected */ + *dst++= '\\'; + *dst++= 'x'; + *dst++= _dig_vec_upper[((unsigned char) *src) >> 4]; + *dst++= _dig_vec_upper[((unsigned char) *src) & 15]; + src++; + dstlen-= 4; + } + else + { + if (dstlen < 1) + break; /* purecov: inspected */ + *dst++= *src++; + dstlen--; + } + } + return dst - dst0; +} + /* copy a string, with optional character set conversion, diff --git a/sql/sql_string.h b/sql/sql_string.h index 128ed749b5f..b4d76a1779a 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -37,6 +37,9 @@ uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs, const char **well_formed_error_pos, const char **cannot_convert_error_pos, const char **from_end_pos); +size_t my_copy_with_hex_escaping(CHARSET_INFO *cs, + char *dst, size_t dstlen, + const char *src, size_t srclen); class String { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c62d0545fda..714701bbf3b 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -42,7 +42,7 @@ static int copy_data_between_tables(TABLE *from,TABLE *to, static bool prepare_blob_field(THD *thd, Create_field *sql_field); static bool check_engine(THD *, const char *, HA_CREATE_INFO *); -static bool +static int mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Alter_info *alter_info, bool tmp_table, @@ -1226,8 +1226,12 @@ uint build_table_shadow_filename(char *buff, size_t bufflen, flags Flags as defined below WFRM_INITIAL_WRITE If set we need to prepare table before creating the frm file - WFRM_CREATE_HANDLER_FILES If set we need to create the handler file as - part of the creation of the frm file + WFRM_INSTALL_SHADOW If set we should install the new frm + WFRM_KEEP_SHARE If set we know that the share is to be + retained and thus we should ensure share + object is correct, if not set we don't + set the new partition syntax string since + we know the share object is destroyed. WFRM_PACK_FRM If set we should pack the frm file and delete the frm file @@ -1370,7 +1374,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) goto err; } #ifdef WITH_PARTITION_STORAGE_ENGINE - if (part_info) + if (part_info && (flags & WFRM_KEEP_SHARE)) { TABLE_SHARE *share= lpt->table->s; char *tmp_part_syntax_str; @@ -2173,7 +2177,7 @@ int prepare_create_field(Create_field *sql_field, TRUE error */ -static bool +static int mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Alter_info *alter_info, bool tmp_table, @@ -3322,8 +3326,9 @@ bool mysql_create_table_no_lock(THD *thd, } } } - DBUG_PRINT("info", ("db_type = %d", - ha_legacy_type(part_info->default_engine_type))); + DBUG_PRINT("info", ("db_type = %s create_info->db_type = %s", + ha_resolve_storage_engine_name(part_info->default_engine_type), + ha_resolve_storage_engine_name(create_info->db_type))); if (part_info->check_partition_info(thd, &engine_type, file, create_info, TRUE)) goto err; @@ -3347,8 +3352,8 @@ bool mysql_create_table_no_lock(THD *thd, The handler assigned to the table cannot handle partitioning. Assign the partition handler as the handler of the table. */ - DBUG_PRINT("info", ("db_type: %d", - ha_legacy_type(create_info->db_type))); + DBUG_PRINT("info", ("db_type: %s", + ha_resolve_storage_engine_name(create_info->db_type))); delete file; create_info->db_type= partition_hton; if (!(file= get_ha_partition(part_info))) @@ -3511,8 +3516,36 @@ bool mysql_create_table_no_lock(THD *thd, thd_proc_info(thd, "creating table"); create_info->table_existed= 0; // Mark that table is created - if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) +#ifdef HAVE_READLINK + if (test_if_data_home_dir(create_info->data_file_name)) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY"); + goto unlock_and_end; + } + if (test_if_data_home_dir(create_info->index_file_name)) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY"); + goto unlock_and_end; + } + +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (check_partition_dirs(thd->lex->part_info)) + { + goto unlock_and_end; + } +#endif /* WITH_PARTITION_STORAGE_ENGINE */ + + if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)) +#endif /* HAVE_READLINK */ + { + if (create_info->data_file_name) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "DATA DIRECTORY option ignored"); + if (create_info->index_file_name) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "INDEX DIRECTORY option ignored"); create_info->data_file_name= create_info->index_file_name= 0; + } create_info->table_options=db_options; path[path_length - reg_ext_length]= '\0'; // Remove .frm extension @@ -3775,8 +3808,9 @@ mysql_rename_table(handlerton *base, const char *old_db, wait_while_table_is_used() thd Thread handler table Table to remove from cache - function HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted - HA_EXTRA_FORCE_REOPEN if table is not be used + function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted + HA_EXTRA_FORCE_REOPEN if table is not be used + HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed NOTES When returning, the table will be unusable for other threads until the table is closed. @@ -3828,7 +3862,7 @@ void close_cached_table(THD *thd, TABLE *table) { DBUG_ENTER("close_cached_table"); - wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE); + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); /* Close lock if this is not got with LOCK TABLES */ if (thd->lock) { @@ -6716,7 +6750,7 @@ view_err: if (lower_case_table_names) my_casedn_str(files_charset_info, old_name); - wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE); + wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME); close_data_files_and_morph_locks(thd, db, table_name); error=0; @@ -7193,7 +7227,6 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list) table_list->table= NULL; bzero((char*) &create_info, sizeof(create_info)); - create_info.db_type= 0; create_info.row_type=ROW_TYPE_NOT_USED; create_info.default_table_charset=default_charset_info; /* Force alter table to recreate table */ diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 0bce4eb4a82..1a522b852e9 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -459,7 +459,7 @@ void mysql_print_status() VOID(my_getwd(current_dir, sizeof(current_dir),MYF(0))); printf("Current dir: %s\n", current_dir); printf("Running threads: %d Stack size: %ld\n", thread_count, - (long) thread_stack); + (long) my_thread_stack_size); thr_print_locks(); // Write some debug info #ifndef DBUG_OFF print_cached_tables(); @@ -536,7 +536,7 @@ Estimated memory (with thread stack): %ld\n", (int) info.uordblks, (int) info.fordblks, (int) info.keepcost, - (long) (thread_count * thread_stack + info.hblkhd + info.arena)); + (long) (thread_count * my_thread_stack_size + info.hblkhd + info.arena)); #endif Events::dump_internal_status(); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index eae755ca87a..23ad5ce9385 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -859,6 +859,19 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_prepare_update"); + /* + Statement-based replication of UPDATE ... LIMIT is not safe as order of + rows is not defined, so in mixed mode we go to row-based. + + Note that we may consider a statement as safe if ORDER BY primary_key + is present. However it may confuse users to see very similiar statements + replicated differently. + */ + if (thd->lex->current_select->select_limit) + { + thd->lex->set_stmt_unsafe(); + thd->set_current_stmt_binlog_row_based_if_mixed(); + } #ifndef NO_EMBEDDED_ACCESS_CHECKS table_list->grant.want_privilege= table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); @@ -930,7 +943,7 @@ static table_map get_table_map(List<Item> *items) TRUE Error */ -bool mysql_multi_update_prepare(THD *thd) +int mysql_multi_update_prepare(THD *thd) { LEX *lex= thd->lex; TABLE_LIST *table_list= lex->query_tables; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 98434d15d87..896b0b26bfa 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -487,6 +487,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, enum enum_tx_isolation tx_isolation; enum Cast_target cast_type; enum Item_udftype udf_type; + enum ha_choice choice; CHARSET_INFO *charset; thr_lock_type lock_type; interval_type interval, interval_time_st; @@ -881,6 +882,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token OWNER_SYM %token PACK_KEYS_SYM %token PAGE_SYM +%token PAGE_CHECKSUM_SYM %token PARAM_MARKER %token PARSER_SYM %token PARTIAL /* SQL-2003-N */ @@ -1021,6 +1023,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token TABLESPACE %token TABLE_REF_PRIORITY %token TABLE_SYM /* SQL-2003-R */ +%token TABLE_CHECKSUM_SYM %token TEMPORARY /* SQL-2003-N */ %token TEMPTABLE_SYM %token TERMINATED @@ -1156,6 +1159,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <ulonglong_number> ulonglong_num real_ulonglong_num size_number +%type <choice> choice + %type <p_elem_value> part_bit_expr @@ -1212,6 +1217,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <interval_time_st> interval_time_st +%type <interval_time_st> interval_time_stamp + %type <db_type> storage_engines known_storage_engines %type <row_type> row_types @@ -4345,6 +4352,16 @@ create_table_option: Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; } + | TABLE_CHECKSUM_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; + Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; + } + | PAGE_CHECKSUM_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_PAGE_CHECKSUM; + Lex->create_info.page_checksum= $3; + } | DELAY_KEY_WRITE_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; @@ -4355,7 +4372,7 @@ create_table_option: Lex->create_info.row_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; } - | UNION_SYM opt_equal '(' table_list ')' + | UNION_SYM opt_equal '(' opt_table_list ')' { /* Move the union list to the merge_list */ LEX *lex=Lex; @@ -4404,11 +4421,10 @@ create_table_option: Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE; Lex->create_info.key_block_size= $3; } - | TRANSACTIONAL_SYM opt_equal ulong_num + | TRANSACTIONAL_SYM opt_equal choice { - Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL; - Lex->create_info.transactional= ($3 != 0 ? HA_CHOICE_YES : - HA_CHOICE_NO); + Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL; + Lex->create_info.transactional= $3; } ; @@ -6993,9 +7009,9 @@ function_call_nonkeyword: $$= new (YYTHD->mem_root) Item_func_now_local($3); Lex->safe_to_cache_query=0; } - | TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')' + | TIMESTAMP_ADD '(' interval_time_stamp ',' expr ',' expr ')' { $$= new (YYTHD->mem_root) Item_date_add_interval($7,$5,$3,0); } - | TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')' + | TIMESTAMP_DIFF '(' interval_time_stamp ',' expr ',' expr ')' { $$= new (YYTHD->mem_root) Item_func_timestamp_diff($5,$7,$3); } | UTC_DATE_SYM optional_braces { @@ -7963,22 +7979,41 @@ interval: | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; } | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; } | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; } - | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; } | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; } | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; } | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; } | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; } ; +interval_time_stamp: + interval_time_st {} + | FRAC_SECOND_SYM { + $$=INTERVAL_MICROSECOND; + /* + FRAC_SECOND was mistakenly implemented with + a wrong resolution. According to the ODBC + standard it should be nanoseconds, not + microseconds. Changing it to nanoseconds + in MySQL would mean making TIMESTAMPDIFF + and TIMESTAMPADD to return DECIMAL, since + the return value would be too big for BIGINT + Hence we just deprecate the incorrect + implementation without changing its + resolution. + */ + WARN_DEPRECATED(yythd, "6.2", "FRAC_SECOND", "MICROSECOND"); + } + ; + interval_time_st: DAY_SYM { $$=INTERVAL_DAY; } | WEEK_SYM { $$=INTERVAL_WEEK; } | HOUR_SYM { $$=INTERVAL_HOUR; } - | FRAC_SECOND_SYM { $$=INTERVAL_MICROSECOND; } | MINUTE_SYM { $$=INTERVAL_MINUTE; } | MONTH_SYM { $$=INTERVAL_MONTH; } | QUARTER_SYM { $$=INTERVAL_QUARTER; } | SECOND_SYM { $$=INTERVAL_SECOND; } + | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; } | YEAR_SYM { $$=INTERVAL_YEAR; } ; @@ -8287,6 +8322,11 @@ dec_num: | FLOAT_NUM ; +choice: + ulong_num { $$= $1 != 0 ? HA_CHOICE_YES : HA_CHOICE_NO; } + | DEFAULT { $$= HA_CHOICE_UNDEF; } + ; + procedure_clause: /* empty */ | PROCEDURE ident /* Procedure name */ @@ -10600,6 +10640,7 @@ keyword_sp: | ONE_SYM {} | PACK_KEYS_SYM {} | PAGE_SYM {} + | PAGE_CHECKSUM_SYM {} | PARTIAL {} | PARTITIONING_SYM {} | PARTITIONS_SYM {} @@ -10669,6 +10710,7 @@ keyword_sp: | SWAPS_SYM {} | SWITCHES_SYM {} | TABLES {} + | TABLE_CHECKSUM_SYM {} | TABLESPACE {} | TEMPORARY {} | TEMPTABLE_SYM {} diff --git a/sql/structs.h b/sql/structs.h index f14cca3c5db..809175fdde4 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -314,8 +314,27 @@ private: */ Discrete_interval *current; uint elements; // number of elements + + /* helper function for copy construct and assignment operator */ + void copy_(const Discrete_intervals_list& from) + { + for (Discrete_interval *i= from.head; i; i= i->next) + { + Discrete_interval j= *i; + append(&j); + } + } public: Discrete_intervals_list() : head(NULL), current(NULL), elements(0) {}; + Discrete_intervals_list(const Discrete_intervals_list& from) + { + copy_(from); + } + void operator=(const Discrete_intervals_list& from) + { + empty(); + copy_(from); + } void empty_no_free() { head= current= NULL; @@ -331,6 +350,7 @@ public: } empty_no_free(); } + const Discrete_interval* get_next() { Discrete_interval *tmp= current; @@ -340,6 +360,7 @@ public: } ~Discrete_intervals_list() { empty(); }; bool append(ulonglong start, ulonglong val, ulonglong incr); + bool append(Discrete_interval *interval); ulonglong minimum() const { return (head ? head->minimum() : 0); }; ulonglong maximum() const { return (head ? tail->maximum() : 0); }; uint nb_elements() const { return elements; } diff --git a/sql/table.cc b/sql/table.cc index cacb3a94582..ebf3dfd748e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -711,7 +711,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!head[32]) // New frm file in 3.23 { share->avg_row_length= uint4korr(head+34); - share->transactional= (ha_choice) head[39]; + share->transactional= (ha_choice) (head[39] & 3); + share->page_checksum= (ha_choice) ((head[39] >> 2) & 3); share->row_type= (row_type) head[40]; share->table_charset= get_charset((uint) head[38],MYF(0)); share->null_field_first= 1; @@ -892,26 +893,31 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, ha_legacy_type(share->db_type()))); } #ifdef WITH_PARTITION_STORAGE_ENGINE - else + else if (str_db_type_length == 9 && + !strncmp((char *) next_chunk + 2, "partition", 9)) { - LEX_STRING pname= { C_STRING_WITH_LEN( "partition" ) }; - if (str_db_type_length == pname.length && - !strncmp((char *) next_chunk + 2, pname.str, pname.length)) - { - /* - Use partition handler - tmp_plugin is locked with a local lock. - we unlock the old value of share->db_plugin before - replacing it with a globally locked version of tmp_plugin - */ - plugin_unlock(NULL, share->db_plugin); - share->db_plugin= ha_lock_engine(NULL, partition_hton); - DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)", - str_db_type_length, next_chunk + 2, - ha_legacy_type(share->db_type()))); - } + /* + Use partition handler + tmp_plugin is locked with a local lock. + we unlock the old value of share->db_plugin before + replacing it with a globally locked version of tmp_plugin + */ + plugin_unlock(NULL, share->db_plugin); + share->db_plugin= ha_lock_engine(NULL, partition_hton); + DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)", + str_db_type_length, next_chunk + 2, + ha_legacy_type(share->db_type()))); } #endif + else if (!tmp_plugin) + { + /* purecov: begin inspected */ + error= 8; + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str); + my_free(buff, MYF(0)); + goto err; + /* purecov: end */ + } next_chunk+= str_db_type_length + 2; } if (next_chunk + 5 < buff_end) @@ -2200,6 +2206,8 @@ void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg) "of MySQL and cannot be read", MYF(0), buff); break; + case 8: + break; default: /* Better wrong error than none */ case 4: strxmov(buff, share->normalized_path.str, reg_ext, NullS); @@ -2447,7 +2455,9 @@ File create_frm(THD *thd, const char *name, const char *db, int2store(fileinfo+16,reclength); int4store(fileinfo+18,create_info->max_rows); int4store(fileinfo+22,create_info->min_rows); + /* fileinfo[26] is set in mysql_create_frm() */ fileinfo[27]=2; // Use long pack-fields + /* fileinfo[28 & 29] is set to key_info_length in mysql_create_frm() */ create_info->table_options|=HA_OPTION_LONG_BLOB_PTR; // Use portable blob pointers int2store(fileinfo+30,create_info->table_options); fileinfo[32]=0; // No filename anymore @@ -2455,9 +2465,10 @@ File create_frm(THD *thd, const char *name, const char *db, int4store(fileinfo+34,create_info->avg_row_length); fileinfo[38]= (create_info->default_table_charset ? create_info->default_table_charset->number : 0); - fileinfo[39]= (uchar) create_info->transactional; + fileinfo[39]= (uchar) ((uint) create_info->transactional | + ((uint) create_info->page_checksum << 2)); fileinfo[40]= (uchar) create_info->row_type; - /* Next few bytes were for RAID support */ + /* Next few bytes where for RAID support */ fileinfo[41]= 0; fileinfo[42]= 0; fileinfo[43]= 0; diff --git a/sql/table.h b/sql/table.h index 941964ee24e..0e0a35b022d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -311,6 +311,7 @@ typedef struct st_table_share enum row_type row_type; /* How rows are stored */ enum tmp_table_type tmp_table; enum ha_choice transactional; + enum ha_choice page_checksum; uint ref_count; /* How many TABLE objects uses this */ uint open_count; /* Number of tables in open list */ @@ -759,12 +760,36 @@ enum enum_schema_tables typedef struct st_field_info { + /** + This is used as column name. + */ const char* field_name; + /** + For string-type columns, this is the maximum number of + characters. Otherwise, it is the 'display-length' for the column. + */ uint field_length; + /** + This denotes data type for the column. For the most part, there seems to + be one entry in the enum for each SQL data type, although there seem to + be a number of additional entries in the enum. + */ enum enum_field_types field_type; int value; + /** + This is used to set column attributes. By default, columns are @c NOT + @c NULL and @c SIGNED, and you can deviate from the default + by setting the appopriate flags. You can use either one of the flags + @c MY_I_S_MAYBE_NULL and @cMY_I_S_UNSIGNED or + combine them using the bitwise or operator @c |. Both flags are + defined in table.h. + */ uint field_flags; // Field atributes(maybe_null, signed, unsigned etc.) const char* old_name; + /** + This should be one of @c SKIP_OPEN_TABLE, + @c OPEN_FRM_ONLY or @c OPEN_FULL_TABLE. + */ uint open_method; } ST_FIELD_INFO; diff --git a/sql/unireg.cc b/sql/unireg.cc index 38f09a7946c..9e42aabfef7 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -328,8 +328,10 @@ bool mysql_create_frm(THD *thd, const char *file_name, my_free(keybuff, MYF(0)); if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE) && - my_sync(file, MYF(MY_WME))) - goto err2; + (my_sync(file, MYF(MY_WME)) || + my_sync_dir_by_file(file_name, MYF(MY_WME)))) + goto err2; + if (my_close(file,MYF(MY_WME))) goto err3; @@ -979,9 +981,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type, type= (Field::utype) MTYP_TYPENR(field->unireg_check); - if (field->def && - (regfield->real_type() != MYSQL_TYPE_YEAR || - field->def->val_int() != 0)) + if (field->def) { int res= field->def->save_in_field(regfield, 1); /* If not ok or warning of level 'note' */ diff --git a/sql/unireg.h b/sql/unireg.h index 2e0172ce31d..18c3ab16f6a 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -35,8 +35,8 @@ #ifndef SHAREDIR #define SHAREDIR "share/" #endif -#ifndef LIBDIR -#define LIBDIR "lib/" +#ifndef PLUGINDIR +#define PLUGINDIR "lib/plugin" #endif #define ER(X) errmesg[(X) - ER_ERROR_FIRST] @@ -108,7 +108,6 @@ #define READ_RECORD_BUFFER (uint) (IO_SIZE*8) /* Pointer_buffer_size */ #define DISK_BUFFER_SIZE (uint) (IO_SIZE*16) /* Size of diskbuffer */ -#define POSTFIX_ERROR DBL_MAX #define ME_INFO (ME_HOLDTANG+ME_OLDWIN+ME_NOREFRESH) #define ME_ERROR (ME_BELL+ME_OLDWIN+ME_NOREFRESH) |