mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			11760 lines
		
	
	
	
		
			342 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			11760 lines
		
	
	
	
		
			342 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
   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
 | 
						|
*/
 | 
						|
 | 
						|
#ifdef USE_PRAGMA_IMPLEMENTATION
 | 
						|
#pragma implementation				// gcc: Class implementation
 | 
						|
#endif
 | 
						|
 | 
						|
#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 canculate 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 %`s.%`s.%`s",
 | 
						|
                      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 %`s",
 | 
						|
                      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);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  @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());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @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 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 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), 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 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.
 | 
						|
 | 
						|
   @param max_length
 | 
						|
   Maximum length of the field, as given in the column definition. For
 | 
						|
   example, for <code>CHAR(1000)</code>, the <code>max_length</code>
 | 
						|
   is 1000. This information is sometimes needed to decide how to pack
 | 
						|
   the data.
 | 
						|
 | 
						|
*/
 | 
						|
uchar *
 | 
						|
Field::pack(uchar *to, const uchar *from, uint max_length)
 | 
						|
{
 | 
						|
  uint32 length= pack_length();
 | 
						|
  set_if_smaller(length, max_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= Lex_cstring_strlen(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(), nullptr, nullptr, nullptr, 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(<ime, Datetime::Options_cmp(thd)))
 | 
						|
    return 0;
 | 
						|
  if (ltime.time_type != MYSQL_TIMESTAMP_TIME)
 | 
						|
    return pack_time(<ime);
 | 
						|
  if (time_to_datetime_with_warn(thd, <ime, &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(<ime, opt))
 | 
						|
    return 0;
 | 
						|
  if (ltime.time_type == MYSQL_TIMESTAMP_TIME)
 | 
						|
    return pack_time(<ime);
 | 
						|
  // Conversion from DATETIME or DATE to TIME is needed
 | 
						|
  return Time(thd, <ime, 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->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 == field_charset() &&
 | 
						|
         new_field.length == max_display_length();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/****************************************************************************
 | 
						|
  Field_row, e.g. for ROW-type SP variables
 | 
						|
****************************************************************************/
 | 
						|
 | 
						|
Field_row::~Field_row()
 | 
						|
{
 | 
						|
  delete m_table;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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();
 | 
						|
  DBUG_RETURN(m_table->sp_set_all_fields_from_item(thd, src));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/****************************************************************************
 | 
						|
  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, ¬_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, ¬_used);
 | 
						|
  return my_charset_bin.strntoll((char*) ptr, field_length, 10, NULL, ¬_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()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(marked_for_read());
 | 
						|
  if (unsigned_flag)
 | 
						|
  {
 | 
						|
    ulonglong j;
 | 
						|
    j= uint8korr(ptr);
 | 
						|
    return j == ULONGLONG_MAX;
 | 
						|
  }
 | 
						|
  longlong j;
 | 
						|
  j= sint8korr(ptr);
 | 
						|
  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());
 | 
						|
  float j;
 | 
						|
  float4get(j,ptr);
 | 
						|
  return ((double) j);
 | 
						|
}
 | 
						|
 | 
						|
longlong Field_float::val_int(void)
 | 
						|
{
 | 
						|
  float j;
 | 
						|
  float4get(j,ptr);
 | 
						|
  return Converter_double_to_longlong(j, 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 sint4korr(pos);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Field_timestamp0::val_native(Native *to)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(marked_for_read());
 | 
						|
  my_time_t sec= (my_time_t) sint4korr(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 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().tv_sec == 0 && ts.tv().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).tv(), TIME_SECOND_PART_DIGITS);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Field_timestamp::val_int(void)
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  if (get_date(<ime, 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 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(<ime, 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 % 100;
 | 
						|
  if (temp < YY_PART_YEAR - 1)
 | 
						|
  {
 | 
						|
    *to++= '2';
 | 
						|
    *to++= '0';
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    *to++= '1';
 | 
						|
    *to++= '9';
 | 
						|
  }
 | 
						|
  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(<ime, date_mode_t(0));
 | 
						|
  return protocol->store_datetime(<ime, 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int Field_timestamp0::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_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 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 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(<ime, 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(<ime, date_mode_t(0));
 | 
						|
  return TIME_to_my_decimal(<ime, 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(<ime, date_mode_t(0));
 | 
						|
  return protocol->store_datetime(<ime, 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 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()
 | 
						|
{
 | 
						|
  DBUG_ENTER("Field_timestampf::is_max");
 | 
						|
  DBUG_ASSERT(marked_for_read());
 | 
						|
 | 
						|
  DBUG_RETURN(mi_sint4korr(ptr) == TIMESTAMP_MAX_VALUE &&
 | 
						|
              mi_sint3korr(ptr + 4) == TIME_MAX_SECOND_PART);
 | 
						|
}
 | 
						|
 | 
						|
my_time_t Field_timestampf::get_timestamp(const uchar *pos,
 | 
						|
                                          ulong *sec_part) const
 | 
						|
{
 | 
						|
  struct timeval tm;
 | 
						|
  my_timestamp_from_binary(&tm, pos, dec);
 | 
						|
  *sec_part= tm.tv_usec;
 | 
						|
  return 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(<ime, ptr_in_record(record), Datetime::Options(thd));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Field_temporal::val_decimal(my_decimal *d)
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  if (get_date(<ime, date_mode_t(0)))
 | 
						|
  {
 | 
						|
    bzero(<ime, sizeof(ltime));
 | 
						|
    ltime.time_type= type_handler()->mysql_timestamp_type();
 | 
						|
  }
 | 
						|
  return TIME_to_my_decimal(<ime, 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, <ime);
 | 
						|
  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(<ime, Datetime::Options(TIME_TIME_ONLY, get_thd()));
 | 
						|
  str->alloc(field_length + 1);
 | 
						|
  str->length(my_time_to_str(<ime, 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(<ime, date_mode_t(0));
 | 
						|
  int warn;
 | 
						|
  return Time(&warn, <ime, 0).to_native(to, decimals());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Field_time::send(Protocol *protocol)
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd()));
 | 
						|
  return protocol->store_time(<ime, 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(<ime, Time::Options(TIME_TIME_ONLY, get_thd()));
 | 
						|
  longlong val= TIME_to_ulonglong_time(<ime);
 | 
						|
  return ltime.neg ? -val : val;
 | 
						|
}
 | 
						|
 | 
						|
double Field_time_with_dec::val_real(void)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(marked_for_read());
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd()));
 | 
						|
  return TIME_to_double(<ime);
 | 
						|
}
 | 
						|
 | 
						|
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(<ime, tmp);
 | 
						|
  return pack_time(<ime);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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(<ime, ptr, date_mode_t(0));
 | 
						|
  val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
 | 
						|
  uint length= (uint) my_date_to_str(<ime,
 | 
						|
                                     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(<ime, date_mode_t(0));
 | 
						|
  return pack_time(<ime);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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(<ime, date_mode_t(0));
 | 
						|
  return protocol->store_datetime(<ime, dec);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Field_datetime_with_dec::val_real(void)
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  get_date(<ime, date_mode_t(0));
 | 
						|
  return TIME_to_double(<ime);
 | 
						|
}
 | 
						|
 | 
						|
longlong Field_datetime_with_dec::val_int(void)
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  get_date(<ime, date_mode_t(0));
 | 
						|
  return TIME_to_ulonglong_datetime(<ime);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Field_datetime_with_dec::val_str(String *str,
 | 
						|
                                         String *unused __attribute__((unused)))
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  get_date(<ime, date_mode_t(0));
 | 
						|
  str->alloc(field_length+1);
 | 
						|
  str->length(field_length);
 | 
						|
  my_datetime_to_str(<ime, (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(<ime, tmp);
 | 
						|
  return pack_time(<ime);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/****************************************************************************
 | 
						|
** 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, ©_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 == 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());
 | 
						|
}
 | 
						|
 | 
						|
uint32 Field_longstr::max_data_length() const
 | 
						|
{
 | 
						|
  return field_length + (field_length > 255 ? 2 : 1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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() != 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() != 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),
 | 
						|
                                     Field_string::charset(),
 | 
						|
                                     str.str, str.length).result();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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)
 | 
						|
{
 | 
						|
#ifdef DBUG_ASSERT_EXISTS
 | 
						|
  size_t tmp=
 | 
						|
#endif
 | 
						|
    field_charset()->strnxfrm(to, length,
 | 
						|
                              char_length() * field_charset()->strxfrm_multiply,
 | 
						|
                              ptr, field_length,
 | 
						|
                              MY_STRXFRM_PAD_WITH_SPACE |
 | 
						|
                              MY_STRXFRM_PAD_TO_MAXLEN);
 | 
						|
  DBUG_ASSERT(tmp == 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, uint max_length)
 | 
						|
{
 | 
						|
  DBUG_PRINT("debug", ("Packing field '%s'", field_name.str));
 | 
						|
  return StringPack(field_charset(), field_length).pack(to, from, max_length);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   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 uchar *data_ptr, uint length)
 | 
						|
{
 | 
						|
  return StringPack::packed_col_length(data_ptr, length);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
uint Field_string::max_packed_col_length(uint max_length)
 | 
						|
{
 | 
						|
  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, ©_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),
 | 
						|
                                     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;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef DBUG_ASSERT_EXISTS
 | 
						|
    size_t rc=
 | 
						|
#endif
 | 
						|
  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 == 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.
 | 
						|
  Here the number of length bytes are depending on the given max_length
 | 
						|
*/
 | 
						|
 | 
						|
uchar *Field_varstring::pack(uchar *to, const uchar *from, uint max_length)
 | 
						|
{
 | 
						|
  uint length= length_bytes == 1 ? (uint) *from : uint2korr(from);
 | 
						|
  set_if_smaller(max_length, field_length);
 | 
						|
  if (length > max_length)
 | 
						|
    length=max_length;
 | 
						|
 | 
						|
  /* Length always stored little-endian */
 | 
						|
  *to++= length & 0xFF;
 | 
						|
  if (max_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 uchar *data_ptr, uint length)
 | 
						|
{
 | 
						|
  if (length > 255)
 | 
						|
    return uint2korr(data_ptr)+2;
 | 
						|
  return (uint) *data_ptr + 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
uint Field_varstring::max_packed_col_length(uint max_length)
 | 
						|
{
 | 
						|
  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::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 == 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 compessed 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), 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)
 | 
						|
{
 | 
						|
  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() == 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, ©_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),
 | 
						|
                                      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);
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef DBUG_ASSERT_EXISTS
 | 
						|
    size_t rc=
 | 
						|
#endif
 | 
						|
    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 == 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, uint max_length)
 | 
						|
{
 | 
						|
  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, MY_MIN(length, max_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 uchar *data_ptr, uint length)
 | 
						|
{
 | 
						|
  if (length > 255)
 | 
						|
    return uint2korr(data_ptr)+2;
 | 
						|
  return (uint) *data_ptr + 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
uint Field_blob::max_packed_col_length(uint max_length)
 | 
						|
{
 | 
						|
  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 == 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), 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(typelib, from, length, 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 > 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 > 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(), (TYPELIB *)get_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 > typelib->count)
 | 
						|
    val_ptr->set("", 0, field_charset());
 | 
						|
  else
 | 
						|
    val_ptr->set((const char*) typelib->type_names[tmp-1],
 | 
						|
		 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= typelib->type_lengths;
 | 
						|
  for (const char **pos= 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->typelib= copy_typelib(root, 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(typelib, from, length, field_charset(),
 | 
						|
                          ¬_used, ¬_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 <= typelib->count)
 | 
						|
    max_nr= ULONGLONG_MAX;
 | 
						|
  else
 | 
						|
    max_nr= (1ULL << 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) typelib->count)
 | 
						|
  {
 | 
						|
    if (tmp & 1)
 | 
						|
    {
 | 
						|
      if (val_buffer->length())
 | 
						|
	val_buffer->append(&field_separator, 1, &my_charset_latin1);
 | 
						|
      String str(typelib->type_names[bitnr], 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= typelib->type_lengths;
 | 
						|
  for (const char **pos= 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, (TYPELIB *)get_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)->typelib;
 | 
						|
 | 
						|
  /* Definition must be strictly equal. */
 | 
						|
  if (typelib->count != values->count)
 | 
						|
    return FALSE;
 | 
						|
 | 
						|
  return compare_type_names(field_charset(), 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
 | 
						|
{
 | 
						|
  const TYPELIB *values= new_field.interval;
 | 
						|
 | 
						|
  /*
 | 
						|
    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 != 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 (typelib->count > values->count)
 | 
						|
    return false;
 | 
						|
 | 
						|
  /* Check whether there are modification before the end. */
 | 
						|
  if (! compare_type_names(field_charset(), typelib, new_field.interval))
 | 
						|
    return false;
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
uchar *Field_enum::pack(uchar *to, const uchar *from, uint max_length)
 | 
						|
{
 | 
						|
  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() == 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 - forth bit of 'c'
 | 
						|
  2           7 - firth 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, uint max_length)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(max_length > 0);
 | 
						|
  uint length;
 | 
						|
  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;
 | 
						|
  }
 | 
						|
  length= MY_MIN(bytes_in_rec, max_length - (bit_len > 0));
 | 
						|
  memcpy(to, from, length);
 | 
						|
  return to + length;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   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(!interval);
 | 
						|
  TYPELIB *tmpint;
 | 
						|
  if (!(interval= tmpint= (TYPELIB*) alloc_root(mem_root, sizeof(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:
 | 
						|
  interval= NULL;  // Avoid having both non-empty interval_list and interval
 | 
						|
  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((interval == 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(interval);
 | 
						|
    if (!(interval= copy_typelib(mem_root, interval)))
 | 
						|
      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_CSTRING *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, 0, &res);
 | 
						|
  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->stored_in_db
 | 
						|
                         ? 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, interval,
 | 
						|
                       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),
 | 
						|
  interval(NULL),
 | 
						|
  charset(field->charset()), // May be NULL ptr
 | 
						|
  srid(0),
 | 
						|
  pack_flag(0),
 | 
						|
  decimals(field->decimals()),
 | 
						|
  unireg_check(field->unireg_check)
 | 
						|
{}
 | 
						|
 | 
						|
 | 
						|
Column_definition_attributes::
 | 
						|
  Column_definition_attributes(const Type_all_attributes &attr)
 | 
						|
 :length(attr.max_length),
 | 
						|
  interval(attr.get_typelib()),
 | 
						|
  charset(attr.collation.collation),
 | 
						|
  srid(0),
 | 
						|
  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)
 | 
						|
{
 | 
						|
  srid= 0;
 | 
						|
  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());
 | 
						|
  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;
 | 
						|
  interval=     dup_field->interval;
 | 
						|
  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::set_compressed_deprecated(THD *thd, const char *method)
 | 
						|
{
 | 
						|
  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                      ER_WARN_DEPRECATED_SYNTAX,
 | 
						|
                      ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
 | 
						|
                      "<data type> <character set clause> ... COMPRESSED...",
 | 
						|
                      "'<data type> COMPRESSED... <character set clause> ...'");
 | 
						|
  return set_compressed(method);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
Column_definition::set_compressed_deprecated_column_attribute(THD *thd,
 | 
						|
                                                              const char *pos,
 | 
						|
                                                              const char *method)
 | 
						|
{
 | 
						|
  if (compression_method_ptr)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Compression method has already been set, e.g.:
 | 
						|
        a VARCHAR(10) COMPRESSED DEFAULT 10 COMPRESSED
 | 
						|
    */
 | 
						|
    thd->parse_error(ER_SYNTAX_ERROR, pos);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  enum enum_field_types sql_type= real_field_type();
 | 
						|
  /* We can't use f_is_blob here as pack_flag is not yet set */
 | 
						|
  if (sql_type == MYSQL_TYPE_VARCHAR || sql_type == MYSQL_TYPE_TINY_BLOB ||
 | 
						|
      sql_type == MYSQL_TYPE_BLOB || sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
 | 
						|
      sql_type == MYSQL_TYPE_LONG_BLOB)
 | 
						|
    return set_compressed_deprecated(thd, method);
 | 
						|
  else
 | 
						|
    my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str);
 | 
						|
  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 %`.*s part[%u] for lookup: "
 | 
						|
                          "%`.*s.%`.*s.%`.*s of collation %`.*s "
 | 
						|
                          "%.*s \"%.*T\" of collation %`.*s",
 | 
						|
                          (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 %`.*s part[%u] for lookup: "
 | 
						|
                          "%`.*s.%`.*s.%`.*s of type %`.*s "
 | 
						|
                          "%.*s \"%.*T\" of type %`.*s",
 | 
						|
                          (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 %`.*s.%`.*s.%`.*s "
 | 
						|
    "in the rewritten condition: %`.*s",
 | 
						|
    (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, 1, 0);
 | 
						|
  }
 | 
						|
  bitmap_set_bit(table->read_set, field_index);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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->build_clone(thd);
 | 
						|
    if (!dst->expr)
 | 
						|
      return NULL;
 | 
						|
  }
 | 
						|
  if (!thd->make_lex_string(&dst->name, name.str, name.length))
 | 
						|
    return NULL;
 | 
						|
  return dst;
 | 
						|
};
 |