mysql5/mysql-5.7.27/sql/ndb_local_connection.cc

413 lines
12 KiB
C++

/*
Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MYSQL_SERVER
#define MYSQL_SERVER
#endif
#include "my_global.h"
#include "ndb_local_connection.h"
#include "sql_class.h"
#include "sql_prepare.h"
#include "log.h"
Ndb_local_connection::Ndb_local_connection(THD* thd_arg):
m_thd(thd_arg)
{
assert(thd_arg);
/*
System(or daemon) threads report error to log file
all other threads use push_warning
*/
m_push_warnings = (thd_arg->get_command() != COM_DAEMON);
}
static inline bool
should_ignore_error(const uint* ignore_error_list, uint error)
{
DBUG_ENTER("should_ignore_error");
DBUG_PRINT("enter", ("error: %u", error));
const uint* ignore_error = ignore_error_list;
while(*ignore_error)
{
DBUG_PRINT("info", ("ignore_error: %u", *ignore_error));
if (*ignore_error == error)
DBUG_RETURN(true);
ignore_error++;
}
DBUG_PRINT("info", ("Don't ignore error"));
DBUG_RETURN(false);
}
class Suppressor {
public:
virtual ~Suppressor() {}
virtual bool should_ignore_error(Ed_connection& con) const = 0;
};
bool
Ndb_local_connection::execute_query(MYSQL_LEX_STRING sql_text,
const uint* ignore_mysql_errors,
const Suppressor* suppressor)
{
DBUG_ENTER("Ndb_local_connection::execute_query");
Ed_connection con(m_thd);
if (con.execute_direct(sql_text))
{
/* Error occured while executing the query */
const uint last_errno = con.get_last_errno();
assert(last_errno); // last_errno must have been set
const char* last_errmsg = con.get_last_error();
DBUG_PRINT("error", ("Query '%s' failed, error: '%d: %s'",
sql_text.str,
last_errno, last_errmsg));
// catch some SQL parse errors in debug
assert(last_errno != ER_PARSE_ERROR ||
last_errno != ER_EMPTY_QUERY);
/* Check if this is a MySQL level errors that should be ignored */
if (ignore_mysql_errors &&
should_ignore_error(ignore_mysql_errors, last_errno))
{
/* MySQL level error suppressed -> return success */
m_thd->clear_error();
DBUG_RETURN(false);
}
/*
Call the suppressor to check if it want to silence
this error
*/
if (suppressor &&
suppressor->should_ignore_error(con))
{
/* Error suppressed -> return sucess */
m_thd->clear_error();
DBUG_RETURN(false);
}
if (m_push_warnings)
{
// Append the error which caused the error to thd's warning list
push_warning(m_thd, Sql_condition::SL_WARNING,
last_errno, last_errmsg);
}
else
{
// Print the error to log file
sql_print_error("NDB: Query '%s' failed, error: %d: %s",
sql_text.str,
last_errno, last_errmsg);
}
DBUG_RETURN(true);
}
// Query returned ok, thd should have no error
assert(!m_thd->is_error());
DBUG_RETURN(false); // Success
}
/*
Execute the query with even higher isolation than what execute_query
provides to avoid that for example THD's status variables are changed
*/
bool
Ndb_local_connection::execute_query_iso(MYSQL_LEX_STRING sql_text,
const uint* ignore_mysql_errors,
const Suppressor* suppressor)
{
/* Don't allow queries to affect THD's status variables */
struct system_status_var save_thd_status_var= m_thd->status_var;
/* Check modified_non_trans_table is false(check if actually needed) */
assert(!m_thd->get_transaction()->has_modified_non_trans_table(
Transaction_ctx::STMT));
#if 0
/*
Saves pseudo_thread_id and assign a "random" thread id from
the global "thread_id" variable without taking a lock
This looks like a copy and paste bug from some THD:: function
should probably be assigned thd->thread_id, if the pseudo_thread_id
need to be changed at all..
*/
ulong save_thd_thread_id= m_thd->variables.pseudo_thread_id;
m_thd->variables.pseudo_thread_id = thread_id;
#endif
/* Turn off binlogging */
ulonglong save_thd_options= m_thd->variables.option_bits;
assert(sizeof(save_thd_options) == sizeof(m_thd->variables.option_bits));
m_thd->variables.option_bits&= ~OPTION_BIN_LOG;
bool result = execute_query(sql_text,
ignore_mysql_errors,
suppressor);
/* Restore THD settings */
m_thd->variables.option_bits= save_thd_options;
#if 0
m_thd->variables.pseudo_thread_id = save_thd_thread_id;
#endif
m_thd->status_var= save_thd_status_var;
return result;
}
bool
Ndb_local_connection::truncate_table(const char* db, size_t db_length,
const char* table, size_t table_length,
bool ignore_no_such_table)
{
DBUG_ENTER("Ndb_local_connection::truncate_table");
DBUG_PRINT("enter", ("db: '%s', table: '%s'", db, table));
// Create the SQL string
String sql_text((uint32)(db_length + table_length + 100));
sql_text.append(STRING_WITH_LEN("TRUNCATE TABLE "));
sql_text.append(db, (uint32)db_length);
sql_text.append(STRING_WITH_LEN("."));
sql_text.append(table, (uint32)table_length);
// Setup list of errors to ignore
uint ignore_mysql_errors[2] = {0, 0};
if (ignore_no_such_table)
ignore_mysql_errors[0] = ER_NO_SUCH_TABLE;
DBUG_RETURN(execute_query_iso(sql_text.lex_string(),
ignore_mysql_errors,
NULL));
}
bool
Ndb_local_connection::flush_table(const char* db, size_t db_length,
const char* table, size_t table_length)
{
DBUG_ENTER("Ndb_local_connection::flush_table");
DBUG_PRINT("enter", ("db: '%s', table: '%s'", db, table));
// Create the SQL string
String sql_text((uint32)(db_length + table_length + 100));
sql_text.append(STRING_WITH_LEN("FLUSH TABLES "));
sql_text.append(db, (uint32)db_length);
sql_text.append(STRING_WITH_LEN("."));
sql_text.append(table, (uint32)table_length);
DBUG_RETURN(execute_query_iso(sql_text.lex_string(),
NULL,
NULL));
}
bool
Ndb_local_connection::delete_rows(const char* db, size_t db_length,
const char* table, size_t table_length,
bool ignore_no_such_table,
...)
{
DBUG_ENTER("Ndb_local_connection::truncate_table");
DBUG_PRINT("enter", ("db: '%s', table: '%s'", db, table));
// Create the SQL string
String sql_text((uint32)(db_length + table_length + 100));
sql_text.append(STRING_WITH_LEN("DELETE FROM "));
sql_text.append(db, (uint32)db_length);
sql_text.append(STRING_WITH_LEN("."));
sql_text.append(table, (uint32)table_length);
sql_text.append(" WHERE ");
va_list args;
va_start(args, ignore_no_such_table);
// Append var args strings until ending NULL as WHERE clause
const char* arg;
bool empty_where = true;
while ((arg= va_arg(args, char *)))
{
sql_text.append(arg);
empty_where = false;
}
va_end(args);
if (empty_where)
sql_text.append("1=1");
// Setup list of errors to ignore
uint ignore_mysql_errors[2] = {0, 0};
if (ignore_no_such_table)
ignore_mysql_errors[0] = ER_NO_SUCH_TABLE;
DBUG_RETURN(execute_query_iso(sql_text.lex_string(),
ignore_mysql_errors,
NULL));
}
class Create_sys_table_suppressor : public Suppressor
{
public:
virtual ~Create_sys_table_suppressor() {}
virtual bool should_ignore_error(Ed_connection& con) const
{
const uint last_errno = con.get_last_errno();
const char* last_errmsg = con.get_last_error();
DBUG_ENTER("Create_sys_table_suppressor::should_ignore_error");
DBUG_PRINT("enter", ("last_errno: %d, last_errmsg: '%s'",
last_errno, last_errmsg));
if (last_errno == ER_CANT_CREATE_TABLE)
{
/*
The CREATE TABLE failed late and it was classifed as a
'Can't create table' error.
*/
/*
Error message always end with " %d)" in all languages. Find last
space and convert number from there
*/
const char* last_space = strrchr(last_errmsg, ' ');
DBUG_PRINT("info", ("last_space: '%s'", last_space));
if (!last_space)
{
// Could not find last space, parse error
assert(false);
DBUG_RETURN(false); // Don't suppress
}
int error;
if (sscanf(last_space, " %d)", &error) != 1)
{
// Not a number here, parse error
assert(false);
DBUG_RETURN(false); // Don't suppress
}
DBUG_PRINT("info", ("error: %d", error));
switch (error)
{
case HA_ERR_TABLE_EXIST:
{
/*
The most common error is that NDB returns error 721
which means 'No such table' and the error is automatically
mapped to MySQL error code ER_TABLE_EXISTS_ERROR
This is most likley caused by another MySQL Server trying
to create the same table inbetween the check if table
exists(on local disk and in storage engine) and the actual
create.
*/
DBUG_RETURN(true); // Suppress
break;
}
case 701: // System busy with other schema operation
case 711: // System busy with node restart, no schema operations
case 702: // Request to non-master(should never pop up to api)
{
/* Different errors from NDB, that just need to be retried later */
DBUG_RETURN(true); // Suppress
break;
}
case 4009: // Cluster failure
case HA_ERR_NO_CONNECTION: // 4009 auto mapped to this error
{
/*
No connection to cluster, don't spam error log with
failed to create ndb_xx tables
*/
DBUG_RETURN(true); // Suppress
break;
}
}
}
DBUG_PRINT("info", ("Don't ignore error"));
DBUG_RETURN(false); // Don't suppress
}
};
bool
Ndb_local_connection::create_sys_table(const char* db, size_t db_length,
const char* table, size_t table_length,
bool create_if_not_exists,
const char* create_definitions,
const char* create_options)
{
DBUG_ENTER("Ndb_local_connection::create_table");
DBUG_PRINT("enter", ("db: '%s', table: '%s'", db, table));
// Create the SQL string
String sql_text(512);
sql_text.append(STRING_WITH_LEN("CREATE TABLE "));
if (create_if_not_exists)
sql_text.append(STRING_WITH_LEN("IF NOT EXISTS "));
sql_text.append(db, (uint32)db_length);
sql_text.append(STRING_WITH_LEN("."));
sql_text.append(table, (uint32)table_length);
sql_text.append(STRING_WITH_LEN(" ( "));
sql_text.append(create_definitions);
sql_text.append(STRING_WITH_LEN(" ) "));
sql_text.append(create_options);
// List of errors to ignore
uint ignore_mysql_errors[2] = {ER_TABLE_EXISTS_ERROR, 0};
/*
This is the only place where an error is suppressed
based one the original NDB error, wich is extracted
by parsing the error string, use a special suppressor
*/
Create_sys_table_suppressor suppressor;
DBUG_RETURN(execute_query_iso(sql_text.lex_string(),
ignore_mysql_errors,
&suppressor));
}
bool
Ndb_local_connection::raw_run_query(const char* query, size_t query_length,
const int* suppress_errors)
{
DBUG_ENTER("Ndb_local_connection::raw_run_query");
LEX_STRING sql_text = { (char*)query, query_length };
DBUG_RETURN(execute_query_iso(sql_text,
(const uint*)suppress_errors,
NULL));
}