summaryrefslogtreecommitdiff
path: root/sql/item.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item.cc')
-rw-r--r--sql/item.cc729
1 files changed, 351 insertions, 378 deletions
diff --git a/sql/item.cc b/sql/item.cc
index 3d3f472afc5..bed8824f68f 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -19,9 +19,8 @@
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include <mysql.h>
#include <m_ctype.h>
#include "my_dir.h"
@@ -41,7 +40,6 @@
// REPORT_EXCEPT_NOT_FOUND,
// find_item_in_list,
// RESOLVED_AGAINST_ALIAS, ...
-#include "log_event.h" // append_query_string
#include "sql_expression_cache.h"
const String my_null_string("NULL", 4, default_charset_info);
@@ -105,7 +103,7 @@ void
Hybrid_type_traits_decimal::fix_length_and_dec(Item *item, Item *arg) const
{
item->decimals= arg->decimals;
- item->max_length= min(arg->max_length + DECIMAL_LONGLONG_DIGITS,
+ item->max_length= MY_MIN(arg->max_length + DECIMAL_LONGLONG_DIGITS,
DECIMAL_MAX_STR_LENGTH);
}
@@ -198,7 +196,7 @@ Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const
void item_init(void)
{
- item_user_lock_init();
+ item_func_sleep_init();
uuid_short_init();
}
@@ -234,6 +232,36 @@ bool Item::val_bool()
}
+/**
+ Get date/time/datetime.
+ Optionally extend TIME result to DATETIME.
+*/
+bool Item::get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ /*
+ Some TIME type items return error when trying to do get_date()
+ without TIME_TIME_ONLY set (e.g. Item_field for Field_time).
+ In the SQL standard time->datetime conversion mode we add TIME_TIME_ONLY.
+ In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY
+ and leave it to get_date() to check date.
+ */
+ ulonglong time_flag= (field_type() == MYSQL_TYPE_TIME &&
+ !(current_thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ?
+ TIME_TIME_ONLY : 0;
+ if (get_date(ltime, fuzzydate | time_flag))
+ return true;
+ if (ltime->time_type == MYSQL_TIMESTAMP_TIME &&
+ !(fuzzydate & TIME_TIME_ONLY))
+ {
+ MYSQL_TIME tmp;
+ if (time_to_datetime_with_warn(current_thd, ltime, &tmp, fuzzydate))
+ return null_value= true;
+ *ltime= tmp;
+ }
+ return false;
+}
+
+
/*
For the items which don't have its own fast val_str_ascii()
implementation we provide a generic slower version,
@@ -312,11 +340,29 @@ String *Item::val_string_from_decimal(String *str)
}
+/*
+ All val_xxx_from_date() must call this method, to expose consistent behaviour
+ regarding SQL_MODE when converting DATE/DATETIME to other data types.
+*/
+bool Item::get_temporal_with_sql_mode(MYSQL_TIME *ltime)
+{
+ return get_date(ltime, field_type() == MYSQL_TYPE_TIME
+ ? TIME_TIME_ONLY
+ : sql_mode_for_dates(current_thd));
+}
+
+
+bool Item::is_null_from_temporal()
+{
+ MYSQL_TIME ltime;
+ return get_temporal_with_sql_mode(&ltime);
+}
+
+
String *Item::val_string_from_date(String *str)
{
MYSQL_TIME ltime;
- if (get_date(&ltime,
- field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0) ||
+ if (get_temporal_with_sql_mode(&ltime) ||
str->alloc(MAX_DATE_STRING_REP_LENGTH))
{
null_value= 1;
@@ -360,7 +406,7 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value)
decimal_value) & E_DEC_BAD_NUM)
{
ErrConvString err(res);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL",
err.ptr());
@@ -373,7 +419,7 @@ my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_temporal_with_sql_mode(&ltime))
{
my_decimal_set_zero(decimal_value);
null_value= 1; // set NULL, stop processing
@@ -400,7 +446,7 @@ longlong Item::val_int_from_date()
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_temporal_with_sql_mode(&ltime))
return 0;
longlong v= TIME_to_ulonglong(&ltime);
return ltime.neg ? -v : v;
@@ -411,7 +457,7 @@ double Item::val_real_from_date()
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_temporal_with_sql_mode(&ltime))
return 0;
return TIME_to_double(&ltime);
}
@@ -453,9 +499,7 @@ int Item::save_time_in_field(Field *field)
int Item::save_date_in_field(Field *field)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, (current_thd->variables.sql_mode &
- (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
- MODE_INVALID_DATES))))
+ if (get_date(&ltime, sql_mode_for_dates(current_thd)))
return set_field_to_null_with_conversions(field, 0);
field->set_notnull();
return field->store_time_dec(&ltime, decimals);
@@ -570,26 +614,10 @@ uint Item::decimal_precision() const
uint prec=
my_decimal_length_to_precision(max_char_length(), decimals,
unsigned_flag);
- return min(prec, DECIMAL_MAX_PRECISION);
- }
- return min(max_char_length(), DECIMAL_MAX_PRECISION);
-}
-
-
-#if MARIADB_VERSION_ID < 1000000
-static uint ms_to_precision(uint ms)
-{
- uint cut, precision;
- for (cut= 10, precision= 6 ; precision > 0 ; cut*= 10, precision--)
- {
- if (ms % cut)
- return precision;
+ return MY_MIN(prec, DECIMAL_MAX_PRECISION);
}
- return 0;
+ return MY_MIN(max_char_length(), DECIMAL_MAX_PRECISION);
}
-#else
-#error Change the code to use MYSQL_TIME_STATUS::precision instead.
-#endif
uint Item::temporal_precision(enum_field_types type)
@@ -599,18 +627,17 @@ uint Item::temporal_precision(enum_field_types type)
{
MYSQL_TIME ltime;
String buf, *tmp;
- int was_cut;
+ MYSQL_TIME_STATUS status;
DBUG_ASSERT(fixed);
if ((tmp= val_str(&buf)) &&
- (type == MYSQL_TYPE_TIME ?
+ !(type == MYSQL_TYPE_TIME ?
str_to_time(tmp->charset(), tmp->ptr(), tmp->length(),
- &ltime, TIME_TIME_ONLY, &was_cut) :
+ &ltime, TIME_TIME_ONLY, &status) :
str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(),
- &ltime, TIME_FUZZY_DATES, &was_cut)) >
- MYSQL_TIMESTAMP_ERROR)
- return min(ms_to_precision(ltime.second_part), TIME_SECOND_PART_DIGITS);
+ &ltime, TIME_FUZZY_DATES, &status)))
+ return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS);
}
- return min(decimals, TIME_SECOND_PART_DIGITS);
+ return MY_MIN(decimals, TIME_SECOND_PART_DIGITS);
}
@@ -658,7 +685,7 @@ void Item::cleanup()
{
DBUG_ENTER("Item::cleanup");
DBUG_PRINT("enter", ("this: %p", this));
- fixed=0;
+ fixed= 0;
marker= 0;
join_tab_idx= MAX_TABLES;
if (orig_name)
@@ -728,9 +755,12 @@ Item_result Item::cmp_type() const
case MYSQL_TYPE_GEOMETRY:
return STRING_RESULT;
case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP2:
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_TIME2:
case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_DATETIME2:
case MYSQL_TYPE_NEWDATE:
return TIME_RESULT;
};
@@ -866,7 +896,7 @@ void Item_ident::cleanup()
We can trust that depended_from set correctly only if this item
was fixed
*/
- can_be_depended= test(depended_from);
+ can_be_depended= MY_TEST(depended_from);
}
DBUG_VOID_RETURN;
}
@@ -884,10 +914,15 @@ bool Item_ident::remove_dependence_processor(uchar * arg)
bool Item_ident::collect_outer_ref_processor(uchar *param)
{
Collect_deps_prm *prm= (Collect_deps_prm *)param;
- if (depended_from &&
+ if (depended_from &&
depended_from->nest_level_base == prm->nest_level_base &&
depended_from->nest_level < prm->nest_level)
- prm->parameters->add_unique(this, &cmp_items);
+ {
+ if (prm->collect)
+ prm->parameters->add_unique(this, &cmp_items);
+ else
+ prm->count++;
+ }
return FALSE;
}
@@ -1037,10 +1072,15 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
name_length= 0;
return;
}
- if (cs->ctype)
- {
- const char *str_start= str;
+ const char *str_start= str;
+ if (!cs->ctype || cs->mbminlen > 1)
+ {
+ str+= cs->cset->scan(cs, str, str + length, MY_SEQ_SPACES);
+ length-= str - str_start;
+ }
+ else
+ {
/*
This will probably need a better implementation in the future:
a function in CHARSET_INFO structure.
@@ -1050,21 +1090,21 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
length--;
str++;
}
- if (str != str_start && !is_autogenerated_name)
- {
- char buff[SAFE_NAME_LEN];
- strmake(buff, str_start,
- min(sizeof(buff)-1, length + (int) (str-str_start)));
-
- if (length == 0)
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY),
- buff);
- else
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES),
- buff);
- }
+ }
+ if (str != str_start && !is_autogenerated_name)
+ {
+ char buff[SAFE_NAME_LEN];
+ strmake(buff, str_start,
+ MY_MIN(sizeof(buff)-1, length + (int) (str-str_start)));
+
+ if (length == 0)
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY),
+ buff);
+ else
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES),
+ buff);
}
if (!my_charset_same(cs, system_charset_info))
{
@@ -1075,7 +1115,7 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
name_length= res_length;
}
else
- name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME)));
+ name= sql_strmake(str, (name_length= MY_MIN(length,MAX_ALIAS_NAME)));
}
@@ -1130,6 +1170,8 @@ bool Item::eq(const Item *item, bool binary_cmp) const
Item *Item::safe_charset_converter(CHARSET_INFO *tocs)
{
+ if (!needs_charset_converter(tocs))
+ return this;
Item_func_conv_charset *conv= new Item_func_conv_charset(this, tocs, 1);
return conv->safe ? conv : NULL;
}
@@ -1192,62 +1234,55 @@ Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs)
if (!(tocs->state & MY_CS_NONASCII))
return this;
- Item_string *conv;
- uint conv_errors;
- char buf[64], buf2[64];
- String tmp(buf, sizeof(buf), &my_charset_bin);
- String cstr(buf2, sizeof(buf2), &my_charset_bin);
- String *ostr= val_str(&tmp);
- char *ptr;
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
- {
- /*
- Safe conversion is not possible (or EOM).
- We could not convert a string into the requested character set
- without data loss. The target charset does not cover all the
- characters from the string. Operation cannot be done correctly.
- */
- return NULL;
- }
- if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length())))
- return NULL;
- conv->str_value.set(ptr, cstr.length(), cstr.charset());
- /* Ensure that no one is going to change the result string */
- conv->str_value.mark_as_const();
- conv->fix_char_length(max_char_length());
+ Item *conv;
+ if ((conv= const_charset_converter(tocs, true)))
+ conv->fix_char_length(max_char_length());
return conv;
}
-Item *Item_static_float_func::safe_charset_converter(CHARSET_INFO *tocs)
+/**
+ Create character set converter for constant items
+ using Item_null, Item_string or Item_static_string_func.
+
+ @param tocs Character set to to convert the string to.
+ @param lossless Whether data loss is acceptable.
+ @param func_name Function name, or NULL.
+
+ @return this, if conversion is not needed,
+ NULL, if safe conversion is not possible, or
+ a new item representing the converted constant.
+*/
+Item *Item::const_charset_converter(CHARSET_INFO *tocs,
+ bool lossless,
+ const char *func_name)
{
- Item_string *conv;
- char buf[64];
- String *s, tmp(buf, sizeof(buf), &my_charset_bin);
- s= val_str(&tmp);
- if ((conv= new Item_static_string_func(func_name, s->ptr(), s->length(),
- s->charset())))
+ DBUG_ASSERT(const_item());
+ DBUG_ASSERT(fixed);
+ StringBuffer<64>tmp;
+ String *s= val_str(&tmp);
+ if (!s)
+ return new Item_null((char *) func_name, tocs);
+
+ if (!needs_charset_converter(s->length(), tocs))
{
- conv->str_value.copy();
- conv->str_value.mark_as_const();
+ if (collation.collation == &my_charset_bin && tocs != &my_charset_bin &&
+ !this->check_well_formed_result(s, true))
+ return NULL;
+ return this;
}
- return conv;
-}
-
-Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
uint conv_errors;
- char *ptr;
- String tmp, cstr, *ostr= val_str(&tmp);
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
+ Item_string *conv= func_name ?
+ new Item_static_string_func(func_name,
+ s, tocs, &conv_errors,
+ collation.derivation,
+ collation.repertoire) :
+ new Item_string(s, tocs, &conv_errors,
+ collation.derivation,
+ collation.repertoire);
+
+ if (!conv || (conv_errors && lossless))
{
/*
Safe conversion is not possible (or EOM).
@@ -1257,70 +1292,28 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
*/
return NULL;
}
- if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length())))
+ if (s->charset() == &my_charset_bin && tocs != &my_charset_bin &&
+ !conv->check_well_formed_result(true))
return NULL;
- conv->str_value.set(ptr, cstr.length(), cstr.charset());
- /* Ensure that no one is going to change the result string */
- conv->str_value.mark_as_const();
return conv;
}
Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs)
{
- if (const_item())
- {
- uint cnv_errors;
- String *ostr= val_str(&cnvstr);
- cnvitem->str_value.copy(ostr->ptr(), ostr->length(),
- ostr->charset(), tocs, &cnv_errors);
- if (cnv_errors)
- return NULL;
- cnvitem->str_value.mark_as_const();
- cnvitem->max_length= cnvitem->str_value.numchars() * tocs->mbmaxlen;
- return cnvitem;
- }
- return Item::safe_charset_converter(tocs);
-}
-
-
-Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
- uint conv_errors;
- String tmp, cstr, *ostr= val_str(&tmp);
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors ||
- !(conv= new Item_static_string_func(func_name,
- cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
- {
- /*
- Safe conversion is not possible (or EOM).
- We could not convert a string into the requested character set
- without data loss. The target charset does not cover all the
- characters from the string. Operation cannot be done correctly.
- */
- return NULL;
- }
- conv->str_value.copy();
- /* Ensure that no one is going to change the result string */
- conv->str_value.mark_as_const();
- return conv;
-}
+ /*
+ Return "this" if in prepare. result_type may change at execition time,
+ to it's possible that the converter will not be needed at all:
+ PREPARE stmt FROM 'SELECT * FROM t1 WHERE field = ?';
+ SET @@arg= 1;
+ EXECUTE stms USING @arg;
-bool Item_string::eq(const Item *item, bool binary_cmp) const
-{
- if (type() == item->type() && item->basic_const_item())
- {
- if (binary_cmp)
- return !stringcmp(&str_value, &item->str_value);
- return (collation.collation == item->collation.collation &&
- !sortcmp(&str_value, &item->str_value, collation.collation));
- }
- return 0;
+ In the above example result_type is STRING_RESULT at prepare time,
+ and INT_RESULT at execution time.
+ */
+ return !const_item() || state == NULL_VALUE ?
+ this : const_charset_converter(tocs, true);
}
@@ -1379,7 +1372,7 @@ bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
+ ltime, fuzzydate))
goto err;
break;
}
@@ -2191,33 +2184,10 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname,
for (i= 0, arg= args; i < nargs; i++, arg+= item_sep)
{
- Item* conv;
- uint32 dummy_offset;
- if (!String::needs_conversion(1, (*arg)->collation.collation,
- coll.collation,
- &dummy_offset))
- continue;
-
- /*
- No needs to add converter if an "arg" is NUMERIC or DATETIME
- value (which is pure ASCII) and at the same time target DTCollation
- is ASCII-compatible. For example, no needs to rewrite:
- SELECT * FROM t1 WHERE datetime_field = '2010-01-01';
- to
- SELECT * FROM t1 WHERE CONVERT(datetime_field USING cs) = '2010-01-01';
-
- TODO: avoid conversion of any values with
- repertoire ASCII and 7bit-ASCII-compatible,
- not only numeric/datetime origin.
- */
- if ((*arg)->collation.derivation == DERIVATION_NUMERIC &&
- (*arg)->collation.repertoire == MY_REPERTOIRE_ASCII &&
- !((*arg)->collation.collation->state & MY_CS_NONASCII) &&
- !(coll.collation->state & MY_CS_NONASCII))
+ Item* conv= (*arg)->safe_charset_converter(coll.collation);
+ if (conv == *arg)
continue;
-
- if (!(conv= (*arg)->safe_charset_converter(coll.collation)) &&
- ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII))
+ if (!conv && ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII))
conv= new Item_func_conv_charset(*arg, coll.collation, 1);
if (!conv)
@@ -2475,7 +2445,7 @@ void Item_field::set_field(Field *field_par)
field_name= field_par->field_name;
db_name= field_par->table->s->db.str;
alias_name_used= field_par->table->alias_name_used;
- unsigned_flag=test(field_par->flags & UNSIGNED_FLAG);
+ unsigned_flag= MY_TEST(field_par->flags & UNSIGNED_FLAG);
collation.set(field_par->charset(), field_par->derivation(),
field_par->repertoire());
fix_char_length(field_par->char_length());
@@ -3003,7 +2973,7 @@ String *Item_float::val_str(String *str)
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
- str->set_real(value,decimals,&my_charset_bin);
+ str->set_real(value, decimals, &my_charset_numeric);
return str;
}
@@ -3098,7 +3068,7 @@ double_from_string_with_check(CHARSET_INFO *cs, const char *cptr,
We can use err.ptr() here as ErrConvString is guranteed to put an
end \0 here.
*/
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE",
err.ptr());
@@ -3135,7 +3105,7 @@ longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr,
(end != end_of_num && !check_if_only_end_space(cs, end_of_num, end))))
{
ErrConvString err(cptr, end - cptr, cs);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
err.ptr());
@@ -3162,10 +3132,6 @@ my_decimal *Item_string::val_decimal(my_decimal *decimal_value)
}
-bool Item_null::eq(const Item *item, bool binary_cmp) const
-{ return item->type() == type(); }
-
-
double Item_null::val_real()
{
// following assert is redundant, because fixed=1 assigned in constructor
@@ -3235,8 +3201,6 @@ Item_param::Item_param(uint pos_in_query_arg) :
value is set.
*/
maybe_null= 1;
- cnvitem= new Item_string("", 0, &my_charset_bin, DERIVATION_COERCIBLE);
- cnvstr.set(cnvbuf, sizeof(cnvbuf), &my_charset_bin);
}
@@ -3341,14 +3305,10 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type,
value.time= *tm;
value.time.time_type= time_type;
- if (value.time.year > 9999 || value.time.month > 12 ||
- value.time.day > 31 ||
- (time_type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23) ||
- value.time.minute > 59 || value.time.second > 59 ||
- value.time.second_part > TIME_MAX_SECOND_PART)
+ if (check_datetime_range(&value.time))
{
ErrConvTime str(&value.time);
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
&str, time_type, 0);
set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR);
}
@@ -3607,7 +3567,7 @@ double Item_param::val_real()
This works for example when user says SELECT ?+0.0 and supplies
time value for the placeholder.
*/
- return ulonglong2double(TIME_to_ulonglong(&value.time));
+ return TIME_to_double(&value.time);
case NULL_VALUE:
return 0.0;
default:
@@ -3665,9 +3625,7 @@ my_decimal *Item_param::val_decimal(my_decimal *dec)
return dec;
case TIME_VALUE:
{
- longlong i= (longlong) TIME_to_ulonglong(&value.time);
- int2my_decimal(E_DEC_FATAL_ERROR, i, 0, dec);
- return dec;
+ return TIME_to_my_decimal(&value.time, dec);
}
case NULL_VALUE:
return 0;
@@ -3761,8 +3719,9 @@ const String *Item_param::query_val_str(THD *thd, String* str) const
case LONG_DATA_VALUE:
{
str->length(0);
- append_query_string(thd, value.cs_info.character_set_client, &str_value,
- str);
+ append_query_string(value.cs_info.character_set_client, str,
+ str_value.ptr(), str_value.length(),
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
break;
}
case NULL_VALUE:
@@ -3801,18 +3760,14 @@ bool Item_param::convert_str_value(THD *thd)
str_value.set_charset(value.cs_info.final_character_set_of_str_value);
/* Here str_value is guaranteed to be in final_character_set_of_str_value */
- max_length= str_value.numchars() * str_value.charset()->mbmaxlen;
-
- /* For the strings converted to numeric form within some functions */
- decimals= NOT_FIXED_DEC;
/*
str_value_ptr is returned from val_str(). It must be not alloced
to prevent it's modification by val_str() invoker.
*/
str_value_ptr.set(str_value.ptr(), str_value.length(),
str_value.charset());
- /* Synchronize item charset with value charset */
- collation.set(str_value.charset(), DERIVATION_COERCIBLE);
+ /* Synchronize item charset and length with value charset */
+ fix_charset_and_length_from_str_value(DERIVATION_COERCIBLE);
}
return rc;
}
@@ -3842,7 +3797,8 @@ Item_param::clone_item()
case STRING_VALUE:
case LONG_DATA_VALUE:
return new Item_string(name, str_value.c_ptr_quick(), str_value.length(),
- str_value.charset());
+ str_value.charset(),
+ collation.derivation, collation.repertoire);
case TIME_VALUE:
break;
case NO_VALUE:
@@ -3854,30 +3810,21 @@ Item_param::clone_item()
bool
-Item_param::eq(const Item *arg, bool binary_cmp) const
+Item_param::eq(const Item *item, bool binary_cmp) const
{
- Item *item;
- if (!basic_const_item() || !arg->basic_const_item() || arg->type() != type())
+ if (!basic_const_item())
return FALSE;
- /*
- We need to cast off const to call val_int(). This should be OK for
- a basic constant.
- */
- item= (Item*) arg;
switch (state) {
case NULL_VALUE:
- return TRUE;
+ return null_eq(item);
case INT_VALUE:
- return value.integer == item->val_int() &&
- unsigned_flag == item->unsigned_flag;
+ return int_eq(value.integer, item);
case REAL_VALUE:
- return value.real == item->val_real();
+ return real_eq(value.real, item);
case STRING_VALUE:
case LONG_DATA_VALUE:
- if (binary_cmp)
- return !stringcmp(&str_value, &item->str_value);
- return !sortcmp(&str_value, &item->str_value, collation.collation);
+ return str_eq(&str_value, item, binary_cmp);
default:
break;
}
@@ -4137,8 +4084,8 @@ double Item_copy_string::val_real()
longlong Item_copy_string::val_int()
{
int err;
- return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(),
- str_value.length(),10, (char**) 0,
+ return null_value ? 0 : my_strntoll(str_value.charset(),str_value.ptr(),
+ str_value.length(), 10, (char**) 0,
&err);
}
@@ -4308,7 +4255,7 @@ double Item_copy_decimal::val_real()
longlong Item_copy_decimal::val_int()
{
if (null_value)
- return LL(0);
+ return 0;
else
{
longlong result;
@@ -4434,7 +4381,7 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
resolved_item->db_name : "");
const char *table_name= (resolved_item->table_name ?
resolved_item->table_name : "");
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_WARN_FIELD_RESOLVED, ER(ER_WARN_FIELD_RESOLVED),
db_name, (db_name[0] ? "." : ""),
table_name, (table_name [0] ? "." : ""),
@@ -4682,7 +4629,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
!((*group_by_ref)->eq(*select_ref, 0)))
{
ambiguous_fields= TRUE;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR,
ER(ER_NON_UNIQ_ERROR), ref->full_name(),
current_thd->where);
@@ -5390,13 +5337,6 @@ bool Item_field::vcol_in_partition_func_processor(uchar *int_arg)
}
-Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs)
-{
- no_const_subst= 1;
- return Item::safe_charset_converter(tocs);
-}
-
-
void Item_field::cleanup()
{
DBUG_ENTER("Item_field::cleanup");
@@ -5714,10 +5654,7 @@ String *Item::check_well_formed_result(String *str, bool send_error)
{
/* Check whether we got a well-formed string */
CHARSET_INFO *cs= str->charset();
- int well_formed_error;
- uint wlen= cs->cset->well_formed_len(cs,
- str->ptr(), str->ptr() + str->length(),
- str->length(), &well_formed_error);
+ uint wlen= str->well_formed_length();
if (wlen < str->length())
{
THD *thd= current_thd;
@@ -5731,8 +5668,7 @@ String *Item::check_well_formed_result(String *str, bool send_error)
cs->csname, hexbuf);
return 0;
}
- if ((thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
+ if (thd->is_strict_mode())
{
null_value= 1;
str= 0;
@@ -5741,7 +5677,7 @@ String *Item::check_well_formed_result(String *str, bool send_error)
{
str->length(wlen);
}
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_INVALID_CHARACTER_STRING,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_INVALID_CHARACTER_STRING,
ER(ER_INVALID_CHARACTER_STRING), cs->csname, hexbuf);
}
return str;
@@ -5877,29 +5813,23 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length)
field= new Field_double((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
name, decimals, 0, unsigned_flag);
break;
- case MYSQL_TYPE_NULL:
- field= new Field_null((uchar*) 0, max_length, Field::NONE,
- name, &my_charset_bin);
- break;
case MYSQL_TYPE_INT24:
field= new Field_medium((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
name, 0, unsigned_flag);
break;
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_DATE:
- field= new Field_newdate(0, null_ptr, 0, Field::NONE, name, &my_charset_bin);
+ field= new Field_newdate(0, null_ptr, 0, Field::NONE, name);
break;
case MYSQL_TYPE_TIME:
- field= new_Field_time(0, null_ptr, 0, Field::NONE, name,
- decimals, &my_charset_bin);
+ field= new_Field_time(0, null_ptr, 0, Field::NONE, name, decimals);
break;
case MYSQL_TYPE_TIMESTAMP:
field= new_Field_timestamp(0, null_ptr, 0,
- Field::NONE, name, 0, decimals, &my_charset_bin);
+ Field::NONE, name, 0, decimals);
break;
case MYSQL_TYPE_DATETIME:
- field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name,
- decimals, &my_charset_bin);
+ field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name, decimals);
break;
case MYSQL_TYPE_YEAR:
field= new Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
@@ -5913,6 +5843,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length)
/* This case should never be chosen */
DBUG_ASSERT(0);
/* If something goes awfully wrong, it's better to get a string than die */
+ case MYSQL_TYPE_NULL:
case MYSQL_TYPE_STRING:
if (fixed_length && !too_big_for_varchar())
{
@@ -6013,13 +5944,51 @@ static int save_field_in_field(Field *from, bool *null_value,
}
+static int memcpy_field_value(Field *to, Field *from)
+{
+ if (to->ptr != from->ptr)
+ memcpy(to->ptr,from->ptr, to->pack_length());
+ return 0;
+}
+
+fast_field_copier Item_field::setup_fast_field_copier(Field *to)
+{
+ DBUG_ENTER("Item_field::setup_fast_field_copier");
+ DBUG_RETURN(memcpy_field_possible(to, field) ?
+ &memcpy_field_value :
+ &field_conv_incompatible);
+}
+
+
/**
Set a field's value from a item.
*/
-void Item_field::save_org_in_field(Field *to)
+void Item_field::save_org_in_field(Field *to,
+ fast_field_copier fast_field_copier_func)
{
- save_field_in_field(field, &null_value, to, TRUE);
+ DBUG_ENTER("Item_field::save_org_in_field");
+ DBUG_PRINT("enter", ("setup: 0x%lx data: 0x%lx",
+ (ulong) to, (ulong) fast_field_copier_func));
+ if (fast_field_copier_func)
+ {
+ if (field->is_null())
+ {
+ null_value= TRUE;
+ set_field_to_null_with_conversions(to, TRUE);
+ DBUG_VOID_RETURN;
+ }
+ to->set_notnull();
+ if (to == field)
+ {
+ null_value= 0;
+ DBUG_VOID_RETURN;
+ }
+ (*fast_field_copier_func)(to, field);
+ }
+ else
+ save_field_in_field(field, &null_value, to, TRUE);
+ DBUG_VOID_RETURN;
}
@@ -6173,24 +6142,6 @@ int Item_decimal::save_in_field(Field *field, bool no_conversions)
}
-bool Item_int::eq(const Item *arg, bool binary_cmp) const
-{
- /* No need to check for null value as basic constant can't be NULL */
- if (arg->basic_const_item() && arg->type() == type())
- {
- /*
- We need to cast off const to call val_int(). This should be OK for
- a basic constant.
- */
- Item *item= (Item*) arg;
- return (item->val_int() == value &&
- ((longlong) value >= 0 ||
- (item->unsigned_flag == unsigned_flag)));
- }
- return FALSE;
-}
-
-
Item *Item_int_with_ref::clone_item()
{
DBUG_ASSERT(ref->const_item());
@@ -6308,27 +6259,6 @@ void Item_float::print(String *str, enum_query_type query_type)
}
-/*
- hex item
- In string context this is a binary string.
- In number context this is a longlong value.
-*/
-
-bool Item_float::eq(const Item *arg, bool binary_cmp) const
-{
- if (arg->basic_const_item() && arg->type() == type())
- {
- /*
- We need to cast off const to call val_int(). This should be OK for
- a basic constant.
- */
- Item *item= (Item*) arg;
- return item->val_real() == value;
- }
- return FALSE;
-}
-
-
inline uint char_val(char X)
{
return (uint) (X >= '0' && X <= '9' ? X-'0' :
@@ -6366,7 +6296,7 @@ longlong Item_hex_hybrid::val_int()
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
char *end=(char*) str_value.ptr()+str_value.length(),
- *ptr=end-min(str_value.length(),sizeof(longlong));
+ *ptr=end-MY_MIN(str_value.length(),sizeof(longlong));
ulonglong value=0;
for (; ptr != end ; ptr++)
@@ -6384,8 +6314,6 @@ int Item_hex_hybrid::save_in_field(Field *field, bool no_conversions)
ulonglong nr;
uint32 length= str_value.length();
- if (!length)
- return 1;
if (length > 8)
{
@@ -6402,7 +6330,7 @@ int Item_hex_hybrid::save_in_field(Field *field, bool no_conversions)
warn:
if (!field->store((longlong) nr, TRUE))
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE,
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE,
1);
return 1;
}
@@ -6410,7 +6338,7 @@ warn:
void Item_hex_hybrid::print(String *str, enum_query_type query_type)
{
- uint32 len= min(str_value.length(), sizeof(longlong));
+ uint32 len= MY_MIN(str_value.length(), sizeof(longlong));
const char *ptr= str_value.ptr() + str_value.length() - len;
str->append("0x");
str->append_hex(ptr, len);
@@ -6425,32 +6353,6 @@ void Item_hex_string::print(String *str, enum_query_type query_type)
}
-bool Item_hex_constant::eq(const Item *arg, bool binary_cmp) const
-{
- if (arg->basic_const_item() && arg->type() == type() &&
- arg->cast_to_int_type() == cast_to_int_type())
- {
- if (binary_cmp)
- return !stringcmp(&str_value, &arg->str_value);
- return !sortcmp(&str_value, &arg->str_value, collation.collation);
- }
- return FALSE;
-}
-
-
-Item *Item_hex_constant::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
- String tmp, *str= val_str(&tmp);
-
- if (!(conv= new Item_string(str->ptr(), str->length(), tocs)))
- return NULL;
- conv->str_value.copy();
- conv->str_value.mark_as_const();
- return conv;
-}
-
-
/*
bin item.
In string context this is a binary string.
@@ -6495,6 +6397,78 @@ Item_bin_string::Item_bin_string(const char *str, uint str_length)
}
+bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const
+{
+ return
+ item->basic_const_item() && type() == item->type() &&
+ field_type() == ((Item_temporal_literal *) item)->field_type() &&
+ !my_time_compare(&cached_time,
+ &((Item_temporal_literal *) item)->cached_time);
+}
+
+
+void Item_date_literal::print(String *str, enum_query_type query_type)
+{
+ str->append("DATE'");
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ my_date_to_str(&cached_time, buf);
+ str->append(buf);
+ str->append('\'');
+}
+
+
+bool Item_date_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+{
+ DBUG_ASSERT(fixed);
+ fuzzy_date |= sql_mode_for_dates(current_thd);
+ *ltime= cached_time;
+ return (null_value= check_date_with_warn(ltime, fuzzy_date,
+ MYSQL_TIMESTAMP_ERROR));
+}
+
+
+void Item_datetime_literal::print(String *str, enum_query_type query_type)
+{
+ str->append("TIMESTAMP'");
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ my_datetime_to_str(&cached_time, buf, decimals);
+ str->append(buf);
+ str->append('\'');
+}
+
+
+bool Item_datetime_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+{
+ DBUG_ASSERT(fixed);
+ fuzzy_date |= sql_mode_for_dates(current_thd);
+ *ltime= cached_time;
+ return (null_value= check_date_with_warn(ltime, fuzzy_date,
+ MYSQL_TIMESTAMP_ERROR));
+}
+
+
+void Item_time_literal::print(String *str, enum_query_type query_type)
+{
+ str->append("TIME'");
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ my_time_to_str(&cached_time, buf, decimals);
+ str->append(buf);
+ str->append('\'');
+}
+
+
+bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+{
+ DBUG_ASSERT(fixed);
+ *ltime= cached_time;
+ if (fuzzy_date & TIME_TIME_ONLY)
+ return (null_value= false);
+ return (null_value= check_date_with_warn(ltime, fuzzy_date,
+ MYSQL_TIMESTAMP_ERROR));
+}
+
+
+
/**
Pack data in buffer for sending.
*/
@@ -6596,7 +6570,7 @@ bool Item::send(Protocol *protocol, String *buffer)
case MYSQL_TYPE_TIMESTAMP:
{
MYSQL_TIME tm;
- get_date(&tm, sql_mode_for_dates());
+ get_date(&tm, sql_mode_for_dates(current_thd));
if (!null_value)
{
if (f_type == MYSQL_TYPE_DATE)
@@ -6682,6 +6656,13 @@ Item* Item::cache_const_expr_transformer(uchar *arg)
return this;
}
+/**
+ Find Item by reference in the expression
+*/
+bool Item::find_item_processor(uchar *arg)
+{
+ return (this == ((Item *) arg));
+}
bool Item_field::send(Protocol *protocol, String *buffer)
{
@@ -7468,9 +7449,9 @@ int Item_ref::save_in_field(Field *to, bool no_conversions)
}
-void Item_ref::save_org_in_field(Field *field)
+void Item_ref::save_org_in_field(Field *field, fast_field_copier optimizer_data)
{
- (*ref)->save_org_in_field(field);
+ (*ref)->save_org_in_field(field, optimizer_data);
}
@@ -8344,7 +8325,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
{
TABLE_LIST *view= field_arg->table->pos_in_table_list->top_table();
push_warning_printf(field_arg->table->in_use,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_VIEW_FIELD,
ER(ER_NO_DEFAULT_FOR_VIEW_FIELD),
view->view_db.str,
@@ -8353,7 +8334,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
else
{
push_warning_printf(field_arg->table->in_use,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_FIELD,
ER(ER_NO_DEFAULT_FOR_FIELD),
field_arg->field_name);
@@ -8766,6 +8747,28 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
{
Item_result res_type=item_cmp_type(field->result_type(),
item->result_type());
+ /*
+ We have to check field->cmp_type() instead of res_type,
+ as result_type() - and thus res_type - can never be TIME_RESULT (yet).
+ */
+ if (field->cmp_type() == TIME_RESULT)
+ {
+ MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time;
+ if (field->type() == MYSQL_TYPE_TIME)
+ {
+ field->get_time(&field_time);
+ item->get_time(&item_time);
+ }
+ else
+ {
+ field->get_date(&field_time, TIME_INVALID_DATES);
+ item->get_date(&item_time, TIME_INVALID_DATES);
+ if (item_time.time_type == MYSQL_TIMESTAMP_TIME)
+ if (time_to_datetime(thd, &item_time, item_time_cmp= &item_time2))
+ return 1;
+ }
+ return my_time_compare(&field_time, item_time_cmp);
+ }
if (res_type == STRING_RESULT)
{
char item_buff[MAX_FIELD_WIDTH];
@@ -8816,25 +8819,6 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
return my_decimal_cmp(field_val, item_val);
}
/*
- We have to check field->cmp_type() instead of res_type,
- as result_type() - and thus res_type - can never be TIME_RESULT (yet).
- */
- if (field->cmp_type() == TIME_RESULT)
- {
- MYSQL_TIME field_time, item_time;
- if (field->type() == MYSQL_TYPE_TIME)
- {
- field->get_time(&field_time);
- item->get_time(&item_time);
- }
- else
- {
- field->get_date(&field_time, TIME_INVALID_DATES);
- item->get_date(&item_time, TIME_INVALID_DATES);
- }
- return my_time_compare(&field_time, &item_time);
- }
- /*
The patch for Bug#13463415 started using this function for comparing
BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode.
Prefixing the auto variables with volatile fixes the problem....
@@ -9549,7 +9533,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
/* fix variable decimals which always is NOT_FIXED_DEC */
if (Field::result_merge_type(fld_type) == INT_RESULT)
item_decimals= 0;
- decimals= max(decimals, item_decimals);
+ decimals= MY_MAX(decimals, item_decimals);
}
if (fld_type == FIELD_TYPE_GEOMETRY)
@@ -9558,10 +9542,10 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
if (Field::result_merge_type(fld_type) == DECIMAL_RESULT)
{
- decimals= min(max(decimals, item->decimals), DECIMAL_MAX_SCALE);
+ decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE);
int item_int_part= item->decimal_int_part();
- int item_prec = max(prev_decimal_int_part, item_int_part) + decimals;
- int precision= min(item_prec, DECIMAL_MAX_PRECISION);
+ int item_prec = MY_MAX(prev_decimal_int_part, item_int_part) + decimals;
+ int precision= MY_MIN(item_prec, DECIMAL_MAX_PRECISION);
unsigned_flag&= item->unsigned_flag;
max_length= my_decimal_precision_to_length_no_truncation(precision,
decimals,
@@ -9592,7 +9576,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
*/
if (collation.collation != &my_charset_bin)
{
- max_length= max(old_max_chars * collation.collation->mbmaxlen,
+ max_length= MY_MAX(old_max_chars * collation.collation->mbmaxlen,
display_length(item) /
item->collation.collation->mbmaxlen *
collation.collation->mbmaxlen);
@@ -9614,7 +9598,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
{
int delta1= max_length_orig - decimals_orig;
int delta2= item->max_length - item->decimals;
- max_length= max(delta1, delta2) + decimals;
+ max_length= MY_MAX(delta1, delta2) + decimals;
if (fld_type == MYSQL_TYPE_FLOAT && max_length > FLT_DIG + 2)
{
max_length= MAX_FLOAT_STR_LENGTH;
@@ -9632,7 +9616,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
break;
}
default:
- max_length= max(max_length, display_length(item));
+ max_length= MY_MAX(max_length, display_length(item));
};
maybe_null|= item->maybe_null;
get_full_info(item);
@@ -9928,14 +9912,3 @@ const char *dbug_print_item(Item *item)
#endif /*DBUG_OFF*/
-/*****************************************************************************
-** Instantiate templates
-*****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List<Item>;
-template class List_iterator<Item>;
-template class List_iterator_fast<Item>;
-template class List_iterator_fast<Item_field>;
-template class List<List_item>;
-#endif