diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.cc | 8 | ||||
-rw-r--r-- | sql/field.h | 3 | ||||
-rw-r--r-- | sql/filesort.cc | 14 | ||||
-rw-r--r-- | sql/handler.h | 6 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 4 | ||||
-rw-r--r-- | sql/item_func.cc | 9 | ||||
-rw-r--r-- | sql/item_sum.cc | 14 | ||||
-rw-r--r-- | sql/lex_string.h | 6 | ||||
-rw-r--r-- | sql/mf_iocache_encr.cc | 15 | ||||
-rw-r--r-- | sql/my_decimal.h | 6 | ||||
-rw-r--r-- | sql/opt_range.cc | 107 | ||||
-rw-r--r-- | sql/opt_split.cc | 12 | ||||
-rw-r--r-- | sql/opt_sum.cc | 24 | ||||
-rw-r--r-- | sql/sql_base.cc | 2 | ||||
-rw-r--r-- | sql/sql_class.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.h | 2 | ||||
-rw-r--r-- | sql/sql_join_cache.cc | 3 | ||||
-rw-r--r-- | sql/sql_lex.cc | 3 | ||||
-rw-r--r-- | sql/sql_parse.cc | 64 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 196 | ||||
-rw-r--r-- | sql/sql_select.cc | 6 | ||||
-rw-r--r-- | sql/sql_string.cc | 11 | ||||
-rw-r--r-- | sql/sql_type.h | 16 | ||||
-rw-r--r-- | sql/sys_vars.ic | 6 | ||||
-rw-r--r-- | sql/table.h | 3 | ||||
-rw-r--r-- | sql/unireg.cc | 7 | ||||
-rw-r--r-- | sql/unireg.h | 3 |
27 files changed, 465 insertions, 88 deletions
diff --git a/sql/field.cc b/sql/field.cc index 4a82eae6a0e..a7d5f6b3328 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8674,7 +8674,10 @@ int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, b_length=get_length(b_ptr); if (b_length > max_length) b_length=max_length; - diff=memcmp(a,b,MY_MIN(a_length,b_length)); + if (uint32 len= MY_MIN(a_length,b_length)) + diff= memcmp(a,b,len); + else + diff= 0; return diff ? diff : (int) (a_length - b_length); } @@ -8731,7 +8734,8 @@ uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg) length=(uint) blob_length; } int2store(buff,length); - memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length); + if (length) + memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length); return HA_KEY_BLOB_LENGTH+length; } diff --git a/sql/field.h b/sql/field.h index b3bc2d4dbea..a512d74b444 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1394,6 +1394,8 @@ public: virtual uint max_packed_col_length(uint max_length) { return max_length;} + virtual bool is_packable() const { return false; } + uint offset(const uchar *record) const { return (uint) (ptr - record); @@ -1986,6 +1988,7 @@ public: bool can_optimize_range(const Item_bool_func *cond, const Item *item, bool is_eq_func) const; + bool is_packable() const { return true; } }; /* base class for float and double and decimal (old one) */ diff --git a/sql/filesort.cc b/sql/filesort.cc index aa25474be1a..ad4cb2b6e6b 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1978,7 +1978,14 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, if (sortorder->field) { CHARSET_INFO *cs= sortorder->field->sort_charset(); + sortorder->type= sortorder->field->is_packable() ? + SORT_FIELD_ATTR::VARIABLE_SIZE : + SORT_FIELD_ATTR::FIXED_SIZE; + sortorder->length= sortorder->field->sort_length(); + if (sortorder->is_variable_sized()) + set_if_smaller(sortorder->length, thd->variables.max_sort_length); + if (use_strnxfrm((cs=sortorder->field->sort_charset()))) { *multi_byte_charset= true; @@ -1989,6 +1996,10 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, } else { + sortorder->type= sortorder->item->type_handler()->is_packable() ? + SORT_FIELD_ATTR::VARIABLE_SIZE : + SORT_FIELD_ATTR::FIXED_SIZE; + sortorder->item->type_handler()->sortlength(thd, sortorder->item, sortorder); if (use_strnxfrm(sortorder->item->collation.collation)) @@ -1998,7 +2009,8 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, if (sortorder->item->maybe_null) length++; // Place for NULL marker } - set_if_smaller(sortorder->length, thd->variables.max_sort_length); + if (sortorder->is_variable_sized()) + set_if_smaller(sortorder->length, thd->variables.max_sort_length); length+=sortorder->length; } sortorder->field= (Field*) 0; // end marker diff --git a/sql/handler.h b/sql/handler.h index 3b0cedfcd96..c516ea4bfc4 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -841,8 +841,10 @@ struct xid_t { void set(long f, const char *g, long gl, const char *b, long bl) { formatID= f; - memcpy(data, g, gtrid_length= gl); - memcpy(data+gl, b, bqual_length= bl); + if ((gtrid_length= gl)) + memcpy(data, g, gl); + if ((bqual_length= bl)) + memcpy(data+gl, b, bl); } void set(ulonglong xid) { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 2904980c825..bf992886eda 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -866,6 +866,8 @@ int Arg_comparator::compare_decimal() { if (set_null) owner->null_value= 0; + val1.round_self_if_needed((*a)->decimals, HALF_UP); + val2.round_self_if_needed((*b)->decimals, HALF_UP); return val1.cmp(val2); } } @@ -888,6 +890,8 @@ int Arg_comparator::compare_e_decimal() VDec val1(*a), val2(*b); if (val1.is_null() || val2.is_null()) return MY_TEST(val1.is_null() && val2.is_null()); + val1.round_self_if_needed((*a)->decimals, HALF_UP); + val2.round_self_if_needed((*b)->decimals, HALF_UP); return MY_TEST(val1.cmp(val2) == 0); } diff --git a/sql/item_func.cc b/sql/item_func.cc index 7a15f919742..837156ce39a 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1481,14 +1481,13 @@ double Item_func_div::real_op() my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) { int err; - my_decimal tmp; VDec2_lazy val(args[0], args[1]); if ((null_value= val.has_null())) return 0; if ((err= check_decimal_overflow(my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW & ~E_DEC_DIV_ZERO, - &tmp, + decimal_value, val.m_a.ptr(), val.m_b.ptr(), prec_increment))) > 3) { @@ -1497,7 +1496,6 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) null_value= 1; return 0; } - tmp.round_to(decimal_value, decimals, HALF_UP); return decimal_value; } @@ -3928,6 +3926,8 @@ int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex) timeout= m_abs_timeout; error= mysql_cond_timedwait(cond, mutex, &timeout); + if (m_thd->check_killed()) + break; if (error == ETIMEDOUT || error == ETIME) { /* Return error if timed out or connection is broken. */ @@ -4774,7 +4774,8 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length, length--; // Fix length change above entry->value[length]= 0; // Store end \0 } - memmove(entry->value, ptr, length); + if (length) + memmove(entry->value, ptr, length); if (type == DECIMAL_RESULT) ((my_decimal*)entry->value)->fix_buffer_pointer(); entry->length= length; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b980923d03d..e00fc2fd3ab 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2008, 2015, MariaDB + Copyright (c) 2008, 2020, MariaDB 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 @@ -472,7 +472,8 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): if (!(orig_args= (Item**) thd->alloc(sizeof(Item*)*arg_count))) return; } - memcpy(orig_args, item->orig_args, sizeof(Item*)*arg_count); + if (arg_count) + memcpy(orig_args, item->orig_args, sizeof(Item*)*arg_count); init_aggregator(); with_distinct= item->with_distinct; if (item->aggr) @@ -1131,7 +1132,8 @@ Item_sum_num::fix_fields(THD *thd, Item **ref) check_sum_func(thd, ref)) return TRUE; - memcpy (orig_args, args, sizeof (Item *) * arg_count); + if (arg_count) + memcpy (orig_args, args, sizeof (Item *) * arg_count); fixed= 1; return FALSE; } @@ -1363,7 +1365,8 @@ Item_sum_sp::fix_fields(THD *thd, Item **ref) if (check_sum_func(thd, ref)) return TRUE; - memcpy(orig_args, args, sizeof(Item *) * arg_count); + if (arg_count) + memcpy(orig_args, args, sizeof(Item *) * arg_count); fixed= 1; return FALSE; } @@ -3779,7 +3782,8 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, /* orig_args is only used for print() */ orig_args= (Item**) (order + arg_count_order); - memcpy(orig_args, args, sizeof(Item*) * arg_count); + if (arg_count) + memcpy(orig_args, args, sizeof(Item*) * arg_count); if (limit_clause) { row_limit= row_limit_arg; diff --git a/sql/lex_string.h b/sql/lex_string.h index 88a7154b064..b638e7111f8 100644 --- a/sql/lex_string.h +++ b/sql/lex_string.h @@ -34,12 +34,12 @@ static inline bool lex_string_cmp(CHARSET_INFO *charset, const LEX_CSTRING *a, static inline bool cmp(const LEX_CSTRING *a, const LEX_CSTRING *b) { - return (a->length != b->length || - memcmp(a->str, b->str, a->length)); + return a->length != b->length || + (a->length && memcmp(a->str, b->str, a->length)); } static inline bool cmp(const LEX_CSTRING a, const LEX_CSTRING b) { - return a.length != b.length || memcmp(a.str, b.str, a.length); + return a.length != b.length || (a.length && memcmp(a.str, b.str, a.length)); } /* diff --git a/sql/mf_iocache_encr.cc b/sql/mf_iocache_encr.cc index 072c0a48269..63830ec620a 100644 --- a/sql/mf_iocache_encr.cc +++ b/sql/mf_iocache_encr.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2015, MariaDB + Copyright (c) 2015, 2020, MariaDB 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 @@ -85,7 +85,6 @@ static int my_b_encr_read(IO_CACHE *info, uchar *Buffer, size_t Count) do { - size_t copied; uint elength, wlength, length; uchar iv[MY_AES_BLOCK_SIZE]= {0}; @@ -116,11 +115,13 @@ static int my_b_encr_read(IO_CACHE *info, uchar *Buffer, size_t Count) DBUG_ASSERT(length <= info->buffer_length); - copied= MY_MIN(Count, (size_t)(length - pos_offset)); - - memcpy(Buffer, info->buffer + pos_offset, copied); - Count-= copied; - Buffer+= copied; + size_t copied= MY_MIN(Count, (size_t)(length - pos_offset)); + if (copied) + { + memcpy(Buffer, info->buffer + pos_offset, copied); + Count-= copied; + Buffer+= copied; + } info->read_pos= info->buffer + pos_offset + copied; info->read_end= info->buffer + length; diff --git a/sql/my_decimal.h b/sql/my_decimal.h index a7f6fc9e88d..9b1aebb98b5 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -207,15 +207,15 @@ public: { return to_string(to, 0, 0, 0); } - String *to_string_round(String *to, uint scale, my_decimal *round_buff) const + String *to_string_round(String *to, int scale, my_decimal *round_buff) const { (void) round_to(round_buff, scale, HALF_UP); // QQ: check result? return round_buff->to_string(to); } - int round_to(my_decimal *to, uint scale, decimal_round_mode mode, + int round_to(my_decimal *to, int scale, decimal_round_mode mode, int mask= E_DEC_FATAL_ERROR) const { - return check_result(mask, decimal_round(this, to, (int) scale, mode)); + return check_result(mask, decimal_round(this, to, scale, mode)); } int to_binary(uchar *bin, int prec, int scale, uint mask= E_DEC_FATAL_ERROR) const; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 2156b877ace..7a45e59b6e4 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -15628,6 +15628,113 @@ static void print_ror_scans_arr(TABLE *table, const char *msg, DBUG_VOID_RETURN; } +static String dbug_print_sel_arg_buf; + +static void +print_sel_arg_key(Field *field, const uchar *key, String *out) +{ + TABLE *table= field->table; + my_bitmap_map *old_sets[2]; + dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set); + + if (field->real_maybe_null()) + { + if (*key) + { + out->append("NULL"); + goto end; + } + key++; // Skip null byte + } + + field->set_key_image(key, field->pack_length()); + + if (field->type() == MYSQL_TYPE_BIT) + (void) field->val_int_as_str(out, 1); + else + field->val_str(out); + +end: + dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); +} + + +/* + @brief + Produce a string representation of an individual SEL_ARG and return pointer + to it + + @detail + Intended usage: + + (gdb) p dbug_print_sel_arg(ptr) +*/ + +const char *dbug_print_sel_arg(SEL_ARG *sel_arg) +{ + StringBuffer<64> buf; + String &out= dbug_print_sel_arg_buf; + out.length(0); + + if (!sel_arg) + { + out.append("NULL"); + goto end; + } + + out.append("SEL_ARG("); + + const char *stype; + switch(sel_arg->type) { + case SEL_ARG::IMPOSSIBLE: + stype="IMPOSSIBLE"; + break; + case SEL_ARG::MAYBE: + stype="MAYBE"; + break; + case SEL_ARG::MAYBE_KEY: + stype="MAYBE_KEY"; + break; + case SEL_ARG::KEY_RANGE: + default: + stype= NULL; + } + + if (stype) + { + out.append("type="); + out.append(stype); + goto end; + } + + if (sel_arg->min_flag & NO_MIN_RANGE) + out.append("-inf"); + else + { + print_sel_arg_key(sel_arg->field, sel_arg->min_value, &buf); + out.append(buf); + } + + out.append((sel_arg->min_flag & NEAR_MIN)? "<" : "<="); + + out.append(sel_arg->field->field_name); + + out.append((sel_arg->max_flag & NEAR_MAX)? "<" : "<="); + + if (sel_arg->max_flag & NO_MAX_RANGE) + out.append("+inf"); + else + { + print_sel_arg_key(sel_arg->field, sel_arg->max_value, &buf); + out.append(buf); + } + + out.append(")"); + +end: + return dbug_print_sel_arg_buf.c_ptr_safe(); +} + /***************************************************************************** ** Print a quick range for debugging diff --git a/sql/opt_split.cc b/sql/opt_split.cc index c70fac49930..6807a623b73 100644 --- a/sql/opt_split.cc +++ b/sql/opt_split.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2017 MariaDB + Copyright (c) 2017, 2020, MariaDB 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 @@ -751,13 +751,13 @@ void JOIN::add_keyuses_for_splitting() added_keyuse_count)) goto err; - memcpy(keyuse.buffer, - save_qep->keyuse.buffer, - (size_t) save_qep->keyuse.elements * keyuse.size_of_element); - keyuse.elements= save_qep->keyuse.elements; + idx= keyuse.elements= save_qep->keyuse.elements; + if (keyuse.elements) + memcpy(keyuse.buffer, + save_qep->keyuse.buffer, + (size_t) keyuse.elements * keyuse.size_of_element); keyuse_ext= &ext_keyuses_for_splitting->at(0); - idx= save_qep->keyuse.elements; for (i=0; i < added_keyuse_count; i++, keyuse_ext++, idx++) { set_dynamic(&keyuse, (KEYUSE *) keyuse_ext, idx); diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 0a3c30a176d..af2d9ddc2e7 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -399,20 +399,28 @@ int opt_sum_query(THD *thd, break; } longlong info_limit= 1; - table->file->info_push(INFO_KIND_FORCE_LIMIT_BEGIN, &info_limit); - if (likely(!(error= table->file->ha_index_init((uint) ref.key, 1)))) - error= (is_max ? - get_index_max_value(table, &ref, range_fl) : - get_index_min_value(table, &ref, item_field, range_fl, - prefix_len)); + error= 0; + table->file->info_push(INFO_KIND_FORCE_LIMIT_BEGIN, &info_limit); + if (!table->const_table) + { + if (likely(!(error= table->file->ha_index_init((uint) ref.key, + 1)))) + error= (is_max ? + get_index_max_value(table, &ref, range_fl) : + get_index_min_value(table, &ref, item_field, range_fl, + prefix_len)); + } /* Verify that the read tuple indeed matches the search key */ if (!error && reckey_in_range(is_max, &ref, item_field->field, conds, range_fl, prefix_len)) error= HA_ERR_KEY_NOT_FOUND; - table->file->ha_end_keyread(); - table->file->ha_index_end(); + if (!table->const_table) + { + table->file->ha_end_keyread(); + table->file->ha_index_end(); + } table->file->info_push(INFO_KIND_FORCE_LIMIT_END, NULL); if (error) { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9648f8922c2..4be70abb7db 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2020,7 +2020,7 @@ retry_share: #ifdef WITH_WSREP if (!((flags & MYSQL_OPEN_IGNORE_FLUSH) || - (wsrep_on(thd) && thd->wsrep_applier))) + (thd->wsrep_applier))) #else if (!(flags & MYSQL_OPEN_IGNORE_FLUSH)) #endif diff --git a/sql/sql_class.cc b/sql/sql_class.cc index ab9a0118bde..041574485fe 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -4887,7 +4887,8 @@ extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen) if (!mysql_mutex_trylock(&thd->LOCK_thd_data)) { len= MY_MIN(buflen - 1, thd->query_length()); - memcpy(buf, thd->query(), len); + if (len) + memcpy(buf, thd->query(), len); mysql_mutex_unlock(&thd->LOCK_thd_data); } buf[len]= '\0'; diff --git a/sql/sql_class.h b/sql/sql_class.h index fb22b0bcf6d..8c479db8936 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6088,6 +6088,8 @@ struct SORT_FIELD_ATTR { uint length; /* Length of sort field */ uint suffix_length; /* Length suffix (0-4) */ + enum Type { FIXED_SIZE, VARIABLE_SIZE } type; + bool is_variable_sized() { return type == VARIABLE_SIZE; } }; diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index e9ad53850dd..20ed976daab 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -1396,7 +1396,8 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) blob_field->get_image(cp, copy->length, blob_field->charset()); DBUG_ASSERT(cp + copy->length + copy->blob_length <= buff + buff_size); - memcpy(cp+copy->length, copy->str, copy->blob_length); + if (copy->blob_length) + memcpy(cp+copy->length, copy->str, copy->blob_length); cp+= copy->length+copy->blob_length; } break; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index cc66ae6b853..00a45907f2f 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -9553,7 +9553,8 @@ bool LEX::new_sp_instr_stmt(THD *thd, qbuff.length= prefix.length + suffix.length; if (!(qbuff.str= (char*) alloc_root(thd->mem_root, qbuff.length + 1))) return true; - memcpy(qbuff.str, prefix.str, prefix.length); + if (prefix.length) + memcpy(qbuff.str, prefix.str, prefix.length); strmake(qbuff.str + prefix.length, suffix.str, suffix.length); i->m_query= qbuff; return sphead->add_instr(i); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 6eb147d0480..8cb25197ea2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -104,6 +104,8 @@ #include "my_json_writer.h" +#define PRIV_LOCK_TABLES (SELECT_ACL | LOCK_TABLES_ACL) + #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") #ifdef WITH_ARIA_STORAGE_ENGINE @@ -1101,7 +1103,6 @@ int bootstrap(MYSQL_FILE *file) thd->reset_kill_query(); /* Ensure that killed_errmsg is released */ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); - free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC)); thd->lex->restore_set_statement_var(); } delete thd; @@ -2939,6 +2940,36 @@ retry: goto err; } } + /* + Check privileges of view tables here, after views were opened. + Either definer or invoker has to have PRIV_LOCK_TABLES to be able + to lock view and its tables. For mysqldump (that locks views + before dumping their structures) compatibility we allow locking + views that select from I_S or P_S tables, but downrade the lock + to TL_READ + */ + if (table->belong_to_view && + check_single_table_access(thd, PRIV_LOCK_TABLES, table, 1)) + { + if (table->grant.m_internal.m_schema_access) + table->lock_type= TL_READ; + else + { + bool error= true; + if (Security_context *sctx= table->security_ctx) + { + table->security_ctx= 0; + error= check_single_table_access(thd, PRIV_LOCK_TABLES, table, 1); + table->security_ctx= sctx; + } + if (error) + { + my_error(ER_VIEW_INVALID, MYF(0), table->belong_to_view->view_db.str, + table->belong_to_view->view_name.str); + goto err; + } + } + } } if (lock_tables(thd, tables, counter, 0) || @@ -5415,7 +5446,7 @@ mysql_execute_command(THD *thd) if (first_table && lex->type & (REFRESH_READ_LOCK|REFRESH_FOR_EXPORT)) { /* Check table-level privileges. */ - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, + if (check_table_access(thd, PRIV_LOCK_TABLES, all_tables, FALSE, UINT_MAX, FALSE)) goto error; @@ -6755,7 +6786,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, @param thd Thread handler @param privilege requested privilege - @param all_tables global table list of query + @param tables global table list of query @param no_errors FALSE/TRUE - report/don't report error to the client (using my_error() call). @@ -6765,28 +6796,25 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, 1 access denied, error is sent to client */ -bool check_single_table_access(THD *thd, ulong privilege, - TABLE_LIST *all_tables, bool no_errors) +bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, + bool no_errors) { - Switch_to_definer_security_ctx backup_sctx(thd, all_tables); + Switch_to_definer_security_ctx backup_sctx(thd, tables); const char *db_name; - if ((all_tables->view || all_tables->field_translation) && - !all_tables->schema_table) - db_name= all_tables->view_db.str; + if ((tables->view || tables->field_translation) && !tables->schema_table) + db_name= tables->view_db.str; else - db_name= all_tables->db.str; + db_name= tables->db.str; - if (check_access(thd, privilege, db_name, - &all_tables->grant.privilege, - &all_tables->grant.m_internal, - 0, no_errors)) + if (check_access(thd, privilege, db_name, &tables->grant.privilege, + &tables->grant.m_internal, 0, no_errors)) return 1; /* Show only 1 table for check_grant */ - if (!(all_tables->belong_to_view && - (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && - check_grant(thd, privilege, all_tables, FALSE, 1, no_errors)) + if (!(tables->belong_to_view && + (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && + check_grant(thd, privilege, tables, FALSE, 1, no_errors)) return 1; return 0; @@ -9870,7 +9898,7 @@ static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables) if (is_temporary_table(table)) continue; - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, table, + if (check_table_access(thd, PRIV_LOCK_TABLES, table, FALSE, 1, FALSE)) return TRUE; } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index bf011bd2236..59a7586364a 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -127,6 +127,9 @@ When one supplies long data for a placeholder: #include "wsrep_trans_observer.h" #endif /* WITH_WSREP */ +/* Constants defining bits in parameter type flags. Flags are read from high byte of short value */ +static const uint PARAMETER_FLAG_UNSIGNED = 128U << 8; + /** A result class used to send cursor rows using the binary protocol. */ @@ -953,11 +956,73 @@ static bool insert_bulk_params(Prepared_statement *stmt, DBUG_RETURN(0); } -static bool set_conversion_functions(Prepared_statement *stmt, - uchar **data, uchar *data_end) + +/** + Checking if parameter type and flags are valid + + @param typecode ushort value with type in low byte, and flags in high byte + + @retval true this parameter is wrong + @retval false this parameter is OK +*/ + +static bool +parameter_type_sanity_check(ushort typecode) +{ + /* Checking if type in lower byte is valid */ + switch (typecode & 0xff) { + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_NEWDATE: + break; + /* + This types normally cannot be sent by client, so maybe it'd be + better to treat them like an error here. + */ + case MYSQL_TYPE_TIMESTAMP2: + case MYSQL_TYPE_TIME2: + case MYSQL_TYPE_DATETIME2: + default: + return true; + }; + + // In Flags in high byte only unsigned bit may be set + if (typecode & ((~PARAMETER_FLAG_UNSIGNED) & 0x0000ff00)) + { + return true; + } + return false; +} + +static bool +set_conversion_functions(Prepared_statement *stmt, uchar **data) { uchar *read_pos= *data; - const uint signed_bit= 1 << 15; + DBUG_ENTER("set_conversion_functions"); /* First execute or types altered by the client, setup the @@ -970,12 +1035,17 @@ static bool set_conversion_functions(Prepared_statement *stmt, { ushort typecode; - if (read_pos >= data_end) - DBUG_RETURN(1); - + /* + stmt_execute_packet_sanity_check has already verified, that there + are enough data in the packet for data types + */ typecode= sint2korr(read_pos); read_pos+= 2; - (**it).unsigned_flag= MY_TEST(typecode & signed_bit); + if (parameter_type_sanity_check(typecode)) + { + DBUG_RETURN(1); + } + (**it).unsigned_flag= MY_TEST(typecode & PARAMETER_FLAG_UNSIGNED); (*it)->setup_conversion(thd, (uchar) (typecode & 0xff)); (*it)->sync_clones(); } @@ -985,7 +1055,7 @@ static bool set_conversion_functions(Prepared_statement *stmt, static bool setup_conversion_functions(Prepared_statement *stmt, - uchar **data, uchar *data_end, + uchar **data, bool bulk_protocol= 0) { /* skip null bits */ @@ -998,7 +1068,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt, if (*read_pos++) //types supplied / first execute { *data= read_pos; - bool res= set_conversion_functions(stmt, data, data_end); + bool res= set_conversion_functions(stmt, data); DBUG_RETURN(res); } *data= read_pos; @@ -3149,11 +3219,19 @@ static void mysql_stmt_execute_common(THD *thd, void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) { + const uint packet_min_lenght= 9; uchar *packet= (uchar*)packet_arg; // GCC 4.0.1 workaround + + DBUG_ENTER("mysqld_stmt_execute"); + + if (packet_length < packet_min_lenght) + { + my_error(ER_MALFORMED_PACKET, MYF(0)); + DBUG_VOID_RETURN; + } ulong stmt_id= uint4korr(packet); ulong flags= (ulong) packet[4]; uchar *packet_end= packet + packet_length; - DBUG_ENTER("mysqld_stmt_execute"); packet+= 9; /* stmt_id + 5 bytes of flags */ @@ -3209,6 +3287,84 @@ void mysqld_stmt_bulk_execute(THD *thd, char *packet_arg, uint packet_length) DBUG_VOID_RETURN; } +/** + Additional packet checks for direct execution + + @param thd THD handle + @param stmt prepared statement being directly executed + @param paket packet with parameters to bind + @param packet_end pointer to the byte after parameters end + @param bulk_op is it bulk operation + @param direct_exec is it direct execution + @param read_bytes need to read types (only with bulk_op) + + @retval true this parameter is wrong + @retval false this parameter is OK +*/ + +static bool +stmt_execute_packet_sanity_check(Prepared_statement *stmt, + uchar *packet, uchar *packet_end, + bool bulk_op, bool direct_exec, + bool read_types) +{ + + DBUG_ASSERT((!read_types) || (read_types && bulk_op)); + if (stmt->param_count > 0) + { + uint packet_length= static_cast<uint>(packet_end - packet); + uint null_bitmap_bytes= (bulk_op ? 0 : (stmt->param_count + 7)/8); + uint min_len_for_param_count = null_bitmap_bytes + + (bulk_op ? 0 : 1); /* sent types byte */ + + if (!bulk_op && packet_length >= min_len_for_param_count) + { + if ((read_types= packet[null_bitmap_bytes])) + { + /* + Should be 0 or 1. If the byte is not 1, that could mean, + e.g. that we read incorrect byte due to incorrect number + of sent parameters for direct execution (i.e. null bitmap + is shorter or longer, than it should be) + */ + if (packet[null_bitmap_bytes] != '\1') + { + return true; + } + } + } + + if (read_types) + { + /* 2 bytes per parameter of the type and flags */ + min_len_for_param_count+= 2*stmt->param_count; + } + else + { + /* + If types are not sent, there is nothing to do here. + But for direct execution types should always be sent + */ + return direct_exec; + } + + /* + If true, the packet is guaranteed too short for the number of + parameters in the PS + */ + return (packet_length < min_len_for_param_count); + } + else + { + /* + If there is no parameters, this should be normally already end + of the packet. If it's not - then error + */ + return (packet_end > packet); + } + return false; +} + /** Common part of prepared statement execution @@ -3248,6 +3404,22 @@ static void mysql_stmt_execute_common(THD *thd, llstr(stmt_id, llbuf), "mysqld_stmt_execute"); DBUG_VOID_RETURN; } + + /* + In case of direct execution application decides how many parameters + to send. + + Thus extra checks are required to prevent crashes caused by incorrect + interpretation of the packet data. Plus there can be always a broken + evil client. + */ + if (stmt_execute_packet_sanity_check(stmt, packet, packet_end, bulk_op, + stmt_id == LAST_STMT_ID, read_types)) + { + my_error(ER_MALFORMED_PACKET, MYF(0)); + DBUG_VOID_RETURN; + } + stmt->read_types= read_types; #if defined(ENABLED_PROFILING) @@ -4160,7 +4332,7 @@ Prepared_statement::set_parameters(String *expanded_query, { #ifndef EMBEDDED_LIBRARY uchar *null_array= packet; - res= (setup_conversion_functions(this, &packet, packet_end) || + res= (setup_conversion_functions(this, &packet) || set_params(this, null_array, packet, packet_end, expanded_query)); #else /* @@ -4360,7 +4532,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query, #ifndef EMBEDDED_LIBRARY if (read_types && - set_conversion_functions(this, &packet, packet_end)) + set_conversion_functions(this, &packet)) #else // bulk parameters are not supported for embedded, so it will an error #endif diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8c27b023c93..7c729769cd2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -27798,10 +27798,10 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, if (save_to) { DBUG_ASSERT(!keyuse.elements); - memcpy(keyuse.buffer, - save_to->keyuse.buffer, - (size_t) save_to->keyuse.elements * keyuse.size_of_element); keyuse.elements= save_to->keyuse.elements; + if (size_t e= keyuse.elements) + memcpy(keyuse.buffer, + save_to->keyuse.buffer, e * keyuse.size_of_element); } /* Add the new access methods to the keyuse array. */ diff --git a/sql/sql_string.cc b/sql/sql_string.cc index c97b8a9a830..e3f4497274c 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2016, MariaDB + Copyright (c) 2016, 2020, MariaDB 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 @@ -237,8 +237,8 @@ bool Binary_string::copy(const Binary_string &str) { if (alloc(str.str_length)) return TRUE; - str_length=str.str_length; - bmove(Ptr,str.Ptr,str_length); // May be overlapping + if ((str_length=str.str_length)) + bmove(Ptr,str.Ptr,str_length); // May be overlapping Ptr[str_length]=0; return FALSE; } @@ -574,8 +574,11 @@ bool Binary_string::append_ulonglong(ulonglong val) bool String::append(const char *s, size_t arg_length, CHARSET_INFO *cs) { + if (!arg_length) + return false; + uint32 offset; - + if (needs_conversion((uint32)arg_length, cs, charset(), &offset)) { size_t add_length; diff --git a/sql/sql_type.h b/sql/sql_type.h index c1b13dfa811..939c651233c 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -230,7 +230,7 @@ class Dec_ptr_and_buffer: public Dec_ptr protected: my_decimal m_buffer; public: - int round_to(my_decimal *to, uint scale, decimal_round_mode mode) + int round_to(my_decimal *to, int scale, decimal_round_mode mode) { DBUG_ASSERT(m_ptr); return m_ptr->round_to(to, scale, mode); @@ -239,6 +239,14 @@ public: { return round_to(&m_buffer, scale, mode); } + int round_self_if_needed(int scale, decimal_round_mode mode) + { + if (scale >= m_ptr->frac) + return E_DEC_OK; + int res= m_ptr->round_to(&m_buffer, scale, mode); + m_ptr= &m_buffer; + return res; + } String *to_string_round(String *to, uint dec) { /* @@ -3647,6 +3655,8 @@ public: const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const= 0; + virtual bool is_packable() const { return false; } + virtual uint32 max_display_length(const Item *item) const= 0; virtual uint32 Item_decimal_notation_int_digits(const Item *item) const { return 0; } virtual uint32 calc_pack_length(uint32 length) const= 0; @@ -4788,7 +4798,11 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + + bool is_packable()const { return true; } + bool union_element_finalize(const Item * item) const; + bool Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *c, diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index 9ab424b3f8d..50e2ec46222 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -726,7 +726,11 @@ public: { DBUG_ASSERT(FALSE); } void global_save_default(THD *thd, set_var *var) - { DBUG_ASSERT(FALSE); } + { + char *ptr= (char*)(intptr)option.def_value; + var->save_result.string_value.str= ptr; + var->save_result.string_value.length= ptr ? strlen(ptr) : 0; + } bool session_update(THD *thd, set_var *var) { diff --git a/sql/table.h b/sql/table.h index 264ece6ac6d..6073e35fa85 100644 --- a/sql/table.h +++ b/sql/table.h @@ -3237,7 +3237,8 @@ inline void mark_as_null_row(TABLE *table) { table->null_row=1; table->status|=STATUS_NULL_ROW; - bfill(table->null_flags,table->s->null_bytes,255); + if (table->s->null_bytes) + bfill(table->null_flags,table->s->null_bytes,255); } bool is_simple_order(ORDER *order); diff --git a/sql/unireg.cc b/sql/unireg.cc index beb754ed852..41481d253ee 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -981,8 +981,11 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, it.rewind(); while ((field=it++)) { - memcpy(buff, field->comment.str, field->comment.length); - buff+= field->comment.length; + if (size_t l= field->comment.length) + { + memcpy(buff, field->comment.str, l); + buff+= l; + } } } *buff_arg= buff; diff --git a/sql/unireg.h b/sql/unireg.h index 8e9fa27ea6a..cac290c7806 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -76,7 +76,8 @@ #define cmp_record(A,B) memcmp((A)->record[0],(A)->B,(size_t) (A)->s->reclength) #define empty_record(A) { \ restore_record((A),s->default_values); \ - bfill((A)->null_flags,(A)->s->null_bytes,255);\ + if ((A)->s->null_bytes) \ + bfill((A)->null_flags,(A)->s->null_bytes,255); \ } /* Defines for use with openfrm, openprt and openfrd */ |