summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/item_func.cc4
-rw-r--r--sql/item_timefunc.h70
-rw-r--r--sql/sql_time.cc10
-rw-r--r--sql/sql_type.cc130
-rw-r--r--sql/sql_type.h82
5 files changed, 257 insertions, 39 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc
index ff315aef458..ccac348e4bc 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2649,13 +2649,13 @@ bool Item_func_min_max::get_time_native(THD *thd, MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed == 1);
- Time value(thd, args[0]);
+ Time value(thd, args[0], Time::Options(), decimals);
if (!value.is_valid_time())
return (null_value= true);
for (uint i= 1; i < arg_count ; i++)
{
- Time tmp(thd, args[i]);
+ Time tmp(thd, args[i], Time::Options(), decimals);
if (!tmp.is_valid_time())
return (null_value= true);
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index ad261635cc0..c1e77eae5bc 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -1202,20 +1202,22 @@ public:
bool fix_length_and_dec()
{
THD *thd= current_thd;
- uint dec= MY_MAX(args[0]->datetime_precision(thd),
- args[1]->time_precision(thd));
- fix_attributes_datetime(dec);
+ uint dec0= args[0]->datetime_precision(thd);
+ uint dec1= Interval_DDhhmmssff::fsp(thd, args[1]);
+ fix_attributes_datetime(MY_MAX(dec0, dec1));
maybe_null= true;
return false;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
Datetime dt(thd, args[0], date_mode_t(0));
- MYSQL_TIME ltime2;
- return (null_value= (!dt.is_valid_datetime() ||
- args[1]->get_time(thd, &ltime2) ||
- Sec6_add(dt.get_mysql_time(), &ltime2, 1).
- to_datetime(ltime)));
+ if (!dt.is_valid_datetime())
+ return null_value= true;
+ Interval_DDhhmmssff it(thd, args[1]);
+ if (!it.is_valid_interval_DDhhmmssff())
+ return null_value= true;
+ return (null_value= Sec6_add(dt.get_mysql_time(), it.get_mysql_time(), 1).
+ to_datetime(ltime));
}
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_timestamp>(thd, this); }
@@ -1558,20 +1560,23 @@ public:
bool fix_length_and_dec(Item_handled_func *item) const
{
THD *thd= current_thd;
- uint dec= MY_MAX(item->arguments()[0]->datetime_precision(thd),
- item->arguments()[1]->time_precision(thd));
- item->fix_attributes_datetime(dec);
+ uint dec0= item->arguments()[0]->datetime_precision(thd);
+ uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]);
+ item->fix_attributes_datetime(MY_MAX(dec0, dec1));
return false;
}
bool get_date(THD *thd, Item_handled_func *item,
MYSQL_TIME *to, date_mode_t fuzzy) const
{
DBUG_ASSERT(item->is_fixed());
- MYSQL_TIME l_time2;
Datetime dt(thd, item->arguments()[0], date_mode_t(0));
- return (item->null_value= (!dt.is_valid_datetime() ||
- item->arguments()[1]->get_time(current_thd, &l_time2) ||
- Sec6_add(dt.get_mysql_time(), &l_time2, m_sign).
+ if (!dt.is_valid_datetime())
+ return item->null_value= true;
+ Interval_DDhhmmssff it(thd, item->arguments()[1]);
+ if (!it.is_valid_interval_DDhhmmssff())
+ return item->null_value= true;
+ return (item->null_value= (Sec6_add(dt.get_mysql_time(),
+ it.get_mysql_time(), m_sign).
to_datetime(to)));
}
};
@@ -1588,20 +1593,23 @@ public:
bool fix_length_and_dec(Item_handled_func *item) const
{
THD *thd= current_thd;
- uint dec= MY_MAX(item->arguments()[0]->time_precision(thd),
- item->arguments()[1]->time_precision(thd));
- item->fix_attributes_time(dec);
+ uint dec0= item->arguments()[0]->time_precision(thd);
+ uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]);
+ item->fix_attributes_time(MY_MAX(dec0, dec1));
return false;
}
bool get_date(THD *thd, Item_handled_func *item,
MYSQL_TIME *to, date_mode_t fuzzy) const
{
DBUG_ASSERT(item->is_fixed());
- MYSQL_TIME l_time2;
Time t(thd, item->arguments()[0]);
- return (item->null_value= (!t.is_valid_time() ||
- item->arguments()[1]->get_time(current_thd, &l_time2) ||
- Sec6_add(t.get_mysql_time(), &l_time2, m_sign).
+ if (!t.is_valid_time())
+ return item->null_value= true;
+ Interval_DDhhmmssff i(thd, item->arguments()[1]);
+ if (!i.is_valid_interval_DDhhmmssff())
+ return item->null_value= true;
+ return (item->null_value= (Sec6_add(t.get_mysql_time(),
+ i.get_mysql_time(), m_sign).
to_time(thd, to, item->decimals)));
}
};
@@ -1617,8 +1625,9 @@ public:
{ }
bool fix_length_and_dec(Item_handled_func *item) const
{
- uint dec= MY_MAX(item->arguments()[0]->decimals,
- item->arguments()[1]->decimals);
+ uint dec0= item->arguments()[0]->decimals;
+ uint dec1= Interval_DDhhmmssff::fsp(current_thd, item->arguments()[1]);
+ uint dec= MY_MAX(dec0, dec1);
item->collation.set(item->default_charset(),
DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
@@ -1629,12 +1638,15 @@ public:
{
DBUG_ASSERT(item->is_fixed());
// Detect a proper timestamp type based on the argument values
- MYSQL_TIME l_time1, l_time2;
- if (item->arguments()[0]->get_time(thd, &l_time1) ||
- item->arguments()[1]->get_time(thd, &l_time2))
+ Temporal_hybrid l_time1(thd, item->arguments()[0], TIME_TIME_ONLY);
+ if (!l_time1.is_valid_temporal())
+ return (item->null_value= true);
+ Interval_DDhhmmssff l_time2(thd, item->arguments()[1]);
+ if (!l_time2.is_valid_interval_DDhhmmssff())
return (item->null_value= true);
- Sec6_add add(&l_time1, &l_time2, m_sign);
- return (item->null_value= (l_time1.time_type == MYSQL_TIMESTAMP_TIME ?
+ Sec6_add add(l_time1.get_mysql_time(), l_time2.get_mysql_time(), m_sign);
+ return (item->null_value= (l_time1.get_mysql_time()->time_type ==
+ MYSQL_TIMESTAMP_TIME ?
add.to_time(thd, to, item->decimals) :
add.to_datetime(to)));
}
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index ce6eb2047a8..90803b48539 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -395,6 +395,16 @@ bool Temporal::str_to_datetime(MYSQL_TIME_STATUS *status,
}
+/* Character set-aware version of str_to_DDhhmmssff() */
+bool Interval_DDhhmmssff::str_to_DDhhmmssff(MYSQL_TIME_STATUS *status,
+ const char *str, size_t length,
+ CHARSET_INFO *cs, ulong max_hour)
+{
+ TemporalAsciiBuffer tmp(str, length, cs);
+ return ::str_to_DDhhmmssff(tmp.str, tmp.length, this, UINT_MAX32, status);
+}
+
+
/*
Convert a timestamp string to a MYSQL_TIME value and produce a warning
if string was truncated during conversion.
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 869c3a1cc2e..1dfaa3a73b0 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -154,6 +154,12 @@ VDec_op::VDec_op(Item_func_hybrid_field_type *item)
}
+date_mode_t Temporal::sql_mode_for_dates(THD *thd)
+{
+ return ::sql_mode_for_dates(thd);
+}
+
+
bool Dec_ptr::to_datetime_with_warn(THD *thd, MYSQL_TIME *to,
date_mode_t fuzzydate, Item *item)
{
@@ -176,9 +182,9 @@ my_decimal *Temporal::bad_to_decimal(my_decimal *to) const
}
-Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item)
+Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate)
{
- if (item->get_date(thd, this, sql_mode_for_dates(thd)))
+ if (item->get_date(thd, this, fuzzydate))
time_type= MYSQL_TIMESTAMP_NONE;
}
@@ -331,6 +337,117 @@ VYear_op::VYear_op(Item_func_hybrid_field_type *item)
{ }
+const LEX_CSTRING Interval_DDhhmmssff::m_type_name=
+ {STRING_WITH_LEN("INTERVAL DAY TO SECOND")};
+
+
+Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, MYSQL_TIME_STATUS *st,
+ bool push_warnings,
+ Item *item, ulong max_hour)
+{
+ my_time_status_init(st);
+ switch (item->cmp_type()) {
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ time_type= MYSQL_TIMESTAMP_NONE;
+ break;
+ case TIME_RESULT:
+ {
+ if (item->get_date(thd, this, TIME_TIME_ONLY))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else if (time_type != MYSQL_TIMESTAMP_TIME)
+ {
+ st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ push_warning_wrong_or_truncated_value(thd, ErrConvTime(this),
+ st->warnings);
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+ break;
+ }
+ case INT_RESULT:
+ case REAL_RESULT:
+ case DECIMAL_RESULT:
+ case STRING_RESULT:
+ {
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> tmp;
+ String *str= item->val_str(&tmp);
+ if (!str)
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else if (str_to_DDhhmmssff(st, str->ptr(), str->length(), str->charset(),
+ UINT_MAX32))
+ {
+ if (push_warnings)
+ thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
+ m_type_name.str,
+ ErrConvString(str).ptr());
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+ else
+ {
+ if (hour > max_hour)
+ {
+ st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+ // Warn if hour or nanosecond truncation happened
+ if (push_warnings)
+ push_warning_wrong_or_truncated_value(thd, ErrConvString(str),
+ st->warnings);
+ }
+ }
+ break;
+ }
+ DBUG_ASSERT(is_valid_value_slow());
+}
+
+
+void
+Interval_DDhhmmssff::push_warning_wrong_or_truncated_value(THD *thd,
+ const ErrConv &str,
+ int warnings)
+{
+ if (warnings & MYSQL_TIME_WARN_OUT_OF_RANGE)
+ {
+ thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
+ m_type_name.str, str.ptr());
+ }
+ else if (MYSQL_TIME_WARN_HAVE_WARNINGS(warnings))
+ {
+ thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_WARN,
+ m_type_name.str, str.ptr());
+ }
+ else if (MYSQL_TIME_WARN_HAVE_NOTES(warnings))
+ {
+ thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_NOTE,
+ m_type_name.str, str.ptr());
+ }
+}
+
+
+uint Interval_DDhhmmssff::fsp(THD *thd, Item *item)
+{
+ MYSQL_TIME_STATUS st;
+ switch (item->cmp_type()) {
+ case INT_RESULT:
+ case TIME_RESULT:
+ return item->decimals;
+ case REAL_RESULT:
+ case DECIMAL_RESULT:
+ return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ return 0;
+ case STRING_RESULT:
+ break;
+ }
+ if (!item->const_item() || item->is_expensive())
+ return TIME_SECOND_PART_DIGITS;
+ Interval_DDhhmmssff it(thd, &st, false/*no warnings*/, item, UINT_MAX32);
+ return it.is_valid_interval_DDhhmmssff() ? st.precision :
+ TIME_SECOND_PART_DIGITS;
+}
+
+
void Time::make_from_item(THD *thd, int *warn, Item *item, const Options opt)
{
*warn= 0;
@@ -3487,6 +3604,15 @@ bool Type_handler_temporal_result::
{
bool rc= Type_handler::Item_func_min_max_fix_attributes(thd, func,
items, nitems);
+ bool is_time= func->field_type() == MYSQL_TYPE_TIME;
+ func->decimals= 0;
+ for (uint i= 0; i < nitems; i++)
+ {
+ uint deci= is_time ? items[i]->time_precision(thd) :
+ items[i]->datetime_precision(thd);
+ set_if_bigger(func->decimals, deci);
+ }
+
if (rc || func->maybe_null)
return rc;
/*
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 9708e9b2705..1a6c77008e8 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -446,6 +446,7 @@ public:
class Temporal: protected MYSQL_TIME
{
public:
+ static date_mode_t sql_mode_for_dates(THD *thd);
bool is_valid_temporal() const
{
DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR);
@@ -469,6 +470,16 @@ protected:
CHARSET_INFO *cs, date_mode_t fuzzydate);
bool str_to_datetime(MYSQL_TIME_STATUS *st, const char *str, size_t length,
CHARSET_INFO *cs, date_mode_t fuzzydate);
+ bool has_valid_mmssff() const
+ {
+ return minute <= TIME_MAX_MINUTE &&
+ second <= TIME_MAX_SECOND &&
+ second_part <= TIME_MAX_SECOND_PART;
+ }
+ bool has_zero_YYYYMMDD() const
+ {
+ return year == 0 && month == 0 && day == 0;
+ }
public:
static void *operator new(size_t size, MYSQL_TIME *ltime) throw()
{
@@ -492,8 +503,13 @@ public:
class Temporal_hybrid: public Temporal
{
public:
- Temporal_hybrid(THD *thd, Item *item);
- Temporal_hybrid(Item *item): Temporal_hybrid(current_thd, item) { }
+ Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate);
+ Temporal_hybrid(THD *thd, Item *item)
+ :Temporal_hybrid(thd, item, sql_mode_for_dates(thd))
+ { }
+ Temporal_hybrid(Item *item)
+ :Temporal_hybrid(current_thd, item)
+ { }
Temporal_hybrid(MYSQL_TIME_STATUS *st, const char *str, size_t length,
CHARSET_INFO *cs, date_mode_t fuzzydate)
{
@@ -538,6 +554,63 @@ public:
};
+class Interval_DDhhmmssff: public Temporal
+{
+ static const LEX_CSTRING m_type_name;
+ bool str_to_DDhhmmssff(MYSQL_TIME_STATUS *status,
+ const char *str, size_t length, CHARSET_INFO *cs,
+ ulong max_hour);
+ void push_warning_wrong_or_truncated_value(THD *thd,
+ const ErrConv &str,
+ int warnings);
+ bool is_valid_interval_DDhhmmssff_slow() const
+ {
+ return time_type == MYSQL_TIMESTAMP_TIME &&
+ has_zero_YYYYMMDD() && has_valid_mmssff();
+ }
+ bool is_valid_value_slow() const
+ {
+ return time_type == MYSQL_TIMESTAMP_NONE ||
+ is_valid_interval_DDhhmmssff_slow();
+ }
+public:
+ // Get fractional second precision from an Item
+ static uint fsp(THD *thd, Item *item);
+ /*
+ Maximum useful HOUR value:
+ TIMESTAMP'0001-01-01 00:00:00' + '87649415:59:59' = '9999-12-31 23:59:59'
+ This gives maximum possible interval values:
+ - '87649415:59:59.999999' (in 'hh:mm:ss.ff' format)
+ - '3652058 23:59:59.999999' (in 'DD hh:mm:ss.ff' format)
+ */
+ static uint max_useful_hour()
+ {
+ return 87649415;
+ }
+public:
+ Interval_DDhhmmssff(THD *thd, MYSQL_TIME_STATUS *st, bool push_warnings,
+ Item *item, ulong max_hour);
+ Interval_DDhhmmssff(THD *thd, Item *item)
+ {
+ MYSQL_TIME_STATUS st;
+ new(this) Interval_DDhhmmssff(thd, &st, true, item, max_useful_hour());
+ }
+ const MYSQL_TIME *get_mysql_time() const
+ {
+ DBUG_ASSERT(is_valid_interval_DDhhmmssff_slow());
+ return this;
+ }
+ bool is_valid_interval_DDhhmmssff() const
+ {
+ return time_type == MYSQL_TIMESTAMP_TIME;
+ }
+ bool is_valid_value() const
+ {
+ return time_type == MYSQL_TIMESTAMP_NONE || is_valid_interval_DDhhmmssff();
+ }
+};
+
+
/**
Class Time is designed to store valid TIME values.
@@ -609,10 +682,7 @@ private:
bool is_valid_time_slow() const
{
return time_type == MYSQL_TIMESTAMP_TIME &&
- year == 0 && month == 0 && day == 0 &&
- minute <= TIME_MAX_MINUTE &&
- second <= TIME_MAX_SECOND &&
- second_part <= TIME_MAX_SECOND_PART;
+ has_zero_YYYYMMDD() && has_valid_mmssff();
}
void hhmmssff_copy(const MYSQL_TIME *from)
{