mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-25529 Fix to_string() OOM return semantics for temporal types
Temporal_hybrid::to_string() returned the destination String with a stale length when String::alloc() failed, instead of signalling the failure. Return NULL on allocation failure. Apply the same fix to the sibling to_string() methods of Interval_DDhhmmssff, Time, Datetime and Date, which shared the identical flaw, so all five report OOM consistently. The val_str() implementations that return the to_string() result directly did not set null_value, so a NULL result would be taken for a value. They now go through the new Item::check_str(), which sets null_value from the result, so a NULL does not stick to the item for the next execution. The time_to_string_oom debug keyword makes Time::to_string() fail as on out of memory.
This commit is contained in:
@@ -1967,3 +1967,18 @@ Warning 1292 Truncated incorrect DECIMAL value: '0x'
|
||||
SET SESSION debug_dbug="-d,Predicant_to_list_comparator";
|
||||
SET SESSION debug_dbug="-d,Item_func_in";
|
||||
SET SESSION debug_dbug="-d,cmp_item";
|
||||
#
|
||||
# MDEV-25529 Fix to_string() OOM return semantics for temporal types
|
||||
#
|
||||
# NULL from to_string() does not stick to the next execution
|
||||
prepare s from "select trim(time'10:00:00') as t";
|
||||
set @save_dbug= @@debug_dbug;
|
||||
set debug_dbug= '+d,time_to_string_oom';
|
||||
execute s;
|
||||
ERROR HY000: Out of memory.
|
||||
set debug_dbug= @save_dbug;
|
||||
execute s;
|
||||
t
|
||||
10:00:00
|
||||
deallocate prepare s;
|
||||
# End of 13.2 tests
|
||||
|
||||
@@ -592,3 +592,18 @@ SELECT ('0x',1) IN ((0,1),(1,1));
|
||||
SET SESSION debug_dbug="-d,Predicant_to_list_comparator";
|
||||
SET SESSION debug_dbug="-d,Item_func_in";
|
||||
SET SESSION debug_dbug="-d,cmp_item";
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-25529 Fix to_string() OOM return semantics for temporal types
|
||||
--echo #
|
||||
--echo # NULL from to_string() does not stick to the next execution
|
||||
prepare s from "select trim(time'10:00:00') as t";
|
||||
set @save_dbug= @@debug_dbug;
|
||||
set debug_dbug= '+d,time_to_string_oom';
|
||||
--error ER_OUT_OF_RESOURCES
|
||||
execute s;
|
||||
set debug_dbug= @save_dbug;
|
||||
execute s;
|
||||
deallocate prepare s;
|
||||
|
||||
--echo # End of 13.2 tests
|
||||
|
||||
33
sql/item.h
33
sql/item.h
@@ -1849,6 +1849,16 @@ public:
|
||||
virtual double val_result() { return val_real(); }
|
||||
virtual longlong val_int_result() { return val_int(); }
|
||||
virtual String *str_result(String* tmp) { return val_str(tmp); }
|
||||
/*
|
||||
Return a val_str() result and set null_value from it on every call. NULL
|
||||
is SQL NULL, or an error that is already reported (e.g. out of memory),
|
||||
which must not stay in null_value for the next execution.
|
||||
*/
|
||||
String *check_str(String *res)
|
||||
{
|
||||
null_value= !res;
|
||||
return res;
|
||||
}
|
||||
virtual my_decimal *val_decimal_result(my_decimal *val)
|
||||
{ return val_decimal(val); }
|
||||
virtual bool val_bool_result() { return val_bool(); }
|
||||
@@ -5663,7 +5673,7 @@ public:
|
||||
}
|
||||
String *val_str(String *to) override
|
||||
{
|
||||
return m_value.to_datetime(current_thd).to_string(to, decimals);
|
||||
return check_str(m_value.to_datetime(current_thd).to_string(to, decimals));
|
||||
}
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{
|
||||
@@ -5775,7 +5785,7 @@ public:
|
||||
}
|
||||
String *val_str(String *to) override
|
||||
{
|
||||
return update_null() ? 0 : cached_time.to_string(to);
|
||||
return update_null() ? 0 : check_str(cached_time.to_string(to));
|
||||
}
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{
|
||||
@@ -5829,7 +5839,7 @@ public:
|
||||
}
|
||||
double val_real() override { return cached_time.to_double(); }
|
||||
String *val_str(String *to) override
|
||||
{ return cached_time.to_string(to, decimals); }
|
||||
{ return check_str(cached_time.to_string(to, decimals)); }
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{ return cached_time.to_decimal(to); }
|
||||
longlong val_time_packed(THD *thd) override
|
||||
@@ -5909,7 +5919,8 @@ public:
|
||||
}
|
||||
String *val_str(String *to) override
|
||||
{
|
||||
return update_null() ? NULL : cached_time.to_string(to, decimals);
|
||||
return update_null() ? NULL :
|
||||
check_str(cached_time.to_string(to, decimals));
|
||||
}
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{
|
||||
@@ -7406,8 +7417,9 @@ public:
|
||||
{
|
||||
DBUG_ASSERT(copied_in);
|
||||
DBUG_ASSERT(sane());
|
||||
return null_value ? NULL :
|
||||
m_value.to_datetime(current_thd).to_string(to, decimals);
|
||||
if (null_value)
|
||||
return NULL;
|
||||
return check_str(m_value.to_datetime(current_thd).to_string(to, decimals));
|
||||
}
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{
|
||||
@@ -8302,7 +8314,7 @@ public:
|
||||
}
|
||||
String *val_str(String *to) override
|
||||
{
|
||||
return has_value() ? Time(this).to_string(to, decimals) : NULL;
|
||||
return has_value() ? check_str(Time(this).to_string(to, decimals)) : NULL;
|
||||
}
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{
|
||||
@@ -8343,7 +8355,8 @@ public:
|
||||
}
|
||||
String *val_str(String *to) override
|
||||
{
|
||||
return has_value() ? Datetime(this).to_string(to, decimals) : NULL;
|
||||
return has_value() ? check_str(Datetime(this).to_string(to, decimals)) :
|
||||
NULL;
|
||||
}
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{
|
||||
@@ -8376,7 +8389,7 @@ public:
|
||||
{ return has_value() ? Date(this).to_double() : 0; }
|
||||
String *val_str(String *to) override
|
||||
{
|
||||
return has_value() ? Date(this).to_string(to) : NULL;
|
||||
return has_value() ? check_str(Date(this).to_string(to)) : NULL;
|
||||
}
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{
|
||||
@@ -8401,7 +8414,7 @@ public:
|
||||
bool cache_value() override;
|
||||
String* val_str(String *to) override
|
||||
{
|
||||
return to_datetime(current_thd).to_string(to, decimals);
|
||||
return check_str(to_datetime(current_thd).to_string(to, decimals));
|
||||
}
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{
|
||||
|
||||
@@ -659,7 +659,8 @@ public:
|
||||
}
|
||||
String *val_str_ascii(Item_handled_func *item, String *to) const override
|
||||
{
|
||||
return Temporal_hybrid(item).to_string(to, item->decimals);
|
||||
return item->check_str(Temporal_hybrid(item).to_string(to,
|
||||
item->decimals));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -690,7 +691,7 @@ public:
|
||||
}
|
||||
String *val_str_ascii(Item_handled_func *item, String *to) const override
|
||||
{
|
||||
return Date(item).to_string(to);
|
||||
return item->check_str(Date(item).to_string(to));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -716,7 +717,7 @@ public:
|
||||
}
|
||||
String *val_str_ascii(Item_handled_func *item, String *to) const override
|
||||
{
|
||||
return Time(item).to_string(to, item->decimals);
|
||||
return item->check_str(Time(item).to_string(to, item->decimals));
|
||||
}
|
||||
bool val_native(THD *thd, Item_handled_func *item, Native *to) const override
|
||||
{
|
||||
@@ -746,7 +747,7 @@ public:
|
||||
}
|
||||
String *val_str_ascii(Item_handled_func *item, String *to) const override
|
||||
{
|
||||
return Datetime(item).to_string(to, item->decimals);
|
||||
return item->check_str(Datetime(item).to_string(to, item->decimals));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -692,7 +692,7 @@ public:
|
||||
double val_real() override
|
||||
{ return Date(this).to_double(); }
|
||||
String *val_str(String *to) override
|
||||
{ return Date(this).to_string(to); }
|
||||
{ return check_str(Date(this).to_string(to)); }
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{ return Date(this).to_decimal(to); }
|
||||
bool fix_length_and_dec(THD *thd) override
|
||||
@@ -721,7 +721,7 @@ public:
|
||||
double val_real() override
|
||||
{ return Time(this).to_double(); }
|
||||
String *val_str(String *to) override
|
||||
{ return Time(this).to_string(to, decimals); }
|
||||
{ return check_str(Time(this).to_string(to, decimals)); }
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{ return Time(this).to_decimal(to); }
|
||||
bool val_native(THD *thd, Native *to) override
|
||||
@@ -746,7 +746,7 @@ public:
|
||||
}
|
||||
double val_real() override { return Datetime(this).to_double(); }
|
||||
String *val_str(String *to) override
|
||||
{ return Datetime(this).to_string(to, decimals); }
|
||||
{ return check_str(Datetime(this).to_string(to, decimals)); }
|
||||
my_decimal *val_decimal(my_decimal *to) override
|
||||
{ return Datetime(this).to_decimal(to); }
|
||||
};
|
||||
@@ -790,8 +790,7 @@ public:
|
||||
String *val_str(String *to) override
|
||||
{
|
||||
const Datetime dt= to_datetime(current_thd);
|
||||
null_value= !dt.is_valid_datetime();
|
||||
return dt.to_string(to, decimals);
|
||||
return check_str(dt.to_string(to, decimals));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1502,8 +1501,7 @@ public:
|
||||
String *val_str(String *to) override
|
||||
{
|
||||
Interval_DDhhmmssff it(current_thd, args[0], m_fsp);
|
||||
null_value= !it.is_valid_interval_DDhhmmssff();
|
||||
return it.to_string(to, m_fsp);
|
||||
return check_str(it.to_string(to, m_fsp));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6267,21 +6267,21 @@ String *Type_handler_string_result::
|
||||
String *Type_handler_time_common::
|
||||
Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
|
||||
{
|
||||
return Time(func).to_string(str, func->decimals);
|
||||
return func->check_str(Time(func).to_string(str, func->decimals));
|
||||
}
|
||||
|
||||
|
||||
String *Type_handler_date_common::
|
||||
Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
|
||||
{
|
||||
return Date(func).to_string(str);
|
||||
return func->check_str(Date(func).to_string(str));
|
||||
}
|
||||
|
||||
|
||||
String *Type_handler_datetime_common::
|
||||
Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
|
||||
{
|
||||
return Datetime(func).to_string(str, func->decimals);
|
||||
return func->check_str(Datetime(func).to_string(str, func->decimals));
|
||||
}
|
||||
|
||||
|
||||
@@ -6289,8 +6289,8 @@ String *Type_handler_timestamp_common::
|
||||
Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
|
||||
{
|
||||
THD *thd= current_thd;
|
||||
return Timestamp_or_zero_datetime_native_null(thd, func).
|
||||
to_datetime(thd).to_string(str, func->decimals);
|
||||
return func->check_str(Timestamp_or_zero_datetime_native_null(thd, func).
|
||||
to_datetime(thd).to_string(str, func->decimals));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1421,8 +1421,9 @@ public:
|
||||
if (!is_valid_temporal())
|
||||
return NULL;
|
||||
str->set_charset(&my_charset_numeric);
|
||||
if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
str->length(my_TIME_to_str(this, const_cast<char*>(str->ptr()), dec));
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
return NULL;
|
||||
str->length(my_TIME_to_str(this, const_cast<char*>(str->ptr()), dec));
|
||||
return str;
|
||||
}
|
||||
const MYSQL_TIME *get_mysql_time() const
|
||||
@@ -1622,10 +1623,11 @@ public:
|
||||
if (!is_valid_interval_DDhhmmssff())
|
||||
return NULL;
|
||||
str->set_charset(&my_charset_numeric);
|
||||
if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
str->length(my_interval_DDhhmmssff_to_str(this,
|
||||
const_cast<char*>(str->ptr()),
|
||||
dec));
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
return NULL;
|
||||
str->length(my_interval_DDhhmmssff_to_str(this,
|
||||
const_cast<char*>(str->ptr()),
|
||||
dec));
|
||||
return str;
|
||||
}
|
||||
};
|
||||
@@ -2060,8 +2062,11 @@ public:
|
||||
if (!is_valid_time())
|
||||
return NULL;
|
||||
str->set_charset(&my_charset_numeric);
|
||||
if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
str->length(my_time_to_str(this, const_cast<char*>(str->ptr()), dec));
|
||||
DBUG_EXECUTE_IF("time_to_string_oom",
|
||||
my_error(ER_OUT_OF_RESOURCES, MYF(0)); return NULL;);
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
return NULL;
|
||||
str->length(my_time_to_str(this, const_cast<char*>(str->ptr()), dec));
|
||||
return str;
|
||||
}
|
||||
my_decimal *to_decimal(my_decimal *to) const
|
||||
@@ -2422,8 +2427,9 @@ public:
|
||||
if (!is_valid_date())
|
||||
return NULL;
|
||||
str->set_charset(&my_charset_numeric);
|
||||
if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
str->length(my_date_to_str(this, const_cast<char*>(str->ptr())));
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
return NULL;
|
||||
str->length(my_date_to_str(this, const_cast<char*>(str->ptr())));
|
||||
return str;
|
||||
}
|
||||
my_decimal *to_decimal(my_decimal *to) const
|
||||
@@ -2748,8 +2754,9 @@ public:
|
||||
if (!is_valid_datetime())
|
||||
return NULL;
|
||||
str->set_charset(&my_charset_numeric);
|
||||
if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
str->length(my_datetime_to_str(this, const_cast<char*>(str->ptr()), dec));
|
||||
if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||
return NULL;
|
||||
str->length(my_datetime_to_str(this, const_cast<char*>(str->ptr()), dec));
|
||||
return str;
|
||||
}
|
||||
my_decimal *to_decimal(my_decimal *to) const
|
||||
|
||||
Reference in New Issue
Block a user