From 1b9de642d95963c24885a28751411f1c5be88aad Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Wed, 2 Jan 2013 10:49:28 -0800 Subject: [PATCH] The fractional part of the def, min and max values of system variables is ignored. Allow the default, minimum and maximum values of system variables of type double to have a meaningful fractional part. Since the command-line option parsing interface (my_getopt) uses fields of type unsigned long long (ull) to store these values, the double values were being stored in a lossy way that discards the fractional part. This change introduces a couple of functions that can be used to store the raw representation of a double value in the raw bits of unsigned long long field in a way that the binary representation remains the same. Using these function the real value can be converted back and forth between the types. --- include/my_getopt.h | 3 +++ mysql-test/r/plugin.result | 28 +++++++++++++++++++++------ mysql-test/t/plugin.test | 9 +++++++++ mysys/my_getopt.c | 44 ++++++++++++++++++++++++++++++++++++++----- sql/sql_plugin.cc | 12 ++++++++++-- sql/sys_vars.h | 11 ++++++----- storage/example/ha_example.cc | 6 +++--- 7 files changed, 92 insertions(+), 21 deletions(-) diff --git a/include/my_getopt.h b/include/my_getopt.h index 04278c0..2b84267 100644 --- a/include/my_getopt.h +++ b/include/my_getopt.h @@ -120,6 +120,9 @@ double getopt_double_limit_value(double num, const struct my_option *optp, my_bool *fix); my_bool getopt_compare_strings(const char *s, const char *t, uint length); +ulonglong getopt_double2ulonglong(double); +double getopt_ulonglong2double(ulonglong); + C_MODE_END #endif /* _my_getopt_h */ diff --git a/mysql-test/r/plugin.result b/mysql-test/r/plugin.result index 4a06573..7f0e25b 100644 --- a/mysql-test/r/plugin.result +++ b/mysql-test/r/plugin.result @@ -16,10 +16,10 @@ set global example_ulong_var=500; set global example_enum_var= e1; show status like 'example%'; Variable_name Value -example_func_example enum_var is 0, ulong_var is 500, double_var is 8.000000, really +example_func_example enum_var is 0, ulong_var is 500, double_var is 8.500000, really show variables like 'example%'; Variable_name Value -example_double_var 8.000000 +example_double_var 8.500000 example_enum_var e1 example_ulong_var 500 UNINSTALL PLUGIN example; @@ -69,11 +69,23 @@ Warnings: Warning 1292 Truncated incorrect example_double_var value: '-0.1' SELECT @@GLOBAL.example_double_var; @@GLOBAL.example_double_var -0.000000 +0.500000 SET GLOBAL example_double_var = 0.000001; +Warnings: +Warning 1292 Truncated incorrect example_double_var value: '0.000001' +SELECT @@GLOBAL.example_double_var; +@@GLOBAL.example_double_var +0.500000 +SET GLOBAL example_double_var = 0.4; +Warnings: +Warning 1292 Truncated incorrect example_double_var value: '0.4' SELECT @@GLOBAL.example_double_var; @@GLOBAL.example_double_var -0.000001 +0.500000 +SET GLOBAL example_double_var = 0.5; +SELECT @@GLOBAL.example_double_var; +@@GLOBAL.example_double_var +0.500000 SET GLOBAL example_double_var = 123.456789; SELECT @@GLOBAL.example_double_var; @@GLOBAL.example_double_var @@ -87,9 +99,13 @@ SELECT @@GLOBAL.example_double_var; @@GLOBAL.example_double_var 999.999999 SET GLOBAL example_double_var = 1000.1; +SELECT @@GLOBAL.example_double_var; +@@GLOBAL.example_double_var +1000.100000 +SET GLOBAL example_double_var = 1000.51; Warnings: -Warning 1292 Truncated incorrect example_double_var value: '1000.1' +Warning 1292 Truncated incorrect example_double_var value: '1000.51' SELECT @@GLOBAL.example_double_var; @@GLOBAL.example_double_var -1000.000000 +1000.500000 UNINSTALL PLUGIN example; diff --git a/mysql-test/t/plugin.test b/mysql-test/t/plugin.test index d6987f1..53d246f 100644 --- a/mysql-test/t/plugin.test +++ b/mysql-test/t/plugin.test @@ -91,6 +91,12 @@ SELECT @@GLOBAL.example_double_var; SET GLOBAL example_double_var = 0.000001; SELECT @@GLOBAL.example_double_var; +SET GLOBAL example_double_var = 0.4; +SELECT @@GLOBAL.example_double_var; + +SET GLOBAL example_double_var = 0.5; +SELECT @@GLOBAL.example_double_var; + SET GLOBAL example_double_var = 123.456789; SELECT @@GLOBAL.example_double_var; @@ -103,4 +109,7 @@ SELECT @@GLOBAL.example_double_var; SET GLOBAL example_double_var = 1000.1; SELECT @@GLOBAL.example_double_var; +SET GLOBAL example_double_var = 1000.51; +SELECT @@GLOBAL.example_double_var; + UNINSTALL PLUGIN example; diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index 2ab9d44..a897151 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -90,6 +90,35 @@ void my_getopt_register_get_addr(my_getopt_value func_addr) getopt_get_addr= func_addr; } +union ull_dbl +{ + ulonglong ull; + double dbl; +}; + +/** + Returns an ulonglong value containing a raw + representation of the given double value. +*/ +ulonglong getopt_double2ulonglong(double v) +{ + union ull_dbl u; + u.dbl= v; + compile_time_assert(sizeof(ulonglong) >= sizeof(double)); + return u.ull; +} + +/** + Returns the double value which corresponds to + the given raw representation. +*/ +double getopt_ulonglong2double(ulonglong v) +{ + union ull_dbl u; + u.ull= v; + return u.dbl; +} + /** Handle command line options. Sort options. @@ -1044,14 +1073,19 @@ double getopt_double_limit_value(double num, const struct my_option *optp, { my_bool adjusted= FALSE; double old= num; - if (optp->max_value && num > (double) optp->max_value) + double max, min; + + max= getopt_ulonglong2double(optp->max_value); + min= getopt_ulonglong2double(optp->min_value); + + if (max && num > max) { - num= (double) optp->max_value; + num= max; adjusted= TRUE; } - if (num < (double) optp->min_value) + if (num < min) { - num= (double) optp->min_value; + num= min; adjusted= TRUE; } if (fix) @@ -1134,7 +1168,7 @@ static void init_one_value(const struct my_option *option, void *variable, *((ulonglong*) variable)= (ulonglong) value; break; case GET_DOUBLE: - *((double*) variable)= ulonglong2double(value); + *((double*) variable)= getopt_ulonglong2double(value); break; case GET_STR: /* diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 30ca773..26820f5 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -3135,6 +3135,14 @@ bool sys_var_pluginvar::global_update(THD *thd, set_var *var) options->block_size= (long) (opt)->blk_sz +#define OPTION_SET_LIMITS_DOUBLE(options, opt) \ + options->var_type= GET_DOUBLE; \ + options->def_value= (longlong) getopt_double2ulonglong((opt)->def_val); \ + options->min_value= (longlong) getopt_double2ulonglong((opt)->min_val); \ + options->max_value= getopt_double2ulonglong((opt)->max_val); \ + options->block_size= (long) (opt)->blk_sz + + static void plugin_opt_set_limits(struct my_option *options, const struct st_mysql_sys_var *opt) { @@ -3162,7 +3170,7 @@ static void plugin_opt_set_limits(struct my_option *options, OPTION_SET_LIMITS(GET_ULL, options, (sysvar_ulonglong_t*) opt); break; case PLUGIN_VAR_DOUBLE: - OPTION_SET_LIMITS(GET_DOUBLE, options, (sysvar_double_t*) opt); + OPTION_SET_LIMITS_DOUBLE(options, (sysvar_double_t*) opt); break; case PLUGIN_VAR_ENUM: options->var_type= GET_ENUM; @@ -3207,7 +3215,7 @@ static void plugin_opt_set_limits(struct my_option *options, OPTION_SET_LIMITS(GET_ULL, options, (thdvar_ulonglong_t*) opt); break; case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL: - OPTION_SET_LIMITS(GET_DOUBLE, options, (thdvar_double_t*) opt); + OPTION_SET_LIMITS_DOUBLE(options, (thdvar_double_t*) opt); break; case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL: options->var_type= GET_ENUM; diff --git a/sql/sys_vars.h b/sql/sys_vars.h index 6a43bc2..19401b1 100644 --- a/sql/sys_vars.h +++ b/sql/sys_vars.h @@ -771,14 +771,15 @@ public: const char *substitute=0, int parse_flag= PARSE_NORMAL) : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id, - getopt.arg_type, SHOW_DOUBLE, (longlong) double2ulonglong(def_val), + getopt.arg_type, SHOW_DOUBLE, + (longlong) getopt_double2ulonglong(def_val), lock, binlog_status_arg, on_check_func, on_update_func, substitute, parse_flag) { option.var_type= GET_DOUBLE; - option.min_value= (longlong) double2ulonglong(min_val); - option.max_value= (longlong) double2ulonglong(max_val); - global_var(double)= (double)option.def_value; + option.min_value= (longlong) getopt_double2ulonglong(min_val); + option.max_value= (longlong) getopt_double2ulonglong(max_val); + global_var(double)= def_val; DBUG_ASSERT(min_val < max_val); DBUG_ASSERT(min_val <= def_val); DBUG_ASSERT(max_val >= def_val); @@ -809,7 +810,7 @@ public: 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= (double)option.def_value; } + { var->save_result.double_value= getopt_ulonglong2double(option.def_value); } }; /** diff --git a/storage/example/ha_example.cc b/storage/example/ha_example.cc index 88f9f65..12c6d5b 100644 --- a/storage/example/ha_example.cc +++ b/storage/example/ha_example.cc @@ -1046,9 +1046,9 @@ static MYSQL_SYSVAR_DOUBLE( "0.0..1000.0", NULL, NULL, - 8.0, - 0.0, - 1000.0, + 8.5, + 0.5, + 1000.5, 0); static struct st_mysql_sys_var* example_system_variables[]= { -- 1.8.0