/*
   Copyright (c) 2000, 2017, Oracle and/or its affiliates.
   Copyright (c) 2008, 2022, MariaDB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA
*/

/**
  @file

  @brief
  This file implements classes defined in field.h
*/

#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "rpl_rli.h"                            // Pull in Relay_log_info
#include "slave.h"                              // Pull in rpl_master_has_bug()
#include "strfunc.h"                            // find_type2, find_set
#include "tztime.h"                      // struct Time_zone
#include "filesort.h"                    // change_double_for_sort
#include "log_event.h"                   // class Table_map_log_event
#include <m_ctype.h>

// Maximum allowed exponent value for converting string to decimal
#define MAX_EXPONENT 1024

/*****************************************************************************
  Instantiate templates and static variables
*****************************************************************************/

static const char *zero_timestamp="0000-00-00 00:00:00.000000";
LEX_CSTRING temp_lex_str= {STRING_WITH_LEN("temp")};

uchar Field_null::null[1]={1};
const char field_separator=',';

#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE FLOATING_POINT_BUFFER
#define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
                        ((ulong) ((1LL << MY_MIN(arg, 4) * 8) - 1))

// Column marked for read or the field set to read out of record[0]
bool Field::marked_for_read() const
{
  return !table ||
         (!table->read_set ||
          bitmap_is_set(table->read_set, field_index) ||
          (!(ptr >= table->record[0] &&
             ptr < table->record[0] + table->s->reclength)));
}

/*
  The name of this function is a bit misleading as in 10.4 we don't
  have to test anymore if the field is computed. Instead we mark
  changed fields with DBUG_FIX_WRITE_SET() in table.cc
*/

bool Field::marked_for_write_or_computed() const
{
  return (!table ||
          (!table->write_set ||
           bitmap_is_set(table->write_set, field_index) ||
           (!(ptr >= table->record[0] &&
              ptr < table->record[0] + table->s->reclength))));
}


#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "")

/*
  Rules for merging different types of fields in UNION

  NOTE: to avoid 256*256 table, gap in table types numeration is skipped
  following #defines describe that gap and how to calculate number of fields
  and index of field in this array.
*/
const int FIELDTYPE_TEAR_FROM= (MYSQL_TYPE_BIT + 1);
const int FIELDTYPE_TEAR_TO=   (MYSQL_TYPE_NEWDECIMAL - 1);
const int FIELDTYPE_LAST=      254;
const int FIELDTYPE_NUM=       FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST -
                                                      FIELDTYPE_TEAR_TO);

static inline int merge_type2index(enum_field_types merge_type)
{
  DBUG_ASSERT(merge_type < FIELDTYPE_TEAR_FROM ||
              merge_type > FIELDTYPE_TEAR_TO);
  DBUG_ASSERT(merge_type <= FIELDTYPE_LAST);
  if (merge_type < FIELDTYPE_TEAR_FROM)
    return merge_type;
  return FIELDTYPE_TEAR_FROM + (merge_type - FIELDTYPE_TEAR_TO) - 1;
}


/**
  Implements data type merge rules for the built-in traditional data types.
  Used for operations such as:
  - UNION
  - CASE and its abbreviations COALESCE, IF, IFNULL
  - LEAST/GREATEST

  Given Fields A and B of real_types a and b, we find the result type of
  COALESCE(A, B) by querying:
    field_types_merge_rules[field_type_to_index(a)][field_type_to_index(b)].
*/
static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
{
  /* MYSQL_TYPE_DECIMAL -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_TINY -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_TINY,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_TINY,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_SHORT -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_SHORT,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_SHORT,       MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_SHORT,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_LONG -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_LONG,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_LONG,        MYSQL_TYPE_LONG,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_LONG,         MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONG,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_LONG,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_FLOAT -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_FLOAT,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_FLOAT,       MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_FLOAT,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_FLOAT,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_DOUBLE -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_NULL -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_TINY,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_NULL,        MYSQL_TYPE_TIMESTAMP,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONGLONG,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_TIME,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_YEAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_BIT,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_ENUM,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_SET,         MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_TIMESTAMP -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_TIMESTAMP,   MYSQL_TYPE_TIMESTAMP,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_DATETIME,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_LONGLONG -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_LONGLONG,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONGLONG,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONGLONG,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_LONGLONG,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_INT24 -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_INT24,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_INT24,       MYSQL_TYPE_LONG,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_INT24,       MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_INT24,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL    MYSQL_TYPE_ENUM
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_DATE -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,  MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_TIME -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_TIME,        MYSQL_TYPE_DATETIME,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_TIME,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_DATETIME -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_DATETIME,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_DATETIME,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_YEAR -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_TINY,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_YEAR,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_YEAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_NEWDATE -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_VARCHAR -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_VARCHAR
  },
  /* MYSQL_TYPE_BIT -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_BIT,         MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_BIT,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_NEWDECIMAL -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_NEWDECIMAL,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_ENUM -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_ENUM,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_SET -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_SET,         MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  },
  /* MYSQL_TYPE_TINY_BLOB -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_TINY_BLOB
  },
  /* MYSQL_TYPE_MEDIUM_BLOB -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_MEDIUM_BLOB
  },
  /* MYSQL_TYPE_LONG_BLOB -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_LONG_BLOB
  },
  /* MYSQL_TYPE_BLOB -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_BLOB
  },
  /* MYSQL_TYPE_VAR_STRING -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_VARCHAR
  },
  /* MYSQL_TYPE_STRING -> */
  {
  //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
  //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
  //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
  //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
  //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
  //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
  //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
  //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_BIT          <16>-<245>
    MYSQL_TYPE_STRING,
  //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
  //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    MYSQL_TYPE_STRING,      MYSQL_TYPE_TINY_BLOB,
  //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
  //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
  //MYSQL_TYPE_STRING
    MYSQL_TYPE_STRING
  }
};


const Type_handler *
Type_handler::aggregate_for_result_traditional(const Type_handler *a,
                                               const Type_handler *b)
{
  if (a == b)
  {
    /*
      If two traditional handlers are equal, quickly return "a".
      Some handlers (e.g. Type_handler_bool) pretend to be traditional,
      but in fact they are not traditional in full extent, they are
      only sub-types for now (and don't have a corresponding Field_xxx yet).
      Here we preserve such handlers during aggregation.
      As a result, COALESCE(true,true) preserves the "boolean" data type.

      Need to do this conversion for deprecated data types,
      similar to what field_type_merge_rules[][] does.
    */
    switch (a->field_type()) {
    case MYSQL_TYPE_DECIMAL:     return &type_handler_newdecimal;
    case MYSQL_TYPE_DATE:        return &type_handler_newdate;
    case MYSQL_TYPE_VAR_STRING:  return &type_handler_varchar;
    default: break;
    }
    return a;
  }
  enum_field_types ta= a->traditional_merge_field_type();
  enum_field_types tb= b->traditional_merge_field_type();
  enum_field_types res= field_types_merge_rules[merge_type2index(ta)]
                                               [merge_type2index(tb)];
  return Type_handler::get_handler_by_real_type(res);
}


bool Field::check_assignability_from(const Type_handler *from,
                                     bool ignore) const
{
  /*
    Using type_handler_for_item_field() here to get the data type handler
    on both sides. This is needed to make sure aggregation for Field
    works the same way with how Item_field aggregates for UNION or CASE,
    so these statements:
      SELECT a FROM t1 UNION SELECT b FROM t1; // Item_field vs Item_field
      UPDATE t1 SET a=b;                       // Field      vs Item_field
    either both return "Illegal parameter data types" or both pass
    the data type compatibility test.
    For MariaDB standard data types, using type_handler_for_item_field()
    turns ENUM/SET into just CHAR.
  */
  Type_handler_hybrid_field_type th(type_handler()->
                                      type_handler_for_item_field());
  if (th.aggregate_for_result(from->type_handler_for_item_field()))
  {
    bool error= (!ignore && get_thd()->is_strict_mode()) ||
                (type_handler()->is_scalar_type() != from->is_scalar_type());
    /*
      Display fully qualified column name for table columns.
      Display non-qualified names for other things,
      e.g. SP variables, SP return values, SP and CURSOR parameters.
    */
    if (table->s->db.str && table->s->table_name.str)
      my_printf_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION,
                      "Cannot cast '%s' as '%s' in assignment of %sQ.%sQ.%sQ",
                      MYF(error ? 0 : ME_WARNING),
                      from->name().ptr(), type_handler()->name().ptr(),
                      table->s->db.str, table->s->table_name.str,
                      field_name.str);
    else
      my_printf_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION,
                      "Cannot cast '%s' as '%s' in assignment of %sQ",
                      MYF(error ? 0 : ME_WARNING),
                      from->name().ptr(), type_handler()->name().ptr(),
                      field_name.str);
    return error;
  }
  return false;
}


/*
  Test if the given string contains important data:
  not spaces for character string,
  or any data for binary string.

  SYNOPSIS
    test_if_important_data()
    cs          Character set
    str         String to test
    strend      String end

  RETURN
    FALSE - If string does not have important data
    TRUE  - If string has some important data
*/

static bool
test_if_important_data(CHARSET_INFO *cs, const char *str, const char *strend)
{
  if (cs != &my_charset_bin)
    str+= cs->scan(str, strend, MY_SEQ_SPACES);
  return (str < strend);
}


/**
   Function to compare two unsigned integers for their relative order.
   Used below. In an anonymous namespace to not clash with definitions
   in other files.
 */

CPP_UNNAMED_NS_START

int compare(unsigned int a, unsigned int b)
{
  if (a < b)
    return -1;
  if (b < a)
    return 1;
  return 0;
}

CPP_UNNAMED_NS_END


/*****************************************************************************
  Static help functions
*****************************************************************************/

/*
  @brief
    Create a fixed size sort key part

  @param  buff             buffer where values are written
  @param  length           fixed size of the sort column
*/

void Field::make_sort_key_part(uchar *buff,uint length)
{
  if (maybe_null())
  {
    if (is_null())
    {
      bzero(buff, length + 1);
      return;
    }
    *buff++= 1;
  }
  sort_string(buff, length);
}


Item_field *Field::make_item_field_spvar(THD *thd, const Spvar_definition &def)
{
  return new (thd->mem_root) Item_field(thd, this);
}


/*
  @brief
    Create a packed sort key part

  @param  buff           buffer where values are written
  @param  sort_field     sort column structure

  @retval
    length of the bytes written, does not include the NULL bytes
*/
uint
Field::make_packed_sort_key_part(uchar *buff,
                                 const SORT_FIELD_ATTR *sort_field)
{
  if (maybe_null())
  {
    if (is_null())
    {
      *buff++= 0;
      return 0;  // For NULL values don't write any data
    }
    *buff++=1;
  }
  sort_string(buff, sort_field->original_length);
  return sort_field->original_length;
}


uint
Field_longstr::make_packed_sort_key_part(uchar *buff,
                                         const SORT_FIELD_ATTR *sort_field)
{
  if (maybe_null())
  {
    if (is_null())
    {
      *buff++= 0;
      return 0;   // For NULL values don't write any data
    }
    *buff++=1;
  }
  uchar *end= pack_sort_string(buff, sort_field);
  return (uint) (end-buff);
}


uchar*
Field_longstr::pack_sort_string(uchar *to, const SORT_FIELD_ATTR *sort_field)
{
  StringBuffer<LONGLONG_BUFFER_SIZE+1> buf;
  val_str(&buf, &buf);
  return to + sort_field->pack_sort_string(to, &buf, field_charset());
}


bool Field_longstr::val_bool()
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  StringBuffer<STRING_BUFFER_USUAL_SIZE> str;
  val_str(&str, &str);
  double res= Converter_strntod_with_warn(thd, Warn_filter(thd),
                                          "BOOLEAN",
                                          charset(),
                                          str.ptr(), str.length()).result();
  return res != 0.0;
}


/**
  @brief
  Determine the relative position of the field value in a numeric interval

  @details
  The function returns a double number between 0.0 and 1.0 as the relative
  position of the value of the this field in the numeric interval of [min,max].
  If the value is not in the interval the function returns 0.0 when
  the value is less than min, and, 1.0 when the value is greater than max.

  @param  min  value of the left end of the interval
  @param  max  value of the right end of the interval

  @return
  relative position of the field value in the numeric interval [min,max] 
*/

double pos_in_interval_for_double(double midp_val, double min_val,
                                  double max_val)
{
  double n, d;
  n= midp_val - min_val;
  if (n < 0)
    return 0.0;
  d= max_val - min_val;
  if (d <= 0)
    return 1.0;
  return MY_MIN(n/d, 1.0);
}


double Field::pos_in_interval_val_real(Field *min, Field *max)
{
  return pos_in_interval_for_double(val_real(), min->val_real(),
                                    max->val_real());
}


static
inline ulonglong char_prefix_to_ulonglong(uchar *src)
{
  uint sz= sizeof(ulonglong);
  for (uint i= 0; i < sz/2; i++)
  {
    uchar tmp= src[i];
    src[i]= src[sz-1-i];
    src[sz-1-i]= tmp;
  }
  return uint8korr(src); 
}

/*
  Compute res = a - b, without losing precision and taking care that these are
  unsigned numbers.
*/
static inline double safe_substract(ulonglong a, ulonglong b)
{
  return (a > b)? double(a - b) : -double(b - a);
}


/**
  @brief
  Determine the relative position of the field value in a string interval

  @details
  The function returns a double number between 0.0 and 1.0 as the relative
  position of the value of the this field in the string interval of [min,max].
  If the value is not in the interval the function returns 0.0 when
  the value is less than min, and, 1.0 when the value is greater than max.

  @note
  To calculate the relative position of the string value v in the interval
  [min, max] the function first converts the beginning of these three
  strings v, min, max into the strings that are used for byte comparison.
  For each string not more sizeof(ulonglong) first bytes are taken
  from the result of conversion. Then these bytes are interpreted as the
  big-endian representation of an ulonglong integer. The values of these
  integer numbers obtained for the strings v, min, max are used to calculate
  the position of v in [min,max] in the same way is it's done for numeric
  fields (see Field::pos_in_interval_val_real).

  @todo
  Improve the procedure for the case when min and max have the same
  beginning
     
  @param  min  value of the left end of the interval
  @param  max  value of the right end of the interval

  @return
  relative position of the field value in the string interval [min,max] 
*/

double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
{
  return pos_in_interval_for_string(charset(),
            ptr      + data_offset, data_length(),
            min->ptr + data_offset, min->data_length(),
            max->ptr + data_offset, max->data_length()
         );
}


double pos_in_interval_for_string(CHARSET_INFO *cset,
                                  const uchar *midp_val, uint32 midp_len,
                                  const uchar *min_val,  uint32 min_len,
                                  const uchar *max_val,  uint32 max_len)
{
  uchar mp_prefix[sizeof(ulonglong)];
  uchar minp_prefix[sizeof(ulonglong)];
  uchar maxp_prefix[sizeof(ulonglong)];
  ulonglong mp, minp, maxp;

  cset->strnxfrm(mp_prefix, sizeof(mp), midp_val, midp_len);
  cset->strnxfrm(minp_prefix, sizeof(minp), min_val, min_len);
  cset->strnxfrm(maxp_prefix, sizeof(maxp), max_val, max_len);

  mp=   char_prefix_to_ulonglong(mp_prefix);
  minp= char_prefix_to_ulonglong(minp_prefix);
  maxp= char_prefix_to_ulonglong(maxp_prefix);

  double n, d;
  n= safe_substract(mp, minp);
  if (n < 0)
    return 0.0;
  d= safe_substract(maxp, minp);
  if (d <= 0)
    return 1.0;
  return MY_MIN(n/d, 1.0);
}


bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const
{
  DBUG_ASSERT(cmp_type() != STRING_RESULT); // For STRING_RESULT see Field_str
  /*
    We use result_type() rather than cmp_type() in the below condition,
    because it covers a special case that string literals guarantee uniqueness
    for temporal columns, so the query:
      WHERE temporal_column='string'
    cannot return multiple distinct temporal values.

    TODO: perhaps we could allow INT/DECIMAL/DOUBLE types for temporal items.
  */
  return result_type() == item->result_type();
}


/**
  Check whether a field item can be substituted for an equal item

  @details
  The function checks whether a substitution of a field item for
  an equal item is valid.

  @param arg   *arg != NULL <-> the field is in the context
               where substitution for an equal item is valid

  @note
    The following statement is not always true:
  @n
    x=y => F(x)=F(x/y).
  @n
    This means substitution of an item for an equal item not always
    yields an equavalent condition. Here's an example:
    @code
    'a'='a '
    (LENGTH('a')=1) != (LENGTH('a ')=2)
  @endcode
    Such a substitution is surely valid if either the substituted
    field is not of a STRING type or if it is an argument of
    a comparison predicate.

  @retval
    TRUE   substitution is valid
  @retval
    FALSE  otherwise
*/

bool Field::can_be_substituted_to_equal_item(const Context &ctx,
                                             const Item_equal *item_equal)
{
  DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT);
  DBUG_ASSERT(cmp_type() != STRING_RESULT);
  switch (ctx.subst_constraint()) {
  case ANY_SUBST:
    /*
      Disable const propagation for items used in different comparison contexts.
      This must be done because, for example, Item_hex_string->val_int() is not
      the same as (Item_hex_string->val_str() in BINARY column)->val_int().
      We cannot simply disable the replacement in a particular context (
      e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since
      Items don't know the context they are in and there are functions like
      IF (<hex_string>, 'yes', 'no').
    */
    return ctx.compare_type_handler() == item_equal->compare_type_handler();
  case IDENTITY_SUBST:
    return true;
  }
  return false;
}


bool Field::cmp_is_done_using_type_handler_of_this(const Item_bool_func *cond,
                                                   const Item *item) const
{
  /*
    We could eventually take comparison_type_handler() from cond,
    instead of calculating it again. But only some descendants of
    Item_bool_func has this method. So this needs some hierarchy changes.
    Another option is to pass "class Context" to this method.
  */
  Type_handler_hybrid_field_type cmp(type_handler_for_comparison());
  return !cmp.aggregate_for_comparison(item->type_handler_for_comparison()) &&
         cmp.type_handler() == type_handler_for_comparison();
}


/*
  This handles all numeric and BIT data types.
*/ 
Data_type_compatibility
Field::can_optimize_keypart_ref(const Item_bool_func *cond,
                                const Item *item) const
{
  DBUG_ASSERT(cmp_type() != STRING_RESULT);
  DBUG_ASSERT(cmp_type() != TIME_RESULT);
  return item->cmp_type() != TIME_RESULT ?
         Data_type_compatibility::OK :
         Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
}


/*
  This handles all numeric and BIT data types.
*/ 
Data_type_compatibility
Field::can_optimize_group_min_max(const Item_bool_func *cond,
                                  const Item *const_item) const
{
  DBUG_ASSERT(cmp_type() != STRING_RESULT);
  DBUG_ASSERT(cmp_type() != TIME_RESULT);
  return const_item->cmp_type() != TIME_RESULT ?
         Data_type_compatibility::OK :
         Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
}


/*
  This covers all numeric types, BIT
*/
Data_type_compatibility
Field::can_optimize_range(const Item_bool_func *cond,
                          const Item *item,
                          bool is_eq_func) const
{
  DBUG_ASSERT(cmp_type() != TIME_RESULT);   // Handled in Field_temporal
  DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants
  return item->cmp_type() != TIME_RESULT ?
         Data_type_compatibility::OK :
         Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
}


int Field::store_hex_hybrid(const char *str, size_t length)
{
  DBUG_ASSERT(result_type() != STRING_RESULT);
  ulonglong nr;

  if (length > 8)
  {
    nr= flags & UNSIGNED_FLAG ? ULONGLONG_MAX : LONGLONG_MAX;
    goto warn;
  }
  nr= (ulonglong) longlong_from_hex_hybrid(str, length);
  if ((length == 8) && cmp_type()== INT_RESULT &&
      !(flags & UNSIGNED_FLAG) && (nr > LONGLONG_MAX))
  {
    nr= LONGLONG_MAX;
    goto warn;
  }
  return store((longlong) nr, true);  // Assume hex numbers are unsigned

warn:
  if (!store((longlong) nr, true))
    set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
  return 1;
}


/**
  If a field does not have a corresponding data, it's behavior can vary:
  - In case of the fixed file format
    it's set to the default value for the data type,
    such as 0 for numbers or '' for strings.
  - In case of a non-fixed format
    it's set to NULL for nullable fields, and
    it's set to the default value for the data type for NOT NULL fields.
  This seems to be by design.
*/
bool Field::load_data_set_no_data(THD *thd, bool fixed_format)
{
  reset();                  // Do not use the DEFAULT value
  if (fixed_format)
  {
    set_notnull();
    /*
      We're loading a fixed format file, e.g.:
        LOAD DATA INFILE 't1.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
      Suppose the file ended unexpectedly and no data was provided for an
      auto-increment column in the current row.
      Historically, if sql_mode=NO_AUTO_VALUE_ON_ZERO, then the column value
      is set to 0 in such case (the next auto_increment value is not used).
      This behaviour was introduced by the fix for "bug#12053" in mysql-4.1.
      Note, loading a delimited file works differently:
      "no data" is not converted to 0 on NO_AUTO_VALUE_ON_ZERO:
      it's considered as equal to setting the column to NULL,
      which is then replaced to the next auto_increment value.
      This difference seems to be intentional.
    */
    if (this == table->next_number_field)
      table->auto_increment_field_not_null= true;
  }
  set_has_explicit_value(); // Do not auto-update this field
  return false;
}


bool Field::load_data_set_null(THD *thd)
{
  reset();
  set_null();
  if (!maybe_null())
  {
    if (this != table->next_number_field)
      set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_NULL_TO_NOTNULL, 1);
  }
  set_has_explicit_value(); // Do not auto-update this field
  return false;
}


void Field::load_data_set_value(const char *pos, uint length,
                                CHARSET_INFO *cs)
{
  /*
    Mark field as not null, we should do this for each row because of
    restore_record...
  */
  set_notnull();
  if (this == table->next_number_field)
    table->auto_increment_field_not_null= true;
  store(pos, length, cs);
  set_has_explicit_value(); // Do not auto-update this field
}


bool Field::sp_prepare_and_store_item(THD *thd, Item **value)
{
  DBUG_ENTER("Field::sp_prepare_and_store_item");
  DBUG_ASSERT(value);

  Item *expr_item;

  if (!(expr_item= thd->sp_fix_func_item_for_assignment(this, value)))
    goto error;

  if (expr_item->check_is_evaluable_expression_or_error())
    goto error;

  /* Save the value in the field. Convert the value if needed. */

  expr_item->save_in_field(this, 0);

  if (likely(!thd->is_error()))
    DBUG_RETURN(false);

error:
  /*
    In case of error during evaluation, leave the result field set to NULL.
    Sic: we can't do it in the beginning of the function because the 
    result field might be needed for its own re-evaluation, e.g. case of 
    set x = x + 1;
  */
  set_null();
  DBUG_ASSERT(thd->is_error());
  DBUG_RETURN(true);
}


void Field::error_generated_column_function_is_not_allowed(THD *thd,
                                                           bool error) const
{
  StringBuffer<64> tmp;
  vcol_info->expr->print(&tmp, (enum_query_type)
                               (QT_TO_SYSTEM_CHARSET |
                                QT_ITEM_IDENT_SKIP_DB_NAMES |
                                QT_ITEM_IDENT_SKIP_TABLE_NAMES));
  my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED,
           MYF(error ? 0 : ME_WARNING),
           tmp.c_ptr_safe(), vcol_info->get_vcol_type_name(),
           const_cast<const char*>(field_name.str));
}


/*
  Check if an indexed or a persistent virtual column depends on sql_mode flags
  that it cannot handle.
  See sql_mode.h for details.
*/
bool Field::check_vcol_sql_mode_dependency(THD *thd, vcol_init_mode mode) const
{
  DBUG_ASSERT(vcol_info);
  if ((flags & PART_KEY_FLAG) != 0 || stored_in_db())
  {
    Sql_mode_dependency valdep= vcol_info->expr->value_depends_on_sql_mode();
    sql_mode_t cnvdep= conversion_depends_on_sql_mode(thd, vcol_info->expr);
    Sql_mode_dependency dep=
        (valdep | Sql_mode_dependency(0, cnvdep)) &
        Sql_mode_dependency(~0, ~can_handle_sql_mode_dependency_on_store());
    if (dep)
    {
      bool error= (mode & VCOL_INIT_DEPENDENCY_FAILURE_IS_ERROR) != 0;
      error_generated_column_function_is_not_allowed(thd, error);
      dep.push_dependency_warnings(thd);
      return error;
    }
  }
  return false;
}


bool Field::make_empty_rec_store_default_value(THD *thd, Item *item)
{
  DBUG_ASSERT(!(flags & BLOB_FLAG));
  int res= item->save_in_field(this, true);
  return res != 0 && res != 3;
}


/**
  Numeric fields base class constructor.
*/
Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
                     uchar null_bit_arg, utype unireg_check_arg,
                     const LEX_CSTRING *field_name_arg,
                     decimal_digits_t dec_arg, bool zero_arg, bool unsigned_arg)
  :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
         unireg_check_arg, field_name_arg),
  dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg)
{
  if (zerofill)
    flags|=ZEROFILL_FLAG;
  if (unsigned_flag)
    flags|=UNSIGNED_FLAG;
}


void Field_num::prepend_zeros(String *value) const
{
  int diff;
  if ((diff= (int) (field_length - value->length())) > 0)
  {
    const bool error= value->realloc(field_length);
    if (likely(!error))
    {
      bmove_upp((uchar*) value->ptr()+field_length,
                (uchar*) value->ptr()+value->length(),
	        value->length());
      bfill((uchar*) value->ptr(),diff,'0');
      value->length(field_length);
    }
  }
}


sql_mode_t Field_num::can_handle_sql_mode_dependency_on_store() const
{
  return MODE_PAD_CHAR_TO_FULL_LENGTH;
}


Item *Field_num::get_equal_zerofill_const_item(THD *thd, const Context &ctx,
                                               Item *const_item)
{
  switch (ctx.subst_constraint()) {
  case IDENTITY_SUBST:
    return NULL; // Not safe to propagate if not in comparison. See MDEV-8369.
  case ANY_SUBST:
    break;
  }
  DBUG_ASSERT(const_item->const_item());
  DBUG_ASSERT(ctx.compare_type_handler()->cmp_type() != STRING_RESULT);
  return const_item;
}


/**
Construct warning parameters using thd->no_errors
  to determine whether to generate or suppress warnings.
  We can get here in a query like this:
    SELECT COUNT(@@basedir);
  from Item_func_get_system_var::update_null_value().
*/
Value_source::Warn_filter::Warn_filter(const THD *thd)
 :m_want_warning_edom(!thd->no_errors),
  m_want_note_truncated_spaces(!thd->no_errors)
{ }


/**
  Check string-to-number conversion and produce a warning if
  - could not convert any digits (EDOM-alike error)
  - found garbage at the end of the string
  - found trailing spaces (a note)
  See also Field_num::check_edom_and_truncation() for a similar function.

  @param thd        - the thread
  @param filter     - which warnings/notes are allowed
  @param type       - name of the data type (e.g. "INTEGER", "DECIMAL", "DOUBLE")
  @param cs         - character set of the original string
  @param str        - the original string
  @param end        - the end of the string

  Unlike Field_num::check_edom_and_truncation(), this function does not
  distinguish between EDOM and truncation and reports the same warning for
  both cases. Perhaps we should eventually print different warnings, to make
  the explicit CAST work closer to the implicit cast in Field_xxx::store().
*/
void
Value_source::Converter_string_to_number::check_edom_and_truncation(THD *thd,
                                                       Warn_filter filter,
                                                       const char *type,
                                                       CHARSET_INFO *cs,
                                                       const char *str,
                                                       size_t length) const
{
  DBUG_ASSERT(str <= m_end_of_num);
  DBUG_ASSERT(m_end_of_num <= str + length);
  if (m_edom || (m_end_of_num < str + length &&
                 !check_if_only_end_space(cs, m_end_of_num, str + length)))
  {
    // EDOM or important trailing data truncation
    if (filter.want_warning_edom())
    {
      /*
        We can use err.ptr() here as ErrConvString is guaranteed to put an
        end \0 here.
      */
      THD *wthd= thd ? thd : current_thd;
      push_warning_printf(wthd, Sql_condition::WARN_LEVEL_WARN,
                          ER_TRUNCATED_WRONG_VALUE,
                          ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type,
                          ErrConvString(str, length, cs).ptr());
    }
  }
  else if (m_end_of_num < str + length)
  {
    // Unimportant trailing data (spaces) truncation
    if (filter.want_note_truncated_spaces())
    {
      THD *wthd= thd ? thd : current_thd;
      push_warning_printf(wthd, Sql_condition::WARN_LEVEL_NOTE,
                          ER_TRUNCATED_WRONG_VALUE,
                          ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type,
                          ErrConvString(str, length, cs).ptr());
    }
  }
}


/**
  Check a string-to-number conversion routine result and generate warnings
  in case when it:
  - could not convert any digits
  - found garbage at the end of the string.

  @param type          Data type name (e.g. "decimal", "integer", "double")
  @param edom          Indicates that the string-to-number routine returned
                       an error code equivalent to EDOM (value out of domain),
                       i.e. the string fully consisted of garbage and the
                       conversion routine could not get any digits from it.
  @param str           The original string
  @param length        Length of 'str'
  @param cs            Character set
  @param end           Pointer to char after last used digit

  @note
    This is called after one has called one of the following functions:
    - strntoull10rnd()
    - strntod()
    - str2my_decimal()

  @retval
    0        OK
  @retval
    1        error: could not scan any digits (EDOM),
             e.g. empty string, or garbage.
  @retval
    2        error: scanned some digits,
             but then found garbage at the end of the string.
*/


int Field_num::check_edom_and_important_data_truncation(const char *type,
                                                        bool edom,
                                                        CHARSET_INFO *cs,
                                                        const char *str, size_t length,
                                                        const char *end)
{
  /* Test if we get an empty string or garbage */
  if (edom)
  {
    ErrConvString err(str, length, cs);
    set_warning_truncated_wrong_value(type, err.ptr());
    return 1;
  }
  /* Test if we have garbage at the end of the given string. */
  if (test_if_important_data(cs, end, str + length))
  {
    set_warning(WARN_DATA_TRUNCATED, 1);
    return 2;
  }
  return 0;
}


int Field_num::check_edom_and_truncation(const char *type, bool edom,
                                         CHARSET_INFO *cs,
                                         const char *str, size_t length,
                                         const char *end)
{
  int rc= check_edom_and_important_data_truncation(type, edom,
                                                   cs, str, length, end);
  if (!rc && end < str + length)
    set_note(WARN_DATA_TRUNCATED, 1);
  return rc;
}


/*
  Convert a string to an integer then check bounds.
  
  SYNOPSIS
    Field_num::get_int
    cs            Character set
    from          String to convert
    len           Length of the string
    rnd           OUT longlong value
    unsigned_max  max unsigned value
    signed_min    min signed value
    signed_max    max signed value

  DESCRIPTION
    The function calls strntoull10rnd() to get an integer value then
    check bounds and errors returned. In case of any error a warning
    is raised.

  RETURN
    0   ok
    1   error
*/

bool Field_num::get_int(CHARSET_INFO *cs, const char *from, size_t len,
                        longlong *rnd, ulonglong unsigned_max, 
                        longlong signed_min, longlong signed_max)
{
  char *end;
  int error;
  
  *rnd= (longlong) cs->strntoull10rnd(from, len,
                                      unsigned_flag, &end,
                                      &error);
  if (unsigned_flag)
  {

    if ((((ulonglong) *rnd > unsigned_max) &&
         (*rnd= (longlong) unsigned_max)) ||
        error == MY_ERRNO_ERANGE)
    {
      goto out_of_range;
    }
  }
  else
  {
    if (*rnd < signed_min)
    {
      *rnd= signed_min;
      goto out_of_range;
    }
    else if (*rnd > signed_max)
    {
      *rnd= signed_max;
      goto out_of_range;
    }
  }
  if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
      check_int(cs, from, len, end, error))
    return 1;

  return error && get_thd()->count_cuted_fields == CHECK_FIELD_EXPRESSION;

out_of_range:
  set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
  return 1;
}


double Field_real::get_double(const char *str, size_t length, CHARSET_INFO *cs,
                              int *error)
{
  char *end;
  double nr= cs->strntod((char*) str, length, &end, error);
  if (unlikely(*error))
  {
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    *error= 1;
  }
  else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
           check_edom_and_truncation("double", str == end,
                                     cs, str, length, end))
    *error= 1;
  return nr;
}


/**
  Process decimal library return codes and issue warnings for overflow and
  truncation.

  @param op_result  decimal library return code (E_DEC_* see include/decimal.h)

  @retval
    1  there was overflow
  @retval
    0  no error or some other errors except overflow
*/

int Field::warn_if_overflow(int op_result)
{
  if (op_result == E_DEC_OVERFLOW)
  {
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    return 1;
  }
  if (op_result == E_DEC_TRUNCATED)
  {
    set_note(WARN_DATA_TRUNCATED, 1);
    /* We return 0 here as this is not a critical issue */
  }
  return 0;
}


/**
  Interpret field value as an integer but return the result as a string.

  This is used for printing bit_fields as numbers while debugging.
*/

String *Field::val_int_as_str(String *val_buffer, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_read());
  CHARSET_INFO *cs= &my_charset_bin;
  uint length;
  longlong value= val_int();

  if (val_buffer->alloc(MY_INT64_NUM_DECIMAL_DIGITS))
    return 0;
  length= (uint) (cs->longlong10_to_str)((char*) val_buffer->ptr(),
                                         MY_INT64_NUM_DECIMAL_DIGITS,
                                         unsigned_val ? 10 : -10,
                                         value);
  val_buffer->length(length);
  return val_buffer;
}


/// This is used as a table name when the table structure is not set up
Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
	     uchar null_bit_arg,
	     utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
  :ptr(ptr_arg),
  null_ptr(null_ptr_arg), table(0), orig_table(0),
  table_name(0), field_name(*field_name_arg), option_list(0),
  option_struct(0), key_start(0), part_of_key(0),
  part_of_key_not_clustered(0), vcol_direct_part_of_key(0), part_of_sortkey(0),
  unireg_check(unireg_check_arg), invisible(VISIBLE), field_length(length_arg),
  null_bit(null_bit_arg), is_created_from_null_item(FALSE),
  read_stats(NULL), collected_stats(0), vcol_info(0), check_constraint(0),
  default_value(0)
{
  flags=null_ptr ? 0: NOT_NULL_FLAG;
  comment.str= (char*) "";
  comment.length=0;
  field_index= 0;
  cond_selectivity= 1.0;
  next_equal_field= NULL;
}


void Field::hash_not_null(Hasher *hasher)
{
  DBUG_ASSERT(marked_for_read());
  DBUG_ASSERT(!is_null());
  hasher->add(sort_charset(), ptr, pack_length());
}

size_t
Field::do_last_null_byte() const
{
  DBUG_ASSERT(null_ptr == NULL || null_ptr >= table->record[0]);
  if (null_ptr)
    return (size_t) (null_ptr - table->record[0]) + 1;
  return LAST_NULL_BYTE_UNDEF;
}


void Field::copy_from_tmp(int row_offset)
{
  memcpy(ptr,ptr+row_offset,pack_length());
  if (null_ptr)
  {
    *null_ptr= (uchar) ((null_ptr[0] & (uchar) ~(uint) null_bit) |
			(null_ptr[row_offset] & (uchar) null_bit));
  }
}


bool Field::send(Protocol *protocol)
{
  StringBuffer<MAX_FIELD_WIDTH> tmp(charset());
  val_str(&tmp);
  return protocol->store(tmp.ptr(), tmp.length(), tmp.charset());
}


bool Field_num::send_numeric_zerofill_str(Protocol_text *protocol,
                                          protocol_send_type_t send_type)
{
  DBUG_ASSERT(marked_for_read());
  StringBuffer<MAX_FIELD_WIDTH> tmp(&my_charset_latin1);
  val_str(&tmp);
  return protocol->store_numeric_zerofill_str(tmp.ptr(),
                                              tmp.length(),
                                              send_type);
}


/**
   Check to see if field size is compatible with destination.

   This method is used in row-based replication to verify that the
   slave's field size is less than or equal to the master's field
   size. The encoded field metadata (from the master or source) is
   decoded and compared to the size of this field (the slave or
   destination).

   @note

   The comparison is made so that if the source data (from the master)
   is less than the target data (on the slave), -1 is returned in @c
   <code>*order_var</code>. This implies that a conversion is
   necessary, but that it is lossy and can result in truncation of the
   value.

   If the source data is strictly greater than the target data, 1 is
   returned in <code>*order_var</code>. This implies that the source
   type can is contained in the target type and that a conversion is
   necessary but is non-lossy.

   If no conversion is required to fit the source type in the target
   type, 0 is returned in <code>*order_var</code>.

   @param   field_metadata   Encoded size in field metadata
   @param   mflags           Flags from the table map event for the table.
   @param   order_var        Pointer to variable where the order
                             between the source field and this field
                             will be returned.

   @return @c true if this field's size is compatible with the
   master's field size, @c false otherwise.
*/
bool Field::compatible_field_size(uint field_metadata,
                                  const Relay_log_info *rli_arg __attribute__((unused)),
                                  uint16 mflags __attribute__((unused)),
                                  int *order_var) const
{
  uint const source_size= pack_length_from_metadata(field_metadata);
  uint const destination_size= row_pack_length();
  DBUG_PRINT("debug", ("real_type: %d, source_size: %u, destination_size: %u",
                       real_type(), source_size, destination_size));
  *order_var = compare(source_size, destination_size);
  return true;
}


int Field::store(const char *to, size_t length, CHARSET_INFO *cs,
                 enum_check_fields check_level)
{
  Check_level_instant_set tmp_level(get_thd(), check_level);
  return store(to, length, cs);
}


int Field::store_text(const char *to, size_t length, CHARSET_INFO *cs,
                      enum_check_fields check_level)
{
  Check_level_instant_set tmp_level(get_thd(), check_level);
  return store_text(to, length, cs);
}


int Field::store_timestamp_dec(const my_timeval &ts, uint dec)
{
  return store_time_dec(Datetime(get_thd(), ts).get_mysql_time(), dec);
}


int Field::store_to_statistical_minmax_field(Field *field, String *val)
{
  val_str(val);
  size_t length= Well_formed_prefix(val->charset(), val->ptr(),
                 MY_MIN(val->length(), field->field_length)).length();
  return field->store(val->ptr(), length, &my_charset_bin);
}


int Field::store_from_statistical_minmax_field(Field *stat_field, String *str,
                                               MEM_ROOT *mem)
{
  stat_field->val_str(str);
  return store_text(str->ptr(), str->length(), &my_charset_bin);
}


/*
  Same as above, but store the string in the statistics mem_root to make it
  easy to free everything by just freeing the mem_root.
*/

int Field_blob::store_from_statistical_minmax_field(Field *stat_field,
                                                    String *str,
                                                    MEM_ROOT *mem)
{
  String *tmp= stat_field->val_str(str);
  uchar *ptr;
  if (!(ptr= (uchar*) memdup_root(mem, tmp->ptr(), tmp->length())))
  {
    set_ptr((uint32) 0, NULL);
    return 1;
  }
  set_ptr(tmp->length(), ptr);
  return 0;
}


/**
   Pack the field into a format suitable for storage and transfer.

   To implement packing functionality, only the virtual function
   should be overridden. The other functions are just convenience
   functions and hence should not be overridden.

   @note The default method for packing fields just copy the raw bytes
   of the record into the destination, but never more than
   <code>max_length</code> characters.

   @param to
   Pointer to memory area where representation of field should be put.

   @param from
   Pointer to memory area where record representation of field is
   stored.
*/
uchar *
Field::pack(uchar *to, const uchar *from) const
{
  uint32 length= pack_length();
  memcpy(to, from, length);
  return to+length;
}

/**
   Unpack a field from row data.

   This method is used to unpack a field from a master whose size of
   the field is less than that of the slave.

   The <code>param_data</code> parameter is a two-byte integer (stored
   in the least significant 16 bits of the unsigned integer) usually
   consisting of two parts: the real type in the most significant byte
   and a original pack length in the least significant byte.

   The exact layout of the <code>param_data</code> field is given by
   the <code>Table_map_log_event::save_field_metadata()</code>.

   This is the default method for unpacking a field. It just copies
   the memory block in byte order (of original pack length bytes or
   length of field, whichever is smaller).

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Real type and original pack length of the field
                       data

   @return  New pointer into memory based on from + length of the data
   @return  0 if wrong data
*/
const uchar *
Field::unpack(uchar* to, const uchar *from, const uchar *from_end,
              uint param_data)
{
  uint length=pack_length(), len;
  int from_type= 0;
  /*
    If from length is > 255, it has encoded data in the upper bits. Need
    to mask it out.
  */
  if (param_data > 255)
  {
    from_type= (param_data & 0xff00) >> 8U;  // real_type.
    param_data= param_data & 0x00ff;        // length.
  }

  if ((param_data == 0) ||
      (length == param_data) ||
      (from_type != real_type()))
  {
    if (from + length > from_end)
      return 0;                                 // Error in data

    memcpy(to, from, length);
    return from+length;
  }

  len= (param_data && (param_data < length)) ? param_data : length;

  if (from + len > from_end)
    return 0;                                   // Error in data

  memcpy(to, from, len);
  return from+len;
}


void Field_num::add_zerofill_and_unsigned(String &res) const
{
  if (unsigned_flag)
    res.append(STRING_WITH_LEN(" unsigned"));
  if (zerofill)
    res.append(STRING_WITH_LEN(" zerofill"));
}


void Field::make_send_field(Send_field *field)
{
  if (orig_table && orig_table->s->db.str && *orig_table->s->db.str)
  {
    field->db_name= orig_table->s->db;
    if (orig_table->pos_in_table_list &&
        orig_table->pos_in_table_list->schema_table)
      field->org_table_name= orig_table->pos_in_table_list->
                                                schema_table->table_name;
    else
      field->org_table_name= orig_table->s->table_name;
  }
  else
    field->org_table_name= field->db_name= empty_clex_str;
  if (orig_table && orig_table->alias.ptr())
  {
    orig_table->alias.get_value(&field->table_name);
    field->org_col_name= field_name;
  }
  else
  {
    field->table_name= empty_clex_str;
    field->org_col_name= empty_clex_str;
  }
  field->col_name= field_name;
  field->length=field_length;
  field->set_handler(type_handler());
  field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
  field->decimals= 0;
}


/**
  Conversion from decimal to longlong with checking overflow and
  setting correct value (min/max) in case of overflow.

  @param val             value which have to be converted
  @param unsigned_flag   type of integer in which we convert val
  @param err             variable to pass error code

  @return
    value converted from val
*/
longlong Field::convert_decimal2longlong(const my_decimal *val,
                                         bool unsigned_flag, int *err)
{
  longlong i;
  if (unsigned_flag)
  {
    if (val->sign())
    {
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      i= 0;
      *err= 1;
    }
    else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR &
                                              ~E_DEC_OVERFLOW &
                                              ~E_DEC_TRUNCATED),
                                             val, TRUE, &i)))
    {
      i= ~(longlong) 0;
      *err= 1;
    }
  }
  else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR &
                                            ~E_DEC_OVERFLOW &
                                            ~E_DEC_TRUNCATED),
                                           val, FALSE, &i)))
  {
    i= (val->sign() ? LONGLONG_MIN : LONGLONG_MAX);
    *err= 1;
  }
  return i;
}


/**
  Storing decimal in integer fields.

  @param val       value for storing

  @note
    This method is used by all integer fields, real/decimal redefine it

  @retval
    0     OK
  @retval
    !=0  error
*/

int Field_int::store_decimal(const my_decimal *val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int err= 0;
  longlong i= convert_decimal2longlong(val, unsigned_flag, &err);
  return MY_TEST(err | store(i, unsigned_flag));
}


/**
  Return decimal value of integer field.

  @param decimal_value     buffer for storing decimal value

  @note
    This method is used by all integer fields, real/decimal redefine it.
    All longlong values fit in our decimal buffer which cal store 8*9=72
    digits of integer number

  @return
    pointer to decimal buffer with value of field
*/

my_decimal* Field_int::val_decimal(my_decimal *decimal_value)
{
  DBUG_ASSERT(marked_for_read());
  longlong nr= val_int();
  int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value);
  return decimal_value;
}


bool Field_int::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
{
  DBUG_ASSERT(marked_for_read());
  Longlong_hybrid nr(val_int(), (flags & UNSIGNED_FLAG));
  return int_to_datetime_with_warn(get_thd(), nr, ltime,
                                   fuzzydate, table->s, field_name.str);
}


bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id)
{
  DBUG_ASSERT(marked_for_read());
  DBUG_ASSERT(ltime);
  if (!table || !table->s)
    return true;
  DBUG_ASSERT(table->versioned(VERS_TRX_ID) ||
    (table->versioned() && table->s->table_category == TABLE_CATEGORY_TEMPORARY));
  if (!trx_id)
    return true;

  THD *thd= get_thd();
  DBUG_ASSERT(thd);
  if (trx_id == ULONGLONG_MAX)
  {
    thd->variables.time_zone->gmt_sec_to_TIME(ltime, TIMESTAMP_MAX_VALUE);
    ltime->second_part= TIME_MAX_SECOND_PART;
    return false;
  }
  if (cached == trx_id)
  {
    *ltime= cache;
    return false;
  }

  TR_table trt(thd);
  bool found= trt.query(trx_id);
  if (found)
  {
    trt[TR_table::FLD_COMMIT_TS]->get_date(&cache, fuzzydate);
    *ltime= cache;
    cached= trx_id;
    return false;
  }

  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                      ER_VERS_NO_TRX_ID, ER_THD(thd, ER_VERS_NO_TRX_ID),
                      (longlong) trx_id);
  return true;
}


Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
                     uchar null_bit_arg, utype unireg_check_arg,
                     const LEX_CSTRING *field_name_arg,
                     const DTCollation &collation)
  :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
         unireg_check_arg, field_name_arg)
{
  m_collation= collation;
  if (collation.collation->state & MY_CS_BINSORT)
    flags|=BINARY_FLAG;
}


bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const
{
  /*
    Can't guarantee uniqueness when comparing a CHAR/VARCHAR/TEXT,
    BINARY/VARBINARY/BLOB, ENUM,SET columns to an item with cmp_type()
    of INT_RESULT, DOUBLE_RESULT, DECIMAL_RESULT or TIME_RESULT.
    Example:
      SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01'
    return non-unuque values, e.g. '2001-01-01' and '2001-01-01x'.
  */
  if (!field_charset()->propagate(0, 0) ||
      item->cmp_type() != STRING_RESULT)
    return false;
  /*
    Can't guarantee uniqueness when comparing to
    an item of a different collation.
    Example:
      SELECT * FROM t1
      WHERE latin1_bin_column = _latin1'A' COLLATE latin1_swedish_ci
    return non-unique values 'a' and 'A'.
  */
  DTCollation tmp(dtcollation());
  return !tmp.aggregate(item->collation) && tmp.collation == field_charset();
}


bool Field_str::can_be_substituted_to_equal_item(const Context &ctx,
                                                  const Item_equal *item_equal)
{
  DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() == STRING_RESULT);
  switch (ctx.subst_constraint()) {
  case ANY_SUBST:
    return ctx.compare_type_handler() == item_equal->compare_type_handler() &&
          (ctx.compare_type_handler()->cmp_type() != STRING_RESULT ||
           ctx.compare_collation() == item_equal->compare_collation());
  case IDENTITY_SUBST:
    return ((charset()->state & MY_CS_BINSORT) &&
            (charset()->state & MY_CS_NOPAD));
  }
  return false;
}


void Field_str::change_charset(const DTCollation &new_cs)
{
  if (!has_charset())
    return;

  field_length= (field_length * new_cs.collation->mbmaxlen) /
    m_collation.collation->mbmaxlen;
  m_collation= new_cs;
}


void Field_num::make_send_field(Send_field *field)
{
  Field::make_send_field(field);
  field->decimals= dec;
}

/**
  Decimal representation of Field_str.

  @param d         value for storing

  @note
    Field_str is the base class for fields like Field_enum,
    Field_date and some similar. Some dates use fraction and also
    string value should be converted to floating point value according
    our rules, so we use double to store value of decimal in string.

  @todo
    use decimal2string?

  @retval
    0     OK
  @retval
    !=0  error
*/

int Field_str::store_decimal(const my_decimal *d)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  double val;
  /* TODO: use decimal2string? */
  int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR &
                                            ~E_DEC_OVERFLOW, d, &val));
  return err | store(val);
}


my_decimal *Field_str::val_decimal(my_decimal *decimal_value)
{
  DBUG_ASSERT(marked_for_read());
  longlong nr= val_int();
  int2my_decimal(E_DEC_FATAL_ERROR, nr, 0, decimal_value);
  return decimal_value;
}


uint Field::fill_cache_field(CACHE_FIELD *copy)
{
  uint store_length;
  copy->str= ptr;
  copy->length= pack_length_in_rec();
  copy->field= this;
  if (flags & BLOB_FLAG)
  {
    copy->type= CACHE_BLOB;
    copy->length-= portable_sizeof_char_ptr;
    return copy->length;
  }
  else if (!zero_pack() &&
           (type() == MYSQL_TYPE_STRING && copy->length >= 4 &&
            copy->length < 256))
  {
    copy->type= CACHE_STRIPPED;			    /* Remove end space */
    store_length= 2;
  }
  else if (type() ==  MYSQL_TYPE_VARCHAR)
  {
    copy->type= pack_length()-row_pack_length() == 1 ? CACHE_VARSTR1:
                                                      CACHE_VARSTR2;
    store_length= 0;
  }
  else
  {
    copy->type= 0;
    store_length= 0;
  }
  return copy->length + store_length;
}


bool Field::get_date(MYSQL_TIME *to, date_mode_t mode)
{
  StringBuffer<40> tmp;
  Temporal::Warn_push warn(get_thd(), table ? table->s->db.str : nullptr,
                           table ? table->s->table_name.str : nullptr,
                           field_name.str, to, mode);
  Temporal_hybrid *t= new(to) Temporal_hybrid(get_thd(), &warn,
                                              val_str(&tmp), mode);
  return !t->is_valid_temporal();
}


longlong Field::val_datetime_packed(THD *thd)
{
  MYSQL_TIME ltime, tmp;
  if (get_date(&ltime, Datetime::Options_cmp(thd)))
    return 0;
  if (ltime.time_type != MYSQL_TIMESTAMP_TIME)
    return pack_time(&ltime);
  if (time_to_datetime_with_warn(thd, &ltime, &tmp, TIME_CONV_NONE))
    return 0;
  return pack_time(&tmp);
}


longlong Field::val_time_packed(THD *thd)
{
  MYSQL_TIME ltime;
  Time::Options_cmp opt(thd);
  if (get_date(&ltime, opt))
    return 0;
  if (ltime.time_type == MYSQL_TIMESTAMP_TIME)
    return pack_time(&ltime);
  // Conversion from DATETIME or DATE to TIME is needed
  return Time(thd, &ltime, opt).to_packed();
}


/**
  This is called when storing a date in a string.

  @note
    Needs to be changed if/when we want to support different time formats.
*/

int Field::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  char buff[MAX_DATE_STRING_REP_LENGTH];
  uint length= (uint) my_TIME_to_str(ltime, buff, dec);
  /* Avoid conversion when field character set is ASCII compatible */
  return store(buff, length, (charset()->state & MY_CS_NONASCII) ?
                              &my_charset_latin1 : charset());
}


bool Field::optimize_range(uint idx, uint part) const
{
  return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE);
}


Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table,
                             bool keep_type __attribute__((unused)))
{
  Field *tmp;
  if (!(tmp= (Field*) memdup_root(root,(char*) this,size_of())))
    return 0;

  if (tmp->table->maybe_null)
    tmp->flags&= ~NOT_NULL_FLAG;
  tmp->table= new_table;
  tmp->key_start.init(0);
  tmp->part_of_key.init(0);
  tmp->vcol_direct_part_of_key.init(0);
  tmp->part_of_sortkey.init(0);
  tmp->read_stats= NULL;
  /*
    TODO: it is not clear why this method needs to reset unireg_check.
    Try not to reset it, or explain why it needs to be reset.
  */
  tmp->unireg_check= Field::NONE;
  tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
                ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
  tmp->reset_fields();
  tmp->invisible= VISIBLE;
  return tmp;
}


Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table,
                            uchar *new_ptr, uint32 length,
                            uchar *new_null_ptr, uint new_null_bit)
{
  Field *tmp;
  if ((tmp= make_new_field(root, new_table, table == new_table)))
  {
    tmp->ptr=      new_ptr;
    tmp->null_ptr= new_null_ptr;
    tmp->null_bit= new_null_bit;
  }
  return tmp;
}


/**
  Create field for temporary table from given field.

  @param thd	        Thread handler
  @param table	        Temporary table
  @param maybe_null_arg If the result field should be NULL-able,
                        even if the original field is NOT NULL, e.g. for:
                        - OUTER JOIN fields
                        - WITH ROLLUP fields
                        - arguments of aggregate functions, e.g. SUM(column1)
  @retval               NULL, on error
  @retval               pointer to the new field created, on success.
*/

Field *Field::create_tmp_field(MEM_ROOT *mem_root, TABLE *new_table,
                               bool maybe_null_arg)
{
  Field *new_field;

  if ((new_field= make_new_field(mem_root, new_table, new_table == table)))
  {
    new_field->init_for_tmp_table(this, new_table);
    new_field->flags|= flags & NO_DEFAULT_VALUE_FLAG;
    if (maybe_null_arg)
      new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join
  }
  return new_field;
}


/* This is used to generate a field in TABLE from TABLE_SHARE */

Field *Field::clone(MEM_ROOT *root, TABLE *new_table)
{
  Field *tmp;
  if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
  {
    tmp->init(new_table);
    tmp->move_field_offset((my_ptrdiff_t) (new_table->record[0] -
                                           new_table->s->default_values));
  }
  return tmp;
}


Field *Field::clone(MEM_ROOT *root, TABLE *new_table, my_ptrdiff_t diff)
{
  Field *tmp;
  if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
  {
    if (new_table)
      tmp->init(new_table);
    tmp->move_field_offset(diff);
  }
  return tmp;
}


int Field::set_default()
{
  if (default_value)
  {
    Query_arena backup_arena;
    /*
      TODO: this may impose memory leak until table flush.
          See comment in
          TABLE::update_virtual_fields(handler *, enum_vcol_update_mode).
    */
    table->in_use->set_n_backup_active_arena(table->expr_arena, &backup_arena);
    int rc= default_value->expr->save_in_field(this, 0);
    table->in_use->restore_active_arena(table->expr_arena, &backup_arena);
    return rc;
  }
  /* Copy constant value stored in s->default_values */
  my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values -
                                        table->record[0]);
  memcpy(ptr, ptr + l_offset, pack_length_in_rec());
  if (maybe_null_in_table())
    *null_ptr= ((*null_ptr & (uchar) ~null_bit) |
                (null_ptr[l_offset] & null_bit));
  return 0;
}


/****************************************************************************
  Field_null, a field that always return NULL
****************************************************************************/

void Field_null::sql_type(String &res) const
{
  res.set_ascii(STRING_WITH_LEN("null"));
}


bool Field_null::is_equal(const Column_definition &new_field) const
{
  DBUG_ASSERT(!compression_method());
  return (new_field.type_handler() == type_handler() &&
          new_field.charset->eq_collation(field_charset()) &&
          new_field.length == max_display_length());
}


/****************************************************************************
  Field_row, e.g. for ROW-type SP variables
****************************************************************************/

Field_row::~Field_row()
{
  delete m_table;
}


/*
  Create a virtual table with Fields for a ROW members
  from an explicit structure.
*/
bool Field_row::row_create_fields(THD *thd, List<Spvar_definition> *list)
{
  DBUG_ASSERT(!m_table);
  return !(m_table= create_virtual_tmp_table(thd, *list));
}


/*
  Create a virtual table with Fields for a ROW members
  from an explicit or an anchored definition.
  It covers the following scenarios:
  - ROW(a INT, b VARCHAR(32)) - explicit
  - ROW TYPE OF test.t2       - anchored table data type
  Note, in case of a `ROW TYPE OF cursor` definition the
  virtual table is not created (it's done later).
*/
bool Field_row::row_create_fields(THD *thd, const Spvar_definition &def)
{
  if (def.is_table_rowtype_ref()) // e.g. ROW TYPE OF test.t1
  {
    Row_definition_list defs;
    return def.table_rowtype_ref()->resolve_table_rowtype_ref(thd, defs) ||
           row_create_fields(thd, &defs);
  }

  if (def.is_cursor_rowtype_ref()) // e.g. ROW TYPE OF cursor
  {
    /*
      The structure is resolved later,
      in sp_instr_cursor_copy_struct::exec_core()
    */
    return false;
  }

  if (def.is_row()) // e.g. ROW(a INT, b VARCHAR(32))
    return row_create_fields(thd, def.row_field_definitions());

  DBUG_ASSERT(0); // Unknown ROW declaration style
  return true;
}


bool Field_row::sp_prepare_and_store_item(THD *thd, Item **value)
{
  DBUG_ENTER("Field_row::sp_prepare_and_store_item");

  if (value[0]->type() == Item::NULL_ITEM)
  {
    /*
      We're in a auto-generated sp_inst_set, to assign
      the explicit default NULL value to a ROW variable.
    */
    m_table->set_all_fields_to_null();
    DBUG_RETURN(false);
  }

  /**
    - In case if we're assigning a ROW variable from another ROW variable,
      value[0] points to Item_splocal. sp_fix_func_item() will return the
      fixed underlying Item_field pointing to Field_row.
    - In case if we're assigning from a ROW() value, src and value[0] will
      point to the same Item_row.
    - In case if we're assigning from a subselect, src and value[0] also
      point to the same Item_singlerow_subselect.
  */
  Item *src;
  if (!(src= thd->sp_fix_func_item(value)) ||
      src->cmp_type() != ROW_RESULT ||
      src->cols() != m_table->s->fields)
  {
    my_error(ER_OPERAND_COLUMNS, MYF(0), m_table->s->fields);
    m_table->set_all_fields_to_null();
    DBUG_RETURN(true);
  }

  src->bring_value();
  if (m_table->sp_set_all_fields_from_item(thd, src))
  {
    set_null();
    DBUG_RETURN(true);
  }
  set_notnull();
  DBUG_RETURN(false);
}


uint Field_row::cols() const
{
  // The table with ROW members must be already instantiated
  DBUG_ASSERT(m_table);
  return m_table->s->fields;
}


void Field_row::sql_type(String &res) const
{
  res.set_ascii(STRING_WITH_LEN("row"));
}


void Field_row::sql_type_for_sp_returns(String &res) const
{
  res.set_ascii(STRING_WITH_LEN("row("));
  for (uint i= 0; i < m_table->s->fields; i++)
  {
    if (i > 0)
      res.append(',');
    Field *field= m_table->field[i];
    res.append(field->field_name);
    res.append(' ');
    StringBuffer<64> col;
    field->sql_type_for_sp_returns(col);
    res.append(col.to_lex_cstring());
  }
  res.append(')');
}


void Field_row::expr_event_handler(THD *thd, expr_event_t event)
{
  if (m_table)
    m_table->expr_event_handler(thd, event);
}


/****************************************************************************
  Functions for the Field_decimal class
  This is an number stored as a pre-space (or pre-zero) string
****************************************************************************/

int
Field_decimal::reset(void)
{
  Field_decimal::store(STRING_WITH_LEN("0"),&my_charset_bin);
  return 0;
}

void Field_decimal::overflow(bool negative)
{
  uint len=field_length;
  uchar *to=ptr, filler= '9';

  set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
  if (negative)
  {
    if (!unsigned_flag)
    {
      /* Put - sign as a first digit so we'll have -999..999 or 999..999 */
      *to++ = '-';
      len--;
    }
    else
    {
      filler= '0';				// Fill up with 0
      if (!zerofill)
      {
	/*
	  Handle unsigned integer without zerofill, in which case
	  the number should be of format '   0' or '   0.000'
	*/
	uint whole_part=field_length- (dec ? dec+2 : 1);
	// Fill with spaces up to the first digit
	bfill(to, whole_part, ' ');
	to+=  whole_part;
	len-= whole_part;
	// The main code will also handle the 0 before the decimal point
      }
    }
  }
  bfill(to, len, filler);
  if (dec)
    ptr[field_length-dec-1]='.';
  return;
}


int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  char buff[STRING_BUFFER_USUAL_SIZE];
  String tmp(buff,sizeof(buff), &my_charset_bin);
  const uchar *from= (uchar*) from_arg;

  /* Convert character set if the old one is multi uchar */
  if (cs->mbmaxlen > 1)
  { 
    uint dummy_errors;
    tmp.copy((char*) from, len, cs, &my_charset_bin, &dummy_errors);
    from= (uchar*) tmp.ptr();
    len=  tmp.length();
  }

  const uchar *end= from+len;
  /* The pointer where the field value starts (i.e., "where to write") */
  uchar *to= ptr;
  uint tmp_dec, tmp_uint;
  /*
    The sign of the number : will be 0 (means positive but sign not
    specified), '+' or '-'
  */
  uchar sign_char=0;
  /* The pointers where prezeros start and stop */
  const uchar *pre_zeros_from, *pre_zeros_end;
  /* The pointers where digits at the left of '.' start and stop */
  const uchar *int_digits_from, *int_digits_end;
  /* The pointers where digits at the right of '.' start and stop */
  const uchar *frac_digits_from, *frac_digits_end;
  /* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */
  char expo_sign_char=0;
  uint exponent=0;                                // value of the exponent
  /*
    Pointers used when digits move from the left of the '.' to the
    right of the '.' (explained below)
  */
  const uchar *UNINIT_VAR(int_digits_tail_from);
  /* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */
  uint UNINIT_VAR(int_digits_added_zeros);
  /*
    Pointer used when digits move from the right of the '.' to the left
    of the '.'
  */
  const uchar *UNINIT_VAR(frac_digits_head_end);
  /* Number of 0 that need to be added at the right of the '.' (for 1E-3) */
  uint UNINIT_VAR(frac_digits_added_zeros);
  uchar *pos,*tmp_left_pos,*tmp_right_pos;
  /* Pointers that are used as limits (begin and end of the field buffer) */
  uchar *left_wall,*right_wall;
  uchar tmp_char;
  /*
    To remember if get_thd()->cuted_fields has already been incremented,
    to do that only once
  */
  bool is_cuted_fields_incr=0;

  /*
    There are three steps in this function :
    - parse the input string
    - modify the position of digits around the decimal dot '.' 
      according to the exponent value (if specified)
    - write the formatted number
  */

  if ((tmp_dec=dec))
    tmp_dec++;

  /* skip pre-space */
  while (from != end && my_isspace(&my_charset_bin,*from))
    from++;
  if (from == end)
  {
    set_warning(WARN_DATA_TRUNCATED, 1);
    is_cuted_fields_incr=1;
  }
  else if (*from == '+' || *from == '-')	// Found some sign ?
  {
    sign_char= *from++;
    /*
      We allow "+" for unsigned decimal unless defined different
      Both options allowed as one may wish not to have "+" for unsigned numbers
      because of data processing issues
    */ 
    if (unsigned_flag)  
    { 
      if (sign_char=='-')
      {
        Field_decimal::overflow(1);
        return 1;
      }
      /* 
	 Defining this will not store "+" for unsigned decimal type even if
	 it is passed in numeric string. This will make some tests to fail
      */	 
#ifdef DONT_ALLOW_UNSIGNED_PLUS      
      else 
        sign_char=0;
#endif 	
    }
  }

  pre_zeros_from= from;
  for (; from!=end && *from == '0'; from++) ;	// Read prezeros
  pre_zeros_end=int_digits_from=from;      
  /* Read non zero digits at the left of '.'*/
  for (; from != end && my_isdigit(&my_charset_bin, *from) ; from++) ;
  int_digits_end=from;
  if (from!=end && *from == '.')		// Some '.' ?
    from++;
  frac_digits_from= from;
  /* Read digits at the right of '.' */
  for (;from!=end && my_isdigit(&my_charset_bin, *from); from++) ;
  frac_digits_end=from;
  // Some exponentiation symbol ?
  if (from != end && (*from == 'e' || *from == 'E'))
  {   
    from++;
    if (from != end && (*from == '+' || *from == '-'))  // Some exponent sign ?
      expo_sign_char= *from++;
    else
      expo_sign_char= '+';
    /*
      Read digits of the exponent and compute its value.  We must care about
      'exponent' overflow, because as unsigned arithmetic is "modulo", big 
      exponents will become small (e.g. 1e4294967296 will become 1e0, and the 
      field will finally contain 1 instead of its max possible value).
    */
    for (;from!=end && my_isdigit(&my_charset_bin, *from); from++)
    {
      exponent=10*exponent+(*from-'0');
      if (exponent>MAX_EXPONENT)
        break;
    }
  }
  
  /*
    We only have to generate warnings if count_cuted_fields is set.
    This is to avoid extra checks of the number when they are not needed.
    Even if this flag is not set, it's OK to increment warnings, if
    it makes the code easier to read.
  */

  if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
  {
    // Skip end spaces
    for (;from != end && my_isspace(&my_charset_bin, *from); from++) ;
    if (from != end)                     // If still something left, warn
    {
      set_warning(WARN_DATA_TRUNCATED, 1);
      is_cuted_fields_incr=1;
    }
  }
  
  /*
    Now "move" digits around the decimal dot according to the exponent value,
    and add necessary zeros.
    Examples :
    - 1E+3 : needs 3 more zeros at the left of '.' (int_digits_added_zeros=3)
    - 1E-3 : '1' moves at the right of '.', and 2 more zeros are needed
    between '.' and '1'
    - 1234.5E-3 : '234' moves at the right of '.'
    These moves are implemented with pointers which point at the begin
    and end of each moved segment. Examples :
    - 1234.5E-3 : before the code below is executed, the int_digits part is
    from '1' to '4' and the frac_digits part from '5' to '5'. After the code
    below, the int_digits part is from '1' to '1', the frac_digits_head
    part is from '2' to '4', and the frac_digits part from '5' to '5'.
    - 1234.5E3 : before the code below is executed, the int_digits part is
    from '1' to '4' and the frac_digits part from '5' to '5'. After the code
    below, the int_digits part is from '1' to '4', the int_digits_tail
    part is from '5' to '5', the frac_digits part is empty, and
    int_digits_added_zeros=2 (to make 1234500).
  */
  
  /* 
     Below tmp_uint cannot overflow with small enough MAX_EXPONENT setting,
     as int_digits_added_zeros<=exponent<4G and 
     (int_digits_end-int_digits_from)<=max_allowed_packet<=2G and
     (frac_digits_from-int_digits_tail_from)<=max_allowed_packet<=2G
  */

  if (!expo_sign_char)
    tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from);
  else if (expo_sign_char == '-') 
  {
    tmp_uint=MY_MIN(exponent,(uint)(int_digits_end-int_digits_from));
    frac_digits_added_zeros=exponent-tmp_uint;
    int_digits_end -= tmp_uint;
    frac_digits_head_end=int_digits_end+tmp_uint;
    tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from);     
  }
  else // (expo_sign_char=='+') 
  {
    tmp_uint=MY_MIN(exponent,(uint)(frac_digits_end-frac_digits_from));
    int_digits_added_zeros=exponent-tmp_uint;
    int_digits_tail_from=frac_digits_from;
    frac_digits_from=frac_digits_from+tmp_uint;
    /*
      We "eat" the heading zeros of the 
      int_digits.int_digits_tail.int_digits_added_zeros concatenation
      (for example 0.003e3 must become 3 and not 0003)
    */
    if (int_digits_from == int_digits_end) 
    {
      /*
	There was nothing in the int_digits part, so continue
	eating int_digits_tail zeros
      */
      for (; int_digits_tail_from != frac_digits_from &&
	     *int_digits_tail_from == '0'; int_digits_tail_from++) ;
      if (int_digits_tail_from == frac_digits_from) 
      {
	// there were only zeros in int_digits_tail too
	int_digits_added_zeros=0;
      }
    }
    tmp_uint= (uint) (tmp_dec+(int_digits_end-int_digits_from)+
               (uint)(frac_digits_from-int_digits_tail_from)+
               int_digits_added_zeros);
  }
  
  /*
    Now write the formatted number
    
    First the digits of the int_% parts.
    Do we have enough room to write these digits ?
    If the sign is defined and '-', we need one position for it
  */

  if (field_length < tmp_uint + (int) (sign_char == '-')) 
  {
    // too big number, change to max or min number
    Field_decimal::overflow(sign_char == '-');
    return 1;
  }
 
  /*
    Tmp_left_pos is the position where the leftmost digit of
    the int_% parts will be written
  */
  tmp_left_pos=pos=to+(uint)(field_length-tmp_uint);
  
  // Write all digits of the int_% parts
  while (int_digits_from != int_digits_end)
    *pos++ = *int_digits_from++ ;

  if (expo_sign_char == '+')
  {    
    while (int_digits_tail_from != frac_digits_from)
      *pos++= *int_digits_tail_from++;
    while (int_digits_added_zeros-- >0)
      *pos++= '0';  
  }
  /*
    Note the position where the rightmost digit of the int_% parts has been
    written (this is to later check if the int_% parts contained nothing,
    meaning an extra 0 is needed).
  */
  tmp_right_pos=pos;

  /*
    Step back to the position of the leftmost digit of the int_% parts,
    to write sign and fill with zeros or blanks or prezeros.
  */
  pos=tmp_left_pos-1;
  if (zerofill)
  {
    left_wall=to-1;
    while (pos > left_wall)			// Fill with zeros
      *pos--='0';
  }
  else
  {
    left_wall=to+(sign_char != 0)-1;
    if (!expo_sign_char)	// If exponent was specified, ignore prezeros
    {
      for (;pos > left_wall && pre_zeros_from !=pre_zeros_end;
	   pre_zeros_from++)
	*pos--= '0';
    }
    if (pos == tmp_right_pos-1)
      *pos--= '0';		// no 0 has ever been written, so write one
    left_wall= to-1;
    if (sign_char && pos != left_wall)
    {
      /* Write sign if possible (it is if sign is '-') */
      *pos--= sign_char;
    }
    while (pos != left_wall)
      *pos--=' ';  //fill with blanks
  }
  
  /*
    Write digits of the frac_% parts ;
    Depending on get_thd()->count_cuted_fields, we may also want
    to know if some non-zero tail of these parts will
    be truncated (for example, 0.002->0.00 will generate a warning,
    while 0.000->0.00 will not)
    (and 0E1000000000 will not, while 1E-1000000000 will)
  */
      
  pos=to+(uint)(field_length-tmp_dec);	// Calculate post to '.'
  right_wall=to+field_length;
  if (pos != right_wall) 
    *pos++='.';

  if (expo_sign_char == '-')
  {
    while (frac_digits_added_zeros-- > 0)
    {
      if (pos == right_wall) 
      {
        if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
            !is_cuted_fields_incr)
          break; // Go on below to see if we lose non zero digits
        return 0;
      }
      *pos++='0';
    }
    while (int_digits_end != frac_digits_head_end)
    {
      tmp_char= *int_digits_end++;
      if (pos == right_wall)
      {
        if (tmp_char != '0')			// Losing a non zero digit ?
        {
          if (!is_cuted_fields_incr)
            set_warning(WARN_DATA_TRUNCATED, 1);
          return 0;
        }
        continue;
      }
      *pos++= tmp_char;
    }
  }

  for (;frac_digits_from!=frac_digits_end;) 
  {
    tmp_char= *frac_digits_from++;
    if (pos == right_wall)
    {
      if (tmp_char != '0')			// Losing a non zero digit ?
      {
        if (!is_cuted_fields_incr)
        {
          /*
            This is a note, not a warning, as we don't want to abort
            when we cut decimals in strict mode
          */
	  set_note(WARN_DATA_TRUNCATED, 1);
        }
        return 0;
      }
      continue;
    }
    *pos++= tmp_char;
  }
      
  while (pos != right_wall)
   *pos++='0';			// Fill with zeros at right of '.'
  return 0;
}


int Field_decimal::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  if (unsigned_flag && nr < 0)
  {
    overflow(1);
    return 1;
  }
  
  if (!std::isfinite(nr)) // Handle infinity as special case
  {
    overflow(nr < 0.0);
    return 1;
  }

  size_t length;
  uchar fyllchar,*to;
  char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];

  fyllchar = zerofill ? (char) '0' : (char) ' ';
  length= my_fcvt(nr, dec, buff, NULL);

  if (length > field_length)
  {
    overflow(nr < 0.0);
    return 1;
  }
  else
  {
    to=ptr;
    for (size_t i=field_length-length ; i-- > 0 ;)
      *to++ = fyllchar;
    memcpy(to,buff,length);
    return 0;
  }
}


int Field_decimal::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  char buff[22];
  uint length, int_part;
  char fyllchar;
  uchar *to;

  if (nr < 0 && unsigned_flag && !unsigned_val)
  {
    overflow(1);
    return 1;
  }
  length= (uint) (longlong10_to_str(nr,buff,unsigned_val ? 10 : -10) - buff);
  int_part= field_length- (dec  ? dec+1 : 0);

  if (length > int_part)
  {
    overflow(!unsigned_val && nr < 0L);		/* purecov: inspected */
    return 1;
  }

  fyllchar = zerofill ? (char) '0' : (char) ' ';
  to= ptr;
  for (uint i=int_part-length ; i-- > 0 ;)
    *to++ = fyllchar;
  memcpy(to,buff,length);
  if (dec)
  {
    to[length]='.';
    bfill(to+length+1,dec,'0');
  }
  return 0;
}


double Field_decimal::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  int not_used;
  char *end_not_used;
  return my_charset_bin.strntod((char*) ptr, field_length, &end_not_used, &not_used);
}

longlong Field_decimal::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  int not_used;
  if (unsigned_flag)
    return my_charset_bin.strntoull((char*) ptr, field_length, 10, NULL, &not_used);
  return my_charset_bin.strntoll((char*) ptr, field_length, 10, NULL, &not_used);
}


String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
			       String *val_ptr)
{
  DBUG_ASSERT(marked_for_read());
  uchar *str;
  size_t tmp_length;

  for (str=ptr ; *str == ' ' ; str++) ;
  val_ptr->set_charset(&my_charset_numeric);
  tmp_length= (size_t) (str-ptr);
  if (field_length < tmp_length)		// Error in data
    val_ptr->length(0);
  else
    val_ptr->set_ascii((const char*) str, field_length-tmp_length);
  return val_ptr;
}

/**
  Should be able to handle at least the following fixed decimal formats:
  5.00 , -1.0,  05,  -05, +5 with optional pre/end space
*/

int Field_decimal::cmp(const uchar *a_ptr,const uchar *b_ptr) const
{
  const uchar *end;
  int swap=0;
  /* First remove prefixes '0', ' ', and '-' */
  for (end=a_ptr+field_length;
       a_ptr != end &&
	 (*a_ptr == *b_ptr ||
	  ((my_isspace(&my_charset_bin,*a_ptr)  || *a_ptr == '+' || 
            *a_ptr == '0') &&
	   (my_isspace(&my_charset_bin,*b_ptr) || *b_ptr == '+' || 
            *b_ptr == '0')));
       a_ptr++,b_ptr++)
  {
    if (*a_ptr == '-')				// If both numbers are negative
      swap= -1 ^ 1;				// Swap result      
  }
  if (a_ptr == end)
    return 0;
  if (*a_ptr == '-')
    return -1;
  if (*b_ptr == '-')
    return 1;

  while (a_ptr != end)
  {
    if (*a_ptr++ != *b_ptr++)
      return swap ^ (a_ptr[-1] < b_ptr[-1] ? -1 : 1); // compare digits
  }
  return 0;
}


void Field_decimal::sort_string(uchar *to,uint length)
{
  uchar *str,*end;
  for (str=ptr,end=ptr+length;
       str != end &&
	 ((my_isspace(&my_charset_bin,*str) || *str == '+' ||
	   *str == '0')) ;
       str++)
    *to++=' ';
  if (str == end)
    return;					/* purecov: inspected */

  if (*str == '-')
  {
    *to++=1;					// Smaller than any number
    str++;
    while (str != end)
      if (my_isdigit(&my_charset_bin,*str))
	*to++= (char) ('9' - *str++);
      else
	*to++= *str++;
  }
  else memcpy(to,str,(uint) (end-str));
}


void Field_decimal::sql_type(String &res) const
{
  CHARSET_INFO *cs=res.charset();
  uint tmp=field_length;
  if (!unsigned_flag)
    tmp--;
  if (dec)
    tmp--;
  res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
			  "decimal(%d,%d)/*old*/",tmp,dec));
  add_zerofill_and_unsigned(res);
}


Field *Field_decimal::make_new_field(MEM_ROOT *root, TABLE *new_table,
                                     bool keep_type)
{
  if (keep_type)
    return Field_real::make_new_field(root, new_table, keep_type);

  Field *field= new (root) Field_new_decimal(NULL, field_length,
                                             maybe_null() ? (uchar*) "" : 0, 0,
                                             NONE, &field_name,
                                             dec, flags & ZEROFILL_FLAG,
                                             unsigned_flag);
  if (field)
    field->init_for_make_new_field(new_table, orig_table);
  return field;
}


/****************************************************************************
** Field_new_decimal
****************************************************************************/

static decimal_digits_t get_decimal_precision(uint len, decimal_digits_t dec,
                                              bool unsigned_val)
{
  uint precision= my_decimal_length_to_precision(len, dec, unsigned_val);
  return (decimal_digits_t) MY_MIN(precision, DECIMAL_MAX_PRECISION);
}

Field_new_decimal::Field_new_decimal(uchar *ptr_arg,
                                     uint32 len_arg, uchar *null_ptr_arg,
                                     uchar null_bit_arg,
                                     enum utype unireg_check_arg,
                                     const LEX_CSTRING *field_name_arg,
                                     decimal_digits_t dec_arg,bool zero_arg,
                                     bool unsigned_arg)
  :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
             unireg_check_arg, field_name_arg,
             MY_MIN(dec_arg, DECIMAL_MAX_SCALE), zero_arg, unsigned_arg)
{
  precision= get_decimal_precision(len_arg, dec_arg, unsigned_arg);
  DBUG_ASSERT(precision <= DECIMAL_MAX_PRECISION);
  DBUG_ASSERT(dec <= DECIMAL_MAX_SCALE);
  bin_size= my_decimal_get_binary_size(precision, dec);
}


int Field_new_decimal::reset(void)
{
  store_value(&decimal_zero);
  return 0;
}


/**
  Generate max/min decimal value in case of overflow.

  @param decimal_value     buffer for value
  @param sign              sign of value which caused overflow
*/

void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
                                              bool sign)
{
  DBUG_ENTER("Field_new_decimal::set_value_on_overflow");
  max_my_decimal(decimal_value, precision, decimals());
  if (sign)
  {
    if (unsigned_flag)
      my_decimal_set_zero(decimal_value);
    else
      decimal_value->sign(TRUE);
  }
  DBUG_VOID_RETURN;
}


/**
  Store decimal value in the binary buffer.

  Checks if decimal_value fits into field size.
  If it does, stores the decimal in the buffer using binary format.
  Otherwise sets maximal number that can be stored in the field.

  @param       decimal_value   my_decimal
  @param [OUT] native_error    the error returned by my_decimal::to_binary().

  @retval
    0 ok
  @retval
    1 error
*/

bool Field_new_decimal::store_value(const my_decimal *decimal_value,
                                    int *native_error)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  DBUG_ENTER("Field_new_decimal::store_value");
#ifdef DBUG_TRACE
  {
    char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
    DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, decimal_value)));
  }
#endif

  /* check that we do not try to write negative value in unsigned field */
  if (unsigned_flag && decimal_value->sign())
  {
    DBUG_PRINT("info", ("unsigned overflow"));
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    error= 1;
    decimal_value= &decimal_zero;
  }
#ifdef DBUG_TRACE
  {
    char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
    DBUG_PRINT("info", ("saving with precision %d  scale: %d  value %s",
                        (int)precision, (int)dec,
                        dbug_decimal_as_string(dbug_buff, decimal_value)));
  }
#endif

  *native_error= decimal_value->to_binary(ptr, precision, dec,
                                          E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW);

  if (unlikely(*native_error == E_DEC_OVERFLOW))
  {
    my_decimal buff;
    DBUG_PRINT("info", ("overflow"));
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    set_value_on_overflow(&buff, decimal_value->sign());
    buff.to_binary(ptr, precision, dec);
    error= 1;
  }
  DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
                                          bin_size););
  DBUG_RETURN(error);
}


bool Field_new_decimal::store_value(const my_decimal *decimal_value)
{
  int native_error;
  bool rc= store_value(decimal_value, &native_error);
  if (unlikely(!rc && native_error == E_DEC_TRUNCATED))
    set_note(WARN_DATA_TRUNCATED, 1);
  return rc;
}


int Field_new_decimal::store(const char *from, size_t length,
                             CHARSET_INFO *charset_arg)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  my_decimal decimal_value;
  THD *thd= get_thd();
  DBUG_ENTER("Field_new_decimal::store(char*)");

  const char *end;
  int err= str2my_decimal(E_DEC_FATAL_ERROR &
                          ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM),
                           from, length, charset_arg,
                           &decimal_value, &end);

  if (err == E_DEC_OVERFLOW) // Too many digits (>81) in the integer part
  {
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    if (!thd->abort_on_warning)
    {
      set_value_on_overflow(&decimal_value, decimal_value.sign());
      store_decimal(&decimal_value);
    }
    DBUG_RETURN(1);
  }

  if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION)
  {
    if (check_edom_and_important_data_truncation("decimal",
                                                 err && err != E_DEC_TRUNCATED,
                                                 charset_arg,
                                                 from, length, end))
    {
      if (!thd->abort_on_warning)
      {
        if (err && err != E_DEC_TRUNCATED)
        {
          /*
            If check_decimal() failed because of EDOM-alike error,
            (e.g. E_DEC_BAD_NUM), we have to initialize decimal_value to zero.
            Note: if check_decimal() failed because of truncation,
            decimal_value is already properly initialized.
          */
          my_decimal_set_zero(&decimal_value);
          /*
            TODO: check str2my_decimal() with HF. It seems to do
            decimal_make_zero() on fatal errors, so my_decimal_set_zero()
            is probably not needed here.
          */
        }
        store_decimal(&decimal_value);
      }
      DBUG_RETURN(1);
    }
  }

#ifdef DBUG_TRACE
  char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
  DBUG_PRINT("enter", ("value: %s",
                       dbug_decimal_as_string(dbug_buff, &decimal_value)));
#endif
  int err2;
  if (store_value(&decimal_value, &err2))
    DBUG_RETURN(1);

  /*
    E_DEC_TRUNCATED means minor truncation, a note should be enough:
    - in err: str2my_decimal() truncated '1e-1000000000000' to 0.0
    - in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2)
    Also, we send a note if a string had some trailing spaces: '1.12 '
  */
  if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
      (err == E_DEC_TRUNCATED ||
       err2 == E_DEC_TRUNCATED ||
       end < from + length))
    set_note(WARN_DATA_TRUNCATED, 1);
  DBUG_RETURN(0);
}


/**
  @todo
  Fix following when double2my_decimal when double2decimal
  will return E_DEC_TRUNCATED always correctly
*/

int Field_new_decimal::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  my_decimal decimal_value;
  int err;
  THD *thd= get_thd();
  DBUG_ENTER("Field_new_decimal::store(double)");

  err= double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr,
                         &decimal_value);
  if (err)
  {
    if (check_overflow(err))
      set_value_on_overflow(&decimal_value, decimal_value.sign());
    /* Only issue a warning if store_value doesn't issue an warning */
    thd->got_warning= 0;
  }
  if (store_value(&decimal_value))
    err= 1;
  else if (err && !thd->got_warning)
    err= warn_if_overflow(err);
  DBUG_RETURN(err);
}


int Field_new_decimal::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  my_decimal decimal_value;
  int err;

  if ((err= int2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
                           nr, unsigned_val, &decimal_value)))
  {
    if (check_overflow(err))
      set_value_on_overflow(&decimal_value, decimal_value.sign());
    /* Only issue a warning if store_value doesn't issue an warning */
    get_thd()->got_warning= 0;
  }
  if (store_value(&decimal_value))
    err= 1;
  else if (err && !get_thd()->got_warning)
    err= warn_if_overflow(err);
  return err;
}


int Field_new_decimal::store_decimal(const my_decimal *decimal_value)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  return store_value(decimal_value);
}


int Field_new_decimal::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
{
  my_decimal decimal_value;
  return store_value(date2my_decimal(ltime, &decimal_value));
}


my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
{
  DBUG_ASSERT(marked_for_read());
  DBUG_ENTER("Field_new_decimal::val_decimal");
  binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value,
                    precision, dec);
  DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
                                          bin_size););
  DBUG_RETURN(decimal_value);
}


int Field_new_decimal::cmp(const uchar *a,const uchar*b) const
{
  return memcmp(a, b, bin_size);
}


void Field_new_decimal::sort_string(uchar *buff, uint length)
{
  memcpy(buff, ptr, length);
}


void Field_new_decimal::sql_type(String &str) const
{
  CHARSET_INFO *cs= str.charset();
  str.length(cs->cset->snprintf(cs, (char*) str.ptr(), str.alloced_length(),
                                "decimal(%d,%d)", precision, (int)dec));
  add_zerofill_and_unsigned(str);
}


/**
   Save the field metadata for new decimal fields.

   Saves the precision in the first byte and decimals() in the second
   byte of the field metadata array at index of *metadata_ptr and 
   *(metadata_ptr + 1).

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/

Binlog_type_info Field_new_decimal::binlog_type_info() const
{
  DBUG_ASSERT(Field_new_decimal::type() == binlog_type());
  return Binlog_type_info(Field_new_decimal::type(), precision +
                          (decimals() << 8), 2, binlog_signedness());
}


/**
   Returns the number of bytes field uses in row-based replication 
   row packed size.

   This method is used in row-based replication to determine the number
   of bytes that the field consumes in the row record format. This is
   used to skip fields in the master that do not exist on the slave.

   @param   field_metadata   Encoded size in field metadata

   @returns The size of the field based on the field metadata.
*/
uint Field_new_decimal::pack_length_from_metadata(uint field_metadata) const
{
  uint const source_precision= (field_metadata >> 8U) & 0x00ff;
  uint const source_decimal= field_metadata & 0x00ff; 
  uint const source_size= my_decimal_get_binary_size(source_precision, 
                                                     source_decimal);
  return (source_size);
}


bool Field_new_decimal::compatible_field_size(uint field_metadata,
                                              const Relay_log_info * __attribute__((unused)),
                                              uint16 mflags __attribute__((unused)),
                                              int *order_var) const
{
  uint const source_precision= (field_metadata >> 8U) & 0x00ff;
  uint const source_decimal= field_metadata & 0x00ff; 
  int order= compare(source_precision, precision);
  *order_var= order != 0 ? order : compare(source_decimal, dec);
  return true;
}


bool Field_new_decimal::is_equal(const Column_definition &new_field) const
{
  return ((new_field.type_handler() == type_handler()) &&
          ((new_field.flags & UNSIGNED_FLAG) ==
           (uint) (flags & UNSIGNED_FLAG)) &&
          ((new_field.flags & AUTO_INCREMENT_FLAG) <=
           (uint) (flags & AUTO_INCREMENT_FLAG)) &&
          (new_field.length == max_display_length()) &&
          (new_field.decimals == dec));
}


/**
   Unpack a decimal field from row data.

   This method is used to unpack a decimal or numeric field from a master
   whose size of the field is less than that of the slave.
  
   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Precision (upper) and decimal (lower) values

   @return  New pointer into memory based on from + length of the data
*/
const uchar *
Field_new_decimal::unpack(uchar* to, const uchar *from, const uchar *from_end,
                          uint param_data)
{
  if (param_data == 0)
    return Field::unpack(to, from, from_end, param_data);

  uint from_precision= (param_data & 0xff00) >> 8U;
  uint from_decimal= param_data & 0x00ff;
  uint length=pack_length();
  uint from_pack_len= my_decimal_get_binary_size(from_precision, from_decimal);
  uint len= (param_data && (from_pack_len < length)) ?
            from_pack_len : length;
  if ((from_pack_len && (from_pack_len < length)) ||
      (from_precision < precision) ||
      (from_decimal < decimals()))
  {
    /*
      If the master's data is smaller than the slave, we need to convert
      the binary to decimal then resize the decimal converting it back to
      a decimal and write that to the raw data buffer.
    */
    decimal_digit_t dec_buf[DECIMAL_MAX_PRECISION];
    decimal_t dec_val;
    dec_val.len= from_precision;
    dec_val.buf= dec_buf;
    /*
      Note: bin2decimal does not change the length of the field. So it is
      just the first step the resizing operation. The second step does the
      resizing using the precision and decimals from the slave.
    */
    bin2decimal((uchar *)from, &dec_val, from_precision, from_decimal);
    decimal2bin(&dec_val, to, precision, decimals());
  }
  else
  {
    if (from + len > from_end)
      return 0;                                 // Wrong data
    memcpy(to, from, len); // Sizes are the same, just copy the data.
  }
  return from+len;
}


Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx,
                                              Item *const_item)
{
  if (flags & ZEROFILL_FLAG)
    return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item);
  switch (ctx.subst_constraint()) {
  case IDENTITY_SUBST:
    if (const_item->field_type() != MYSQL_TYPE_NEWDECIMAL ||
        const_item->decimal_scale() != decimals())
    {
      VDec val(const_item);
      if (val.is_null())
      {
        DBUG_ASSERT(0);
        return const_item;
      }
      /*
        Truncate or extend the decimal value to the scale of the field.
        See comments about truncation in the same place in
        Field_time::get_equal_const_item().
      */
      my_decimal tmp;
      val.round_to(&tmp, decimals(), TRUNCATE);
      return new (thd->mem_root) Item_decimal(thd, field_name.str, &tmp,
                                              decimals(), field_length);
    }
    break;
  case ANY_SUBST:
    break;
  }
  return const_item;
}


int Field_int::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
{
  longlong v= TIME_to_ulonglong(ltime);
  if (ltime->neg == 0)
    return store(v, true);
  return store(-v, false);
}


void Field_int::sql_type(String &res) const
{
  CHARSET_INFO *cs=res.charset();
  Name name= type_handler()->type_handler_signed()->name();
  res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
			  "%.*s(%d)", (int) name.length(), name.ptr(),
			  (int) field_length));
  add_zerofill_and_unsigned(res);
}


/****************************************************************************
** tiny int
****************************************************************************/

int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error;
  longlong rnd;
  
  error= get_int(cs, from, len, &rnd, 255, -128, 127);
  ptr[0]= unsigned_flag ? (char) (ulonglong) rnd : (char) rnd;
  return error;
}


int Field_tiny::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  nr=rint(nr);
  if (unsigned_flag)
  {
    if (nr < 0.0)
    {
      *ptr=0;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if (nr > 255.0)
    {
      *ptr= (uchar) 255;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      *ptr= (uchar) nr;
  }
  else
  {
    if (nr < -128.0)
    {
      *ptr= (uchar) -128;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if (nr > 127.0)
    {
      *ptr=127;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      *ptr=(uchar) (int) nr;
  }
  return error;
}


int Field_tiny::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;

  if (unsigned_flag)
  {
    if (nr < 0 && !unsigned_val)
    {
      *ptr= 0;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if ((ulonglong) nr > (ulonglong) 255)
    {
      *ptr= (char) 255;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      *ptr=(char) nr;
  }
  else
  {
    if (nr < 0 && unsigned_val)
      nr= 256;                                    // Generate overflow
    if (nr < -128)
    {
      *ptr= (char) -128;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if (nr > 127)
    {
      *ptr=127;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      *ptr=(char) nr;
  }
  return error;
}


double Field_tiny::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  int tmp= unsigned_flag ? (int) ptr[0] :
    (int) ((signed char*) ptr)[0];
  return (double) tmp;
}


longlong Field_tiny::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  int tmp= unsigned_flag ? (int) ptr[0] :
    (int) ((signed char*) ptr)[0];
  return (longlong) tmp;
}


String *Field_tiny::val_str(String *val_buffer,
			    String *val_ptr __attribute__((unused)))
{
  DBUG_ASSERT(marked_for_read());
  long nr= unsigned_flag ? (long) ptr[0] : (long) ((signed char*) ptr)[0];
  return val_str_from_long(val_buffer, 5, -10, nr);
}

bool Field_tiny::send(Protocol *protocol)
{
  DBUG_ASSERT(marked_for_read());
  Protocol_text *txt;
  if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
    return send_numeric_zerofill_str(txt, PROTOCOL_SEND_TINY);
  return protocol->store_tiny(Field_tiny::val_int());
}


int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  signed char a,b;
  a=(signed char) a_ptr[0]; b= (signed char) b_ptr[0];
  if (unsigned_flag)
    return ((uchar) a < (uchar) b) ? -1 : ((uchar) a > (uchar) b) ? 1 : 0;
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_tiny::sort_string(uchar *to,uint length __attribute__((unused)))
{
  if (unsigned_flag)
    *to= *ptr;
  else
    to[0] = (char) (ptr[0] ^ (uchar) 128);	/* Revers signbit */
}

/****************************************************************************
 Field type short int (2 byte)
****************************************************************************/

int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int store_tmp;
  int error;
  longlong rnd;
  
  error= get_int(cs, from, len, &rnd, UINT_MAX16, INT_MIN16, INT_MAX16);
  store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd;
  int2store(ptr, store_tmp);
  return error;
}


int Field_short::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  int16 res;
  nr=rint(nr);
  if (unsigned_flag)
  {
    if (nr < 0)
    {
      res=0;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if (nr > (double) UINT_MAX16)
    {
      res=(int16) UINT_MAX16;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      res=(int16) (uint16) nr;
  }
  else
  {
    if (nr < (double) INT_MIN16)
    {
      res=INT_MIN16;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if (nr > (double) INT_MAX16)
    {
      res=INT_MAX16;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      res=(int16) (int) nr;
  }
  int2store(ptr,res);
  return error;
}


int Field_short::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  int16 res;

  if (unsigned_flag)
  {
    if (nr < 0L && !unsigned_val)
    {
      res=0;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if ((ulonglong) nr > (ulonglong) UINT_MAX16)
    {
      res=(int16) UINT_MAX16;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      res=(int16) (uint16) nr;
  }
  else
  {
    if (nr < 0 && unsigned_val)
      nr= UINT_MAX16+1;                         // Generate overflow

    if (nr < INT_MIN16)
    {
      res=INT_MIN16;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if (nr > (longlong) INT_MAX16)
    {
      res=INT_MAX16;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      res=(int16) nr;
  }
  int2store(ptr,res);
  return error;
}


double Field_short::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  short j;
  j=sint2korr(ptr);
  return unsigned_flag ? (double) (unsigned short) j : (double) j;
}

longlong Field_short::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  short j;
  j=sint2korr(ptr);
  return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j;
}


String *Field_short::val_str(String *val_buffer,
			     String *val_ptr __attribute__((unused)))
{
  DBUG_ASSERT(marked_for_read());
  short j= sint2korr(ptr);
  long nr= unsigned_flag ? (long) (unsigned short) j : (long) j;
  return val_str_from_long(val_buffer, 7, -10, nr);
}


bool Field_short::send(Protocol *protocol)
{
  DBUG_ASSERT(marked_for_read());
  Protocol_text *txt;
  if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
    return send_numeric_zerofill_str(txt, PROTOCOL_SEND_SHORT);
  return protocol->store_short(Field_short::val_int());
}


int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  short a,b;
  a=sint2korr(a_ptr);
  b=sint2korr(b_ptr);

  if (unsigned_flag)
    return ((unsigned short) a < (unsigned short) b) ? -1 :
    ((unsigned short) a > (unsigned short) b) ? 1 : 0;
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_short::sort_string(uchar *to,uint length __attribute__((unused)))
{
  if (unsigned_flag)
    to[0] = ptr[1];
  else
    to[0] = (char) (ptr[1] ^ 128);              /* Revers signbit */
  to[1]   = ptr[0];
}

/****************************************************************************
  Field type medium int (3 byte)
****************************************************************************/

int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int store_tmp;
  int error;
  longlong rnd;
  
  error= get_int(cs, from, len, &rnd, UINT_MAX24, INT_MIN24, INT_MAX24);
  store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd;
  int3store(ptr, store_tmp);
  return error;
}


int Field_medium::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  nr=rint(nr);
  if (unsigned_flag)
  {
    if (nr < 0)
    {
      int3store(ptr,0);
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if (nr >= (double) (long) (1L << 24))
    {
      uint32 tmp=(uint32) (1L << 24)-1L;
      int3store(ptr,tmp);
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      int3store(ptr,(uint32) nr);
  }
  else
  {
    if (nr < (double) INT_MIN24)
    {
      long tmp=(long) INT_MIN24;
      int3store(ptr,tmp);
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if (nr > (double) INT_MAX24)
    {
      long tmp=(long) INT_MAX24;
      int3store(ptr,tmp);
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      int3store(ptr,(long) nr);
  }
  return error;
}


int Field_medium::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;

  if (unsigned_flag)
  {
    if (nr < 0 && !unsigned_val)
    {
      int3store(ptr,0);
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24))
    {
      long tmp= (long) (1L << 24)-1L;
      int3store(ptr,tmp);
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      int3store(ptr,(uint32) nr);
  }
  else
  {
    if (nr < 0 && unsigned_val)
      nr= (ulonglong) (long) (1L << 24);        // Generate overflow

    if (nr < (longlong) INT_MIN24)
    {
      long tmp= (long) INT_MIN24;
      int3store(ptr,tmp);
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else if (nr > (longlong) INT_MAX24)
    {
      long tmp=(long) INT_MAX24;
      int3store(ptr,tmp);
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      int3store(ptr,(long) nr);
  }
  return error;
}


double Field_medium::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
  return (double) j;
}


longlong Field_medium::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
  return (longlong) j;
}


String *Field_medium::val_str(String *val_buffer,
			      String *val_ptr __attribute__((unused)))
{
  DBUG_ASSERT(marked_for_read());
  long nr= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
  return val_str_from_long(val_buffer, 10, -10, nr);
}


String *Field_int::val_str_from_long(String *val_buffer,
                                     uint max_char_length,
                                     int radix, long nr)
{
  CHARSET_INFO *cs= &my_charset_numeric;
  uint length;
  uint mlength= MY_MAX(field_length + 1, max_char_length * cs->mbmaxlen);
  val_buffer->alloc(mlength);
  char *to=(char*) val_buffer->ptr();
  length= (uint) cs->long10_to_str(to, mlength, radix, nr);
  val_buffer->length(length);
  if (zerofill)
    prepend_zeros(val_buffer); /* purecov: inspected */
  val_buffer->set_charset(cs);
  return val_buffer;
}


bool Field_medium::send(Protocol *protocol)
{
  DBUG_ASSERT(marked_for_read());
  Protocol_text *txt;
  if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
    return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG);
  return protocol->store_long(Field_medium::val_int());
}


int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  long a,b;
  if (unsigned_flag)
  {
    a=uint3korr(a_ptr);
    b=uint3korr(b_ptr);
  }
  else
  {
    a=sint3korr(a_ptr);
    b=sint3korr(b_ptr);
  }
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_medium::sort_string(uchar *to,uint length __attribute__((unused)))
{
  if (unsigned_flag)
    to[0] = ptr[2];
  else
    to[0] = (uchar) (ptr[2] ^ 128);		/* Revers signbit */
  to[1] = ptr[1];
  to[2] = ptr[0];
}


/****************************************************************************
** long int
****************************************************************************/

int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  long store_tmp;
  int error;
  longlong rnd;
  
  error= get_int(cs, from, len, &rnd, UINT_MAX32, INT_MIN32, INT_MAX32);
  store_tmp= unsigned_flag ? (long) (ulonglong) rnd : (long) rnd;
  int4store(ptr, store_tmp);
  return error;
}


int Field_long::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  int32 res;
  nr=rint(nr);
  if (unsigned_flag)
  {
    if (nr < 0)
    {
      res=0;
      error= 1;
    }
    else if (nr > (double) UINT_MAX32)
    {
      res= UINT_MAX32;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
    else
      res=(int32) (ulong) nr;
  }
  else
  {
    if (nr < (double) INT_MIN32)
    {
      res=(int32) INT_MIN32;
      error= 1;
    }
    else if (nr > (double) INT_MAX32)
    {
      res=(int32) INT_MAX32;
      error= 1;
    }
    else
      res=(int32) (longlong) nr;
  }
  if (unlikely(error))
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);

  int4store(ptr,res);
  return error;
}


int Field_long::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  int32 res;

  if (unsigned_flag)
  {
    if (nr < 0 && !unsigned_val)
    {
      res=0;
      error= 1;
    }
    else if ((ulonglong) nr >= (1LL << 32))
    {
      res=(int32) (uint32) ~0L;
      error= 1;
    }
    else
      res=(int32) (uint32) nr;
  }
  else
  {
    if (nr < 0 && unsigned_val)
      nr= ((longlong) INT_MAX32) + 1;           // Generate overflow
    if (nr < (longlong) INT_MIN32) 
    {
      res=(int32) INT_MIN32;
      error= 1;
    }
    else if (nr > (longlong) INT_MAX32)
    {
      res=(int32) INT_MAX32;
      error= 1;
    }
    else
      res=(int32) nr;
  }
  if (unlikely(error))
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);

  int4store(ptr,res);
  return error;
}


double Field_long::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  int32 j;
  j=sint4korr(ptr);
  return unsigned_flag ? (double) (uint32) j : (double) j;
}

longlong Field_long::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  int32 j;
  /* See the comment in Field_long::store(long long) */
  DBUG_ASSERT(!table || table->in_use == current_thd);
  j=sint4korr(ptr);
  return unsigned_flag ? (longlong) (uint32) j : (longlong) j;
}


String *Field_long::val_str(String *val_buffer,
			    String *val_ptr __attribute__((unused)))
{
  DBUG_ASSERT(marked_for_read());
  long nr= unsigned_flag ? (long) uint4korr(ptr) : sint4korr(ptr);
  return val_str_from_long(val_buffer, 12, unsigned_flag ? 10 : -10, nr);
}


bool Field_long::send(Protocol *protocol)
{
  DBUG_ASSERT(marked_for_read());
  Protocol_text *txt;
  if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
    return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG);
  return protocol->store_long(Field_long::val_int());
}


int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  int32 a,b;
  a=sint4korr(a_ptr);
  b=sint4korr(b_ptr);
  if (unsigned_flag)
    return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_long::sort_string(uchar *to,uint length __attribute__((unused)))
{
  if (unsigned_flag)
    to[0] = ptr[3];
  else
    to[0] = (char) (ptr[3] ^ 128);              /* Revers signbit */
  to[1]   = ptr[2];
  to[2]   = ptr[1];
  to[3]   = ptr[0];
}


/****************************************************************************
 Field type longlong int (8 bytes)
****************************************************************************/

int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  char *end;
  ulonglong tmp;

  tmp= cs->strntoull10rnd(from, len, unsigned_flag, &end, &error);
  if (unlikely(error == MY_ERRNO_ERANGE))
  {
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    error= 1;
  }
  else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
           check_int(cs, from, len, end, error))
    error= 1;
  else
    error= 0;
  int8store(ptr,tmp);
  return error;
}


int Field_longlong::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  Converter_double_to_longlong conv(nr, unsigned_flag);

  if (unlikely(conv.error()))
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);

  int8store(ptr, conv.result());
  return conv.error();
}


int Field_longlong::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;

  if (unlikely(nr < 0))                         // Only possible error
  {
    /*
      if field is unsigned and value is signed (< 0) or
      if field is signed and value is unsigned we have an overflow
    */
    if (unsigned_flag != unsigned_val)
    {
      nr= unsigned_flag ? (ulonglong) 0 : (ulonglong) LONGLONG_MAX;
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
      error= 1;
    }
  }

  int8store(ptr,nr);
  return error;
}


double Field_longlong::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  longlong j;
  j=sint8korr(ptr);
  /* The following is open coded to avoid a bug in gcc 3.3 */
  if (unsigned_flag)
  {
    ulonglong tmp= (ulonglong) j;
    return ulonglong2double(tmp);
  }
  return (double) j;
}


longlong Field_longlong::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  longlong j;
  j=sint8korr(ptr);
  return j;
}


String *Field_longlong::val_str(String *val_buffer,
				String *val_ptr __attribute__((unused)))
{
  CHARSET_INFO *cs= &my_charset_numeric;
  uint length;
  uint mlength=MY_MAX(field_length+1,22*cs->mbmaxlen);
  val_buffer->alloc(mlength);
  char *to=(char*) val_buffer->ptr();
  longlong j;
  j=sint8korr(ptr);

  length=(uint) (cs->longlong10_to_str)(to, mlength,
		                        unsigned_flag ? 10 : -10, j);
  val_buffer->length(length);
  if (zerofill)
    prepend_zeros(val_buffer);
  val_buffer->set_charset(cs);
  return val_buffer;
}


bool Field_longlong::send(Protocol *protocol)
{
  DBUG_ASSERT(marked_for_read());
  Protocol_text *txt;
  if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
    return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONGLONG);
  return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag);
}


int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  longlong a,b;
  a=sint8korr(a_ptr);
  b=sint8korr(b_ptr);
  if (unsigned_flag)
    return ((ulonglong) a < (ulonglong) b) ? -1 :
    ((ulonglong) a > (ulonglong) b) ? 1 : 0;
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_longlong::sort_string(uchar *to,uint length __attribute__((unused)))
{
  if (unsigned_flag)
    to[0] = ptr[7];
  else
    to[0] = (char) (ptr[7] ^ 128);		/* Revers signbit */
  to[1]   = ptr[6];
  to[2]   = ptr[5];
  to[3]   = ptr[4];
  to[4]   = ptr[3];
  to[5]   = ptr[2];
  to[6]   = ptr[1];
  to[7]   = ptr[0];
}


void Field_longlong::set_max()
{
  DBUG_ASSERT(marked_for_write_or_computed());
  set_notnull();
  int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX);
}

bool Field_longlong::is_max(const uchar *ptr_arg) const
{
  DBUG_ASSERT(marked_for_read());
  if (unsigned_flag)
  {
    ulonglong j;
    j= uint8korr(ptr_arg);
    return j == ULONGLONG_MAX;
  }
  longlong j;
  j= sint8korr(ptr_arg);
  return j == LONGLONG_MAX;
}

/*
  Floating-point numbers
 */

/****************************************************************************
  single precision float
****************************************************************************/

int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs)
{
  int error;
  Field_float::store(get_double(from, len, cs, &error));
  return error;
}


int Field_float::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= truncate_double(&nr, field_length,
                             not_fixed ? NOT_FIXED_DEC : dec,
                             unsigned_flag, FLT_MAX);
  if (unlikely(error))
  {
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    if (error < 0)                                // Wrong double value
    {
      error= 1;
      set_null();
    }
  }
  float j= (float)nr;

  float4store(ptr,j);
  return error;
}


int Field_float::store(longlong nr, bool unsigned_val)
{
  return Field_float::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
                            (double) nr);
}


double Field_float::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  return ((double) get_float(ptr));
}

longlong Field_float::val_int(void)
{
  return Converter_double_to_longlong(get_float(ptr), false).result();
}


String *Field_float::val_str(String *val_buffer,
			     String *val_ptr __attribute__((unused)))
{
  DBUG_ASSERT(marked_for_read());
  DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);

  if (Float(ptr).to_string(val_buffer, dec))
  {
    my_error(ER_OUT_OF_RESOURCES, MYF(0));
    return val_buffer;
  }

  if (zerofill)
    prepend_zeros(val_buffer);
  return val_buffer;
}


int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  float a,b;
  float4get(a,a_ptr);
  float4get(b,b_ptr);
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}

#define FLT_EXP_DIG (sizeof(float)*8-FLT_MANT_DIG)

void Field_float::sort_string(uchar *to,uint length __attribute__((unused)))
{
  float nr;
  float4get(nr,ptr);

  uchar *tmp= to;
  if (nr == (float) 0.0)
  {						/* Change to zero string */
    tmp[0]=(uchar) 128;
    bzero((char*) tmp+1,sizeof(nr)-1);
  }
  else
  {
#ifdef WORDS_BIGENDIAN
    memcpy(tmp, &nr, sizeof(nr));
#else
    tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0];
#endif
    if (tmp[0] & 128)				/* Negative */
    {						/* make complement */
      uint i;
      for (i=0 ; i < sizeof(nr); i++)
	tmp[i]= (uchar) (tmp[i] ^ (uchar) 255);
    }
    else
    {
      ushort exp_part=(((ushort) tmp[0] << 8) | (ushort) tmp[1] |
		       (ushort) 32768);
      exp_part+= (ushort) 1 << (16-1-FLT_EXP_DIG);
      tmp[0]= (uchar) (exp_part >> 8);
      tmp[1]= (uchar) exp_part;
    }
  }
}


bool Field_float::send(Protocol *protocol)
{
  DBUG_ASSERT(marked_for_read());
  Protocol_text *txt;
  if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
    return send_numeric_zerofill_str(txt, PROTOCOL_SEND_FLOAT);
  return protocol->store_float((float) Field_float::val_real(), dec);
}


/**
   Save the field metadata for float fields.

   Saves the pack length in the first byte.

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
Binlog_type_info Field_float::binlog_type_info() const
{
  DBUG_ASSERT(Field_float::type() == binlog_type());
  return Binlog_type_info(Field_float::type(), pack_length(), 1,
                          binlog_signedness());
}


/****************************************************************************
  double precision floating point numbers
****************************************************************************/

int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs)
{
  int error;
  Field_double::store(get_double(from, len, cs, &error));
  return error;
}


int Field_double::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= truncate_double(&nr, field_length,
                             not_fixed ? NOT_FIXED_DEC : dec,
                             unsigned_flag, DBL_MAX);
  if (unlikely(error))
  {
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    if (error < 0)                                // Wrong double value
    {
      error= 1;
      set_null();
    }
  }

  float8store(ptr,nr);
  return error;
}


int Field_double::store(longlong nr, bool unsigned_val)
{
  return Field_double::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
                             (double) nr);
}

/*
  If a field has fixed length, truncate the double argument pointed to by 'nr'
  appropriately.
  Also ensure that the argument is within [-max_value; max_value] range.

  return
    0   ok
    -1  Illegal double value
    1   Value was truncated
*/

int truncate_double(double *nr, uint field_length, decimal_digits_t dec,
                    bool unsigned_flag, double max_value)
{
  int error= 0;
  double res= *nr;
  
  if (std::isnan(res))
  {
    *nr= 0;
    return -1;
  }
  else if (unsigned_flag && res < 0)
  {
    *nr= 0;
    return 1;
  }

  if (dec < FLOATING_POINT_DECIMALS)
  {
    uint order= field_length - dec;
    uint step= array_elements(log_10) - 1;
    double max_value_by_dec= 1.0;
    for (; order > step; order-= step)
      max_value_by_dec*= log_10[step];
    max_value_by_dec*= log_10[order];
    max_value_by_dec-= 1.0 / log_10[dec];
    set_if_smaller(max_value, max_value_by_dec);

    /* Check for infinity so we don't get NaN in calculations */
    if (!std::isinf(res))
    {
      double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec];
      res= floor(res) + tmp;
    }
  }
  
  if (res < -max_value)
  {
    res= -max_value;
    error= 1;
  }
  else if (res > max_value)
  {
    res= max_value;
    error= 1;
  }

  *nr= res;
  return error;
}

/*
  Convert double to longlong / ulonglong.
  If double is outside of the supported range,
  adjust m_result and set m_error.

  @param nr             Number to convert
  @param unsigned_flag  true if result is unsigned
*/

Value_source::
Converter_double_to_longlong::Converter_double_to_longlong(double nr,
                                                           bool unsigned_flag)
  :m_error(false)
{
  nr= rint(nr);
  if (unsigned_flag)
  {
    if (nr < 0)
    {
      m_result= 0;
      m_error= true;
    }
    else if (nr >= (double) ULONGLONG_MAX)
    {
      m_result= ~(longlong) 0;
      m_error= true;
    }
    else
      m_result= (longlong) double2ulonglong(nr);
  }
  else
  {
    if (nr <= (double) LONGLONG_MIN)
    {
      m_result= LONGLONG_MIN;
      m_error= (nr < (double) LONGLONG_MIN);
    }
    else if (nr >= (double) (ulonglong) LONGLONG_MAX)
    {
      m_result= LONGLONG_MAX;
      m_error= (nr > (double) LONGLONG_MAX);
    }
    else
      m_result= (longlong) nr;
  }
}


void Value_source::
Converter_double_to_longlong::push_warning(THD *thd,
                                           double nr,
                                           bool unsigned_flag)
{
  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                      ER_DATA_OVERFLOW, ER_THD(thd, ER_DATA_OVERFLOW),
                      ErrConvDouble(nr).ptr(),
                      unsigned_flag ? "UNSIGNED INT" : "INT");
}


int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint)
{
  return store(TIME_to_double(ltime));
}


double Field_double::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  double j;
  float8get(j,ptr);
  return j;
}


longlong Field_double::val_int_from_real(bool want_unsigned_result)
{
  Converter_double_to_longlong conv(val_real(), want_unsigned_result);
  if (unlikely(!want_unsigned_result && conv.error()))
    conv.push_warning(get_thd(), Field_double::val_real(), false);
  return conv.result();
}


my_decimal *Field_real::val_decimal(my_decimal *decimal_value)
{
  DBUG_ASSERT(marked_for_read());
  double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value);
  return decimal_value;
}


bool Field_real::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
{
  DBUG_ASSERT(marked_for_read());
  double nr= val_real();
  return double_to_datetime_with_warn(get_thd(), nr, ltime, fuzzydate,
                                      table->s, field_name.str);
}


Item *Field_real::get_equal_const_item(THD *thd, const Context &ctx,
                                       Item *const_item)
{
  if (flags & ZEROFILL_FLAG)
    return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item);
  switch (ctx.subst_constraint()) {
  case IDENTITY_SUBST:
    if (const_item->decimal_scale() != Field_real::decimals())
    {
      double val= const_item->val_real();
      return new (thd->mem_root) Item_float(thd, val, Field_real::decimals());
    }
    break;
  case ANY_SUBST:
    break;
  }
  return const_item;
}


void Field_real::sql_type(String &res) const
{
  const Name name= type_handler()->name();
  if (dec >= FLOATING_POINT_DECIMALS)
  {
    res.set_ascii(name.ptr(), name.length());
  }
  else
  {
    CHARSET_INFO *cs= res.charset();
    res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
			    "%.*s(%d,%d)", (int) name.length(), name.ptr(),
			    (int) field_length,dec));
  }
  add_zerofill_and_unsigned(res);
}


String *Field_double::val_str(String *val_buffer,
			      String *val_ptr __attribute__((unused)))
{
  DBUG_ASSERT(marked_for_read());
  DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
  double nr;
  float8get(nr,ptr);

  uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE;
  if (val_buffer->alloc(to_length))
  {
    my_error(ER_OUT_OF_RESOURCES, MYF(0));
    return val_buffer;
  }

  char *to=(char*) val_buffer->ptr();
  size_t len;

  if (dec >= FLOATING_POINT_DECIMALS)
    len= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, to_length - 1, to, NULL);
  else
    len= my_fcvt(nr, dec, to, NULL);

  val_buffer->length((uint) len);
  if (zerofill)
    prepend_zeros(val_buffer);
  val_buffer->set_charset(&my_charset_numeric);
  return val_buffer;
}

bool Field_double::send(Protocol *protocol)
{
  DBUG_ASSERT(marked_for_read());
  Protocol_text *txt;
  if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
    return send_numeric_zerofill_str(txt, PROTOCOL_SEND_DOUBLE);
  return protocol->store_double(Field_double::val_real(), dec);
}


int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  double a,b;
  float8get(a,a_ptr);
  float8get(b,b_ptr);
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}


#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)

/* The following should work for IEEE */

void Field_double::sort_string(uchar *to,uint length __attribute__((unused)))
{
  double nr;
  float8get(nr,ptr);
  change_double_for_sort(nr, to);
}


/**
   Save the field metadata for double fields.

   Saves the pack length in the first byte of the field metadata array
   at index of *metadata_ptr.

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
Binlog_type_info Field_double::binlog_type_info() const
{
  DBUG_ASSERT(Field_double::type() == binlog_type());
  return Binlog_type_info(Field_double::type(), pack_length(), 1,
                          binlog_signedness());
}


/**
  TIMESTAMP type holds datetime values in range from 1970-01-01 00:00:01 UTC to 
  2038-01-01 00:00:00 UTC stored as number of seconds since Unix 
  Epoch in UTC.
  
  Actually SQL-99 says that we should allow niladic functions (like NOW())
  as defaults for any field. The current limitation (only NOW() and only 
  for TIMESTAMP and DATETIME fields) are because of restricted binary .frm
  format and should go away in the future.
  
  Also because of this limitation of binary .frm format we use 5 different
  unireg_check values with TIMESTAMP field to distinguish various cases of
  DEFAULT or ON UPDATE values. These values are:
  
  TIMESTAMP_OLD_FIELD - old timestamp, if there was not any fields with
    auto-set-on-update (or now() as default) in this table before, then this 
    field has NOW() as default and is updated when row changes, else it is 
    field which has 0 as default value and is not automatically updated.
  TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update
    automatically (TIMESTAMP DEFAULT NOW()), not used in Field since 10.2.2
  TIMESTAMP_UN_FIELD - field which is set on update automatically but has not 
    NOW() as default (but it may has 0 or some other const timestamp as 
    default) (TIMESTAMP ON UPDATE NOW()).
  TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on 
    update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW()), not used in Field since 10.2.2
  NONE - field which is not auto-set on update with some other than NOW() 
    default value (TIMESTAMP DEFAULT 0).

  Note that TIMESTAMP_OLD_FIELDs are never created explicitly now, they are 
  left only for preserving ability to read old tables. Such fields replaced 
  with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE. This is 
  because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for 
  "TIMESTAMP DEFAULT 'Const'" field. (Old timestamps allowed such 
  specification too but ignored default value for first timestamp, which of 
  course is non-standard.) In most cases user won't notice any change, only
  exception is different behavior of old/new timestamps during ALTER TABLE.
 */

Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
                                 uchar *null_ptr_arg, uchar null_bit_arg,
				 enum utype unireg_check_arg,
				 const LEX_CSTRING *field_name_arg,
				 TABLE_SHARE *share)
  :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
                  unireg_check_arg, field_name_arg)
{
  /* For 4.0 MYD and 4.0 InnoDB compatibility */
  flags|= UNSIGNED_FLAG;
  if (unireg_check != NONE)
  {
    /*
      We mark the flag with TIMESTAMP_FLAG to indicate to the client that
      this field will be automatically updated on insert.
    */
    flags|= TIMESTAMP_FLAG;
    if (unireg_check != TIMESTAMP_DN_FIELD)
      flags|= ON_UPDATE_NOW_FLAG;
  }
}


sql_mode_t
Field_timestamp::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
{
  return expr->datetime_precision(thd) > decimals() ?
         MODE_TIME_ROUND_FRACTIONAL : 0;
}


int Field_timestamp::save_in_field(Field *to)
{
  ulong sec_part;
  my_time_t ts= get_timestamp(&sec_part);
  if (!ts && !sec_part)
    return to->store_time_dec(Datetime::zero().get_mysql_time(), decimals());
  return to->store_timestamp_dec(Timeval(ts, sec_part), decimals());
}

my_time_t Field_timestamp0::get_timestamp(const uchar *pos,
                                          ulong *sec_part) const
{
  DBUG_ASSERT(marked_for_read());
  *sec_part= 0;
  return uint4korr(pos);
}


bool Field_timestamp0::val_native(Native *to)
{
  DBUG_ASSERT(marked_for_read());
  my_time_t sec= (my_time_t) uint4korr(ptr);
  return Timestamp_or_zero_datetime(Timestamp(sec, 0), sec == 0).
           to_native(to, 0);
}


int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt,
                                             const ErrConv *str, int was_cut)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  static const Timestamp zero(0, 0);

  // Handle totally bad values
  if (!dt->is_valid_datetime())
  {
    set_datetime_warning(WARN_DATA_TRUNCATED, str, "datetime", 1);
    store_TIMESTAMP(zero);
    return 1;
  }

  // Handle values that do not need DATETIME to TIMESTAMP conversion
  if (!dt->get_mysql_time()->month)
  {
    /*
      Zero date is allowed by the current sql_mode. Store zero timestamp.
      Return success or a warning about non-fatal truncation, e.g.:
        INSERT INTO t1 (ts) VALUES ('0000-00-00 00:00:00 some tail');
    */
    store_TIMESTAMP(zero);
    return store_TIME_return_code_with_warnings(was_cut, str, "datetime");
  }

  // Convert DATETIME to TIMESTAMP
  uint conversion_error;
  const MYSQL_TIME *l_time= dt->get_mysql_time();
  my_time_t timestamp= TIME_to_timestamp(thd, l_time, &conversion_error);
  if (timestamp == 0 && l_time->second_part == 0)
  {
    set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, "datetime", 1);
    store_TIMESTAMP(zero);
    return 1; // date was fine but pointed to a DST gap
  }

  // Store the value
  DBUG_ASSERT(!dt->fraction_remainder(decimals()));
  store_TIMESTAMP(Timestamp(timestamp, l_time->second_part));

  // Calculate return value and send warnings if needed
  if (unlikely(conversion_error)) // e.g. DATETIME in the DST gap
  {
    set_datetime_warning(conversion_error, str, "datetime", 1);
    return 1;
  }
  return store_TIME_return_code_with_warnings(was_cut, str, "datetime");
}


date_conv_mode_t Timestamp::sql_mode_for_timestamp(THD *thd)
{
  // We don't want to store invalid or fuzzy datetime values in TIMESTAMP
  return date_conv_mode_t((thd->variables.sql_mode & MODE_NO_ZERO_DATE) |
                          MODE_NO_ZERO_IN_DATE);
}


int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
  int warn;
  ErrConvTime str(ltime);
  THD *thd= get_thd();
  Datetime dt(thd, &warn, ltime, Timestamp::DatetimeOptions(thd), decimals());
  return store_TIME_with_warning(thd, &dt, &str, warn);
}


int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs)
{
  ErrConvString str(from, len, cs);
  THD *thd= get_thd();
  MYSQL_TIME_STATUS st;
  Datetime dt(thd, &st, from, len, cs, Timestamp::DatetimeOptions(thd), decimals());
  return store_TIME_with_warning(thd, &dt, &str, st.warnings);
}


int Field_timestamp::store(double nr)
{
  int error;
  ErrConvDouble str(nr);
  THD *thd= get_thd();
  Datetime dt(thd, &error, nr, Timestamp::DatetimeOptions(thd), decimals());
  return store_TIME_with_warning(thd, &dt, &str, error);
}


int Field_timestamp::store(longlong nr, bool unsigned_val)
{
  int error;
  Longlong_hybrid tmp(nr, unsigned_val);
  ErrConvInteger str(tmp);
  THD *thd= get_thd();
  Datetime dt(&error, tmp, Timestamp::DatetimeOptions(thd));
  return store_TIME_with_warning(thd, &dt, &str, error);
}


int Field_timestamp::store_timestamp_dec(const my_timeval &tv, uint dec)
{
  int warn= 0;
  time_round_mode_t mode= Datetime::default_round_mode(get_thd());
  const Timestamp ts= Timestamp(tv).round(decimals(), mode, &warn);
  store_TIMESTAMP(ts);
  if (ts.tv_sec == 0 && ts.tv_usec == 0)
  {
    /*
      The value {tv_sec==0, tv_usec==0} here means '1970-01-01 00:00:00 +00'.
      It does not mean zero datetime! because store_timestamp_dec() knows
      nothing about zero dates. It inserts only real timeval values.
      Zero ts={0,0} here is possible in two scenarios:
      - the passed tv was already {0,0} meaning '1970-01-01 00:00:00 +00'
      - the passed tv had some microseconds but they were rounded/truncated
        to zero: '1970-01-01 00:00:00.1 +00' -> '1970-01-01 00:00:00 +00'.
      It does not matter whether rounding/truncation really happened.
      In both cases the call for store_TIMESTAMP(ts) above re-interpreted
      '1970-01-01 00:00:00 +00:00' to zero date. Return 1 no matter what
      sql_mode is. Even if sql_mode allows zero dates, there is still a problem
      here: '1970-01-01 00:00:00 +00' could not be stored as-is!
    */
    ErrConvString str(STRING_WITH_LEN("1970-01-01 00:00:00 +00:00"),
                      system_charset_info);
    set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, &str, "datetime", 1);
    return 1; // '1970-01-01 00:00:00 +00' was converted to a zero date
  }
  if (warn)
  {
    /*
      We're here if rounding would overflow outside of the supported TIMESTAMP
      range, so truncation happened instead:
        CREATE TABLE t1 (a TIMESTAMP(6));
        INSERT INTO t1 VALUES ('maximum-possible-timestamp.999999');
        ALTER TABLE t1 MODIFY a TIMESTAMP(5);
        SELECT * FROM t1; --> 'maximum-possible-timestamp.99999' (5 digits)
      Raise a warning, like DATETIME does for '9999-12-31 23:59:59.999999'.
    */
    set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
  }
  return 0;
}


int Field_timestamp::zero_time_stored_return_code_with_warning()
{
  if (get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE)
  {
    ErrConvString s(
      STRING_WITH_LEN("0000-00-00 00:00:00.000000") - (decimals() ? 6 - decimals() : 7),
      system_charset_info);
    set_datetime_warning(WARN_DATA_TRUNCATED, &s, "datetime", 1);
    return 1;
  }
  return 0;

}


int Field_timestamp::store_native(const Native &value)
{
  if (!value.length()) // Zero datetime
  {
    reset();
    return zero_time_stored_return_code_with_warning();
  }
  /*
    The exact second precision is not important here.
    Field_timestamp*::store_timestamp_dec() do not use the "dec" parameter.
    Passing TIME_SECOND_PART_DIGITS is OK.
  */
  return store_timestamp_dec(Timestamp(value), TIME_SECOND_PART_DIGITS);
}


longlong Field_timestamp::val_int(void)
{
  MYSQL_TIME ltime;
  if (get_date(&ltime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
    return 0;

  return ltime.year * 10000000000LL + ltime.month * 100000000LL +
         ltime.day * 1000000L + ltime.hour * 10000L +
         ltime.minute * 100 + ltime.second;
}


String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
{
  MYSQL_TIME ltime;
  uint32 year, temp, temp2;
  uint dec;
  char *to;

  val_buffer->alloc(field_length+1);
  to= (char*) val_buffer->ptr();
  val_buffer->length(field_length);

  if (get_date(&ltime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
  {				      /* Zero time is "000000" */
    val_ptr->set(zero_timestamp, field_length, &my_charset_numeric);
    return val_ptr;
  }
  val_buffer->set_charset(&my_charset_numeric);	// Safety
   
  temp= ltime.year;
  DBUG_ASSERT(temp >= 1969);

  year= temp/100;
  temp-= year*100;
  temp2= year/10; year= year-temp2*10;
  *to++= (char) ('0'+(char) (temp2));
  *to++= (char) ('0'+(char) (year));
  temp2=temp/10; temp=temp-temp2*10;
  *to++= (char) ('0'+(char) (temp2));
  *to++= (char) ('0'+(char) (temp));
  *to++= '-';
  temp=ltime.month;
  temp2=temp/10; temp=temp-temp2*10;
  *to++= (char) ('0'+(char) (temp2));
  *to++= (char) ('0'+(char) (temp));
  *to++= '-';
  temp=ltime.day;
  temp2=temp/10; temp=temp-temp2*10;
  *to++= (char) ('0'+(char) (temp2));
  *to++= (char) ('0'+(char) (temp));
  *to++= ' ';
  temp=ltime.hour;
  temp2=temp/10; temp=temp-temp2*10;
  *to++= (char) ('0'+(char) (temp2));
  *to++= (char) ('0'+(char) (temp));
  *to++= ':';
  temp=ltime.minute;
  temp2=temp/10; temp=temp-temp2*10;
  *to++= (char) ('0'+(char) (temp2));
  *to++= (char) ('0'+(char) (temp));
  *to++= ':';
  temp=ltime.second;
  temp2=temp/10; temp=temp-temp2*10;
  *to++= (char) ('0'+(char) (temp2));
  *to++= (char) ('0'+(char) (temp));
  *to= 0;
  val_buffer->set_charset(&my_charset_numeric);

  if ((dec= decimals()))
  {
    ulong sec_part= (ulong) sec_part_shift(ltime.second_part, dec);
    char *buf= const_cast<char*>(val_buffer->ptr() + MAX_DATETIME_WIDTH);
    for (int i= dec; i > 0; i--, sec_part/= 10)
    buf[i]= (char)(sec_part % 10) + '0';
    buf[0]= '.';
    buf[dec + 1]= 0;
  }
  return val_buffer;
}


bool
Field_timestamp::validate_value_in_record(THD *thd, const uchar *record) const
{
  DBUG_ASSERT(!is_null_in_record(record));
  ulong sec_part;
  return !get_timestamp(ptr_in_record(record), &sec_part) && !sec_part &&
         bool(sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != false;
}


bool Field_timestamp::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
  ulong sec_part;
  my_time_t ts= get_timestamp(&sec_part);
  return get_thd()->timestamp_to_TIME(ltime, ts, sec_part, fuzzydate);
}


bool Field_timestamp0::send(Protocol *protocol)
{
  MYSQL_TIME ltime;
  Field_timestamp0::get_date(&ltime, date_mode_t(0));
  return protocol->store_datetime(&ltime, 0);
}


int Field_timestamp0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  time_t a,b;
  a= uint4korr(a_ptr);
  b= uint4korr(b_ptr);
  return (a < b ) ? -1 : (a > b) ? 1 : 0;
}


void Field_timestamp0::sort_string(uchar *to,uint length __attribute__((unused)))
{
  to[0] = ptr[3];
  to[1] = ptr[2];
  to[2] = ptr[1];
  to[3] = ptr[0];
}


int Field_timestamp0::set_time()
{
  set_notnull();
  store_TIMESTAMP(Timestamp(get_thd()->query_start(), 0));
  return 0;
}


bool Field_timestamp::load_data_set_no_data(THD *thd, bool fixed_format)
{
  if (!maybe_null())
  {
    /*
      Timestamp fields that are NOT NULL are autoupdated if there is no
      corresponding value in the data file.
    */
    set_time();
    set_has_explicit_value();
    return false;
  }
  return Field::load_data_set_no_data(thd, fixed_format);
}


bool Field_timestamp::load_data_set_null(THD *thd)
{
  if (!maybe_null())
  {
    /*
      Timestamp fields that are NOT NULL are autoupdated if there is no
      corresponding value in the data file.
    */
    set_time();
  }
  else
  {
    reset();
    set_null();
  }
  set_has_explicit_value(); // Do not auto-update this field
  return false;
}


#ifdef NOT_USED
static void store_native(ulonglong num, uchar *to, uint bytes)
{
  switch(bytes) {
  case 1: *to= (uchar)num;              break;
  case 2: shortstore(to, (ushort)num);  break;
  case 3: int3store(to, num); /* Sic!*/ break;
  case 4: longstore(to, (ulong)num);    break;
  case 8: longlongstore(to, num);       break;
  default: DBUG_ASSERT(0);
  }
}

static longlong read_native(const uchar *from, uint bytes)
{
  switch(bytes) {
  case 1: return from[0];
  case 2: { uint16 tmp; shortget(tmp, from); return tmp; }
  case 3: return uint3korr(from);
  case 4: { uint32 tmp; longget(tmp, from); return tmp; }
  case 8: { longlong tmp; longlongget(tmp, from); return tmp; }
  default: DBUG_ASSERT(0); return 0;
  }
}
#endif


void Field_timestamp_hires::store_TIMEVAL(const my_timeval &tv)
{
  mi_int4store(ptr, tv.tv_sec);
  store_bigendian(sec_part_shift(tv.tv_usec, dec), ptr+4, sec_part_bytes(dec));
}

my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos,
                                               ulong *sec_part) const
{
  DBUG_ASSERT(marked_for_read());
  *sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes(dec)), dec);
  return mi_uint4korr(pos);
}


bool Field_timestamp_hires::val_native(Native *to)
{
  DBUG_ASSERT(marked_for_read());
  struct my_timeval tm;
  tm.tv_sec= mi_uint4korr(ptr);
  tm.tv_usec= (ulong) sec_part_unshift(read_bigendian(ptr+4, sec_part_bytes(dec)), dec);
  return Timestamp_or_zero_datetime(Timestamp(tm), tm.tv_sec == 0).
           to_native(to, dec);
}


double Field_timestamp_with_dec::val_real(void)
{
  MYSQL_TIME ltime;
  if (get_date(&ltime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
    return 0;
  
  return ltime.year * 1e10 + ltime.month * 1e8 +
         ltime.day * 1e6 + ltime.hour * 1e4 +
         ltime.minute * 1e2 + ltime.second + ltime.second_part*1e-6;
}

my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d)
{
  MYSQL_TIME ltime;
  get_date(&ltime, date_mode_t(0));
  return TIME_to_my_decimal(&ltime, d);
}
 
int Field_timestamp::store_decimal(const my_decimal *d)
{
  int error;
  THD *thd= get_thd();
  ErrConvDecimal str(d);
  Datetime dt(thd, &error, d, Timestamp::DatetimeOptions(thd), decimals());
  return store_TIME_with_warning(thd, &dt, &str, error);
}

int Field_timestamp_with_dec::set_time()
{
  THD *thd= get_thd();
  set_notnull();
  // Avoid writing microseconds into binlog for FSP=0
  ulong msec= decimals() ? thd->query_start_sec_part() : 0;
  store_TIMESTAMP(Timestamp(thd->query_start(), msec).trunc(decimals()));
  return 0;
}

bool Field_timestamp_with_dec::send(Protocol *protocol)
{
  MYSQL_TIME ltime;
  Field_timestamp::get_date(&ltime, date_mode_t(0));
  return protocol->store_datetime(&ltime, dec);
}


int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  int32 a,b;
  ulong a_sec_part, b_sec_part;
  a= mi_uint4korr(a_ptr);
  a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes(dec));
  b= mi_uint4korr(b_ptr);
  b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes(dec));
  return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 :
          a_sec_part < b_sec_part  ? -1 :  a_sec_part > b_sec_part  ? 1 : 0;
}


void Field_timestamp_with_dec::make_send_field(Send_field *field)
{
  Field::make_send_field(field);
  field->decimals= dec;
}


/*************************************************************
** MySQL-5.6 compatible TIMESTAMP(N)
**************************************************************/

void Field_timestampf::store_TIMEVAL(const my_timeval &tm)
{
  my_timestamp_to_binary(&tm, ptr, dec);
}

void Field_timestampf::set_max()
{
  DBUG_ENTER("Field_timestampf::set_max");
  DBUG_ASSERT(marked_for_write_or_computed());
  DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS);

  set_notnull();
  mi_int4store(ptr, TIMESTAMP_MAX_VALUE);
  mi_int3store(ptr + 4, TIME_MAX_SECOND_PART);

  DBUG_VOID_RETURN;
}

bool Field_timestampf::is_max(const uchar *ptr_arg) const
{
  longlong timestamp= mi_uint4korr(ptr_arg);
  DBUG_ENTER("Field_timestampf::is_max");
  DBUG_ASSERT(marked_for_read());

  /* Allow old max value and new max value */
  DBUG_RETURN((timestamp == TIMESTAMP_MAX_VALUE ||
               timestamp == INT_MAX32) &&
              mi_uint3korr(ptr_arg + 4) == TIME_MAX_SECOND_PART);
}

my_time_t Field_timestampf::get_timestamp(const uchar *pos,
                                          ulong *sec_part) const
{
  struct my_timeval tm;
  my_timestamp_from_binary(&tm, pos, dec);
  *sec_part= tm.tv_usec;
  return (my_time_t) tm.tv_sec;
}


bool Field_timestampf::val_native(Native *to)
{
  DBUG_ASSERT(marked_for_read());
  char zero[8]= "\0\0\0\0\0\0\0";
  DBUG_ASSERT(pack_length () <= sizeof(zero));
  // Check if it's '0000-00-00 00:00:00' rather than a real timestamp
  if (!memcmp(ptr, zero, pack_length()))
  {
    to->length(0);
    return false;
  }
  return Field::val_native(to);
}

Binlog_type_info Field_timestampf::binlog_type_info() const
{
  return Binlog_type_info(Field_timestampf::binlog_type(), decimals(), 1);
}


/*************************************************************/
sql_mode_t Field_temporal::can_handle_sql_mode_dependency_on_store() const
{
  return MODE_PAD_CHAR_TO_FULL_LENGTH;
}


bool Field_temporal::is_equal(const Column_definition &new_field) const
{
  return new_field.type_handler() == type_handler() &&
         new_field.length == max_display_length();
}


void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
                                  const ErrConv *str, int was_cut,
                                  const char *typestr)
{
  /*
    error code logic:
    MYSQL_TIME_WARN_TRUNCATED means that the value was not a date/time at all.
      it will be stored as zero date/time.
    MYSQL_TIME_WARN_OUT_OF_RANGE means that the value was a date/time,
      that is, it was parsed as such, but the value was invalid.

    Also, MYSQL_TIME_WARN_TRUNCATED is used when storing a DATETIME in
    a DATE field and non-zero time part is thrown away.
  */
  if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
    set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, str, typestr, 1);
  if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
    set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, typestr, 1);
}


void Field_temporal::sql_type_dec_comment(String &res,
                                          const Name &name,
                                          uint dec,
                                          const Name &comment) const
{
  CHARSET_INFO *cs=res.charset();
  res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
                                "%.*s(%u)%s%.*s%s",
                                (uint) name.length(), name.ptr(),
                                dec,
                                comment.length() ? " /* " : "",
                                (uint) comment.length(), comment.ptr(),
                                comment.length() ? " */" : ""));
}


void Field_temporal::sql_type_comment(String &res,
                                      const Name &name,
                                      const Name &comment) const
{
  CHARSET_INFO *cs=res.charset();
  res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
                                "%.*s%s%.*s%s",
                                (uint) name.length(), name.ptr(),
                                comment.length() ? " /* " : "",
                                (uint) comment.length(), comment.ptr(),
                                comment.length() ? " */" : ""));
}


const Name & Field_temporal::type_version_mysql56()
{
  DBUG_EXECUTE_IF("sql_type", return Type_handler::version_mysql56(); );
  static Name none(NULL, 0);
  return none;
}


/*
  Store string into a date/time field

  RETURN
    0  ok
    1  Value was cut during conversion
    2  value was out of range
    3  Datetime value that was cut (warning level NOTE)
       This is used by opt_range.cc:get_mm_leaf().
*/
int Field_datetime::store_TIME_with_warning(const Datetime *dt,
                                            const ErrConv *str,
                                            int was_cut)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  // Handle totally bad values
  if (!dt->is_valid_datetime())
    return store_invalid_with_warning(str, was_cut, "datetime");
  // Store the value
  DBUG_ASSERT(!dt->fraction_remainder(decimals()));
  store_datetime(*dt);
  // Caclulate return value and send warnings if needed
  return store_TIME_return_code_with_warnings(was_cut, str, "datetime");
}


int Field_datetime::store(const char *from, size_t len, CHARSET_INFO *cs)
{
  MYSQL_TIME_STATUS st;
  ErrConvString str(from, len, cs);
  THD *thd= get_thd();
  Datetime dt(thd, &st, from, len, cs, Datetime::Options(thd), decimals());
  return store_TIME_with_warning(&dt, &str, st.warnings);
}

int Field_datetime::store(double nr)
{
  int error;
  ErrConvDouble str(nr);
  THD *thd= get_thd();
  Datetime dt(thd, &error, nr, Datetime::Options(thd), decimals());
  return store_TIME_with_warning(&dt, &str, error);
}


int Field_datetime::store(longlong nr, bool unsigned_val)
{
  int error;
  Longlong_hybrid tmp(nr, unsigned_val);
  ErrConvInteger str(tmp);
  THD *thd= get_thd();
  Datetime dt(&error, tmp, Datetime::Options(thd));
  return store_TIME_with_warning(&dt, &str, error);
}

int Field_datetime::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
  int error;
  ErrConvTime str(ltime);
  THD *thd= get_thd();
  Datetime dt(thd, &error, ltime, Datetime::Options(thd), decimals());
  return store_TIME_with_warning(&dt, &str, error);
}


int Field_datetime::store_decimal(const my_decimal *d)
{
  int error;
  ErrConvDecimal str(d);
  THD *thd= get_thd();
  Datetime tm(thd, &error, d, Datetime::Options(thd), decimals());
  return store_TIME_with_warning(&tm, &str, error);
}


bool
Field_temporal_with_date::validate_value_in_record(THD *thd,
                                                   const uchar *record) const
{
  DBUG_ASSERT(!is_null_in_record(record));
  MYSQL_TIME ltime;
  return get_TIME(&ltime, ptr_in_record(record), Datetime::Options(thd));
}


my_decimal *Field_temporal::val_decimal(my_decimal *d)
{
  MYSQL_TIME ltime;
  if (get_date(&ltime, date_mode_t(0)))
  {
    bzero(&ltime, sizeof(ltime));
    ltime.time_type= type_handler()->mysql_timestamp_type();
  }
  return TIME_to_my_decimal(&ltime, d);
}


Data_type_compatibility
Field_temporal::can_optimize_keypart_ref(const Item_bool_func *cond,
                                         const Item *value) const
{
  // Field is of TIME_RESULT, which supersedes everything else.
  return Data_type_compatibility::OK;
}


Data_type_compatibility
Field_temporal::can_optimize_group_min_max(const Item_bool_func *cond,
                                           const Item *const_item) const
{
  // Field is of TIME_RESULT, which supersedes everything else.
  return Data_type_compatibility::OK;
}


Item *Field_temporal::get_equal_const_item_datetime(THD *thd,
                                                    const Context &ctx,
                                                    Item *const_item)
{
  switch (ctx.subst_constraint()) {
  case IDENTITY_SUBST:
    if ((const_item->field_type() != MYSQL_TYPE_DATETIME &&
         const_item->field_type() != MYSQL_TYPE_TIMESTAMP) ||
        const_item->decimals != decimals())
    {
      Datetime::Options opt(TIME_CONV_NONE, thd);
      Datetime dt(thd, const_item, opt, decimals());
      if (!dt.is_valid_datetime())
        return NULL;
      /*
        See comments about truncation in the same place in
        Field_time::get_equal_const_item().
      */
      return new (thd->mem_root) Item_datetime_literal(thd, &dt, decimals());
    }
    break;
  case ANY_SUBST:
    if (!is_temporal_type_with_date(const_item->field_type()))
    {
      Datetime dt= type_handler()->field_type() == MYSQL_TYPE_TIMESTAMP ?
        Datetime(thd, const_item, Timestamp::DatetimeOptions(thd)) :
        Datetime(thd, const_item, Datetime::Options_cmp(thd));
      if (!dt.is_valid_datetime())
        return NULL;
      return new (thd->mem_root)
        Item_datetime_literal_for_invalid_dates(thd, &dt,
                                                dt.get_mysql_time()->
                                                second_part ?
                                                TIME_SECOND_PART_DIGITS : 0);
    }
    break;
  }
  return const_item;
}


/****************************************************************************
** time type
** In string context: HH:MM:SS
** In number context: HHMMSS
** Stored as a 3 byte unsigned int
****************************************************************************/
sql_mode_t
Field_time::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
{
  return expr->time_precision(thd) > decimals() ?
         MODE_TIME_ROUND_FRACTIONAL : 0;
}


int Field_time::store_TIME_with_warning(const Time *t,
                                        const ErrConv *str, int warn)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  // Handle totally bad values
  if (!t->is_valid_time())
    return store_invalid_with_warning(str, warn, "time");
  // Store the value
  DBUG_ASSERT(!t->fraction_remainder(decimals()));
  store_TIME(*t);
  // Calculate return value and send warnings if needed
  return store_TIME_return_code_with_warnings(warn, str, "time");
}


void Field_time0::store_TIME(const MYSQL_TIME *ltime)
{
  DBUG_ASSERT(ltime->year == 0);
  DBUG_ASSERT(ltime->month == 0);
  long tmp= (ltime->day*24L+ltime->hour)*10000L +
            (ltime->minute*100+ltime->second);
  if (ltime->neg)
    tmp= -tmp;
  int3store(ptr,tmp);
}

int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs)
{
  ErrConvString str(from, len, cs);
  MYSQL_TIME_STATUS st;
  THD *thd= get_thd();
  /*
    Unlike number-to-time conversion, we need to additionally pass
    MODE_NO_ZERO_DATE here (if it presents in the current sql_mode):
      SET sql_mode='STRICT_ALL_TABLES,NO_ZERO_DATE';
      INSERT INTO t1 VALUES ('0000-00-00 00:00:00');  -- error
      INSERT INTO t1 VALUES (0);                      -- ok
    In the first INSERT we have a zero date.
    In the second INSERT we don't have a zero date (it is just a zero time).
  */
  Time::Options opt(sql_mode_for_dates(thd), thd);
  Time tm(thd, &st, from, len, cs, opt, decimals());
  return store_TIME_with_warning(&tm, &str, st.warnings);
}


int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
  ErrConvTime str(ltime);
  int warn;
  Time tm(&warn, ltime, curdays, Time::Options(get_thd()), decimals());
  return store_TIME_with_warning(&tm, &str, warn);
}


int Field_time::store(double nr)
{
  ErrConvDouble str(nr);
  int was_cut;
  Time tm(get_thd(), &was_cut, nr, Time::Options(get_thd()), decimals());
  return store_TIME_with_warning(&tm, &str, was_cut);
}


int Field_time::store(longlong nr, bool unsigned_val)
{
  Longlong_hybrid tmp(nr, unsigned_val);
  ErrConvInteger str(tmp);
  int was_cut;
  THD *thd= get_thd();
  /*
    Need fractional digit truncation if nr overflows to '838:59:59.999999'.
    The constructor used below will always truncate (never round).
    We don't need to care to overwrite the default session rounding mode
    from HALF_UP to TRUNCATE.
  */
  Time tm(thd, &was_cut, tmp, Time::Options(thd), decimals());
  return store_TIME_with_warning(&tm, &str, was_cut);
}


void Field_time::set_curdays(THD *thd)
{
  MYSQL_TIME ltime;
  set_current_date(thd, &ltime);
  curdays= calc_daynr(ltime.year, ltime.month, ltime.day);
}


Field *Field_time::new_key_field(MEM_ROOT *root, TABLE *new_table,
                                 uchar *new_ptr, uint32 length,
                                 uchar *new_null_ptr, uint new_null_bit)
{
  THD *thd= get_thd();
  Field_time *res=
    (Field_time*) Field::new_key_field(root, new_table, new_ptr, length,
                                       new_null_ptr, new_null_bit);
  if (!(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) && res)
    res->set_curdays(thd);
  return res;
}


double Field_time0::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  uint32 j= (uint32) uint3korr(ptr);
  return (double) j;
}

longlong Field_time0::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  return (longlong) sint3korr(ptr);
}


/**
  @note
  This function is multi-byte safe as the result string is always of type
  my_charset_bin
*/

String *Field_time::val_str(String *str,
			    String *unused __attribute__((unused)))
{
  DBUG_ASSERT(marked_for_read());
  MYSQL_TIME ltime;
  get_date(&ltime, Datetime::Options(TIME_TIME_ONLY, get_thd()));
  str->alloc(field_length + 1);
  str->length(my_time_to_str(&ltime, const_cast<char*>(str->ptr()), decimals()));
  str->set_charset(&my_charset_numeric);
  return str;
}


bool Field_time::check_zero_in_date_with_warn(date_mode_t fuzzydate)
{
  date_conv_mode_t tmp= date_conv_mode_t(fuzzydate);
  if (!(tmp & TIME_TIME_ONLY) && (tmp & TIME_NO_ZERO_IN_DATE))
  {
    THD *thd= get_thd();
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                        ER_WARN_DATA_OUT_OF_RANGE,
                        ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name.str,
                        thd->get_stmt_da()->current_row_for_warning());
    return true;
  }
  return false;
}


/**
  @note
  Normally we would not consider 'time' as a valid date, but we allow
  get_date() here to be able to do things like
  DATE_FORMAT(time, "%l.%i %p")
*/
 
bool Field_time0::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
  if (check_zero_in_date_with_warn(fuzzydate))
    return true;
  long tmp=(long) sint3korr(ptr);
  ltime->neg=0;
  if (tmp < 0)
  {
    ltime->neg= 1;
    tmp=-tmp;
  }
  ltime->year= ltime->month= ltime->day= 0;
  ltime->hour=   (int) (tmp/10000);
  tmp-=ltime->hour*10000;
  ltime->minute= (int) tmp/100;
  ltime->second= (int) tmp % 100;
  ltime->second_part=0;
  ltime->time_type= MYSQL_TIMESTAMP_TIME;
  return 0;
}


int Field_time::store_native(const Native &value)
{
  Time t(value);
  DBUG_ASSERT(t.is_valid_time());
  store_TIME(t);
  return 0;
}


bool Field_time::val_native(Native *to)
{
  MYSQL_TIME ltime;
  get_date(&ltime, date_mode_t(0));
  int warn;
  return Time(&warn, &ltime, 0).to_native(to, decimals());
}


bool Field_time::send(Protocol *protocol)
{
  MYSQL_TIME ltime;
  get_date(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
  return protocol->store_time(&ltime, decimals());
}


int Field_time0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  int32 a,b;
  a=(int32) sint3korr(a_ptr);
  b=(int32) sint3korr(b_ptr);
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_time0::sort_string(uchar *to,uint length __attribute__((unused)))
{
  to[0] = (uchar) (ptr[2] ^ 128);
  to[1] = ptr[1];
  to[2] = ptr[0];
}


int Field_time_hires::reset()
{
  store_bigendian(zero_point, ptr, Field_time_hires::pack_length());
  return 0;
}


void Field_time_hires::store_TIME(const MYSQL_TIME *ltime)
{
  DBUG_ASSERT(ltime->year == 0);
  DBUG_ASSERT(ltime->month == 0);
  ulonglong packed= sec_part_shift(pack_time(ltime), dec) + zero_point;
  store_bigendian(packed, ptr, Field_time_hires::pack_length());
}

int Field_time::store_decimal(const my_decimal *d)
{
  ErrConvDecimal str(d);
  int was_cut;
  Time tm(get_thd(), &was_cut, d, Time::Options(get_thd()), decimals());
  return store_TIME_with_warning(&tm, &str, was_cut);
}


bool Field_time::can_be_substituted_to_equal_item(const Context &ctx,
                                                  const Item_equal *item_equal)
{
  DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT);
  switch (ctx.subst_constraint()) {
  case ANY_SUBST:
    /*
      A TIME field in a DATETIME comparison can be substituted to
      Item_equal with TIME comparison.

        SET timestamp=UNIX_TIMESTAMP('2015-08-30 10:20:30');
        CREATE OR REPLACE TABLE t1 (a TIME);
        INSERT INTO t1 VALUES ('00:00:00'),('00:00:01');
        SELECT * FROM t1 WHERE a>=TIMESTAMP'2015-08-30 00:00:00'
                         AND a='00:00:00';

      The above query can be simplified to:
        SELECT * FROM t1 WHERE TIME'00:00:00'>=TIMESTAMP'2015-08-30 00:00:00'
                           AND a='00:00:00';
      And further to:
        SELECT * FROM t1 WHERE a=TIME'00:00:00';
    */
    if (ctx.compare_type_handler() == &type_handler_datetime &&
        item_equal->compare_type_handler() == &type_handler_time)
      return true;
    return ctx.compare_type_handler() == item_equal->compare_type_handler();
  case IDENTITY_SUBST:
    return true;
  }
  return false;
}


Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
                                       Item *const_item)
{
  /*
    Old mode conversion from DATETIME with non-zero YYYYMMDD part
    to TIME works very inconsistently. Possible variants:
    - truncate the YYYYMMDD part
    - add (MM*33+DD)*24 to hours
    - add (MM*31+DD)*24 to hours
    Let's disallow propagation of DATETIME with non-zero YYYYMMDD
    as an equal constant for a TIME field.
  */
  Time::datetime_to_time_mode_t mode=
    (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) ?
    Time::DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY :
    Time::DATETIME_TO_TIME_MINUS_CURRENT_DATE;

  switch (ctx.subst_constraint()) {
  case ANY_SUBST:
    if (const_item->field_type() != MYSQL_TYPE_TIME)
    {
      // Get the value of const_item with conversion from DATETIME to TIME
      Time tm(get_thd(), const_item, Time::Options_cmp(thd, mode));
      if (!tm.is_valid_time())
        return NULL;
      /*
        Replace a DATE/DATETIME constant to a TIME constant:
          WHERE LENGTH(time_column)=8
            AND time_column=TIMESTAMP'2015-08-30 10:20:30';
        to:
          WHERE LENGTH(time_column)=10
            AND time_column=TIME'10:20:30'

        (assuming CURRENT_DATE is '2015-08-30'
      */
      return new (thd->mem_root) Item_time_literal(thd, &tm,
                                                   tm.get_mysql_time()->
                                                   second_part ?
                                                   TIME_SECOND_PART_DIGITS :
                                                   0);
    }
    break;
   case IDENTITY_SUBST:
    if (const_item->field_type() != MYSQL_TYPE_TIME ||
        const_item->decimals != decimals())
    {
      /*
        Note, the value returned in "ltime" can have more fractional
        digits that decimals(). The Item_time_literal constructor will
        truncate these digits. We could disallow propagation is such
        cases, but it's still useful (and safe) to optimize:
          WHERE time0_column='00:00:00.123' AND LENGTH(a)=12
        to
          WHERE time0_column='00:00:00.123' AND LENGTH(TIME'00:00:00')=12
        and then to
          WHERE FALSE
        The original WHERE would do the full table scan (in case of no keys).
        The optimized WHERE will return with "Impossible WHERE", without
        having to do the full table scan.
      */
      Time tm(thd, const_item, Time::Options(TIME_TIME_ONLY, thd, mode),
              decimals());
      if (!tm.is_valid_time())
        return NULL;
      return new (thd->mem_root) Item_time_literal(thd, &tm, decimals());
    }
    break;
  }
  return const_item;
}


longlong Field_time_with_dec::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  MYSQL_TIME ltime;
  get_date(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
  longlong val= TIME_to_ulonglong_time(&ltime);
  return ltime.neg ? -val : val;
}

double Field_time_with_dec::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  MYSQL_TIME ltime;
  get_date(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
  return TIME_to_double(&ltime);
}

bool Field_time_hires::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
  if (check_zero_in_date_with_warn(fuzzydate))
    return true;
  uint32 len= pack_length();
  longlong packed= read_bigendian(ptr, len);

  packed= sec_part_unshift(packed - zero_point, dec);

  unpack_time(packed, ltime, MYSQL_TIMESTAMP_TIME);
  return false;
}


int Field_time_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  ulonglong a=read_bigendian(a_ptr, Field_time_hires::pack_length());
  ulonglong b=read_bigendian(b_ptr, Field_time_hires::pack_length());
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_time_hires::sort_string(uchar *to,uint length __attribute__((unused)))
{
  DBUG_ASSERT(length == Field_time_hires::pack_length());
  memcpy(to, ptr, length);
  to[0]^= 128;
}

void Field_time_with_dec::make_send_field(Send_field *field)
{
  Field::make_send_field(field);
  field->decimals= dec;
}

/****************************************************************************
** time type with fsp (MySQL-5.6 version)
** In string context: HH:MM:SS.FFFFFF
** In number context: HHMMSS.FFFFFF
****************************************************************************/

int Field_timef::reset()
{
  my_time_packed_to_binary(0, ptr, dec);
  return 0;
}

void Field_timef::store_TIME(const MYSQL_TIME *ltime)
{
  longlong tmp= TIME_to_longlong_time_packed(ltime);
  my_time_packed_to_binary(tmp, ptr, dec);
}

bool Field_timef::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
  if (check_zero_in_date_with_warn(fuzzydate))
    return true;
  longlong tmp= my_time_packed_from_binary(ptr, dec);
  TIME_from_longlong_time_packed(ltime, tmp);
  return false;
}
Binlog_type_info Field_timef::binlog_type_info() const
{
  return Binlog_type_info(Field_timef::binlog_type(), decimals(), 1);
}


longlong Field_timef::val_time_packed(THD *thd)
{
  DBUG_ASSERT(marked_for_read());
  longlong tmp= my_time_packed_from_binary(ptr, dec);
  MYSQL_TIME ltime;
  TIME_from_longlong_time_packed(&ltime, tmp);
  return pack_time(&ltime);
}


int Field_timef::store_native(const Native &value)
{
  DBUG_ASSERT(value.length() == my_time_binary_length(dec));
  DBUG_ASSERT(Time(value).is_valid_time());
  memcpy(ptr, value.ptr(), value.length());
  return 0;
}


bool Field_timef::val_native(Native *to)
{
  uint32 binlen= my_time_binary_length(dec);
  return to->copy((const char*) ptr, binlen);
}


/****************************************************************************
** year type
** Save in a byte the year 0, 1901->2155
** Can handle 2 byte or 4 byte years!
****************************************************************************/

int Field_year::store(const char *from, size_t len,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  THD *thd= get_thd();
  char *end;
  int error;
  longlong nr= cs->strntoull10rnd(from, len, 0, &end, &error);

  if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155 || 
      error == MY_ERRNO_ERANGE)
  {
    *ptr=0;
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    return 1;
  }

  if (thd->count_cuted_fields <= CHECK_FIELD_EXPRESSION && error == MY_ERRNO_EDOM)
  {
    *ptr= 0;
    return 1;
  }

  if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION && 
      (error= check_int(cs, from, len, end, error)))
  {
    if (unlikely(error == 1)  /* empty or incorrect string */)
    {
      *ptr= 0;
      return 1;
    }
    error= 1;
  }

  if (nr != 0 || len != 4)
  {
    if (nr < YY_PART_YEAR)
      nr+=100;					// 2000 - 2069
    else if (nr > 1900)
      nr-= 1900;
  }
  *ptr= (char) (uchar) nr;
  return error;
}


int Field_year::store(double nr)
{
  if (nr < 0.0 || nr > 2155.0)
  {
    (void) Field_year::store((longlong) -1, FALSE);
    return 1;
  }
  return Field_year::store((longlong) nr, FALSE);
}


int Field_year::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155)
  {
    *ptr= 0;
    set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    return 1;
  }
  if (nr != 0 || field_length != 4)		// 0000 -> 0; 00 -> 2000
  {
    if (nr < YY_PART_YEAR)
      nr+=100;					// 2000 - 2069
    else if (nr > 1900)
      nr-= 1900;
  }
  *ptr= (char) (uchar) nr;
  return 0;
}


int Field_year::store_time_dec(const MYSQL_TIME *ltime, uint)
{
  ErrConvTime str(ltime);
  if (Field_year::store(ltime->year, 0))
    return 1;

  const char *typestr= Temporal::type_name_by_timestamp_type(ltime->time_type);
  set_datetime_warning(WARN_DATA_TRUNCATED, &str, typestr, 1);
  return 0;
}

bool Field_year::send(Protocol *protocol)
{
  DBUG_ASSERT(marked_for_read());
  Protocol_text *txt;
  if ((txt= dynamic_cast<Protocol_text*>(protocol)))
    return send_numeric_zerofill_str(txt, PROTOCOL_SEND_SHORT);
  ulonglong tmp= Field_year::val_int();
  return protocol->store_short(tmp);
}


double Field_year::val_real(void)
{
  return (double) Field_year::val_int();
}


longlong Field_year::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  DBUG_ASSERT(field_length == 2 || field_length == 4);
  int tmp= (int) ptr[0];
  if (field_length != 4)
    tmp%=100;					// Return last 2 char
  else if (tmp)
    tmp+=1900;
  return (longlong) tmp;
}


String *Field_year::val_str(String *val_buffer,
			    String *val_ptr __attribute__((unused)))
{
  DBUG_ASSERT(field_length < 5);
  val_buffer->alloc(5);
  val_buffer->length(field_length);
  char *to=(char*) val_buffer->ptr();
  sprintf(to,field_length == 2 ? "%02d" : "%04d",(int) Field_year::val_int());
  val_buffer->set_charset(&my_charset_numeric);
  return val_buffer;
}


bool Field_year::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
{
  int tmp= (int) ptr[0];
  if (tmp || field_length != 4)
    tmp+= 1900;
  return int_to_datetime_with_warn(get_thd(),
                                   Longlong_hybrid(tmp * 10000, true),
                                   ltime, fuzzydate, table->s, field_name.str);
}


void Field_year::sql_type(String &res) const
{
  CHARSET_INFO *cs=res.charset();
  res.length(cs->cset->snprintf(cs,(char*)res.ptr(),res.alloced_length(),
			  "year(%d)",(int) field_length));
}


/*****************************************************************************/

int Field_date_common::store_TIME_with_warning(const Datetime *dt,
                                               const ErrConv *str,
                                               int was_cut)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  // Handle totally bad values
  if (!dt->is_valid_datetime())
    return store_invalid_with_warning(str, was_cut, "date");
  // Store the value
  if (!dt->hhmmssff_is_zero())
    was_cut|= MYSQL_TIME_NOTE_TRUNCATED;
  store_datetime(*dt);
  // Caclulate return value and send warnings if needed
  return store_TIME_return_code_with_warnings(was_cut, str, "date");
}

int Field_date_common::store(const char *from, size_t len, CHARSET_INFO *cs)
{
  MYSQL_TIME_STATUS st;
  ErrConvString str(from, len, cs);
  THD *thd= get_thd();
  Datetime dt(thd, &st, from, len, cs, Date::Options(thd), 0);
  return store_TIME_with_warning(&dt, &str, st.warnings);
}

int Field_date_common::store(double nr)
{
  int error;
  ErrConvDouble str(nr);
  THD *thd= get_thd();
  Datetime dt(thd, &error, nr, Date::Options(thd), 0);
  return store_TIME_with_warning(&dt, &str, error);
}

int Field_date_common::store(longlong nr, bool unsigned_val)
{
  int error;
  Longlong_hybrid tmp(nr, unsigned_val);
  ErrConvInteger str(tmp);
  THD *thd= get_thd();
  Datetime dt(&error, tmp, Date::Options(thd));
  return store_TIME_with_warning(&dt, &str, error);
}

int Field_date_common::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
  int error;
  ErrConvTime str(ltime);
  THD *thd= get_thd();
  Datetime dt(thd, &error, ltime, Date::Options(thd), 0);
  return store_TIME_with_warning(&dt, &str, error);
}

int Field_date_common::store_decimal(const my_decimal *d)
{
  int error;
  ErrConvDecimal str(d);
  THD *thd= get_thd();
  Datetime tm(thd, &error, d, Date::Options(thd), 0);
  return store_TIME_with_warning(&tm, &str, error);
}


/****************************************************************************
** date type
** In string context: YYYY-MM-DD
** In number context: YYYYMMDD
** Stored as a 4 byte unsigned int
****************************************************************************/

void Field_date::store_TIME(const MYSQL_TIME *ltime)
{
  uint tmp= ltime->year*10000L + ltime->month*100+ltime->day;
  int4store(ptr,tmp);
}

bool Field_date::send(Protocol *protocol)
{
  longlong tmp= Field_date::val_int();
  MYSQL_TIME tm;
  tm.year= (uint32) tmp/10000L % 10000;
  tm.month= (uint32) tmp/100 % 100;
  tm.day= (uint32) tmp % 100;
  return protocol->store_date(&tm);
}


double Field_date::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  int32 j;
  j=sint4korr(ptr);
  return (double) (uint32) j;
}


longlong Field_date::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  int32 j;
  j=sint4korr(ptr);
  return (longlong) (uint32) j;
}


bool Field_date::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
                          date_mode_t fuzzydate) const
{
  DBUG_ASSERT(marked_for_read());
  int32 tmp= sint4korr(pos);
  ltime->year= (int) ((uint32) tmp/10000L % 10000);
  ltime->month= (int) ((uint32) tmp/100 % 100);
  ltime->day= (int) ((uint32) tmp % 100);
  ltime->time_type= MYSQL_TIMESTAMP_DATE;
  ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0;
  return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
}


String *Field_date::val_str(String *val_buffer,
			    String *val_ptr __attribute__((unused)))
{
  MYSQL_TIME ltime;
  get_TIME(&ltime, ptr, date_mode_t(0));
  val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
  uint length= (uint) my_date_to_str(&ltime,
                                     const_cast<char*>(val_buffer->ptr()));
  val_buffer->length(length);
  val_buffer->set_charset(&my_charset_numeric);

  return val_buffer;
}


int Field_date::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  int32 a,b;
  a=sint4korr(a_ptr);
  b=sint4korr(b_ptr);
  return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
}


void Field_date::sort_string(uchar *to,uint length __attribute__((unused)))
{
  to[0] = ptr[3];
  to[1] = ptr[2];
  to[2] = ptr[1];
  to[3] = ptr[0];
}

void Field_date::sql_type(String &res) const
{
  res.set_ascii(STRING_WITH_LEN("date"));
}


/****************************************************************************
** The new date type
** This is identical to the old date type, but stored on 3 bytes instead of 4
** In number context: YYYYMMDD
****************************************************************************/

void Field_newdate::store_TIME(const MYSQL_TIME *ltime)
{
  uint tmp= ltime->year*16*32 + ltime->month*32+ltime->day;
  int3store(ptr,tmp);
}


bool Field_newdate::send(Protocol *protocol)
{
  MYSQL_TIME tm;
  Field_newdate::get_date(&tm, date_mode_t(0));
  return protocol->store_date(&tm);
}


double Field_newdate::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  return (double) Field_newdate::val_int();
}


longlong Field_newdate::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  ulong j= uint3korr(ptr);
  j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L;
  return (longlong) j;
}


String *Field_newdate::val_str(String *val_buffer,
			       String *val_ptr __attribute__((unused)))
{
  DBUG_ASSERT(marked_for_read());
  val_buffer->alloc(field_length+1);
  val_buffer->length(field_length);
  uint32 tmp=(uint32) uint3korr(ptr);
  int part;
  char *pos=(char*) val_buffer->ptr()+10;

  /* Open coded to get more speed */
  *pos--=0;					// End NULL
  part=(int) (tmp & 31);
  *pos--= (char) ('0'+part%10);
  *pos--= (char) ('0'+part/10);
  *pos--= '-';
  part=(int) (tmp >> 5 & 15);
  *pos--= (char) ('0'+part%10);
  *pos--= (char) ('0'+part/10);
  *pos--= '-';
  part=(int) (tmp >> 9);
  *pos--= (char) ('0'+part%10); part/=10;
  *pos--= (char) ('0'+part%10); part/=10;
  *pos--= (char) ('0'+part%10); part/=10;
  *pos=   (char) ('0'+part);
  val_buffer->set_charset(&my_charset_numeric);
  return val_buffer;
}


bool Field_newdate::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
                             date_mode_t fuzzydate) const
{
  DBUG_ASSERT(marked_for_read());
  uint32 tmp=(uint32) uint3korr(pos);
  ltime->day=   tmp & 31;
  ltime->month= (tmp >> 5) & 15;
  ltime->year=  (tmp >> 9);
  ltime->time_type= MYSQL_TIMESTAMP_DATE;
  ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0;
  return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
}


longlong Field_newdate::val_datetime_packed(THD *thd)
{
  MYSQL_TIME ltime;
  Field_newdate::get_date(&ltime, date_mode_t(0));
  return pack_time(&ltime);
}


int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  uint32 a,b;
  a=(uint32) uint3korr(a_ptr);
  b=(uint32) uint3korr(b_ptr);
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}


void Field_newdate::sort_string(uchar *to,uint length __attribute__((unused)))
{
  to[0] = ptr[2];
  to[1] = ptr[1];
  to[2] = ptr[0];
}


void Field_newdate::sql_type(String &res) const
{
  res.set_ascii(STRING_WITH_LEN("date"));
}


Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx,
                                          Item *const_item)
{
  switch (ctx.subst_constraint()) {
  case ANY_SUBST:
    if (!is_temporal_type_with_date(const_item->field_type()))
    {
      /*
        DATE is compared to DATETIME-alike non-temporal values
        (such as VARCHAR, DECIMAL) as DATETIME, e.g.:
          WHERE date_column=20010101235959.0000009
        So here we convert the constant to DATETIME normally.
        In case if TIME_ROUND_FRACTIONAL is enabled, nanoseconds will round.
      */
      Datetime dt(thd, const_item, Datetime::Options_cmp(thd));
      if (!dt.is_valid_datetime())
        return NULL;
      /*
        Replace the constant to a DATE or DATETIME constant.
        Example:
          WHERE LENGTH(date_column)=10
            AND date_column=TIME'10:20:30';
        to:
          WHERE LENGTH(date_column)=10
            AND date_column=TIMESTAMP'2015-08-30 10:20:30'

        (assuming CURRENT_DATE is '2015-08-30'
      */
      if (!dt.hhmmssff_is_zero())
        return new (thd->mem_root)
          Item_datetime_literal_for_invalid_dates(thd, &dt,
                                                  dt.get_mysql_time()->
                                                    second_part ?
                                                  TIME_SECOND_PART_DIGITS : 0);
      Date d(&dt);
      return new (thd->mem_root) Item_date_literal_for_invalid_dates(thd, &d);
    }
    break;
  case IDENTITY_SUBST:
    if (const_item->field_type() != MYSQL_TYPE_DATE)
    {
      /*
        DATE is compared to non-temporal as DATETIME.
        We need to convert to DATETIME first, taking into account the
        current session rounding mode (even though this is IDENTITY_SUBSTS!),
        then convert the result to DATE.
      */
      Datetime dt(thd, const_item, Datetime::Options(TIME_CONV_NONE, thd));
      if (!dt.is_valid_datetime())
        return NULL;
      Date d(&dt);
      return new (thd->mem_root) Item_date_literal(thd, &d);
    }
    break;
  }
  return const_item;
}


/****************************************************************************
** datetime type
** In string context: YYYY-MM-DD HH:MM:DD
** In number context: YYYYMMDDHHMMDD
** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int.
****************************************************************************/

void Field_datetime0::store_TIME(const MYSQL_TIME *ltime)
{
  ulonglong tmp= TIME_to_ulonglong_datetime(ltime);
  int8store(ptr,tmp);
}


sql_mode_t
Field_datetime::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
{
  return expr->datetime_precision(thd) > decimals() ?
         MODE_TIME_ROUND_FRACTIONAL : 0;
}


bool Field_datetime0::send(Protocol *protocol)
{
  MYSQL_TIME tm;
  Field_datetime0::get_date(&tm, date_mode_t(0));
  return protocol->store_datetime(&tm, 0);
}


longlong Field_datetime0::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  longlong j;
  j=sint8korr(ptr);
  return j;
}


String *Field_datetime0::val_str(String *val_buffer,
                                 String *val_ptr __attribute__((unused)))
{
  val_buffer->alloc(field_length+1);
  val_buffer->length(field_length);

  DBUG_ASSERT(marked_for_read());
  ulonglong tmp;
  long part1,part2;
  char *pos;
  int part3;

  tmp= Field_datetime0::val_int();

  /*
    Avoid problem with slow longlong arithmetic and sprintf
  */

  part1=(long) (tmp/1000000LL);
  part2=(long) (tmp - (ulonglong) part1*1000000LL);

  pos=(char*) val_buffer->ptr() + MAX_DATETIME_WIDTH;
  *pos--=0;
  *pos--= (char) ('0'+(char) (part2%10)); part2/=10;
  *pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10);
  *pos--= ':';
  *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
  *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
  *pos--= ':';
  *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
  *pos--= (char) ('0'+(char) part3);
  *pos--= ' ';
  *pos--= (char) ('0'+(char) (part1%10)); part1/=10;
  *pos--= (char) ('0'+(char) (part1%10)); part1/=10;
  *pos--= '-';
  *pos--= (char) ('0'+(char) (part1%10)); part1/=10;
  *pos--= (char) ('0'+(char) (part1%10)); part3= (int) (part1/10);
  *pos--= '-';
  *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
  *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
  *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
  *pos=(char) ('0'+(char) part3);
  val_buffer->set_charset(&my_charset_numeric);
  return val_buffer;
}

bool Field_datetime0::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
                               date_mode_t fuzzydate) const
{
  DBUG_ASSERT(marked_for_read());
  longlong tmp= sint8korr(pos);
  uint32 part1,part2;
  part1=(uint32) (tmp/1000000LL);
  part2=(uint32) (tmp - (ulonglong) part1*1000000LL);

  ltime->time_type=	MYSQL_TIMESTAMP_DATETIME;
  ltime->neg=		0;
  ltime->second_part=	0;
  ltime->second=	(int) (part2%100);
  ltime->minute=	(int) (part2/100%100);
  ltime->hour=		(int) (part2/10000);
  ltime->day=		(int) (part1%100);
  ltime->month= 	(int) (part1/100%100);
  ltime->year= 		(int) (part1/10000);
  return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
}


int Field_datetime0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  longlong a,b;
  a=sint8korr(a_ptr);
  b=sint8korr(b_ptr);
  return ((ulonglong) a < (ulonglong) b) ? -1 :
    ((ulonglong) a > (ulonglong) b) ? 1 : 0;
}

void Field_datetime0::sort_string(uchar *to,uint length __attribute__((unused)))
{
  to[0] = ptr[7];
  to[1] = ptr[6];
  to[2] = ptr[5];
  to[3] = ptr[4];
  to[4] = ptr[3];
  to[5] = ptr[2];
  to[6] = ptr[1];
  to[7] = ptr[0];
}


int Field_datetime::set_time()
{
  THD *thd= table->in_use;
  set_notnull();
  // Here we always truncate (not round), no matter what sql_mode is
  if (decimals())
    store_datetime(Datetime(thd, Timeval(thd->query_start(),
                                         thd->query_start_sec_part())
                            ).trunc(decimals()));
  else
    store_datetime(Datetime(thd, Timeval(thd->query_start(), 0)));
  return 0;
}


void Field_datetime_hires::store_TIME(const MYSQL_TIME *ltime)
{
  ulonglong packed= sec_part_shift(pack_time(ltime), dec);
  store_bigendian(packed, ptr, Field_datetime_hires::pack_length());
}

bool Field_datetime_with_dec::send(Protocol *protocol)
{
  MYSQL_TIME ltime;
  get_date(&ltime, date_mode_t(0));
  return protocol->store_datetime(&ltime, dec);
}


double Field_datetime_with_dec::val_real(void)
{
  MYSQL_TIME ltime;
  get_date(&ltime, date_mode_t(0));
  return TIME_to_double(&ltime);
}

longlong Field_datetime_with_dec::val_int(void)
{
  MYSQL_TIME ltime;
  get_date(&ltime, date_mode_t(0));
  return TIME_to_ulonglong_datetime(&ltime);
}


String *Field_datetime_with_dec::val_str(String *str,
                                         String *unused __attribute__((unused)))
{
  MYSQL_TIME ltime;
  get_date(&ltime, date_mode_t(0));
  str->alloc(field_length+1);
  str->length(field_length);
  my_datetime_to_str(&ltime, (char*) str->ptr(), dec);
  str->set_charset(&my_charset_numeric);
  return str;
}


bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
                                    date_mode_t fuzzydate) const
{
  DBUG_ASSERT(marked_for_read());
  ulonglong packed= read_bigendian(pos, Field_datetime_hires::pack_length());
  unpack_time(sec_part_unshift(packed, dec), ltime, MYSQL_TIMESTAMP_DATETIME);
  return validate_MMDD(packed, ltime->month, ltime->day, fuzzydate);
}


int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length());
  ulonglong b=read_bigendian(b_ptr, Field_datetime_hires::pack_length());
  return a < b ? -1 : a > b ? 1 : 0;
}

void Field_datetime_with_dec::make_send_field(Send_field *field)
{
  Field::make_send_field(field);
  field->decimals= dec;
}


/****************************************************************************
** MySQL-5.6 compatible DATETIME(N)
**
****************************************************************************/
int Field_datetimef::reset()
{
  my_datetime_packed_to_binary(0, ptr, dec);
  return 0;
}

void Field_datetimef::store_TIME(const MYSQL_TIME *ltime)
{
  longlong tmp= TIME_to_longlong_datetime_packed(ltime);
  my_datetime_packed_to_binary(tmp, ptr, dec);
}

bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
                               date_mode_t fuzzydate) const
{
  DBUG_ASSERT(marked_for_read());
  longlong tmp= my_datetime_packed_from_binary(pos, dec);
  TIME_from_longlong_datetime_packed(ltime, tmp);
  return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
}
Binlog_type_info Field_datetimef::binlog_type_info() const
{
  return Binlog_type_info(Field_datetimef::binlog_type(), decimals(), 1);
}

longlong Field_datetimef::val_datetime_packed(THD *thd)
{
  DBUG_ASSERT(marked_for_read());
  longlong tmp= my_datetime_packed_from_binary(ptr, dec);
  MYSQL_TIME ltime;
  TIME_from_longlong_datetime_packed(&ltime, tmp);
  return pack_time(&ltime);
}


/****************************************************************************
** string type
** A string may be varchar or binary
****************************************************************************/

/*
  Report "not well formed" or "cannot convert" error
  after storing a character string info a field.

  SYNOPSIS
    check_string_copy_error()
    copier                   - the conversion status
    end                      - the very end of the source string
                               that was just copied
    cs                       - character set of the string

  NOTES
    As of version 5.0 both cases return the same error:
  
      "Invalid string value: 'xxx' for column 't' at row 1"
  
  Future versions will possibly introduce a new error message:

      "Cannot convert character string: 'xxx' for column 't' at row 1"

  RETURN
    FALSE - If errors didn't happen
    TRUE  - If an error happened
*/

bool
Field_longstr::check_string_copy_error(const String_copier *copier,
                                       const char *end,
                                       CHARSET_INFO *cs)
{
  const char *pos;
  char tmp[32];

  if (likely(!(pos= copier->most_important_error_pos())))
    return FALSE;

  /* Ignore errors from internal expressions */
  if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
  {
    DBUG_ASSERT(sizeof(tmp) >= convert_to_printable_required_length(6));
    convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
    set_warning_truncated_wrong_value("string", tmp);
  }
  return TRUE;
}


/*
  Check if we lost any important data and send a truncation error/warning

  SYNOPSIS
    Field_longstr::report_if_important_data()
    pstr                     - Truncated rest of string
    end                      - End of truncated string
    count_spaces             - Treat traling spaces as important data

  RETURN VALUES
    0   - None was truncated (or we don't count cut fields)
    2   - Some bytes was truncated

  NOTE
    Check if we lost any important data (anything in a binary string,
    or any non-space in others). If only trailing spaces was lost,
    send a truncation note, otherwise send a truncation error.
    Silently ignore traling spaces if the count_space parameter is FALSE.
*/

int
Field_longstr::report_if_important_data(const char *pstr, const char *end,
                                        bool count_spaces)
{
  THD *thd;
  if ((pstr < end) &&
      (thd= get_thd())->count_cuted_fields > CHECK_FIELD_EXPRESSION)
  {
    if (test_if_important_data(field_charset(), pstr, end))
    {
      if (thd->abort_on_warning)
        set_warning(ER_DATA_TOO_LONG, 1);
      else
        set_warning(WARN_DATA_TRUNCATED, 1);
      return 2;
    }
    else if (count_spaces)
    {
      /* If we lost only spaces then produce a NOTE, not a WARNING */
      set_note(WARN_DATA_TRUNCATED, 1);
      return 2;
    }
  }
  return 0;
}


/*
  This is JSON specific.
  We should eventually add Field_json_varchar and Field_json_blob
  and move make_send_field() to the new classes.
*/
void Field_longstr::make_send_field(Send_field *field)
{
  Field_str::make_send_field(field);
  if (check_constraint)
  {
    /*
      Append the format that is implicitly implied by the CHECK CONSTRAINT.
      For example:
        CREATE TABLE t1 (js longtext DEFAULT NULL CHECK (json_valid(a)));
        SELECT j FROM t1;
      will add "format=json" to the extended type info metadata for t1.js.
    */
    check_constraint->expr->set_format_by_check_constraint(field);
  }
}


/*
  An optimized version that uses less stack than Field::send().
*/
bool Field_longstr::send(Protocol *protocol)
{
  String tmp;
  val_str(&tmp, &tmp);
  /*
    Ensure this function is only used with classes that do not allocate
    memory in val_str()
  */
  DBUG_ASSERT(tmp.alloced_length() == 0);
  return protocol->store(tmp.ptr(), tmp.length(), tmp.charset());
}


const Type_handler *Field_string::type_handler() const
{
  if (is_var_string())
    return &type_handler_var_string;
  /*
    This is a temporary solution and will be fixed soon (in 10.9?).
    Type_handler_string_json will provide its own Field_string_json.
  */
  if (Type_handler_json_common::has_json_valid_constraint(this))
   return &type_handler_string_json;
  return &type_handler_string;
}

	/* Copy a string and fill with space */

int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  uint copy_length;
  int rc;

  /* See the comment for Field_long::store(long long) */
  DBUG_ASSERT(!table || table->in_use == current_thd);

  rc= well_formed_copy_with_check((char*) ptr, field_length,
                                  cs, from, length,
                                  Field_string::char_length(),
                                  false, &copy_length);

  /* Append spaces if the string was shorter than the field. */
  if (copy_length < field_length)
    field_charset()->fill((char*) ptr + copy_length,
                          field_length - copy_length,
                          field_charset()->pad_char);

  return rc;
}


int Field_str::store(longlong nr, bool unsigned_val)
{
  char buff[64];
  uint  length;
  length= (uint) (field_charset()->longlong10_to_str)(buff, sizeof(buff),
                                                      (unsigned_val ? 10: -10),
                                                      nr);
  return store(buff, length, field_charset());
}


/**
  Store double value in Field_string or Field_varstring.

  Pretty prints double number into field_length characters buffer.

  @param nr            number
*/

int Field_str::store(double nr)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
  uint local_char_length= MY_MIN(sizeof(buff), Field_str::char_length());
  size_t length= 0;
  my_bool error= (local_char_length == 0);

  // my_gcvt() requires width > 0, and we may have a CHAR(0) column.
  if (likely(!error))
    length= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, local_char_length, buff, &error);

  if (unlikely(error))
  {
    if (get_thd()->abort_on_warning)
      set_warning(ER_DATA_TOO_LONG, 1);
    else
      set_warning(WARN_DATA_TRUNCATED, 1);
  }
  return store(buff, (uint)length, &my_charset_numeric);
}

bool Field_string::is_equal(const Column_definition &new_field) const
{
  DBUG_ASSERT(!compression_method());
  return (new_field.type_handler() == type_handler() &&
          new_field.char_length == char_length() &&
          new_field.charset->eq_collation(field_charset()) &&
          new_field.length == max_display_length());
}


int Field_longstr::store_decimal(const my_decimal *d)
{
  StringBuffer<DECIMAL_MAX_STR_LENGTH+1> str;
  d->to_string(&str);
  return store(str.ptr(), str.length(), str.charset());
}

Data_type_compatibility
Field_longstr::cmp_to_string_with_same_collation(const Item_bool_func *cond,
                                                 const Item *item) const
{
  return (!cmp_is_done_using_type_handler_of_this(cond, item) ?
          Data_type_compatibility::INCOMPATIBLE_DATA_TYPE :
          !charset()->eq_collation(cond->compare_collation()) ?
          Data_type_compatibility::INCOMPATIBLE_COLLATION :
          Data_type_compatibility::OK);
}


Data_type_compatibility
Field_longstr::cmp_to_string_with_stricter_collation(const Item_bool_func *cond,
                                                     const Item *item) const
{
  return (!cmp_is_done_using_type_handler_of_this(cond, item) ?
          Data_type_compatibility::INCOMPATIBLE_DATA_TYPE :
          (!charset()->eq_collation(cond->compare_collation()) &&
           !(cond->compare_collation()->state & MY_CS_BINSORT) &&
           !Utf8_narrow::should_do_narrowing(this, cond->compare_collation())) ?
          Data_type_compatibility::INCOMPATIBLE_COLLATION :
          Data_type_compatibility::OK);
}


Data_type_compatibility
Field_longstr::can_optimize_keypart_ref(const Item_bool_func *cond,
                                        const Item *item) const
{
  DBUG_ASSERT(cmp_type() == STRING_RESULT);
  /*
    So, we have an equality:  tbl.string_key = 'abc'

    The comparison is the string comparison. Can we use index lookups to
    find matching rows?  We can do that when:
     - The comparison uses the same collation as tbl.string_key
     - the comparison uses binary collation, while tbl.string_key
       uses some other collation.
       In this case, we will find matches in some collation. For example, for
       'abc' we may find 'abc', 'ABC', and 'äbc'.
       But we're certain that will find the row with the identical binary, 'abc'.
  */
  return cmp_to_string_with_stricter_collation(cond, item);
}


Data_type_compatibility
Field_longstr::can_optimize_hash_join(const Item_bool_func *cond,
                                      const Item *item) const
{
  DBUG_ASSERT(cmp_type() == STRING_RESULT);
  return cmp_to_string_with_same_collation(cond, item);
}


Data_type_compatibility
Field_longstr::can_optimize_group_min_max(const Item_bool_func *cond,
                                          const Item *const_item) const
{
  /*
    Can't use indexes when comparing a string to a number or a date
    Don't use an index when comparing strings of different collations.
  */
  DBUG_ASSERT(cmp_type() == STRING_RESULT);
  return cmp_to_string_with_same_collation(cond, const_item);
}


Data_type_compatibility
Field_longstr::can_optimize_range(const Item_bool_func *cond,
                                  const Item *item,
                                  bool is_eq_func) const
{
  return is_eq_func ?
         cmp_to_string_with_stricter_collation(cond, item) :
         cmp_to_string_with_same_collation(cond, item);
}


/**
  This overrides the default behavior of the parent constructor
  Warn_filter(thd) to suppress notes about trailing spaces in case of CHAR(N),
  as they are truncated during val_str().
  We still do want truncation notes in case of BINARY(N),
  as trailing spaces are not truncated in val_str().
*/
Field_string::Warn_filter_string::Warn_filter_string(const THD *thd,
                                                     const Field_string *field)
  :Warn_filter(!thd->no_errors,
               !thd->no_errors &&
               field->field_charset() == &my_charset_bin)
{ }


double Field_string::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  const LEX_CSTRING str= to_lex_cstring();
  return Converter_strntod_with_warn(thd,
                                     Warn_filter_string(thd, this),
                                     "DOUBLE",
                                     Field_string::charset(),
                                     str.str, str.length).result();
}


bool Field_string::val_bool()
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  const LEX_CSTRING str= to_lex_cstring();
  double res= Converter_strntod_with_warn(thd,
                                          Warn_filter_string(thd, this),
                                          "BOOLEAN",
                                          Field_string::charset(),
                                          str.str, str.length).result();
  return res != 0.0;
}


longlong Field_string::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  const LEX_CSTRING str= to_lex_cstring();
  return Converter_strntoll_with_warn(thd, Warn_filter_string(thd, this),
                                      Field_string::charset(),
                                      str.str, str.length).result();
}


sql_mode_t Field_string::value_depends_on_sql_mode() const
{
  return has_charset() ? MODE_PAD_CHAR_TO_FULL_LENGTH : sql_mode_t(0);
};


sql_mode_t Field_string::can_handle_sql_mode_dependency_on_store() const
{
  return has_charset() ? MODE_PAD_CHAR_TO_FULL_LENGTH : sql_mode_t(0);
}


LEX_CSTRING Field_string::to_lex_cstring() const
{
  DBUG_ASSERT(marked_for_read());
  /* See the comment for Field_long::store(long long) */
  DBUG_ASSERT(!table || table->in_use == current_thd);
  if (get_thd()->variables.sql_mode & MODE_PAD_CHAR_TO_FULL_LENGTH)
    return Lex_cstring((const char*) ptr,
                       field_charset()->charpos(ptr, ptr + field_length,
                                                Field_string::char_length()));
  return Lex_cstring((const char *) ptr,
                     field_charset()->lengthsp((const char*) ptr, field_length));
}


String *Field_string::val_str(String *val_buffer __attribute__((unused)),
			      String *val_ptr)
{
  DBUG_ASSERT(marked_for_read());
  const LEX_CSTRING str= to_lex_cstring();
  val_ptr->set(str.str, str.length, field_charset());
  return val_ptr;
}


my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  const LEX_CSTRING str= to_lex_cstring();
  Converter_str2my_decimal_with_warn(thd,
                                     Warn_filter_string(thd, this),
                                     E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM,
                                     Field_string::charset(),
                                     str.str, str.length, decimal_value);
  return decimal_value;
}


struct Check_field_param {
  const Field *field;
};

#ifdef HAVE_REPLICATION
static bool
check_field_for_37426(const void *param_arg)
{
  Check_field_param *param= (Check_field_param*) param_arg;
  DBUG_ASSERT(param->field->real_type() == MYSQL_TYPE_STRING);
  DBUG_PRINT("debug", ("Field %s - type: %d, size: %d",
                       param->field->field_name.str,
                       param->field->real_type(),
                       param->field->row_pack_length()));
  return param->field->row_pack_length() > 255;
}
#endif

bool
Field_string::compatible_field_size(uint field_metadata,
                                    const Relay_log_info *rli_arg,
                                    uint16 mflags __attribute__((unused)),
                                    int *order_var) const
{
#ifdef HAVE_REPLICATION
  const Check_field_param check_param = { this };
  if (rpl_master_has_bug(rli_arg, 37426, TRUE,
                         check_field_for_37426, &check_param))
    return FALSE;                        // Not compatible field sizes
#endif
  return Field::compatible_field_size(field_metadata, rli_arg, mflags, order_var);
}


int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  return field_charset()->coll->strnncollsp_nchars(field_charset(),
                                                   a_ptr, field_length,
                                                   b_ptr, field_length,
                                                   Field_string::char_length(),
                                                   0);
}


int Field_string::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
                             size_t prefix_char_len) const
{
  size_t field_len= table->field[field_index]->field_length;

  return field_charset()->coll->strnncollsp_nchars(field_charset(),
                                                   a_ptr, field_len,
                                                   b_ptr, field_len,
                                                   prefix_char_len,
                                                   0);
}


void Field_string::sort_string(uchar *to,uint length)
{
  /*
    Let's find the real value length to truncate trailing padding spaces.
    This is needed to avoid redundant WARN_SORTING_ON_TRUNCATED_LENGTH
    warnings.
  */
  const LEX_CSTRING str= to_lex_cstring();
  my_strnxfrm_ret_t rc=
    field_charset()->strnxfrm(to, length,
                              char_length() * field_charset()->strxfrm_multiply,
                              (const uchar *) str.str, str.length,
                              MY_STRXFRM_PAD_WITH_SPACE |
                              MY_STRXFRM_PAD_TO_MAXLEN);
  DBUG_ASSERT(rc.m_result_length == length);
  if (rc.m_warnings & MY_STRNXFRM_TRUNCATED_WEIGHT_REAL_CHAR)
    get_thd()->num_of_strings_sorted_on_truncated_length++;
}


void Field_string::sql_type(String &res) const
{
  THD *thd= table->in_use;
  CHARSET_INFO *cs=res.charset();
  size_t length;

  length= cs->cset->snprintf(cs,(char*) res.ptr(),
                             res.alloced_length(), "%s(%d)%s",
                             (type() == MYSQL_TYPE_VAR_STRING ?
                              (has_charset() ? "varchar" : "varbinary") :
			      (has_charset() ? "char" : "binary")),
                             (int) field_length / charset()->mbmaxlen,
                             type() == MYSQL_TYPE_VAR_STRING ? "/*old*/" : "");
  res.length(length);
  if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
      has_charset() && (charset()->state & MY_CS_BINSORT))
    res.append(STRING_WITH_LEN(" binary"));
}

/**
   For fields which are associated with character sets their length is provided
   in octets and their character set information is also provided as part of
   type information.

   @param   res       String which contains filed type and length.
*/
void Field_string::sql_rpl_type(String *res) const
{
  if (Field_string::has_charset())
  {
    CHARSET_INFO *cs= res->charset();
    DBUG_ASSERT(cs->mbminlen == 1);
    size_t length= cs->cset->snprintf(cs, (char*) res->ptr(),
                                      res->alloced_length(),
                                      "char(%u octets) character set %s",
                                      field_length,
                                      charset()->cs_name.str);
    res->length(length);
  }
  else
    Field_string::sql_type(*res);
 }

uchar *Field_string::pack(uchar *to, const uchar *from) const
{
  DBUG_PRINT("debug", ("Packing field '%s'", field_name.str));
  return StringPack(field_charset(), field_length).pack(to, from);
}


/**
   Unpack a string field from row data.

   This method is used to unpack a string field from a master whose size 
   of the field is less than that of the slave. Note that there can be a
   variety of field types represented with this class. Certain types like
   ENUM or SET are processed differently. Hence, the upper byte of the 
   @c param_data argument contains the result of field->real_type() from
   the master.

   @note For information about how the length is packed, see @c
   Field_string::save_field_metadata

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Real type (upper) and length (lower) values

   @return  New pointer into memory based on from + length of the data
*/
const uchar *
Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end,
                     uint param_data)
{
  return StringPack(field_charset(), field_length).unpack(to, from, from_end,
                                                          param_data);
}


/**
   Save the field metadata for string fields.

   Saves the real type in the first byte and the field length in the 
   second byte of the field metadata array at index of *metadata_ptr and
   *(metadata_ptr + 1).

   @note In order to be able to handle lengths exceeding 255 and be
   backwards-compatible with pre-5.1.26 servers, an extra two bits of
   the length has been added to the metadata in such a way that if
   they are set, a new unrecognized type is generated.  This will
   cause pre-5.1-26 servers to stop due to a field type mismatch,
   while new servers will be able to extract the extra bits. If the
   length is <256, there will be no difference and both a new and an
   old server will be able to handle it.

   @note The extra two bits are added to bits 13 and 14 of the
   parameter data (with 1 being the least siginficant bit and 16 the
   most significant bit of the word) by xoring the extra length bits
   with the real type.  Since all allowable types have 0xF as most
   significant bits of the metadata word, lengths <256 will not affect
   the real type at all, while all other values will result in a
   non-existent type in the range 17-244.

   @see Field_string::unpack

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/

Binlog_type_info_fixed_string::Binlog_type_info_fixed_string(uchar type_code,
                                                             uint32 octets,
                                                             CHARSET_INFO *cs)
 :Binlog_type_info(type_code, 0, 2, cs)
{
  DBUG_ASSERT(octets < 1024);
  DBUG_ASSERT((type_code & 0xF0) == 0xF0);
  DBUG_PRINT("debug", ("octets: %u, type_code: %u", octets, type_code));
  m_metadata= (type_code ^ ((octets & 0x300) >> 4)) +
              (((uint)(octets & 0xFF)) << 8);
}


Binlog_type_info Field_string::binlog_type_info() const
{
  DBUG_ASSERT(Field_string::type() == binlog_type());
  return Binlog_type_info_fixed_string(Field_string::binlog_type(),
                                       field_length, charset());
}


uint Field_string::packed_col_length() const
{
  return StringPack(field_charset(), field_length).
           packed_col_length(ptr);
}


uint Field_string::max_packed_col_length(uint max_length) const
{
  return StringPack::max_packed_col_length(max_length);
}


uint Field_string::get_key_image(uchar *buff, uint length, const uchar *ptr_arg,
                                 imagetype type_arg) const
{
  size_t bytes= field_charset()->charpos((char*) ptr_arg,
                                         (char*) ptr_arg + field_length,
                                         length / mbmaxlen());
  memcpy(buff, ptr_arg, bytes);
  if (bytes < length)
    field_charset()->fill((char*) buff + bytes,
                          length - bytes,
                          field_charset()->pad_char);
  return (uint)bytes;
}


Field *Field_string::make_new_field(MEM_ROOT *root, TABLE *new_table,
                                    bool keep_type)
{
  Field *field;
  if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
    field= Field::make_new_field(root, new_table, keep_type);
  else if ((field= new (root) Field_varstring(field_length, maybe_null(),
                                              &field_name,
                                              new_table->s, charset())))
  {
    /*
      Old VARCHAR field which should be modified to a VARCHAR on copy
      This is done to ensure that ALTER TABLE will convert old VARCHAR fields
      to now VARCHAR fields.
    */
    field->init_for_make_new_field(new_table, orig_table);
  }
  return field;
}


en_fieldtype Field_string::tmp_engine_column_type(bool use_packed_rows) const
{
  return field_length >= MIN_STRING_LENGTH_TO_PACK_ROWS ? FIELD_SKIP_ENDSPACE :
                                                          FIELD_NORMAL;
}

/****************************************************************************
  VARCHAR type
  Data in field->ptr is stored as:
    1 or 2 bytes length-prefix-header  (from Field_varstring::length_bytes)
    data

  NOTE:
  When VARCHAR is stored in a key (for handler::index_read() etc) it's always
  stored with a 2 byte prefix. (Just like blob keys).

  Normally length_bytes is calculated as (field_length < 256 : 1 ? 2)
  The exception is if there is a prefix key field that is part of a long
  VARCHAR, in which case field_length for this may be 1 but the length_bytes
  is 2.
****************************************************************************/

const uint Field_varstring::MAX_SIZE= UINT_MAX16;


const Type_handler *Field_varstring::type_handler() const
{
  /*
    This is a temporary solution and will be fixed soon (in 10.9?).
    Type_handler_varchar_json will provide its own Field_varstring_json
    and Field_varstring_compressed_json
  */
  if (Type_handler_json_common::has_json_valid_constraint(this))
    return &type_handler_varchar_json;
  return &type_handler_varchar;
}


/**
   Save the field metadata for varstring fields.

   Saves the field length in the first byte. Note: may consume
   2 bytes. Caller must ensure second byte is contiguous with
   first byte (e.g. array index 0,1).

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
Binlog_type_info Field_varstring::binlog_type_info() const
{
  DBUG_ASSERT(Field_varstring::type() == binlog_type());
  return Binlog_type_info(Field_varstring::type(), field_length, 2, charset());
}


bool Field_varstring::memcpy_field_possible(const Field *from) const
{
  return (Field_str::memcpy_field_possible(from) &&
          !compression_method() == !from->compression_method() &&
          length_bytes == ((Field_varstring*) from)->length_bytes &&
          (table->file && !(table->file->ha_table_flags() &
                            HA_RECORD_MUST_BE_CLEAN_ON_WRITE)));
}


int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  uint copy_length;
  int rc;

  rc= well_formed_copy_with_check((char*) get_data(), field_length,
                                  cs, from, length,
                                  Field_varstring::char_length(),
                                  true, &copy_length);

  store_length(copy_length);

  return rc;
}


double Field_varstring::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  return Converter_strntod_with_warn(thd, Warn_filter(thd), "DOUBLE",
                                     Field_varstring::charset(),
                                     (const char *) get_data(),
                                     get_length()).result();
}


longlong Field_varstring::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  return Converter_strntoll_with_warn(thd, Warn_filter(thd),
                                      Field_varstring::charset(),
                                      (const char *) get_data(),
                                      get_length()).result();
}


String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
				 String *val_ptr)
{
  DBUG_ASSERT(marked_for_read());
  val_ptr->set((const char*) get_data(), get_length(), field_charset());
  return val_ptr;
}


my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  Converter_str2my_decimal_with_warn(thd, Warn_filter(thd),
                                     E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM,
                                     Field_varstring::charset(),
                                     (const char *) get_data(),
                                     get_length(), decimal_value);
  return decimal_value;

}


/*
  An optimized version that uses less stack and less temporary
  variable initialization than Field_longstr::send()
*/
bool Field_varstring::send(Protocol *protocol)
{
  return protocol->store((const char *) get_data(), get_length(),
                         field_charset());
}


#ifdef HAVE_MEM_CHECK

/*
  Mark the unused part of the varstring as defined.

  This function is only used be Unique when calculating statistics.

  The marking is needed as we write the whole tree to disk in case of
  overflows.  For using or comparing values the undefined value part
  is never used. We could also use bzero() here, but it would be
  slower in production environments.
  This function is tested by main.stat_tables-enospc
*/

void Field_varstring::mark_unused_memory_as_defined()
{
  uint used_length __attribute__((unused)) = get_length();
  MEM_MAKE_DEFINED(get_data() + used_length, field_length - used_length);
}
#endif


int Field_varstring::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  uint a_length, b_length;
  int diff;

  if (length_bytes == 1)
  {
    a_length= (uint) *a_ptr;
    b_length= (uint) *b_ptr;
  }
  else
  {
    a_length= uint2korr(a_ptr);
    b_length= uint2korr(b_ptr);
  }
  set_if_smaller(a_length, field_length);
  set_if_smaller(b_length, field_length);
  diff= field_charset()->strnncollsp(a_ptr + length_bytes, a_length,
                                     b_ptr + length_bytes, b_length);
  return diff;
}


int Field_varstring::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
                                size_t prefix_char_len) const
{
  /* avoid more expensive strnncollsp_nchars() if possible */
  if (prefix_char_len * field_charset()->mbmaxlen ==
      table->field[field_index]->field_length)
    return Field_varstring::cmp(a_ptr, b_ptr);

  size_t a_length, b_length;

  if (length_bytes == 1)
  {
    a_length= *a_ptr;
    b_length= *b_ptr;
  }
  else
  {
    a_length= uint2korr(a_ptr);
    b_length= uint2korr(b_ptr);
  }
  return field_charset()->coll->strnncollsp_nchars(field_charset(),
                                                   a_ptr + length_bytes,
                                                   a_length,
                                                   b_ptr + length_bytes,
                                                   b_length,
                                                   prefix_char_len,
                                                   0);
}


/**
  @note
    varstring and blob keys are ALWAYS stored with a 2 byte length prefix
*/

int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length) const
{
  size_t length=  length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
  size_t local_char_length= max_key_length / mbmaxlen();

  local_char_length= field_charset()->charpos(ptr + length_bytes,
                                              ptr + length_bytes + length,
                                              local_char_length);
  set_if_smaller(length, local_char_length);
  return field_charset()->strnncollsp(ptr + length_bytes,
                                      length,
                                      key_ptr + HA_KEY_BLOB_LENGTH,
                                      uint2korr(key_ptr));
}


/**
  Compare to key segments (always 2 byte length prefix).

  @note
    This is used only to compare key segments created for index_read().
    (keys are created and compared in key.cc)
*/

int Field_varstring::key_cmp(const uchar *a,const uchar *b) const
{
  return field_charset()->strnncollsp(a + HA_KEY_BLOB_LENGTH, uint2korr(a),
                                      b + HA_KEY_BLOB_LENGTH, uint2korr(b));
}


void Field_varstring::sort_string(uchar *to,uint length)
{
  String buf;

  val_str(&buf, &buf);

  if (field_charset() == &my_charset_bin)
  {
    /* Store length last in high-byte order to sort longer strings first */
    if (length_bytes == 1)
      to[length - 1]= buf.length();
    else
      mi_int2store(to + length - 2, buf.length());
    length-= length_bytes;
  }

  my_strnxfrm_ret_t rc=
    field_charset()->strnxfrm(to, length,
                              char_length() * field_charset()->strxfrm_multiply,
                              (const uchar *) buf.ptr(), buf.length(),
                              MY_STRXFRM_PAD_WITH_SPACE |
                              MY_STRXFRM_PAD_TO_MAXLEN);
  DBUG_ASSERT(rc.m_result_length == length);
  if (rc.m_warnings & MY_STRNXFRM_TRUNCATED_WEIGHT_REAL_CHAR)
    get_thd()->num_of_strings_sorted_on_truncated_length++;
}


enum ha_base_keytype Field_varstring::key_type() const
{
  enum ha_base_keytype res;

  if (binary())
    res= length_bytes == 1 ? HA_KEYTYPE_VARBINARY1 : HA_KEYTYPE_VARBINARY2;
  else
    res= length_bytes == 1 ? HA_KEYTYPE_VARTEXT1 : HA_KEYTYPE_VARTEXT2;
  return res;
}


/*
  Compressed columns need one extra byte to store the compression method.
  This byte is invisible to the end user, but not for the storage engine.
*/

void Field_varstring::sql_type(String &res) const
{
  THD *thd= table->in_use;
  CHARSET_INFO *cs=res.charset();
  size_t length;

  length= cs->cset->snprintf(cs,(char*) res.ptr(),
                             res.alloced_length(), "%s(%u)",
                              (has_charset() ? "varchar" : "varbinary"),
                              (uint) char_length());
  res.length(length);
  if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
      has_charset() && (charset()->state & MY_CS_BINSORT))
    res.append(STRING_WITH_LEN(" binary"));
}

/**
   For fields which are associated with character sets their length is provided
   in octets and their character set information is also provided as part of
   type information.

   @param   res       String which contains filed type and length.
*/
void Field_varstring::sql_rpl_type(String *res) const
{
  if (Field_varstring::has_charset())
  {
    CHARSET_INFO *cs= res->charset();
    DBUG_ASSERT(cs->mbminlen == 1);
    size_t length= cs->cset->snprintf(cs, (char*) res->ptr(),
                                      res->alloced_length(),
                                      "varchar(%u octets) character set %s",
                                      field_length,
                                      charset()->cs_name.str);
    res->length(length);
  }
  else
    Field_varstring::sql_type(*res);
}


uint32 Field_varstring::data_length()
{
  return length_bytes == 1 ? (uint32) *ptr : uint2korr(ptr);
}

/*
  Functions to create a packed row.
*/

uchar *Field_varstring::pack(uchar *to, const uchar *from) const
{
  uint length= length_bytes == 1 ? (uint) *from : uint2korr(from);
  DBUG_ASSERT(length <= field_length);

  /* Length always stored little-endian */
  *to++= length & 0xFF;
  if (field_length > 255)
    *to++= (length >> 8) & 0xFF;

  /* Store bytes of string */
  if (length > 0)
    memcpy(to, from+length_bytes, length);
  return to+length;
}


/**
   Unpack a varstring field from row data.

   This method is used to unpack a varstring field from a master
   whose size of the field is less than that of the slave.

   @note
   The string length is always packed little-endian.
  
   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Length bytes from the master's field data

   @return  New pointer into memory based on from + length of the data
*/
const uchar *
Field_varstring::unpack(uchar *to, const uchar *from, const uchar *from_end,
                        uint param_data)
{
  uint length;
  uint l_bytes= (param_data && (param_data < field_length)) ? 
                (param_data <= 255) ? 1 : 2 : length_bytes;

  if (from + l_bytes > from_end)
    return 0;                                 // Error in data

  if (l_bytes == 1)
  {
    to[0]= *from++;
    length= to[0];
    if (length_bytes == 2)
      to[1]= 0;
  }
  else /* l_bytes == 2 */
  {
    length= uint2korr(from);
    to[0]= *from++;
    to[1]= *from++;
  }
  if (length)
  {
    if (from + length > from_end || length > field_length)
      return 0;                                 // Error in data
    memcpy(to+ length_bytes, from, length);
  }
  return from+length;
}


uint Field_varstring::packed_col_length() const
{
  if (field_length > 255)
    return uint2korr(ptr) + 2;
  return (uint) *ptr + 1;
}


uint Field_varstring::max_packed_col_length(uint max_length) const
{
  return (max_length > 255 ? 2 : 1)+max_length;
}

void Field_varstring::val_str_from_ptr(String *val, const uchar *ptr) const
{
  val->set((const char*) get_data(ptr), get_length(ptr), field_charset());
}

uint Field_varstring::get_key_image(uchar *buff, uint length,
                                    const uchar *ptr_arg,
                                    imagetype type_arg) const
{
  String val;
  val_str_from_ptr(&val, ptr_arg);

  uint local_char_length= val.charpos(length / mbmaxlen());
  if (local_char_length < val.length())
    val.length(local_char_length);
  /* Key is always stored with 2 bytes */
  int2store(buff, val.length());
  memcpy(buff + HA_KEY_BLOB_LENGTH, val.ptr(), val.length());
  if (val.length() < length)
  {
    /*
      Must clear this as we do a memcmp in opt_range.cc to detect
      identical keys
    */
    memset(buff + HA_KEY_BLOB_LENGTH + val.length(), 0, length - val.length());
  }
  return HA_KEY_BLOB_LENGTH + val.length();
}


void Field_varstring::set_key_image(const uchar *buff,uint length)
{
  length= uint2korr(buff);			// Real length is here
  (void) store((const char*) buff + HA_KEY_BLOB_LENGTH, length, field_charset());
}


int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
                                uint32 max_length) const
{
  uint32 a_length,b_length;

  if (length_bytes == 1)
  {
    a_length= (uint) *a_ptr;
    b_length= (uint) *b_ptr;
  }
  else
  {
    a_length= uint2korr(a_ptr);
    b_length= uint2korr(b_ptr);
  }
  set_if_smaller(a_length, max_length);
  set_if_smaller(b_length, max_length);
  if (a_length != b_length)
    return 1;
  return memcmp(a_ptr+length_bytes, b_ptr+length_bytes, a_length);
}


Field *Field_varstring::make_new_field(MEM_ROOT *root, TABLE *new_table,
                                       bool keep_type)
{
  Field_varstring *res= (Field_varstring*) Field::make_new_field(root,
                                                                 new_table,
                                                                 keep_type);
  if (res)
    res->length_bytes= length_bytes;
  return res;
}


Field *Field_varstring_compressed::make_new_field(MEM_ROOT *root, TABLE *new_table,
                                                  bool keep_type)
{
  Field_varstring *res;
  if (new_table->s->is_optimizer_tmp_table())
  {
    /*
      Compressed field cannot be part of a key. For optimizer temporary
      table we create uncompressed substitute.
    */
    res= new (root) Field_varstring(ptr, field_length, length_bytes, null_ptr,
                                    null_bit, Field::NONE, &field_name,
                                    new_table->s, charset());
    if (res)
    {
      res->init_for_make_new_field(new_table, orig_table);
      /* See Column_definition::create_length_to_internal_length_string() */
      res->field_length--;
    }
  }
  else
    res= (Field_varstring*) Field::make_new_field(root, new_table, keep_type);
  if (res)
    res->length_bytes= length_bytes;
  return res;
}

Field *Field_blob_compressed::make_new_field(MEM_ROOT *root, TABLE *new_table,
                                                  bool keep_type)
{
  Field_blob *res;
  if (new_table->s->is_optimizer_tmp_table())
  {
    /*
      Compressed field cannot be part of a key. For optimizer temporary
      table we create uncompressed substitute.
    */
    res= new (root) Field_blob(ptr, null_ptr, null_bit, Field::NONE, &field_name,
                               new_table->s, packlength, charset());
    if (res)
    {
      res->init_for_make_new_field(new_table, orig_table);
      /* See Column_definition::create_length_to_internal_length_string() */
      res->field_length--;
    }
  }
  else
    res= (Field_blob *) Field::make_new_field(root, new_table, keep_type);
  return res;
}



Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table,
                                      uchar *new_ptr, uint32 length,
                                      uchar *new_null_ptr, uint new_null_bit)
{
  Field_varstring *res;
  if ((res= (Field_varstring*) Field::new_key_field(root, new_table,
                                                    new_ptr, length,
                                                    new_null_ptr, new_null_bit)))
  {
    /* Keys length prefixes are always packed with 2 bytes */
    res->length_bytes= 2;
  }
  return res;
}

bool Field_varstring::is_equal(const Column_definition &new_field) const
{
  return (new_field.type_handler() == type_handler() &&
          new_field.length == field_length &&
          new_field.char_length == char_length() &&
          !new_field.compression_method() == !compression_method() &&
          new_field.charset->eq_collation(field_charset()));
}


void Field_varstring::hash_not_null(Hasher *hasher)
{
  DBUG_ASSERT(marked_for_read());
  DBUG_ASSERT(!is_null());
  uint len=  length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
  hasher->add(charset(), ptr + length_bytes, len);
}


/**
  Compress field

  @param[out]    to         destination buffer for compressed data
  @param[in]     to_length  size of to
  @param[in]     from       data to compress
  @param[in]     length     from length
  @param[in]     max_length truncate `from' to this length
  @param[out]    out_length compressed data length
  @param[in]     cs         from character set
  @param[in]     nchars     copy no more than "nchars" characters

  In worst case (no compression performed) storage requirement is increased by
  1 byte to store header. If it exceeds field length, normal data truncation is
  performed.

  Generic compressed header format (1 byte):

  Bits 1-4: method specific bits
  Bits 5-8: compression method

  If compression method is 0 then header is immediately followed by
  uncompressed data.

  If compression method is zlib:

  Bits 1-3: number of bytes occupied by original data length
  Bits   4: true if zlib wrapper not present
  Bits 5-8: store 8 (zlib)

  Header is immediately followed by original data length,
  followed by compressed data.
*/

int Field_longstr::compress(char *to, uint to_length,
                            const char *from, uint length,
                            uint max_length,
                            uint *out_length,
                            CHARSET_INFO *cs, size_t nchars)
{
  THD *thd= get_thd();
  char *buf;
  uint buf_length;
  int rc= 0;

  if (String::needs_conversion_on_storage(length, cs, field_charset()) ||
      max_length < length)
  {
    set_if_smaller(max_length, static_cast<ulonglong>(mbmaxlen()) * length + 1);
    if (!(buf= (char*) my_malloc(PSI_INSTRUMENT_ME, max_length, MYF(MY_WME))))
    {
      *out_length= 0;
      return -1;
    }

    rc= well_formed_copy_with_check(buf, max_length, cs, from, length,
                                    nchars, true, &buf_length);
  }
  else
  {
    buf= const_cast<char*>(from);
    buf_length= length;
  }

  if (buf_length == 0)
    *out_length= 0;
  else if (buf_length >= thd->variables.column_compression_threshold &&
      (*out_length= compression_method()->compress(thd, to, buf, buf_length)))
    status_var_increment(thd->status_var.column_compressions);
  else
  {
    /* Store uncompressed */
    to[0]= 0;
    if (buf_length < to_length)
      memcpy(to + 1, buf, buf_length);
    else
    {
      /* Storing string at blob capacity, e.g. 255 bytes string to TINYBLOB. */
      rc= well_formed_copy_with_check(to + 1, to_length - 1, cs, from, length,
                                      nchars, true, &buf_length);
    }
    *out_length= buf_length + 1;
  }

  if (buf != from)
    my_free(buf);
  return rc;
}


/*
  Memory is allocated only when original data was actually compressed.
  Otherwise val_ptr points at data located immediately after header.

  Data can be stored uncompressed if data was shorter than threshold
  or compressed data was longer than original data.
*/

String *Field_longstr::uncompress(String *val_buffer, String *val_ptr,
                                  const uchar *from, uint from_length) const
{
  if (from_length)
  {
    uchar method= (*from & 0xF0) >> 4;

    /* Uncompressed data */
    if (!method)
    {
      val_ptr->set((const char*) from + 1, from_length - 1, field_charset());
      return val_ptr;
    }

    if (compression_methods[method].uncompress)
    {
      if (!compression_methods[method].uncompress(val_buffer, from, from_length,
                                                  field_length))
      {
        val_buffer->set_charset(field_charset());
        status_var_increment(get_thd()->status_var.column_decompressions);
        return val_buffer;
      }
    }
  }

  /*
    It would be better to return 0 in case of errors, but to take the
    safer route, let's return a zero string and let the general
    handler catch the error.
  */
  val_ptr->set("", 0, field_charset());
  return val_ptr;
}


int Field_varstring_compressed::store(const char *from, size_t length,
                                      CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  uint compressed_length;
  int rc= compress((char*) get_data(), field_length, from, (uint) length,
                   Field_varstring_compressed::max_display_length(),
                   &compressed_length, cs,
                   Field_varstring_compressed::char_length());
  store_length(compressed_length);
  return rc;
}

void Field_varstring_compressed::val_str_from_ptr(String *val, const uchar *ptr) const
{
  uncompress(val, val, get_data(ptr), get_length(ptr));
}


String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr)
{
  DBUG_ASSERT(marked_for_read());
  return uncompress(val_buffer, val_ptr, get_data(), get_length());
}


double Field_varstring_compressed::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  String buf;
  val_str(&buf, &buf);
  return Converter_strntod_with_warn(thd, Warn_filter(thd), "DOUBLE",
                                     field_charset(),
                                     buf.ptr(), buf.length()).result();
}


longlong Field_varstring_compressed::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  String buf;
  val_str(&buf, &buf);
  return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(),
                                      buf.ptr(), buf.length()).result();
}


int Field_varstring_compressed::cmp(const uchar *a_ptr,
                                    const uchar *b_ptr) const
{
  String a, b;
  uint a_length, b_length;

  if (length_bytes == 1)
  {
    a_length= (uint) *a_ptr;
    b_length= (uint) *b_ptr;
  }
  else
  {
    a_length= uint2korr(a_ptr);
    b_length= uint2korr(b_ptr);
  }

  uncompress(&a, &a, a_ptr + length_bytes, a_length);
  uncompress(&b, &b, b_ptr + length_bytes, b_length);

  return sortcmp(&a, &b, field_charset());
}


Binlog_type_info Field_varstring_compressed::binlog_type_info() const
{
  return Binlog_type_info(Field_varstring_compressed::binlog_type(),
                          field_length, 2, charset());
}


/****************************************************************************
** blob type
** A blob is saved as a length and a pointer. The length is stored in the
** packlength slot and may be from 1-4.
****************************************************************************/

Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
		       enum utype unireg_check_arg,
                       const LEX_CSTRING *field_name_arg,
                       TABLE_SHARE *share, uint blob_pack_length,
		       const DTCollation &collation)
  :Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
                 null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
                 collation),
   packlength(blob_pack_length)
{
  DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently
  flags|= BLOB_FLAG;
  share->blob_fields++;
  /* TODO: why do not fill table->s->blob_field array here? */
}


void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number)
                                                                          const
{
  store_lowendian(i_number, i_ptr, i_packlength);
}


uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) const
{
  return (uint32)read_lowendian(pos, packlength_arg);
}


/**
  Copy a value from another BLOB field of the same character set.
  This method is used by Copy_field, e.g. during ALTER TABLE.
*/
int Field_blob::copy_value(Field_blob *from)
{
  DBUG_ASSERT(field_charset()->eq_collation(from->charset()));
  DBUG_ASSERT(!compression_method() == !from->compression_method());
  int rc= 0;
  uint32 length= from->get_length();
  uchar *data= from->get_ptr();
  if (packlength < from->packlength)
  {
    set_if_smaller(length, Field_blob::max_data_length());
    length= (uint32) Well_formed_prefix(field_charset(),
                                        (const char *) data, length).length();
    rc= report_if_important_data((const char *) data + length,
                                 (const char *) data + from->get_length(),
                                 true);
  }
  store_length(length);
  bmove(ptr + packlength, (uchar*) &data, sizeof(char*));
  return rc;
}


int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  size_t copy_length, new_length;
  uint copy_len;
  char *tmp;
  char buff[STRING_BUFFER_USUAL_SIZE];
  String tmpstr(buff,sizeof(buff), &my_charset_bin);
  int rc;

  if (!length)
  {
    bzero(ptr,Field_blob::pack_length());
    return 0;
  }

  /*
    For min/max fields of statistical data 'table' is set to NULL.
    It could not be otherwise as this data is shared by many instances
    of the same base table.
  */

  if (table && table->blob_storage)    // GROUP_CONCAT with ORDER BY | DISTINCT
  {
    DBUG_ASSERT(!f_is_hex_escape(flags));
    DBUG_ASSERT(field_charset() == cs);
    DBUG_ASSERT(length <= max_data_length());
    
    new_length= length;
    copy_length= table->in_use->variables.group_concat_max_len;
    if (new_length > copy_length)
    {
      new_length= Well_formed_prefix(cs,
                                     from, copy_length, new_length).length();
      table->blob_storage->set_truncated_value(true);
    }
    if (!(tmp= table->blob_storage->store(from, new_length)))
      goto oom_error;

    Field_blob::store_length(new_length);
    bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
    return 0;
  }

  /*
    If the 'from' address is in the range of the temporary 'value'-
    object we need to copy the content to a different location or it will be
    invalidated when the 'value'-object is reallocated to make room for
    the new character set.
  */
  if (from >= value.ptr() && from <= value.ptr()+value.length())
  {
    /*
      If content of the 'from'-address is cached in the 'value'-object
      it is possible that the content needs a character conversion.
    */
    if (!String::needs_conversion_on_storage(length, cs, field_charset()))
    {
      Field_blob::store_length(length);
      bmove(ptr + packlength, &from, sizeof(char*));
      return 0;
    }
    if (tmpstr.copy(from, length, cs))
      goto oom_error;
    from= tmpstr.ptr();
  }

  new_length= MY_MIN(max_data_length(), mbmaxlen() * length);
  if (value.alloc(new_length))
    goto oom_error;
  tmp= const_cast<char*>(value.ptr());

  if (f_is_hex_escape(flags))
  {
    copy_length= my_copy_with_hex_escaping(field_charset(),
                                           tmp, new_length,
                                           from, length);
    Field_blob::store_length(copy_length);
    bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
    return 0;
  }
  rc= well_formed_copy_with_check((char*) value.ptr(), (uint) new_length,
                                  cs, from, length,
                                  length, true, &copy_len);
  value.length(copy_len);
  Field_blob::store_length(copy_len);
  bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));

  return rc;

oom_error:
  /* Fatal OOM error */
  bzero(ptr,Field_blob::pack_length());
  return -1; 
}


void Field_blob::hash_not_null(Hasher *hasher)
{
  DBUG_ASSERT(marked_for_read());
  DBUG_ASSERT(!is_null());
  char *blob;
  memcpy(&blob, ptr + packlength, sizeof(char*));
  if (blob)
    hasher->add(Field_blob::charset(), blob, get_length(ptr));
}


double Field_blob::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  char *blob;
  memcpy(&blob, ptr+packlength, sizeof(char*));
  if (!blob)
    return 0.0;
  THD *thd= get_thd();
  return  Converter_strntod_with_warn(thd, Warn_filter(thd), "DOUBLE",
                                      Field_blob::charset(),
                                      blob, get_length(ptr)).result();
}


longlong Field_blob::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  char *blob;
  memcpy(&blob, ptr+packlength, sizeof(char*));
  if (!blob)
    return 0;
  THD *thd= get_thd();
  return Converter_strntoll_with_warn(thd, Warn_filter(thd),
                                      Field_blob::charset(),
                                      blob, get_length(ptr)).result();
}


String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
			    String *val_ptr)
{
  DBUG_ASSERT(marked_for_read());
  char *blob;
  memcpy(&blob, ptr+packlength, sizeof(char*));
  if (!blob)
    val_ptr->set("",0,charset());	// A bit safer than ->length(0)
  else
    val_ptr->set((const char*) blob,get_length(ptr),charset());
  return val_ptr;
}


my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
{
  DBUG_ASSERT(marked_for_read());
  const char *blob;
  size_t length;
  memcpy(&blob, ptr+packlength, sizeof(const uchar*));
  if (!blob)
  {
    blob= "";
    length= 0;
  }
  else
    length= get_length(ptr);

  THD *thd= get_thd();
  Converter_str2my_decimal_with_warn(thd, Warn_filter(thd),
                                     E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM,
                                     Field_blob::charset(),
                                     blob, length, decimal_value);
  return decimal_value;
}


int Field_blob::cmp(const uchar *a,uint32 a_length, const uchar *b,
		    uint32 b_length) const
{
  return field_charset()->strnncollsp(a, a_length, b, b_length);
}


int Field_blob::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  uchar *blob1,*blob2;
  memcpy(&blob1, a_ptr+packlength, sizeof(char*));
  memcpy(&blob2, b_ptr+packlength, sizeof(char*));
  size_t a_len= get_length(a_ptr), b_len= get_length(b_ptr);
  return cmp(blob1, (uint32)a_len, blob2, (uint32)b_len);
}


int Field_blob::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
                           size_t prefix_char_len) const
{
  uchar *blob1,*blob2;
  memcpy(&blob1, a_ptr+packlength, sizeof(char*));
  memcpy(&blob2, b_ptr+packlength, sizeof(char*));
  size_t a_len= get_length(a_ptr), b_len= get_length(b_ptr);
  return field_charset()->coll->strnncollsp_nchars(field_charset(),
                                                   blob1, a_len,
                                                   blob2, b_len,
                                                   prefix_char_len,
                                                   0);
}


int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
			   uint32 max_length) const
{
  char *a,*b;
  uint diff;
  uint32 a_length,b_length;
  memcpy(&a, a_ptr+packlength, sizeof(char*));
  memcpy(&b, b_ptr+packlength, sizeof(char*));
  a_length=get_length(a_ptr);
  if (a_length > max_length)
    a_length=max_length;
  b_length=get_length(b_ptr);
  if (b_length > max_length)
    b_length=max_length;
  if (uint32 len= MY_MIN(a_length,b_length))
    diff= memcmp(a,b,len);
  else
    diff= 0;
  return diff ? diff : (int) (a_length - b_length);
}


/* The following is used only when comparing a key */

uint Field_blob::get_key_image_itRAW(const uchar *ptr_arg, uchar *buff,
                                     uint length) const
{
  size_t blob_length= get_length(ptr_arg);
  const uchar *blob= get_ptr(ptr_arg);
  size_t local_char_length= length / mbmaxlen();
  local_char_length= field_charset()->charpos(blob, blob + blob_length,
                                              local_char_length);
  set_if_smaller(blob_length, local_char_length);

  if (length > blob_length)
  {
    /*
      Must clear this as we do a memcmp in opt_range.cc to detect
      identical keys
    */
    bzero(buff+HA_KEY_BLOB_LENGTH+blob_length, (length-blob_length));
    length=(uint) blob_length;
  }
  int2store(buff,length);
  if (length)
    memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length);
  return HA_KEY_BLOB_LENGTH+length;
}


void Field_blob::set_key_image(const uchar *buff,uint length)
{
  length= uint2korr(buff);
  (void) Field_blob::store((const char*) buff+HA_KEY_BLOB_LENGTH, length,
                           field_charset());
}


int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length) const
{
  uchar *blob1;
  size_t blob_length=get_length(ptr);
  memcpy(&blob1, ptr+packlength, sizeof(char*));
  CHARSET_INFO *cs= charset();
  size_t local_char_length= max_key_length / cs->mbmaxlen;
  local_char_length= cs->charpos(blob1, blob1+blob_length,
                                 local_char_length);
  set_if_smaller(blob_length, local_char_length);
  return Field_blob::cmp(blob1, (uint32)blob_length,
			 key_ptr+HA_KEY_BLOB_LENGTH,
			 uint2korr(key_ptr));
}

int Field_blob::key_cmp(const uchar *a,const uchar *b) const
{
  return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a),
			 b+HA_KEY_BLOB_LENGTH, uint2korr(b));
}


#ifndef DBUG_OFF
/* helper to assert that new_table->blob_storage is NULL */
static struct blob_storage_check
{
  union { bool b; intptr p; } val;
  blob_storage_check() { val.p= -1; val.b= false; }
} blob_storage_check;
#endif
Field *Field_blob::make_new_field(MEM_ROOT *root, TABLE *newt, bool keep_type)
{
  DBUG_ASSERT((intptr(newt->blob_storage) & blob_storage_check.val.p) == 0);
  if (newt->group_concat)
    return new (root) Field_blob(field_length, maybe_null(), &field_name,
                                 charset());
  return Field::make_new_field(root, newt, keep_type);
}


Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table,
                                 uchar *new_ptr, uint32 length,
                                 uchar *new_null_ptr, uint new_null_bit)
{
  Field_varstring *res= new (root) Field_varstring(new_ptr, length, 2,
                                                   new_null_ptr,
                                                   new_null_bit, Field::NONE,
                                                   &field_name,
                                                   table->s, charset());
  res->init(new_table);
  return res;
}


/**
   Save the field metadata for blob fields.

   Saves the pack length in the first byte of the field metadata array
   at index of *metadata_ptr.

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
Binlog_type_info Field_blob::binlog_type_info() const
{
  DBUG_ASSERT(Field_blob::type() == binlog_type());
  return Binlog_type_info(Field_blob::type(), pack_length_no_ptr(), 1,
                          charset());
}


uint32 Field_blob::sort_length() const
{
  return packlength == 4 ?
    UINT_MAX32 :
    (uint32) field_length + sort_suffix_length();
}


uint32 Field_blob::sort_suffix_length() const
{
  return field_charset() == &my_charset_bin ?  packlength : 0;
}


void Field_blob::sort_string(uchar *to,uint length)
{
  String buf;

  val_str(&buf, &buf);
  if (!buf.length() && field_charset()->pad_char == 0)
    bzero(to,length);
  else
  {
    if (field_charset() == &my_charset_bin)
    {
      /*
        Store length of blob last in blob to shorter blobs before longer blobs
      */
      length-= packlength;
      store_bigendian(buf.length(), to + length, packlength);
    }

    my_strnxfrm_ret_t rc=
      field_charset()->strnxfrm(to, length, length,
                                (const uchar *) buf.ptr(), buf.length(),
                                MY_STRXFRM_PAD_WITH_SPACE |
                                MY_STRXFRM_PAD_TO_MAXLEN);
    DBUG_ASSERT(rc.m_result_length == length);
    if (rc.m_warnings & MY_STRNXFRM_TRUNCATED_WEIGHT_REAL_CHAR)
      get_thd()->num_of_strings_sorted_on_truncated_length++;
  }
}


/*
  Return the data type handler, according to packlength.
  Implemented in field.cc rather than in field.h
  to avoid exporting type_handler_xxx with MYSQL_PLUGIN_IMPORT.
*/
const Type_handler *Field_blob::type_handler() const
{
  /*
    This is a temporary solution and will be fixed soon (in 10.9?).
    Type_handler_*blob_json will provide its own Field_blob_json
    and Field_blob_compressed_json.
  */
  if (Type_handler_json_common::has_json_valid_constraint(this))
    return Type_handler_json_common::
             json_blob_type_handler_by_length_bytes(packlength);

  switch (packlength) {
  case 1: return &type_handler_tiny_blob;
  case 2: return &type_handler_blob;
  case 3: return &type_handler_medium_blob;
  }
  return &type_handler_long_blob;
}


void Field_blob::sql_type(String &res) const
{
  const char *str;
  uint length;
  switch (packlength) {
  default: str="tiny"; length=4; break;
  case 2:  str="";     length=0; break;
  case 3:  str="medium"; length= 6; break;
  case 4:  str="long";  length=4; break;
  }
  res.set_ascii(str,length);
  if (charset() == &my_charset_bin)
  {
    res.append(STRING_WITH_LEN("blob"));
    if (packlength == 2 && (get_thd()->variables.sql_mode & MODE_ORACLE))
      res.append(STRING_WITH_LEN("(65535)"));
  }
  else
  {
    res.append(STRING_WITH_LEN("text"));
  }
}

uchar *Field_blob::pack(uchar *to, const uchar *from) const
{
  uint32 length=get_length(from, packlength);			// Length of from string

  /*
    Store max length, which will occupy packlength bytes. If the max
    length given is smaller than the actual length of the blob, we
    just store the initial bytes of the blob.
  */
  store_length(to, packlength, length);

  /*
    Store the actual blob data, which will occupy 'length' bytes.
   */
  if (length > 0)
  {
    from= get_ptr(from);
    memcpy(to+packlength, from,length);
  }
  return to+packlength+length;
}


/**
   Unpack a blob field from row data.

   This method is used to unpack a blob field from a master whose size of 
   the field is less than that of the slave. Note: This method is included
   to satisfy inheritance rules, but is not needed for blob fields. It
   simply is used as a pass-through to the original unpack() method for
   blob fields.

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data @c TRUE if base types should be stored in little-
                       endian format, @c FALSE if native format should
                       be used.

   @return  New pointer into memory based on from + length of the data
*/

const uchar *Field_blob::unpack(uchar *to, const uchar *from,
                                const uchar *from_end, uint param_data)
{
  DBUG_ENTER("Field_blob::unpack");
  DBUG_PRINT("enter", ("to: %p; from: %p; param_data: %u",
                       to, from, param_data));
  uint const master_packlength=
    param_data > 0 ? param_data & 0xFF : packlength;
  if (from + master_packlength > from_end)
    DBUG_RETURN(0);                             // Error in data
  uint32 const length= get_length(from, master_packlength);
  DBUG_DUMP("packed", from, length + master_packlength);
  if (from + master_packlength + length > from_end)
    DBUG_RETURN(0);
  set_ptr(length, const_cast<uchar*> (from) + master_packlength);
  DBUG_RETURN(from + master_packlength + length);
}


uint Field_blob::packed_col_length() const
{
  return (uint) read_lowendian(ptr, packlength) + packlength;
}


uint Field_blob::max_packed_col_length(uint max_length) const
{
  return (max_length > 255 ? 2 : 1)+max_length;
}


/*
  Blob fields are regarded equal if they have same character set,
  same blob store length and if either both are compressed or both are
  uncompressed.
  The logic for compression is that we don't have to uncompress and compress
  again an already compressed field just because compression method changes.
*/

bool Field_blob::is_equal(const Column_definition &new_field) const
{
  return (new_field.type_handler() == type_handler() &&
          !new_field.compression_method() == !compression_method() &&
          new_field.pack_length == pack_length() &&
          new_field.charset->eq_collation(field_charset()));
}


void Field_blob::make_send_field(Send_field *field)
{
  /*
    Historically all BLOB variant Fields are displayed as MYSQL_TYPE_BLOB
    in the result set metadata. Note, Item can work differently and
    display the exact BLOB type, such as
    MYSQL_TYPE_{TINY_BLOB|BLOB|MEDIUM_BLOB|LONG_BLOB}.
    QQ: this should be made consistent eventually.
  */
  Field_longstr::make_send_field(field);
  field->set_handler(&type_handler_blob);
}


bool Field_blob::make_empty_rec_store_default_value(THD *thd, Item *item)
{
  DBUG_ASSERT(flags & BLOB_FLAG);
  int res= item->save_in_field(this, true);
  DBUG_ASSERT(res != 3); // Field_blob never returns 3
  if (res)
    return true; // E.g. truncation happened
  reset(); // Clear the pointer to a String, it should not be written to frm
  return false;
}


int Field_blob_compressed::store(const char *from, size_t length,
                                 CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  uint compressed_length;
  uint max_length= max_data_length();
  uint to_length= (uint) MY_MIN(max_length, mbmaxlen() * length + 1);
  String tmp(from, length, cs);
  int rc;

  if (from >= value.ptr() && from <= value.end() && tmp.copy(from, length, cs))
    goto oom;

  if (value.alloc(to_length))
    goto oom;

  rc= compress((char*) value.ptr(), to_length, tmp.ptr(), (uint) length,
               max_length, &compressed_length, cs, (uint) length);
  set_ptr(compressed_length, (uchar*) value.ptr());
  return rc;

oom:
  set_ptr((uint32) 0, NULL);
  return -1;
}


String *Field_blob_compressed::val_str(String *val_buffer, String *val_ptr)
{
  DBUG_ASSERT(marked_for_read());
  return uncompress(val_buffer, val_ptr, get_ptr(), get_length());
}


double Field_blob_compressed::val_real(void)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  String buf;
  val_str(&buf, &buf);
  return Converter_strntod_with_warn(thd, Warn_filter(thd), "DOUBLE",
                                     field_charset(),
                                     buf.ptr(), buf.length()).result();
}


longlong Field_blob_compressed::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  THD *thd= get_thd();
  String buf;
  val_str(&buf, &buf);
  return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(),
                                      buf.ptr(), buf.length()).result();
}

Binlog_type_info Field_blob_compressed::binlog_type_info() const
{
  return Binlog_type_info(Field_blob_compressed::binlog_type(),
                          pack_length_no_ptr(), 1, charset());
}

/****************************************************************************
** enum type.
** This is a string which only can have a selection of different values.
** If one uses this string in a number context one gets the type number.
****************************************************************************/

sql_mode_t Field_enum::can_handle_sql_mode_dependency_on_store() const
{
  return MODE_PAD_CHAR_TO_FULL_LENGTH;
}


enum ha_base_keytype Field_enum::key_type() const
{
  switch (packlength) {
  default: return HA_KEYTYPE_BINARY;
  case 2: return HA_KEYTYPE_USHORT_INT;
  case 3: return HA_KEYTYPE_UINT24;
  case 4: return HA_KEYTYPE_ULONG_INT;
  case 8: return HA_KEYTYPE_ULONGLONG;
  }
}

void Field_enum::store_type(ulonglong value)
{
  store_lowendian(value, ptr, packlength);
}


/**
  @note
    Storing a empty string in a enum field gives a warning
    (if there isn't a empty value in the enum)
*/

int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int err= 0;
  char buff[STRING_BUFFER_USUAL_SIZE];
  String tmpstr(buff,sizeof(buff), &my_charset_bin);

  /* Convert character set if necessary */
  if (String::needs_conversion_on_storage(length, cs, field_charset()))
  { 
    uint dummy_errors;
    tmpstr.copy(from, length, cs, field_charset(), &dummy_errors);
    from= tmpstr.ptr();
    length=  tmpstr.length();
  }

  /* Remove end space */
  length= (uint) field_charset()->lengthsp(from, length);
  uint tmp=find_type2(m_typelib, from, length, 0, field_charset());
  if (!tmp)
  {
    if (length < 6) // Can't be more than 99999 enums
    {
      /* This is for reading numbers with LOAD DATA INFILE */
      char *end;
      tmp=(uint) cs->strntoul(from,length,10,&end,&err);
      if (err || end != from + length || tmp > m_typelib->count)
      {
	tmp=0;
	set_warning(WARN_DATA_TRUNCATED, 1);
	err= 1;
      }
      if ((get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) && !length)
        err= 0;
    }
    else
    {
      set_warning(WARN_DATA_TRUNCATED, 1);
      err= 1;
    }
  }
  store_type((ulonglong) tmp);
  return err;
}


int Field_enum::store(double nr)
{
  return Field_enum::store((longlong) nr, FALSE);
}


int Field_enum::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  if ((ulonglong) nr > m_typelib->count || nr == 0)
  {
    set_warning(WARN_DATA_TRUNCATED, 1);
    if (nr != 0 || get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
    {
      nr= 0;
      error= 1;
    }
  }
  store_type((ulonglong) (uint) nr);
  return error;
}


double Field_enum::val_real(void)
{
  return (double) Field_enum::val_int();
}


longlong Field_enum::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  return val_int(ptr);
}

longlong Field_enum::val_int(const uchar *real_ptr) const
{
  return read_lowendian(real_ptr, packlength);
}

/**
   Save the field metadata for enum fields.

   Saves the real type in the first byte and the pack length in the 
   second byte of the field metadata array at index of *metadata_ptr and
   *(metadata_ptr + 1).

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
Binlog_type_info Field_enum::binlog_type_info() const
{
  DBUG_ASSERT(Field_enum::type() == binlog_type());
  return Binlog_type_info(Field_enum::type(), real_type() + (pack_length() << 8),
                          2, charset(), m_typelib, NULL);
}


String *Field_enum::val_str(String *val_buffer __attribute__((unused)),
			    String *val_ptr)
{
  uint tmp=(uint) Field_enum::val_int();
  if (!tmp || tmp > m_typelib->count)
    val_ptr->set("", 0, field_charset());
  else
    val_ptr->set((const char*) m_typelib->type_names[tmp - 1],
                 m_typelib->type_lengths[tmp - 1],
                 field_charset());
  return val_ptr;
}

int Field_enum::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
  ulonglong a=Field_enum::val_int(a_ptr);
  ulonglong b=Field_enum::val_int(b_ptr);
  return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_enum::sort_string(uchar *to,uint length __attribute__((unused)))
{
  ulonglong value=Field_enum::val_int();
  to+=packlength-1;
  for (uint i=0 ; i < packlength ; i++)
  {
    *to-- = (uchar) (value & 255);
    value>>=8;
  }
}


void Field_enum::sql_type(String &res) const
{
  char buffer[255];
  String enum_item(buffer, sizeof(buffer), res.charset());

  res.length(0);
  res.append(STRING_WITH_LEN("enum("));

  bool flag=0;
  uint *len= m_typelib->type_lengths;
  for (const char **pos= m_typelib->type_names; *pos; pos++, len++)
  {
    uint dummy_errors;
    if (flag)
      res.append(',');
    /* convert to res.charset() == utf8, then quote */
    enum_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
    append_unescaped(&res, enum_item.ptr(), enum_item.length());
    flag= 1;
  }
  res.append(')');
}


Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table,
                                  bool keep_type)
{
  Field_enum *res= (Field_enum*) Field::make_new_field(root, new_table,
                                                       keep_type);
  if (res)
    res->m_typelib= copy_typelib(root, m_typelib);
  return res;
}


/*
   set type.
   This is a string which can have a collection of different values.
   Each string value is separated with a ','.
   For example "One,two,five"
   If one uses this string in a number context one gets the bits as a longlong
   number.
*/


int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  bool got_warning= 0;
  int err= 0;
  char *not_used;
  uint not_used2;
  char buff[STRING_BUFFER_USUAL_SIZE];
  String tmpstr(buff,sizeof(buff), &my_charset_bin);

  /* Convert character set if necessary */
  if (String::needs_conversion_on_storage(length, cs, field_charset()))
  { 
    uint dummy_errors;
    tmpstr.copy(from, length, cs, field_charset(), &dummy_errors);
    from= tmpstr.ptr();
    length=  tmpstr.length();
  }
  ulonglong tmp= find_set(m_typelib, from, length, field_charset(),
                          &not_used, &not_used2, &got_warning);
  if (!tmp && length && length < 22)
  {
    /* This is for reading numbers with LOAD DATA INFILE */
    char *end;
    tmp= cs->strntoull(from, length, 10, &end, &err);
    if (err || end != from + length)
    {
      set_warning(WARN_DATA_TRUNCATED, 1);
      store_type(0);
      return 1;
    }
    return Field_set::store((longlong) tmp, true/*unsigned*/);
  }
  else if (got_warning)
    set_warning(WARN_DATA_TRUNCATED, 1);
  store_type(tmp);
  return err;
}


int Field_set::store(longlong nr, bool unsigned_val)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int error= 0;
  ulonglong max_nr;

  if (sizeof(ulonglong) * 8 <= m_typelib->count)
    max_nr= ULONGLONG_MAX;
  else
    max_nr= (1ULL << m_typelib->count) - 1;

  if ((ulonglong) nr > max_nr)
  {
    nr&= max_nr;
    set_warning(WARN_DATA_TRUNCATED, 1);
    error=1;
  }
  store_type((ulonglong) nr);
  return error;
}


String *Field_set::val_str(String *val_buffer,
			   String *val_ptr __attribute__((unused)))
{
  ulonglong tmp=(ulonglong) Field_enum::val_int();
  uint bitnr=0;

  val_buffer->copy("", 0, field_charset());

  while (tmp && bitnr < (uint) m_typelib->count)
  {
    if (tmp & 1)
    {
      if (val_buffer->length())
	val_buffer->append(&field_separator, 1, &my_charset_latin1);
      String str(m_typelib->type_names[bitnr], m_typelib->type_lengths[bitnr],
		 field_charset());
      val_buffer->append(str);
    }
    tmp>>=1;
    bitnr++;
  }
  return val_buffer;
}


void Field_set::sql_type(String &res) const
{
  char buffer[255];
  String set_item(buffer, sizeof(buffer), res.charset());

  res.length(0);
  res.append(STRING_WITH_LEN("set("));

  bool flag=0;
  uint *len= m_typelib->type_lengths;
  for (const char **pos= m_typelib->type_names; *pos; pos++, len++)
  {
    uint dummy_errors;
    if (flag)
      res.append(',');
    /* convert to res.charset() == utf8, then quote */
    set_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
    append_unescaped(&res, set_item.ptr(), set_item.length());
    flag= 1;
  }
  res.append(')');
}

Binlog_type_info Field_set::binlog_type_info() const
{
  DBUG_ASSERT(Field_set::type() == binlog_type());
  return Binlog_type_info(Field_set::type(), real_type()
           + (pack_length() << 8), 2, charset(), NULL, m_typelib);
}

/**
  @retval
    1  if the fields are equally defined
  @retval
    0  if the fields are unequally defined
*/

bool Field::eq_def(const Field *field) const
{
  if (real_type() != field->real_type() || charset() != field->charset() ||
      pack_length() != field->pack_length())
    return 0;
  return 1;
}


/**
  Compare the first t1::count type names.

  @return TRUE if the type names of t1 match those of t2. FALSE otherwise.
*/

static bool compare_type_names(CHARSET_INFO *charset, const TYPELIB *t1,
                                                      const TYPELIB *t2)
{
  for (uint i= 0; i < t1->count; i++)
    if (charset->strnncoll(t1->type_names[i], t1->type_lengths[i],
                           t2->type_names[i], t2->type_lengths[i]))
      return FALSE;
  return TRUE;
}

/**
  @return
  returns 1 if the fields are equally defined
*/

bool Field_enum::eq_def(const Field *field) const
{
  const TYPELIB *values;

  if (!Field::eq_def(field))
    return FALSE;

  values= ((Field_enum*) field)->m_typelib;

  /* Definition must be strictly equal. */
  if (m_typelib->count != values->count)
    return FALSE;

  return compare_type_names(field_charset(), m_typelib, values);
}


/**
  Check whether two fields can be considered 'equal' for table
  alteration purposes. Fields are equal if they retain the same
  pack length and if new members are added to the end of the list.

  @return true if fields are compatible.
          false otherwise.
*/

bool Field_enum::is_equal(const Column_definition &new_field) const
{
  /*
    The fields are compatible if they have the same flags,
    type, charset and have the same underlying length.
  */
  if (new_field.type_handler() != type_handler() ||
      !new_field.charset->eq_collation(field_charset()) ||
      new_field.pack_length != pack_length())
    return false;

  /*
    Changing the definition of an ENUM or SET column by adding a new
    enumeration or set members to the end of the list of valid member
    values only alters table metadata and not table data.
  */
  if (m_typelib->count > new_field.typelib()->count)
    return false;

  /* Check whether there are modification before the end. */
  if (! compare_type_names(field_charset(), m_typelib, new_field.typelib()))
    return false;

  return true;
}


uchar *Field_enum::pack(uchar *to, const uchar *from) const
{
  DBUG_ENTER("Field_enum::pack");
  DBUG_PRINT("debug", ("packlength: %d", packlength));
  DBUG_DUMP("from", from, packlength);
  DBUG_RETURN(pack_int(to, from, packlength));
}

const uchar *Field_enum::unpack(uchar *to, const uchar *from, 
                                const uchar *from_end, uint param_data)
{
  DBUG_ENTER("Field_enum::unpack");
  DBUG_PRINT("debug", ("packlength: %d", packlength));
  DBUG_DUMP("from", from, packlength);
  DBUG_RETURN(unpack_int(to, from, from_end, packlength));
}


/**
  @return
  returns 1 if the fields are equally defined
*/
bool Field_num::eq_def(const Field *field) const
{
  if (!Field::eq_def(field))
    return 0;
  Field_num *from_num= (Field_num*) field;

  if (unsigned_flag != from_num->unsigned_flag ||
      (zerofill && !from_num->zerofill && !zero_pack()) ||
      dec != from_num->dec)
    return 0;
  return 1;
}


/**
  Check whether two numeric fields can be considered 'equal' for table
  alteration purposes. Fields are equal if they are of the same type
  and retain the same pack length.
*/

bool Field_num::is_equal(const Column_definition &new_field) const
{
  if (((new_field.flags & UNSIGNED_FLAG) != (flags & UNSIGNED_FLAG)) ||
      ((new_field.flags & AUTO_INCREMENT_FLAG) > (flags & AUTO_INCREMENT_FLAG)))
    return false;

  const Type_handler *th= type_handler(), *new_th = new_field.type_handler();

  if (th == new_th && new_field.pack_length == pack_length())
    return true;
  /* FIXME: Test and consider returning true for the following:
  TINYINT UNSIGNED to BIT(8)
  SMALLINT UNSIGNED to BIT(16)
  MEDIUMINT UNSIGNED to BIT(24)
  INT UNSIGNED to BIT(32)
  BIGINT UNSIGNED to BIT(64)

  BIT(1..7) to TINYINT, or BIT(1..8) to TINYINT UNSIGNED
  BIT(9..15) to SMALLINT, or BIT(9..16) to SMALLINT UNSIGNED
  BIT(17..23) to MEDIUMINT, or BIT(17..24) to MEDIUMINT UNSIGNED
  BIT(25..31) to INT, or BIT(25..32) to INT UNSIGNED
  BIT(57..63) to BIGINT, or BIT(57..64) to BIGINT UNSIGNED

  Note: InnoDB stores integers in big-endian format, and BIT appears
  to use big-endian format. For storage engines that use little-endian
  format for integers, we can only return true for the TINYINT
  conversion. */

  return false;
}


Data_type_compatibility
Field_enum::can_optimize_range_or_keypart_ref(const Item_bool_func *cond,
                                              const Item *item) const
{
  switch (item->cmp_type())
  {
  case TIME_RESULT:
    return Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
  case INT_RESULT:
  case DECIMAL_RESULT:
  case REAL_RESULT:
    return Data_type_compatibility::OK;
  case STRING_RESULT:
    return (charset()->eq_collation(cond->compare_collation()) ?
            Data_type_compatibility::OK :
            Data_type_compatibility::INCOMPATIBLE_COLLATION);
  case ROW_RESULT:
    DBUG_ASSERT(0);
    break;
  }
  return Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
}


/*
  Bit field.

  We store the first 0 - 6 uneven bits among the null bits 
  at the start of the record. The rest bytes are stored in 
  the record itself.

  For example:

  CREATE TABLE t1 (a int, b bit(17), c bit(21) not null, d bit(8));
  We would store data  as follows in the record:

  Byte        Bit
  1           7 - reserve for delete
              6 - null bit for 'a'
              5 - null bit for 'b'
              4 - first (high) bit of 'b'
              3 - first (high) bit of 'c'
              2 - second bit of 'c'
              1 - third bit of 'c'
              0 - fourth bit of 'c'
  2           7 - fifth bit of 'c'
              6 - null bit for 'd'
  3 - 6       four bytes for 'a'
  7 - 8       two bytes for 'b'
  9 - 10      two bytes for 'c'
  11          one byte for 'd'
*/

Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
                     uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
                     enum utype unireg_check_arg,
                     const LEX_CSTRING *field_name_arg)
  : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
          unireg_check_arg, field_name_arg),
    bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7),
    bytes_in_rec(len_arg / 8)
{
  DBUG_ENTER("Field_bit::Field_bit");
  DBUG_PRINT("enter", ("ptr_arg: %p, null_ptr_arg: %p, len_arg: %u, bit_len: %u, bytes_in_rec: %u",
                       ptr_arg, null_ptr_arg, len_arg, bit_len, bytes_in_rec));
  flags|= UNSIGNED_FLAG;
  /*
    Ensure that Field::eq() can distinguish between two different bit fields.
    (two bit fields that are not null, may have same ptr and null_ptr)
  */
  if (!null_ptr_arg)
    null_bit= bit_ofs_arg;
  DBUG_VOID_RETURN;
}


const DTCollation & Field_bit::dtcollation() const
{
  static DTCollation tmp(&my_charset_bin,
                         DERIVATION_IMPLICIT, MY_REPERTOIRE_UNICODE30);
  return tmp;
}


/*
  This method always calculates hash over 8 bytes.
  This is different from how the HEAP engine calculate hash:
  HEAP takes into account the actual octet size, so say for BIT(18)
  it calculates hash over three bytes only:
  - the incomplete byte with bits 16..17
  - the two full bytes with bits 0..15
  See hp_rec_hashnr(), hp_hashnr() for details.

  The HEAP way is more efficient, especially for short lengths.
  Let's consider fixing Field_bit eventually to do it in the HEAP way,
  with proper measures to upgrade partitioned tables easy.
*/
void Field_bit::hash_not_null(Hasher *hasher)
{
  DBUG_ASSERT(marked_for_read());
  DBUG_ASSERT(!is_null());
  longlong value= Field_bit::val_int();
  uchar tmp[8];
  mi_int8store(tmp,value);
  hasher->add(&my_charset_bin, tmp, 8);
}


size_t
Field_bit::do_last_null_byte() const
{
  /*
    Code elsewhere is assuming that bytes are 8 bits, so I'm using
    that value instead of the correct one: CHAR_BIT.

    REFACTOR SUGGESTION (Matz): Change to use the correct number of
    bits. On systems with CHAR_BIT > 8 (not very common), the storage
    will lose the extra bits.
  */
  DBUG_PRINT("test", ("bit_ofs: %d, bit_len: %d  bit_ptr: %p",
                      bit_ofs, bit_len, bit_ptr));
  uchar *result;
  if (bit_len == 0)
    result= null_ptr;
  else if (bit_ofs + bit_len > 8)
    result= bit_ptr + 1;
  else
    result= bit_ptr;

  if (result)
    return (size_t) (result - table->record[0]) + 1;
  return LAST_NULL_BYTE_UNDEF;
}


Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table,
                                uchar *new_ptr, uint32 length, 
                                uchar *new_null_ptr, uint new_null_bit)
{
  Field_bit *res;
  if ((res= (Field_bit*) Field::new_key_field(root, new_table, new_ptr, length,
                                              new_null_ptr, new_null_bit)))
  {
    /* Move bits normally stored in null_pointer to new_ptr */
    res->bit_ptr= new_ptr;
    res->bit_ofs= 0;
    if (bit_len)
      res->ptr++;                               // Store rest of data here
  }
  return res;
}


bool Field_bit::is_equal(const Column_definition &new_field) const
{
  return new_field.type_handler() == type_handler() &&
         new_field.length == max_display_length();
}

                       
int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int delta;

  for (; length && !*from; from++, length--)          // skip left 0's
    ;
  delta= (int)(bytes_in_rec - length);

  if (delta < -1 ||
      (delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) ||
      (!bit_len && delta < 0))
  {
    set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len);
    memset(ptr, 0xff, bytes_in_rec);
    if (get_thd()->really_abort_on_warning())
      set_warning(ER_DATA_TOO_LONG, 1);
    else
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    return 1;
  }
  /* delta is >= -1 here */
  if (delta > 0)
  {
    if (bit_len)
      clr_rec_bits(bit_ptr, bit_ofs, bit_len);
    bzero(ptr, delta);
    memcpy(ptr + delta, from, length);
  }
  else if (delta == 0)
  {
    if (bit_len)
      clr_rec_bits(bit_ptr, bit_ofs, bit_len);
    memcpy(ptr, from, length);
  }
  else
  {
    if (bit_len)
    {
      set_rec_bits((uchar) *from, bit_ptr, bit_ofs, bit_len);
      from++;
    }
    memcpy(ptr, from, bytes_in_rec);
  }
  return 0;
}


int Field_bit::store(double nr)
{
  return Field_bit::store((longlong) nr, FALSE);
}


int Field_bit::store(longlong nr, bool unsigned_val)
{
  char buf[8];

  mi_int8store(buf, nr);
  return store(buf, 8, NULL);
}


int Field_bit::store_decimal(const my_decimal *val)
{
  int err= 0;
  longlong i= convert_decimal2longlong(val, 1, &err);
  return MY_TEST(err | store(i, TRUE));
}


double Field_bit::val_real(void)
{
  return (double) Field_bit::val_int();
}


longlong Field_bit::val_int(void)
{
  DBUG_ASSERT(marked_for_read());
  ulonglong bits= 0;
  if (bit_len)
  {
    bits= get_rec_bits(bit_ptr, bit_ofs, bit_len);
    bits<<= (bytes_in_rec * 8);
  }

  switch (bytes_in_rec) {
  case 0: return bits;
  case 1: return bits | (ulonglong) ptr[0];
  case 2: return bits | mi_uint2korr(ptr);
  case 3: return bits | mi_uint3korr(ptr);
  case 4: return bits | mi_uint4korr(ptr);
  case 5: return bits | mi_uint5korr(ptr);
  case 6: return bits | mi_uint6korr(ptr);
  case 7: return bits | mi_uint7korr(ptr);
  default: return mi_uint8korr(ptr + bytes_in_rec - sizeof(longlong));
  }
}  


String *Field_bit::val_str(String *val_buffer,
                           String *val_ptr __attribute__((unused)))
{
  DBUG_ASSERT(marked_for_read());
  char buff[sizeof(longlong)];
  uint length= MY_MIN(pack_length(), sizeof(longlong));
  ulonglong bits= val_int();
  mi_int8store(buff,bits);

  val_buffer->alloc(length);
  memcpy((char *) val_buffer->ptr(), buff+8-length, length);
  val_buffer->length(length);
  val_buffer->set_charset(&my_charset_bin);
  return val_buffer;
}


my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value)
{
  DBUG_ASSERT(marked_for_read());
  int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 1, deciaml_value);
  return deciaml_value;
}


/*
  Compare two bit fields using pointers within the record.
  SYNOPSIS
    cmp_max()
    a                 Pointer to field->ptr in first record
    b                 Pointer to field->ptr in second record
    max_len           Maximum length used in index
  DESCRIPTION
    This method is used from key_rec_cmp used by merge sorts used
    by partitioned index read and later other similar places.
    The a and b pointer must be pointers to the field in a record
    (not the table->record[0] necessarily)
*/
int Field_bit::cmp_prefix(const uchar *a, const uchar *b,
                          size_t prefix_char_len) const
{
  my_ptrdiff_t a_diff= a - ptr;
  my_ptrdiff_t b_diff= b - ptr;
  if (bit_len)
  {
    int flag;
    uchar bits_a= get_rec_bits(bit_ptr+a_diff, bit_ofs, bit_len);
    uchar bits_b= get_rec_bits(bit_ptr+b_diff, bit_ofs, bit_len);
    if ((flag= (int) (bits_a - bits_b)))
      return flag;
  }
  if (!bytes_in_rec)
    return 0;
  return memcmp(a, b, bytes_in_rec);
}


int Field_bit::key_cmp(const uchar *str, uint) const
{
  if (bit_len)
  {
    int flag;
    uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len);
    if ((flag= (int) (bits - *str)))
      return flag;
    str++;
  }
  return memcmp(ptr, str, bytes_in_rec);
}


int Field_bit::cmp_offset(my_ptrdiff_t row_offset)
{
  if (bit_len)
  {
    int flag;
    uchar bits_a= get_rec_bits(bit_ptr, bit_ofs, bit_len);
    uchar bits_b= get_rec_bits(bit_ptr + row_offset, bit_ofs, bit_len);
    if ((flag= (int) (bits_a - bits_b)))
      return flag;
  }
  return memcmp(ptr, ptr + row_offset, bytes_in_rec);
}


uint Field_bit::get_key_image(uchar *buff, uint length, const uchar *ptr_arg, imagetype type_arg) const
{
  if (bit_len)
  {
    const uchar *bit_ptr_for_arg= ptr_arg + (bit_ptr - ptr);
    uchar bits= get_rec_bits(bit_ptr_for_arg, bit_ofs, bit_len);
    *buff++= bits;
    length--;
  }
  uint tmp_data_length = MY_MIN(length, bytes_in_rec);
  memcpy(buff, ptr, tmp_data_length);
  return tmp_data_length + 1;
}


/**
   Returns the number of bytes field uses in row-based replication 
   row packed size.

   This method is used in row-based replication to determine the number
   of bytes that the field consumes in the row record format. This is
   used to skip fields in the master that do not exist on the slave.

   @param   field_metadata   Encoded size in field metadata

   @returns The size of the field based on the field metadata.
*/
uint Field_bit::pack_length_from_metadata(uint field_metadata) const
{
  uint const from_len= (field_metadata >> 8U) & 0x00ff;
  uint const from_bit_len= field_metadata & 0x00ff;
  uint const source_size= from_len + ((from_bit_len > 0) ? 1 : 0);
  return (source_size);
}


bool
Field_bit::compatible_field_size(uint field_metadata,
                                 const Relay_log_info * __attribute__((unused)),
                                 uint16 mflags,
                                 int *order_var) const
{
  DBUG_ENTER("Field_bit::compatible_field_size");
  DBUG_ASSERT((field_metadata >> 16) == 0);
  uint from_bit_len=
    8 * (field_metadata >> 8) + (field_metadata & 0xff);
  uint to_bit_len= max_display_length();
  DBUG_PRINT("debug", ("from_bit_len: %u, to_bit_len: %u",
                       from_bit_len, to_bit_len));
  /*
    If the bit length exact flag is clear, we are dealing with an old
    master, so we allow some less strict behaviour if replicating by
    moving both bit lengths to an even multiple of 8.

    We do this by computing the number of bytes to store the field
    instead, and then compare the result.
   */
  if (!(mflags & Table_map_log_event::TM_BIT_LEN_EXACT_F)) {
    from_bit_len= (from_bit_len + 7) / 8;
    to_bit_len= (to_bit_len + 7) / 8;
  }

  *order_var= compare(from_bit_len, to_bit_len);
  DBUG_RETURN(TRUE);
}



void Field_bit::sql_type(String &res) const
{
  CHARSET_INFO *cs= res.charset();
  size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
                                   "bit(%d)", (int) field_length);
  res.length(length);
}


uchar *
Field_bit::pack(uchar *to, const uchar *from) const
{
  if (bit_len > 0)
  {
    /*
      We have the following:

      ptr        Points into a field in record R1
      from       Points to a field in a record R2
      bit_ptr    Points to the byte (in the null bytes) that holds the
                 odd bits of R1
      from_bitp  Points to the byte that holds the odd bits of R2

      We have the following:

          ptr - bit_ptr = from - from_bitp

      We want to isolate 'from_bitp', so this gives:

          ptr - bit_ptr - from = - from_bitp
          - ptr + bit_ptr + from = from_bitp
          bit_ptr + from - ptr = from_bitp
     */
    uchar bits= get_rec_bits(bit_ptr + (from - ptr), bit_ofs, bit_len);
    *to++= bits;
  }
  memcpy(to, from, bytes_in_rec);
  return to + bytes_in_rec;
}


/**
   Unpack a bit field from row data.

   This method is used to unpack a bit field from a master whose size
   of the field is less than that of the slave.

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Bit length (upper) and length (lower) values

   @return  New pointer into memory based on from + length of the data
*/
const uchar *
Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end,
                  uint param_data)
{
  DBUG_ENTER("Field_bit::unpack");
  DBUG_PRINT("enter", ("to: %p, from: %p, param_data: 0x%x",
                       to, from, param_data));
  DBUG_PRINT("debug", ("bit_ptr: %p, bit_len: %u, bit_ofs: %u",
                       bit_ptr, bit_len, bit_ofs));
  uint const from_len= (param_data >> 8U) & 0x00ff;
  uint const from_bit_len= param_data & 0x00ff;
  DBUG_PRINT("debug", ("from_len: %u, from_bit_len: %u",
                       from_len, from_bit_len));
  /*
    If the parameter data is zero (i.e., undefined), or if the master
    and slave have the same sizes, then use the old unpack() method.
  */
  if (param_data == 0 ||
      ((from_bit_len == bit_len) && (from_len == bytes_in_rec)))
  {
    if (from + bytes_in_rec + MY_TEST(bit_len) > from_end)
      return 0;                                 // Error in data

    if (bit_len > 0)
    {
      /*
        set_rec_bits is a macro, don't put the post-increment in the
        argument since that might cause strange side-effects.

        For the choice of the second argument, see the explanation for
        Field_bit::pack().
      */
      set_rec_bits(*from, bit_ptr + (to - ptr), bit_ofs, bit_len);
      from++;
    }
    memcpy(to, from, bytes_in_rec);
    DBUG_RETURN(from + bytes_in_rec);
  }

  /*
    We are converting a smaller bit field to a larger one here.
    To do that, we first need to construct a raw value for the original
    bit value stored in the from buffer. Then that needs to be converted
    to the larger field then sent to store() for writing to the field.
    Lastly the odd bits need to be masked out if the bytes_in_rec > 0.
    Otherwise stray bits can cause spurious values.
  */

  uint len= from_len + ((from_bit_len > 0) ? 1 : 0);
  uint new_len= (field_length + 7) / 8;

  if (from + len > from_end || new_len < len)
    return 0;                                 // Error in data

  char *value= (char *)my_alloca(new_len);
  bzero(value, new_len);

  memcpy(value + (new_len - len), from, len);
  /*
    Mask out the unused bits in the partial byte. 
    TODO: Add code to the master to always mask these bits and remove
          the following.
  */
  if ((from_bit_len > 0) && (from_len > 0))
    value[new_len - len]= value[new_len - len] & ((1U << from_bit_len) - 1);
  bitmap_set_bit(table->write_set,field_index);
  store(value, new_len, system_charset_info);
  my_afree(value);
  DBUG_RETURN(from + len);
}


int Field_bit::set_default()
{
  if (bit_len > 0)
  {
    my_ptrdiff_t const col_offset= table->s->default_values - table->record[0];
    uchar bits= get_rec_bits(bit_ptr + col_offset, bit_ofs, bit_len);
    set_rec_bits(bits, bit_ptr, bit_ofs, bit_len);
  }
  return Field::set_default();
}

/*
  Bit field support for non-MyISAM tables.
*/

Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg,
                                     uchar *null_ptr_arg, uchar null_bit_arg,
                                     enum utype unireg_check_arg,
                                     const LEX_CSTRING *field_name_arg)
  :Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0,
             unireg_check_arg, field_name_arg)
{
  flags|= UNSIGNED_FLAG;
  bit_len= 0;
  bytes_in_rec= (len_arg + 7) / 8;
}


int Field_bit_as_char::store(const char *from, size_t length, CHARSET_INFO *cs)
{
  DBUG_ASSERT(marked_for_write_or_computed());
  int delta;
  uchar bits= (uchar) (field_length & 7);

  for (; length && !*from; from++, length--)          // skip left 0's
    ;
  delta= (int)(bytes_in_rec - length);

  if (delta < 0 ||
      (delta == 0 && bits && (uint) (uchar) *from >= (uint) (1 << bits)))
  {
    memset(ptr, 0xff, bytes_in_rec);
    if (bits)
      *ptr&= ((1 << bits) - 1); /* set first uchar */
    if (get_thd()->really_abort_on_warning())
      set_warning(ER_DATA_TOO_LONG, 1);
    else
      set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
    return 1;
  }
  bzero(ptr, delta);
  memcpy(ptr + delta, from, length);
  return 0;
}


void Field_bit_as_char::sql_type(String &res) const
{
  CHARSET_INFO *cs= res.charset();
  size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
                                   "bit(%d)", (int) field_length);
  res.length(length);
}


/*****************************************************************************
  Handling of field and Create_field
*****************************************************************************/

bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
                                               bool reuse_interval_list_values)
{
  DBUG_ENTER("Column_definition::create_interval_from_interval_list");
  DBUG_ASSERT(!typelib());
  TYPELIB *tmpint;
  if (!set_typelib(tmpint= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB))).
         typelib())
    DBUG_RETURN(true); // EOM

  List_iterator<String> it(interval_list);
  StringBuffer<64> conv;
  char comma_buf[5]; /* 5 bytes for 'filename' charset */
  DBUG_ASSERT(sizeof(comma_buf) >= charset->mbmaxlen);
  int comma_length= charset->wc_mb(',',
                                   (uchar*) comma_buf,
                                   (uchar*) comma_buf + sizeof(comma_buf));
  DBUG_ASSERT(comma_length >= 0 && comma_length <= (int) sizeof(comma_buf));

  if (!multi_alloc_root(mem_root,
                        &tmpint->type_names,
                        sizeof(char*) * (interval_list.elements + 1),
                        &tmpint->type_lengths,
                        sizeof(uint) * (interval_list.elements + 1),
                        NullS))
    goto err; // EOM

  tmpint->name= "";
  tmpint->count= interval_list.elements;

  for (uint i= 0; i < interval_list.elements; i++)
  {
    uint32 dummy;
    String *tmp= it++;
    LEX_CSTRING value;
    if (String::needs_conversion(tmp->length(), tmp->charset(),
                                 charset, &dummy))
    {
      uint cnv_errs;
      conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), charset, &cnv_errs);
      value.str= strmake_root(mem_root, conv.ptr(), conv.length());
      value.length= conv.length();
    }
    else
    {
      value.str= reuse_interval_list_values ? tmp->ptr() :
                                              strmake_root(mem_root,
                                                           tmp->ptr(),
                                                           tmp->length());
      value.length= tmp->length();
    }
    if (!value.str)
      goto err; // EOM

    // Strip trailing spaces.
    value.length= charset->lengthsp(value.str, value.length);
    ((char*) value.str)[value.length]= '\0';

    if (real_field_type() == MYSQL_TYPE_SET)
    {
      if (charset->instr(value.str, value.length,
                         comma_buf, comma_length, NULL, 0))
      {
        ErrConvString err(tmp);
        my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr());
        goto err;
      }
    }
    tmpint->type_names[i]= value.str;
    tmpint->type_lengths[i]= (uint)value.length;
  }
  tmpint->type_names[interval_list.elements]= 0;    // End marker
  tmpint->type_lengths[interval_list.elements]= 0;
  interval_list.empty();  // Don't need interval_list anymore
  DBUG_RETURN(false);
err:
  // Avoid having both non-empty interval_list and the typelib attribute
  set_typelib(NULL);
  DBUG_RETURN(true);
}


bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root,
                                               bool reuse_interval_list_values)
{
  DBUG_ENTER("Column_definition::prepare_interval_field");
  DBUG_ASSERT(real_field_type() == MYSQL_TYPE_ENUM ||
              real_field_type() == MYSQL_TYPE_SET);
  /*
    Interval values are either in "interval" or in "interval_list",
    but not in both at the same time, and are not empty at the same time.
    - Values are in "interval_list" when we're coming from the parser
      in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}.
    - Values are in "interval" when we're in ALTER TABLE.

    In a corner case with an empty set like SET(''):
    - after the parser we have interval_list.elements==1
    - in ALTER TABLE we have a non-NULL interval with interval->count==1,
      with interval->type_names[0]=="" and interval->type_lengths[0]==0.
    So the assert is still valid for this corner case.

    ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible,
    as the parser requires at least one element, so for a ENUM or SET field it
    should never happen that both internal_list.elements and interval are 0.
  */
  DBUG_ASSERT((typelib() == NULL) == (interval_list.elements > 0));

  /*
    Create typelib from interval_list, and if necessary
    convert strings from client character set to the
    column character set.
  */
  if (interval_list.elements &&
      create_interval_from_interval_list(mem_root,
                                         reuse_interval_list_values))
    DBUG_RETURN(true);

  if (!reuse_interval_list_values)
  {
    /*
      We're initializing from an existing table or view Field_enum
      (e.g. for a %TYPE variable) rather than from the parser.
      The constructor Column_definition(THD*,Field*,Field*) has already
      copied the TYPELIB pointer from the original Field_enum.
      Now we need to make a permanent copy of that TYPELIB,
      as the original field can be freed before the end of the life
      cycle of "this".
    */
    DBUG_ASSERT(typelib());
    if (!set_typelib(copy_typelib(mem_root, typelib())).typelib())
      DBUG_RETURN(true);
  }
  prepare_interval_field_calc_length();
  DBUG_RETURN(false);
}


bool Column_definition::set_attributes(THD *thd,
                                       const Lex_field_type_st &def,
                                       column_definition_type_t type)
{
  DBUG_ASSERT(type_handler() == &type_handler_null);
  DBUG_ASSERT(length == 0);
  DBUG_ASSERT(decimals == 0);

  set_handler(def.type_handler());
  return type_handler()->Column_definition_set_attributes(thd, this,
                                                          def, type);
}


void
Column_definition_attributes::set_length_and_dec(const Lex_length_and_dec_st
                                                 &type)
{
  if (type.has_explicit_length())
    length= type.length_overflowed() ? (ulonglong) UINT_MAX32 + 1 :
                                       (ulonglong) type.length();

  if (type.has_explicit_dec())
    decimals= type.dec();
}


void Column_definition::create_length_to_internal_length_bit()
{
  if (f_bit_as_char(pack_flag))
  {
    pack_length= ((length + 7) & ~7) / 8;
  }
  else
  {
    pack_length= (uint) length / 8;
  }
}


void Column_definition::create_length_to_internal_length_newdecimal()
{
  DBUG_ASSERT(length < UINT_MAX32);
  decimal_digit_t prec= get_decimal_precision((uint)length, decimals,
                                              flags & UNSIGNED_FLAG);
  pack_length= my_decimal_get_binary_size(prec, decimals);
}


bool check_expression(Virtual_column_info *vcol, const Lex_ident_column &name,
                      enum_vcol_info_type type, Alter_info *alter_info)
{
  bool ret;
  Item::vcol_func_processor_result res;
  res.alter_info= alter_info;

  if (!vcol->name.length)
    vcol->name= name;

  /*
    Walk through the Item tree checking if all items are valid
    to be part of the virtual column
  */
  ret= vcol->expr->walk(&Item::check_vcol_func_processor, &res, 0);
  vcol->flags= res.errors;

  uint filter= VCOL_IMPOSSIBLE;
  if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT)
    filter|= VCOL_NOT_STRICTLY_DETERMINISTIC;
  if (type != VCOL_DEFAULT)
    filter|= VCOL_NEXTVAL;

  if (unlikely(ret || (res.errors & filter)))
  {
    my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
             vcol_type_name(type), name.str);
    return TRUE;
  }
  /*
    Safe to call before fix_fields as long as vcol's don't include sub
    queries (which is now checked in check_vcol_func_processor)
  */
  if (vcol->expr->check_cols(1))
    return TRUE;
  return FALSE;
}


bool Column_definition::check_length(uint mysql_errno, uint limit) const
{
  if (length <= limit)
    return false;
  my_error(mysql_errno, MYF(0), field_name.str, static_cast<ulong>(limit));
  return true;
}


bool Column_definition::fix_attributes_int(uint default_length)
{
  if (length)
    return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
  length= default_length;
  return false;
}


bool Column_definition::fix_attributes_real(uint default_length)
{
  /* change FLOAT(precision) to FLOAT or DOUBLE */
  if (!length && !decimals)
  {
    length= default_length;
    decimals= NOT_FIXED_DEC;
  }
  if (length < decimals && decimals != NOT_FIXED_DEC)
  {
    my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str);
    return true;
  }
  if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS)
  {
    my_error(ER_TOO_BIG_SCALE, MYF(0),
             field_name.str, static_cast<uint>(FLOATING_POINT_DECIMALS-1));
    return true;
  }
  return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
}


bool Column_definition::fix_attributes_decimal()
{
  if (decimals >= NOT_FIXED_DEC)
  {
    my_error(ER_TOO_BIG_SCALE, MYF(0),
             field_name.str, static_cast<uint>(NOT_FIXED_DEC - 1));
    return true;
  }
  my_decimal_trim(&length, &decimals);
  if (length > DECIMAL_MAX_PRECISION)
  {
    my_error(ER_TOO_BIG_PRECISION, MYF(0), field_name.str,
             DECIMAL_MAX_PRECISION);
    return true;
  }
  if (length < decimals)
  {
    my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str);
    return true;
  }
  length= my_decimal_precision_to_length((uint) length, decimals,
                                         flags & UNSIGNED_FLAG);
  pack_length= my_decimal_get_binary_size((uint) length, decimals);
  return false;
}


bool Column_definition::fix_attributes_bit()
{
  if (!length)
    length= 1;
  pack_length= ((uint) length + 7) / 8;
  return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_BIT_FIELD_LENGTH);
}


bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length)
{
  if (length > MAX_DATETIME_PRECISION)
  {
    my_error(ER_TOO_BIG_PRECISION, MYF(0), field_name.str,
             MAX_DATETIME_PRECISION);
    return true;
  }
  decimals= (uint) length;
  length+= int_part_length + (length ? 1 : 0);
  return false;
}


bool Column_definition::validate_check_constraint(THD *thd)
{
  return check_constraint &&
         check_expression(check_constraint, field_name, VCOL_CHECK_FIELD);
}


bool Column_definition::check(THD *thd)
{
  DBUG_ENTER("Column_definition::check");

  /* Initialize data for a computed field */
  if (vcol_info)
  {
    DBUG_ASSERT(vcol_info->expr);
    vcol_info->set_handler(type_handler());
    if (check_expression(vcol_info, field_name, vcol_info->is_stored()
                         ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL))
      DBUG_RETURN(TRUE);
  }

  if (type_handler()->Column_definition_validate_check_constraint(thd, this))
    DBUG_RETURN(TRUE);

  if (default_value)
  {
    Item *def_expr= default_value->expr;
    if (check_expression(default_value, field_name, VCOL_DEFAULT))
      DBUG_RETURN(TRUE);

    /* Constant's are stored in the 'empty_record', except for blobs */
    if (def_expr->basic_const_item())
    {
      if (def_expr->type() == Item::NULL_ITEM)
      {
        default_value= 0;
        if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG)
        {
          my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
          DBUG_RETURN(1);
        }
      }
    }
  }

  if (default_value && (flags & AUTO_INCREMENT_FLAG))
  {
    my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
    DBUG_RETURN(1);
  }

  if (default_value && !default_value->expr->basic_const_item() &&
      mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME &&
      default_value->expr->type() == Item::FUNC_ITEM)
  {
    /*
      Special case: NOW() for TIMESTAMP and DATETIME fields are handled
      as in MariaDB 10.1 by marking them in unireg_check.
    */
    Item_func *fn= static_cast<Item_func*>(default_value->expr);
    if (fn->functype() == Item_func::NOW_FUNC &&
        (fn->decimals == 0 || fn->decimals >= length))
    {
      default_value= 0;
      unireg_check= Field::TIMESTAMP_DN_FIELD;
    }
  }

  if (on_update)
  {
    if (mysql_timestamp_type() != MYSQL_TIMESTAMP_DATETIME ||
        on_update->decimals < length)
    {
      my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name.str);
      DBUG_RETURN(TRUE);
    }
    unireg_check= unireg_check == Field::NONE ? Field::TIMESTAMP_UN_FIELD
                                              : Field::TIMESTAMP_DNUN_FIELD;
  }
  else if (flags & AUTO_INCREMENT_FLAG)
    unireg_check= Field::NEXT_NUMBER;

  if (type_handler()->Column_definition_fix_attributes(this))
    DBUG_RETURN(true);

  /* Remember the value of length */
  char_length= (uint)length;

  /*
    Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and
    it is NOT NULL, not an AUTO_INCREMENT field.
    We need to do this check here and in mysql_create_prepare_table() as
    sp_head::fill_field_definition() calls this function.
  */
  if (!default_value && unireg_check == Field::NONE && (flags & NOT_NULL_FLAG))
  {
    /*
      TIMESTAMP columns get implicit DEFAULT value when
      explicit_defaults_for_timestamp is not set.
    */
    if (((thd->variables.option_bits & OPTION_EXPLICIT_DEF_TIMESTAMP) ||
        !is_timestamp_type()) && !vers_sys_field())
    {
      flags|= NO_DEFAULT_VALUE_FLAG;
    }
  }


  if ((flags & AUTO_INCREMENT_FLAG) &&
      !type_handler()->type_can_have_auto_increment_attribute())
  {
    my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str);
    DBUG_RETURN(TRUE);
  }

  DBUG_RETURN(FALSE); /* success */
}

enum_field_types get_blob_type_from_length(ulong length)
{
  enum_field_types type;
  if (length < 256)
    type= MYSQL_TYPE_TINY_BLOB;
  else if (length < 65536)
    type= MYSQL_TYPE_BLOB;
  else if (length < 256L*256L*256L)
    type= MYSQL_TYPE_MEDIUM_BLOB;
  else
    type= MYSQL_TYPE_LONG_BLOB;
  return type;
}


uint pack_length_to_packflag(uint type)
{
  switch (type) {
    case 1: return f_settype((uint) MYSQL_TYPE_TINY);
    case 2: return f_settype((uint) MYSQL_TYPE_SHORT);
    case 3: return f_settype((uint) MYSQL_TYPE_INT24);
    case 4: return f_settype((uint) MYSQL_TYPE_LONG);
    case 8: return f_settype((uint) MYSQL_TYPE_LONGLONG);
  }
  return 0;					// This shouldn't happen
}


uint Column_definition_attributes::pack_flag_to_pack_length() const
{
  uint type= f_packtype(pack_flag); // 0..15
  DBUG_ASSERT(type < 16);
  switch (type) {
  case MYSQL_TYPE_TINY:     return 1;
  case MYSQL_TYPE_SHORT:    return 2;
  case MYSQL_TYPE_LONG:     return 4;
  case MYSQL_TYPE_LONGLONG: return 8;
  case MYSQL_TYPE_INT24:    return 3;
  }
  return 0; // This should not happen
}


Field *Column_definition_attributes::make_field(TABLE_SHARE *share,
                                                MEM_ROOT *mem_root,
                                                const Record_addr *rec,
                                                const Type_handler *handler,
                                                const LEX_CSTRING *field_name,
                                                uint32 flags)
                                                const
{
  DBUG_ASSERT(length <= UINT_MAX32);
  DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
                       handler->name().ptr(), (uint) length, typelib(),
                       FLAGSTR(pack_flag, FIELDFLAG_BINARY),
                       FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
                       FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
                       FLAGSTR(pack_flag, FIELDFLAG_PACK),
                       FLAGSTR(pack_flag, FIELDFLAG_BLOB)));

  Record_addr addr(rec->ptr(), f_maybe_null(pack_flag) ? rec->null() :
                                                         Bit_addr());
  /*
    Special code for the BIT-alike data types
    who store data bits together with NULL-bits.
  */
  Bit_addr bit(rec->null());
  if (f_maybe_null(pack_flag))
    bit.inc();
  return handler->make_table_field_from_def(share, mem_root, field_name,
                                            addr, bit, this, flags);
}


bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const
{
  return item->is_of_type(Item::CONST_ITEM, TIME_RESULT);
}


Column_definition_attributes::Column_definition_attributes(const Field *field)
 :length(field->character_octet_length() / field->charset()->mbmaxlen),
  charset(field->charset()), // May be NULL ptr
  pack_flag(0),
  decimals(field->decimals()),
  unireg_check(field->unireg_check)
{}


Column_definition_attributes::
  Column_definition_attributes(const Type_all_attributes &attr)
  :Type_extra_attributes(attr.type_extra_attributes()),
  length(attr.max_length),
  charset(attr.collation.collation),
  pack_flag(attr.unsigned_flag ? 0 : FIELDFLAG_DECIMAL),
  decimals(attr.decimals),
  unireg_check(Field::NONE)
{}


/** Create a field suitable for create of table. */

Column_definition::Column_definition(THD *thd, Field *old_field,
                                               Field *orig_field)
 :Column_definition_attributes(old_field)
{
  on_update=  NULL;
  field_name= old_field->field_name;
  flags=      old_field->flags;
  pack_length=old_field->pack_length();
  set_handler(old_field->type_handler());
  comment=    old_field->comment;
  vcol_info=  old_field->vcol_info;
  option_list= old_field->option_list;
  explicitly_nullable= !(old_field->flags & NOT_NULL_FLAG);
  compression_method_ptr= 0;
  versioning= VERSIONING_NOT_SET;
  invisible= old_field->invisible;
  interval_list.empty(); // prepare_interval_field() needs this
  char_length= (uint) length;

  if (orig_field)
  {
    default_value= orig_field->default_value;
    check_constraint= orig_field->check_constraint;
    if (orig_field->unireg_check == Field::TMYSQL_COMPRESSED)
    {
      unireg_check= Field::TMYSQL_COMPRESSED;
      compression_method_ptr= zlib_compression_method;
    }
  }
  else
  {
    default_value= 0;
    check_constraint= 0;
  }

  type_handler()->Column_definition_reuse_fix_attributes(thd, this, old_field);

  /*
    Copy the default (constant/function) from the column object orig_field, if
    supplied. We do this if all these conditions are met:

    - The column allows a default.

    - The column type is not a BLOB type (as BLOB's doesn't have constant
      defaults)

    - The original column (old_field) was properly initialized with a record
      buffer pointer.

    - The column didn't have a default expression
  */
  if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
      old_field->ptr != NULL && orig_field != NULL)
  {
    if (orig_field->unireg_check != Field::NEXT_NUMBER)
      unireg_check= orig_field->unireg_check;

    /* Get the value from default_values */
    const uchar *dv= orig_field->table->s->default_values;
    if (!default_value && !orig_field->is_null_in_record(dv))
    {
      StringBuffer<MAX_FIELD_WIDTH> tmp(charset);
      String *res= orig_field->val_str(&tmp, orig_field->ptr_in_record(dv));
      char *pos= (char*) thd->strmake(res->ptr(), res->length());
      default_value= new (thd->mem_root) Virtual_column_info();
      default_value->expr=
        new (thd->mem_root) Item_string(thd, pos, res->length(), charset);
      default_value->utf8= 0;
    }
  }
}


/**
  The common part for data type redefinition:
    CREATE TABLE t1 (a INT) AS SELECT a FROM t2;
  See Type_handler::Column_definition_redefine_stage1()
  for data type specific code.

  @param this         - The field definition corresponding to the expression
                        in the "AS SELECT.." part.

  @param dup_field    - The field definition from the "CREATE TABLE (...)" part.
                        It has already underwent prepare_stage1(), so
                        must be fully initialized:
                        -- dup_field->charset is set and BINARY
                           sorting style is applied, see find_bin_collation().

  @param file         - The table handler
*/
void
Column_definition::redefine_stage1_common(const Column_definition *dup_field,
                                          const handler *file)
{
  set_handler(dup_field->type_handler());
  Type_extra_attributes::operator=(*dup_field);
  default_value= dup_field->default_value;
  DBUG_ASSERT(dup_field->charset); // Set by prepare_stage1()
  charset=      dup_field->charset;
  length=       dup_field->char_length;
  pack_length=  dup_field->pack_length;
  decimals=     dup_field->decimals;
  unireg_check= dup_field->unireg_check;
  flags=        dup_field->flags;
  vcol_info=    dup_field->vcol_info;
  invisible=    dup_field->invisible;
  check_constraint= dup_field->check_constraint;
  comment=      dup_field->comment;
  option_list=  dup_field->option_list;
  versioning=   dup_field->versioning;
}



/**
  maximum possible character length for blob.
  
  This method is used in Item_field::set_field to calculate
  max_length for Item.
  
  For example:
    CREATE TABLE t2 SELECT CONCAT(tinyblob_utf8_column) FROM t1;
  must create a "VARCHAR(255) CHARACTER SET utf8" column.
  
  @return
    length
*/

uint32 Field_blob::char_length() const
{
  return Field_blob::character_octet_length();
}


uint32 Field_blob::character_octet_length() const
{
  switch (packlength)
  {
  case 1:
    return 255;
  case 2:
    return 65535;
  case 3:
    return 16777215;
  case 4:
    return (uint32) UINT_MAX32;
  default:
    DBUG_ASSERT(0); // we should never go here
    return 0;
  }
}


/**
  Makes a clone of this object for ALTER/CREATE TABLE

  @param mem_root        MEM_ROOT where to clone the field
*/

Create_field *Create_field::clone(MEM_ROOT *mem_root) const
{
  Create_field *res= new (mem_root) Create_field(*this);
  return res;
}

/**
   Return true if default is an expression that must be saved explicitly

   This is:
     - Not basic constants
     - If field is a BLOB (Which doesn't support normal DEFAULT)
*/

bool Column_definition::has_default_expression()
{
  return (default_value &&
          (!default_value->expr->basic_const_item() ||
           (flags & BLOB_FLAG)));
}


bool Column_definition::set_compressed(const char *method)
{
  if (!method || !strcmp(method, zlib_compression_method->name))
  {
    unireg_check= Field::TMYSQL_COMPRESSED;
    compression_method_ptr= zlib_compression_method;
    return false;
  }
  my_error(ER_UNKNOWN_COMPRESSION_METHOD, MYF(0), method);
  return true;
}


bool Column_definition::check_vcol_for_key(THD *thd) const
{
  if (vcol_info && (vcol_info->flags & VCOL_NOT_STRICTLY_DETERMINISTIC))
  {
    /* use check_expression() to report an error */
    check_expression(vcol_info, field_name, VCOL_GENERATED_STORED);
    DBUG_ASSERT(thd->is_error());
    return true;
  }
  return false;
}


Send_field::Send_field(THD *thd, Item *item)
{
  item->make_send_field(thd, this);
  normalize();
}


/**
  maximum possible display length for blob.

  @return
    length
*/

uint32 Field_blob::max_display_length() const
{
  switch (packlength)
  {
  case 1:
    return 255 * mbmaxlen();
  case 2:
    return 65535 * mbmaxlen();
  case 3:
    return 16777215 * mbmaxlen();
  case 4:
    return (uint32) UINT_MAX32;
  default:
    DBUG_ASSERT(0); // we should never go here
    return 0;
  }
}


/*****************************************************************************
 Warning handling
*****************************************************************************/

/**
*  Produce warning or note about data saved into field.

  @param level            - level of message (Note/Warning/Error)
  @param code             - error code of message to be produced
  @param cut_increment    - whenever we should increase cut fields count
  @current_row            - current row number

  @note
    This function won't produce warning or notes or increase cut fields counter
    if count_cuted_fields == CHECK_FIELD_IGNORE or CHECK_FIELD_EXPRESSION
    for the current thread.

    This allows us to avoid notes in optimisation, like
    convert_constant_item().

  @retval
    1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE
  @retval
    0 otherwise
*/

bool 
Field::set_warning(Sql_condition::enum_warning_level level, uint code,
                   int cut_increment, ulong current_row) const
{
  /*
    If this field was created only for type conversion purposes it
    will have table == NULL.
  */
  THD *thd= get_thd();

  /*
    In INPLACE ALTER, server can't know which row has generated
    the warning, so the value of current row is supplied by the engine.
  */
  if (current_row)
    thd->get_stmt_da()->reset_current_row_for_warning(current_row);

  if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION)
  {
    thd->cuted_fields+= cut_increment;
    push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str,
                        current_row ? current_row
                        : thd->get_stmt_da()->current_row_for_warning());
    return 0;
  }
  return level >= Sql_condition::WARN_LEVEL_WARN;
}


/**
  Produce warning or note about datetime string data saved into field.

  @param level            level of message (Note/Warning/Error)
  @param code             error code of message to be produced
  @param str              string value which we tried to save
  @param ts_type          type of datetime value (datetime/date/time)
  @param cuted_increment  whenever we should increase cut fields count or not

  @note
    This function will always produce some warning but won't increase cut
    fields counter if count_cuted_fields ==FIELD_CHECK_IGNORE for current
    thread.

    See also bug#2336

*/

void Field::set_datetime_warning(Sql_condition::enum_warning_level level,
                                 uint code, const ErrConv *str,
                                 const char *typestr, int cuted_increment)
                                 const
{
  THD *thd= get_thd();
  if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN)
  {
    /*
      field_name.str can be NULL when field is not in the select list:
        SET SESSION SQL_MODE= 'STRICT_ALL_TABLES,NO_ZERO_DATE';
        CREATE OR REPLACE TABLE t2 SELECT 1 AS f FROM t1 GROUP BY FROM_DAYS(d);
      Can't call push_warning_truncated_value_for_field() directly here,
      as it expect a non-NULL name.
    */
    thd->push_warning_wrong_or_truncated_value(level, false, typestr,
                                               str->ptr(),
                                               table->s->db.str,
                                               table->s->table_name.str,
                                               field_name.str);
  }
  else
    set_warning(level, code, cuted_increment);
}


void Field::set_warning_truncated_wrong_value(const char *type_arg,
                                              const char *value)
{
  THD *thd= get_thd();
  const char *db_name;
  const char *table_name;
  /*
    table has in the past been 0 in case of wrong calls when processing
    statistics tables. Let's protect against that.
  */
  DBUG_ASSERT(table);

  db_name= (table && table->s->db.str) ? table->s->db.str : "";
  table_name= (table && table->s->table_name.str) ?
              table->s->table_name.str : "";

  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                      ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
                      ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
                      type_arg, value, db_name, table_name, field_name.str,
                      static_cast<ulong>(thd->get_stmt_da()->
                      current_row_for_warning()));
}


/*
  Give warning for unusable key

  Note that the caller is responsible to call it only under ther
  right note_verbosity level
*/

void Field::raise_note_cannot_use_key_part(THD *thd,
                                           uint keynr, uint part,
                                           const LEX_CSTRING &op,
                                           CHARSET_INFO *op_collation,
                                           Item *value,
                                           Data_type_compatibility reason)
                                           const
{
  StringBuffer<128> value_buffer;
  const LEX_CSTRING keyname= table->s->key_info[keynr].name;
  size_t value_length;

  value->print(&value_buffer, QT_EXPLAIN);
  value_length= Well_formed_prefix(value_buffer.charset(),
                                   value_buffer.ptr(),
                                   MY_MIN(value_buffer.length(), 64)).length();
  /*
    We must use c_ptr() here for the 'T' argument as it only works with
    zero terminated strings.
  */
  switch (reason){
  case Data_type_compatibility::INCOMPATIBLE_COLLATION:
    {
      const LEX_CSTRING colf(charset()->coll_name);
      const LEX_CSTRING colv(op_collation->coll_name);
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
                          ER_UNKNOWN_ERROR,
                          "Cannot use key %.*sQ part[%u] for lookup: "
                          "%.*sQ.%.*sQ.%.*sQ of collation %.*sQ "
                          "%.*s \"%.*sT\" of collation %.*sQ",
                          (int) keyname.length, keyname.str,
                          part,
                          (int) table->s->db.length, table->s->db.str,
                          (int) table->s->table_name.length,
                          table->s->table_name.str,
                          (int) field_name.length, field_name.str,
                          (int) colf.length, colf.str,
                          (int) op.length, op.str,
                          (int) value_length, value_buffer.c_ptr_safe(),
                          (int) colv.length, colv.str);
    }
    break;
  case Data_type_compatibility::OK:
    DBUG_ASSERT(0);
    /* fall through */
  case Data_type_compatibility::INCOMPATIBLE_DATA_TYPE:
    {
      const LEX_CSTRING dtypef(type_handler()->name().lex_cstring());
      const LEX_CSTRING dtypev(value->type_handler()->name().lex_cstring());
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
                          ER_UNKNOWN_ERROR,
                          "Cannot use key %.*sQ part[%u] for lookup: "
                          "%.*sQ.%.*sQ.%.*sQ of type %.*sQ "
                          "%.*s \"%.*sT\" of type %.*sQ",
                          (int) keyname.length, keyname.str,
                          part,
                          (int) table->s->db.length, table->s->db.str,
                          (int) table->s->table_name.length,
                          table->s->table_name.str,
                          (int) field_name.length, field_name.str,
                          (int) dtypef.length, dtypef.str,
                          (int) op.length, op.str,
                          (int) value_length, value_buffer.c_ptr_safe(),
                          (int) dtypev.length, dtypev.str);
    }
    break;
  }
}


/*
  Give warning for unusable key

  Note that the caller is responsible to call it only under ther
  right note_verbosity level
*/

void Field::raise_note_key_become_unused(THD *thd, const String &expr) const
{
  push_warning_printf(thd,
    Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
    "Cannot use key parts with %.*sQ.%.*sQ.%.*sQ "
    "in the rewritten condition: %.*sQ",
    (int) table->s->db.length, table->s->db.str,
    (int) table->s->table_name.length, table->s->table_name.str,
    (int) field_name.length, field_name.str,
    (int) expr.length(), expr.ptr());
}


/*
  @brief
  Return possible keys for a field

  @details
  Return bit map of keys over this field which can be used by the range
  optimizer. For a field of a generic table such keys are all keys that starts
  from this field. For a field of a materialized derived table/view such keys
  are all keys in which this field takes a part. This is less restrictive as
  keys for a materialized derived table/view are generated on the fly from
  present fields, thus the case when a field for the beginning of a key is
  absent is impossible.

  @return map of possible keys
*/

key_map Field::get_possible_keys()
{
  DBUG_ASSERT(table->pos_in_table_list);
  return (table->pos_in_table_list->is_materialized_derived() ?
          part_of_key : key_start);
}


bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record)
{
  MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->read_set);
  bool rc;
  if ((rc= validate_value_in_record(thd, record)))
  {
    // Get and report val_str() for the DEFAULT value
    StringBuffer<MAX_FIELD_WIDTH> tmp;
    val_str(&tmp, ptr_in_record(record));
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                        ER_INVALID_DEFAULT_VALUE_FOR_FIELD,
                        ER_THD(thd, ER_INVALID_DEFAULT_VALUE_FOR_FIELD),
                        ErrConvString(&tmp).ptr(), field_name.str);
  }
  dbug_tmp_restore_column_map(&table->read_set, old_map);
  return rc;
}


/**
  Find which reaction should be for IGNORE value.
*/

ignore_value_reaction find_ignore_reaction(THD *thd)
{
  enum_sql_command com= thd->lex->sql_command;

  // All insert-like commands
  if (com == SQLCOM_INSERT || com == SQLCOM_REPLACE ||
      com == SQLCOM_INSERT_SELECT || com == SQLCOM_REPLACE_SELECT ||
      com == SQLCOM_LOAD)
  {
    return IGNORE_MEANS_DEFAULT;
  }
  // Update commands
  if (com == SQLCOM_UPDATE || com == SQLCOM_UPDATE_MULTI)
  {
    return IGNORE_MEANS_FIELD_VALUE;
  }
  return IGNORE_MEANS_ERROR;
}


bool Field::save_in_field_default_value(bool view_error_processing)
{
  THD *thd= table->in_use;

  /*
     TODO: MDEV-19597 Refactor TABLE::vers_update_fields() via stored virtual columns
     This condition will go away as well as other conditions with vers_sys_field().
  */
  if (vers_sys_field())
  {
    if (flags & VERS_ROW_START)
      set_time();
    else
      set_max();
    return false;
  }

  if (unlikely(flags & NO_DEFAULT_VALUE_FLAG &&
               real_type() != MYSQL_TYPE_ENUM))
  {
    if (reset())
    {
      my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
                 ER_THD(thd, ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
      return true;
    }

    if (view_error_processing)
    {
      TABLE_LIST *view= table->pos_in_table_list->top_table();
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                          ER_NO_DEFAULT_FOR_VIEW_FIELD,
                          ER_THD(thd, ER_NO_DEFAULT_FOR_VIEW_FIELD),
                          view->view_db.str,
                          view->view_name.str);
    }
    else
    {
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                          ER_NO_DEFAULT_FOR_FIELD,
                          ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD),
                          field_name.str);
    }
    return true;
  }
  set_default();
  return
    !is_null() &&
    validate_value_in_record_with_warn(thd, table->record[0]) &&
    thd->is_error();
}


bool Field::save_in_field_ignore_value(bool view_error_processing)
{
  enum_sql_command com= table->in_use->lex->sql_command;
  // All insert-like commands
  if (com == SQLCOM_INSERT || com == SQLCOM_REPLACE ||
      com == SQLCOM_INSERT_SELECT || com == SQLCOM_REPLACE_SELECT ||
      com == SQLCOM_LOAD)
    return save_in_field_default_value(view_error_processing);
  return 0; // ignore
}


void Field::register_field_in_read_map()
{
  if (vcol_info)
  {
    Item *vcol_item= vcol_info->expr;
    vcol_item->walk(&Item::register_field_in_read_map,
                    0, WALK_SUBQUERY);
  }
  bitmap_set_bit(table->read_set, field_index);
}


LEX_STRING Field::val_lex_string_strmake(MEM_ROOT *mem)
{
  StringBuffer<MAX_FIELD_WIDTH> str;
  val_str(&str);
  char *to= strmake_root(mem, str.ptr(), str.length());
  return to ? LEX_STRING{to, str.length()} : LEX_STRING{NULL, 0};
}


bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to)
{
  StringBuffer<MAX_FIELD_WIDTH> str;
  bool rc= false;
  THD *thd= get_thd();
  Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);

  val_str(&str);
  if (!(to->length= str.length()))
    *to= empty_clex_str;
  else if ((rc= !(to->str= strmake_root(mem_root, str.ptr(), str.length()))))
    to->length= 0;

  return rc;
}


void Field::print_key_value(String *out, uint32 length)
{
  if (charset() == &my_charset_bin)
    print_key_value_binary(out, ptr, length);
  else
    val_str(out);
}


void Field_string::print_key_value(String *out, uint32 length)
{
  if (charset() == &my_charset_bin)
  {
    size_t len= field_charset()->lengthsp((const char*) ptr, length);
    print_key_value_binary(out, ptr, static_cast<uint32>(len));
  }
  else
  {
    THD *thd= get_thd();
    sql_mode_t sql_mode_backup= thd->variables.sql_mode;
    thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
    val_str(out,out);
    thd->variables.sql_mode= sql_mode_backup;
  }
}


void Field_varstring::print_key_value(String *out, uint32 length)
{
  if (charset() == &my_charset_bin)
    print_key_value_binary(out, get_data(), get_length());
  else
    val_str(out,out);
}


void Field_blob::print_key_value(String *out, uint32 length)
{
  if (charset() == &my_charset_bin)
  {
    uchar *blob;
    memcpy(&blob, ptr+packlength, sizeof(uchar*));
    print_key_value_binary(out, blob, get_length());
  }
  else
    val_str(out, out);
}


/*
  @brief Print value of the key part

  @param
    out                Output string
    key                value of the key
    length             Length of field in bytes,
                       excluding NULL flag and length bytes
*/


void
Field::print_key_part_value(String *out, const uchar* key, uint32 length)
{
  StringBuffer<128> tmp(system_charset_info);
  uint null_byte= 0;
  if (real_maybe_null())
  {
    /*
      Byte 0 of key is the null-byte. If set, key is NULL.
      Otherwise, print the key value starting immediately after the
      null-byte
    */
    if (*key)
    {
      out->append(NULL_clex_str);
      return;
    }
    null_byte++;  // Skip null byte
  }

  set_key_image(key + null_byte, length);
  print_key_value(&tmp, length);
  if (charset() == &my_charset_bin)
    out->append(tmp.ptr(), tmp.length(), tmp.charset());
  else
    tmp.print(out, system_charset_info);
}


void Field::print_key_value_binary(String *out, const uchar* key, uint32 length)
{
  out->append_semi_hex((const char*)key, length, charset());
}


Virtual_column_info* Virtual_column_info::clone(THD *thd)
{
  Virtual_column_info* dst= new (thd->mem_root) Virtual_column_info(*this);
  if (!dst)
    return NULL;
  if (expr)
  {
    dst->expr= expr->deep_copy_with_checks(thd);
    if (!dst->expr)
      return NULL;
  }
  if (!thd->make_lex_string(&dst->name, name.str, name.length))
    return NULL;
  return dst;
};
