mariadb/sql/sys_vars.inl
Monty b66cdbd1ea Changing all cost calculation to be given in milliseconds
This makes it easier to compare different costs and also allows
the optimizer to optimizer different storage engines more reliably.

- Added tests/check_costs.pl, a tool to verify optimizer cost calculations.
  - Most engine costs has been found with this program. All steps to
    calculate the new costs are documented in Docs/optimizer_costs.txt

- User optimizer_cost variables are given in microseconds (as individual
  costs can be very small). Internally they are stored in ms.
- Changed DISK_READ_COST (was DISK_SEEK_BASE_COST) from a hard disk cost
  (9 ms) to common SSD cost (400MB/sec).
- Removed cost calculations for hard disks (rotation etc).
- Changed the following handler functions to return IO_AND_CPU_COST.
  This makes it easy to apply different cost modifiers in ha_..time()
  functions for io and cpu costs.
  - scan_time()
  - rnd_pos_time() & rnd_pos_call_time()
  - keyread_time()
- Enhanched keyread_time() to calculate the full cost of reading of a set
  of keys with a given number of ranges and optional number of blocks that
  need to be accessed.
- Removed read_time() as keyread_time() + rnd_pos_time() can do the same
  thing and more.
- Tuned cost for: heap, myisam, Aria, InnoDB, archive and MyRocks.
  Used heap table costs for json_table. The rest are using default engine
  costs.
- Added the following new optimizer variables:
  - optimizer_disk_read_ratio
  - optimizer_disk_read_cost
  - optimizer_key_lookup_cost
  - optimizer_row_lookup_cost
  - optimizer_row_next_find_cost
  - optimizer_scan_cost
- Moved all engine specific cost to OPTIMIZER_COSTS structure.
- Changed costs to use 'records_out' instead of 'records_read' when
  recalculating costs.
- Split optimizer_costs.h to optimizer_costs.h and optimizer_defaults.h.
  This allows one to change costs without having to compile a lot of
  files.
- Updated costs for filter lookup.
- Use a better cost estimate in best_extension_by_limited_search()
  for the sorting cost.
- Fixed previous issues with 'filtered' explain column as we are now
  using 'records_out' (min rows seen for table) to calculate filtering.
  This greatly simplifies the filtering code in
  JOIN_TAB::save_explain_data().

This change caused a lot of queries to be optimized differently than
before, which exposed different issues in the optimizer that needs to
be fixed.  These fixes are in the following commits.  To not have to
change the same test case over and over again, the changes in the test
cases are done in a single commit after all the critical change sets
are done.

InnoDB changes:
- Updated InnoDB to not divide big range cost with 2.
- Added cost for InnoDB (innobase_update_optimizer_costs()).
- Don't mark clustered primary key with HA_KEYREAD_ONLY. This will
  prevent that the optimizer is trying to use index-only scans on
  the clustered key.
- Disabled ha_innobase::scan_time() and ha_innobase::read_time() and
  ha_innobase::rnd_pos_time() as the default engine cost functions now
  works good for InnoDB.

Other things:
- Added  --show-query-costs (\Q) option to mysql.cc to show the query
  cost after each query (good when working with query costs).
- Extended my_getopt with GET_ADJUSTED_VALUE which allows one to adjust
  the value that user is given. This is used to change cost from
  microseconds (user input) to milliseconds (what the server is
  internally using).
- Added include/my_tracker.h  ; Useful include file to quickly test
  costs of a function.
- Use handler::set_table() in all places instead of 'table= arg'.
- Added SHOW_OPTIMIZER_COSTS to sys variables. These are input and
  shown in microseconds for the user but stored as milliseconds.
  This is to make the numbers easier to read for the user (less
  pre-zeros).  Implemented in 'Sys_var_optimizer_cost' class.
- In test_quick_select() do not use index scans if 'no_keyread' is set
  for the table. This is what we do in other places of the server.
- Added THD parameter to Unique::get_use_cost() and
  check_index_intersect_extension() and similar functions to be able
  to provide costs to called functions.
- Changed 'records' to 'rows' in optimizer_trace.
- Write more information to optimizer_trace.
- Added INDEX_BLOCK_FILL_FACTOR_MUL (4) and INDEX_BLOCK_FILL_FACTOR_DIV (3)
  to calculate usage space of keys in b-trees. (Before we used numeric
  constants).
- Removed code that assumed that b-trees has similar costs as binary
  trees. Replaced with engine calls that returns the cost.
- Added Bitmap::find_first_bit()
- Added timings to join_cache for ANALYZE table (patch by Sergei Petrunia).
- Added records_init and records_after_filter to POSITION to remember
  more of what best_access_patch() calculates.
- table_after_join_selectivity() changed to recalculate 'records_out'
  based on the new fields from best_access_patch()

Bug fixes:
- Some queries did not update last_query_cost (was 0). Fixed by moving
  setting thd->...last_query_cost in JOIN::optimize().
- Write '0' as number of rows for const tables with a matching row.

Some internals:
- Engine cost are stored in OPTIMIZER_COSTS structure.  When a
  handlerton is created, we also created a new cost variable for the
  handlerton. We also create a new variable if the user changes a
  optimizer cost for a not yet loaded handlerton either with command
  line arguments or with SET
  @@global.engine.optimizer_cost_variable=xx.
- There are 3 global OPTIMIZER_COSTS variables:
  default_optimizer_costs   The default costs + changes from the
                            command line without an engine specifier.
  heap_optimizer_costs      Heap table costs, used for temporary tables
  tmp_table_optimizer_costs The cost for the default on disk internal
                            temporary table (MyISAM or Aria)
- The engine cost for a table is stored in table_share. To speed up
  accesses the handler has a pointer to this. The cost is copied
  to the table on first access. If one wants to change the cost one
  must first update the global engine cost and then do a FLUSH TABLES.
  This was done to be able to access the costs for an open table
  without any locks.
- When a handlerton is created, the cost are updated the following way:
  See sql/keycaches.cc for details:
  - Use 'default_optimizer_costs' as a base
  - Call hton->update_optimizer_costs() to override with the engines
    default costs.
  - Override the costs that the user has specified for the engine.
  - One handler open, copy the engine cost from handlerton to TABLE_SHARE.
  - Call handler::update_optimizer_costs() to allow the engine to update
    cost for this particular table.
  - There are two costs stored in THD. These are copied to the handler
    when the table is used in a query:
    - optimizer_where_cost
    - optimizer_scan_setup_cost
- Simply code in best_access_path() by storing all cost result in a
  structure. (Idea/Suggestion by Igor)
2023-02-02 23:54:45 +03:00

2945 lines
95 KiB
C++

/* Copyright (c) 2002, 2011, Oracle and/or its affiliates.
Copyright (c) 2010, 2020, MariaDB Corporation.
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-1335 USA */
/**
@file
"private" interface to sys_var - server configuration variables.
This header is included only by the file that contains declarations
of sys_var variables (sys_vars.cc).
*/
#include "sys_vars_shared.h"
#include <my_getopt.h>
#include <my_bit.h>
#include <my_dir.h>
#include "keycaches.h"
#include "strfunc.h"
#include "tztime.h" // my_tz_find, my_tz_SYSTEM, struct Time_zone
#include "rpl_mi.h" // For Multi-Source Replication
#include "debug_sync.h"
#include "sql_acl.h" // check_global_access()
#include "optimizer_defaults.h" // create_optimizer_costs
/*
a set of mostly trivial (as in f(X)=X) defines below to make system variable
declarations more readable
*/
#define VALID_RANGE(X,Y) X,Y
#define DEFAULT(X) X
#define BLOCK_SIZE(X) X
#define COST_ADJUST(X) X
#define GLOBAL_VAR(X) sys_var::GLOBAL, (((char*)&(X))-(char*)&global_system_variables), sizeof(X)
#define SESSION_VAR(X) sys_var::SESSION, offsetof(SV, X), sizeof(((SV *)0)->X)
#define SESSION_ONLY(X) sys_var::ONLY_SESSION, offsetof(SV, X), sizeof(((SV *)0)->X)
#define NO_CMD_LINE CMD_LINE(NO_ARG, sys_var::NO_GETOPT)
#define CMD_LINE_HELP_ONLY CMD_LINE(NO_ARG, sys_var::GETOPT_ONLY_HELP)
/*
the define below means that there's no *second* mutex guard,
LOCK_global_system_variables always guards all system variables
*/
#define NO_MUTEX_GUARD ((PolyLock*)0)
#define IN_BINLOG sys_var::SESSION_VARIABLE_IN_BINLOG
#define NOT_IN_BINLOG sys_var::VARIABLE_NOT_IN_BINLOG
#define ON_READ(X) X
#define ON_CHECK(X) X
#define ON_UPDATE(X) X
#define READ_ONLY sys_var::READONLY+
#define AUTO_SET sys_var::AUTO_SET+
// this means that Sys_var_charptr initial value was malloc()ed
#define PREALLOCATED sys_var::ALLOCATED+
#define PARSED_EARLY sys_var::PARSE_EARLY+
#define NO_SET_STMT sys_var::NO_SET_STATEMENT+
/*
Sys_var_bit meaning is reversed, like in
@@foreign_key_checks <-> OPTION_NO_FOREIGN_KEY_CHECKS
*/
#define REVERSE(X) ~(X)
#define DEPRECATED(X) X
#define session_var(THD, TYPE) (*(TYPE*)session_var_ptr(THD))
#define global_var(TYPE) (*(TYPE*)global_var_ptr())
#if SIZEOF_OFF_T > 4 && defined(BIG_TABLES)
#define GET_HA_ROWS GET_ULL
#else
#define GET_HA_ROWS GET_ULONG
#endif
// Disable warning caused by SESSION_VAR() macro
#ifdef __clang__
#pragma clang diagnostic ignored "-Winvalid-offsetof"
#endif
/*
special assert for sysvars. Tells the name of the variable,
and fails even in non-debug builds.
It is supposed to be used *only* in Sys_var* constructors,
and has name_arg hard-coded to prevent incorrect usage.
*/
#define SYSVAR_ASSERT(X) \
while(!(X)) \
{ \
fprintf(stderr, "Sysvar '%s' failed '%s'\n", name_arg, #X); \
DBUG_ASSERT(0); \
exit(255); \
}
static const char *bool_values[3]= {"OFF", "ON", 0};
TYPELIB bool_typelib={ array_elements(bool_values)-1, "", bool_values, 0 };
template<class BASE, privilege_t GLOBAL_PRIV, privilege_t SESSION_PRIV>
class Sys_var_on_access: public BASE
{
using BASE::BASE;
bool on_check_access_global(THD *thd) const override
{
return check_global_access(thd, GLOBAL_PRIV);
}
bool on_check_access_session(THD *thd) const override
{
return check_global_access(thd, SESSION_PRIV);
}
};
template<class BASE, privilege_t GLOBAL_PRIV>
class Sys_var_on_access_global: public BASE
{
using BASE::BASE;
bool on_check_access_global(THD *thd) const override
{
return check_global_access(thd, GLOBAL_PRIV);
}
};
template<class BASE, privilege_t SESSION_PRIV>
class Sys_var_on_access_session: public BASE
{
using BASE::BASE;
bool on_check_access_session(THD *thd) const override
{
return check_global_access(thd, SESSION_PRIV);
}
};
/**
A small wrapper class to pass getopt arguments as a pair
to the Sys_var_* constructors. It improves type safety and helps
to catch errors in the argument order.
*/
struct CMD_LINE
{
int id;
enum get_opt_arg_type arg_type;
CMD_LINE(enum get_opt_arg_type getopt_arg_type, int getopt_id=0)
: id(getopt_id), arg_type(getopt_arg_type) {}
};
/**
Sys_var_integer template is used to generate Sys_var_* classes
for variables that represent the value as an integer number.
They are Sys_var_uint, Sys_var_ulong, Sys_var_harows, Sys_var_ulonglong,
Sys_var_int.
An integer variable has a minimal and maximal values, and a "block_size"
(any valid value of the variable must be divisible by the block_size).
Class specific constructor arguments: min, max, block_size
Backing store: int, uint, ulong, ha_rows, ulonglong, depending on the class
*/
template <typename T, ulong ARGT, enum enum_mysql_show_type SHOWT>
class Sys_var_integer: public sys_var
{
public:
Sys_var_integer(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
T min_val, T max_val, T def_val, uint block_size, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOWT, def_val, lock, binlog_status_arg,
on_check_func, on_update_func, substitute)
{
option.var_type|= ARGT;
option.min_value= min_val;
option.max_value= max_val;
option.block_size= block_size;
if ((option.u_max_value= (uchar**) max_var_ptr()))
{
*((T*) option.u_max_value)= max_val;
}
global_var(T)= def_val;
SYSVAR_ASSERT(size == sizeof(T));
SYSVAR_ASSERT(min_val < max_val);
SYSVAR_ASSERT(min_val <= def_val);
SYSVAR_ASSERT(max_val >= def_val);
SYSVAR_ASSERT(block_size > 0);
SYSVAR_ASSERT(def_val % block_size == 0);
}
bool do_check(THD *thd, set_var *var)
{
my_bool fixed= FALSE, unused;
longlong v= var->value->val_int();
if ((ARGT == GET_HA_ROWS) || (ARGT == GET_UINT) ||
(ARGT == GET_ULONG) || (ARGT == GET_ULL))
{
ulonglong uv;
/*
if the value is signed and negative,
and a variable is unsigned, it is set to zero
*/
if ((fixed= (!var->value->unsigned_flag && v < 0)))
uv= 0;
else
uv= v;
var->save_result.ulonglong_value=
getopt_ull_limit_value(uv, &option, &unused);
if (max_var_ptr() && (T)var->save_result.ulonglong_value > get_max_var())
var->save_result.ulonglong_value= get_max_var();
fixed= fixed || var->save_result.ulonglong_value != uv;
}
else
{
/*
if the value is unsigned and has the highest bit set
and a variable is signed, it is set to max signed value
*/
if ((fixed= (var->value->unsigned_flag && v < 0)))
v= LONGLONG_MAX;
var->save_result.longlong_value=
getopt_ll_limit_value(v, &option, &unused);
if (max_var_ptr() && (T)var->save_result.longlong_value > get_max_var())
var->save_result.longlong_value= get_max_var();
fixed= fixed || var->save_result.longlong_value != v;
}
return throw_bounds_warning(thd, name.str, fixed,
var->value->unsigned_flag, v);
}
bool session_update(THD *thd, set_var *var)
{
session_var(thd, T)= static_cast<T>(var->save_result.ulonglong_value);
return false;
}
bool global_update(THD *thd, set_var *var)
{
global_var(T)= static_cast<T>(var->save_result.ulonglong_value);
return false;
}
void session_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= (ulonglong)*(T*)global_value_ptr(thd, 0); }
void global_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= option.def_value; }
private:
T get_max_var() { return *((T*) max_var_ptr()); }
const uchar *default_value_ptr(THD *thd) const { return (uchar*) &option.def_value; }
};
typedef Sys_var_integer<int, GET_INT, SHOW_SINT> Sys_var_int;
typedef Sys_var_integer<uint, GET_UINT, SHOW_UINT> Sys_var_uint;
typedef Sys_var_integer<ulong, GET_ULONG, SHOW_ULONG> Sys_var_ulong;
typedef Sys_var_integer<ha_rows, GET_HA_ROWS, SHOW_HA_ROWS> Sys_var_harows;
typedef Sys_var_integer<ulonglong, GET_ULL, SHOW_ULONGLONG> Sys_var_ulonglong;
typedef Sys_var_integer<long, GET_LONG, SHOW_SLONG> Sys_var_long;
template<> const uchar *Sys_var_int::default_value_ptr(THD *thd) const
{
thd->sys_var_tmp.int_value= (int)option.def_value;
return (uchar*) &thd->sys_var_tmp.int_value;
}
template<> const uchar *Sys_var_uint::default_value_ptr(THD *thd) const
{
thd->sys_var_tmp.uint_value= (uint)option.def_value;
return (uchar*) &thd->sys_var_tmp.uint_value;
}
template<> const uchar *Sys_var_long::default_value_ptr(THD *thd) const
{
thd->sys_var_tmp.long_value= (long)option.def_value;
return (uchar*) &thd->sys_var_tmp.long_value;
}
template<> const uchar *Sys_var_ulong::default_value_ptr(THD *thd) const
{
thd->sys_var_tmp.ulong_value= (ulong)option.def_value;
return (uchar*) &thd->sys_var_tmp.ulong_value;
}
/**
Helper class for variables that take values from a TYPELIB
*/
class Sys_var_typelib: public sys_var
{
protected:
TYPELIB typelib;
virtual bool check_maximum(THD *thd, set_var *var,
const char *c_val, longlong i_val)
{ return FALSE; }
public:
Sys_var_typelib(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off,
CMD_LINE getopt,
SHOW_TYPE show_val_type_arg, const char *values[],
ulonglong def_val, PolyLock *lock,
enum binlog_status_enum binlog_status_arg,
on_check_function on_check_func, on_update_function on_update_func,
const char *substitute)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, show_val_type_arg, def_val, lock,
binlog_status_arg, on_check_func,
on_update_func, substitute)
{
for (typelib.count= 0; values[typelib.count]; typelib.count++) /*no-op */;
typelib.name="";
typelib.type_names= values;
typelib.type_lengths= 0; // only used by Fields_enum and Field_set
option.typelib= &typelib;
}
bool do_check(THD *thd, set_var *var) // works for enums and my_bool
{
char buff[STRING_BUFFER_USUAL_SIZE];
String str(buff, sizeof(buff), system_charset_info), *res;
if (var->value->result_type() == STRING_RESULT)
{
if (!(res=var->value->val_str(&str)))
return true;
else
if (!(var->save_result.ulonglong_value=
find_type(&typelib, res->ptr(), res->length(), false)))
return true;
else
var->save_result.ulonglong_value--;
return check_maximum(thd, var, res->ptr(), 0);
}
longlong tmp=var->value->val_int();
if (tmp < 0 || tmp >= typelib.count)
return true;
var->save_result.ulonglong_value= tmp;
return check_maximum(thd, var, 0, tmp);
}
};
/**
The class for ENUM variables - variables that take one value from a fixed
list of values.
Class specific constructor arguments:
char* values[] - 0-terminated list of strings of valid values
Backing store: ulong
@note
Do *not* use "enum FOO" variables as a backing store, there is no
guarantee that sizeof(enum FOO) == sizeof(uint), there is no guarantee
even that sizeof(enum FOO) == sizeof(enum BAR)
*/
class Sys_var_enum: public Sys_var_typelib
{
public:
Sys_var_enum(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *values[], uint def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: Sys_var_typelib(name_arg, comment, flag_args, off, getopt,
SHOW_CHAR, values, def_val, lock,
binlog_status_arg, on_check_func, on_update_func,
substitute)
{
option.var_type|= GET_ENUM;
option.min_value= 0;
option.max_value= ULONG_MAX;
global_var(ulong)= def_val;
if ((option.u_max_value= (uchar**)max_var_ptr()))
{
*((ulong *) option.u_max_value)= ULONG_MAX;
}
SYSVAR_ASSERT(def_val < typelib.count);
SYSVAR_ASSERT(size == sizeof(ulong));
}
bool check_maximum(THD *thd, set_var *var,
const char *c_val, longlong i_val)
{
if (!max_var_ptr() ||
var->save_result.ulonglong_value <= get_max_var())
return FALSE;
var->save_result.ulonglong_value= get_max_var();
return c_val ? throw_bounds_warning(thd, name.str, c_val) :
throw_bounds_warning(thd, name.str, TRUE,
var->value->unsigned_flag, i_val);
}
bool session_update(THD *thd, set_var *var)
{
session_var(thd, ulong)= static_cast<ulong>(var->save_result.ulonglong_value);
return false;
}
bool global_update(THD *thd, set_var *var)
{
global_var(ulong)= static_cast<ulong>(var->save_result.ulonglong_value);
return false;
}
void session_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= global_var(ulong); }
void global_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= option.def_value; }
const uchar *valptr(THD *thd, ulong val) const
{ return reinterpret_cast<const uchar*>(typelib.type_names[val]); }
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, session_var(thd, ulong)); }
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, global_var(ulong)); }
const uchar *default_value_ptr(THD *thd) const
{ return valptr(thd, (ulong)option.def_value); }
ulong get_max_var() { return *((ulong *) max_var_ptr()); }
};
/**
The class for boolean variables - a variant of ENUM variables
with the fixed list of values of { OFF , ON }
Backing store: my_bool
*/
class Sys_var_mybool: public Sys_var_typelib
{
public:
Sys_var_mybool(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
my_bool def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: Sys_var_typelib(name_arg, comment, flag_args, off, getopt,
SHOW_MY_BOOL, bool_values, def_val, lock,
binlog_status_arg, on_check_func, on_update_func,
substitute)
{
option.var_type|= GET_BOOL;
global_var(my_bool)= def_val;
SYSVAR_ASSERT(def_val < 2);
SYSVAR_ASSERT(getopt.arg_type == OPT_ARG || getopt.id < 0);
SYSVAR_ASSERT(size == sizeof(my_bool));
}
bool session_update(THD *thd, set_var *var)
{
session_var(thd, my_bool)= var->save_result.ulonglong_value != 0;
return false;
}
bool global_update(THD *thd, set_var *var)
{
global_var(my_bool)= var->save_result.ulonglong_value != 0;
return false;
}
void session_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= (ulonglong)*(my_bool *)global_value_ptr(thd, 0); }
void global_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= option.def_value; }
const uchar *default_value_ptr(THD *thd) const
{
thd->sys_var_tmp.my_bool_value=(my_bool) option.def_value;
return (uchar*) &thd->sys_var_tmp.my_bool_value;
}
};
/**
The class for string variables. The string can be in character_set_filesystem
or in character_set_system. The string can be allocated with my_malloc()
or not. The state of the initial value is specified in the constructor,
after that it's managed automatically. The value of NULL is supported.
Backing store: char*
@note
This class supports only GLOBAL variables, because THD on destruction
does not destroy individual members of SV, there's no way to free
allocated string variables for every thread.
*/
class Sys_var_charptr_base: public sys_var
{
public:
Sys_var_charptr_base(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR_PTR, (intptr)def_val,
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{
/*
use GET_STR_ALLOC - if ALLOCATED it must be *always* allocated,
otherwise (GET_STR) you'll never know whether to free it or not.
(think of an exit because of an error right after my_getopt)
*/
option.var_type|= (flags & ALLOCATED) ? GET_STR_ALLOC : GET_STR;
global_var(const char*)= def_val;
}
void cleanup()
{
if (flags & ALLOCATED)
{
my_free(global_var(char*));
global_var(char *)= NULL;
}
flags&= ~ALLOCATED;
}
static bool do_string_check(THD *thd, set_var *var, CHARSET_INFO *charset)
{
char buff[STRING_BUFFER_USUAL_SIZE], buff2[STRING_BUFFER_USUAL_SIZE];
String str(buff, sizeof(buff), charset);
String str2(buff2, sizeof(buff2), charset), *res;
if (!(res=var->value->val_str(&str)))
{
var->save_result.string_value.str= 0;
var->save_result.string_value.length= 0; // safety
}
else
{
uint32 unused;
if (String::needs_conversion(res->length(), res->charset(),
charset, &unused))
{
uint errors;
str2.copy(res->ptr(), res->length(), res->charset(), charset,
&errors);
res=&str2;
}
var->save_result.string_value.str= thd->strmake(res->ptr(), res->length());
var->save_result.string_value.length= res->length();
}
return false;
}
bool do_check(THD *thd, set_var *var)
{ return do_string_check(thd, var, charset(thd)); }
bool session_update(THD *thd, set_var *var)= 0;
char *global_update_prepare(THD *thd, set_var *var)
{
char *new_val, *ptr= var->save_result.string_value.str;
size_t len=var->save_result.string_value.length;
if (ptr)
{
new_val= (char*)my_memdup(key_memory_Sys_var_charptr_value,
ptr, len+1, MYF(MY_WME));
if (!new_val) return 0;
new_val[len]=0;
}
else
new_val= 0;
return new_val;
}
void global_update_finish(char *new_val)
{
if (flags & ALLOCATED)
my_free(global_var(char*));
flags|= ALLOCATED;
global_var(char*)= new_val;
}
bool global_update(THD *thd, set_var *var)
{
char *new_val= global_update_prepare(thd, var);
global_update_finish(new_val);
return (new_val == 0 && var->save_result.string_value.str != 0);
}
void session_save_default(THD *thd, set_var *var)= 0;
void global_save_default(THD *thd, set_var *var)
{
char *ptr= (char*)(intptr)option.def_value;
var->save_result.string_value.str= ptr;
var->save_result.string_value.length= ptr ? strlen(ptr) : 0;
}
};
class Sys_var_charptr: public Sys_var_charptr_base
{
public:
Sys_var_charptr(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0) :
Sys_var_charptr_base(name_arg, comment, flag_args, off, size, getopt,
def_val, lock, binlog_status_arg,
on_check_func, on_update_func, substitute)
{
SYSVAR_ASSERT(scope() == GLOBAL);
SYSVAR_ASSERT(size == sizeof(char *));
}
bool session_update(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return true;
}
void session_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
};
class Sys_var_charptr_fscs: public Sys_var_charptr
{
using Sys_var_charptr::Sys_var_charptr;
public:
CHARSET_INFO *charset(THD *thd) const override
{
return thd->variables.character_set_filesystem;
}
};
#ifndef EMBEDDED_LIBRARY
class Sys_var_sesvartrack: public Sys_var_charptr_base
{
public:
Sys_var_sesvartrack(const char *name_arg,
const char *comment,
CMD_LINE getopt,
const char *def_val, PolyLock *lock= 0) :
Sys_var_charptr_base(name_arg, comment,
SESSION_VAR(session_track_system_variables),
getopt, def_val, lock,
VARIABLE_NOT_IN_BINLOG, 0, 0, 0)
{}
bool do_check(THD *thd, set_var *var)
{
if (Sys_var_charptr_base::do_check(thd, var) ||
sysvartrack_validate_value(thd, var->save_result.string_value.str,
var->save_result.string_value.length))
return TRUE;
return FALSE;
}
bool global_update(THD *thd, set_var *var)
{
char *new_val= global_update_prepare(thd, var);
if (new_val)
{
if (sysvartrack_global_update(thd, new_val,
var->save_result.string_value.length))
new_val= 0;
}
global_update_finish(new_val);
return (new_val == 0 && var->save_result.string_value.str != 0);
}
bool session_update(THD *thd, set_var *var)
{ return thd->session_tracker.sysvars.update(thd, var); }
void session_save_default(THD *thd, set_var *var)
{
var->save_result.string_value.str= global_var(char*);
var->save_result.string_value.length=
strlen(var->save_result.string_value.str);
/* parse and feel list with default values */
if (thd)
{
#ifdef DBUG_ASSERT_EXISTS
bool res=
#endif
sysvartrack_validate_value(thd,
var->save_result.string_value.str,
var->save_result.string_value.length);
DBUG_ASSERT(res == 0);
}
}
};
#endif //EMBEDDED_LIBRARY
class Sys_var_proxy_user: public sys_var
{
public:
Sys_var_proxy_user(const char *name_arg, const char *comment)
: sys_var(&all_sys_vars, name_arg, comment,
sys_var::READONLY+sys_var::ONLY_SESSION, 0, NO_GETOPT,
NO_ARG, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL)
{
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return true;
}
bool session_update(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return true;
}
bool global_update(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return false;
}
void session_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
void global_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
protected:
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
return thd->security_ctx->proxy_user[0] ?
(uchar *) &(thd->security_ctx->proxy_user[0]) : NULL;
}
};
class Sys_var_external_user : public Sys_var_proxy_user
{
public:
Sys_var_external_user(const char *name_arg, const char *comment_arg)
: Sys_var_proxy_user (name_arg, comment_arg)
{}
protected:
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
return (uchar*)thd->security_ctx->external_user;
}
};
class Master_info;
class Sys_var_rpl_filter: public sys_var
{
private:
int opt_id;
privilege_t m_access_global;
public:
Sys_var_rpl_filter(const char *name, int getopt_id, const char *comment,
privilege_t access_global)
: sys_var(&all_sys_vars, name, comment, sys_var::GLOBAL, 0, NO_GETOPT,
NO_ARG, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL), opt_id(getopt_id),
m_access_global(access_global)
{
option.var_type|= GET_STR | GET_ASK_ADDR;
}
bool do_check(THD *thd, set_var *var) override
{
return Sys_var_charptr::do_string_check(thd, var, charset(thd));
}
void session_save_default(THD *, set_var *) override
{ DBUG_ASSERT(FALSE); }
void global_save_default(THD *thd, set_var *var) override
{
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 *, set_var *) override
{
DBUG_ASSERT(FALSE);
return true;
}
bool global_update(THD *thd, set_var *var) override;
bool on_check_access_global(THD *thd) const override
{
return check_global_access(thd, m_access_global);
}
protected:
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
const override;
bool set_filter_value(const char *value, Master_info *mi);
};
/**
The class for string variables. Useful for strings that aren't necessarily
\0-terminated. Otherwise the same as Sys_var_charptr.
Backing store: LEX_CSTRING
@note
Behaves exactly as Sys_var_charptr, only the backing store is different.
*/
class Sys_var_lexstring: public Sys_var_charptr
{
public:
Sys_var_lexstring(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: Sys_var_charptr(name_arg, comment, flag_args, off, sizeof(char*),
getopt, def_val, lock, binlog_status_arg,
on_check_func, on_update_func, substitute)
{
global_var(LEX_CSTRING).length= strlen(def_val);
SYSVAR_ASSERT(size == sizeof(LEX_CSTRING));
*const_cast<SHOW_TYPE*>(&show_val_type)= SHOW_LEX_STRING;
}
bool global_update(THD *thd, set_var *var)
{
if (Sys_var_charptr::global_update(thd, var))
return true;
global_var(LEX_CSTRING).length= var->save_result.string_value.length;
return false;
}
};
/*
A LEX_CSTRING stored only in thd->variables
Only to be used for small buffers
*/
class Sys_var_session_lexstring: public sys_var
{
size_t max_length;
public:
Sys_var_session_lexstring(const char *name_arg,
const char *comment, int flag_args,
ptrdiff_t off, size_t size, CMD_LINE getopt,
const char *def_val, size_t max_length_arg,
on_check_function on_check_func=0,
on_update_function on_update_func=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, (intptr)def_val,
0, VARIABLE_NOT_IN_BINLOG, on_check_func, on_update_func,
0),max_length(max_length_arg)
{
option.var_type|= GET_STR;
SYSVAR_ASSERT(scope() == ONLY_SESSION)
*const_cast<SHOW_TYPE*>(&show_val_type)= SHOW_LEX_STRING;
}
bool do_check(THD *thd, set_var *var)
{
char buff[STRING_BUFFER_USUAL_SIZE];
String str(buff, sizeof(buff), system_charset_info), *res;
if (!(res=var->value->val_str(&str)))
{
var->save_result.string_value.str= 0; /* NULL */
var->save_result.string_value.length= 0;
}
else
{
if (res->length() > max_length)
{
my_error(ER_WRONG_STRING_LENGTH, MYF(0),
res->ptr(), name.str, (int) max_length);
return true;
}
var->save_result.string_value.str= thd->strmake(res->ptr(),
res->length());
var->save_result.string_value.length= res->length();
}
return false;
}
bool session_update(THD *thd, set_var *var)
{
LEX_CSTRING *tmp= &session_var(thd, LEX_CSTRING);
tmp->length= var->save_result.string_value.length;
/* Store as \0 terminated string (just to be safe) */
strmake((char*) tmp->str, var->save_result.string_value.str, tmp->length);
return false;
}
bool global_update(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return false;
}
void session_save_default(THD *thd, set_var *var)
{
char *ptr= (char*)(intptr)option.def_value;
var->save_result.string_value.str= ptr;
var->save_result.string_value.length= strlen(ptr);
}
void global_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(FALSE);
return NULL;
}
};
#ifndef DBUG_OFF
/**
@@session.debug_dbug and @@global.debug_dbug variables.
@@dbug variable differs from other variables in one aspect:
if its value is not assigned in the session, it "points" to the global
value, and so when the global value is changed, the change
immediately takes effect in the session.
This semantics is intentional, to be able to debug one session from
another.
*/
class Sys_var_dbug: public sys_var
{
public:
Sys_var_dbug(const char *name_arg,
const char *comment, int flag_args,
CMD_LINE getopt,
const char *def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args,
(char*)&current_dbug_option-(char*)&global_system_variables, getopt.id,
getopt.arg_type, SHOW_CHAR, (intptr)def_val,
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{ option.var_type|= GET_STR; }
bool do_check(THD *thd, set_var *var)
{
char buff[STRING_BUFFER_USUAL_SIZE];
String str(buff, sizeof(buff), system_charset_info), *res;
if (!(res=var->value->val_str(&str)))
{
var->save_result.string_value.str= const_cast<char*>("");
var->save_result.string_value.length= 0;
}
else
{
size_t len= res->length();
var->save_result.string_value.str= thd->strmake(res->ptr(), len);
var->save_result.string_value.length= len;
}
return false;
}
bool session_update(THD *thd, set_var *var)
{
const char *val= var->save_result.string_value.str;
if (!var->value)
DBUG_POP();
else
DBUG_SET(val);
return false;
}
bool global_update(THD *thd, set_var *var)
{
const char *val= var->save_result.string_value.str;
DBUG_SET_INITIAL(val);
return false;
}
void session_save_default(THD *thd, set_var *var)
{ }
void global_save_default(THD *thd, set_var *var)
{
char *ptr= (char*)(intptr)option.def_value;
var->save_result.string_value.str= ptr;
var->save_result.string_value.length= safe_strlen(ptr);
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
char buf[256];
DBUG_EXPLAIN(buf, sizeof(buf));
return (uchar*) thd->strdup(buf);
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
char buf[256];
DBUG_EXPLAIN_INITIAL(buf, sizeof(buf));
return (uchar*) thd->strdup(buf);
}
const uchar *default_value_ptr(THD *thd) const
{ return (uchar*)""; }
};
#endif
#define KEYCACHE_VAR(X) GLOBAL_VAR(dflt_key_cache_var.X)
#define keycache_var_ptr(KC, OFF) (((uchar*)(KC))+(OFF))
#define keycache_var(KC, OFF) (*(ulonglong*)keycache_var_ptr(KC, OFF))
typedef bool (*keycache_update_function)(THD *, KEY_CACHE *, ptrdiff_t, ulonglong);
/**
The class for keycache_* variables. Supports structured names,
keycache_name.variable_name.
Class specific constructor arguments:
everything derived from Sys_var_ulonglong
Backing store: ulonglong
@note these variables can be only GLOBAL
*/
class Sys_var_keycache: public Sys_var_ulonglong
{
keycache_update_function keycache_update;
public:
Sys_var_keycache(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
ulonglong min_val, ulonglong max_val, ulonglong def_val,
uint block_size, PolyLock *lock,
enum binlog_status_enum binlog_status_arg,
on_check_function on_check_func,
keycache_update_function on_update_func,
const char *substitute=0)
: Sys_var_ulonglong(name_arg, comment, flag_args, off, size,
getopt, min_val, max_val, def_val,
block_size, lock, binlog_status_arg, on_check_func, 0,
substitute),
keycache_update(on_update_func)
{
option.var_type|= GET_ASK_ADDR;
option.value= (uchar**)1; // crash me, please
// fix an offset from global_system_variables to be an offset in KEY_CACHE
offset= global_var_ptr() - (uchar*)dflt_key_cache;
SYSVAR_ASSERT(scope() == GLOBAL);
}
bool global_update(THD *thd, set_var *var)
{
ulonglong new_value= var->save_result.ulonglong_value;
LEX_CSTRING *base_name= &var->base;
KEY_CACHE *key_cache;
/* If no basename, assume it's for the key cache named 'default' */
if (!base_name->length)
base_name= &default_base;
key_cache= get_key_cache(base_name);
if (!key_cache)
{ // Key cache didn't exists */
if (!new_value) // Tried to delete cache
return false; // Ok, nothing to do
if (!(key_cache= create_key_cache(base_name->str, base_name->length)))
return true;
}
/**
Abort if some other thread is changing the key cache
@todo This should be changed so that we wait until the previous
assignment is done and then do the new assign
*/
if (key_cache->in_init)
return true;
return keycache_update(thd, key_cache, offset, new_value);
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
KEY_CACHE *key_cache= get_key_cache(base);
if (!key_cache)
key_cache= &zero_key_cache;
return keycache_var_ptr(key_cache, offset);
}
};
static bool update_buffer_size(THD *thd, KEY_CACHE *key_cache,
ptrdiff_t offset, ulonglong new_value)
{
bool error= false;
DBUG_ASSERT(offset == offsetof(KEY_CACHE, param_buff_size));
if (new_value == 0)
{
if (key_cache == dflt_key_cache)
{
my_error(ER_WARN_CANT_DROP_DEFAULT_KEYCACHE, MYF(0));
return true;
}
if (key_cache->key_cache_inited) // If initied
{
/*
Move tables using this key cache to the default key cache
and clear the old key cache.
*/
key_cache->in_init= 1;
mysql_mutex_unlock(&LOCK_global_system_variables);
key_cache->param_buff_size= 0;
ha_resize_key_cache(key_cache);
ha_change_key_cache(key_cache, dflt_key_cache);
/*
We don't delete the key cache as some running threads my still be in
the key cache code with a pointer to the deleted (empty) key cache
*/
mysql_mutex_lock(&LOCK_global_system_variables);
key_cache->in_init= 0;
}
return error;
}
key_cache->param_buff_size= new_value;
/* If key cache didn't exist initialize it, else resize it */
key_cache->in_init= 1;
mysql_mutex_unlock(&LOCK_global_system_variables);
if (!key_cache->key_cache_inited)
error= ha_init_key_cache(0, key_cache, 0);
else
error= ha_resize_key_cache(key_cache);
mysql_mutex_lock(&LOCK_global_system_variables);
key_cache->in_init= 0;
return error;
}
static bool update_keycache(THD *thd, KEY_CACHE *key_cache,
ptrdiff_t offset, ulonglong new_value,
int (*func)(KEY_CACHE *))
{
bool error= false;
DBUG_ASSERT(offset != offsetof(KEY_CACHE, param_buff_size));
keycache_var(key_cache, offset)= new_value;
key_cache->in_init= 1;
mysql_mutex_unlock(&LOCK_global_system_variables);
error= func(key_cache);
mysql_mutex_lock(&LOCK_global_system_variables);
key_cache->in_init= 0;
return error;
}
static bool resize_keycache(THD *thd, KEY_CACHE *key_cache,
ptrdiff_t offset, ulonglong new_value)
{
return update_keycache(thd, key_cache, offset, new_value,
ha_resize_key_cache);
}
static bool change_keycache_param(THD *thd, KEY_CACHE *key_cache,
ptrdiff_t offset, ulonglong new_value)
{
return update_keycache(thd, key_cache, offset, new_value,
ha_change_key_cache_param);
}
static bool repartition_keycache(THD *thd, KEY_CACHE *key_cache,
ptrdiff_t offset, ulonglong new_value)
{
return update_keycache(thd, key_cache, offset, new_value,
ha_repartition_key_cache);
}
/**
The class for floating point variables
Class specific constructor arguments: min, max
Backing store: double
*/
class Sys_var_double: public sys_var
{
public:
Sys_var_double(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
double min_val, double max_val, double def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_DOUBLE,
(longlong) getopt_double2ulonglong(def_val),
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{
option.var_type|= GET_DOUBLE;
option.min_value= (longlong) getopt_double2ulonglong(min_val);
option.max_value= (longlong) getopt_double2ulonglong(max_val);
global_var(double)= (double)option.def_value;
SYSVAR_ASSERT(min_val < max_val);
SYSVAR_ASSERT(min_val <= def_val);
SYSVAR_ASSERT(max_val >= def_val);
SYSVAR_ASSERT(size == sizeof(double));
}
bool do_check(THD *thd, set_var *var)
{
my_bool fixed;
double v= var->value->val_real();
var->save_result.double_value= getopt_double_limit_value(v, &option, &fixed);
return throw_bounds_warning(thd, name.str, fixed, v);
}
bool session_update(THD *thd, set_var *var)
{
session_var(thd, double)= var->save_result.double_value;
return false;
}
bool global_update(THD *thd, set_var *var)
{
global_var(double)= var->save_result.double_value;
return false;
}
void session_save_default(THD *thd, set_var *var)
{ var->save_result.double_value= global_var(double); }
void global_save_default(THD *thd, set_var *var)
{ var->save_result.double_value= getopt_ulonglong2double(option.def_value); }
};
/*
Optimizer costs
Stored as cost factor (1 cost = 1 ms).
Given and displayed as microsconds (as most values are very small)
*/
class Sys_var_optimizer_cost: public Sys_var_double
{
public:
double cost_adjust;
Sys_var_optimizer_cost(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
double min_val, double max_val, double def_val,
ulong arg_cost_adjust, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
:Sys_var_double(name_arg, comment, flag_args, off, size, getopt,
min_val, max_val, def_val, lock,
binlog_status_arg,
on_check_func,
on_update_func,
substitute)
{
if (arg_cost_adjust == 1000)
{
show_val_type= SHOW_OPTIMIZER_COST; // For select @@var
option.var_type|= GET_ADJUST_VALUE;
}
cost_adjust= (double) arg_cost_adjust;
global_var(double)= (double)option.def_value/cost_adjust; // To usec
}
bool session_update(THD *thd, set_var *var)
{
session_var(thd, double)= var->save_result.double_value/cost_adjust;
return false;
}
bool global_update(THD *thd, set_var *var)
{
global_var(double)= var->save_result.double_value/cost_adjust;
return false;
}
void session_save_default(THD *thd, set_var *var)
{ var->save_result.double_value= global_var(double) * cost_adjust; }
void global_save_default(THD *thd, set_var *var)
{
var->save_result.double_value= getopt_ulonglong2double(option.def_value)*
cost_adjust;
}
};
/*
The class for optimzer costs with structured names, unique for each engine.
Used as 'engine.variable_name'
Class specific constructor arguments:
everything derived from Sys_var_optimizer_cost
Backing store: double
@note these variables can be only GLOBAL
*/
#define COST_VAR(X) GLOBAL_VAR(default_optimizer_costs.X)
#define cost_var_ptr(KC, OFF) (((uchar*)(KC))+(OFF))
#define cost_var(KC, OFF) (*(double*)cost_var_ptr(KC, OFF))
typedef bool (*cost_update_function)(THD *, OPTIMIZER_COSTS *, ptrdiff_t,
double, double);
static bool update_cost(THD *thd, OPTIMIZER_COSTS *key_cache,
ptrdiff_t offset, double new_value, double cost_adjust)
{
cost_var(key_cache, offset)= new_value / cost_adjust;
return 0;
}
class Sys_var_engine_optimizer_cost: public Sys_var_optimizer_cost
{
cost_update_function cost_update;
public:
Sys_var_engine_optimizer_cost(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
double min_val, double max_val, double def_val,
long cost_adjust, PolyLock *lock= 0,
cost_update_function on_update_func= update_cost,
const char *substitute=0)
: Sys_var_optimizer_cost(name_arg, comment, flag_args, off, size,
getopt, min_val, max_val, def_val, cost_adjust,
lock, VARIABLE_NOT_IN_BINLOG, 0,
0, substitute),
cost_update(on_update_func)
{
option.var_type|= GET_ASK_ADDR;
option.value= (uchar**)1; // crash me, please
// fix an offset from global_system_variables to be an offset in KEY_CACHE
offset= global_var_ptr() - (uchar*) &default_optimizer_costs;
SYSVAR_ASSERT(scope() == GLOBAL);
}
bool global_update(THD *thd, set_var *var)
{
double new_value= var->save_result.double_value;
LEX_CSTRING *base_name= &var->base;
OPTIMIZER_COSTS *optimizer_costs;
bool res;
/* If no basename, assume it's for the default costs */
if (!base_name->length)
base_name= &default_base;
mysql_mutex_lock(&LOCK_optimizer_costs);
if (!(optimizer_costs= get_or_create_optimizer_costs(base_name->str,
base_name->length)))
{
mysql_mutex_unlock(&LOCK_optimizer_costs);
return true;
}
res= cost_update(thd, optimizer_costs, offset, new_value, cost_adjust);
mysql_mutex_unlock(&LOCK_optimizer_costs);
return res;
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
OPTIMIZER_COSTS *optimizer_costs= get_optimizer_costs(base);
if (!optimizer_costs)
optimizer_costs= &default_optimizer_costs;
return cost_var_ptr(optimizer_costs, offset);
}
};
/**
The class for the @max_user_connections.
It's derived from Sys_var_uint, but non-standard session value
requires a new class.
Class specific constructor arguments:
everything derived from Sys_var_uint
Backing store: uint
*/
class Sys_var_max_user_conn: public Sys_var_int
{
public:
Sys_var_max_user_conn(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
int min_val, int max_val, int def_val,
uint block_size, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: Sys_var_int(name_arg, comment, SESSION, off, size, getopt,
min_val, max_val, def_val, block_size,
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{ }
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
if (thd->user_connect && thd->user_connect->user_resources.user_conn)
return (uchar*) &(thd->user_connect->user_resources.user_conn);
return global_value_ptr(thd, base);
}
};
/**
The class for flagset variables - a variant of SET that allows in-place
editing (turning on/off individual bits). String representations looks like
a "flag=val,flag=val,...". Example: @@optimizer_switch
Class specific constructor arguments:
char* values[] - 0-terminated list of strings of valid values
Backing store: ulonglong
@note
the last value in the values[] array should
*always* be the string "default".
*/
class Sys_var_flagset: public Sys_var_typelib
{
public:
Sys_var_flagset(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *values[], ulonglong def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: Sys_var_typelib(name_arg, comment, flag_args, off, getopt,
SHOW_CHAR, values, def_val, lock,
binlog_status_arg, on_check_func, on_update_func,
substitute)
{
option.var_type|= GET_FLAGSET;
global_var(ulonglong)= def_val;
SYSVAR_ASSERT(typelib.count > 1);
SYSVAR_ASSERT(typelib.count <= 65);
SYSVAR_ASSERT(def_val <= my_set_bits(typelib.count-1));
SYSVAR_ASSERT(strcmp(values[typelib.count-1], "default") == 0);
SYSVAR_ASSERT(size == sizeof(ulonglong));
}
bool do_check(THD *thd, set_var *var)
{
char buff[STRING_BUFFER_USUAL_SIZE];
String str(buff, sizeof(buff), system_charset_info), *res;
ulonglong default_value, current_value;
if (var->type == OPT_GLOBAL)
{
default_value= option.def_value;
current_value= global_var(ulonglong);
}
else
{
default_value= global_var(ulonglong);
current_value= session_var(thd, ulonglong);
}
if (var->value->result_type() == STRING_RESULT)
{
if (!(res=var->value->val_str(&str)))
return true;
else
{
char *error;
uint error_len;
var->save_result.ulonglong_value=
find_set_from_flags(&typelib,
typelib.count,
current_value,
default_value,
res->ptr(), res->length(),
&error, &error_len);
if (unlikely(error))
{
ErrConvString err(error, error_len, res->charset());
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, err.ptr());
return true;
}
}
}
else
{
longlong tmp=var->value->val_int();
if ((tmp < 0 && ! var->value->unsigned_flag)
|| (ulonglong)tmp > my_set_bits(typelib.count))
return true;
else
var->save_result.ulonglong_value= tmp;
}
return false;
}
bool session_update(THD *thd, set_var *var)
{
session_var(thd, ulonglong)= var->save_result.ulonglong_value;
return false;
}
bool global_update(THD *thd, set_var *var)
{
global_var(ulonglong)= var->save_result.ulonglong_value;
return false;
}
void session_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= global_var(ulonglong); }
void global_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= option.def_value; }
const uchar *valptr(THD *thd, ulonglong val) const
{ return (uchar*)flagset_to_string(thd, 0, val, typelib.type_names); }
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, session_var(thd, ulonglong)); }
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, global_var(ulonglong)); }
const uchar *default_value_ptr(THD *thd) const
{ return valptr(thd, option.def_value); }
};
/**
The class for SET variables - variables taking zero or more values
from the given list. Example: @@sql_mode
Class specific constructor arguments:
char* values[] - 0-terminated list of strings of valid values
Backing store: ulonglong
*/
class Sys_var_set: public Sys_var_typelib
{
public:
Sys_var_set(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *values[], ulonglong def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: Sys_var_typelib(name_arg, comment, flag_args, off, getopt,
SHOW_CHAR, values, def_val, lock,
binlog_status_arg, on_check_func, on_update_func,
substitute)
{
option.var_type|= GET_SET;
option.min_value= 0;
option.max_value= ~0ULL;
global_var(ulonglong)= def_val;
if ((option.u_max_value= (uchar**)max_var_ptr()))
{
*((ulonglong*) option.u_max_value)= ~0ULL;
}
SYSVAR_ASSERT(typelib.count > 0);
SYSVAR_ASSERT(typelib.count <= 64);
SYSVAR_ASSERT(def_val <= my_set_bits(typelib.count));
SYSVAR_ASSERT(size == sizeof(ulonglong));
}
bool check_maximum(THD *thd, set_var *var,
const char *c_val, longlong i_val)
{
if (!max_var_ptr() ||
(var->save_result.ulonglong_value & ~(get_max_var())) == 0)
return FALSE;
var->save_result.ulonglong_value&= get_max_var();
return c_val ? throw_bounds_warning(thd, name.str, c_val) :
throw_bounds_warning(thd, name.str, TRUE,
var->value->unsigned_flag, i_val);
}
bool do_check(THD *thd, set_var *var)
{
char buff[STRING_BUFFER_USUAL_SIZE];
String str(buff, sizeof(buff), system_charset_info), *res;
if (var->value->result_type() == STRING_RESULT)
{
char *error;
uint error_len;
bool not_used;
if (!(res= var->value->val_str_ascii_revert_empty_string_is_null(thd,
&str)))
return true;
var->save_result.ulonglong_value=
find_set(&typelib, res->ptr(), res->length(), NULL,
&error, &error_len, &not_used);
/*
note, we only issue an error if error_len > 0.
That is even while empty (zero-length) values are considered
errors by find_set(), these errors are ignored here
*/
if (error_len)
{
ErrConvString err(error, error_len, res->charset());
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, err.ptr());
return true;
}
return check_maximum(thd, var, res->ptr(), 0);
}
longlong tmp=var->value->val_int();
if ((tmp < 0 && ! var->value->unsigned_flag)
|| (ulonglong)tmp > my_set_bits(typelib.count))
return true;
var->save_result.ulonglong_value= tmp;
return check_maximum(thd, var, 0, tmp);
}
bool session_update(THD *thd, set_var *var)
{
session_var(thd, ulonglong)= var->save_result.ulonglong_value;
return false;
}
bool global_update(THD *thd, set_var *var)
{
global_var(ulonglong)= var->save_result.ulonglong_value;
return false;
}
void session_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= global_var(ulonglong); }
void global_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= option.def_value; }
const uchar *valptr(THD *thd, ulonglong val) const
{ return reinterpret_cast<const uchar*>(set_to_string(thd, 0, val, typelib.type_names)); }
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, session_var(thd, ulonglong)); }
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, global_var(ulonglong)); }
const uchar *default_value_ptr(THD *thd) const
{ return valptr(thd, option.def_value); }
ulonglong get_max_var() { return *((ulonglong*) max_var_ptr()); }
};
/**
The class for variables which value is a plugin.
Example: @@default_storage_engine
Class specific constructor arguments:
int plugin_type_arg (for example MYSQL_STORAGE_ENGINE_PLUGIN)
Backing store: plugin_ref
@note
these variables don't support command-line equivalents, any such
command-line options should be added manually to my_long_options in mysqld.cc
*/
class Sys_var_plugin: public sys_var
{
int plugin_type;
public:
Sys_var_plugin(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
int plugin_type_arg, char **def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, (intptr)def_val,
lock, binlog_status_arg, on_check_func, on_update_func,
substitute),
plugin_type(plugin_type_arg)
{
option.var_type|= GET_STR;
SYSVAR_ASSERT(size == sizeof(plugin_ref));
SYSVAR_ASSERT(getopt.id < 0); // force NO_CMD_LINE
}
bool do_check(THD *thd, set_var *var)
{
char buff[STRING_BUFFER_USUAL_SIZE];
String str(buff,sizeof(buff), system_charset_info), *res;
if (!(res=var->value->val_str(&str)))
var->save_result.plugin= NULL;
else
{
const LEX_CSTRING pname= { const_cast<char*>(res->ptr()), res->length() };
plugin_ref plugin;
// special code for storage engines (e.g. to handle historical aliases)
if (plugin_type == MYSQL_STORAGE_ENGINE_PLUGIN)
plugin= ha_resolve_by_name(thd, &pname, false);
else
plugin= my_plugin_lock_by_name(thd, &pname, plugin_type);
if (unlikely(!plugin))
{
// historically different error code
if (plugin_type == MYSQL_STORAGE_ENGINE_PLUGIN)
{
ErrConvString err(res);
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), err.ptr());
}
return true;
}
var->save_result.plugin= plugin;
}
return false;
}
void do_update(plugin_ref *valptr, plugin_ref newval)
{
plugin_ref oldval= *valptr;
if (oldval != newval)
{
*valptr= newval ? my_plugin_lock(NULL, newval) : 0;
plugin_unlock(NULL, oldval);
}
}
bool session_update(THD *thd, set_var *var)
{
do_update((plugin_ref*)session_var_ptr(thd),
var->save_result.plugin);
return false;
}
bool global_update(THD *thd, set_var *var)
{
do_update((plugin_ref*)global_var_ptr(),
var->save_result.plugin);
return false;
}
void session_save_default(THD *thd, set_var *var)
{
plugin_ref plugin= global_var(plugin_ref);
var->save_result.plugin= plugin ? my_plugin_lock(thd, plugin) : 0;
}
plugin_ref get_default(THD *thd) const
{
char *default_value= *reinterpret_cast<char**>(option.def_value);
if (!default_value)
return 0;
LEX_CSTRING pname= { default_value, strlen(default_value) };
plugin_ref plugin;
if (plugin_type == MYSQL_STORAGE_ENGINE_PLUGIN)
plugin= ha_resolve_by_name(thd, &pname, false);
else
plugin= my_plugin_lock_by_name(thd, &pname, plugin_type);
DBUG_ASSERT(plugin);
return my_plugin_lock(thd, plugin);
}
void global_save_default(THD *thd, set_var *var)
{
var->save_result.plugin= get_default(thd);
}
uchar *valptr(THD *thd, plugin_ref plugin) const
{
return (uchar*)(plugin ? thd->strmake(plugin_name(plugin)->str,
plugin_name(plugin)->length) : 0);
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, session_var(thd, plugin_ref)); }
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, global_var(plugin_ref)); }
const uchar *default_value_ptr(THD *thd) const
{ return valptr(thd, get_default(thd)); }
};
/**
Class for variables that containg a list of plugins.
Currently this is used only for @@gtid_pos_auto_create_engines
Backing store: plugin_ref
@note
Currently this is only used for storage engine type plugins, and thus only
storage engine type plugin is implemented. It could be extended to other
plugin types later if needed, similar to Sys_var_plugin.
These variables don't support command-line equivalents, any such
command-line options should be added manually to my_long_options in mysqld.cc
Note on lifetimes of resources allocated: We allocate a zero-terminated array
of plugin_ref*, and lock the contained plugins. The list in the global
variable must be freed (with free_engine_list()). However, the way Sys_var
works, there is no place to explicitly free other lists, like the one
returned from get_default().
Therefore, the code needs to work with temporary lists, which are
registered in the THD to be automatically freed (and plugins similarly
automatically unlocked). This is why do_check() allocates a temporary
list, from which do_update() then makes a permanent copy.
*/
class Sys_var_pluginlist: public sys_var
{
public:
Sys_var_pluginlist(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
char **def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, (intptr)def_val,
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{
option.var_type|= GET_STR;
SYSVAR_ASSERT(size == sizeof(plugin_ref));
SYSVAR_ASSERT(getopt.id < 0); // force NO_CMD_LINE
}
bool do_check(THD *thd, set_var *var)
{
char buff[STRING_BUFFER_USUAL_SIZE];
String str(buff,sizeof(buff), system_charset_info), *res;
plugin_ref *plugins;
if (!(res=var->value->val_str(&str)))
plugins= resolve_engine_list(thd, "", 0, true, true);
else
plugins= resolve_engine_list(thd, res->ptr(), res->length(), true, true);
if (!plugins)
return true;
var->save_result.plugins= plugins;
return false;
}
void do_update(plugin_ref **valptr, plugin_ref* newval)
{
plugin_ref *oldval= *valptr;
*valptr= copy_engine_list(newval);
free_engine_list(oldval);
}
bool session_update(THD *thd, set_var *var)
{
do_update((plugin_ref**)session_var_ptr(thd),
var->save_result.plugins);
return false;
}
bool global_update(THD *thd, set_var *var)
{
do_update((plugin_ref**)global_var_ptr(),
var->save_result.plugins);
return false;
}
void session_save_default(THD *thd, set_var *var)
{
plugin_ref* plugins= global_var(plugin_ref *);
var->save_result.plugins= plugins ? temp_copy_engine_list(thd, plugins) : 0;
}
plugin_ref *get_default(THD *thd) const
{
char *default_value= *reinterpret_cast<char**>(option.def_value);
if (!default_value)
return 0;
return resolve_engine_list(thd, default_value, strlen(default_value),
false, true);
}
void global_save_default(THD *thd, set_var *var)
{
var->save_result.plugins= get_default(thd);
}
uchar *valptr(THD *thd, plugin_ref *plugins) const
{
return reinterpret_cast<uchar*>(pretty_print_engine_list(thd, plugins));
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, session_var(thd, plugin_ref*)); }
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, global_var(plugin_ref*)); }
const uchar *default_value_ptr(THD *thd) const
{ return valptr(thd, get_default(thd)); }
};
#if defined(ENABLED_DEBUG_SYNC)
#include "debug_sync.h"
/**
The class for @@debug_sync session-only variable
*/
class Sys_var_debug_sync :public sys_var
{
public:
Sys_var_debug_sync(const char *name_arg,
const char *comment, int flag_args,
CMD_LINE getopt,
const char *def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, 0, getopt.id,
getopt.arg_type, SHOW_CHAR, (intptr)def_val,
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{
SYSVAR_ASSERT(scope() == ONLY_SESSION);
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var)
{
char buff[STRING_BUFFER_USUAL_SIZE];
String str(buff, sizeof(buff), system_charset_info), *res;
if (!(res=var->value->val_str(&str)))
var->save_result.string_value= empty_lex_str;
else
{
if (!thd->make_lex_string(&var->save_result.string_value,
res->ptr(), res->length()))
return true;
}
return false;
}
bool session_update(THD *thd, set_var *var)
{
return debug_sync_update(thd, var->save_result.string_value.str,
var->save_result.string_value.length);
}
bool global_update(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return true;
}
void session_save_default(THD *thd, set_var *var)
{
var->save_result.string_value.str= const_cast<char*>("");
var->save_result.string_value.length= 0;
}
void global_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
return debug_sync_value_ptr(thd);
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(FALSE);
return 0;
}
const uchar *default_value_ptr(THD *thd) const
{ return (uchar*)""; }
};
#endif /* defined(ENABLED_DEBUG_SYNC) */
/**
The class for bit variables - a variant of boolean that stores the value
in a bit.
Class specific constructor arguments:
ulonglong bitmask_arg - the mask for the bit to set in the ulonglong
backing store
Backing store: ulonglong
@note
This class supports the "reverse" semantics, when the value of the bit
being 0 corresponds to the value of variable being set. To activate it
use REVERSE(bitmask) instead of simply bitmask in the constructor.
@note
variables of this class cannot be set from the command line as
my_getopt does not support bits.
*/
class Sys_var_bit: public Sys_var_typelib
{
ulonglong bitmask;
bool reverse_semantics;
void set(uchar *ptr, ulonglong value)
{
if ((value != 0) ^ reverse_semantics)
(*(ulonglong *)ptr)|= bitmask;
else
(*(ulonglong *)ptr)&= ~bitmask;
}
public:
Sys_var_bit(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
ulonglong bitmask_arg, my_bool def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: Sys_var_typelib(name_arg, comment, flag_args, off, getopt,
SHOW_MY_BOOL, bool_values, def_val, lock,
binlog_status_arg, on_check_func, on_update_func,
substitute)
{
option.var_type|= GET_BIT;
reverse_semantics= my_count_bits(bitmask_arg) > 1;
bitmask= reverse_semantics ? ~bitmask_arg : bitmask_arg;
option.block_size= reverse_semantics ? -(long) bitmask : (long)bitmask;
set(global_var_ptr(), def_val);
SYSVAR_ASSERT(def_val < 2);
SYSVAR_ASSERT(size == sizeof(ulonglong));
}
bool session_update(THD *thd, set_var *var)
{
set(session_var_ptr(thd), var->save_result.ulonglong_value);
return false;
}
bool global_update(THD *thd, set_var *var)
{
set(global_var_ptr(), var->save_result.ulonglong_value);
return false;
}
void session_save_default(THD *thd, set_var *var)
{
var->save_result.ulonglong_value=
(reverse_semantics == !(global_var(ulonglong) & bitmask));
}
void global_save_default(THD *thd, set_var *var)
{ var->save_result.ulonglong_value= option.def_value; }
uchar *valptr(THD *thd, ulonglong val) const
{
thd->sys_var_tmp.my_bool_value= (reverse_semantics == !(val & bitmask));
return (uchar*) &thd->sys_var_tmp.my_bool_value;
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, session_var(thd, ulonglong)); }
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, global_var(ulonglong)); }
const uchar *default_value_ptr(THD *thd) const
{
thd->sys_var_tmp.my_bool_value= option.def_value != 0;
return (uchar*) &thd->sys_var_tmp.my_bool_value;
}
};
/**
The class for variables that have a special meaning for a session,
such as @@timestamp or @@rnd_seed1, their values typically cannot be read
from SV structure, and a special "read" callback is provided.
Class specific constructor arguments:
everything derived from Sys_var_ulonglong
session_special_read_function read_func_arg
Backing store: ulonglong
@note
These variables are session-only, global or command-line equivalents
are not supported as they're generally meaningless.
*/
class Sys_var_session_special: public Sys_var_ulonglong
{
typedef bool (*session_special_update_function)(THD *thd, set_var *var);
typedef ulonglong (*session_special_read_function)(THD *thd);
session_special_read_function read_func;
session_special_update_function update_func;
public:
Sys_var_session_special(const char *name_arg,
const char *comment, int flag_args,
CMD_LINE getopt,
ulonglong min_val, ulonglong max_val, uint block_size,
PolyLock *lock, enum binlog_status_enum binlog_status_arg,
on_check_function on_check_func,
session_special_update_function update_func_arg,
session_special_read_function read_func_arg,
const char *substitute=0)
: Sys_var_ulonglong(name_arg, comment, flag_args, 0,
sizeof(ulonglong), getopt, min_val,
max_val, 0, block_size, lock, binlog_status_arg, on_check_func, 0,
substitute),
read_func(read_func_arg), update_func(update_func_arg)
{
SYSVAR_ASSERT(scope() == ONLY_SESSION);
SYSVAR_ASSERT(getopt.id < 0); // NO_CMD_LINE, because the offset is fake
}
bool session_update(THD *thd, set_var *var)
{ return update_func(thd, var); }
bool global_update(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return true;
}
void session_save_default(THD *thd, set_var *var)
{ var->value= 0; }
void global_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
thd->sys_var_tmp.ulonglong_value= read_func(thd);
return (uchar*) &thd->sys_var_tmp.ulonglong_value;
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(FALSE);
return 0;
}
const uchar *default_value_ptr(THD *thd) const
{
thd->sys_var_tmp.ulonglong_value= 0;
return (uchar*) &thd->sys_var_tmp.ulonglong_value;
}
};
/*
Dedicated class because of a weird behavior of a default value.
Assigning timestamp to itself
SET @@timestamp = @@timestamp
make it non-default and stops the time flow.
*/
class Sys_var_timestamp: public Sys_var_double
{
public:
Sys_var_timestamp(const char *name_arg,
const char *comment, int flag_args,
CMD_LINE getopt,
double min_val, double max_val,
PolyLock *lock, enum binlog_status_enum binlog_status_arg,
on_check_function on_check_func=0)
: Sys_var_double(name_arg, comment, flag_args, 0,
sizeof(double), getopt, min_val,
max_val, 0, lock, binlog_status_arg, on_check_func)
{
SYSVAR_ASSERT(scope() == ONLY_SESSION);
SYSVAR_ASSERT(getopt.id < 0); // NO_CMD_LINE, because the offset is fake
}
bool session_update(THD *thd, set_var *var)
{
if (var->value)
{
my_hrtime_t hrtime = { hrtime_from_time(var->save_result.double_value) };
thd->set_time(hrtime);
}
else // SET timestamp=DEFAULT
thd->user_time.val= 0;
return false;
}
bool global_update(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return true;
}
bool session_is_default(THD *thd)
{
return thd->user_time.val == 0;
}
void session_save_default(THD *thd, set_var *var)
{ var->value= 0; }
void global_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
thd->sys_var_tmp.double_value= thd->start_time +
thd->start_time_sec_part/(double)TIME_SECOND_PART_FACTOR;
return (uchar*) &thd->sys_var_tmp.double_value;
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(FALSE);
return 0;
}
const uchar *default_value_ptr(THD *thd) const
{
thd->sys_var_tmp.double_value= 0;
return (uchar*) &thd->sys_var_tmp.double_value;
}
bool on_check_access_session(THD *thd) const;
};
/**
The class for read-only variables that show whether a particular
feature is supported by the server. Example: have_compression
Backing store: enum SHOW_COMP_OPTION
@note
These variables are necessarily read-only, only global, and have no
command-line equivalent.
*/
class Sys_var_have: public sys_var
{
public:
Sys_var_have(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, 0,
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{
SYSVAR_ASSERT(scope() == GLOBAL);
SYSVAR_ASSERT(getopt.id < 0);
SYSVAR_ASSERT(lock == 0);
SYSVAR_ASSERT(binlog_status_arg == VARIABLE_NOT_IN_BINLOG);
SYSVAR_ASSERT(is_readonly());
SYSVAR_ASSERT(on_update == 0);
SYSVAR_ASSERT(size == sizeof(enum SHOW_COMP_OPTION));
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var) {
DBUG_ASSERT(FALSE);
return true;
}
bool session_update(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return true;
}
bool global_update(THD *thd, set_var *var)
{
DBUG_ASSERT(FALSE);
return true;
}
void session_save_default(THD *thd, set_var *var) { }
void global_save_default(THD *thd, set_var *var) { }
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(FALSE);
return 0;
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
return (uchar*)show_comp_option_name[global_var(enum SHOW_COMP_OPTION)];
}
};
/**
Generic class for variables for storing entities that are internally
represented as structures, have names, and possibly can be referred to by
numbers. Examples: character sets, collations, locales,
Class specific constructor arguments:
ptrdiff_t name_offset - offset of the 'name' field in the structure
Backing store: void*
@note
As every such a structure requires special treatment from my_getopt,
these variables don't support command-line equivalents, any such
command-line options should be added manually to my_long_options in mysqld.cc
*/
class Sys_var_struct: public sys_var
{
ptrdiff_t name_offset; // offset to the 'name' property in the structure
public:
Sys_var_struct(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
ptrdiff_t name_off, void *def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, (intptr)def_val,
lock, binlog_status_arg, on_check_func, on_update_func,
substitute),
name_offset(name_off)
{
option.var_type|= GET_ENUM; // because we accept INT and STRING here
/*
struct variables are special on the command line - often (e.g. for
charsets) the name cannot be immediately resolved, but only after all
options (in particular, basedir) are parsed.
thus all struct command-line options should be added manually
to my_long_options in mysqld.cc
*/
SYSVAR_ASSERT(getopt.id < 0);
SYSVAR_ASSERT(size == sizeof(void *));
}
bool do_check(THD *thd, set_var *var)
{ return false; }
bool session_update(THD *thd, set_var *var)
{
session_var(thd, const void*)= var->save_result.ptr;
return false;
}
bool global_update(THD *thd, set_var *var)
{
global_var(const void*)= var->save_result.ptr;
return false;
}
void session_save_default(THD *thd, set_var *var)
{ var->save_result.ptr= global_var(void*); }
void global_save_default(THD *thd, set_var *var)
{
void **default_value= reinterpret_cast<void**>(option.def_value);
var->save_result.ptr= *default_value;
}
uchar *valptr(THD *thd, uchar *val) const
{ return val ? *(uchar**)(val+name_offset) : 0; }
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, session_var(thd, uchar*)); }
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, global_var(uchar*)); }
const uchar *default_value_ptr(THD *thd) const
{ return valptr(thd, *(uchar**)option.def_value); }
};
/**
The class for variables that store time zones
Backing store: Time_zone*
@note
Time zones cannot be supported directly by my_getopt, thus
these variables don't support command-line equivalents, any such
command-line options should be added manually to my_long_options in mysqld.cc
*/
class Sys_var_tz: public sys_var
{
public:
Sys_var_tz(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
Time_zone **def_val, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, (intptr)def_val,
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{
SYSVAR_ASSERT(getopt.id < 0);
SYSVAR_ASSERT(size == sizeof(Time_zone *));
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var)
{
char buff[MAX_TIME_ZONE_NAME_LENGTH];
String str(buff, sizeof(buff), &my_charset_latin1);
String *res= var->value->val_str(&str);
if (!res)
return true;
if (!(var->save_result.time_zone= my_tz_find(thd, res)))
{
ErrConvString err(res);
my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), err.ptr());
return true;
}
return false;
}
bool session_update(THD *thd, set_var *var)
{
session_var(thd, Time_zone*)= var->save_result.time_zone;
return false;
}
bool global_update(THD *thd, set_var *var)
{
global_var(Time_zone*)= var->save_result.time_zone;
return false;
}
void session_save_default(THD *thd, set_var *var)
{
var->save_result.time_zone= global_var(Time_zone*);
}
void global_save_default(THD *thd, set_var *var)
{
var->save_result.time_zone=
*(Time_zone**)(intptr)option.def_value;
}
const uchar *valptr(THD *thd, Time_zone *val) const
{ return reinterpret_cast<const uchar*>(val->get_name()->ptr()); }
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
/*
This is an ugly fix for replication: we don't replicate properly queries
invoking system variables' values to update tables; but
CONVERT_TZ(,,@@session.time_zone) is so popular that we make it
replicable (i.e. we tell the binlog code to store the session
timezone). If it's the global value which was used we can't replicate
(binlog code stores session value only).
*/
thd->used|= THD::TIME_ZONE_USED;
return valptr(thd, session_var(thd, Time_zone *));
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return valptr(thd, global_var(Time_zone*)); }
const uchar *default_value_ptr(THD *thd) const
{ return valptr(thd, *(Time_zone**)option.def_value); }
};
/**
Special implementation for transaction isolation, that
distingushes between
SET GLOBAL TRANSACTION ISOLATION (stored in global_system_variables)
SET SESSION TRANSACTION ISOLATION (stored in thd->variables)
SET TRANSACTION ISOLATION (stored in thd->tx_isolation)
where the last statement sets isolation level for the next transaction only
*/
class Sys_var_tx_isolation: public Sys_var_enum
{
public:
Sys_var_tx_isolation(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *values[], uint def_val, PolyLock *lock,
enum binlog_status_enum binlog_status_arg,
on_check_function on_check_func)
:Sys_var_enum(name_arg, comment, flag_args, off, size, getopt,
values, def_val, lock, binlog_status_arg, on_check_func)
{}
bool session_update(THD *thd, set_var *var)
{
if (var->type == OPT_SESSION && Sys_var_enum::session_update(thd, var))
return TRUE;
if (var->type == OPT_DEFAULT || !thd->in_active_multi_stmt_transaction())
{
thd->tx_isolation= (enum_tx_isolation) var->save_result.ulonglong_value;
#ifndef EMBEDDED_LIBRARY
if (var->type == OPT_DEFAULT)
{
enum enum_tx_isol_level l;
switch (thd->tx_isolation) {
case ISO_READ_UNCOMMITTED:
l= TX_ISOL_UNCOMMITTED;
break;
case ISO_READ_COMMITTED:
l= TX_ISOL_COMMITTED;
break;
case ISO_REPEATABLE_READ:
l= TX_ISOL_REPEATABLE;
break;
case ISO_SERIALIZABLE:
l= TX_ISOL_SERIALIZABLE;
break;
default:
DBUG_ASSERT(0);
return TRUE;
}
if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
thd->session_tracker.transaction_info.set_isol_level(thd, l);
}
else if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
thd->session_tracker.transaction_info.set_isol_level(thd, TX_ISOL_INHERIT);
#endif //EMBEDDED_LIBRARY
}
return FALSE;
}
};
/**
Class representing the tx_read_only system variable for setting
default transaction access mode.
Note that there is a special syntax - SET TRANSACTION READ ONLY
(or READ WRITE) that sets the access mode for the next transaction
only.
*/
class Sys_var_tx_read_only: public Sys_var_mybool
{
public:
Sys_var_tx_read_only(const char *name_arg, const char *comment, int flag_args,
ptrdiff_t off, size_t size, CMD_LINE getopt,
my_bool def_val, PolyLock *lock,
enum binlog_status_enum binlog_status_arg,
on_check_function on_check_func)
:Sys_var_mybool(name_arg, comment, flag_args, off, size, getopt,
def_val, lock, binlog_status_arg, on_check_func)
{}
virtual bool session_update(THD *thd, set_var *var);
};
/*
Class for replicate_events_marked_for_skip.
We need a custom update function that ensures the slave is stopped when
the update is happening.
*/
class Sys_var_replicate_events_marked_for_skip: public Sys_var_enum
{
public:
Sys_var_replicate_events_marked_for_skip(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *values[], uint def_val, PolyLock *lock= 0,
enum binlog_status_enum binlog_status_arg= VARIABLE_NOT_IN_BINLOG)
:Sys_var_enum(name_arg, comment, flag_args, off, size, getopt,
values, def_val, lock, binlog_status_arg)
{}
bool global_update(THD *thd, set_var *var);
};
/*
Class for handing multi-source replication variables
Variable values are store in Master_info, but to make it possible to
access variable without locks we also store it thd->variables.
These can be used as GLOBAL or SESSION, but both points to the same
variable. This is to make things compatible with MySQL 5.5 where variables
like sql_slave_skip_counter are GLOBAL.
*/
#define MASTER_INFO_VAR(X) my_offsetof(Master_info, X), sizeof(((Master_info *)0x10)->X)
class Sys_var_multi_source_ulonglong;
class Master_info;
typedef bool (*on_multi_source_update_function)(sys_var *self, THD *thd,
Master_info *mi);
bool update_multi_source_variable(sys_var *self,
THD *thd, enum_var_type type);
class Sys_var_multi_source_ulonglong :public Sys_var_ulonglong
{
ptrdiff_t master_info_offset;
on_multi_source_update_function update_multi_source_variable_func;
public:
Sys_var_multi_source_ulonglong(const char *name_arg,
const char *comment, int flag_args,
ptrdiff_t off, size_t size,
CMD_LINE getopt,
ptrdiff_t master_info_offset_arg,
size_t master_info_arg_size,
ulonglong min_val, ulonglong max_val,
ulonglong def_val, uint block_size,
on_multi_source_update_function on_update_func)
:Sys_var_ulonglong(name_arg, comment, flag_args, off, size,
getopt, min_val, max_val, def_val, block_size,
0, VARIABLE_NOT_IN_BINLOG, 0, update_multi_source_variable),
master_info_offset(master_info_offset_arg),
update_multi_source_variable_func(on_update_func)
{
SYSVAR_ASSERT(master_info_arg_size == size);
}
bool global_update(THD *thd, set_var *var)
{
return session_update(thd, var);
}
void session_save_default(THD *thd, set_var *var)
{
/* Use value given in variable declaration */
global_save_default(thd, var);
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
ulonglong *tmp, res;
tmp= (ulonglong*) (((uchar*)&(thd->variables)) + offset);
res= get_master_info_ulonglong_value(thd, master_info_offset);
*tmp= res;
return (uchar*) tmp;
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
return session_value_ptr(thd, base);
}
ulonglong get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset) const;
bool update_variable(THD *thd, Master_info *mi)
{
return update_multi_source_variable_func(this, thd, mi);
}
};
/**
Class for @@global.gtid_current_pos.
*/
class Sys_var_gtid_current_pos: public sys_var
{
public:
Sys_var_gtid_current_pos(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL)
{
SYSVAR_ASSERT(getopt.id < 0);
SYSVAR_ASSERT(is_readonly());
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
bool session_update(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
bool global_update(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
void session_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
}
void global_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(false);
return NULL;
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const;
};
/**
Class for @@global.gtid_binlog_pos.
*/
class Sys_var_gtid_binlog_pos: public sys_var
{
public:
Sys_var_gtid_binlog_pos(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL)
{
SYSVAR_ASSERT(getopt.id < 0);
SYSVAR_ASSERT(is_readonly());
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
bool session_update(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
bool global_update(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
void session_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
}
void global_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(false);
return NULL;
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const;
};
/**
Class for @@global.gtid_slave_pos.
*/
class Sys_var_gtid_slave_pos: public sys_var
{
public:
Sys_var_gtid_slave_pos(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL)
{
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var);
bool session_update(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
bool global_update(THD *thd, set_var *var);
void session_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
}
void global_save_default(THD *thd, set_var *var)
{
/* Record the attempt to use default so we can error. */
var->value= 0;
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(false);
return NULL;
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const;
const uchar *default_value_ptr(THD *thd) const
{ return 0; }
bool on_check_access_global(THD *thd) const
{
return check_global_access(thd, PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_SLAVE_POS);
}
};
/**
Class for @@global.gtid_binlog_state.
*/
class Sys_var_gtid_binlog_state: public sys_var
{
public:
Sys_var_gtid_binlog_state(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL)
{
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var);
bool session_update(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
bool global_update(THD *thd, set_var *var);
void session_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
}
void global_save_default(THD *thd, set_var *var)
{
/* Record the attempt to use default so we can error. */
var->value= 0;
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(false);
return NULL;
}
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const;
const uchar *default_value_ptr(THD *thd) const
{ return 0; }
bool on_check_access_global(THD *thd) const
{
return
check_global_access(thd, PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_BINLOG_STATE);
}
};
/**
Class for @@session.last_gtid.
*/
class Sys_var_last_gtid: public sys_var
{
public:
Sys_var_last_gtid(const char *name_arg,
const char *comment, int flag_args, CMD_LINE getopt)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, 0, getopt.id,
getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL)
{
SYSVAR_ASSERT(getopt.id < 0);
SYSVAR_ASSERT(is_readonly());
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
bool session_update(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
bool global_update(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
void session_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
}
void global_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
}
const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const;
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{
DBUG_ASSERT(false);
return NULL;
}
};
/**
Class for connection_name.slave_parallel_mode.
*/
class Sys_var_slave_parallel_mode: public Sys_var_enum
{
public:
Sys_var_slave_parallel_mode(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt, const char *values[],
enum_slave_parallel_mode def_val)
: Sys_var_enum(name_arg, comment, flag_args, off, size,
getopt, values, def_val)
{
option.var_type|= GET_ASK_ADDR;
option.value= (uchar**)1; // crash me, please
SYSVAR_ASSERT(scope() == GLOBAL);
}
bool global_update(THD *thd, set_var *var);
const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const;
};
class Sys_var_vers_asof: public sys_var
{
public:
Sys_var_vers_asof(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt, uint def_val,
PolyLock *lock= NO_MUTEX_GUARD,
binlog_status_enum binlog_status_arg= VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func= NULL,
on_update_function on_update_func= NULL,
const char *substitute= NULL)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off,
getopt.id, getopt.arg_type, SHOW_CHAR, def_val, lock,
binlog_status_arg, on_check_func, on_update_func, substitute)
{
option.var_type= GET_STR;
}
virtual bool do_check(THD *thd, set_var *var)
{
if (!var->value)
return false;
MYSQL_TIME ltime;
Datetime::Options opt(TIME_CONV_NONE |
TIME_NO_ZERO_IN_DATE |
TIME_NO_ZERO_DATE, thd);
bool res= var->value->get_date(thd, &ltime, opt);
if (!res)
{
uint error;
var->save_result.timestamp.unix_time=
thd->variables.time_zone->TIME_to_gmt_sec(&ltime, &error);
var->save_result.timestamp.second_part= ltime.second_part;
res= error != 0;
}
return res;
}
private:
static bool update(THD *thd, set_var *var, vers_asof_timestamp_t *out)
{
if (var->value)
{
out->type = SYSTEM_TIME_AS_OF;
out->unix_time = var->save_result.timestamp.unix_time;
out->second_part= var->save_result.timestamp.second_part;
}
return 0;
}
static void save_default(set_var *var, vers_asof_timestamp_t *out)
{
out->type= SYSTEM_TIME_UNSPECIFIED;
}
public:
virtual bool global_update(THD *thd, set_var *var)
{
return update(thd, var, &global_var(vers_asof_timestamp_t));
}
virtual bool session_update(THD *thd, set_var *var)
{
return update(thd, var, &session_var(thd, vers_asof_timestamp_t));
}
virtual bool session_is_default(THD *thd)
{
const vers_asof_timestamp_t &var= session_var(thd, vers_asof_timestamp_t);
return var.type == SYSTEM_TIME_UNSPECIFIED;
}
virtual void session_save_default(THD *thd, set_var *var)
{
save_default(var, &session_var(thd, vers_asof_timestamp_t));
}
virtual void global_save_default(THD *thd, set_var *var)
{
save_default(var, &global_var(vers_asof_timestamp_t));
}
private:
const uchar *value_ptr(THD *thd, vers_asof_timestamp_t &val) const
{
const char *value;
switch (val.type)
{
case SYSTEM_TIME_UNSPECIFIED:
return (uchar*)"DEFAULT";
break;
case SYSTEM_TIME_AS_OF:
{
char *buf= (char*) thd->alloc(MAX_DATE_STRING_REP_LENGTH);
MYSQL_TIME ltime;
thd->variables.time_zone->gmt_sec_to_TIME(&ltime, val.unix_time);
ltime.second_part= val.second_part;
value= buf;
if (buf && !my_datetime_to_str(&ltime, buf, 6))
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, "NULL (wrong datetime)");
value= thd->strdup("Error: wrong datetime");
}
break;
}
default:
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, "NULL (wrong range type)");
value= thd->strdup("Error: wrong range type");
}
return reinterpret_cast<const uchar *>(value);
}
public:
virtual const uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return value_ptr(thd, session_var(thd, vers_asof_timestamp_t)); }
virtual const uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) const
{ return value_ptr(thd, global_var(vers_asof_timestamp_t)); }
};