1
0
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:
Aleksey Midenkov
2026-06-29 15:10:58 +03:00
parent 6300b34c16
commit 1d2978a3f6
7 changed files with 87 additions and 38 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
{

View File

@@ -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));
}
};

View File

@@ -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));
}
};

View File

@@ -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));
}

View File

@@ -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