summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty <monty@mariadb.org>2020-09-16 11:23:50 +0300
committerSergei Golubchik <serg@mariadb.org>2021-05-19 22:27:27 +0200
commit36cdd5c3cdb06d8538f64c0b312ffe4672a92e75 (patch)
treef1c675fab2e79fc8cd7466b080ddbc5ce3a1b920
parentda85ad798708d045e7ba1963172daf81aeb80ab9 (diff)
downloadmariadb-git-36cdd5c3cdb06d8538f64c0b312ffe4672a92e75.tar.gz
Optimize usage of c_ptr(), c_ptr_quick() and String::alloc()
The problem was that when one used String::alloc() to allocate a string, the String ensures that there is space for an extra NULL byte in the buffer and if not, reallocates the string. This is a problem with the String::set_int() that calls alloc(21), which forces extra malloc/free calls to happen. - We do not anymore re-allocate String if alloc() is called with the Allocated_length. This reduces number of malloc() allocations, especially one big re-allocation in Protocol::send_result_Set_metadata() for almost every query that produced a result to the connnected client. - Avoid extra mallocs when using LONGLONG_BUFFER_SIZE This can now be done as alloc() doesn't increase buffers if new length is not bigger than old one. - c_ptr() is redesigned to be safer (but a bit longer) than before. - Remove wrong usage of c_ptr_quick() c_ptr_quick() was used in many cases to get the pointer to the used buffer, even when it didn't need to be \0 terminated. In this case ptr() is a better substitute. Another problem with c_ptr_quick() is that it did not guarantee that the string would be \0 terminated. - item_val_str(), an API function not used currently by the server, now always returns a null terminated string (before it didn't always do that). - Ensure that all String allocations uses STRING_PSI_MEMORY_KEY. The old mixed usage of performance keys caused assert's when String buffers where shrunk. - Binary_string::shrink() is simplifed - Fixed bug in String(const char *str, size_t len, CHARSET_INFO *cs) that used Binary_string((char *) str, len) instead of Binary_string(str,len). - Changed argument to String() creations and String.set() functions to use 'const char*' instead of 'char*'. This ensures that Alloced_length is not set, which gives safety against someone trying to change the original string. This also would allow us to use !Alloced_length in c_ptr() if needed. - Changed string_ptr_cmp() to use memcmp() instead of c_ptr() to avoid a possible malloc during string comparision.
-rw-r--r--sql/field_conv.cc6
-rw-r--r--sql/item.cc4
-rw-r--r--sql/partition_info.cc11
-rw-r--r--sql/sql_analyze_stmt.cc2
-rw-r--r--sql/sql_help.cc7
-rw-r--r--sql/sql_plugin.cc8
-rw-r--r--sql/sql_repl.cc6
-rw-r--r--sql/sql_select.cc2
-rw-r--r--sql/sql_show.cc2
-rw-r--r--sql/sql_string.cc54
-rw-r--r--sql/sql_string.h63
-rw-r--r--sql/sql_type.cc2
-rw-r--r--sql/sql_yacc.yy23
-rw-r--r--sql/table.cc2
-rw-r--r--sql/wsrep_schema.cc4
15 files changed, 124 insertions, 72 deletions
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index ff6d60e7626..47d63fe4645 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -369,7 +369,7 @@ void Field::do_field_string(Copy_field *copy)
res.length(0U);
copy->from_field->val_str(&res);
- copy->to_field->store(res.c_ptr_quick(), res.length(), res.charset());
+ copy->to_field->store(res.ptr(), res.length(), res.charset());
}
@@ -389,10 +389,10 @@ static void do_field_varbinary_pre50(Copy_field *copy)
copy->from_field->val_str(&copy->tmp);
/* Use the same function as in 4.1 to trim trailing spaces */
- size_t length= my_lengthsp_8bit(&my_charset_bin, copy->tmp.c_ptr_quick(),
+ size_t length= my_lengthsp_8bit(&my_charset_bin, copy->tmp.ptr(),
copy->from_field->field_length);
- copy->to_field->store(copy->tmp.c_ptr_quick(), length,
+ copy->to_field->store(copy->tmp.ptr(), length,
copy->tmp.charset());
}
diff --git a/sql/item.cc b/sql/item.cc
index 438c46894d5..5e0f2f72440 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -4599,7 +4599,7 @@ const String *Item_param::value_query_val_str(THD *thd, String *str) const
break;
}
DBUG_ASSERT(str->length() <= typelen);
- buf= str->c_ptr_quick();
+ buf= (char*) str->ptr();
ptr= buf + str->length();
*ptr++= '\'';
ptr+= (uint) my_TIME_to_str(&value.time, ptr, decimals);
@@ -4705,7 +4705,7 @@ Item *Item_param::value_clone_item(THD *thd)
return 0; // Should create Item_decimal. See MDEV-11361.
case STRING_RESULT:
return new (mem_root) Item_string(thd, name,
- Lex_cstring(value.m_string.c_ptr_quick(),
+ Lex_cstring(value.m_string.ptr(),
value.m_string.length()),
value.m_string.charset(),
collation.derivation,
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index 871411cf6c4..fd92e437cac 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -126,7 +126,7 @@ partition_info *partition_info::get_clone(THD *thd)
/**
Mark named [sub]partition to be used/locked.
- @param part_name Partition name to match.
+ @param part_name Partition name to match. Must be \0 terminated!
@param length Partition name length.
@return Success if partition found
@@ -140,7 +140,10 @@ bool partition_info::add_named_partition(const char *part_name, size_t length)
PART_NAME_DEF *part_def;
Partition_share *part_share;
DBUG_ENTER("partition_info::add_named_partition");
- DBUG_ASSERT(table && table->s && table->s->ha_share);
+ DBUG_ASSERT(part_name[length] == 0);
+ DBUG_ASSERT(table);
+ DBUG_ASSERT(table->s);
+ DBUG_ASSERT(table->s->ha_share);
part_share= static_cast<Partition_share*>((table->s->ha_share));
DBUG_ASSERT(part_share->partition_name_hash_initialized);
part_name_hash= &part_share->partition_name_hash;
@@ -172,9 +175,9 @@ bool partition_info::add_named_partition(const char *part_name, size_t length)
else
bitmap_set_bit(&read_partitions, part_def->part_id);
}
- DBUG_PRINT("info", ("Found partition %u is_subpart %d for name %s",
+ DBUG_PRINT("info", ("Found partition %u is_subpart %d for name %.*s",
part_def->part_id, part_def->is_subpart,
- part_name));
+ length, part_name));
DBUG_RETURN(false);
}
diff --git a/sql/sql_analyze_stmt.cc b/sql/sql_analyze_stmt.cc
index 2f87b9b0d40..8df2f76aeaf 100644
--- a/sql/sql_analyze_stmt.cc
+++ b/sql/sql_analyze_stmt.cc
@@ -81,7 +81,7 @@ void Filesort_tracker::print_json_members(Json_writer *writer)
}
get_data_format(&str);
- writer->add_member("r_sort_mode").add_str(str.c_ptr(), str.length());
+ writer->add_member("r_sort_mode").add_str(str.ptr(), str.length());
}
void Filesort_tracker::get_data_format(String *str)
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 01c47bc2b21..8ce0f724e55 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -541,7 +541,12 @@ extern "C" int string_ptr_cmp(const void* ptr1, const void* ptr2)
{
String *str1= *(String**)ptr1;
String *str2= *(String**)ptr2;
- return strcmp(str1->c_ptr(),str2->c_ptr());
+ uint length1= str1->length();
+ uint length2= str2->length();
+ int tmp= memcmp(str1->ptr(),str2->ptr(), MY_MIN(length1, length2));
+ if (tmp)
+ return tmp;
+ return (int) length2 - (int) length1;
}
/*
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 02ae08c910c..195267b9abc 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -404,12 +404,16 @@ static int item_value_type(struct st_mysql_value *value)
static const char *item_val_str(struct st_mysql_value *value,
char *buffer, int *length)
{
- String str(buffer, *length, system_charset_info), *res;
+ size_t org_length= *length;
+ String str(buffer, org_length, system_charset_info), *res;
if (!(res= ((st_item_value_holder*)value)->item->val_str(&str)))
return NULL;
*length= res->length();
- if (res->c_ptr_quick() == buffer)
+ if (res->ptr() == buffer && res->length() < org_length)
+ {
+ buffer[res->length()]= 0;
return buffer;
+ }
/*
Lets be nice and create a temporary string since the
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index ff2faca5ecf..49fd29a6a31 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -2153,7 +2153,7 @@ static int init_binlog_sender(binlog_send_info *info,
"Start binlog_dump to slave_server(%lu), pos(%s, %lu), "
"using_gtid(%d), gtid('%s')", thd->variables.server_id,
log_ident, (ulong)*pos, info->using_gtid_state,
- connect_gtid_state.c_ptr_quick());
+ connect_gtid_state.c_ptr_safe());
}
#ifndef DBUG_OFF
@@ -2176,7 +2176,7 @@ static int init_binlog_sender(binlog_send_info *info,
const char *name=search_file_name;
if (info->using_gtid_state)
{
- if (info->gtid_state.load(connect_gtid_state.c_ptr_quick(),
+ if (info->gtid_state.load(connect_gtid_state.ptr(),
connect_gtid_state.length()))
{
info->errmsg= "Out of memory or malformed slave request when obtaining "
@@ -2185,7 +2185,7 @@ static int init_binlog_sender(binlog_send_info *info,
return 1;
}
if (info->until_gtid_state &&
- info->until_gtid_state->load(slave_until_gtid_str.c_ptr_quick(),
+ info->until_gtid_state->load(slave_until_gtid_str.ptr(),
slave_until_gtid_str.length()))
{
info->errmsg= "Out of memory or malformed slave request when "
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 2f317f58da6..14351268af6 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -27860,7 +27860,7 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
for (i= 1; i <= num_parts; i++)
{
String *name= name_it++;
- append_identifier(thd, str, name->c_ptr(), name->length());
+ append_identifier(thd, str, name->ptr(), name->length());
if (i != num_parts)
str->append(',');
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 62942e29344..617caf12aa3 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -10121,7 +10121,7 @@ char *thd_get_error_context_description(THD *thd, char *buffer,
*/
DBUG_ASSERT(buffer != NULL);
length= MY_MIN(str.length(), length-1);
- memcpy(buffer, str.c_ptr_quick(), length);
+ memcpy(buffer, str.ptr(), length);
/* Make sure that the new string is null terminated */
buffer[length]= '\0';
return buffer;
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index f2a0f55aec8..aca9ede9000 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -41,7 +41,7 @@ bool Binary_string::real_alloc(size_t length)
if (Alloced_length < arg_length)
{
free();
- if (!(Ptr=(char*) my_malloc(PSI_INSTRUMENT_ME,
+ if (!(Ptr=(char*) my_malloc(STRING_PSI_MEMORY_KEY,
arg_length,MYF(MY_WME | (thread_specific ?
MY_THREAD_SPECIFIC : 0)))))
return TRUE;
@@ -55,7 +55,8 @@ bool Binary_string::real_alloc(size_t length)
/**
- Allocates a new buffer on the heap for this String.
+ Allocates a new buffer on the heap for this String if current buffer is
+ smaller.
- If the String's internal buffer is privately owned and heap allocated,
one of the following is performed.
@@ -70,7 +71,8 @@ bool Binary_string::real_alloc(size_t length)
will be allocated and the string copied accoring to its length, as found
in String::length().
- For C compatibility, the new string buffer is null terminated.
+ For C compatibility, the new string buffer is null terminated if it was
+ allocated.
@param alloc_length The requested string size in characters, excluding any
null terminator.
@@ -81,9 +83,10 @@ bool Binary_string::real_alloc(size_t length)
@retval true An error occurred when attempting to allocate memory.
*/
+
bool Binary_string::realloc_raw(size_t alloc_length)
{
- if (Alloced_length <= alloc_length)
+ if (Alloced_length < alloc_length)
{
char *new_ptr;
uint32 len= ALIGN_SIZE(alloc_length+1);
@@ -92,13 +95,13 @@ bool Binary_string::realloc_raw(size_t alloc_length)
return TRUE; /* Overflow */
if (alloced)
{
- if (!(new_ptr= (char*) my_realloc(PSI_INSTRUMENT_ME, Ptr,len,
+ if (!(new_ptr= (char*) my_realloc(STRING_PSI_MEMORY_KEY, Ptr,len,
MYF(MY_WME |
(thread_specific ?
MY_THREAD_SPECIFIC : 0)))))
return TRUE; // Signal error
}
- else if ((new_ptr= (char*) my_malloc(PSI_INSTRUMENT_ME, len,
+ else if ((new_ptr= (char*) my_malloc(STRING_PSI_MEMORY_KEY, len,
MYF(MY_WME |
(thread_specific ?
MY_THREAD_SPECIFIC : 0)))))
@@ -118,9 +121,14 @@ bool Binary_string::realloc_raw(size_t alloc_length)
return FALSE;
}
+
bool String::set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs)
{
- uint l=20*cs->mbmaxlen+1;
+ /*
+ This allocates a few bytes extra in the unlikely case that cs->mb_maxlen
+ > 1, but we can live with that
+ */
+ uint l= LONGLONG_BUFFER_SIZE * cs->mbmaxlen;
int base= unsigned_flag ? 10 : -10;
if (alloc(l))
@@ -235,7 +243,7 @@ bool Binary_string::copy()
*/
bool Binary_string::copy(const Binary_string &str)
{
- if (alloc(str.str_length))
+ if (alloc(str.str_length+1))
return TRUE;
if ((str_length=str.str_length))
bmove(Ptr,str.Ptr,str_length); // May be overlapping
@@ -246,7 +254,7 @@ bool Binary_string::copy(const Binary_string &str)
bool Binary_string::copy(const char *str, size_t arg_length)
{
DBUG_ASSERT(arg_length < UINT_MAX32);
- if (alloc(arg_length))
+ if (alloc(arg_length+1))
return TRUE;
if (Ptr == str && arg_length == uint32(str_length))
{
@@ -272,7 +280,7 @@ bool Binary_string::copy(const char *str, size_t arg_length)
bool Binary_string::copy_or_move(const char *str, size_t arg_length)
{
DBUG_ASSERT(arg_length < UINT_MAX32);
- if (alloc(arg_length))
+ if (alloc(arg_length+1))
return TRUE;
if ((str_length=uint32(arg_length)))
memmove(Ptr,str,arg_length);
@@ -1251,24 +1259,16 @@ bool String::append_semi_hex(const char *s, uint len, CHARSET_INFO *cs)
return false;
}
+
// Shrink the buffer, but only if it is allocated on the heap.
void Binary_string::shrink(size_t arg_length)
{
- if (!is_alloced())
- return;
- if (ALIGN_SIZE(arg_length + 1) < Alloced_length)
- {
- char* new_ptr;
- if (!(new_ptr = (char*)my_realloc(STRING_PSI_MEMORY_KEY, Ptr, arg_length,
- MYF(thread_specific ? MY_THREAD_SPECIFIC : 0))))
- {
- Alloced_length = 0;
- real_alloc(arg_length);
- }
- else
- {
- Ptr = new_ptr;
- Alloced_length = (uint32)arg_length;
- }
- }
+ if (is_alloced() && ALIGN_SIZE(arg_length + 1) < Alloced_length)
+ {
+ /* my_realloc() can't fail as new buffer is less than the original one */
+ Ptr= (char*) my_realloc(STRING_PSI_MEMORY_KEY, Ptr, arg_length,
+ MYF(thread_specific ?
+ MY_THREAD_SPECIFIC : 0));
+ Alloced_length= (uint32) arg_length;
+ }
}
diff --git a/sql/sql_string.h b/sql/sql_string.h
index b3eca118b63..c731f6189c8 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -600,25 +600,55 @@ public:
inline char *c_ptr()
{
- DBUG_ASSERT(!alloced || !Ptr || !Alloced_length ||
- (Alloced_length >= (str_length + 1)));
-
- if (!Ptr || Ptr[str_length]) // Should be safe
- (void) realloc(str_length);
+ if (unlikely(!Ptr))
+ return (char*) "";
+ /*
+ Here we assume that any buffer used to initalize String has
+ an end \0 or have at least an accessable character at end.
+ This is to handle the case of String("Hello",5) and
+ String("hello",5) efficiently.
+
+ We have two options here. To test for !Alloced_length or !alloced.
+ Using "Alloced_length" is slightly safer so that we do not read
+ from potentially unintialized memory (normally not dangerous but
+ may give warnings in valgrind), but "alloced" is safer as there
+ are less change to get memory loss from code that is using
+ String((char*), length) or String.set((char*), length) and does
+ not free things properly (and there is several places in the code
+ where this happens and it is hard to find out if any of these will call
+ c_ptr().
+ */
+ if (unlikely(!alloced && !Ptr[str_length]))
+ return Ptr;
+ if (str_length < Alloced_length)
+ {
+ Ptr[str_length]=0;
+ return Ptr;
+ }
+ (void) realloc(str_length+1); /* This will add end \0 */
return Ptr;
}
+ /*
+ One should use c_ptr() instead for most cases. This will be deleted soon,
+ kept for compatiblity.
+ */
inline char *c_ptr_quick()
{
- if (Ptr && str_length < Alloced_length)
- Ptr[str_length]=0;
- return Ptr;
+ return c_ptr_safe();
}
+ /*
+ This is to be used only in the case when one cannot use c_ptr().
+ The cases are:
+ - When one initializes String with an external buffer and length and
+ buffer[length] could be uninitalized when c_ptr() is called.
+ - When valgrind gives warnings about uninitialized memory with c_ptr().
+ */
inline char *c_ptr_safe()
{
if (Ptr && str_length < Alloced_length)
Ptr[str_length]=0;
else
- (void) realloc(str_length);
+ (void) realloc(str_length + 1);
return Ptr;
}
@@ -634,7 +664,16 @@ public:
}
inline bool alloc(size_t arg_length)
{
- if (arg_length < Alloced_length)
+ /*
+ Allocate if we need more space or if we don't have p_done any
+ allocation yet (we don't want to have Ptr to be NULL for empty strings).
+
+ Note that if arg_length == Alloced_length then we don't allocate.
+ This ensures we don't do any extra allocations in protocol and String:int,
+ but the string will not be atomically null terminated if c_ptr() is not
+ called.
+ */
+ if (arg_length <= Alloced_length && Alloced_length)
return 0;
return real_alloc(arg_length);
}
@@ -642,7 +681,7 @@ public:
bool realloc_raw(size_t arg_length);
bool realloc(size_t arg_length)
{
- if (realloc_raw(arg_length))
+ if (realloc_raw(arg_length+1))
return TRUE;
Ptr[arg_length]= 0; // This make other funcs shorter
return FALSE;
@@ -743,7 +782,7 @@ public:
*/
String(const char *str, size_t len, CHARSET_INFO *cs)
:Charset(cs),
- Binary_string((char *) str, len)
+ Binary_string(str, len)
{ }
String(char *str, size_t len, CHARSET_INFO *cs)
:Charset(cs),
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 6a1ccbbbd22..8ee4572a422 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -9258,7 +9258,7 @@ Type_handler_general_purpose_int::partition_field_append_value(
const
{
DBUG_ASSERT(item_expr->cmp_type() == INT_RESULT);
- StringBuffer<21> tmp;
+ StringBuffer<LONGLONG_BUFFER_SIZE> tmp;
longlong value= item_expr->val_int();
tmp.set(value, system_charset_info);
return str->append(tmp);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index c407a395d04..d3d99ef9852 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -12077,18 +12077,18 @@ using_list:
{
if (unlikely(!($$= new (thd->mem_root) List<String>)))
MYSQL_YYABORT;
- String *s= new (thd->mem_root) String((const char *) $1.str,
- $1.length,
- system_charset_info);
+ String *s= new (thd->mem_root) String((const char*) $1.str,
+ $1.length,
+ system_charset_info);
if (unlikely(unlikely(s == NULL)))
MYSQL_YYABORT;
$$->push_back(s, thd->mem_root);
}
| using_list ',' ident
{
- String *s= new (thd->mem_root) String((const char *) $3.str,
- $3.length,
- system_charset_info);
+ String *s= new (thd->mem_root) String((const char*) $3.str,
+ $3.length,
+ system_charset_info);
if (unlikely(unlikely(s == NULL)))
MYSQL_YYABORT;
if (unlikely($1->push_back(s, thd->mem_root)))
@@ -14256,8 +14256,9 @@ wild_and_where:
/* empty */ { $$= 0; }
| LIKE remember_tok_start TEXT_STRING_sys
{
- Lex->wild= new (thd->mem_root) String($3.str, $3.length,
- system_charset_info);
+ Lex->wild= new (thd->mem_root) String((const char*) $3.str,
+ $3.length,
+ system_charset_info);
if (unlikely(Lex->wild == NULL))
MYSQL_YYABORT;
$$= $2;
@@ -14931,9 +14932,9 @@ text_literal:
text_string:
TEXT_STRING_literal
{
- $$= new (thd->mem_root) String($1.str,
- $1.length,
- thd->variables.collation_connection);
+ $$= new (thd->mem_root) String((const char*) $1.str,
+ $1.length,
+ thd->variables.collation_connection);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
diff --git a/sql/table.cc b/sql/table.cc
index fef3982e3bd..f62b7496ce7 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -4841,7 +4841,7 @@ rename_file_ext(const char * from,const char * to,const char * ext)
bool get_field(MEM_ROOT *mem, Field *field, String *res)
{
- char *to;
+ const char *to;
StringBuffer<MAX_FIELD_WIDTH> str;
bool rc;
THD *thd= field->get_thd();
diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc
index 122233b239a..1bfce7874ae 100644
--- a/sql/wsrep_schema.cc
+++ b/sql/wsrep_schema.cc
@@ -1250,7 +1250,7 @@ int Wsrep_schema::replay_transaction(THD* orig_thd,
{
Wsrep_schema_impl::thd_context_switch thd_context_switch(&thd, orig_thd);
- ret= wsrep_apply_events(orig_thd, rli, buf.c_ptr_quick(), buf.length());
+ ret= wsrep_apply_events(orig_thd, rli, buf.ptr(), buf.length());
if (ret)
{
WSREP_WARN("Wsrep_schema::replay_transaction: failed to apply fragments");
@@ -1404,7 +1404,7 @@ int Wsrep_schema::recover_sr_transactions(THD *orig_thd)
String data_str;
(void)frag_table->field[4]->val_str(&data_str);
- wsrep::const_buffer data(data_str.c_ptr_quick(), data_str.length());
+ wsrep::const_buffer data(data_str.ptr(), data_str.length());
wsrep::ws_meta ws_meta(gtid,
wsrep::stid(server_id,
transaction_id,