mariadb/plugin/feedback/sender_thread.cc
Monty b6ff139aa3 Reduce usage of strlen()
Changes:
- To detect automatic strlen() I removed the methods in String that
  uses 'const char *' without a length:
  - String::append(const char*)
  - Binary_string(const char *str)
  - String(const char *str, CHARSET_INFO *cs)
  - append_for_single_quote(const char *)
  All usage of append(const char*) is changed to either use
  String::append(char), String::append(const char*, size_t length) or
  String::append(LEX_CSTRING)
- Added STRING_WITH_LEN() around constant string arguments to
  String::append()
- Added overflow argument to escape_string_for_mysql() and
  escape_quotes_for_mysql() instead of returning (size_t) -1 on overflow.
  This was needed as most usage of the above functions never tested the
  result for -1 and would have given wrong results or crashes in case
  of overflows.
- Added Item_func_or_sum::func_name_cstring(), which returns LEX_CSTRING.
  Changed all Item_func::func_name()'s to func_name_cstring()'s.
  The old Item_func_or_sum::func_name() is now an inline function that
  returns func_name_cstring().str.
- Changed Item::mode_name() and Item::func_name_ext() to return
  LEX_CSTRING.
- Changed for some functions the name argument from const char * to
  to const LEX_CSTRING &:
  - Item::Item_func_fix_attributes()
  - Item::check_type_...()
  - Type_std_attributes::agg_item_collations()
  - Type_std_attributes::agg_item_set_converter()
  - Type_std_attributes::agg_arg_charsets...()
  - Type_handler_hybrid_field_type::aggregate_for_result()
  - Type_handler_geometry::check_type_geom_or_binary()
  - Type_handler::Item_func_or_sum_illegal_param()
  - Predicant_to_list_comparator::add_value_skip_null()
  - Predicant_to_list_comparator::add_value()
  - cmp_item_row::prepare_comparators()
  - cmp_item_row::aggregate_row_elements_for_comparison()
  - Cursor_ref::print_func()
- Removes String_space() as it was only used in one cases and that
  could be simplified to not use String_space(), thanks to the fixed
  my_vsnprintf().
- Added some const LEX_CSTRING's for common strings:
  - NULL_clex_str, DATA_clex_str, INDEX_clex_str.
- Changed primary_key_name to a LEX_CSTRING
- Renamed String::set_quick() to String::set_buffer_if_not_allocated() to
  clarify what the function really does.
- Rename of protocol function:
  bool store(const char *from, CHARSET_INFO *cs) to
  bool store_string_or_null(const char *from, CHARSET_INFO *cs).
  This was done to both clarify the difference between this 'store' function
  and also to make it easier to find unoptimal usage of store() calls.
- Added Protocol::store(const LEX_CSTRING*, CHARSET_INFO*)
- Changed some 'const char*' arrays to instead be of type LEX_CSTRING.
- class Item_func_units now used LEX_CSTRING for name.

Other things:
- Fixed a bug in mysql.cc:construct_prompt() where a wrong escape character
  in the prompt would cause some part of the prompt to be duplicated.
- Fixed a lot of instances where the length of the argument to
  append is known or easily obtain but was not used.
- Removed some not needed 'virtual' definition for functions that was
  inherited from the parent. I added override to these.
- Fixed Ordered_key::print() to preallocate needed buffer. Old code could
  case memory overruns.
- Simplified some loops when adding char * to a String with delimiters.
2021-05-19 22:27:48 +02:00

298 lines
7.8 KiB
C++

/* Copyright (C) 2010 Sergei Golubchik and Monty Program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#include "feedback.h"
#include <sql_acl.h>
#include <sql_parse.h>
#include <sql_show.h>
#include <time.h>
namespace feedback {
static THD *thd= 0; ///< background thread thd
static my_thread_id thd_thread_id; ///< its thread_id
static size_t needed_size= 20480;
ulong startup_interval= 60*5; ///< in seconds (5 minutes)
ulong first_interval= 60*60*24; ///< in seconds (one day)
ulong interval= 60*60*24*7; ///< in seconds (one week)
/**
reads the rows from a table and puts them, concatenated, in a String
@note
1. only supports two column tables - no less, no more.
2. it emulates mysql -e "select * from..." and thus it separates
columns with \t and starts the output with column names.
*/
static int table_to_string(TABLE *table, String *result)
{
int res;
char buff1[MAX_FIELD_WIDTH], buff2[MAX_FIELD_WIDTH];
String str1(buff1, sizeof(buff1), system_charset_info);
String str2(buff2, sizeof(buff2), system_charset_info);
res= table->file->ha_rnd_init(1);
dbug_tmp_use_all_columns(table, &table->read_set);
while(!res && !table->file->ha_rnd_next(table->record[0]))
{
table->field[0]->val_str(&str1);
table->field[1]->val_str(&str2);
if (result->reserve(str1.length() + str2.length() + 3))
res= 1;
else
{
result->qs_append(str1.ptr(), str1.length());
result->qs_append('\t');
result->qs_append(str2.ptr(), str2.length());
result->qs_append('\n');
}
}
res = res || (int)result->append('\n');
/*
Note, "|=" and not "||" - because we want to call ha_rnd_end()
even if res is already 1.
*/
res |= table->file->ha_rnd_end();
return res;
}
/**
Initialize the THD and TABLE_LIST
The structures must be sufficiently initialized for create_tmp_table()
and fill_feedback() to work.
*/
static int prepare_for_fill(TABLE_LIST *tables)
{
/*
Add our thd to the list, for it to be visible in SHOW PROCESSLIST.
But don't generate thread_id every time - use the saved value
(every increment of global thread_id counts as a new connection
in SHOW STATUS and we want to avoid skewing the statistics)
*/
thd->variables.pseudo_thread_id= thd->thread_id;
server_threads.insert(thd);
thd->thread_stack= (char*) &tables;
thd->store_globals();
thd->mysys_var->current_cond= &sleep_condition;
thd->mysys_var->current_mutex= &sleep_mutex;
thd->proc_info="feedback";
thd->set_command(COM_SLEEP);
thd->system_thread= SYSTEM_THREAD_EVENT_WORKER; // whatever
thd->set_time();
thd->init_for_queries();
thd->real_id= pthread_self();
thd->db= null_clex_str;
thd->security_ctx->host_or_ip= "";
thd->security_ctx->db_access= DB_ACLS;
thd->security_ctx->master_access= ALL_KNOWN_ACL;
bzero((char*) &thd->net, sizeof(thd->net));
lex_start(thd);
mysql_init_select(thd->lex);
LEX_CSTRING tbl_name= {i_s_feedback->table_name, strlen(i_s_feedback->table_name) };
tables->init_one_table(&INFORMATION_SCHEMA_NAME, &tbl_name, 0, TL_READ);
tables->schema_table= i_s_feedback;
tables->schema_table_reformed= 1;
tables->select_lex= thd->lex->first_select_lex();
DBUG_ASSERT(tables->select_lex);
tables->table= create_schema_table(thd, tables);
if (!tables->table)
return 1;
tables->table->pos_in_table_list= tables;
return 0;
}
/**
Try to detect if this thread is going down
which can happen for different reasons:
* plugin is being unloaded
* mysqld server is being shut down
* the thread is being killed
*/
static bool going_down()
{
return shutdown_plugin || abort_loop || (thd && thd->killed);
}
/**
just like sleep, but waits on a condition and checks "plugin shutdown" status
*/
static int slept_ok(time_t sec)
{
struct timespec abstime;
int ret= 0;
set_timespec(abstime, sec);
mysql_mutex_lock(&sleep_mutex);
while (!going_down() && ret != ETIMEDOUT)
ret= mysql_cond_timedwait(&sleep_condition, &sleep_mutex, &abstime);
mysql_mutex_unlock(&sleep_mutex);
return !going_down();
}
/**
create a feedback report and send it to all specified urls
If "when" argument is not null, only it and the server uid are sent.
Otherwise a full report is generated.
*/
static void send_report(const char *when)
{
TABLE_LIST tables;
String str;
int i, last_todo;
Url **todo= (Url**)alloca(url_count*sizeof(Url*));
str.alloc(needed_size); // preallocate it to avoid many small mallocs
/*
on startup and shutdown the server may not be completely
initialized, and full report won't work.
We send a short status notice only.
*/
if (when)
{
str.length(0);
str.append(STRING_WITH_LEN("FEEDBACK_SERVER_UID"));
str.append('\t');
str.append(server_uid_buf, sizeof(server_uid_buf)-1);
str.append('\n');
str.append(STRING_WITH_LEN("FEEDBACK_WHEN"));
str.append('\t');
str.append(when, strlen(when));
str.append('\n');
str.append(STRING_WITH_LEN("FEEDBACK_USER_INFO"));
str.append('\t');
str.append(user_info, strlen(user_info));
str.append('\n');
str.append('\n');
}
else
{
/*
otherwise, prepare the THD and TABLE_LIST,
create and fill the temporary table with data just like
SELECT * FROM INFORMATION_SCHEMA.FEEDBACK is doing,
read and concatenate table data into a String.
*/
if (!(thd= new THD(thd_thread_id)))
return;
if (prepare_for_fill(&tables))
goto ret;
if (fill_feedback(thd, &tables, NULL))
goto ret;
if (table_to_string(tables.table, &str))
goto ret;
needed_size= (size_t)(str.length() * 1.1);
free_tmp_table(thd, tables.table);
tables.table= 0;
}
/*
Try to send the report on every url from the list, remove url on success,
keep failed in the list. Repeat until the list is empty.
*/
memcpy(todo, urls, url_count*sizeof(Url*));
last_todo= url_count - 1;
do
{
for (i= 0; i <= last_todo;)
{
Url *url= todo[i];
if (thd) // for nicer SHOW PROCESSLIST
thd->set_query(const_cast<char*>(url->url()), (uint) url->url_length());
if (url->send(str.ptr(), str.length()))
i++;
else
todo[i]= todo[last_todo--];
}
if (last_todo < 0)
break;
} while (slept_ok(send_retry_wait)); // wait a little bit before retrying
ret:
if (thd)
{
if (tables.table)
free_tmp_table(thd, tables.table);
thd->cleanup_after_query();
/*
clean up, free the thd.
reset all thread local status variables to minimize
the effect of the background thread on SHOW STATUS.
*/
server_threads.erase(thd);
thd->set_status_var_init();
thd->killed= KILL_CONNECTION;
delete thd;
thd= 0;
}
}
/**
background sending thread
*/
pthread_handler_t background_thread(void *arg __attribute__((unused)))
{
if (my_thread_init())
return 0;
thd_thread_id= next_thread_id();
if (slept_ok(startup_interval))
{
send_report("startup");
if (slept_ok(first_interval))
{
send_report(NULL);
while(slept_ok(interval))
send_report(NULL);
}
send_report("shutdown");
}
my_thread_end();
pthread_exit(0);
return 0;
}
} // namespace feedback