Commit c45050d2 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-16935 Change the parameter of Field_xxx::store_TIME_with_dec() to const...

MDEV-16935 Change the parameter of Field_xxx::store_TIME_with_dec() to const Datetime* and const Time*
parent 522cd3c7
This diff is collapsed.
...@@ -2595,6 +2595,20 @@ class Field_temporal: public Field { ...@@ -2595,6 +2595,20 @@ class Field_temporal: public Field {
protected: protected:
Item *get_equal_const_item_datetime(THD *thd, const Context &ctx, Item *get_equal_const_item_datetime(THD *thd, const Context &ctx,
Item *const_item); Item *const_item);
int store_TIME_return_code_with_warnings(int warn, const ErrConv *str,
timestamp_type ts_type)
{
if (!MYSQL_TIME_WARN_HAVE_WARNINGS(warn) &&
MYSQL_TIME_WARN_HAVE_NOTES(warn))
{
set_warnings(Sql_condition::WARN_LEVEL_NOTE, str,
warn | MYSQL_TIME_WARN_TRUNCATED, ts_type);
return 3;
}
set_warnings(Sql_condition::WARN_LEVEL_WARN, str, warn, ts_type);
return warn ? 2 : 0;
}
public: public:
Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg, uchar null_bit_arg, utype unireg_check_arg,
...@@ -2660,9 +2674,10 @@ class Field_temporal: public Field { ...@@ -2660,9 +2674,10 @@ class Field_temporal: public Field {
*/ */
class Field_temporal_with_date: public Field_temporal { class Field_temporal_with_date: public Field_temporal {
protected: protected:
int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str,
int was_cut); int was_cut);
virtual void store_TIME(MYSQL_TIME *ltime) = 0; void store_TIME_with_trunc(const Time *);
virtual void store_TIME(const MYSQL_TIME *ltime) = 0;
virtual bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, virtual bool get_TIME(MYSQL_TIME *ltime, const uchar *pos,
ulonglong fuzzydate) const = 0; ulonglong fuzzydate) const = 0;
bool validate_MMDD(bool not_zero_date, uint month, uint day, bool validate_MMDD(bool not_zero_date, uint month, uint day,
...@@ -2694,7 +2709,8 @@ class Field_temporal_with_date: public Field_temporal { ...@@ -2694,7 +2709,8 @@ class Field_temporal_with_date: public Field_temporal {
class Field_timestamp :public Field_temporal { class Field_timestamp :public Field_temporal {
protected: protected:
sql_mode_t sql_mode_for_timestamp(THD *thd) const; sql_mode_t sql_mode_for_timestamp(THD *thd) const;
int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *, int warn); int store_TIME_with_warning(THD *, const Datetime *,
const ErrConv *, int warn);
public: public:
Field_timestamp(uchar *ptr_arg, uint32 len_arg, Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg, uchar *null_ptr_arg, uchar null_bit_arg,
...@@ -2948,7 +2964,7 @@ class Field_date_common: public Field_temporal_with_date ...@@ -2948,7 +2964,7 @@ class Field_date_common: public Field_temporal_with_date
class Field_date :public Field_date_common class Field_date :public Field_date_common
{ {
void store_TIME(MYSQL_TIME *ltime); void store_TIME(const MYSQL_TIME *ltime);
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
public: public:
Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
...@@ -2984,7 +3000,7 @@ class Field_date :public Field_date_common ...@@ -2984,7 +3000,7 @@ class Field_date :public Field_date_common
class Field_newdate :public Field_date_common class Field_newdate :public Field_date_common
{ {
void store_TIME(MYSQL_TIME *ltime); void store_TIME(const MYSQL_TIME *ltime);
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
public: public:
Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
...@@ -3019,7 +3035,7 @@ class Field_time :public Field_temporal { ...@@ -3019,7 +3035,7 @@ class Field_time :public Field_temporal {
long curdays; long curdays;
protected: protected:
virtual void store_TIME(const MYSQL_TIME *ltime); virtual void store_TIME(const MYSQL_TIME *ltime);
int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, int warn); int store_TIME_with_warning(const Time *ltime, const ErrConv *str, int warn);
void set_warnings(Sql_condition::enum_warning_level level, void set_warnings(Sql_condition::enum_warning_level level,
const ErrConv *str, int was_cut) const ErrConv *str, int was_cut)
{ {
...@@ -3177,7 +3193,7 @@ class Field_timef :public Field_time_with_dec { ...@@ -3177,7 +3193,7 @@ class Field_timef :public Field_time_with_dec {
class Field_datetime :public Field_temporal_with_date { class Field_datetime :public Field_temporal_with_date {
void store_TIME(MYSQL_TIME *ltime); void store_TIME(const MYSQL_TIME *ltime);
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
public: public:
Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
...@@ -3270,7 +3286,7 @@ class Field_datetime_with_dec :public Field_datetime { ...@@ -3270,7 +3286,7 @@ class Field_datetime_with_dec :public Field_datetime {
DATETIME(1..6) DATETIME(1..6)
*/ */
class Field_datetime_hires :public Field_datetime_with_dec { class Field_datetime_hires :public Field_datetime_with_dec {
void store_TIME(MYSQL_TIME *ltime); void store_TIME(const MYSQL_TIME *ltime);
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
public: public:
Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg, Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg,
...@@ -3293,7 +3309,7 @@ class Field_datetime_hires :public Field_datetime_with_dec { ...@@ -3293,7 +3309,7 @@ class Field_datetime_hires :public Field_datetime_with_dec {
DATETIME(0..6) - MySQL56 version DATETIME(0..6) - MySQL56 version
*/ */
class Field_datetimef :public Field_datetime_with_dec { class Field_datetimef :public Field_datetime_with_dec {
void store_TIME(MYSQL_TIME *ltime); void store_TIME(const MYSQL_TIME *ltime);
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
int save_field_metadata(uchar *metadata_ptr) int save_field_metadata(uchar *metadata_ptr)
{ {
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "sql_time.h" #include "sql_time.h"
#include "item.h" #include "item.h"
#include "log.h" #include "log.h"
#include "tztime.h"
Type_handler_row type_handler_row; Type_handler_row type_handler_row;
...@@ -359,12 +360,13 @@ VYear_op::VYear_op(Item_func_hybrid_field_type *item) ...@@ -359,12 +360,13 @@ VYear_op::VYear_op(Item_func_hybrid_field_type *item)
{ } { }
void Time::make_from_item(Item *item, const Options opt) void Time::make_from_item(int *warn, Item *item, const Options opt)
{ {
*warn= 0;
if (item->get_date(this, opt.get_date_flags())) if (item->get_date(this, opt.get_date_flags()))
time_type= MYSQL_TIMESTAMP_NONE; time_type= MYSQL_TIMESTAMP_NONE;
else else
valid_MYSQL_TIME_to_valid_value(opt); valid_MYSQL_TIME_to_valid_value(warn, opt);
} }
......
...@@ -466,6 +466,10 @@ class Temporal: protected MYSQL_TIME ...@@ -466,6 +466,10 @@ class Temporal: protected MYSQL_TIME
time_type= MYSQL_TIMESTAMP_NONE; time_type= MYSQL_TIMESTAMP_NONE;
} }
public: public:
long fraction_remainder(uint dec) const
{
return my_time_fraction_remainder(second_part, dec);
}
}; };
...@@ -474,7 +478,7 @@ class Temporal: protected MYSQL_TIME ...@@ -474,7 +478,7 @@ class Temporal: protected MYSQL_TIME
using Item's native timestamp type, without automatic timestamp using Item's native timestamp type, without automatic timestamp
type conversion. type conversion.
*/ */
class Temporal_hybrid: private Temporal class Temporal_hybrid: public Temporal
{ {
public: public:
Temporal_hybrid(THD *thd, Item *item); Temporal_hybrid(THD *thd, Item *item);
...@@ -524,7 +528,7 @@ class Temporal_hybrid: private Temporal ...@@ -524,7 +528,7 @@ class Temporal_hybrid: private Temporal
Time derives from MYSQL_TIME privately to make sure it is accessed Time derives from MYSQL_TIME privately to make sure it is accessed
externally only in the valid state. externally only in the valid state.
*/ */
class Time: private Temporal class Time: public Temporal
{ {
public: public:
enum datetime_to_time_mode_t enum datetime_to_time_mode_t
...@@ -599,30 +603,27 @@ class Time: private Temporal ...@@ -599,30 +603,27 @@ class Time: private Temporal
/* /*
Convert a valid DATE or DATETIME to TIME. Convert a valid DATE or DATETIME to TIME.
Before this call, "this" must be a valid DATE or DATETIME value, Before this call, "this" must be a valid DATE or DATETIME value,
e.g. returned from Item::get_date(). e.g. returned from Item::get_date(), str_to_time(), number_to_time().
After this call, "this" is a valid TIME value. After this call, "this" is a valid TIME value.
*/ */
void valid_datetime_to_valid_time(const Options opt) void valid_datetime_to_valid_time(int *warn, const Options opt)
{ {
DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATE || DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATE ||
time_type == MYSQL_TIMESTAMP_DATETIME); time_type == MYSQL_TIMESTAMP_DATETIME);
/* /*
Make sure that day and hour are valid, so the result hour value We're dealing with a DATE or DATETIME returned from
str_to_time(), number_to_time() or unpack_time().
Do some asserts to make sure the result hour value
after mixing days to hours does not go out of the valid TIME range. after mixing days to hours does not go out of the valid TIME range.
*/
DBUG_ASSERT(day < 32);
DBUG_ASSERT(hour < 24);
if (year == 0 && month == 0 &&
opt.datetime_to_time_mode() ==
DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
{
/*
The maximum hour value after mixing days will be 31*24+23=767, The maximum hour value after mixing days will be 31*24+23=767,
which is within the supported TIME range. which is within the supported TIME range.
Thus no adjust_time_range_or_invalidate() is needed here. Thus no adjust_time_range_or_invalidate() is needed here.
*/ */
hour+= day * 24; DBUG_ASSERT(day < 32);
} DBUG_ASSERT(hour < 24);
if (opt.datetime_to_time_mode() ==
DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(warn, year, month, day);
year= month= day= 0; year= month= day= 0;
time_type= MYSQL_TIMESTAMP_TIME; time_type= MYSQL_TIMESTAMP_TIME;
DBUG_ASSERT(is_valid_time_slow()); DBUG_ASSERT(is_valid_time_slow());
...@@ -630,6 +631,7 @@ class Time: private Temporal ...@@ -630,6 +631,7 @@ class Time: private Temporal
/** /**
Convert valid DATE/DATETIME to valid TIME if needed. Convert valid DATE/DATETIME to valid TIME if needed.
This method is called after Item::get_date(), This method is called after Item::get_date(),
str_to_time(), number_to_time().
which can return only valid TIME/DATE/DATETIME values. which can return only valid TIME/DATE/DATETIME values.
Before this call, "this" is: Before this call, "this" is:
- either a valid TIME/DATE/DATETIME value - either a valid TIME/DATE/DATETIME value
...@@ -639,12 +641,12 @@ class Time: private Temporal ...@@ -639,12 +641,12 @@ class Time: private Temporal
- either a valid TIME (within the supported TIME range), - either a valid TIME (within the supported TIME range),
- or MYSQL_TIMESTAMP_NONE - or MYSQL_TIMESTAMP_NONE
*/ */
void valid_MYSQL_TIME_to_valid_value(const Options opt) void valid_MYSQL_TIME_to_valid_value(int *warn, const Options opt)
{ {
switch (time_type) { switch (time_type) {
case MYSQL_TIMESTAMP_DATE: case MYSQL_TIMESTAMP_DATE:
case MYSQL_TIMESTAMP_DATETIME: case MYSQL_TIMESTAMP_DATETIME:
valid_datetime_to_valid_time(opt); valid_datetime_to_valid_time(warn, opt);
break; break;
case MYSQL_TIMESTAMP_NONE: case MYSQL_TIMESTAMP_NONE:
break; break;
...@@ -656,6 +658,19 @@ class Time: private Temporal ...@@ -656,6 +658,19 @@ class Time: private Temporal
break; break;
} }
} }
/*
This method is called after number_to_time() and str_to_time(),
which can return DATE or DATETIME values. Convert to TIME if needed.
We trust that xxx_to_time() returns a valid TIME/DATE/DATETIME value,
so here we need to do only simple validation.
*/
void xxx_to_time_result_to_valid_value(int *warn, const Options opt)
{
// str_to_time(), number_to_time() never return MYSQL_TIMESTAMP_ERROR
DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR);
valid_MYSQL_TIME_to_valid_value(warn, opt);
}
void adjust_time_range_or_invalidate(int *warn) void adjust_time_range_or_invalidate(int *warn)
{ {
if (check_time_range(this, TIME_SECOND_PART_DIGITS, warn)) if (check_time_range(this, TIME_SECOND_PART_DIGITS, warn))
...@@ -667,12 +682,49 @@ class Time: private Temporal ...@@ -667,12 +682,49 @@ class Time: private Temporal
long curdays); long curdays);
void make_from_time(int *warn, const MYSQL_TIME *from); void make_from_time(int *warn, const MYSQL_TIME *from);
void make_from_datetime(int *warn, const MYSQL_TIME *from, long curdays); void make_from_datetime(int *warn, const MYSQL_TIME *from, long curdays);
void make_from_item(class Item *item, const Options opt); void make_from_item(int *warn, Item *item, const Options opt);
public: public:
/*
All constructors that accept an "int *warn" parameter initialize *warn.
The old value gets lost.
*/
Time() { time_type= MYSQL_TIMESTAMP_NONE; } Time() { time_type= MYSQL_TIMESTAMP_NONE; }
Time(Item *item) { make_from_item(item, Options()); } Time(Item *item)
Time(Item *item, const Options opt) { make_from_item(item, opt); } {
int warn;
make_from_item(&warn, item, Options());
}
Time(Item *item, const Options opt)
{
int warn;
make_from_item(&warn, item, opt);
}
Time(int *warn, const MYSQL_TIME *from, long curdays); Time(int *warn, const MYSQL_TIME *from, long curdays);
Time(int *warn, const char *str, uint len, CHARSET_INFO *cs,
const Options opt)
{
MYSQL_TIME_STATUS status;
if (str_to_time(cs, str, len, this, opt.get_date_flags(), &status))
time_type= MYSQL_TIMESTAMP_NONE;
// The below call will optionally add notes to already collected warnings:
xxx_to_time_result_to_valid_value(&status.warnings, opt);
*warn= status.warnings;
}
Time(int *warn, const Sec6 &nr, const Options opt)
{
if (nr.to_time(this, warn))
time_type= MYSQL_TIMESTAMP_NONE;
xxx_to_time_result_to_valid_value(warn, opt);
}
Time(int *warn, double nr)
:Temporal(Time(warn, Sec6(nr), Options()))
{ }
Time(int *warn, longlong nr, bool unsigned_val)
:Temporal(Time(warn, Sec6(nr, unsigned_val), Options()))
{ }
Time(int *warn, const my_decimal *d)
:Temporal(Time(warn, Sec6(d), Options()))
{ }
static sql_mode_t flags_for_get_date() static sql_mode_t flags_for_get_date()
{ return TIME_TIME_ONLY | TIME_INVALID_DATES; } { return TIME_TIME_ONLY | TIME_INVALID_DATES; }
static sql_mode_t comparison_flags_for_get_date() static sql_mode_t comparison_flags_for_get_date()
...@@ -748,6 +800,12 @@ class Time: private Temporal ...@@ -748,6 +800,12 @@ class Time: private Temporal
{ {
return is_valid_time() ? Temporal::to_packed() : 0; return is_valid_time() ? Temporal::to_packed() : 0;
} }
Time trunc(uint dec) const
{
Time tm(*this);
my_time_trunc(&tm, dec);
return tm;
}
}; };
...@@ -774,7 +832,7 @@ class Time: private Temporal ...@@ -774,7 +832,7 @@ class Time: private Temporal
it is accessed externally only in the valid state. it is accessed externally only in the valid state.
*/ */
class Temporal_with_date: protected Temporal class Temporal_with_date: public Temporal
{ {
protected: protected:
void check_date_or_invalidate(int *warn, sql_mode_t flags); void check_date_or_invalidate(int *warn, sql_mode_t flags);
...@@ -792,6 +850,21 @@ class Temporal_with_date: protected Temporal ...@@ -792,6 +850,21 @@ class Temporal_with_date: protected Temporal
{ {
make_from_item(thd, item); make_from_item(thd, item);
} }
Temporal_with_date(int *warn, const Sec6 &nr, sql_mode_t flags)
{
DBUG_ASSERT((flags & TIME_TIME_ONLY) == 0);
if (nr.to_datetime(this, flags, warn))
time_type= MYSQL_TIMESTAMP_NONE;
}
Temporal_with_date(int *warn, const char *str, uint len, CHARSET_INFO *cs,
sql_mode_t flags)
{
DBUG_ASSERT((flags & TIME_TIME_ONLY) == 0);
MYSQL_TIME_STATUS status;
if (str_to_datetime(cs, str, len, this, flags, &status))
time_type= MYSQL_TIMESTAMP_NONE;
*warn= status.warnings;
}
public: public:
bool check_date_with_warn(ulonglong flags) bool check_date_with_warn(ulonglong flags)
{ {
...@@ -916,6 +989,11 @@ class Datetime: public Temporal_with_date ...@@ -916,6 +989,11 @@ class Datetime: public Temporal_with_date
DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATETIME); DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATETIME);
return !check_datetime_range(this); return !check_datetime_range(this);
} }
void date_to_datetime_if_needed()
{
if (time_type == MYSQL_TIMESTAMP_DATE)
date_to_datetime(this);
}
void make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, void make_from_time(THD *thd, int *warn, const MYSQL_TIME *from,
sql_mode_t flags); sql_mode_t flags);
void make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from, void make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from,
...@@ -924,22 +1002,19 @@ class Datetime: public Temporal_with_date ...@@ -924,22 +1002,19 @@ class Datetime: public Temporal_with_date
Datetime(THD *thd, Item *item, sql_mode_t flags) Datetime(THD *thd, Item *item, sql_mode_t flags)
:Temporal_with_date(thd, item, flags) :Temporal_with_date(thd, item, flags)
{ {
if (time_type == MYSQL_TIMESTAMP_DATE) date_to_datetime_if_needed();
date_to_datetime(this);
DBUG_ASSERT(is_valid_value_slow()); DBUG_ASSERT(is_valid_value_slow());
} }
Datetime(THD *thd, Item *item) Datetime(THD *thd, Item *item)
:Temporal_with_date(thd, item) :Temporal_with_date(thd, item)
{ {
if (time_type == MYSQL_TIMESTAMP_DATE) date_to_datetime_if_needed();
date_to_datetime(this);
DBUG_ASSERT(is_valid_value_slow()); DBUG_ASSERT(is_valid_value_slow());
} }
Datetime(Item *item) Datetime(Item *item)
:Temporal_with_date(current_thd, item) :Temporal_with_date(current_thd, item)
{ {
if (time_type == MYSQL_TIMESTAMP_DATE) date_to_datetime_if_needed();
date_to_datetime(this);
DBUG_ASSERT(is_valid_value_slow()); DBUG_ASSERT(is_valid_value_slow());
} }
Datetime(THD *thd, int *warn, const MYSQL_TIME *from, sql_mode_t flags); Datetime(THD *thd, int *warn, const MYSQL_TIME *from, sql_mode_t flags);
...@@ -947,6 +1022,39 @@ class Datetime: public Temporal_with_date ...@@ -947,6 +1022,39 @@ class Datetime: public Temporal_with_date
{ {
set_zero_time(this, MYSQL_TIMESTAMP_DATETIME); set_zero_time(this, MYSQL_TIMESTAMP_DATETIME);
} }
Datetime(int *warn, const char *str, uint len, CHARSET_INFO *cs,
sql_mode_t flags)
:Temporal_with_date(warn, str, len, cs, flags)
{
date_to_datetime_if_needed();
DBUG_ASSERT(is_valid_value_slow());
}
Datetime(int *warn, double nr, sql_mode_t flags)
:Temporal_with_date(warn, Sec6(nr), flags)
{
date_to_datetime_if_needed();
DBUG_ASSERT(is_valid_value_slow());
}
Datetime(int *warn, const my_decimal *d, sql_mode_t flags)
:Temporal_with_date(warn, Sec6(d), flags)
{
date_to_datetime_if_needed();
DBUG_ASSERT(is_valid_value_slow());
}
/*
Create a Datime object from a longlong number.
Note, unlike in Time(), we don't need an "unsigned_val" here,
as it's not important if overflow happened because
of a negative number, or because of a huge positive number.
*/
Datetime(int *warn, longlong sec, ulong usec, sql_mode_t flags)
:Temporal_with_date(warn, Sec6(false, (ulonglong) sec, usec), flags)
{
Sec6 nr(false, (ulonglong) sec, usec);
date_to_datetime_if_needed();
DBUG_ASSERT(is_valid_value_slow());
}
bool is_valid_datetime() const bool is_valid_datetime() const
{ {
/* /*
...@@ -1019,6 +1127,12 @@ class Datetime: public Temporal_with_date ...@@ -1019,6 +1127,12 @@ class Datetime: public Temporal_with_date
{ {
return is_valid_datetime() ? Temporal::to_packed() : 0; return is_valid_datetime() ? Temporal::to_packed() : 0;
} }
Datetime trunc(uint dec) const
{
Datetime tm(*this);
my_time_trunc(&tm, dec);
return tm;
}
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment