Commit a78d1aaa authored by Alexander Barkov's avatar Alexander Barkov

MDEV-16806 Add Type_handler::create_literal_item()

parent fee46323
......@@ -6951,14 +6951,18 @@ Item_string::make_string_literal_concat(THD *thd, const LEX_CSTRING *str)
*/
Item *Item_string::make_odbc_literal(THD *thd, const LEX_CSTRING *typestr)
{
enum_field_types type= odbc_temporal_literal_type(typestr);
Item *res= type == MYSQL_TYPE_STRING ? this :
create_temporal_literal(thd, val_str(NULL), type, false);
Item_literal *res;
const Type_handler *h;
if (collation.repertoire == MY_REPERTOIRE_ASCII &&
str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4 &&
(h= Type_handler::odbc_literal_type_handler(typestr)) &&
(res= h->create_literal_item(thd, val_str(NULL), false)))
return res;
/*
create_temporal_literal() returns NULL if failed to parse the string,
h->create_literal_item() returns NULL if failed to parse the string,
or the string format did not match the type, e.g.: {d'2001-01-01 10:10:10'}
*/
return res ? res : this;
return this;
}
......
......@@ -4330,34 +4330,6 @@ class Item_string :public Item_literal
String *check_well_formed_result(bool send_error)
{ return Item::check_well_formed_result(&str_value, send_error); }
enum_field_types odbc_temporal_literal_type(const LEX_CSTRING *type_str) const
{
/*
If string is a reasonably short pure ASCII string literal,
try to parse known ODBC style date, time or timestamp literals,
e.g:
SELECT {d'2001-01-01'};
SELECT {t'10:20:30'};
SELECT {ts'2001-01-01 10:20:30'};
*/
if (collation.repertoire == MY_REPERTOIRE_ASCII &&
str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4)
{
if (type_str->length == 1)
{
if (type_str->str[0] == 'd') /* {d'2001-01-01'} */
return MYSQL_TYPE_DATE;
else if (type_str->str[0] == 't') /* {t'10:20:30'} */
return MYSQL_TYPE_TIME;
}
else if (type_str->length == 2) /* {ts'2001-01-01 10:20:30'} */
{
if (type_str->str[0] == 't' && type_str->str[1] == 's')
return MYSQL_TYPE_DATETIME;
}
}
return MYSQL_TYPE_STRING; // Not a temporal literal
}
Item_basic_constant *make_string_literal_concat(THD *thd,
const LEX_CSTRING *);
Item *make_odbc_literal(THD *thd, const LEX_CSTRING *typestr);
......
......@@ -7432,84 +7432,6 @@ find_qualified_function_builder(THD *thd)
}
static bool
have_important_literal_warnings(const MYSQL_TIME_STATUS *status)
{
return (status->warnings & ~MYSQL_TIME_NOTE_TRUNCATED) != 0;
}
/**
Builder for datetime literals:
TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'.
@param thd The current thread
@param str Character literal
@param length Length of str
@param type Type of literal (TIME, DATE or DATETIME)
@param send_error Whether to generate an error on failure
*/
Item *create_temporal_literal(THD *thd,
const char *str, size_t length,
CHARSET_INFO *cs,
enum_field_types type,
bool send_error)
{
MYSQL_TIME_STATUS status;
MYSQL_TIME ltime;
Item *item= NULL;
sql_mode_t flags= sql_mode_for_dates(thd);
switch(type)
{
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_NEWDATE:
if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
ltime.time_type == MYSQL_TIMESTAMP_DATE && !status.warnings)
item= new (thd->mem_root) Item_date_literal(thd, &ltime);
break;
case MYSQL_TYPE_DATETIME:
if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
ltime.time_type == MYSQL_TIMESTAMP_DATETIME &&
!have_important_literal_warnings(&status))
item= new (thd->mem_root) Item_datetime_literal(thd, &ltime,
status.precision);
break;
case MYSQL_TYPE_TIME:
if (!str_to_time(cs, str, length, &ltime, 0, &status) &&
ltime.time_type == MYSQL_TIMESTAMP_TIME &&
!have_important_literal_warnings(&status))
item= new (thd->mem_root) Item_time_literal(thd, &ltime,
status.precision);
break;
default:
DBUG_ASSERT(0);
}
if (likely(item))
{
if (status.warnings) // e.g. a note on nanosecond truncation
{
ErrConvString err(str, length, cs);
make_truncated_value_warning(thd,
Sql_condition::time_warn_level(status.warnings),
&err, ltime.time_type, 0);
}
return item;
}
if (send_error)
{
const char *typestr=
(type == MYSQL_TYPE_DATE) ? "DATE" :
(type == MYSQL_TYPE_TIME) ? "TIME" : "DATETIME";
ErrConvString err(str, length, thd->variables.character_set_client);
my_error(ER_WRONG_VALUE, MYF(0), typestr, err.ptr());
}
return NULL;
}
static List<Item> *create_func_dyncol_prepare(THD *thd,
DYNCALL_CREATE_DEF **dfs,
List<DYNCALL_CREATE_DEF> &list)
......
......@@ -191,21 +191,6 @@ class Create_udf_func : public Create_func
#endif
Item *create_temporal_literal(THD *thd,
const char *str, size_t length,
CHARSET_INFO *cs,
enum_field_types type,
bool send_error);
inline
Item *create_temporal_literal(THD *thd, const String *str,
enum_field_types type,
bool send_error)
{
return create_temporal_literal(thd,
str->ptr(), str->length(), str->charset(),
type, send_error);
}
struct Native_func_registry
{
LEX_CSTRING name;
......
......@@ -285,6 +285,32 @@ bool Type_std_attributes::count_string_length(const char *func_name,
}
/*
Find a handler by its ODBC literal data type.
@param type_str - data type name, not necessarily 0-terminated
@retval - a pointer to data type handler if type_str points
to a known ODBC literal data type, or NULL otherwise
*/
const Type_handler *
Type_handler::odbc_literal_type_handler(const LEX_CSTRING *type_str)
{
if (type_str->length == 1)
{
if (type_str->str[0] == 'd') // {d'2001-01-01'}
return &type_handler_newdate;
else if (type_str->str[0] == 't') // {t'10:20:30'}
return &type_handler_time2;
}
else if (type_str->length == 2) // {ts'2001-01-01 10:20:30'}
{
if (type_str->str[0] == 't' && type_str->str[1] == 's')
return &type_handler_datetime2;
}
return NULL; // Not a known ODBC literal type
}
/**
This method is used by:
- Item_user_var_as_out_param::field_type()
......@@ -6663,3 +6689,94 @@ int Type_handler_real_result::stored_field_cmp_to_item(THD *thd,
return 1;
return 0;
}
/***************************************************************************/
static bool have_important_literal_warnings(const MYSQL_TIME_STATUS *status)
{
return (status->warnings & ~MYSQL_TIME_NOTE_TRUNCATED) != 0;
}
static void literal_warn(THD *thd, const Item *item,
const char *str, size_t length, CHARSET_INFO *cs,
const MYSQL_TIME *ltime,
const MYSQL_TIME_STATUS *st,
const char *typestr, bool send_error)
{
if (likely(item))
{
if (st->warnings) // e.g. a note on nanosecond truncation
{
ErrConvString err(str, length, cs);
make_truncated_value_warning(thd,
Sql_condition::time_warn_level(st->warnings),
&err, ltime->time_type, 0);
}
}
else if (send_error)
{
ErrConvString err(str, length, cs);
my_error(ER_WRONG_VALUE, MYF(0), typestr, err.ptr());
}
}
Item_literal *
Type_handler_date_common::create_literal_item(THD *thd,
const char *str,
size_t length,
CHARSET_INFO *cs,
bool send_error) const
{
MYSQL_TIME_STATUS st;
MYSQL_TIME ltime;
Item_literal *item= NULL;
sql_mode_t flags= sql_mode_for_dates(thd);
if (!str_to_datetime(cs, str, length, &ltime, flags, &st) &&
ltime.time_type == MYSQL_TIMESTAMP_DATE && !st.warnings)
item= new (thd->mem_root) Item_date_literal(thd, &ltime);
literal_warn(thd, item, str, length, cs, &ltime, &st, "DATE", send_error);
return item;
}
Item_literal *
Type_handler_temporal_with_date::create_literal_item(THD *thd,
const char *str,
size_t length,
CHARSET_INFO *cs,
bool send_error) const
{
MYSQL_TIME_STATUS st;
MYSQL_TIME ltime;
Item_literal *item= NULL;
sql_mode_t flags= sql_mode_for_dates(thd);
if (!str_to_datetime(cs, str, length, &ltime, flags, &st) &&
ltime.time_type == MYSQL_TIMESTAMP_DATETIME &&
!have_important_literal_warnings(&st))
item= new (thd->mem_root) Item_datetime_literal(thd, &ltime, st.precision);
literal_warn(thd, item, str, length, cs, &ltime, &st, "DATETIME", send_error);
return item;
}
Item_literal *
Type_handler_time_common::create_literal_item(THD *thd,
const char *str,
size_t length,
CHARSET_INFO *cs,
bool send_error) const
{
MYSQL_TIME_STATUS st;
MYSQL_TIME ltime;
Item_literal *item= NULL;
if (!str_to_time(cs, str, length, &ltime, 0, &st) &&
ltime.time_type == MYSQL_TIMESTAMP_TIME &&
!have_important_literal_warnings(&st))
item= new (thd->mem_root) Item_time_literal(thd, &ltime, st.precision);
literal_warn(thd, item, str, length, cs, &ltime, &st, "TIME", send_error);
return item;
}
......@@ -31,6 +31,7 @@ class Column_definition;
class Column_definition_attributes;
class Item;
class Item_const;
class Item_literal;
class Item_param;
class Item_cache;
class Item_func_or_sum;
......@@ -1077,6 +1078,7 @@ class Type_handler
enum_field_types type)
const;
public:
static const Type_handler *odbc_literal_type_handler(const LEX_CSTRING *str);
static const Type_handler *blob_type_handler(uint max_octet_length);
static const Type_handler *string_type_handler(uint max_octet_length);
static const Type_handler *bit_and_int_mixture_handler(uint max_char_len);
......@@ -1416,6 +1418,32 @@ class Type_handler
Item *src,
const Item *cmp) const= 0;
virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0;
/**
A builder for literals with data type name prefix, e.g.:
TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'.
@param thd The current thread
@param str Character literal
@param length Length of str
@param cs Character set of the string
@param send_error Whether to generate an error on failure
@retval A pointer to a new Item on success
NULL on error (wrong literal value, EOM)
*/
virtual Item_literal *create_literal_item(THD *thd,
const char *str, size_t length,
CHARSET_INFO *cs,
bool send_error) const
{
DBUG_ASSERT(0);
return NULL;
}
Item_literal *create_literal_item(THD *thd, const String *str,
bool send_error) const
{
return create_literal_item(thd, str->ptr(), str->length(), str->charset(),
send_error);
}
virtual Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const
{
......@@ -2894,6 +2922,8 @@ class Type_handler_time_common: public Type_handler_temporal_result
{
return MYSQL_TIMESTAMP_TIME;
}
Item_literal *create_literal_item(THD *thd, const char *str, size_t length,
CHARSET_INFO *cs, bool send_error) const;
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const;
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
......@@ -3008,6 +3038,8 @@ class Type_handler_temporal_with_date: public Type_handler_temporal_result
{
public:
virtual ~Type_handler_temporal_with_date() {}
Item_literal *create_literal_item(THD *thd, const char *str, size_t length,
CHARSET_INFO *cs, bool send_error) const;
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
Item *a, Item *b) const;
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
......@@ -3037,6 +3069,8 @@ class Type_handler_date_common: public Type_handler_temporal_with_date
{
return MYSQL_TIMESTAMP_DATE;
}
Item_literal *create_literal_item(THD *thd, const char *str, size_t length,
CHARSET_INFO *cs, bool send_error) const;
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
......
......@@ -9259,8 +9259,9 @@ history_point:
TIMESTAMP TEXT_STRING
{
Item *item;
if (!(item= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
MYSQL_TYPE_DATETIME, true)))
if (!(item= type_handler_datetime.create_literal_item(thd,
$2.str, $2.length,
YYCSCL, true)))
MYSQL_YYABORT;
$$= Vers_history_point(VERS_TIMESTAMP, item);
}
......@@ -14801,26 +14802,23 @@ NUM_literal:
temporal_literal:
DATE_SYM TEXT_STRING
{
if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
YYCSCL,
MYSQL_TYPE_DATE,
true))))
if (unlikely(!($$= type_handler_newdate.create_literal_item(thd,
$2.str, $2.length,
YYCSCL, true))))
MYSQL_YYABORT;
}
| TIME_SYM TEXT_STRING
{
if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
YYCSCL,
MYSQL_TYPE_TIME,
true))))
if (unlikely(!($$= type_handler_time2.create_literal_item(thd,
$2.str, $2.length,
YYCSCL, true))))
MYSQL_YYABORT;
}
| TIMESTAMP TEXT_STRING
{
if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
YYCSCL,
MYSQL_TYPE_DATETIME,
true))))
if (unlikely(!($$= type_handler_datetime.create_literal_item(thd,
$2.str, $2.length,
YYCSCL, true))))
MYSQL_YYABORT;
}
;
......
......@@ -9387,8 +9387,9 @@ history_point:
TIMESTAMP TEXT_STRING
{
Item *item;
if (!(item= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
MYSQL_TYPE_DATETIME, true)))
if (!(item= type_handler_datetime2.create_literal_item(thd,
$2.str, $2.length,
YYCSCL, true)))
MYSQL_YYABORT;
$$= Vers_history_point(VERS_TIMESTAMP, item);
}
......@@ -15046,26 +15047,23 @@ NUM_literal:
temporal_literal:
DATE_SYM TEXT_STRING
{
if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
YYCSCL,
MYSQL_TYPE_DATE,
true))))
if (unlikely(!($$= type_handler_newdate.create_literal_item(thd,
$2.str, $2.length,
YYCSCL, true))))
MYSQL_YYABORT;
}
| TIME_SYM TEXT_STRING
{
if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
YYCSCL,
MYSQL_TYPE_TIME,
true))))
if (unlikely(!($$= type_handler_time2.create_literal_item(thd,
$2.str, $2.length,
YYCSCL, true))))
MYSQL_YYABORT;
}
| TIMESTAMP TEXT_STRING
{
if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
YYCSCL,
MYSQL_TYPE_DATETIME,
true))))
if (unlikely(!($$= type_handler_datetime2.create_literal_item(thd,
$2.str, $2.length,
YYCSCL, true))))
MYSQL_YYABORT;
}
;
......
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