/*
Copyright ( c ) 2000 , 2017 , Oracle and / or its affiliates .
Copyright ( c ) 2008 , 2022 , MariaDB
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1335 USA
*/
/**
@ file
@ brief
This file implements classes defined in field . h
*/
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "rpl_rli.h" // Pull in Relay_log_info
#include "slave.h" // Pull in rpl_master_has_bug()
#include "strfunc.h" // find_type2, find_set
#include "tztime.h" // struct Time_zone
#include "filesort.h" // change_double_for_sort
#include "log_event.h" // class Table_map_log_event
#include <m_ctype.h>
// Maximum allowed exponent value for converting string to decimal
#define MAX_EXPONENT 1024
/*****************************************************************************
Instantiate templates and static variables
*****************************************************************************/
static const char *zero_timestamp="0000-00-00 00:00:00.000000" ;
LEX_CSTRING temp_lex_str= {STRING_WITH_LEN("temp" )};
uchar Field_null::null[1 ]={1 };
const char field_separator=',' ;
#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE FLOATING_POINT_BUFFER
#define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
((ulong) ((1 LL << MY_MIN(arg, 4 ) * 8 ) - 1 ))
// Column marked for read or the field set to read out of record[0]
bool Field::marked_for_read() const
{
return !table ||
(!table->read_set ||
bitmap_is_set(table->read_set, field_index) ||
(!(ptr >= table->record[0 ] &&
ptr < table->record[0 ] + table->s->reclength)));
}
/*
The name of this function is a bit misleading as in 10 . 4 we don ' t
have to test anymore if the field is computed . Instead we mark
changed fields with DBUG_FIX_WRITE_SET ( ) in table . cc
*/
bool Field::marked_for_write_or_computed() const
{
return (!table ||
(!table->write_set ||
bitmap_is_set(table->write_set, field_index) ||
(!(ptr >= table->record[0 ] &&
ptr < table->record[0 ] + table->s->reclength))));
}
#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "" )
/*
Rules for merging different types of fields in UNION
NOTE : to avoid 256 * 256 table , gap in table types numeration is skipped
following # defines describe that gap and how to calculate number of fields
and index of field in this array .
*/
const int FIELDTYPE_TEAR_FROM= (MYSQL_TYPE_BIT + 1 );
const int FIELDTYPE_TEAR_TO= (MYSQL_TYPE_NEWDECIMAL - 1 );
const int FIELDTYPE_LAST= 254 ;
const int FIELDTYPE_NUM= FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST -
FIELDTYPE_TEAR_TO);
static inline int merge_type2index(enum_field_types merge_type)
{
DBUG_ASSERT(merge_type < FIELDTYPE_TEAR_FROM ||
merge_type > FIELDTYPE_TEAR_TO);
DBUG_ASSERT(merge_type <= FIELDTYPE_LAST);
if (merge_type < FIELDTYPE_TEAR_FROM)
return merge_type;
return FIELDTYPE_TEAR_FROM + (merge_type - FIELDTYPE_TEAR_TO) - 1 ;
}
/**
Implements data type merge rules for the built - in traditional data types .
Used for operations such as :
- UNION
- CASE and its abbreviations COALESCE , IF , IFNULL
- LEAST / GREATEST
Given Fields A and B of real_types a and b , we find the result type of
COALESCE ( A , B ) by querying :
field_types_merge_rules [ field_type_to_index ( a ) ] [ field_type_to_index ( b ) ] .
*/
static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
{
/* MYSQL_TYPE_DECIMAL -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_TINY -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_TINY, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_SHORT -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_SHORT,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_SHORT, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_SHORT,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_LONG -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_LONG,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_LONG, MYSQL_TYPE_LONG,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_LONG, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONG,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_LONG,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_FLOAT -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_FLOAT,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_FLOAT, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_FLOAT,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_FLOAT,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_DOUBLE -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_NULL -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONGLONG,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_TIME,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_BIT,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_ENUM,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_TIMESTAMP -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_TIMESTAMP,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_LONGLONG -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_LONGLONG,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONGLONG,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_LONGLONG, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONGLONG,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_LONGLONG,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_INT24 -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_INT24,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_INT24, MYSQL_TYPE_LONG,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_INT24, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_INT24,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_DATE -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_TIME -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIME,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_DATETIME -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_YEAR -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_YEAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_YEAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_NEWDATE -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_VARCHAR -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_VARCHAR
},
/* MYSQL_TYPE_BIT -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_BIT, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_BIT,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_NEWDECIMAL -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_NEWDECIMAL,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_ENUM -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_ENUM, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_SET -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_SET, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_TINY_BLOB -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_STRING
MYSQL_TYPE_TINY_BLOB
},
/* MYSQL_TYPE_MEDIUM_BLOB -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
//MYSQL_TYPE_STRING
MYSQL_TYPE_MEDIUM_BLOB
},
/* MYSQL_TYPE_LONG_BLOB -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_STRING
MYSQL_TYPE_LONG_BLOB
},
/* MYSQL_TYPE_BLOB -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_BLOB,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
//MYSQL_TYPE_STRING
MYSQL_TYPE_BLOB
},
/* MYSQL_TYPE_VAR_STRING -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_VARCHAR
},
/* MYSQL_TYPE_STRING -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_BIT <16>-<245>
MYSQL_TYPE_STRING,
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
MYSQL_TYPE_STRING, MYSQL_TYPE_TINY_BLOB,
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_STRING
MYSQL_TYPE_STRING
}
};
const Type_handler *
Type_handler::aggregate_for_result_traditional(const Type_handler *a,
const Type_handler *b)
{
if (a == b)
{
/*
If two traditional handlers are equal , quickly return " a " .
Some handlers ( e . g . Type_handler_bool ) pretend to be traditional ,
but in fact they are not traditional in full extent , they are
only sub - types for now ( and don ' t have a corresponding Field_xxx yet ) .
Here we preserve such handlers during aggregation .
As a result , COALESCE ( true , true ) preserves the " boolean " data type .
Need to do this conversion for deprecated data types ,
similar to what field_type_merge_rules [ ] [ ] does .
*/
switch (a->field_type()) {
case MYSQL_TYPE_DECIMAL: return &type_handler_newdecimal;
case MYSQL_TYPE_DATE: return &type_handler_newdate;
case MYSQL_TYPE_VAR_STRING: return &type_handler_varchar;
default : break ;
}
return a;
}
/* Keep a hex hybrid through a typeless NULL (as the rules keep BIT); the
merge below has no hybrid type and would demote it to VARCHAR. */
if (a == &type_handler_hex_hybrid && b == &type_handler_null)
return a;
if (b == &type_handler_hex_hybrid && a == &type_handler_null)
return b;
enum_field_types ta= a->traditional_merge_field_type();
enum_field_types tb= b->traditional_merge_field_type();
enum_field_types res= field_types_merge_rules[merge_type2index(ta)]
[merge_type2index(tb)];
return Type_handler::get_handler_by_real_type(res);
}
bool Field::check_assignability_from(const Type_handler *from,
bool ignore) const
{
/*
Using type_handler_for_item_field ( ) here to get the data type handler
on both sides . This is needed to make sure aggregation for Field
works the same way with how Item_field aggregates for UNION or CASE ,
so these statements :
SELECT a FROM t1 UNION SELECT b FROM t1 ; // Item_field vs Item_field
UPDATE t1 SET a = b ; // Field vs Item_field
either both return " Illegal parameter data types " or both pass
the data type compatibility test .
For MariaDB standard data types , using type_handler_for_item_field ( )
turns ENUM / SET into just CHAR .
*/
Type_handler_hybrid_field_type th(type_handler()->
type_handler_for_item_field());
if (th.aggregate_for_result(from->type_handler_for_item_field()))
{
bool error= (!ignore && get_thd()->is_strict_mode()) ||
(type_handler()->is_scalar_type() != from->is_scalar_type());
/*
Display fully qualified column name for table columns .
Display non - qualified names for other things ,
e . g . SP variables , SP return values , SP and CURSOR parameters .
*/
if (table->s->db.str && table->s->table_name.str)
my_printf_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION,
"Cannot cast '%s' as '%s' in assignment of %sQ.%sQ.%sQ" ,
MYF(error ? 0 : ME_WARNING),
from->name().ptr(), type_handler()->name().ptr(),
table->s->db.str, table->s->table_name.str,
field_name.str);
else
my_printf_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION,
"Cannot cast '%s' as '%s' in assignment of %sQ" ,
MYF(error ? 0 : ME_WARNING),
from->name().ptr(), type_handler()->name().ptr(),
field_name.str);
return error;
}
return false ;
}
/*
Test if the given string contains important data :
not spaces for character string ,
or any data for binary string .
SYNOPSIS
test_if_important_data ( )
cs Character set
str String to test
strend String end
RETURN
FALSE - If string does not have important data
TRUE - If string has some important data
*/
static bool
test_if_important_data(CHARSET_INFO *cs, const char *str, const char *strend)
{
if (cs != &my_charset_bin)
str+= cs->scan(str, strend, MY_SEQ_SPACES);
return (str < strend);
}
/**
Function to compare two unsigned integers for their relative order .
Used below . In an anonymous namespace to not clash with definitions
in other files .
*/
CPP_UNNAMED_NS_START
int compare(unsigned int a, unsigned int b)
{
if (a < b)
return -1 ;
if (b < a)
return 1 ;
return 0 ;
}
CPP_UNNAMED_NS_END
/*****************************************************************************
Static help functions
*****************************************************************************/
/*
@ brief
Create a fixed size sort key part
@ param buff buffer where values are written
@ param length fixed size of the sort column
*/
void Field::make_sort_key_part(uchar *buff,uint length)
{
if (maybe_null())
{
if (is_null())
{
bzero(buff, length + 1 );
return ;
}
*buff++= 1 ;
}
sort_string(buff, length);
}
Item_field *Field::make_item_field_spvar(THD *thd, const Spvar_definition &def)
{
return new (thd->mem_root) Item_field(thd, this );
}
/*
@ brief
Create a packed sort key part
@ param buff buffer where values are written
@ param sort_field sort column structure
@ retval
length of the bytes written , does not include the NULL bytes
*/
uint
Field::make_packed_sort_key_part(uchar *buff,
const SORT_FIELD_ATTR *sort_field)
{
if (maybe_null())
{
if (is_null())
{
*buff++= 0 ;
return 0 ; // For NULL values don't write any data
}
*buff++=1 ;
}
sort_string(buff, sort_field->original_length);
return sort_field->original_length;
}
uint
Field_longstr::make_packed_sort_key_part(uchar *buff,
const SORT_FIELD_ATTR *sort_field)
{
if (maybe_null())
{
if (is_null())
{
*buff++= 0 ;
return 0 ; // For NULL values don't write any data
}
*buff++=1 ;
}
uchar *end= pack_sort_string(buff, sort_field);
return (uint) (end-buff);
}
uchar*
Field_longstr::pack_sort_string(uchar *to, const SORT_FIELD_ATTR *sort_field)
{
StringBuffer<LONGLONG_BUFFER_SIZE+1 > buf;
val_str(&buf, &buf);
return to + sort_field->pack_sort_string(to, &buf, field_charset());
}
bool Field_longstr::val_bool()
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
StringBuffer<STRING_BUFFER_USUAL_SIZE> str;
val_str(&str, &str);
double res= Converter_strntod_with_warn(thd, Warn_filter(thd),
"BOOLEAN" ,
charset(),
str.ptr(), str.length()).result();
return res != 0 .0 ;
}
/**
@ brief
Determine the relative position of the field value in a numeric interval
@ details
The function returns a double number between 0 . 0 and 1 . 0 as the relative
position of the value of the this field in the numeric interval of [ min , max ] .
If the value is not in the interval the function returns 0 . 0 when
the value is less than min , and , 1 . 0 when the value is greater than max .
@ param min value of the left end of the interval
@ param max value of the right end of the interval
@ return
relative position of the field value in the numeric interval [ min , max ]
*/
double pos_in_interval_for_double(double midp_val, double min_val,
double max_val)
{
double n, d;
n= midp_val - min_val;
if (n < 0 )
return 0 .0 ;
d= max_val - min_val;
if (d <= 0 )
return 1 .0 ;
return MY_MIN(n/d, 1 .0 );
}
double Field::pos_in_interval_val_real(Field *min, Field *max)
{
return pos_in_interval_for_double(val_real(), min->val_real(),
max->val_real());
}
static
inline ulonglong char_prefix_to_ulonglong(uchar *src)
{
uint sz= sizeof (ulonglong);
for (uint i= 0 ; i < sz/2 ; i++)
{
uchar tmp= src[i];
src[i]= src[sz-1 -i];
src[sz-1 -i]= tmp;
}
return uint8korr(src);
}
/*
Compute res = a - b , without losing precision and taking care that these are
unsigned numbers .
*/
static inline double safe_substract(ulonglong a, ulonglong b)
{
return (a > b)? double (a - b) : -double (b - a);
}
/**
@ brief
Determine the relative position of the field value in a string interval
@ details
The function returns a double number between 0 . 0 and 1 . 0 as the relative
position of the value of the this field in the string interval of [ min , max ] .
If the value is not in the interval the function returns 0 . 0 when
the value is less than min , and , 1 . 0 when the value is greater than max .
@ note
To calculate the relative position of the string value v in the interval
[ min , max ] the function first converts the beginning of these three
strings v , min , max into the strings that are used for byte comparison .
For each string not more sizeof ( ulonglong ) first bytes are taken
from the result of conversion . Then these bytes are interpreted as the
big - endian representation of an ulonglong integer . The values of these
integer numbers obtained for the strings v , min , max are used to calculate
the position of v in [ min , max ] in the same way is it ' s done for numeric
fields ( see Field : : pos_in_interval_val_real ) .
@ todo
Improve the procedure for the case when min and max have the same
beginning
@ param min value of the left end of the interval
@ param max value of the right end of the interval
@ return
relative position of the field value in the string interval [ min , max ]
*/
double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
{
return pos_in_interval_for_string(charset(),
ptr + data_offset, data_length(),
min->ptr + data_offset, min->data_length(),
max->ptr + data_offset, max->data_length()
);
}
double pos_in_interval_for_string(CHARSET_INFO *cset,
const uchar *midp_val, uint32 midp_len,
const uchar *min_val, uint32 min_len,
const uchar *max_val, uint32 max_len)
{
uchar mp_prefix[sizeof (ulonglong)];
uchar minp_prefix[sizeof (ulonglong)];
uchar maxp_prefix[sizeof (ulonglong)];
ulonglong mp, minp, maxp;
cset->strnxfrm(mp_prefix, sizeof (mp), midp_val, midp_len);
cset->strnxfrm(minp_prefix, sizeof (minp), min_val, min_len);
cset->strnxfrm(maxp_prefix, sizeof (maxp), max_val, max_len);
mp= char_prefix_to_ulonglong(mp_prefix);
minp= char_prefix_to_ulonglong(minp_prefix);
maxp= char_prefix_to_ulonglong(maxp_prefix);
double n, d;
n= safe_substract(mp, minp);
if (n < 0 )
return 0 .0 ;
d= safe_substract(maxp, minp);
if (d <= 0 )
return 1 .0 ;
return MY_MIN(n/d, 1 .0 );
}
bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const
{
DBUG_ASSERT(cmp_type() != STRING_RESULT); // For STRING_RESULT see Field_str
/*
We use result_type ( ) rather than cmp_type ( ) in the below condition ,
because it covers a special case that string literals guarantee uniqueness
for temporal columns , so the query :
WHERE temporal_column = ' string '
cannot return multiple distinct temporal values .
TODO : perhaps we could allow INT / DECIMAL / DOUBLE types for temporal items .
*/
return result_type() == item->result_type();
}
/**
Check whether a field item can be substituted for an equal item
@ details
The function checks whether a substitution of a field item for
an equal item is valid .
@ param arg * arg ! = NULL < - > the field is in the context
where substitution for an equal item is valid
@ note
The following statement is not always true :
@ n
x = y = > F ( x ) = F ( x / y ) .
@ n
This means substitution of an item for an equal item not always
yields an equavalent condition . Here ' s an example :
@ code
' a ' = ' a '
( LENGTH ( ' a ' ) = 1 ) ! = ( LENGTH ( ' a ' ) = 2 )
@ endcode
Such a substitution is surely valid if either the substituted
field is not of a STRING type or if it is an argument of
a comparison predicate .
@ retval
TRUE substitution is valid
@ retval
FALSE otherwise
*/
bool Field::can_be_substituted_to_equal_item(const Context &ctx,
const Item_equal *item_equal)
{
DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT);
DBUG_ASSERT(cmp_type() != STRING_RESULT);
switch (ctx.subst_constraint()) {
case ANY_SUBST:
/*
Disable const propagation for items used in different comparison contexts .
This must be done because , for example , Item_hex_string - > val_int ( ) is not
the same as ( Item_hex_string - > val_str ( ) in BINARY column ) - > val_int ( ) .
We cannot simply disable the replacement in a particular context (
e . g . < bin_col > = < int_col > AND < bin_col > = < hex_string > ) since
Items don ' t know the context they are in and there are functions like
IF ( < hex_string > , ' yes ' , ' no ' ) .
*/
return ctx.compare_type_handler() == item_equal->compare_type_handler();
case IDENTITY_SUBST:
return true ;
}
return false ;
}
bool Field::cmp_is_done_using_type_handler_of_this(const Item_bool_func *cond,
const Item *item) const
{
/*
We could eventually take comparison_type_handler ( ) from cond ,
instead of calculating it again . But only some descendants of
Item_bool_func has this method . So this needs some hierarchy changes .
Another option is to pass " class Context " to this method .
*/
Type_handler_hybrid_field_type cmp(type_handler_for_comparison());
return !cmp.aggregate_for_comparison(item->type_handler_for_comparison()) &&
cmp.type_handler() == type_handler_for_comparison();
}
/*
This handles all numeric and BIT data types .
*/
Data_type_compatibility
Field::can_optimize_keypart_ref(const Item_bool_func *cond,
const Item *item) const
{
DBUG_ASSERT(cmp_type() != STRING_RESULT);
DBUG_ASSERT(cmp_type() != TIME_RESULT);
return item->cmp_type() != TIME_RESULT ?
Data_type_compatibility::OK :
Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
}
/*
This handles all numeric and BIT data types .
*/
Data_type_compatibility
Field::can_optimize_group_min_max(const Item_bool_func *cond,
const Item *const_item) const
{
DBUG_ASSERT(cmp_type() != STRING_RESULT);
DBUG_ASSERT(cmp_type() != TIME_RESULT);
return const_item->cmp_type() != TIME_RESULT ?
Data_type_compatibility::OK :
Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
}
/*
This covers all numeric types , BIT
*/
Data_type_compatibility
Field::can_optimize_range(const Item_bool_func *cond,
const Item *item,
bool is_eq_func) const
{
DBUG_ASSERT(cmp_type() != TIME_RESULT); // Handled in Field_temporal
DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants
return item->cmp_type() != TIME_RESULT ?
Data_type_compatibility::OK :
Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
}
int Field::store_hex_hybrid(const char *str, size_t length)
{
DBUG_ASSERT(result_type() != STRING_RESULT);
ulonglong nr;
if (length > 8 )
{
nr= flags & UNSIGNED_FLAG ? ULONGLONG_MAX : LONGLONG_MAX;
goto warn;
}
nr= (ulonglong) longlong_from_hex_hybrid(str, length);
if ((length == 8 ) && cmp_type()== INT_RESULT &&
!(flags & UNSIGNED_FLAG) && (nr > LONGLONG_MAX))
{
nr= LONGLONG_MAX;
goto warn;
}
return store((longlong) nr, true ); // Assume hex numbers are unsigned
warn:
if (!store((longlong) nr, true ))
set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1 );
return 1 ;
}
/**
If a field does not have a corresponding data , it ' s behavior can vary :
- In case of the fixed file format
it ' s set to the default value for the data type ,
such as 0 for numbers or ' ' for strings .
- In case of a non - fixed format
it ' s set to NULL for nullable fields , and
it ' s set to the default value for the data type for NOT NULL fields .
This seems to be by design .
*/
bool Field::load_data_set_no_data(THD *thd, bool fixed_format)
{
reset(); // Do not use the DEFAULT value
if (fixed_format)
{
set_notnull();
/*
We ' re loading a fixed format file , e . g . :
LOAD DATA INFILE ' t1 . txt ' INTO TABLE t1 FIELDS TERMINATED BY ' ' ;
Suppose the file ended unexpectedly and no data was provided for an
auto - increment column in the current row .
Historically , if sql_mode = NO_AUTO_VALUE_ON_ZERO , then the column value
is set to 0 in such case ( the next auto_increment value is not used ) .
This behaviour was introduced by the fix for " bug # 12053 " in mysql - 4 . 1 .
Note , loading a delimited file works differently :
" no data " is not converted to 0 on NO_AUTO_VALUE_ON_ZERO :
it ' s considered as equal to setting the column to NULL ,
which is then replaced to the next auto_increment value .
This difference seems to be intentional .
*/
if (this == table->next_number_field)
table->auto_increment_field_not_null= true ;
}
set_has_explicit_value(); // Do not auto-update this field
return false ;
}
bool Field::load_data_set_null(THD *thd)
{
reset();
set_null();
if (!maybe_null())
{
if (this != table->next_number_field)
set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_NULL_TO_NOTNULL, 1 );
}
set_has_explicit_value(); // Do not auto-update this field
return false ;
}
void Field::load_data_set_value(const char *pos, uint length,
CHARSET_INFO *cs)
{
/*
Mark field as not null , we should do this for each row because of
restore_record . . .
*/
set_notnull();
if (this == table->next_number_field)
table->auto_increment_field_not_null= true ;
store(pos, length, cs);
set_has_explicit_value(); // Do not auto-update this field
}
bool Field::sp_prepare_and_store_item(THD *thd, Item **value)
{
DBUG_ENTER("Field::sp_prepare_and_store_item" );
DBUG_ASSERT(value);
Item *expr_item;
if (!(expr_item= thd->sp_fix_func_item_for_assignment(this , value)))
goto error;
if (expr_item->check_is_evaluable_expression_or_error())
goto error;
/* Save the value in the field. Convert the value if needed. */
expr_item->save_in_field(this , 0 );
if (likely(!thd->is_error()))
DBUG_RETURN(false );
error:
/*
In case of error during evaluation , leave the result field set to NULL .
Sic : we can ' t do it in the beginning of the function because the
result field might be needed for its own re - evaluation , e . g . case of
set x = x + 1 ;
*/
set_null();
DBUG_ASSERT(thd->is_error());
DBUG_RETURN(true );
}
void Field::error_generated_column_function_is_not_allowed(THD *thd,
bool error) const
{
StringBuffer<64 > tmp;
vcol_info->expr->print(&tmp, (enum_query_type)
(QT_TO_SYSTEM_CHARSET |
QT_ITEM_IDENT_SKIP_DB_NAMES |
QT_ITEM_IDENT_SKIP_TABLE_NAMES));
my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED,
MYF(error ? 0 : ME_WARNING),
tmp.c_ptr_safe(), vcol_info->get_vcol_type_name(),
const_cast <const char *>(field_name.str));
}
/*
Check if an indexed or a persistent virtual column depends on sql_mode flags
that it cannot handle .
See sql_mode . h for details .
*/
bool Field::check_vcol_sql_mode_dependency(THD *thd, vcol_init_mode mode) const
{
DBUG_ASSERT(vcol_info);
if ((flags & PART_KEY_FLAG) != 0 || stored_in_db())
{
Sql_mode_dependency valdep= vcol_info->expr->value_depends_on_sql_mode();
sql_mode_t cnvdep= conversion_depends_on_sql_mode(thd, vcol_info->expr);
Sql_mode_dependency dep=
(valdep | Sql_mode_dependency(0 , cnvdep)) &
Sql_mode_dependency(~0 , ~can_handle_sql_mode_dependency_on_store());
if (dep)
{
bool error= (mode & VCOL_INIT_DEPENDENCY_FAILURE_IS_ERROR) != 0 ;
error_generated_column_function_is_not_allowed(thd, error);
dep.push_dependency_warnings(thd);
return error;
}
}
return false ;
}
bool Field::make_empty_rec_store_default_value(THD *thd, Item *item)
{
DBUG_ASSERT(!(flags & BLOB_FLAG));
int res= item->save_in_field(this , true );
return res != 0 && res != 3 ;
}
/**
Numeric fields base class constructor .
*/
Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
const LEX_CSTRING *field_name_arg,
decimal_digits_t dec_arg, bool zero_arg, bool unsigned_arg)
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg),
dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg)
{
if (zerofill)
flags|=ZEROFILL_FLAG;
if (unsigned_flag)
flags|=UNSIGNED_FLAG;
}
void Field_num::prepend_zeros(String *value) const
{
int diff;
if ((diff= (int ) (field_length - value->length())) > 0 )
{
const bool error= value->realloc(field_length);
if (likely(!error))
{
bmove_upp((uchar*) value->ptr()+field_length,
(uchar*) value->ptr()+value->length(),
value->length());
bfill((uchar*) value->ptr(),diff,'0' );
value->length(field_length);
}
}
}
sql_mode_t Field_num::can_handle_sql_mode_dependency_on_store() const
{
return MODE_PAD_CHAR_TO_FULL_LENGTH;
}
Item *Field_num::get_equal_zerofill_const_item(THD *thd, const Context &ctx,
Item *const_item)
{
switch (ctx.subst_constraint()) {
case IDENTITY_SUBST:
return NULL; // Not safe to propagate if not in comparison. See MDEV-8369.
case ANY_SUBST:
break ;
}
DBUG_ASSERT(const_item->const_item());
DBUG_ASSERT(ctx.compare_type_handler()->cmp_type() != STRING_RESULT);
return const_item;
}
/**
Construct warning parameters using thd - > no_errors
to determine whether to generate or suppress warnings .
We can get here in a query like this :
SELECT COUNT ( @ @ basedir ) ;
from Item_func_get_system_var : : update_null_value ( ) .
*/
Value_source::Warn_filter::Warn_filter(const THD *thd)
:m_want_warning_edom(!thd->no_errors),
m_want_note_truncated_spaces(!thd->no_errors)
{ }
/**
Check string - to - number conversion and produce a warning if
- could not convert any digits ( EDOM - alike error )
- found garbage at the end of the string
- found trailing spaces ( a note )
See also Field_num : : check_edom_and_truncation ( ) for a similar function .
@ param thd - the thread
@ param filter - which warnings / notes are allowed
@ param type - name of the data type ( e . g . " INTEGER " , " DECIMAL " , " DOUBLE " )
@ param cs - character set of the original string
@ param str - the original string
@ param end - the end of the string
Unlike Field_num : : check_edom_and_truncation ( ) , this function does not
distinguish between EDOM and truncation and reports the same warning for
both cases . Perhaps we should eventually print different warnings , to make
the explicit CAST work closer to the implicit cast in Field_xxx : : store ( ) .
*/
void
Value_source::Converter_string_to_number::check_edom_and_truncation(THD *thd,
Warn_filter filter,
const char *type,
CHARSET_INFO *cs,
const char *str,
size_t length) const
{
DBUG_ASSERT(str <= m_end_of_num);
DBUG_ASSERT(m_end_of_num <= str + length);
if (m_edom || (m_end_of_num < str + length &&
!check_if_only_end_space(cs, m_end_of_num, str + length)))
{
// EDOM or important trailing data truncation
if (filter.want_warning_edom())
{
/*
We can use err . ptr ( ) here as ErrConvString is guaranteed to put an
end \ 0 here .
*/
THD *wthd= thd ? thd : current_thd;
push_warning_printf(wthd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type,
ErrConvString(str, length, cs).ptr());
}
}
else if (m_end_of_num < str + length)
{
// Unimportant trailing data (spaces) truncation
if (filter.want_note_truncated_spaces())
{
THD *wthd= thd ? thd : current_thd;
push_warning_printf(wthd, Sql_condition::WARN_LEVEL_NOTE,
ER_TRUNCATED_WRONG_VALUE,
ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type,
ErrConvString(str, length, cs).ptr());
}
}
}
/**
Check a string - to - number conversion routine result and generate warnings
in case when it :
- could not convert any digits
- found garbage at the end of the string .
@ param type Data type name ( e . g . " decimal " , " integer " , " double " )
@ param edom Indicates that the string - to - number routine returned
an error code equivalent to EDOM ( value out of domain ) ,
i . e . the string fully consisted of garbage and the
conversion routine could not get any digits from it .
@ param str The original string
@ param length Length of ' str '
@ param cs Character set
@ param end Pointer to char after last used digit
@ note
This is called after one has called one of the following functions :
- strntoull10rnd ( )
- strntod ( )
- str2my_decimal ( )
@ retval
0 OK
@ retval
1 error : could not scan any digits ( EDOM ) ,
e . g . empty string , or garbage .
@ retval
2 error : scanned some digits ,
but then found garbage at the end of the string .
*/
int Field_num::check_edom_and_important_data_truncation(const char *type,
bool edom,
CHARSET_INFO *cs,
const char *str, size_t length,
const char *end)
{
/* Test if we get an empty string or garbage */
if (edom)
{
ErrConvString err(str, length, cs);
set_warning_truncated_wrong_value(type, err.ptr());
return 1 ;
}
/* Test if we have garbage at the end of the given string. */
if (test_if_important_data(cs, end, str + length))
{
set_warning(WARN_DATA_TRUNCATED, 1 );
return 2 ;
}
return 0 ;
}
int Field_num::check_edom_and_truncation(const char *type, bool edom,
CHARSET_INFO *cs,
const char *str, size_t length,
const char *end)
{
int rc= check_edom_and_important_data_truncation(type, edom,
cs, str, length, end);
if (!rc && end < str + length)
set_note(WARN_DATA_TRUNCATED, 1 );
return rc;
}
/*
Convert a string to an integer then check bounds .
SYNOPSIS
Field_num : : get_int
cs Character set
from String to convert
len Length of the string
rnd OUT longlong value
unsigned_max max unsigned value
signed_min min signed value
signed_max max signed value
DESCRIPTION
The function calls strntoull10rnd ( ) to get an integer value then
check bounds and errors returned . In case of any error a warning
is raised .
RETURN
0 ok
1 error
*/
bool Field_num::get_int(CHARSET_INFO *cs, const char *from, size_t len,
longlong *rnd, ulonglong unsigned_max,
longlong signed_min, longlong signed_max)
{
char *end;
int error;
*rnd= (longlong) cs->strntoull10rnd(from, len,
unsigned_flag, &end,
&error);
if (unsigned_flag)
{
if ((((ulonglong) *rnd > unsigned_max) &&
(*rnd= (longlong) unsigned_max)) ||
error == MY_ERRNO_ERANGE)
{
goto out_of_range;
}
}
else
{
if (*rnd < signed_min)
{
*rnd= signed_min;
goto out_of_range;
}
else if (*rnd > signed_max)
{
*rnd= signed_max;
goto out_of_range;
}
}
if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
check_int(cs, from, len, end, error))
return 1 ;
return error && get_thd()->count_cuted_fields == CHECK_FIELD_EXPRESSION;
out_of_range:
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
return 1 ;
}
double Field_real::get_double(const char *str, size_t length, CHARSET_INFO *cs,
int *error)
{
char *end;
double nr= cs->strntod((char *) str, length, &end, error);
if (unlikely(*error))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
*error= 1 ;
}
else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
check_edom_and_truncation("double" , str == end,
cs, str, length, end))
*error= 1 ;
return nr;
}
/**
Process decimal library return codes and issue warnings for overflow and
truncation .
@ param op_result decimal library return code ( E_DEC_ * see include / decimal . h )
@ retval
1 there was overflow
@ retval
0 no error or some other errors except overflow
*/
int Field::warn_if_overflow(int op_result)
{
if (op_result == E_DEC_OVERFLOW)
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
return 1 ;
}
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), ptr_old(nullptr),
null_ptr(null_ptr_arg), null_ptr_old(nullptr), table(0 ), orig_table(0 ),
table_name(0 ), field_name(*field_name_arg), option_list(0 ),
option_struct(0 ), key_start(0 ), part_of_key(0 ),
part_of_key_not_clustered(0 ), vcol_direct_part_of_key(0 ), part_of_sortkey(0 ),
unireg_check(unireg_check_arg), invisible(VISIBLE), field_length(length_arg),
null_bit(null_bit_arg), is_created_from_null_item(FALSE ),
read_stats(NULL), collected_stats(0 ), vcol_info(0 ), check_constraint(0 ),
default_value(0 )
{
flags=null_ptr ? 0 : NOT_NULL_FLAG;
comment.str= (char *) "" ;
comment.length=0 ;
field_index= 0 ;
cond_selectivity= 1 .0 ;
next_equal_field= NULL;
}
void Field::hash_not_null(Hasher *hasher)
{
DBUG_ASSERT(marked_for_read());
DBUG_ASSERT(!is_null());
hasher->add(sort_charset(), ptr, pack_length());
}
size_t
Field::do_last_null_byte() const
{
DBUG_ASSERT(null_ptr == NULL || null_ptr >= table->record[0 ]);
if (null_ptr)
return (size_t) (null_ptr - table->record[0 ]) + 1 ;
return LAST_NULL_BYTE_UNDEF;
}
void Field::copy_from_tmp(int row_offset)
{
memcpy(ptr,ptr+row_offset,pack_length());
if (null_ptr)
{
*null_ptr= (uchar) ((null_ptr[0 ] & (uchar) ~(uint) null_bit) |
(null_ptr[row_offset] & (uchar) null_bit));
}
}
bool Field::send(Protocol *protocol)
{
StringBuffer<MAX_FIELD_WIDTH> tmp(charset());
val_str(&tmp);
return protocol->store(tmp.ptr(), tmp.length(), tmp.charset());
}
bool Field_num::send_numeric_zerofill_str(Protocol_text *protocol,
protocol_send_type_t send_type)
{
DBUG_ASSERT(marked_for_read());
StringBuffer<MAX_FIELD_WIDTH> tmp(&my_charset_latin1);
val_str(&tmp);
return protocol->store_numeric_zerofill_str(tmp.ptr(),
tmp.length(),
send_type);
}
/**
Check to see if field size is compatible with destination .
This method is used in row - based replication to verify that the
slave ' s field size is less than or equal to the master ' s field
size . The encoded field metadata ( from the master or source ) is
decoded and compared to the size of this field ( the slave or
destination ) .
@ note
The comparison is made so that if the source data ( from the master )
is less than the target data ( on the slave ) , - 1 is returned in @ c
< code > * order_var < / code > . This implies that a conversion is
necessary , but that it is lossy and can result in truncation of the
value .
If the source data is strictly greater than the target data , 1 is
returned in < code > * order_var < / code > . This implies that the source
type can is contained in the target type and that a conversion is
necessary but is non - lossy .
If no conversion is required to fit the source type in the target
type , 0 is returned in < code > * order_var < / code > .
@ param field_metadata Encoded size in field metadata
@ param mflags Flags from the table map event for the table .
@ param order_var Pointer to variable where the order
between the source field and this field
will be returned .
@ return @ c true if this field ' s size is compatible with the
master ' s field size , @ c false otherwise .
*/
bool Field::compatible_field_size(uint field_metadata,
const Relay_log_info *rli_arg __attribute__((unused)),
uint16 mflags __attribute__((unused)),
int *order_var) const
{
uint const source_size= pack_length_from_metadata(field_metadata);
uint const destination_size= row_pack_length();
DBUG_PRINT("debug" , ("real_type: %d, source_size: %u, destination_size: %u" ,
real_type(), source_size, destination_size));
*order_var = compare(source_size, destination_size);
return true ;
}
int Field::store(const char *to, size_t length, CHARSET_INFO *cs,
enum_check_fields check_level)
{
Check_level_instant_set tmp_level(get_thd(), check_level);
return store(to, length, cs);
}
int Field::store_text(const char *to, size_t length, CHARSET_INFO *cs,
enum_check_fields check_level)
{
Check_level_instant_set tmp_level(get_thd(), check_level);
return store_text(to, length, cs);
}
int Field::store_timestamp_dec(const my_timeval &ts, uint dec)
{
return store_time_dec(Datetime(get_thd(), ts).get_mysql_time(), dec);
}
int Field::store_to_statistical_minmax_field(Field *field, String *val)
{
val_str(val);
size_t length= Well_formed_prefix(val->charset(), val->ptr(),
MY_MIN(val->length(), field->field_length)).length();
return field->store(val->ptr(), length, &my_charset_bin);
}
int Field::store_from_statistical_minmax_field(Field *stat_field, String *str,
MEM_ROOT *mem)
{
stat_field->val_str(str);
return store_text(str->ptr(), str->length(), &my_charset_bin);
}
/**
Set a field value from another field
@ param from Field to take the value from
@ param no_conversions How to deal with NULL value
@ details
The method takes the value of the field ' from ' and :
- if this value is not null , it saves in ' this '
- otherwise ' this ' is set to null ( possibly with null - conversion )
@ retval 0 OK
@ retval ! = 0 Error or warning or note happened
*/
int Field::store_field_maybe_null(Field *from, bool no_conversions)
{
int res;
DBUG_ENTER("Field::store_field_maybe_null" );
if (from->is_null())
DBUG_RETURN(set_field_to_null_with_conversions(this , no_conversions));
set_notnull();
/*
If we ' re setting the same field as the one we ' re reading from there ' s
nothing to do . This can happen in ' SET x = x ' type of scenarios .
*/
if (this == from)
DBUG_RETURN(0 );
res= field_conv(this , from);
DBUG_RETURN(res);
}
/*
Same as above , but store the string in the statistics mem_root to make it
easy to free everything by just freeing the mem_root .
*/
int Field_blob::store_from_statistical_minmax_field(Field *stat_field,
String *str,
MEM_ROOT *mem)
{
String *tmp= stat_field->val_str(str);
uchar *ptr;
if (!(ptr= (uchar*) memdup_root(mem, tmp->ptr(), tmp->length())))
{
set_ptr((uint32) 0 , NULL);
return 1 ;
}
set_ptr(tmp->length(), ptr);
return 0 ;
}
/**
Pack the field into a format suitable for storage and transfer .
To implement packing functionality , only the virtual function
should be overridden . The other functions are just convenience
functions and hence should not be overridden .
@ note The default method for packing fields just copy the raw bytes
of the record into the destination , but never more than
< code > max_length < / code > characters .
@ param to
Pointer to memory area where representation of field should be put .
@ param from
Pointer to memory area where record representation of field is
stored .
*/
uchar *
Field::pack (uchar *to, const uchar *from) const
{
uint32 length= pack_length();
memcpy(to, from, length);
return to+length;
}
/**
Unpack a field from row data .
This method is used to unpack a field from a master whose size of
the field is less than that of the slave .
The < code > param_data < / code > parameter is a two - byte integer ( stored
in the least significant 16 bits of the unsigned integer ) usually
consisting of two parts : the real type in the most significant byte
and a original pack length in the least significant byte .
The exact layout of the < code > param_data < / code > field is given by
the < code > Table_map_log_event : : save_field_metadata ( ) < / code > .
This is the default method for unpacking a field . It just copies
the memory block in byte order ( of original pack length bytes or
length of field , whichever is smaller ) .
@ param to Destination of the data
@ param from Source of the data
@ param param_data Real type and original pack length of the field
data
@ return New pointer into memory based on from + length of the data
@ return 0 if wrong data
*/
const uchar *
Field::unpack(uchar* to, const uchar *from, const uchar *from_end,
uint param_data)
{
uint length=pack_length(), len;
int from_type= 0 ;
/*
If from length is > 255 , it has encoded data in the upper bits . Need
to mask it out .
*/
if (param_data > 255 )
{
from_type= (param_data & 0 xff00) >> 8 U; // real_type.
param_data= param_data & 0 x00ff; // length.
}
if ((param_data == 0 ) ||
(length == param_data) ||
(from_type != real_type()))
{
if (from + length > from_end)
return 0 ; // Error in data
memcpy(to, from, length);
return from+length;
}
len= (param_data && (param_data < length)) ? param_data : length;
if (from + len > from_end)
return 0 ; // Error in data
memcpy(to, from, len);
return from+len;
}
void Field_num::add_zerofill_and_unsigned(String &res) const
{
if (unsigned_flag)
res.append(STRING_WITH_LEN(" unsigned" ));
if (zerofill)
res.append(STRING_WITH_LEN(" zerofill" ));
}
void Field::make_send_field(Send_field *field)
{
if (orig_table && orig_table->s->db.str && *orig_table->s->db.str)
{
field->db_name= orig_table->s->db;
if (orig_table->pos_in_table_list &&
orig_table->pos_in_table_list->schema_table)
field->org_table_name= orig_table->pos_in_table_list->
schema_table->table_name;
else
field->org_table_name= orig_table->s->table_name;
}
else
field->org_table_name= field->db_name= empty_clex_str;
if (orig_table && orig_table->alias.ptr())
{
orig_table->alias.get_value(&field->table_name);
field->org_col_name= field_name;
}
else
{
field->table_name= empty_clex_str;
field->org_col_name= empty_clex_str;
}
field->col_name= field_name;
field->length=field_length;
field->set_handler(type_handler());
field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
field->decimals= 0 ;
}
/**
Conversion from decimal to longlong with checking overflow and
setting correct value ( min / max ) in case of overflow .
@ param val value which have to be converted
@ param unsigned_flag type of integer in which we convert val
@ param err variable to pass error code
@ return
value converted from val
*/
longlong Field::convert_decimal2longlong(const my_decimal *val,
bool unsigned_flag, int *err)
{
longlong i;
int res= E_DEC_OVERFLOW;
if (unsigned_flag)
{
if (val->sign())
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
i= 0 ;
}
else if (warn_if_overflow(res= my_decimal2int(E_DEC_FATAL_ERROR &
~E_DEC_OVERFLOW,
val, TRUE , &i)))
{
i= ~(longlong) 0 ;
}
}
else if (warn_if_overflow(res= my_decimal2int(E_DEC_FATAL_ERROR &
~E_DEC_OVERFLOW,
val, FALSE , &i)))
{
i= (val->sign() ? LONGLONG_MIN : LONGLONG_MAX);
}
*err= res != 0 ;
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 - 01 x ' .
*/
if (!field_charset()->propagate(0 , 0 ) ||
item->cmp_type() != STRING_RESULT)
return false ;
/*
Can ' t guarantee uniqueness when comparing to
an item of a different collation .
Example :
SELECT * FROM t1
WHERE latin1_bin_column = _ latin1 ' A ' COLLATE latin1_swedish_ci
return non - unique values ' a ' and ' A ' .
*/
DTCollation tmp(dtcollation());
return !tmp.aggregate(item->collation) && tmp.collation == field_charset();
}
bool Field_str::can_be_substituted_to_equal_item(const Context &ctx,
const Item_equal *item_equal)
{
DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() == STRING_RESULT);
switch (ctx.subst_constraint()) {
case ANY_SUBST:
return ctx.compare_type_handler() == item_equal->compare_type_handler() &&
(ctx.compare_type_handler()->cmp_type() != STRING_RESULT ||
ctx.compare_collation() == item_equal->compare_collation());
case IDENTITY_SUBST:
return ((charset()->state & MY_CS_BINSORT) &&
(charset()->state & MY_CS_NOPAD));
}
return false ;
}
void Field_str::change_charset(const DTCollation &new_cs)
{
if (!has_charset())
return ;
field_length= (field_length * new_cs.collation->mbmaxlen) /
m_collation.collation->mbmaxlen;
m_collation= new_cs;
}
void Field_num::make_send_field(Send_field *field)
{
Field::make_send_field(field);
field->decimals= dec;
}
/**
Decimal representation of Field_str .
@ param d value for storing
@ note
Field_str is the base class for fields like Field_enum ,
Field_date and some similar . Some dates use fraction and also
string value should be converted to floating point value according
our rules , so we use double to store value of decimal in string .
@ todo
use decimal2string ?
@ retval
0 OK
@ retval
! = 0 error
*/
int Field_str::store_decimal(const my_decimal *d)
{
DBUG_ASSERT(marked_for_write_or_computed());
double val;
/* TODO: use decimal2string? */
int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR &
~E_DEC_OVERFLOW, d, &val));
return err | store(val);
}
my_decimal *Field_str::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(marked_for_read());
longlong nr= val_int();
int2my_decimal(E_DEC_FATAL_ERROR, nr, 0 , decimal_value);
return decimal_value;
}
uint Field::fill_cache_field(CACHE_FIELD *copy)
{
uint store_length;
copy->str= ptr;
copy->length= pack_length_in_rec();
copy->field= this ;
if (flags & BLOB_FLAG)
{
copy->type= CACHE_BLOB;
copy->length-= portable_sizeof_char_ptr;
return copy->length;
}
else if (!zero_pack() &&
(type() == MYSQL_TYPE_STRING && copy->length >= 4 &&
copy->length < 256 ))
{
copy->type= CACHE_STRIPPED; /* Remove end space */
store_length= 2 ;
}
else if (type() == MYSQL_TYPE_VARCHAR)
{
copy->type= pack_length()-row_pack_length() == 1 ? CACHE_VARSTR1:
CACHE_VARSTR2;
store_length= 0 ;
}
else
{
copy->type= 0 ;
store_length= 0 ;
}
return copy->length + store_length;
}
bool Field::get_date(MYSQL_TIME *to, date_mode_t mode)
{
StringBuffer<40 > tmp;
Temporal::Warn_push warn(get_thd(), table ? table->s->db.str : nullptr,
table ? table->s->table_name.str : nullptr,
field_name.str, to, mode);
Temporal_hybrid *t= new (to) Temporal_hybrid(get_thd(), &warn,
val_str(&tmp), mode);
return !t->is_valid_temporal();
}
longlong Field::val_datetime_packed(THD *thd)
{
MYSQL_TIME ltime, tmp;
if (get_date(<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->vcol_direct_part_of_key.init(0 );
tmp->part_of_sortkey.init(0 );
tmp->read_stats= NULL;
/*
TODO : it is not clear why this method needs to reset unireg_check .
Try not to reset it , or explain why it needs to be reset .
*/
tmp->unireg_check= Field::NONE;
tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
tmp->reset_fields();
tmp->invisible= VISIBLE;
return tmp;
}
Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uint32 length,
uchar *new_null_ptr, uint new_null_bit)
{
Field *tmp;
if ((tmp= make_new_field(root, new_table, table == new_table)))
{
tmp->ptr= new_ptr;
tmp->null_ptr= new_null_ptr;
tmp->null_bit= new_null_bit;
}
return tmp;
}
/**
Create field for temporary table from given field .
@ param thd Thread handler
@ param table Temporary table
@ param maybe_null_arg If the result field should be NULL - able ,
even if the original field is NOT NULL , e . g . for :
- OUTER JOIN fields
- WITH ROLLUP fields
- arguments of aggregate functions , e . g . SUM ( column1 )
@ retval NULL , on error
@ retval pointer to the new field created , on success .
*/
Field *Field::create_tmp_field(MEM_ROOT *mem_root, TABLE *new_table,
bool maybe_null_arg)
{
Field *new_field;
if ((new_field= make_new_field(mem_root, new_table, new_table == table)))
{
new_field->init_for_tmp_table(this , new_table);
new_field->flags|= flags & NO_DEFAULT_VALUE_FLAG;
if (maybe_null_arg)
new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join
}
return new_field;
}
/* This is used to generate a field in TABLE from TABLE_SHARE */
Field *Field::clone(MEM_ROOT *root, TABLE *new_table)
{
Field *tmp;
if ((tmp= (Field*) memdup_root(root,(char *) this ,size_of())))
{
tmp->init(new_table);
tmp->move_field_offset((my_ptrdiff_t) (new_table->record[0 ] -
new_table->s->default_values));
}
return tmp;
}
Field *Field::clone(MEM_ROOT *root, TABLE *new_table, my_ptrdiff_t diff)
{
Field *tmp;
if ((tmp= (Field*) memdup_root(root,(char *) this ,size_of())))
{
if (new_table)
tmp->init(new_table);
tmp->move_field_offset(diff);
}
return tmp;
}
int Field::set_default()
{
if (default_value)
{
Query_arena backup_arena;
/*
TODO : this may impose memory leak until table flush .
See comment in
TABLE : : update_virtual_fields ( handler * , enum_vcol_update_mode ) .
*/
table->in_use->set_n_backup_active_arena(table->expr_arena, &backup_arena);
int rc= default_value->expr->save_in_field(this , 0 );
table->in_use->restore_active_arena(table->expr_arena, &backup_arena);
return rc;
}
/* Copy constant value stored in s->default_values */
my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values -
table->record[0 ]);
memcpy(ptr, ptr + l_offset, pack_length_in_rec());
if (maybe_null_in_table())
*null_ptr= ((*null_ptr & (uchar) ~null_bit) |
(null_ptr[l_offset] & null_bit));
return 0 ;
}
/****************************************************************************
Field_null , a field that always return NULL
****************************************************************************/
void Field_null::sql_type(String &res) const
{
res.set_ascii(STRING_WITH_LEN("null" ));
}
bool Field_null::is_equal(const Column_definition &new_field) const
{
DBUG_ASSERT(!compression_method());
return (new_field.type_handler() == type_handler() &&
new_field.charset->eq_collation(field_charset()) &&
new_field.length == max_display_length());
}
/****************************************************************************
Field_row , e . g . for ROW - type SP variables
****************************************************************************/
Field_row::~Field_row()
{
delete m_table;
}
/*
Create a virtual table with Fields for a ROW members
from an explicit structure .
*/
bool Field_row::row_create_fields(THD *thd, List<Spvar_definition> *list)
{
DBUG_ASSERT(!m_table);
return !(m_table= create_virtual_tmp_table(thd, *list));
}
/*
Create a virtual table with Fields for a ROW members
from an explicit or an anchored definition .
It covers the following scenarios :
- ROW ( a INT , b VARCHAR ( 32 ) ) - explicit
- ROW TYPE OF test . t2 - anchored table data type
Note , in case of a ` ROW TYPE OF cursor ` definition the
virtual table is not created ( it ' s done later ) .
*/
bool Field_row::row_create_fields(THD *thd, const Spvar_definition &def)
{
if (def.is_table_rowtype_ref()) // e.g. ROW TYPE OF test.t1
{
Row_definition_list defs;
return def.table_rowtype_ref()->resolve_table_rowtype_ref(thd, defs) ||
row_create_fields(thd, &defs);
}
if (def.is_cursor_rowtype_ref()) // e.g. ROW TYPE OF cursor
{
/*
The structure is resolved later ,
in sp_instr_cursor_copy_struct : : exec_core ( )
*/
return false ;
}
if (def.is_row()) // e.g. ROW(a INT, b VARCHAR(32))
return row_create_fields(thd, def.row_field_definitions());
DBUG_ASSERT(0 ); // Unknown ROW declaration style
return true ;
}
bool Field_row::sp_prepare_and_store_item(THD *thd, Item **value)
{
DBUG_ENTER("Field_row::sp_prepare_and_store_item" );
if (value[0 ]->type() == Item::NULL_ITEM)
{
/*
We ' re in a auto - generated sp_inst_set , to assign
the explicit default NULL value to a ROW variable .
*/
m_table->set_all_fields_to_null();
DBUG_RETURN(false );
}
/**
- In case if we ' re assigning a ROW variable from another ROW variable ,
value [ 0 ] points to Item_splocal . sp_fix_func_item ( ) will return the
fixed underlying Item_field pointing to Field_row .
- In case if we ' re assigning from a ROW ( ) value , src and value [ 0 ] will
point to the same Item_row .
- In case if we ' re assigning from a subselect , src and value [ 0 ] also
point to the same Item_singlerow_subselect .
*/
Item *src;
if (!(src= thd->sp_fix_func_item(value)) ||
src->cmp_type() != ROW_RESULT ||
src->cols() != m_table->s->fields)
{
my_error(ER_OPERAND_COLUMNS, MYF(0 ), m_table->s->fields);
m_table->set_all_fields_to_null();
DBUG_RETURN(true );
}
src->bring_value();
if (m_table->sp_set_all_fields_from_item(thd, src))
{
set_null();
DBUG_RETURN(true );
}
set_notnull();
DBUG_RETURN(false );
}
uint Field_row::cols() const
{
// The table with ROW members must be already instantiated
DBUG_ASSERT(m_table);
return m_table->s->fields;
}
void Field_row::sql_type(String &res) const
{
res.set_ascii(STRING_WITH_LEN("row" ));
}
void Field_row::sql_type_for_sp_returns(String &res) const
{
res.set_ascii(STRING_WITH_LEN("row(" ));
for (uint i= 0 ; i < m_table->s->fields; i++)
{
if (i > 0 )
res.append(',' );
Field *field= m_table->field[i];
res.append(field->field_name);
res.append(' ' );
StringBuffer<64 > col;
field->sql_type_for_sp_returns(col);
res.append(col.to_lex_cstring());
}
res.append(')' );
}
void Field_row::expr_event_handler(THD *thd, expr_event_t event)
{
if (m_table)
m_table->expr_event_handler(thd, event);
}
/****************************************************************************
Functions for the Field_decimal class
This is an number stored as a pre - space ( or pre - zero ) string
****************************************************************************/
int
Field_decimal::reset(void )
{
Field_decimal::store(STRING_WITH_LEN("0" ),&my_charset_bin);
return 0 ;
}
void Field_decimal::overflow(bool negative)
{
uint len=field_length;
uchar *to=ptr, filler= '9' ;
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
if (negative)
{
if (!unsigned_flag)
{
/* Put - sign as a first digit so we'll have -999..999 or 999..999 */
*to++ = '-' ;
len--;
}
else
{
filler= '0' ; // Fill up with 0
if (!zerofill)
{
/*
Handle unsigned integer without zerofill , in which case
the number should be of format ' 0 ' or ' 0 . 000 '
*/
uint whole_part=field_length- (dec ? dec+2 : 1 );
// Fill with spaces up to the first digit
bfill(to, whole_part, ' ' );
to+= whole_part;
len-= whole_part;
// The main code will also handle the 0 before the decimal point
}
}
}
bfill(to, len, filler);
if (dec)
ptr[field_length-dec-1 ]='.' ;
return ;
}
int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs)
{
DBUG_ASSERT(marked_for_write_or_computed());
char buff[STRING_BUFFER_USUAL_SIZE];
String tmp(buff,sizeof (buff), &my_charset_bin);
const uchar *from= (uchar*) from_arg;
/* Convert character set if the old one is multi uchar */
if (cs->mbmaxlen > 1 )
{
uint dummy_errors;
tmp.copy((char *) from, len, cs, &my_charset_bin, &dummy_errors);
from= (uchar*) tmp.ptr();
len= tmp.length();
}
const uchar *end= from+len;
/* The pointer where the field value starts (i.e., "where to write") */
uchar *to= ptr;
uint tmp_dec, tmp_uint;
/*
The sign of the number : will be 0 ( means positive but sign not
specified ) , ' + ' or ' - '
*/
uchar sign_char=0 ;
/* The pointers where prezeros start and stop */
const uchar *pre_zeros_from, *pre_zeros_end;
/* The pointers where digits at the left of '.' start and stop */
const uchar *int_digits_from, *int_digits_end;
/* The pointers where digits at the right of '.' start and stop */
const uchar *frac_digits_from, *frac_digits_end;
/* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */
char expo_sign_char=0 ;
uint exponent=0 ; // value of the exponent
/*
Pointers used when digits move from the left of the ' . ' to the
right of the ' . ' ( explained below )
*/
const uchar *UNINIT_VAR(int_digits_tail_from);
/* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */
uint UNINIT_VAR(int_digits_added_zeros);
/*
Pointer used when digits move from the right of the ' . ' to the left
of the ' . '
*/
const uchar *UNINIT_VAR(frac_digits_head_end);
/* Number of 0 that need to be added at the right of the '.' (for 1E-3) */
uint UNINIT_VAR(frac_digits_added_zeros);
uchar *pos,*tmp_left_pos,*tmp_right_pos;
/* Pointers that are used as limits (begin and end of the field buffer) */
uchar *left_wall,*right_wall;
uchar tmp_char;
/*
To remember if get_thd ( ) - > cuted_fields has already been incremented ,
to do that only once
*/
bool is_cuted_fields_incr=0 ;
/*
There are three steps in this function :
- parse the input string
- modify the position of digits around the decimal dot ' . '
according to the exponent value ( if specified )
- write the formatted number
*/
if ((tmp_dec=dec))
tmp_dec++;
/* skip pre-space */
while (from != end && my_isspace(&my_charset_bin,*from))
from++;
if (from == end)
{
set_warning(WARN_DATA_TRUNCATED, 1 );
is_cuted_fields_incr=1 ;
}
else if (*from == '+' || *from == '-' ) // Found some sign ?
{
sign_char= *from++;
/*
We allow " + " for unsigned decimal unless defined different
Both options allowed as one may wish not to have " + " for unsigned numbers
because of data processing issues
*/
if (unsigned_flag)
{
if (sign_char=='-' )
{
Field_decimal::overflow(1 );
return 1 ;
}
/*
Defining this will not store " + " for unsigned decimal type even if
it is passed in numeric string . This will make some tests to fail
*/
#ifdef DONT_ALLOW_UNSIGNED_PLUS
else
sign_char=0 ;
#endif
}
}
pre_zeros_from= from;
for (; from!=end && *from == '0' ; from++) ; // Read prezeros
pre_zeros_end=int_digits_from=from;
/* Read non zero digits at the left of '.'*/
for (; from != end && my_isdigit(&my_charset_bin, *from) ; from++) ;
int_digits_end=from;
if (from!=end && *from == '.' ) // Some '.' ?
from++;
frac_digits_from= from;
/* Read digits at the right of '.' */
for (;from!=end && my_isdigit(&my_charset_bin, *from); from++) ;
frac_digits_end=from;
// Some exponentiation symbol ?
if (from != end && (*from == 'e' || *from == 'E' ))
{
from++;
if (from != end && (*from == '+' || *from == '-' )) // Some exponent sign ?
expo_sign_char= *from++;
else
expo_sign_char= '+' ;
/*
Read digits of the exponent and compute its value . We must care about
' exponent ' overflow , because as unsigned arithmetic is " modulo " , big
exponents will become small ( e . g . 1 e4294967296 will become 1 e0 , 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 :
- 1 E + 3 : needs 3 more zeros at the left of ' . ' ( int_digits_added_zeros = 3 )
- 1 E - 3 : ' 1 ' moves at the right of ' . ' , and 2 more zeros are needed
between ' . ' and ' 1 '
- 1234 . 5 E - 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 . 5 E - 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 . 5 E3 : 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 < 4 G and
( int_digits_end - int_digits_from ) < = max_allowed_packet < = 2 G and
( frac_digits_from - int_digits_tail_from ) < = max_allowed_packet < = 2 G
*/
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 . 003 e3 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 0 E1000000000 will not , while 1 E - 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 < 0 L); /* 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 ' 1 e - 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 >> 8 U) & 0 x00ff;
uint const source_decimal= field_metadata & 0 x00ff;
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 >> 8 U) & 0 x00ff;
uint const source_decimal= field_metadata & 0 x00ff;
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 & 0 xff00) >> 8 U;
uint from_decimal= param_data & 0 x00ff;
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 < 0 L && !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 ) (1 L << 24 ))
{
uint32 tmp=(uint32) (1 L << 24 )-1 L;
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 ) (1 L << 24 ))
{
long tmp= (long ) (1 L << 24 )-1 L;
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 ) (1 L << 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 val)
{
DBUG_ASSERT(marked_for_write_or_computed());
int error= 0 ;
int32 res;
double nr= rint(val);
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 );
else if (nr != val)
error= 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 >= (1 LL << 32 ))
{
res=(int32) (uint32) ~0 L;
error= 1 ;
}
else
res=(int32) (uint32) nr;
}
else
{
if (nr < 0 && unsigned_val)
nr= ((longlong) INT_MAX32) + 1 ; // Generate overflow
if (nr < (longlong) INT_MIN32)
{
res=(int32) INT_MIN32;
error= 1 ;
}
else if (nr > (longlong) INT_MAX32)
{
res=(int32) INT_MAX32;
error= 1 ;
}
else
res=(int32) nr;
}
if (unlikely(error))
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
int4store(ptr,res);
return error;
}
double Field_long::val_real(void )
{
DBUG_ASSERT(marked_for_read());
int32 j;
j=sint4korr(ptr);
return unsigned_flag ? (double ) (uint32) j : (double ) j;
}
longlong Field_long::val_int(void )
{
DBUG_ASSERT(marked_for_read());
int32 j;
/* See the comment in Field_long::store(long long) */
DBUG_ASSERT(!table || table->in_use == current_thd);
j=sint4korr(ptr);
return unsigned_flag ? (longlong) (uint32) j : (longlong) j;
}
String *Field_long::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
DBUG_ASSERT(marked_for_read());
long nr= unsigned_flag ? (long ) uint4korr(ptr) : sint4korr(ptr);
return val_str_from_long(val_buffer, 12 , unsigned_flag ? 10 : -10 , nr);
}
bool Field_long::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
Protocol_text *txt;
if (unlikely(zerofill) && (txt= dynamic_cast <Protocol_text*>(protocol)))
return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG);
return protocol->store_long(Field_long::val_int());
}
int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
int32 a,b;
a=sint4korr(a_ptr);
b=sint4korr(b_ptr);
if (unsigned_flag)
return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0 ;
return (a < b) ? -1 : (a > b) ? 1 : 0 ;
}
void Field_long::sort_string(uchar *to,uint length __attribute__((unused)))
{
if (unsigned_flag)
to[0 ] = ptr[3 ];
else
to[0 ] = (char ) (ptr[3 ] ^ 128 ); /* Revers signbit */
to[1 ] = ptr[2 ];
to[2 ] = ptr[1 ];
to[3 ] = ptr[0 ];
}
/****************************************************************************
Field type longlong int ( 8 bytes )
****************************************************************************/
int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs)
{
DBUG_ASSERT(marked_for_write_or_computed());
int error= 0 ;
char *end;
ulonglong tmp;
tmp= cs->strntoull10rnd(from, len, unsigned_flag, &end, &error);
if (unlikely(error == MY_ERRNO_ERANGE))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
error= 1 ;
}
else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
check_int(cs, from, len, end, error))
error= 1 ;
else
error= 0 ;
int8store(ptr,tmp);
return error;
}
int Field_longlong::store(double nr)
{
DBUG_ASSERT(marked_for_write_or_computed());
Converter_double_to_longlong conv(nr, unsigned_flag);
if (unlikely(conv.error()))
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
int8store(ptr, conv.result());
return conv.error();
}
int Field_longlong::store(longlong nr, bool unsigned_val)
{
DBUG_ASSERT(marked_for_write_or_computed());
int error= 0 ;
if (unlikely(nr < 0 )) // Only possible error
{
/*
if field is unsigned and value is signed ( < 0 ) or
if field is signed and value is unsigned we have an overflow
*/
if (unsigned_flag != unsigned_val)
{
nr= unsigned_flag ? (ulonglong) 0 : (ulonglong) LONGLONG_MAX;
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
error= 1 ;
}
}
int8store(ptr,nr);
return error;
}
double Field_longlong::val_real(void )
{
DBUG_ASSERT(marked_for_read());
longlong j;
j=sint8korr(ptr);
/* The following is open coded to avoid a bug in gcc 3.3 */
if (unsigned_flag)
{
ulonglong tmp= (ulonglong) j;
return ulonglong2double(tmp);
}
return (double ) j;
}
longlong Field_longlong::val_int(void )
{
DBUG_ASSERT(marked_for_read());
longlong j;
j=sint8korr(ptr);
return j;
}
String *Field_longlong::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
CHARSET_INFO *cs= &my_charset_numeric;
uint length;
uint mlength=MY_MAX(field_length+1 ,22 *cs->mbmaxlen);
val_buffer->alloc(mlength);
char *to=(char *) val_buffer->ptr();
longlong j;
j=sint8korr(ptr);
length=(uint) (cs->longlong10_to_str)(to, mlength,
unsigned_flag ? 10 : -10 , j);
val_buffer->length(length);
if (zerofill)
prepend_zeros(val_buffer);
val_buffer->set_charset(cs);
return val_buffer;
}
bool Field_longlong::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
Protocol_text *txt;
if (unlikely(zerofill) && (txt= dynamic_cast <Protocol_text*>(protocol)))
return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONGLONG);
return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag);
}
int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
longlong a,b;
a=sint8korr(a_ptr);
b=sint8korr(b_ptr);
if (unsigned_flag)
return ((ulonglong) a < (ulonglong) b) ? -1 :
((ulonglong) a > (ulonglong) b) ? 1 : 0 ;
return (a < b) ? -1 : (a > b) ? 1 : 0 ;
}
void Field_longlong::sort_string(uchar *to,uint length __attribute__((unused)))
{
if (unsigned_flag)
to[0 ] = ptr[7 ];
else
to[0 ] = (char ) (ptr[7 ] ^ 128 ); /* Revers signbit */
to[1 ] = ptr[6 ];
to[2 ] = ptr[5 ];
to[3 ] = ptr[4 ];
to[4 ] = ptr[3 ];
to[5 ] = ptr[2 ];
to[6 ] = ptr[1 ];
to[7 ] = ptr[0 ];
}
void Field_longlong::set_max()
{
DBUG_ASSERT(marked_for_write_or_computed());
set_notnull();
int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX);
}
bool Field_longlong::is_max(const uchar *ptr_arg) const
{
DBUG_ASSERT(marked_for_read());
if (unsigned_flag)
{
ulonglong j;
j= uint8korr(ptr_arg);
return j == ULONGLONG_MAX;
}
longlong j;
j= sint8korr(ptr_arg);
return j == LONGLONG_MAX;
}
/*
Floating - point numbers
*/
/****************************************************************************
single precision float
****************************************************************************/
int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs)
{
int error;
Field_float::store(get_double(from, len, cs, &error));
return error;
}
int Field_float::store(double nr)
{
if (nr == 0 .0 )
{
nr= 0 .0 ; // correct negative zero
}
DBUG_ASSERT(marked_for_write_or_computed());
int error= truncate_double(&nr, field_length,
not_fixed ? NOT_FIXED_DEC : dec,
unsigned_flag, FLT_MAX);
if (unlikely(error))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
if (error < 0 ) // Wrong double value
{
error= 1 ;
set_null();
}
}
float j= (float )nr;
float4store(ptr,j);
return error;
}
int Field_float::store(longlong nr, bool unsigned_val)
{
return Field_float::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
(double ) nr);
}
double Field_float::val_real(void )
{
DBUG_ASSERT(marked_for_read());
return ((double ) get_float(ptr));
}
longlong Field_float::val_int(void )
{
return Converter_double_to_longlong(get_float(ptr), false ).result();
}
String *Field_float::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
DBUG_ASSERT(marked_for_read());
DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
if (Float (ptr).to_string(val_buffer, dec))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0 ));
return val_buffer;
}
if (zerofill)
prepend_zeros(val_buffer);
return val_buffer;
}
int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
float a,b;
float4get(a,a_ptr);
float4get(b,b_ptr);
return (a < b) ? -1 : (a > b) ? 1 : 0 ;
}
#define FLT_EXP_DIG (sizeof (float )*8 -FLT_MANT_DIG)
void Field_float::sort_string(uchar *to,uint length __attribute__((unused)))
{
float nr;
float4get(nr,ptr);
uchar *tmp= to;
if (nr == (float ) 0 .0 )
{ /* Change to zero string */
tmp[0 ]=(uchar) 128 ;
bzero((char *) tmp+1 ,sizeof (nr)-1 );
}
else
{
#ifdef WORDS_BIGENDIAN
memcpy(tmp, &nr, sizeof (nr));
#else
tmp[0 ]= ptr[3 ]; tmp[1 ]=ptr[2 ]; tmp[2 ]= ptr[1 ]; tmp[3 ]=ptr[0 ];
#endif
if (tmp[0 ] & 128 ) /* Negative */
{ /* make complement */
uint i;
for (i=0 ; i < sizeof (nr); i++)
tmp[i]= (uchar) (tmp[i] ^ (uchar) 255 );
}
else
{
ushort exp_part=(((ushort) tmp[0 ] << 8 ) | (ushort) tmp[1 ] |
(ushort) 32768 );
exp_part+= (ushort) 1 << (16 -1 -FLT_EXP_DIG);
tmp[0 ]= (uchar) (exp_part >> 8 );
tmp[1 ]= (uchar) exp_part;
}
}
}
bool Field_float::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
Protocol_text *txt;
if (unlikely(zerofill) && (txt= dynamic_cast <Protocol_text*>(protocol)))
return send_numeric_zerofill_str(txt, PROTOCOL_SEND_FLOAT);
return protocol->store_float((float ) Field_float::val_real(), dec);
}
/**
Save the field metadata for float fields .
Saves the pack length in the first byte .
@ param metadata_ptr First byte of field metadata
@ returns number of bytes written to metadata_ptr
*/
Binlog_type_info Field_float::binlog_type_info() const
{
DBUG_ASSERT(Field_float::type() == binlog_type());
return Binlog_type_info(Field_float::type(), pack_length(), 1 ,
binlog_signedness());
}
/****************************************************************************
double precision floating point numbers
****************************************************************************/
int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs)
{
int error;
Field_double::store(get_double(from, len, cs, &error));
return error;
}
int Field_double::store(double nr)
{
if (nr == 0 .0 )
{
nr= 0 .0 ; // correct negative zero
}
DBUG_ASSERT(marked_for_write_or_computed());
int error= truncate_double(&nr, field_length,
not_fixed ? NOT_FIXED_DEC : dec,
unsigned_flag, DBL_MAX);
if (unlikely(error))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
if (error < 0 ) // Wrong double value
{
error= 1 ;
set_null();
}
}
float8store(ptr,nr);
return error;
}
int Field_double::store(longlong nr, bool unsigned_val)
{
return Field_double::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
(double ) nr);
}
/*
If a field has fixed length , truncate the double argument pointed to by ' nr '
appropriately .
Also ensure that the argument is within [ - max_value ; max_value ] range .
return
0 ok
- 1 Illegal double value
1 Value was truncated
*/
int truncate_double(double *nr, uint field_length, decimal_digits_t dec,
bool unsigned_flag, double max_value)
{
int error= 0 ;
double res= *nr;
if (std::isnan(res))
{
*nr= 0 ;
return -1 ;
}
else if (unsigned_flag && res < 0 )
{
*nr= 0 ;
return 1 ;
}
if (dec < FLOATING_POINT_DECIMALS)
{
uint order= field_length - dec;
uint step= array_elements(log_10) - 1 ;
double max_value_by_dec= 1 .0 ;
for (; order > step; order-= step)
max_value_by_dec*= log_10[step];
max_value_by_dec*= log_10[order];
max_value_by_dec-= 1 .0 / log_10[dec];
set_if_smaller(max_value, max_value_by_dec);
/* Check for infinity so we don't get NaN in calculations */
if (!std::isinf(res))
{
double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec];
res= floor(res) + tmp;
}
}
if (res < -max_value)
{
res= -max_value;
error= 1 ;
}
else if (res > max_value)
{
res= max_value;
error= 1 ;
}
*nr= res;
return error;
}
/*
Convert double to longlong / ulonglong .
If double is outside of the supported range ,
adjust m_result and set m_error .
@ param nr Number to convert
@ param unsigned_flag true if result is unsigned
*/
Value_source::
Converter_double_to_longlong::Converter_double_to_longlong(double nr,
bool unsigned_flag)
:m_error(false )
{
nr= rint(nr);
if (unsigned_flag)
{
if (nr < 0 )
{
m_result= 0 ;
m_error= true ;
}
else if (nr >= (double ) ULONGLONG_MAX)
{
m_result= ~(longlong) 0 ;
m_error= true ;
}
else
m_result= (longlong) double2ulonglong(nr);
}
else
{
if (nr <= (double ) LONGLONG_MIN)
{
m_result= LONGLONG_MIN;
m_error= (nr < (double ) LONGLONG_MIN);
}
else if (nr >= (double ) (ulonglong) LONGLONG_MAX)
{
m_result= LONGLONG_MAX;
m_error= (nr > (double ) LONGLONG_MAX);
}
else
m_result= (longlong) nr;
}
}
void Value_source::
Converter_double_to_longlong::push_warning(THD *thd,
double nr,
bool unsigned_flag)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_DATA_OVERFLOW, ER_THD(thd, ER_DATA_OVERFLOW),
ErrConvDouble(nr).ptr(),
unsigned_flag ? "UNSIGNED INT" : "INT" );
}
int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint)
{
return store(TIME_to_double(ltime));
}
double Field_double::val_real(void )
{
DBUG_ASSERT(marked_for_read());
double j;
float8get(j,ptr);
return j;
}
longlong Field_double::val_int_from_real(bool want_unsigned_result)
{
Converter_double_to_longlong conv(val_real(), want_unsigned_result);
if (unlikely(!want_unsigned_result && conv.error()))
conv.push_warning(get_thd(), Field_double::val_real(), false );
return conv.result();
}
my_decimal *Field_real::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(marked_for_read());
double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value);
return decimal_value;
}
bool Field_real::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
{
DBUG_ASSERT(marked_for_read());
double nr= val_real();
return double_to_datetime_with_warn(get_thd(), nr, ltime, fuzzydate,
table->s, field_name.str);
}
Item *Field_real::get_equal_const_item(THD *thd, const Context &ctx,
Item *const_item)
{
if (flags & ZEROFILL_FLAG)
return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item);
switch (ctx.subst_constraint()) {
case IDENTITY_SUBST:
if (const_item->decimal_scale() != Field_real::decimals())
{
double val= const_item->val_real();
return new (thd->mem_root) Item_float(thd, val, Field_real::decimals());
}
break ;
case ANY_SUBST:
break ;
}
return const_item;
}
void Field_real::sql_type(String &res) const
{
const Name name= type_handler()->name();
if (dec >= FLOATING_POINT_DECIMALS)
{
res.set_ascii(name.ptr(), name.length());
}
else
{
CHARSET_INFO *cs= res.charset();
res.length(cs->cset->snprintf(cs,(char *) res.ptr(),res.alloced_length(),
"%.*s(%d,%d)" , (int ) name.length(), name.ptr(),
(int ) field_length,dec));
}
add_zerofill_and_unsigned(res);
}
String *Field_double::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
DBUG_ASSERT(marked_for_read());
DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
double nr;
float8get(nr,ptr);
uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE;
if (val_buffer->alloc(to_length))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0 ));
return val_buffer;
}
char *to=(char *) val_buffer->ptr();
size_t len;
if (dec >= FLOATING_POINT_DECIMALS)
len= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, to_length - 1 , to, NULL);
else
len= my_fcvt(nr, dec, to, NULL);
val_buffer->length((uint) len);
if (zerofill)
prepend_zeros(val_buffer);
val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
bool Field_double::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
Protocol_text *txt;
if (unlikely(zerofill) && (txt= dynamic_cast <Protocol_text*>(protocol)))
return send_numeric_zerofill_str(txt, PROTOCOL_SEND_DOUBLE);
return protocol->store_double(Field_double::val_real(), dec);
}
int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
double a,b;
float8get(a,a_ptr);
float8get(b,b_ptr);
return (a < b) ? -1 : (a > b) ? 1 : 0 ;
}
#define DBL_EXP_DIG (sizeof (double )*8 -DBL_MANT_DIG)
/* The following should work for IEEE */
void Field_double::sort_string(uchar *to,uint length __attribute__((unused)))
{
double nr;
float8get(nr,ptr);
change_double_for_sort(nr, to);
}
/**
Save the field metadata for double fields .
Saves the pack length in the first byte of the field metadata array
at index of * metadata_ptr .
@ param metadata_ptr First byte of field metadata
@ returns number of bytes written to metadata_ptr
*/
Binlog_type_info Field_double::binlog_type_info() const
{
DBUG_ASSERT(Field_double::type() == binlog_type());
return Binlog_type_info(Field_double::type(), pack_length(), 1 ,
binlog_signedness());
}
/**
TIMESTAMP type holds datetime values in range from 1970 - 01 - 01 00 : 00 : 01 UTC to
2038 - 01 - 01 00 : 00 : 00 UTC stored as number of seconds since Unix
Epoch in UTC .
Actually SQL - 99 says that we should allow niladic functions ( like NOW ( ) )
as defaults for any field . The current limitation ( only NOW ( ) and only
for TIMESTAMP and DATETIME fields ) are because of restricted binary . frm
format and should go away in the future .
Also because of this limitation of binary . frm format we use 5 different
unireg_check values with TIMESTAMP field to distinguish various cases of
DEFAULT or ON UPDATE values . These values are :
TIMESTAMP_OLD_FIELD - old timestamp , if there was not any fields with
auto - set - on - update ( or now ( ) as default ) in this table before , then this
field has NOW ( ) as default and is updated when row changes , else it is
field which has 0 as default value and is not automatically updated .
TIMESTAMP_DN_FIELD - field with NOW ( ) as default but not set on update
automatically ( TIMESTAMP DEFAULT NOW ( ) ) , not used in Field since 10 . 2 . 2
TIMESTAMP_UN_FIELD - field which is set on update automatically but has not
NOW ( ) as default ( but it may has 0 or some other const timestamp as
default ) ( TIMESTAMP ON UPDATE NOW ( ) ) .
TIMESTAMP_DNUN_FIELD - field which has now ( ) as default and is auto - set on
update . ( TIMESTAMP DEFAULT NOW ( ) ON UPDATE NOW ( ) ) , not used in Field since 10 . 2 . 2
NONE - field which is not auto - set on update with some other than NOW ( )
default value ( TIMESTAMP DEFAULT 0 ) .
Note that TIMESTAMP_OLD_FIELDs are never created explicitly now , they are
left only for preserving ability to read old tables . Such fields replaced
with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE . This is
because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for
" TIMESTAMP DEFAULT ' Const ' " field . ( Old timestamps allowed such
specification too but ignored default value for first timestamp , which of
course is non - standard . ) In most cases user won ' t notice any change , only
exception is different behavior of old / new timestamps during ALTER TABLE .
*/
Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
const LEX_CSTRING *field_name_arg,
TABLE_SHARE *share)
:Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{
/* For 4.0 MYD and 4.0 InnoDB compatibility */
flags|= UNSIGNED_FLAG;
if (unireg_check != NONE)
{
/*
We mark the flag with TIMESTAMP_FLAG to indicate to the client that
this field will be automatically updated on insert .
*/
flags|= TIMESTAMP_FLAG;
if (unireg_check != TIMESTAMP_DN_FIELD)
flags|= ON_UPDATE_NOW_FLAG;
}
}
sql_mode_t
Field_timestamp::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
{
return expr->datetime_precision(thd) > decimals() ?
MODE_TIME_ROUND_FRACTIONAL : 0 ;
}
int Field_timestamp::save_in_field(Field *to)
{
ulong sec_part;
my_time_t ts= get_timestamp(&sec_part);
if (!ts && !sec_part)
return to->store_time_dec(Datetime::zero().get_mysql_time(), decimals());
return to->store_timestamp_dec(Timeval(ts, sec_part), decimals());
}
my_time_t Field_timestamp0::get_timestamp(const uchar *pos,
ulong *sec_part) const
{
DBUG_ASSERT(marked_for_read());
*sec_part= 0 ;
return uint4korr(pos);
}
bool Field_timestamp0::val_native(Native *to)
{
DBUG_ASSERT(marked_for_read());
my_time_t sec= (my_time_t) uint4korr(ptr);
return Timestamp_or_zero_datetime(Timestamp(sec, 0 ), sec == 0 ).
to_native(to, 0 );
}
int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt,
const ErrConv *str, int was_cut)
{
DBUG_ASSERT(marked_for_write_or_computed());
static const Timestamp zero(0 , 0 );
// Handle totally bad values
if (!dt->is_valid_datetime())
{
set_datetime_warning(WARN_DATA_TRUNCATED, str, "datetime" , 1 );
store_TIMESTAMP(zero);
return 1 ;
}
// Handle values that do not need DATETIME to TIMESTAMP conversion
if (!dt->get_mysql_time()->month)
{
/*
Zero date is allowed by the current sql_mode . Store zero timestamp .
Return success or a warning about non - fatal truncation , e . g . :
INSERT INTO t1 ( ts ) VALUES ( ' 0000 - 00 - 00 00 : 00 : 00 some tail ' ) ;
*/
store_TIMESTAMP(zero);
return store_TIME_return_code_with_warnings(was_cut, str, "datetime" );
}
// Convert DATETIME to TIMESTAMP
uint conversion_error;
const MYSQL_TIME *l_time= dt->get_mysql_time();
my_time_t timestamp= TIME_to_timestamp(thd, l_time, &conversion_error);
if (timestamp == 0 && l_time->second_part == 0 )
{
set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, "datetime" , 1 );
store_TIMESTAMP(zero);
return 1 ; // date was fine but pointed to a DST gap
}
// Store the value
DBUG_ASSERT(!dt->fraction_remainder(decimals()));
store_TIMESTAMP(Timestamp(timestamp, l_time->second_part));
// Calculate return value and send warnings if needed
if (unlikely(conversion_error)) // e.g. DATETIME in the DST gap
{
set_datetime_warning(conversion_error, str, "datetime" , 1 );
return 1 ;
}
return store_TIME_return_code_with_warnings(was_cut, str, "datetime" );
}
date_conv_mode_t Timestamp::sql_mode_for_timestamp(THD *thd)
{
// We don't want to store invalid or fuzzy datetime values in TIMESTAMP
return date_conv_mode_t((thd->variables.sql_mode & MODE_NO_ZERO_DATE) |
MODE_NO_ZERO_IN_DATE);
}
int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
int warn;
ErrConvTime str(ltime);
THD *thd= get_thd();
Datetime dt(thd, &warn, ltime, Timestamp::DatetimeOptions(thd), decimals());
return store_TIME_with_warning(thd, &dt, &str, warn);
}
int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs)
{
ErrConvString str(from, len, cs);
THD *thd= get_thd();
MYSQL_TIME_STATUS st;
Datetime dt(thd, &st, from, len, cs, Timestamp::DatetimeOptions(thd), decimals());
return store_TIME_with_warning(thd, &dt, &str, st.warnings);
}
int Field_timestamp::store(double nr)
{
int error;
ErrConvDouble str(nr);
THD *thd= get_thd();
Datetime dt(thd, &error, nr, Timestamp::DatetimeOptions(thd), decimals());
return store_TIME_with_warning(thd, &dt, &str, error);
}
int Field_timestamp::store(longlong nr, bool unsigned_val)
{
int error;
Longlong_hybrid tmp(nr, unsigned_val);
ErrConvInteger str(tmp);
THD *thd= get_thd();
Datetime dt(&error, tmp, Timestamp::DatetimeOptions(thd));
return store_TIME_with_warning(thd, &dt, &str, error);
}
int Field_timestamp::store_timestamp_dec(const my_timeval &tv, uint dec)
{
int warn= 0 ;
time_round_mode_t mode= Datetime::default_round_mode(get_thd());
const Timestamp ts= Timestamp(tv).round(decimals(), mode, &warn);
store_TIMESTAMP(ts);
if (ts.tv_sec == 0 && ts.tv_usec == 0 )
{
/*
The value { tv_sec = = 0 , tv_usec = = 0 } here means ' 1970 - 01 - 01 00 : 00 : 00 + 00 ' .
It does not mean zero datetime ! because store_timestamp_dec ( ) knows
nothing about zero dates . It inserts only real timeval values .
Zero ts = { 0 , 0 } here is possible in two scenarios :
- the passed tv was already { 0 , 0 } meaning ' 1970 - 01 - 01 00 : 00 : 00 + 00 '
- the passed tv had some microseconds but they were rounded / truncated
to zero : ' 1970 - 01 - 01 00 : 00 : 00 . 1 + 00 ' - > ' 1970 - 01 - 01 00 : 00 : 00 + 00 ' .
It does not matter whether rounding / truncation really happened .
In both cases the call for store_TIMESTAMP ( ts ) above re - interpreted
' 1970 - 01 - 01 00 : 00 : 00 + 00 : 00 ' to zero date . Return 1 no matter what
sql_mode is . Even if sql_mode allows zero dates , there is still a problem
here : ' 1970 - 01 - 01 00 : 00 : 00 + 00 ' could not be stored as - is !
*/
ErrConvString str(STRING_WITH_LEN("1970-01-01 00:00:00 +00:00" ),
system_charset_info);
set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, &str, "datetime" , 1 );
return 1 ; // '1970-01-01 00:00:00 +00' was converted to a zero date
}
if (warn)
{
/*
We ' re here if rounding would overflow outside of the supported TIMESTAMP
range , so truncation happened instead :
CREATE TABLE t1 ( a TIMESTAMP ( 6 ) ) ;
INSERT INTO t1 VALUES ( ' maximum - possible - timestamp . 999999 ' ) ;
ALTER TABLE t1 MODIFY a TIMESTAMP ( 5 ) ;
SELECT * FROM t1 ; - - > ' maximum - possible - timestamp . 99999 ' ( 5 digits )
Raise a warning , like DATETIME does for ' 9999 - 12 - 31 23 : 59 : 59 . 999999 ' .
*/
set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1 );
}
return 0 ;
}
int Field_timestamp::zero_time_stored_return_code_with_warning()
{
if (get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE)
{
ErrConvString s(
STRING_WITH_LEN("0000-00-00 00:00:00.000000" ) - (decimals() ? 6 - decimals() : 7 ),
system_charset_info);
set_datetime_warning(WARN_DATA_TRUNCATED, &s, "datetime" , 1 );
return 1 ;
}
return 0 ;
}
int Field_timestamp::store_native(const Native &value)
{
if (!value.length()) // Zero datetime
{
reset();
return zero_time_stored_return_code_with_warning();
}
/*
The exact second precision is not important here .
Field_timestamp * : : store_timestamp_dec ( ) do not use the " dec " parameter .
Passing TIME_SECOND_PART_DIGITS is OK .
*/
return store_timestamp_dec(Timestamp(value), TIME_SECOND_PART_DIGITS);
}
longlong Field_timestamp::val_int(void )
{
MYSQL_TIME ltime;
if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
return 0 ;
return ltime.year * 10000000000 LL + ltime.month * 100000000 LL +
ltime.day * 1000000 L + ltime.hour * 10000 L +
ltime.minute * 100 + ltime.second;
}
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
{
MYSQL_TIME ltime;
uint32 year, temp, temp2;
uint dec;
char *to;
val_buffer->alloc(field_length+1 );
to= (char *) val_buffer->ptr();
val_buffer->length(field_length);
if (get_date(<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;
DBUG_ASSERT(temp >= 1969 );
year= temp/100 ;
temp-= year*100 ;
temp2= year/10 ; year= year-temp2*10 ;
*to++= (char ) ('0' +(char ) (temp2));
*to++= (char ) ('0' +(char ) (year));
temp2=temp/10 ; temp=temp-temp2*10 ;
*to++= (char ) ('0' +(char ) (temp2));
*to++= (char ) ('0' +(char ) (temp));
*to++= '-' ;
temp=ltime.month;
temp2=temp/10 ; temp=temp-temp2*10 ;
*to++= (char ) ('0' +(char ) (temp2));
*to++= (char ) ('0' +(char ) (temp));
*to++= '-' ;
temp=ltime.day;
temp2=temp/10 ; temp=temp-temp2*10 ;
*to++= (char ) ('0' +(char ) (temp2));
*to++= (char ) ('0' +(char ) (temp));
*to++= ' ' ;
temp=ltime.hour;
temp2=temp/10 ; temp=temp-temp2*10 ;
*to++= (char ) ('0' +(char ) (temp2));
*to++= (char ) ('0' +(char ) (temp));
*to++= ':' ;
temp=ltime.minute;
temp2=temp/10 ; temp=temp-temp2*10 ;
*to++= (char ) ('0' +(char ) (temp2));
*to++= (char ) ('0' +(char ) (temp));
*to++= ':' ;
temp=ltime.second;
temp2=temp/10 ; temp=temp-temp2*10 ;
*to++= (char ) ('0' +(char ) (temp2));
*to++= (char ) ('0' +(char ) (temp));
*to= 0 ;
val_buffer->set_charset(&my_charset_numeric);
if ((dec= decimals()))
{
ulong sec_part= (ulong) sec_part_shift(ltime.second_part, dec);
char *buf= const_cast <char *>(val_buffer->ptr() + MAX_DATETIME_WIDTH);
for (int i= dec; i > 0 ; i--, sec_part/= 10 )
buf[i]= (char )(sec_part % 10 ) + '0' ;
buf[0 ]= '.' ;
buf[dec + 1 ]= 0 ;
}
return val_buffer;
}
bool
Field_timestamp::validate_value_in_record(THD *thd, const uchar *record) const
{
DBUG_ASSERT(!is_null_in_record(record));
ulong sec_part;
return !get_timestamp(ptr_in_record(record), &sec_part) && !sec_part &&
bool (sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != false ;
}
bool Field_timestamp::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
ulong sec_part;
my_time_t ts= get_timestamp(&sec_part);
return get_thd()->timestamp_to_TIME(ltime, ts, sec_part, fuzzydate);
}
bool Field_timestamp0::send(Protocol *protocol)
{
MYSQL_TIME ltime;
Field_timestamp0::get_date(<ime, date_mode_t(0 ));
return protocol->store_datetime(<ime, 0 );
}
int Field_timestamp0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
time_t a,b;
a= uint4korr(a_ptr);
b= uint4korr(b_ptr);
return (a < b ) ? -1 : (a > b) ? 1 : 0 ;
}
void Field_timestamp0::sort_string(uchar *to,uint length __attribute__((unused)))
{
to[0 ] = ptr[3 ];
to[1 ] = ptr[2 ];
to[2 ] = ptr[1 ];
to[3 ] = ptr[0 ];
}
int Field_timestamp0::set_time()
{
set_notnull();
store_TIMESTAMP(Timestamp(get_thd()->query_start(), 0 ));
return 0 ;
}
bool Field_timestamp::load_data_set_no_data(THD *thd, bool fixed_format)
{
if (!maybe_null())
{
/*
Timestamp fields that are NOT NULL are autoupdated if there is no
corresponding value in the data file .
*/
set_time();
set_has_explicit_value();
return false ;
}
return Field::load_data_set_no_data(thd, fixed_format);
}
bool Field_timestamp::load_data_set_null(THD *thd)
{
if (!maybe_null())
{
/*
Timestamp fields that are NOT NULL are autoupdated if there is no
corresponding value in the data file .
*/
set_time();
}
else
{
reset();
set_null();
}
set_has_explicit_value(); // Do not auto-update this field
return false ;
}
#ifdef NOT_USED
static void store_native(ulonglong num, uchar *to, uint bytes)
{
switch (bytes) {
case 1 : *to= (uchar)num; break ;
case 2 : shortstore(to, (ushort)num); break ;
case 3 : int3store(to, num); /* Sic!*/ break;
case 4 : longstore(to, (ulong)num); break ;
case 8 : longlongstore(to, num); break ;
default : DBUG_ASSERT(0 );
}
}
static longlong read_native(const uchar *from, uint bytes)
{
switch (bytes) {
case 1 : return from[0 ];
case 2 : { uint16 tmp; shortget(tmp, from); return tmp; }
case 3 : return uint3korr(from);
case 4 : { uint32 tmp; longget(tmp, from); return tmp; }
case 8 : { longlong tmp; longlongget(tmp, from); return tmp; }
default : DBUG_ASSERT(0 ); return 0 ;
}
}
#endif
void Field_timestamp_hires::store_TIMEVAL(const my_timeval &tv)
{
mi_int4store(ptr, tv.tv_sec);
store_bigendian(sec_part_shift(tv.tv_usec, dec), ptr+4 , sec_part_bytes(dec));
}
my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos,
ulong *sec_part) const
{
DBUG_ASSERT(marked_for_read());
*sec_part= (long )sec_part_unshift(read_bigendian(pos+4 , sec_part_bytes(dec)), dec);
return mi_uint4korr(pos);
}
bool Field_timestamp_hires::val_native(Native *to)
{
DBUG_ASSERT(marked_for_read());
struct my_timeval tm;
tm.tv_sec= mi_uint4korr(ptr);
tm.tv_usec= (ulong) sec_part_unshift(read_bigendian(ptr+4 , sec_part_bytes(dec)), dec);
return Timestamp_or_zero_datetime(Timestamp(tm), tm.tv_sec == 0 ).
to_native(to, dec);
}
double Field_timestamp_with_dec::val_real(void )
{
MYSQL_TIME ltime;
if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
return 0 ;
return ltime.year * 1 e10 + ltime.month * 1 e8 +
ltime.day * 1 e6 + ltime.hour * 1 e4 +
ltime.minute * 1 e2 + ltime.second + ltime.second_part*1 e-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 my_timeval &tm)
{
my_timestamp_to_binary(&tm, ptr, dec);
}
void Field_timestampf::set_max()
{
DBUG_ENTER("Field_timestampf::set_max" );
DBUG_ASSERT(marked_for_write_or_computed());
DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS);
set_notnull();
mi_int4store(ptr, TIMESTAMP_MAX_VALUE);
mi_int3store(ptr + 4 , TIME_MAX_SECOND_PART);
DBUG_VOID_RETURN;
}
bool Field_timestampf::is_max(const uchar *ptr_arg) const
{
longlong timestamp= mi_uint4korr(ptr_arg);
DBUG_ENTER("Field_timestampf::is_max" );
DBUG_ASSERT(marked_for_read());
/* Allow old max value and new max value */
DBUG_RETURN((timestamp == TIMESTAMP_MAX_VALUE ||
timestamp == INT_MAX32) &&
mi_uint3korr(ptr_arg + 4 ) == TIME_MAX_SECOND_PART);
}
my_time_t Field_timestampf::get_timestamp(const uchar *pos,
ulong *sec_part) const
{
struct my_timeval tm;
my_timestamp_from_binary(&tm, pos, dec);
*sec_part= tm.tv_usec;
return (my_time_t) tm.tv_sec;
}
bool Field_timestampf::val_native(Native *to)
{
DBUG_ASSERT(marked_for_read());
char zero[8 ]= "\0\0\0\0\0\0\0" ;
DBUG_ASSERT(pack_length () <= sizeof (zero));
// Check if it's '0000-00-00 00:00:00' rather than a real timestamp
if (!memcmp(ptr, zero, pack_length()))
{
to->length(0 );
return false ;
}
return Field::val_native(to);
}
Binlog_type_info Field_timestampf::binlog_type_info() const
{
return Binlog_type_info(Field_timestampf::binlog_type(), decimals(), 1 );
}
/*************************************************************/
sql_mode_t Field_temporal::can_handle_sql_mode_dependency_on_store() const
{
return MODE_PAD_CHAR_TO_FULL_LENGTH;
}
bool Field_temporal::is_equal(const Column_definition &new_field) const
{
return new_field.type_handler() == type_handler() &&
new_field.length == max_display_length();
}
void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
const ErrConv *str, int was_cut,
const char *typestr)
{
/*
error code logic :
MYSQL_TIME_WARN_TRUNCATED means that the value was not a date / time at all .
it will be stored as zero date / time .
MYSQL_TIME_WARN_OUT_OF_RANGE means that the value was a date / time ,
that is , it was parsed as such , but the value was invalid .
Also , MYSQL_TIME_WARN_TRUNCATED is used when storing a DATETIME in
a DATE field and non - zero time part is thrown away .
*/
if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, str, typestr, 1 );
if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, typestr, 1 );
}
void Field_temporal::sql_type_dec_comment(String &res,
const Name &name,
uint dec,
const Name &comment) const
{
CHARSET_INFO *cs=res.charset();
res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
"%.*s(%u)%s%.*s%s" ,
(uint) name.length(), name.ptr(),
dec,
comment.length() ? " /* " : "",
(uint) comment.length(), comment.ptr(),
comment.length() ? " */" : "" ));
}
void Field_temporal::sql_type_comment(String &res,
const Name &name,
const Name &comment) const
{
CHARSET_INFO *cs=res.charset();
res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
"%.*s%s%.*s%s" ,
(uint) name.length(), name.ptr(),
comment.length() ? " /* " : "",
(uint) comment.length(), comment.ptr(),
comment.length() ? " */" : "" ));
}
const Name & Field_temporal::type_version_mysql56()
{
DBUG_EXECUTE_IF("sql_type" , return Type_handler::version_mysql56(); );
static Name none(NULL, 0 );
return none;
}
/*
Store string into a date / time field
RETURN
0 ok
1 Value was cut during conversion
2 value was out of range
3 Datetime value that was cut ( warning level NOTE )
This is used by opt_range . cc : get_mm_leaf ( ) .
*/
int Field_datetime::store_TIME_with_warning(const Datetime *dt,
const ErrConv *str,
int was_cut)
{
DBUG_ASSERT(marked_for_write_or_computed());
// Handle totally bad values
if (!dt->is_valid_datetime())
return store_invalid_with_warning(str, was_cut, "datetime" );
// Store the value
DBUG_ASSERT(!dt->fraction_remainder(decimals()));
store_datetime(*dt);
// Caclulate return value and send warnings if needed
return store_TIME_return_code_with_warnings(was_cut, str, "datetime" );
}
int Field_datetime::store(const char *from, size_t len, CHARSET_INFO *cs)
{
MYSQL_TIME_STATUS st;
ErrConvString str(from, len, cs);
THD *thd= get_thd();
Datetime dt(thd, &st, from, len, cs, Datetime::Options(thd), decimals());
return store_TIME_with_warning(&dt, &str, st.warnings);
}
int Field_datetime::store(double nr)
{
int error;
ErrConvDouble str(nr);
THD *thd= get_thd();
Datetime dt(thd, &error, nr, Datetime::Options(thd), decimals());
return store_TIME_with_warning(&dt, &str, error);
}
int Field_datetime::store(longlong nr, bool unsigned_val)
{
int error;
Longlong_hybrid tmp(nr, unsigned_val);
ErrConvInteger str(tmp);
THD *thd= get_thd();
Datetime dt(&error, tmp, Datetime::Options(thd));
return store_TIME_with_warning(&dt, &str, error);
}
int Field_datetime::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
int error;
ErrConvTime str(ltime);
THD *thd= get_thd();
Datetime dt(thd, &error, ltime, Datetime::Options(thd), decimals());
return store_TIME_with_warning(&dt, &str, error);
}
int Field_datetime::store_decimal(const my_decimal *d)
{
int error;
ErrConvDecimal str(d);
THD *thd= get_thd();
Datetime tm(thd, &error, d, Datetime::Options(thd), decimals());
return store_TIME_with_warning(&tm, &str, error);
}
bool
Field_temporal_with_date::validate_value_in_record(THD *thd,
const uchar *record) const
{
DBUG_ASSERT(!is_null_in_record(record));
MYSQL_TIME ltime;
return get_TIME(<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*24 L+ltime->hour)*10000 L +
(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)))
{
/* "YYYY" + NUL terminator */
static const size_t YEAR_STR_BUFF_LEN= 5 ;
DBUG_ASSERT(field_length < YEAR_STR_BUFF_LEN);
val_buffer->alloc(YEAR_STR_BUFF_LEN);
val_buffer->length(field_length);
char *to=(char *) val_buffer->ptr();
snprintf(to, YEAR_STR_BUFF_LEN,
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*10000 L + 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/10000 L % 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/10000 L % 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 % 32 L)+(j / 32 L % 16 L)*100 L + (j/(16 L*32 L))*10000 L;
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/1000000 LL);
part2=(long ) (tmp - (ulonglong) part1*1000000 LL);
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/1000000 LL);
part2=(uint32) (tmp - (ulonglong) part1*1000000 LL);
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->eq_collation(field_charset()) &&
new_field.length == max_display_length());
}
int Field_longstr::store_decimal(const my_decimal *d)
{
StringBuffer<DECIMAL_MAX_STR_LENGTH+1 > str;
d->to_string(&str);
return store(str.ptr(), str.length(), str.charset());
}
Data_type_compatibility
Field_longstr::cmp_to_string_with_same_collation(const Item_bool_func *cond,
const Item *item) const
{
return (!cmp_is_done_using_type_handler_of_this(cond, item) ?
Data_type_compatibility::INCOMPATIBLE_DATA_TYPE :
!charset()->eq_collation(cond->compare_collation()) ?
Data_type_compatibility::INCOMPATIBLE_COLLATION :
Data_type_compatibility::OK);
}
Data_type_compatibility
Field_longstr::cmp_to_string_with_stricter_collation(const Item_bool_func *cond,
const Item *item) const
{
return (!cmp_is_done_using_type_handler_of_this(cond, item) ?
Data_type_compatibility::INCOMPATIBLE_DATA_TYPE :
(!charset()->eq_collation(cond->compare_collation()) &&
!(cond->compare_collation()->state & MY_CS_BINSORT) &&
!Utf8_narrow::should_do_narrowing(this , cond->compare_collation())) ?
Data_type_compatibility::INCOMPATIBLE_COLLATION :
Data_type_compatibility::OK);
}
Data_type_compatibility
Field_longstr::can_optimize_keypart_ref(const Item_bool_func *cond,
const Item *item) const
{
DBUG_ASSERT(cmp_type() == STRING_RESULT);
/*
So , we have an equality : tbl . string_key = ' abc '
The comparison is the string comparison . Can we use index lookups to
find matching rows ? We can do that when :
- The comparison uses the same collation as tbl . string_key
- the comparison uses binary collation , while tbl . string_key
uses some other collation .
In this case , we will find matches in some collation . For example , for
' abc ' we may find ' abc ' , ' ABC ' , and ' ä bc ' .
But we ' re certain that will find the row with the identical binary , ' abc ' .
*/
return cmp_to_string_with_stricter_collation(cond, item);
}
Data_type_compatibility
Field_longstr::can_optimize_hash_join(const Item_bool_func *cond,
const Item *item) const
{
DBUG_ASSERT(cmp_type() == STRING_RESULT);
return cmp_to_string_with_same_collation(cond, item);
}
Data_type_compatibility
Field_longstr::can_optimize_group_min_max(const Item_bool_func *cond,
const Item *const_item) const
{
/*
Can ' t use indexes when comparing a string to a number or a date
Don ' t use an index when comparing strings of different collations .
*/
DBUG_ASSERT(cmp_type() == STRING_RESULT);
return cmp_to_string_with_same_collation(cond, const_item);
}
Data_type_compatibility
Field_longstr::can_optimize_range(const Item_bool_func *cond,
const Item *item,
bool is_eq_func) const
{
return is_eq_func ?
cmp_to_string_with_stricter_collation(cond, item) :
cmp_to_string_with_same_collation(cond, item);
}
/**
This overrides the default behavior of the parent constructor
Warn_filter ( thd ) to suppress notes about trailing spaces in case of CHAR ( N ) ,
as they are truncated during val_str ( ) .
We still do want truncation notes in case of BINARY ( N ) ,
as trailing spaces are not truncated in val_str ( ) .
*/
Field_string::Warn_filter_string::Warn_filter_string(const THD *thd,
const Field_string *field)
:Warn_filter(!thd->no_errors,
!thd->no_errors &&
field->field_charset() == &my_charset_bin)
{ }
double Field_string::val_real(void )
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
const LEX_CSTRING str= to_lex_cstring();
return Converter_strntod_with_warn(thd,
Warn_filter_string(thd, this ),
"DOUBLE" ,
Field_string::charset(),
str.str, str.length).result();
}
bool Field_string::val_bool()
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
const LEX_CSTRING str= to_lex_cstring();
double res= Converter_strntod_with_warn(thd,
Warn_filter_string(thd, this ),
"BOOLEAN" ,
Field_string::charset(),
str.str, str.length).result();
return res != 0 .0 ;
}
longlong Field_string::val_int(void )
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
const LEX_CSTRING str= to_lex_cstring();
return Converter_strntoll_with_warn(thd, Warn_filter_string(thd, this ),
Field_string::charset(),
str.str, str.length).result();
}
sql_mode_t Field_string::value_depends_on_sql_mode() const
{
return has_charset() ? MODE_PAD_CHAR_TO_FULL_LENGTH : sql_mode_t(0 );
};
sql_mode_t Field_string::can_handle_sql_mode_dependency_on_store() const
{
return has_charset() ? MODE_PAD_CHAR_TO_FULL_LENGTH : sql_mode_t(0 );
}
LEX_CSTRING Field_string::to_lex_cstring() const
{
DBUG_ASSERT(marked_for_read());
/* See the comment for Field_long::store(long long) */
DBUG_ASSERT(!table || table->in_use == current_thd);
if (get_thd()->variables.sql_mode & MODE_PAD_CHAR_TO_FULL_LENGTH)
return Lex_cstring((const char *) ptr,
field_charset()->charpos(ptr, ptr + field_length,
Field_string::char_length()));
return Lex_cstring((const char *) ptr,
field_charset()->lengthsp((const char *) ptr, field_length));
}
String *Field_string::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
DBUG_ASSERT(marked_for_read());
const LEX_CSTRING str= to_lex_cstring();
val_ptr->set(str.str, str.length, field_charset());
return val_ptr;
}
my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
const LEX_CSTRING str= to_lex_cstring();
Converter_str2my_decimal_with_warn(thd,
Warn_filter_string(thd, this ),
E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM,
Field_string::charset(),
str.str, str.length, decimal_value);
return decimal_value;
}
struct Check_field_param {
const Field *field;
};
#ifdef HAVE_REPLICATION
static bool
check_field_for_37426(const void *param_arg)
{
Check_field_param *param= (Check_field_param*) param_arg;
DBUG_ASSERT(param->field->real_type() == MYSQL_TYPE_STRING);
DBUG_PRINT("debug" , ("Field %s - type: %d, size: %d" ,
param->field->field_name.str,
param->field->real_type(),
param->field->row_pack_length()));
return param->field->row_pack_length() > 255 ;
}
#endif
bool
Field_string::compatible_field_size(uint field_metadata,
const Relay_log_info *rli_arg,
uint16 mflags __attribute__((unused)),
int *order_var) const
{
#ifdef HAVE_REPLICATION
const Check_field_param check_param = { this };
if (rpl_master_has_bug(rli_arg, 37426 , TRUE ,
check_field_for_37426, &check_param))
return FALSE ; // Not compatible field sizes
#endif
return Field::compatible_field_size(field_metadata, rli_arg, mflags, order_var);
}
int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
return field_charset()->coll->strnncollsp_nchars(field_charset(),
a_ptr, field_length,
b_ptr, field_length,
Field_string::char_length(),
0 );
}
int Field_string::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
size_t prefix_char_len) const
{
size_t field_len= table->field[field_index]->field_length;
return field_charset()->coll->strnncollsp_nchars(field_charset(),
a_ptr, field_len,
b_ptr, field_len,
prefix_char_len,
0 );
}
void Field_string::sort_string(uchar *to,uint length)
{
/*
Let ' s find the real value length to truncate trailing padding spaces .
This is needed to avoid redundant WARN_SORTING_ON_TRUNCATED_LENGTH
warnings .
*/
const LEX_CSTRING str= to_lex_cstring();
my_strnxfrm_ret_t rc=
field_charset()->strnxfrm(to, length,
char_length() * field_charset()->strxfrm_multiply,
(const uchar *) str.str, str.length,
MY_STRXFRM_PAD_WITH_SPACE |
MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(rc.m_result_length == length);
if (rc.m_warnings & MY_STRNXFRM_TRUNCATED_WEIGHT_REAL_CHAR)
get_thd()->num_of_strings_sorted_on_truncated_length++;
}
void Field_string::sql_type(String &res) const
{
THD *thd= table->in_use;
CHARSET_INFO *cs=res.charset();
size_t length;
length= cs->cset->snprintf(cs,(char *) res.ptr(),
res.alloced_length(), "%s(%d)%s" ,
(type() == MYSQL_TYPE_VAR_STRING ?
(has_charset() ? "varchar" : "varbinary" ) :
(has_charset() ? "char" : "binary" )),
(int ) field_length / charset()->mbmaxlen,
type() == MYSQL_TYPE_VAR_STRING ? "/*old*/" : "");
res.length(length);
if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
has_charset() && (charset()->state & MY_CS_BINSORT))
res.append(STRING_WITH_LEN(" binary" ));
}
/**
For fields which are associated with character sets their length is provided
in octets and their character set information is also provided as part of
type information .
@ param res String which contains filed type and length .
*/
void Field_string::sql_rpl_type(String *res) const
{
if (Field_string::has_charset())
{
CHARSET_INFO *cs= res->charset();
DBUG_ASSERT(cs->mbminlen == 1 );
size_t length= cs->cset->snprintf(cs, (char *) res->ptr(),
res->alloced_length(),
"char(%u octets) character set %s" ,
field_length,
charset()->cs_name.str);
res->length(length);
}
else
Field_string::sql_type(*res);
}
uchar *Field_string::pack (uchar *to, const uchar *from) const
{
DBUG_PRINT("debug" , ("Packing field '%s'" , field_name.str));
return StringPack(field_charset(), field_length).pack (to, from);
}
/**
Unpack a string field from row data .
This method is used to unpack a string field from a master whose size
of the field is less than that of the slave . Note that there can be a
variety of field types represented with this class . Certain types like
ENUM or SET are processed differently . Hence , the upper byte of the
@ c param_data argument contains the result of field - > real_type ( ) from
the master .
@ note For information about how the length is packed , see @ c
Field_string : : save_field_metadata
@ param to Destination of the data
@ param from Source of the data
@ param param_data Real type ( upper ) and length ( lower ) values
@ return New pointer into memory based on from + length of the data
*/
const uchar *
Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end,
uint param_data)
{
return StringPack(field_charset(), field_length).unpack(to, from, from_end,
param_data);
}
/**
Save the field metadata for string fields .
Saves the real type in the first byte and the field length in the
second byte of the field metadata array at index of * metadata_ptr and
* ( metadata_ptr + 1 ) .
@ note In order to be able to handle lengths exceeding 255 and be
backwards - compatible with pre - 5 . 1 . 26 servers , an extra two bits of
the length has been added to the metadata in such a way that if
they are set , a new unrecognized type is generated . This will
cause pre - 5 . 1 - 26 servers to stop due to a field type mismatch ,
while new servers will be able to extract the extra bits . If the
length is < 256 , there will be no difference and both a new and an
old server will be able to handle it .
@ note The extra two bits are added to bits 13 and 14 of the
parameter data ( with 1 being the least siginficant bit and 16 the
most significant bit of the word ) by xoring the extra length bits
with the real type . Since all allowable types have 0 xF 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 & 0 xF0) == 0 xF0);
DBUG_PRINT("debug" , ("octets: %u, type_code: %u" , octets, type_code));
m_metadata= (type_code ^ ((octets & 0 x300) >> 4 )) +
(((uint)(octets & 0 xFF)) << 8 );
}
Binlog_type_info Field_string::binlog_type_info() const
{
DBUG_ASSERT(Field_string::type() == binlog_type());
return Binlog_type_info_fixed_string(Field_string::binlog_type(),
field_length, charset());
}
uint Field_string::packed_col_length() const
{
return StringPack(field_charset(), field_length).
packed_col_length(ptr);
}
uint Field_string::max_packed_col_length(uint max_length) const
{
return StringPack::max_packed_col_length(max_length);
}
uint Field_string::get_key_image(uchar *buff, uint length, const uchar *ptr_arg,
imagetype type_arg) const
{
size_t bytes= field_charset()->charpos((char *) ptr_arg,
(char *) ptr_arg + field_length,
length / mbmaxlen());
memcpy(buff, ptr_arg, bytes);
if (bytes < length)
field_charset()->fill((char *) buff + bytes,
length - bytes,
field_charset()->pad_char);
return (uint)bytes;
}
Field *Field_string::make_new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type)
{
Field *field;
if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
field= Field::make_new_field(root, new_table, keep_type);
else if ((field= new (root) Field_varstring(field_length, maybe_null(),
&field_name,
new_table->s, charset())))
{
/*
Old VARCHAR field which should be modified to a VARCHAR on copy
This is done to ensure that ALTER TABLE will convert old VARCHAR fields
to now VARCHAR fields .
*/
field->init_for_make_new_field(new_table, orig_table);
}
return field;
}
en_fieldtype Field_string::tmp_engine_column_type(bool use_packed_rows) const
{
return field_length >= MIN_STRING_LENGTH_TO_PACK_ROWS ? FIELD_SKIP_ENDSPACE :
FIELD_NORMAL;
}
/****************************************************************************
VARCHAR type
Data in field - > ptr is stored as :
1 or 2 bytes length - prefix - header ( from Field_varstring : : length_bytes )
data
NOTE :
When VARCHAR is stored in a key ( for handler : : index_read ( ) etc ) it ' s always
stored with a 2 byte prefix . ( Just like blob keys ) .
Normally length_bytes is calculated as ( field_length < 256 : 1 ? 2 )
The exception is if there is a prefix key field that is part of a long
VARCHAR , in which case field_length for this may be 1 but the length_bytes
is 2 .
****************************************************************************/
const uint Field_varstring::MAX_SIZE= UINT_MAX16;
const Type_handler *Field_varstring::type_handler() const
{
/*
This is a temporary solution and will be fixed soon ( in 10 . 9 ? ) .
Type_handler_varchar_json will provide its own Field_varstring_json
and Field_varstring_compressed_json
*/
if (Type_handler_json_common::has_json_valid_constraint(this ))
return &type_handler_varchar_json;
return &type_handler_varchar;
}
/**
Save the field metadata for varstring fields .
Saves the field length in the first byte . Note : may consume
2 bytes . Caller must ensure second byte is contiguous with
first byte ( e . g . array index 0 , 1 ) .
@ param metadata_ptr First byte of field metadata
@ returns number of bytes written to metadata_ptr
*/
Binlog_type_info Field_varstring::binlog_type_info() const
{
DBUG_ASSERT(Field_varstring::type() == binlog_type());
return Binlog_type_info(Field_varstring::type(), field_length, 2 , charset());
}
bool Field_varstring::memcpy_field_possible(const Field *from) const
{
return (Field_str::memcpy_field_possible(from) &&
!compression_method() == !from->compression_method() &&
length_bytes == ((Field_varstring*) from)->length_bytes &&
(table->file && !(table->file->ha_table_flags() &
HA_RECORD_MUST_BE_CLEAN_ON_WRITE)));
}
int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs)
{
DBUG_ASSERT(marked_for_write_or_computed());
uint copy_length;
int rc;
rc= well_formed_copy_with_check((char *) get_data(), field_length,
cs, from, length,
Field_varstring::char_length(),
true , ©_length);
store_length(copy_length);
return rc;
}
double Field_varstring::val_real(void )
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
return Converter_strntod_with_warn(thd, Warn_filter(thd), "DOUBLE" ,
Field_varstring::charset(),
(const char *) get_data(),
get_length()).result();
}
longlong Field_varstring::val_int(void )
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
return Converter_strntoll_with_warn(thd, Warn_filter(thd),
Field_varstring::charset(),
(const char *) get_data(),
get_length()).result();
}
String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
DBUG_ASSERT(marked_for_read());
val_ptr->set((const char *) get_data(), get_length(), field_charset());
return val_ptr;
}
my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
Converter_str2my_decimal_with_warn(thd, Warn_filter(thd),
E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM,
Field_varstring::charset(),
(const char *) get_data(),
get_length(), decimal_value);
return decimal_value;
}
/*
An optimized version that uses less stack and less temporary
variable initialization than Field_longstr : : send ( )
*/
bool Field_varstring::send(Protocol *protocol)
{
return protocol->store((const char *) get_data(), get_length(),
field_charset());
}
#ifdef HAVE_MEM_CHECK
/*
Mark the unused part of the varstring as defined .
This function is only used be Unique when calculating statistics .
The marking is needed as we write the whole tree to disk in case of
overflows . For using or comparing values the undefined value part
is never used . We could also use bzero ( ) here , but it would be
slower in production environments .
This function is tested by main . stat_tables - enospc
*/
void Field_varstring::mark_unused_memory_as_defined()
{
uint used_length __attribute__((unused)) = get_length();
MEM_MAKE_DEFINED(get_data() + used_length, field_length - used_length);
}
#endif
int Field_varstring::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
uint a_length, b_length;
int diff;
if (length_bytes == 1 )
{
a_length= (uint) *a_ptr;
b_length= (uint) *b_ptr;
}
else
{
a_length= uint2korr(a_ptr);
b_length= uint2korr(b_ptr);
}
set_if_smaller(a_length, field_length);
set_if_smaller(b_length, field_length);
diff= field_charset()->strnncollsp(a_ptr + length_bytes, a_length,
b_ptr + length_bytes, b_length);
return diff;
}
int Field_varstring::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
size_t prefix_char_len) const
{
/* avoid more expensive strnncollsp_nchars() if possible */
if (prefix_char_len * field_charset()->mbmaxlen ==
table->field[field_index]->field_length)
return Field_varstring::cmp(a_ptr, b_ptr);
size_t a_length, b_length;
if (length_bytes == 1 )
{
a_length= *a_ptr;
b_length= *b_ptr;
}
else
{
a_length= uint2korr(a_ptr);
b_length= uint2korr(b_ptr);
}
return field_charset()->coll->strnncollsp_nchars(field_charset(),
a_ptr + length_bytes,
a_length,
b_ptr + length_bytes,
b_length,
prefix_char_len,
0 );
}
/**
@ note
varstring and blob keys are ALWAYS stored with a 2 byte length prefix
*/
int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length) const
{
size_t length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
size_t local_char_length= max_key_length / mbmaxlen();
local_char_length= field_charset()->charpos(ptr + length_bytes,
ptr + length_bytes + length,
local_char_length);
set_if_smaller(length, local_char_length);
return field_charset()->strnncollsp(ptr + length_bytes,
length,
key_ptr + HA_KEY_BLOB_LENGTH,
uint2korr(key_ptr));
}
/**
Compare to key segments ( always 2 byte length prefix ) .
@ note
This is used only to compare key segments created for index_read ( ) .
( keys are created and compared in key . cc )
*/
int Field_varstring::key_cmp(const uchar *a,const uchar *b) const
{
return field_charset()->strnncollsp(a + HA_KEY_BLOB_LENGTH, uint2korr(a),
b + HA_KEY_BLOB_LENGTH, uint2korr(b));
}
void Field_varstring::sort_string(uchar *to,uint length)
{
String buf;
val_str(&buf, &buf);
if (field_charset() == &my_charset_bin)
{
/* Store length last in high-byte order to sort longer strings first */
if (length_bytes == 1 )
to[length - 1 ]= buf.length();
else
mi_int2store(to + length - 2 , buf.length());
length-= length_bytes;
}
my_strnxfrm_ret_t rc=
field_charset()->strnxfrm(to, length,
char_length() * field_charset()->strxfrm_multiply,
(const uchar *) buf.ptr(), buf.length(),
MY_STRXFRM_PAD_WITH_SPACE |
MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(rc.m_result_length == length);
if (rc.m_warnings & MY_STRNXFRM_TRUNCATED_WEIGHT_REAL_CHAR)
get_thd()->num_of_strings_sorted_on_truncated_length++;
}
enum ha_base_keytype Field_varstring::key_type() const
{
enum ha_base_keytype res;
if (binary())
res= length_bytes == 1 ? HA_KEYTYPE_VARBINARY1 : HA_KEYTYPE_VARBINARY2;
else
res= length_bytes == 1 ? HA_KEYTYPE_VARTEXT1 : HA_KEYTYPE_VARTEXT2;
return res;
}
/*
Compressed columns need one extra byte to store the compression method .
This byte is invisible to the end user , but not for the storage engine .
*/
void Field_varstring::sql_type(String &res) const
{
THD *thd= table->in_use;
CHARSET_INFO *cs=res.charset();
size_t length;
length= cs->cset->snprintf(cs,(char *) res.ptr(),
res.alloced_length(), "%s(%u)" ,
(has_charset() ? "varchar" : "varbinary" ),
(uint) char_length());
res.length(length);
if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
has_charset() && (charset()->state & MY_CS_BINSORT))
res.append(STRING_WITH_LEN(" binary" ));
}
/**
For fields which are associated with character sets their length is provided
in octets and their character set information is also provided as part of
type information .
@ param res String which contains filed type and length .
*/
void Field_varstring::sql_rpl_type(String *res) const
{
if (Field_varstring::has_charset())
{
CHARSET_INFO *cs= res->charset();
DBUG_ASSERT(cs->mbminlen == 1 );
size_t length= cs->cset->snprintf(cs, (char *) res->ptr(),
res->alloced_length(),
"varchar(%u octets) character set %s" ,
field_length,
charset()->cs_name.str);
res->length(length);
}
else
Field_varstring::sql_type(*res);
}
uint32 Field_varstring::data_length()
{
return length_bytes == 1 ? (uint32) *ptr : uint2korr(ptr);
}
/*
Functions to create a packed row .
*/
uchar *Field_varstring::pack (uchar *to, const uchar *from) const
{
uint length= length_bytes == 1 ? (uint) *from : uint2korr(from);
DBUG_ASSERT(length <= field_length);
/* Length always stored little-endian */
*to++= length & 0 xFF;
if (field_length > 255 )
*to++= (length >> 8 ) & 0 xFF;
/* Store bytes of string */
if (length > 0 )
memcpy(to, from+length_bytes, length);
return to+length;
}
/**
Unpack a varstring field from row data .
This method is used to unpack a varstring field from a master
whose size of the field is less than that of the slave .
@ note
The string length is always packed little - endian .
@ param to Destination of the data
@ param from Source of the data
@ param param_data Length bytes from the master ' s field data
@ return New pointer into memory based on from + length of the data
*/
const uchar *
Field_varstring::unpack(uchar *to, const uchar *from, const uchar *from_end,
uint param_data)
{
uint length;
uint l_bytes= (param_data && (param_data < field_length)) ?
(param_data <= 255 ) ? 1 : 2 : length_bytes;
if (from + l_bytes > from_end)
return 0 ; // Error in data
if (l_bytes == 1 )
{
to[0 ]= *from++;
length= to[0 ];
if (length_bytes == 2 )
to[1 ]= 0 ;
}
else /* l_bytes == 2 */
{
length= uint2korr(from);
to[0 ]= *from++;
to[1 ]= *from++;
}
if (length)
{
if (from + length > from_end || length > field_length)
return 0 ; // Error in data
memcpy(to+ length_bytes, from, length);
}
return from+length;
}
uint Field_varstring::packed_col_length() const
{
if (field_length > 255 )
return uint2korr(ptr) + 2 ;
return (uint) *ptr + 1 ;
}
uint Field_varstring::max_packed_col_length(uint max_length) const
{
return (max_length > 255 ? 2 : 1 )+max_length;
}
void Field_varstring::val_str_from_ptr(String *val, const uchar *ptr) const
{
val->set((const char *) get_data(ptr), get_length(ptr), field_charset());
}
uint Field_varstring::get_key_image(uchar *buff, uint length,
const uchar *ptr_arg,
imagetype type_arg) const
{
String val;
val_str_from_ptr(&val, ptr_arg);
uint local_char_length= val.charpos(length / mbmaxlen());
if (local_char_length < val.length())
val.length(local_char_length);
/* Key is always stored with 2 bytes */
int2store(buff, val.length());
memcpy(buff + HA_KEY_BLOB_LENGTH, val.ptr(), val.length());
if (val.length() < length)
{
/*
Must clear this as we do a memcmp in opt_range . cc to detect
identical keys
*/
memset(buff + HA_KEY_BLOB_LENGTH + val.length(), 0 , length - val.length());
}
return HA_KEY_BLOB_LENGTH + val.length();
}
void Field_varstring::set_key_image(const uchar *buff,uint length)
{
length= uint2korr(buff); // Real length is here
(void ) store((const char *) buff + HA_KEY_BLOB_LENGTH, length, field_charset());
}
int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
uint32 max_length) const
{
uint32 a_length,b_length;
if (length_bytes == 1 )
{
a_length= (uint) *a_ptr;
b_length= (uint) *b_ptr;
}
else
{
a_length= uint2korr(a_ptr);
b_length= uint2korr(b_ptr);
}
set_if_smaller(a_length, max_length);
set_if_smaller(b_length, max_length);
if (a_length != b_length)
return 1 ;
return memcmp(a_ptr+length_bytes, b_ptr+length_bytes, a_length);
}
Field *Field_varstring::make_new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type)
{
Field_varstring *res= (Field_varstring*) Field::make_new_field(root,
new_table,
keep_type);
if (res)
res->length_bytes= length_bytes;
return res;
}
Field *Field_varstring_compressed::make_new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type)
{
Field_varstring *res;
if (new_table->s->is_optimizer_tmp_table())
{
/*
Compressed field cannot be part of a key . For optimizer temporary
table we create uncompressed substitute .
*/
res= new (root) Field_varstring(ptr, field_length, length_bytes, null_ptr,
null_bit, Field::NONE, &field_name,
new_table->s, charset());
if (res)
{
res->init_for_make_new_field(new_table, orig_table);
/* See Column_definition::create_length_to_internal_length_string() */
res->field_length--;
}
}
else
res= (Field_varstring*) Field::make_new_field(root, new_table, keep_type);
if (res)
res->length_bytes= length_bytes;
return res;
}
Field *Field_blob_compressed::make_new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type)
{
Field_blob *res;
if (new_table->s->is_optimizer_tmp_table())
{
/*
Compressed field cannot be part of a key . For optimizer temporary
table we create uncompressed substitute .
*/
res= new (root) Field_blob(ptr, null_ptr, null_bit, Field::NONE, &field_name,
new_table->s, packlength, charset());
if (res)
{
res->init_for_make_new_field(new_table, orig_table);
/* See Column_definition::create_length_to_internal_length_string() */
res->field_length--;
}
}
else
res= (Field_blob *) Field::make_new_field(root, new_table, keep_type);
return res;
}
Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uint32 length,
uchar *new_null_ptr, uint new_null_bit)
{
Field_varstring *res;
if ((res= (Field_varstring*) Field::new_key_field(root, new_table,
new_ptr, length,
new_null_ptr, new_null_bit)))
{
/* Keys length prefixes are always packed with 2 bytes */
res->length_bytes= 2 ;
}
return res;
}
bool Field_varstring::is_equal(const Column_definition &new_field) const
{
return (new_field.type_handler() == type_handler() &&
new_field.length == field_length &&
new_field.char_length == char_length() &&
!new_field.compression_method() == !compression_method() &&
new_field.charset->eq_collation(field_charset()));
}
void Field_varstring::hash_not_null(Hasher *hasher)
{
DBUG_ASSERT(marked_for_read());
DBUG_ASSERT(!is_null());
uint len= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
hasher->add(charset(), ptr + length_bytes, len);
}
/**
Compress field
@ param [ out ] to destination buffer for compressed data
@ param [ in ] to_length size of to
@ param [ in ] from data to compress
@ param [ in ] length from length
@ param [ in ] max_length truncate ` from ' to this length
@ param [ out ] out_length compressed data length
@ param [ in ] cs from character set
@ param [ in ] nchars copy no more than " nchars " characters
In worst case ( no compression performed ) storage requirement is increased by
1 byte to store header . If it exceeds field length , normal data truncation is
performed .
Generic compressed header format ( 1 byte ) :
Bits 1 - 4 : method specific bits
Bits 5 - 8 : compression method
If compression method is 0 then header is immediately followed by
uncompressed data .
If compression method is zlib :
Bits 1 - 3 : number of bytes occupied by original data length
Bits 4 : true if zlib wrapper not present
Bits 5 - 8 : store 8 ( zlib )
Header is immediately followed by original data length ,
followed by compressed data .
*/
int Field_longstr::compress(char *to, uint to_length,
const char *from, uint length,
uint max_length,
uint *out_length,
CHARSET_INFO *cs, size_t nchars)
{
THD *thd= get_thd();
char *buf;
uint buf_length;
int rc= 0 ;
if (String::needs_conversion_on_storage(length, cs, field_charset()) ||
max_length < length)
{
set_if_smaller(max_length, static_cast <ulonglong>(mbmaxlen()) * length + 1 );
if (!(buf= (char *) my_malloc(PSI_INSTRUMENT_ME, max_length, MYF(MY_WME))))
{
*out_length= 0 ;
return -1 ;
}
rc= well_formed_copy_with_check(buf, max_length, cs, from, length,
nchars, true , &buf_length);
}
else
{
buf= const_cast <char *>(from);
buf_length= length;
}
if (buf_length == 0 )
*out_length= 0 ;
else if (buf_length >= thd->variables.column_compression_threshold &&
(*out_length= compression_method()->compress(thd, to, buf, buf_length)))
status_var_increment(thd->status_var.column_compressions);
else
{
/* Store uncompressed */
to[0 ]= 0 ;
if (buf_length < to_length)
memmove(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 & 0 xF0) >> 4 ;
/* Uncompressed data */
if (!method)
{
val_ptr->set((const char *) from + 1 , from_length - 1 , field_charset());
return val_ptr;
}
if (compression_methods[method].uncompress)
{
if (!compression_methods[method].uncompress(val_buffer, from, from_length,
field_length))
{
val_buffer->set_charset(field_charset());
status_var_increment(get_thd()->status_var.column_decompressions);
return val_buffer;
}
}
}
/*
It would be better to return 0 in case of errors , but to take the
safer route , let ' s return a zero string and let the general
handler catch the error .
*/
val_ptr->set("" , 0 , field_charset());
return val_ptr;
}
int Field_varstring_compressed::store(const char *from, size_t length,
CHARSET_INFO *cs)
{
DBUG_ASSERT(marked_for_write_or_computed());
uint compressed_length;
int rc= compress((char *) get_data(), field_length, from, (uint) length,
Field_varstring_compressed::max_display_length(),
&compressed_length, cs,
Field_varstring_compressed::char_length());
store_length(compressed_length);
return rc;
}
void Field_varstring_compressed::val_str_from_ptr(String *val, const uchar *ptr) const
{
uncompress(val, val, get_data(ptr), get_length(ptr));
}
String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr)
{
DBUG_ASSERT(marked_for_read());
return uncompress(val_buffer, val_ptr, get_data(), get_length());
}
double Field_varstring_compressed::val_real(void )
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
return Converter_strntod_with_warn(thd, Warn_filter(thd), "DOUBLE" ,
field_charset(),
buf.ptr(), buf.length()).result();
}
longlong Field_varstring_compressed::val_int(void )
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(),
buf.ptr(), buf.length()).result();
}
int Field_varstring_compressed::cmp(const uchar *a_ptr,
const uchar *b_ptr) const
{
String a, b;
uint a_length, b_length;
if (length_bytes == 1 )
{
a_length= (uint) *a_ptr;
b_length= (uint) *b_ptr;
}
else
{
a_length= uint2korr(a_ptr);
b_length= uint2korr(b_ptr);
}
uncompress(&a, &a, a_ptr + length_bytes, a_length);
uncompress(&b, &b, b_ptr + length_bytes, b_length);
return sortcmp(&a, &b, field_charset());
}
Binlog_type_info Field_varstring_compressed::binlog_type_info() const
{
return Binlog_type_info(Field_varstring_compressed::binlog_type(),
field_length, 2 , charset());
}
/****************************************************************************
* * blob type
* * A blob is saved as a length and a pointer . The length is stored in the
* * packlength slot and may be from 1 - 4 .
****************************************************************************/
Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
const LEX_CSTRING *field_name_arg,
TABLE_SHARE *share, uint blob_pack_length,
const DTCollation &collation)
:Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
collation),
packlength(blob_pack_length)
{
DBUG_ASSERT(blob_pack_length <= 4 ); // Only pack lengths 1-4 supported currently
flags|= BLOB_FLAG;
share->blob_fields++;
/* TODO: why do not fill table->s->blob_field array here? */
}
void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number)
const
{
store_lowendian(i_number, i_ptr, i_packlength);
}
uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) const
{
return (uint32)read_lowendian(pos, packlength_arg);
}
/**
Copy a value from another BLOB field of the same character set .
This method is used by Copy_field , e . g . during ALTER TABLE .
*/
int Field_blob::copy_value(Field_blob *from)
{
DBUG_ASSERT(field_charset()->eq_collation(from->charset()));
DBUG_ASSERT(!compression_method() == !from->compression_method());
int rc= 0 ;
uint32 length= from->get_length();
uchar *data= from->get_ptr();
if (packlength < from->packlength)
{
set_if_smaller(length, Field_blob::max_data_length());
length= (uint32) Well_formed_prefix(field_charset(),
(const char *) data, length).length();
rc= report_if_important_data((const char *) data + length,
(const char *) data + from->get_length(),
true );
}
store_length(length);
bmove(ptr + packlength, (uchar*) &data, sizeof (char *));
return rc;
}
int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
{
DBUG_ASSERT(marked_for_write_or_computed());
size_t copy_length, new_length;
uint copy_len;
char *tmp;
char buff[STRING_BUFFER_USUAL_SIZE];
String tmpstr(buff,sizeof (buff), &my_charset_bin);
int rc;
if (!length)
{
bzero(ptr,Field_blob::pack_length());
return 0 ;
}
/*
For min / max fields of statistical data ' table ' is set to NULL .
It could not be otherwise as this data is shared by many instances
of the same base table .
*/
if (table && table->blob_storage) // GROUP_CONCAT with ORDER BY | DISTINCT
{
DBUG_ASSERT(!f_is_hex_escape(flags));
DBUG_ASSERT(field_charset() == cs);
DBUG_ASSERT(length <= max_data_length());
new_length= length;
copy_length= table->in_use->gconcat_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), "DOUBLE" ,
Field_blob::charset(),
blob, get_length(ptr)).result();
}
longlong Field_blob::val_int(void )
{
DBUG_ASSERT(marked_for_read());
char *blob;
memcpy(&blob, ptr+packlength, sizeof (char *));
if (!blob)
return 0 ;
THD *thd= get_thd();
return Converter_strntoll_with_warn(thd, Warn_filter(thd),
Field_blob::charset(),
blob, get_length(ptr)).result();
}
String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
DBUG_ASSERT(marked_for_read());
char *blob;
memcpy(&blob, ptr+packlength, sizeof (char *));
if (!blob)
val_ptr->set("" ,0 ,charset()); // A bit safer than ->length(0)
else
val_ptr->set((const char *) blob,get_length(ptr),charset());
return val_ptr;
}
my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(marked_for_read());
const char *blob;
size_t length;
memcpy(&blob, ptr+packlength, sizeof (const uchar*));
if (!blob)
{
blob= "" ;
length= 0 ;
}
else
length= get_length(ptr);
THD *thd= get_thd();
Converter_str2my_decimal_with_warn(thd, Warn_filter(thd),
E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM,
Field_blob::charset(),
blob, length, decimal_value);
return decimal_value;
}
int Field_blob::cmp(const uchar *a,uint32 a_length, const uchar *b,
uint32 b_length) const
{
return field_charset()->strnncollsp(a, a_length, b, b_length);
}
int Field_blob::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
uchar *blob1,*blob2;
memcpy(&blob1, a_ptr+packlength, sizeof (char *));
memcpy(&blob2, b_ptr+packlength, sizeof (char *));
size_t a_len= get_length(a_ptr), b_len= get_length(b_ptr);
DBUG_ASSERT(blob1 || !a_len);
DBUG_ASSERT(blob2 || !b_len);
if (!blob1) { blob1= (uchar *) "" ; a_len= 0 ; }
if (!blob2) { blob2= (uchar *) "" ; b_len= 0 ; }
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);
if (blob_length > 0 )
{
size_t local_char_length= length / mbmaxlen();
DBUG_ASSERT(blob);
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
{
DBUG_ASSERT(key_ptr);
uchar *blob1;
size_t blob_length=get_length(ptr);
memcpy(&blob1, ptr+packlength, sizeof (char *));
DBUG_ASSERT(blob1 || !blob_length);
if (!blob1) { blob1= (uchar *) "" ; blob_length= 0 ; }
CHARSET_INFO *cs= charset();
size_t local_char_length= max_key_length / cs->mbmaxlen;
local_char_length= cs->charpos(blob1, blob1+blob_length,
local_char_length);
set_if_smaller(blob_length, local_char_length);
return Field_blob::cmp(blob1, (uint32)blob_length,
key_ptr+HA_KEY_BLOB_LENGTH,
uint2korr(key_ptr));
}
int Field_blob::key_cmp(const uchar *a,const uchar *b) const
{
return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a),
b+HA_KEY_BLOB_LENGTH, uint2korr(b));
}
#ifndef DBUG_OFF
/* helper to assert that new_table->blob_storage is NULL */
static struct blob_storage_check
{
union { bool b; intptr p; } val;
blob_storage_check() { val.p= -1 ; val.b= false ; }
} blob_storage_check;
#endif
Field *Field_blob::make_new_field(MEM_ROOT *root, TABLE *newt, bool keep_type)
{
DBUG_ASSERT((intptr(newt->blob_storage) & blob_storage_check.val.p) == 0 );
if (newt->group_concat)
return new (root) Field_blob(field_length, maybe_null(), &field_name,
charset());
return Field::make_new_field(root, newt, keep_type);
}
Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uint32 length,
uchar *new_null_ptr, uint new_null_bit)
{
Field_varstring *res= new (root) Field_varstring(new_ptr, length, 2 ,
new_null_ptr,
new_null_bit, Field::NONE,
&field_name,
table->s, charset());
res->init(new_table);
return res;
}
/**
Save the field metadata for blob fields .
Saves the pack length in the first byte of the field metadata array
at index of * metadata_ptr .
@ param metadata_ptr First byte of field metadata
@ returns number of bytes written to metadata_ptr
*/
Binlog_type_info Field_blob::binlog_type_info() const
{
DBUG_ASSERT(Field_blob::type() == binlog_type());
return Binlog_type_info(Field_blob::type(), pack_length_no_ptr(), 1 ,
charset());
}
uint32 Field_blob::sort_length() const
{
return packlength == 4 ?
UINT_MAX32 :
(uint32) field_length + sort_suffix_length();
}
uint32 Field_blob::sort_suffix_length() const
{
return field_charset() == &my_charset_bin ? packlength : 0 ;
}
void Field_blob::sort_string(uchar *to,uint length)
{
String buf;
val_str(&buf, &buf);
if (!buf.length() && field_charset()->pad_char == 0 )
bzero(to,length);
else
{
if (field_charset() == &my_charset_bin)
{
/*
Store length of blob last in blob to shorter blobs before longer blobs
*/
length-= packlength;
store_bigendian(buf.length(), to + length, packlength);
}
my_strnxfrm_ret_t rc=
field_charset()->strnxfrm(to, length, length,
(const uchar *) buf.ptr(), buf.length(),
MY_STRXFRM_PAD_WITH_SPACE |
MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(rc.m_result_length == length);
if (rc.m_warnings & MY_STRNXFRM_TRUNCATED_WEIGHT_REAL_CHAR)
get_thd()->num_of_strings_sorted_on_truncated_length++;
}
}
/*
Return the data type handler , according to packlength .
Implemented in field . cc rather than in field . h
to avoid exporting type_handler_xxx with MYSQL_PLUGIN_IMPORT .
*/
const Type_handler *Field_blob::type_handler() const
{
/*
This is a temporary solution and will be fixed soon ( in 10 . 9 ? ) .
Type_handler_ * blob_json will provide its own Field_blob_json
and Field_blob_compressed_json .
*/
if (Type_handler_json_common::has_json_valid_constraint(this ))
return Type_handler_json_common::
json_blob_type_handler_by_length_bytes(packlength);
switch (packlength) {
case 1 : return &type_handler_tiny_blob;
case 2 : return &type_handler_blob;
case 3 : return &type_handler_medium_blob;
}
return &type_handler_long_blob;
}
void Field_blob::sql_type(String &res) const
{
const char *str;
uint length;
switch (packlength) {
default : str="tiny" ; length=4 ; break ;
case 2 : str="" ; length=0 ; break ;
case 3 : str="medium" ; length= 6 ; break ;
case 4 : str="long" ; length=4 ; break ;
}
res.set_ascii(str,length);
if (charset() == &my_charset_bin)
{
res.append(STRING_WITH_LEN("blob" ));
if (packlength == 2 && (get_thd()->variables.sql_mode & MODE_ORACLE))
res.append(STRING_WITH_LEN("(65535)" ));
}
else
{
res.append(STRING_WITH_LEN("text" ));
}
}
uchar *Field_blob::pack (uchar *to, const uchar *from) const
{
uint32 length=get_length(from, packlength); // Length of from string
/*
Store max length , which will occupy packlength bytes . If the max
length given is smaller than the actual length of the blob , we
just store the initial bytes of the blob .
*/
store_length(to, packlength, length);
/*
Store the actual blob data , which will occupy ' length ' bytes .
*/
if (length > 0 )
{
from= get_ptr(from);
memcpy(to+packlength, from,length);
}
return to+packlength+length;
}
/**
Unpack a blob field from row data .
This method is used to unpack a blob field from a master whose size of
the field is less than that of the slave . Note : This method is included
to satisfy inheritance rules , but is not needed for blob fields . It
simply is used as a pass - through to the original unpack ( ) method for
blob fields .
@ param to Destination of the data
@ param from Source of the data
@ param param_data @ c TRUE if base types should be stored in little -
endian format , @ c FALSE if native format should
be used .
@ return New pointer into memory based on from + length of the data
*/
const uchar *Field_blob::unpack(uchar *to, const uchar *from,
const uchar *from_end, uint param_data)
{
DBUG_ENTER("Field_blob::unpack" );
DBUG_PRINT("enter" , ("to: %p; from: %p; param_data: %u" ,
to, from, param_data));
uint const master_packlength=
param_data > 0 ? param_data & 0 xFF : packlength;
if (from + master_packlength > from_end)
DBUG_RETURN(0 ); // Error in data
uint32 const length= get_length(from, master_packlength);
DBUG_DUMP("packed" , from, length + master_packlength);
if (from + master_packlength + length > from_end)
DBUG_RETURN(0 );
set_ptr(length, const_cast <uchar*> (from) + master_packlength);
DBUG_RETURN(from + master_packlength + length);
}
uint Field_blob::packed_col_length() const
{
return (uint) read_lowendian(ptr, packlength) + packlength;
}
uint Field_blob::max_packed_col_length(uint max_length) const
{
return (max_length > 255 ? 2 : 1 )+max_length;
}
/*
Blob fields are regarded equal if they have same character set ,
same blob store length and if either both are compressed or both are
uncompressed .
The logic for compression is that we don ' t have to uncompress and compress
again an already compressed field just because compression method changes .
*/
bool Field_blob::is_equal(const Column_definition &new_field) const
{
return (new_field.type_handler() == type_handler() &&
!new_field.compression_method() == !compression_method() &&
new_field.pack_length == pack_length() &&
new_field.charset->eq_collation(field_charset()));
}
void Field_blob::make_send_field(Send_field *field)
{
/*
Historically all BLOB variant Fields are displayed as MYSQL_TYPE_BLOB
in the result set metadata . Note , Item can work differently and
display the exact BLOB type , such as
MYSQL_TYPE_ { TINY_BLOB | BLOB | MEDIUM_BLOB | LONG_BLOB } .
QQ : this should be made consistent eventually .
*/
Field_longstr::make_send_field(field);
field->set_handler(&type_handler_blob);
}
bool Field_blob::make_empty_rec_store_default_value(THD *thd, Item *item)
{
DBUG_ASSERT(flags & BLOB_FLAG);
int res= item->save_in_field(this , true );
DBUG_ASSERT(res != 3 ); // Field_blob never returns 3
if (res)
return true ; // E.g. truncation happened
reset(); // Clear the pointer to a String, it should not be written to frm
return false ;
}
int Field_blob_compressed::store(const char *from, size_t length,
CHARSET_INFO *cs)
{
DBUG_ASSERT(marked_for_write_or_computed());
uint compressed_length;
uint max_length= max_data_length();
uint to_length= (uint) MY_MIN(max_length, mbmaxlen() * length + 1 );
String tmp(from, length, cs);
int rc;
if (from >= value.ptr() && from <= value.end() && tmp.copy(from, length, cs))
goto oom;
if (value.alloc(to_length))
goto oom;
rc= compress((char *) value.ptr(), to_length, tmp.ptr(), (uint) length,
max_length, &compressed_length, cs, (uint) length);
set_ptr(compressed_length, (uchar*) value.ptr());
return rc;
oom:
set_ptr((uint32) 0 , NULL);
return -1 ;
}
String *Field_blob_compressed::val_str(String *val_buffer, String *val_ptr)
{
DBUG_ASSERT(marked_for_read());
return uncompress(val_buffer, val_ptr, get_ptr(), get_length());
}
double Field_blob_compressed::val_real(void )
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
return Converter_strntod_with_warn(thd, Warn_filter(thd), "DOUBLE" ,
field_charset(),
buf.ptr(), buf.length()).result();
}
longlong Field_blob_compressed::val_int(void )
{
DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(),
buf.ptr(), buf.length()).result();
}
Binlog_type_info Field_blob_compressed::binlog_type_info() const
{
return Binlog_type_info(Field_blob_compressed::binlog_type(),
pack_length_no_ptr(), 1 , charset());
}
/****************************************************************************
* * enum type .
* * This is a string which only can have a selection of different values .
* * If one uses this string in a number context one gets the type number .
****************************************************************************/
sql_mode_t Field_enum::can_handle_sql_mode_dependency_on_store() const
{
return MODE_PAD_CHAR_TO_FULL_LENGTH;
}
enum ha_base_keytype Field_enum::key_type() const
{
switch (packlength) {
default : return HA_KEYTYPE_BINARY;
case 2 : return HA_KEYTYPE_USHORT_INT;
case 3 : return HA_KEYTYPE_UINT24;
case 4 : return HA_KEYTYPE_ULONG_INT;
case 8 : return HA_KEYTYPE_ULONGLONG;
}
}
void Field_enum::store_type(ulonglong value)
{
store_lowendian(value, ptr, packlength);
}
/**
@ note
Storing a empty string in a enum field gives a warning
( if there isn ' t a empty value in the enum )
*/
int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs)
{
DBUG_ASSERT(marked_for_write_or_computed());
int err= 0 ;
char buff[STRING_BUFFER_USUAL_SIZE];
String tmpstr(buff,sizeof (buff), &my_charset_bin);
/* Convert character set if necessary */
if (String::needs_conversion_on_storage(length, cs, field_charset()))
{
uint dummy_errors;
tmpstr.copy(from, length, cs, field_charset(), &dummy_errors);
from= tmpstr.ptr();
length= tmpstr.length();
}
/* Remove end space */
length= (uint) field_charset()->lengthsp(from, length);
uint tmp= find_type2(typelib(), from, length, 0 , field_charset());
if (!tmp)
{
if (length < 6 ) // Can't be more than 99999 enums
{
/* This is for reading numbers with LOAD DATA INFILE */
char *end;
tmp=(uint) cs->strntoul(from,length,10 ,&end,&err);
if (err || end != from + length || !tmp || 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_attr(), 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)
{
DBUG_ASSERT(m_typelib_attr);
Field_enum *res= (Field_enum*) Field::make_new_field(root, new_table,
keep_type);
if (!res ||
!(res->m_typelib_attr= m_typelib_attr->deep_copy(root)))
return nullptr;
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= (1 ULL << 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_attr());
}
/**
@ 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
{
/*
The fields are compatible if they have the same flags ,
type , charset and have the same underlying length .
*/
if (new_field.type_handler() != type_handler() ||
!new_field.charset->eq_collation(field_charset()) ||
new_field.pack_length != pack_length())
return false ;
/*
Changing the definition of an ENUM or SET column by adding a new
enumeration or set members to the end of the list of valid member
values only alters table metadata and not table data .
*/
if (typelib()->count > new_field.typelib()->count)
return false ;
/* Check whether there are modification before the end. */
if (! compare_type_names(field_charset(), typelib(), new_field.typelib()))
return false ;
return true ;
}
uchar *Field_enum::pack (uchar *to, const uchar *from) const
{
DBUG_ENTER("Field_enum::pack" );
DBUG_PRINT("debug" , ("packlength: %d" , packlength));
DBUG_DUMP("from" , from, packlength);
DBUG_RETURN(pack_int(to, from, packlength));
}
const uchar *Field_enum::unpack(uchar *to, const uchar *from,
const uchar *from_end, uint param_data)
{
DBUG_ENTER("Field_enum::unpack" );
DBUG_PRINT("debug" , ("packlength: %d" , packlength));
DBUG_DUMP("from" , from, packlength);
DBUG_RETURN(unpack_int(to, from, from_end, packlength));
}
/**
@ return
returns 1 if the fields are equally defined
*/
bool Field_num::eq_def(const Field *field) const
{
if (!Field::eq_def(field))
return 0 ;
Field_num *from_num= (Field_num*) field;
if (unsigned_flag != from_num->unsigned_flag ||
(zerofill && !from_num->zerofill && !zero_pack()) ||
dec != from_num->dec)
return 0 ;
return 1 ;
}
/**
Check whether two numeric fields can be considered ' equal ' for table
alteration purposes . Fields are equal if they are of the same type
and retain the same pack length .
*/
bool Field_num::is_equal(const Column_definition &new_field) const
{
if (((new_field.flags & UNSIGNED_FLAG) != (flags & UNSIGNED_FLAG)) ||
((new_field.flags & AUTO_INCREMENT_FLAG) > (flags & AUTO_INCREMENT_FLAG)))
return false ;
const Type_handler *th= type_handler(), *new_th = new_field.type_handler();
if (th == new_th && new_field.pack_length == pack_length())
return true ;
/* FIXME: Test and consider returning true for the following:
TINYINT UNSIGNED to BIT ( 8 )
SMALLINT UNSIGNED to BIT ( 16 )
MEDIUMINT UNSIGNED to BIT ( 24 )
INT UNSIGNED to BIT ( 32 )
BIGINT UNSIGNED to BIT ( 64 )
BIT ( 1 . . 7 ) to TINYINT , or BIT ( 1 . . 8 ) to TINYINT UNSIGNED
BIT ( 9 . . 15 ) to SMALLINT , or BIT ( 9 . . 16 ) to SMALLINT UNSIGNED
BIT ( 17 . . 23 ) to MEDIUMINT , or BIT ( 17 . . 24 ) to MEDIUMINT UNSIGNED
BIT ( 25 . . 31 ) to INT , or BIT ( 25 . . 32 ) to INT UNSIGNED
BIT ( 57 . . 63 ) to BIGINT , or BIT ( 57 . . 64 ) to BIGINT UNSIGNED
Note : InnoDB stores integers in big - endian format , and BIT appears
to use big - endian format . For storage engines that use little - endian
format for integers , we can only return true for the TINYINT
conversion. */
return false ;
}
Data_type_compatibility
Field_enum::can_optimize_range_or_keypart_ref(const Item_bool_func *cond,
const Item *item) const
{
switch (item->cmp_type())
{
case TIME_RESULT:
return Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
case INT_RESULT:
case DECIMAL_RESULT:
case REAL_RESULT:
return Data_type_compatibility::OK;
case STRING_RESULT:
return (charset()->eq_collation(cond->compare_collation()) ?
Data_type_compatibility::OK :
Data_type_compatibility::INCOMPATIBLE_COLLATION);
case ROW_RESULT:
DBUG_ASSERT(0 );
break ;
}
return Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
}
/*
Bit field .
We store the first 0 - 6 uneven bits among the null bits
at the start of the record . The rest bytes are stored in
the record itself .
For example :
CREATE TABLE t1 ( a int , b bit ( 17 ) , c bit ( 21 ) not null , d bit ( 8 ) ) ;
We would store data as follows in the record :
Byte Bit
1 7 - reserve for delete
6 - null bit for ' a '
5 - null bit for ' b '
4 - first ( high ) bit of ' b '
3 - first ( high ) bit of ' c '
2 - second bit of ' c '
1 - third bit of ' c '
0 - fourth bit of ' c '
2 7 - fifth bit of ' c '
6 - null bit for ' d '
3 - 6 four bytes for ' a '
7 - 8 two bytes for ' b '
9 - 10 two bytes for ' c '
11 one byte for ' d '
*/
Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
enum utype unireg_check_arg,
const LEX_CSTRING *field_name_arg)
: Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg),
bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7 ),
bytes_in_rec(len_arg / 8 )
{
DBUG_ENTER("Field_bit::Field_bit" );
DBUG_PRINT("enter" , ("ptr_arg: %p, null_ptr_arg: %p, len_arg: %u, bit_len: %u, bytes_in_rec: %u" ,
ptr_arg, null_ptr_arg, len_arg, bit_len, bytes_in_rec));
flags|= UNSIGNED_FLAG;
/*
Ensure that Field : : eq ( ) can distinguish between two different bit fields .
( two bit fields that are not null , may have same ptr and null_ptr )
*/
if (!null_ptr_arg)
null_bit= bit_ofs_arg;
DBUG_VOID_RETURN;
}
const DTCollation & Field_bit::dtcollation() const
{
static DTCollation tmp(&my_charset_bin,
DERIVATION_IMPLICIT, MY_REPERTOIRE_UNICODE30);
return tmp;
}
/*
This method always calculates hash over 8 bytes .
This is different from how the HEAP engine calculate hash :
HEAP takes into account the actual octet size , so say for BIT ( 18 )
it calculates hash over three bytes only :
- the incomplete byte with bits 16 . . 17
- the two full bytes with bits 0 . . 15
See hp_rec_hashnr ( ) , hp_hashnr ( ) for details .
The HEAP way is more efficient , especially for short lengths .
Let ' s consider fixing Field_bit eventually to do it in the HEAP way ,
with proper measures to upgrade partitioned tables easy .
*/
void Field_bit::hash_not_null(Hasher *hasher)
{
DBUG_ASSERT(marked_for_read());
DBUG_ASSERT(!is_null());
longlong value= Field_bit::val_int();
uchar tmp[8 ];
mi_int8store(tmp,value);
hasher->add(&my_charset_bin, tmp, 8 );
}
size_t
Field_bit::do_last_null_byte() const
{
/*
Code elsewhere is assuming that bytes are 8 bits , so I ' m using
that value instead of the correct one : CHAR_BIT .
REFACTOR SUGGESTION ( Matz ) : Change to use the correct number of
bits . On systems with CHAR_BIT > 8 ( not very common ) , the storage
will lose the extra bits .
*/
DBUG_PRINT("test" , ("bit_ofs: %d, bit_len: %d bit_ptr: %p" ,
bit_ofs, bit_len, bit_ptr));
uchar *result;
if (bit_len == 0 )
result= null_ptr;
else if (bit_ofs + bit_len > 8 )
result= bit_ptr + 1 ;
else
result= bit_ptr;
if (result)
return (size_t) (result - table->record[0 ]) + 1 ;
return LAST_NULL_BYTE_UNDEF;
}
Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uint32 length,
uchar *new_null_ptr, uint new_null_bit)
{
Field_bit *res;
if ((res= (Field_bit*) Field::new_key_field(root, new_table, new_ptr, length,
new_null_ptr, new_null_bit)))
{
/* Move bits normally stored in null_pointer to new_ptr */
res->bit_ptr= new_ptr;
res->bit_ofs= 0 ;
if (bit_len)
res->ptr++; // Store rest of data here
}
return res;
}
bool Field_bit::is_equal(const Column_definition &new_field) const
{
return new_field.type_handler() == type_handler() &&
new_field.length == max_display_length();
}
int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs)
{
DBUG_ASSERT(marked_for_write_or_computed());
int delta;
for (; length && !*from; from++, length--) // skip left 0's
;
delta= (int )(bytes_in_rec - length);
if (delta < -1 ||
(delta == -1 && (uchar) *from > ((1 << bit_len) - 1 )) ||
(!bit_len && delta < 0 ))
{
set_rec_bits((1 << bit_len) - 1 , bit_ptr, bit_ofs, bit_len);
memset(ptr, 0 xff, 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 >> 8 U) & 0 x00ff;
uint const from_bit_len= field_metadata & 0 x00ff;
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 & 0 xff);
uint to_bit_len= max_display_length();
DBUG_PRINT("debug" , ("from_bit_len: %u, to_bit_len: %u" ,
from_bit_len, to_bit_len));
/*
If the bit length exact flag is clear , we are dealing with an old
master , so we allow some less strict behaviour if replicating by
moving both bit lengths to an even multiple of 8 .
We do this by computing the number of bytes to store the field
instead , and then compare the result .
*/
if (!(mflags & Table_map_log_event::TM_BIT_LEN_EXACT_F)) {
from_bit_len= (from_bit_len + 7 ) / 8 ;
to_bit_len= (to_bit_len + 7 ) / 8 ;
}
*order_var= compare(from_bit_len, to_bit_len);
DBUG_RETURN(TRUE );
}
void Field_bit::sql_type(String &res) const
{
CHARSET_INFO *cs= res.charset();
size_t length= cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
"bit(%d)" , (int ) field_length);
res.length(length);
}
uchar *
Field_bit::pack (uchar *to, const uchar *from) const
{
if (bit_len > 0 )
{
/*
We have the following :
ptr Points into a field in record R1
from Points to a field in a record R2
bit_ptr Points to the byte ( in the null bytes ) that holds the
odd bits of R1
from_bitp Points to the byte that holds the odd bits of R2
We have the following :
ptr - bit_ptr = from - from_bitp
We want to isolate ' from_bitp ' , so this gives :
ptr - bit_ptr - from = - from_bitp
- ptr + bit_ptr + from = from_bitp
bit_ptr + from - ptr = from_bitp
*/
uchar bits= get_rec_bits(bit_ptr + (from - ptr), bit_ofs, bit_len);
*to++= bits;
}
memcpy(to, from, bytes_in_rec);
return to + bytes_in_rec;
}
/**
Unpack a bit field from row data .
This method is used to unpack a bit field from a master whose size
of the field is less than that of the slave .
@ param to Destination of the data
@ param from Source of the data
@ param param_data Bit length ( upper ) and length ( lower ) values
@ return New pointer into memory based on from + length of the data
*/
const uchar *
Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end,
uint param_data)
{
DBUG_ENTER("Field_bit::unpack" );
DBUG_PRINT("enter" , ("to: %p, from: %p, param_data: 0x%x" ,
to, from, param_data));
DBUG_PRINT("debug" , ("bit_ptr: %p, bit_len: %u, bit_ofs: %u" ,
bit_ptr, bit_len, bit_ofs));
uint const from_len= (param_data >> 8 U) & 0 x00ff;
uint const from_bit_len= param_data & 0 x00ff;
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] & ((1 U << 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, 0 xff, bytes_in_rec);
if (bits)
*ptr&= ((1 << bits) - 1 ); /* set first uchar */
if (get_thd()->really_abort_on_warning())
set_warning(ER_DATA_TOO_LONG, 1 );
else
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1 );
return 1 ;
}
bzero(ptr, delta);
memcpy(ptr + delta, from, length);
return 0 ;
}
void Field_bit_as_char::sql_type(String &res) const
{
CHARSET_INFO *cs= res.charset();
size_t length= cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
"bit(%d)" , (int ) field_length);
res.length(length);
}
/*****************************************************************************
Handling of field and Create_field
*****************************************************************************/
bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
bool reuse_interval_list_values)
{
DBUG_ENTER("Column_definition::create_interval_from_interval_list" );
DBUG_ASSERT(!typelib());
Type_typelib_attributes *tmpint= new (mem_root) Type_typelib_attributes();
if (!tmpint)
DBUG_RETURN(true ); // EOM
Type_typelib_ptr_attributes(tmpint).save_in_type_extra_attributes(this );
List_iterator<String> it(interval_list);
StringBuffer<64 > conv;
char comma_buf[5 ]; /* 5 bytes for 'filename' charset */
DBUG_ASSERT(sizeof (comma_buf) >= charset->mbmaxlen);
int comma_length= charset->wc_mb(',' ,
(uchar*) comma_buf,
(uchar*) comma_buf + sizeof (comma_buf));
DBUG_ASSERT(comma_length >= 0 && comma_length <= (int ) sizeof (comma_buf));
if (!multi_alloc_root(mem_root,
&tmpint->type_names,
sizeof (char *) * (interval_list.elements + 1 ),
&tmpint->type_lengths,
sizeof (uint) * (interval_list.elements + 1 ),
NullS))
goto err; // EOM
tmpint->name= "" ;
tmpint->count= interval_list.elements;
for (uint i= 0 ; i < interval_list.elements; i++)
{
uint32 dummy;
String *tmp= it++;
LEX_CSTRING value;
if (String::needs_conversion(tmp->length(), tmp->charset(),
charset, &dummy))
{
uint cnv_errs;
conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), charset, &cnv_errs);
value.str= strmake_root(mem_root, conv.ptr(), conv.length());
value.length= conv.length();
}
else
{
value.str= reuse_interval_list_values ? tmp->ptr() :
strmake_root(mem_root,
tmp->ptr(),
tmp->length());
value.length= tmp->length();
}
if (!value.str)
goto err; // EOM
// Strip trailing spaces.
value.length= charset->lengthsp(value.str, value.length);
((char *) value.str)[value.length]= '\0' ;
if (real_field_type() == MYSQL_TYPE_SET)
{
if (charset->instr(value.str, value.length,
comma_buf, comma_length, NULL, 0 ))
{
ErrConvString err(tmp);
my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0 ), "set" , err.ptr());
goto err;
}
}
tmpint->type_names[i]= value.str;
tmpint->type_lengths[i]= (uint)value.length;
}
tmpint->type_names[interval_list.elements]= 0 ; // End marker
tmpint->type_lengths[interval_list.elements]= 0 ;
interval_list.empty(); // Don't need interval_list anymore
DBUG_RETURN(false );
err:
// Avoid having both non-empty interval_list and the typelib attribute
Type_typelib_ptr_attributes(nullptr).save_in_type_extra_attributes(this );
DBUG_RETURN(true );
}
bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root,
bool reuse_interval_list_values)
{
DBUG_ENTER("Column_definition::prepare_interval_field" );
DBUG_ASSERT(real_field_type() == MYSQL_TYPE_ENUM ||
real_field_type() == MYSQL_TYPE_SET);
/*
Interval values are either in " interval " or in " interval_list " ,
but not in both at the same time , and are not empty at the same time .
- Values are in " interval_list " when we ' re coming from the parser
in CREATE TABLE or in CREATE { FUNCTION | PROCEDURE } .
- Values are in " interval " when we ' re in ALTER TABLE .
In a corner case with an empty set like SET ( ' ' ) :
- after the parser we have interval_list . elements = = 1
- in ALTER TABLE we have a non - NULL interval with interval - > count = = 1 ,
with interval - > type_names [ 0 ] = = " " and interval - > type_lengths [ 0 ] = = 0 .
So the assert is still valid for this corner case .
ENUM and SET with no values at all ( e . g . ENUM ( ) , SET ( ) ) are not possible ,
as the parser requires at least one element , so for a ENUM or SET field it
should never happen that both internal_list . elements and interval are 0 .
*/
DBUG_ASSERT((typelib() == NULL) == (interval_list.elements > 0 ));
/*
Create typelib from interval_list , and if necessary
convert strings from client character set to the
column character set .
*/
if (interval_list.elements &&
create_interval_from_interval_list(mem_root,
reuse_interval_list_values))
DBUG_RETURN(true );
if (!reuse_interval_list_values)
{
/*
We ' re initializing from an existing table or view Field_enum
( e . g . for a % TYPE variable ) rather than from the parser .
The constructor Column_definition ( THD * , Field * , Field * ) has already
copied the TYPELIB pointer from the original Field_enum .
Now we need to make a permanent copy of that TYPELIB ,
as the original field can be freed before the end of the life
cycle of " this " .
*/
DBUG_ASSERT(typelib());
const Type_typelib_ptr_attributes typelib_ptr_attr(
typelib_attr()->deep_copy(mem_root));
if (!typelib_ptr_attr.typelib_attr())
DBUG_RETURN(true ); // EOM
typelib_ptr_attr.save_in_type_extra_attributes(this );
}
prepare_interval_field_calc_length();
DBUG_RETURN(false );
}
bool Column_definition::set_attributes(THD *thd,
const Lex_field_type_st &def,
column_definition_type_t type)
{
DBUG_ASSERT(type_handler() == &type_handler_null);
DBUG_ASSERT(length == 0 );
DBUG_ASSERT(decimals == 0 );
set_handler(def.type_handler());
return type_handler()->Column_definition_set_attributes(thd, this ,
def, type);
}
void
Column_definition_attributes::set_length_and_dec(const Lex_length_and_dec_st
&type)
{
if (type.has_explicit_length())
length= type.length_overflowed() ? (ulonglong) UINT_MAX32 + 1 :
(ulonglong) type.length();
if (type.has_explicit_dec())
decimals= type.dec();
}
void Column_definition::create_length_to_internal_length_bit()
{
if (f_bit_as_char(pack_flag))
{
pack_length= ((length + 7 ) & ~7 ) / 8 ;
}
else
{
pack_length= (uint) length / 8 ;
}
}
void Column_definition::create_length_to_internal_length_newdecimal()
{
DBUG_ASSERT(length < UINT_MAX32);
decimal_digit_t prec= get_decimal_precision((uint)length, decimals,
flags & UNSIGNED_FLAG);
pack_length= my_decimal_get_binary_size(prec, decimals);
}
bool check_expression(Virtual_column_info *vcol, const Lex_ident_column &name,
enum_vcol_info_type type, Alter_info *alter_info)
{
bool ret;
Item::vcol_func_processor_result res;
res.alter_info= alter_info;
if (!vcol->name.length)
vcol->name= name;
/*
Walk through the Item tree checking if all items are valid
to be part of the virtual column
*/
ret= vcol->expr->walk(&Item::check_vcol_func_processor, &res, 0 );
vcol->flags= res.errors;
uint filter= VCOL_IMPOSSIBLE;
if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT)
filter|= VCOL_NOT_STRICTLY_DETERMINISTIC;
if (type != VCOL_DEFAULT)
filter|= VCOL_NEXTVAL;
if (unlikely(ret || (res.errors & filter)))
{
my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0 ), res.name,
vcol_type_name(type), name.str);
return TRUE ;
}
/*
Safe to call before fix_fields as long as vcol ' s don ' t include sub
queries ( which is now checked in check_vcol_func_processor )
*/
if (vcol->expr->check_cols(1 ))
return TRUE ;
return FALSE ;
}
bool Column_definition::check_length(uint mysql_errno, uint limit) const
{
if (length <= limit)
return false ;
my_error(mysql_errno, MYF(0 ), field_name.str, static_cast <ulong>(limit));
return true ;
}
bool Column_definition::fix_attributes_int(uint default_length)
{
if (length)
return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
length= default_length;
return false ;
}
bool Column_definition::fix_attributes_real(uint default_length)
{
/* change FLOAT(precision) to FLOAT or DOUBLE */
if (!length && !decimals)
{
length= default_length;
decimals= NOT_FIXED_DEC;
}
if (length < decimals && decimals != NOT_FIXED_DEC)
{
my_error(ER_M_BIGGER_THAN_D, MYF(0 ), field_name.str);
return true ;
}
if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS)
{
my_error(ER_TOO_BIG_SCALE, MYF(0 ),
field_name.str, static_cast <uint>(FLOATING_POINT_DECIMALS-1 ));
return true ;
}
return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
}
bool Column_definition::fix_attributes_decimal()
{
if (decimals >= NOT_FIXED_DEC)
{
my_error(ER_TOO_BIG_SCALE, MYF(0 ),
field_name.str, static_cast <uint>(NOT_FIXED_DEC - 1 ));
return true ;
}
my_decimal_trim(&length, &decimals);
if (length > DECIMAL_MAX_PRECISION)
{
my_error(ER_TOO_BIG_PRECISION, MYF(0 ), field_name.str,
DECIMAL_MAX_PRECISION);
return true ;
}
if (length < decimals)
{
my_error(ER_M_BIGGER_THAN_D, MYF(0 ), field_name.str);
return true ;
}
length= my_decimal_precision_to_length((uint) length, decimals,
flags & UNSIGNED_FLAG);
pack_length= my_decimal_get_binary_size((uint) length, decimals);
return false ;
}
bool Column_definition::fix_attributes_bit()
{
if (!length)
length= 1 ;
pack_length= ((uint) length + 7 ) / 8 ;
return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_BIT_FIELD_LENGTH);
}
bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length)
{
if (length > MAX_DATETIME_PRECISION)
{
my_error(ER_TOO_BIG_PRECISION, MYF(0 ), field_name.str,
MAX_DATETIME_PRECISION);
return true ;
}
decimals= (uint) length;
length+= int_part_length + (length ? 1 : 0 );
return false ;
}
bool Column_definition::validate_check_constraint(THD *thd)
{
return check_constraint &&
check_expression(check_constraint, field_name, VCOL_CHECK_FIELD);
}
bool Column_definition::check(THD *thd)
{
DBUG_ENTER("Column_definition::check" );
/* Initialize data for a computed field */
if (vcol_info)
{
DBUG_ASSERT(vcol_info->expr);
vcol_info->set_handler(type_handler());
if (check_expression(vcol_info, field_name, vcol_info->is_stored()
? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL))
DBUG_RETURN(TRUE );
}
if (type_handler()->Column_definition_validate_check_constraint(thd, this ))
DBUG_RETURN(TRUE );
if (default_value)
{
Item *def_expr= default_value->expr;
if (check_expression(default_value, field_name, VCOL_DEFAULT))
DBUG_RETURN(TRUE );
/* Constant's are stored in the 'empty_record', except for blobs */
if (def_expr->basic_const_item())
{
if (def_expr->type() == Item::NULL_ITEM)
{
default_value= 0 ;
if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG)
{
my_error(ER_INVALID_DEFAULT, MYF(0 ), field_name.str);
DBUG_RETURN(1 );
}
}
}
}
if (default_value && (flags & AUTO_INCREMENT_FLAG))
{
my_error(ER_INVALID_DEFAULT, MYF(0 ), field_name.str);
DBUG_RETURN(1 );
}
if (default_value && !default_value->expr->basic_const_item() &&
mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME &&
default_value->expr->type() == Item::FUNC_ITEM)
{
/*
Special case : NOW ( ) for TIMESTAMP and DATETIME fields are handled
as in MariaDB 10 . 1 by marking them in unireg_check .
*/
Item_func *fn= static_cast <Item_func*>(default_value->expr);
if (fn->functype() == Item_func::NOW_FUNC &&
(fn->decimals == 0 || fn->decimals >= decimals))
{
default_value= 0 ;
unireg_check= Field::TIMESTAMP_DN_FIELD;
}
}
if (on_update)
{
if (mysql_timestamp_type() != MYSQL_TIMESTAMP_DATETIME ||
on_update->decimals < decimals)
{
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;
/* 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 < 256 L*256 L*256 L)
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, typelib_attr: %p, "
"pack_flag: %s%s%s%s%s" ,
handler->name().ptr(), (uint) length, typelib_attr(),
FLAGSTR(pack_flag, FIELDFLAG_BINARY),
FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
FLAGSTR(pack_flag, FIELDFLAG_PACK),
FLAGSTR(pack_flag, FIELDFLAG_BLOB)));
Record_addr addr(rec->ptr(), f_maybe_null(pack_flag) ? rec->null() :
Bit_addr());
/*
Special code for the BIT - alike data types
who store data bits together with NULL - bits .
*/
Bit_addr bit(rec->null());
if (f_maybe_null(pack_flag))
bit.inc();
return handler->make_table_field_from_def(share, mem_root, field_name,
addr, bit, this , flags);
}
bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const
{
return item->is_of_type(Item::CONST_ITEM, TIME_RESULT);
}
Column_definition_attributes::Column_definition_attributes(const Field *field)
:length(field->character_octet_length() / field->charset()->mbmaxlen),
charset(field->charset()), // May be NULL ptr
pack_flag(0 ),
decimals(field->decimals()),
unireg_check(field->unireg_check)
{}
Column_definition_attributes::
Column_definition_attributes(const Type_all_attributes &attr)
:Type_extra_attributes(attr.type_extra_attributes()),
length(attr.max_length),
charset(attr.collation.collation),
pack_flag(attr.unsigned_flag ? 0 : FIELDFLAG_DECIMAL),
decimals(attr.decimals),
unireg_check(Field::NONE)
{}
/** Create a field suitable for create of table. */
Column_definition::Column_definition(THD *thd, Field *old_field,
Field *orig_field)
:Column_definition_attributes(old_field)
{
on_update= NULL;
field_name= old_field->field_name;
flags= old_field->flags;
pack_length=old_field->pack_length();
set_handler(old_field->type_handler());
comment= old_field->comment;
vcol_info= old_field->vcol_info;
option_list= old_field->option_list;
explicitly_nullable= !(old_field->flags & NOT_NULL_FLAG);
compression_method_ptr= 0 ;
versioning= VERSIONING_NOT_SET;
invisible= old_field->invisible;
interval_list.empty(); // prepare_interval_field() needs this
char_length= (uint) length;
if (orig_field)
{
default_value= orig_field->default_value;
check_constraint= orig_field->check_constraint;
if (orig_field->unireg_check == Field::TMYSQL_COMPRESSED)
{
unireg_check= Field::TMYSQL_COMPRESSED;
compression_method_ptr= zlib_compression_method;
}
}
else
{
default_value= 0 ;
check_constraint= 0 ;
}
type_handler()->Column_definition_reuse_fix_attributes(thd, this , old_field);
/*
Copy the default ( constant / function ) from the column object orig_field , if
supplied . We do this if all these conditions are met :
- The column allows a default .
- The column type is not a BLOB type ( as BLOB ' s doesn ' t have constant
defaults )
- The original column ( old_field ) was properly initialized with a record
buffer pointer .
- The column didn ' t have a default expression
*/
if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
old_field->ptr != NULL && orig_field != NULL)
{
if (orig_field->unireg_check != Field::NEXT_NUMBER)
unireg_check= orig_field->unireg_check;
/* Get the value from default_values */
const uchar *dv= orig_field->table->s->default_values;
if (!default_value && !orig_field->is_null_in_record(dv))
{
StringBuffer<MAX_FIELD_WIDTH> tmp(charset);
String *res= orig_field->val_str(&tmp, orig_field->ptr_in_record(dv));
char *pos= (char *) thd->strmake(res->ptr(), res->length());
default_value= new (thd->mem_root) Virtual_column_info();
default_value->expr=
new (thd->mem_root) Item_string(thd, pos, res->length(), charset);
default_value->utf8= 0 ;
}
}
}
/**
The common part for data type redefinition :
CREATE TABLE t1 ( a INT ) AS SELECT a FROM t2 ;
See Type_handler : : Column_definition_redefine_stage1 ( )
for data type specific code .
@ param this - The field definition corresponding to the expression
in the " AS SELECT . . " part .
@ param dup_field - The field definition from the " CREATE TABLE ( . . . ) " part .
It has already underwent prepare_stage1 ( ) , so
must be fully initialized :
- - dup_field - > charset is set and BINARY
sorting style is applied , see find_bin_collation ( ) .
@ param file - The table handler
*/
void
Column_definition::redefine_stage1_common(const Column_definition *dup_field,
const handler *file)
{
set_handler(dup_field->type_handler());
Type_extra_attributes::operator =(*dup_field);
default_value= dup_field->default_value;
DBUG_ASSERT(dup_field->charset); // Set by prepare_stage1()
charset= dup_field->charset;
length= dup_field->char_length;
pack_length= dup_field->pack_length;
decimals= dup_field->decimals;
unireg_check= dup_field->unireg_check;
flags= dup_field->flags;
vcol_info= dup_field->vcol_info;
invisible= dup_field->invisible;
check_constraint= dup_field->check_constraint;
comment= dup_field->comment;
option_list= dup_field->option_list;
versioning= dup_field->versioning;
}
/**
maximum possible character length for blob .
This method is used in Item_field : : set_field to calculate
max_length for Item .
For example :
CREATE TABLE t2 SELECT CONCAT ( tinyblob_utf8_column ) FROM t1 ;
must create a " VARCHAR ( 255 ) CHARACTER SET utf8 " column .
@ return
length
*/
uint32 Field_blob::char_length() const
{
return Field_blob::character_octet_length();
}
uint32 Field_blob::character_octet_length() const
{
switch (packlength)
{
case 1 :
return 255 ;
case 2 :
return 65535 ;
case 3 :
return 16777215 ;
case 4 :
return (uint32) UINT_MAX32;
default :
DBUG_ASSERT(0 ); // we should never go here
return 0 ;
}
}
/**
Makes a clone of this object for ALTER / CREATE TABLE
@ param mem_root MEM_ROOT where to clone the field
*/
Create_field *Create_field::clone(MEM_ROOT *mem_root) const
{
Create_field *res= new (mem_root) Create_field(*this );
return res;
}
/**
Return true if default is an expression that must be saved explicitly
This is :
- Not basic constants
- If field is a BLOB ( Which doesn ' t support normal DEFAULT )
*/
bool Column_definition::has_default_expression()
{
return (default_value &&
(!default_value->expr->basic_const_item() ||
(flags & BLOB_FLAG)));
}
bool Column_definition::set_compressed(const char *method)
{
if (!method || !strcmp(method, zlib_compression_method->name))
{
unireg_check= Field::TMYSQL_COMPRESSED;
compression_method_ptr= zlib_compression_method;
return false ;
}
my_error(ER_UNKNOWN_COMPRESSION_METHOD, MYF(0 ), method);
return true ;
}
bool Column_definition::check_vcol_for_key(THD *thd) const
{
if (vcol_info && (vcol_info->flags & VCOL_NOT_STRICTLY_DETERMINISTIC))
{
/* use check_expression() to report an error */
check_expression(vcol_info, field_name, VCOL_GENERATED_STORED);
DBUG_ASSERT(thd->is_error());
return true ;
}
return false ;
}
Send_field::Send_field(THD *thd, Item *item)
{
item->make_send_field(thd, this );
normalize();
}
/**
maximum possible display length for blob .
@ return
length
*/
uint32 Field_blob::max_display_length() const
{
switch (packlength)
{
case 1 :
return 255 * mbmaxlen();
case 2 :
return 65535 * mbmaxlen();
case 3 :
return 16777215 * mbmaxlen();
case 4 :
return (uint32) UINT_MAX32;
default :
DBUG_ASSERT(0 ); // we should never go here
return 0 ;
}
}
/*****************************************************************************
Warning handling
*****************************************************************************/
/**
* Produce warning or note about data saved into field .
@ param level - level of message ( Note / Warning / Error )
@ param code - error code of message to be produced
@ param cut_increment - whenever we should increase cut fields count
@ current_row - current row number
@ note
This function won ' t produce warning or notes or increase cut fields counter
if count_cuted_fields = = CHECK_FIELD_IGNORE or CHECK_FIELD_EXPRESSION
for the current thread .
This allows us to avoid notes in optimisation , like
convert_constant_item ( ) .
@ retval
1 if count_cuted_fields = = CHECK_FIELD_IGNORE and error level is not NOTE
@ retval
0 otherwise
*/
bool
Field::set_warning(Sql_condition::enum_warning_level level, uint code,
int cut_increment, ulong current_row) const
{
/*
If this field was created only for type conversion purposes it
will have table = = NULL .
*/
THD *thd= get_thd();
/*
In INPLACE ALTER , server can ' t know which row has generated
the warning , so the value of current row is supplied by the engine .
*/
if (current_row)
thd->get_stmt_da()->reset_current_row_for_warning(current_row);
if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION)
{
thd->cuted_fields+= cut_increment;
push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str,
current_row ? current_row
: thd->get_stmt_da()->current_row_for_warning());
return 0 ;
}
return level >= Sql_condition::WARN_LEVEL_WARN;
}
/**
Produce warning or note about datetime string data saved into field .
@ param level level of message ( Note / Warning / Error )
@ param code error code of message to be produced
@ param str string value which we tried to save
@ param ts_type type of datetime value ( datetime / date / time )
@ param cuted_increment whenever we should increase cut fields count or not
@ note
This function will always produce some warning but won ' t increase cut
fields counter if count_cuted_fields = = FIELD_CHECK_IGNORE for current
thread .
See also bug # 2336
*/
void Field::set_datetime_warning(Sql_condition::enum_warning_level level,
uint code, const ErrConv *str,
const char *typestr, int cuted_increment)
const
{
THD *thd= get_thd();
if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN)
{
/*
field_name . str can be NULL when field is not in the select list :
SET SESSION SQL_MODE = ' STRICT_ALL_TABLES , NO_ZERO_DATE ' ;
CREATE OR REPLACE TABLE t2 SELECT 1 AS f FROM t1 GROUP BY FROM_DAYS ( d ) ;
Can ' t call push_warning_truncated_value_for_field ( ) directly here ,
as it expect a non - NULL name .
*/
thd->push_warning_wrong_or_truncated_value(level, false , typestr,
str->ptr(),
table->s->db.str,
table->s->table_name.str,
field_name.str);
}
else
set_warning(level, code, cuted_increment);
}
void Field::set_warning_truncated_wrong_value(const char *type_arg,
const char *value)
{
THD *thd= get_thd();
const char *db_name;
const char *table_name;
/*
table has in the past been 0 in case of wrong calls when processing
statistics tables . Let ' s protect against that .
*/
DBUG_ASSERT(table);
db_name= (table && table->s->db.str) ? table->s->db.str : "" ;
table_name= (table && table->s->table_name.str) ?
table->s->table_name.str : "" ;
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
type_arg, value, db_name, table_name, field_name.str,
static_cast <ulong>(thd->get_stmt_da()->
current_row_for_warning()));
}
/*
Give warning for unusable key
Note that the caller is responsible to call it only under ther
right note_verbosity level
*/
void Field::raise_note_cannot_use_key_part(THD *thd,
uint keynr, uint part,
const LEX_CSTRING &op,
CHARSET_INFO *op_collation,
Item *value,
Data_type_compatibility reason)
const
{
StringBuffer<128 > value_buffer;
const LEX_CSTRING keyname= table->s->key_info[keynr].name;
size_t value_length;
value->print(&value_buffer, QT_EXPLAIN);
value_length= Well_formed_prefix(value_buffer.charset(),
value_buffer.ptr(),
MY_MIN(value_buffer.length(), 64 )).length();
/*
We must use c_ptr ( ) here for the ' T ' argument as it only works with
zero terminated strings .
*/
switch (reason){
case Data_type_compatibility::INCOMPATIBLE_COLLATION:
{
const LEX_CSTRING colf(charset()->coll_name);
const LEX_CSTRING colv(op_collation->coll_name);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_UNKNOWN_ERROR,
"Cannot use key %.*sQ part[%u] for lookup: "
"%.*sQ.%.*sQ.%.*sQ of collation %.*sQ "
"%.*s \" %.*sT\" of collation %.*sQ" ,
(int ) keyname.length, keyname.str,
part,
(int ) table->s->db.length, table->s->db.str,
(int ) table->s->table_name.length,
table->s->table_name.str,
(int ) field_name.length, field_name.str,
(int ) colf.length, colf.str,
(int ) op.length, op.str,
(int ) value_length, value_buffer.c_ptr_safe(),
(int ) colv.length, colv.str);
}
break ;
case Data_type_compatibility::OK:
DBUG_ASSERT(0 );
/* fall through */
case Data_type_compatibility::INCOMPATIBLE_DATA_TYPE:
{
const LEX_CSTRING dtypef(type_handler()->name().lex_cstring());
const LEX_CSTRING dtypev(value->type_handler()->name().lex_cstring());
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_UNKNOWN_ERROR,
"Cannot use key %.*sQ part[%u] for lookup: "
"%.*sQ.%.*sQ.%.*sQ of type %.*sQ "
"%.*s \" %.*sT\" of type %.*sQ" ,
(int ) keyname.length, keyname.str,
part,
(int ) table->s->db.length, table->s->db.str,
(int ) table->s->table_name.length,
table->s->table_name.str,
(int ) field_name.length, field_name.str,
(int ) dtypef.length, dtypef.str,
(int ) op.length, op.str,
(int ) value_length, value_buffer.c_ptr_safe(),
(int ) dtypev.length, dtypev.str);
}
break ;
}
}
/*
Give warning for unusable key
Note that the caller is responsible to call it only under ther
right note_verbosity level
*/
void Field::raise_note_key_become_unused(THD *thd, const String &expr) const
{
push_warning_printf(thd,
Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
"Cannot use key parts with %.*sQ.%.*sQ.%.*sQ "
"in the rewritten condition: %.*sQ" ,
(int ) table->s->db.length, table->s->db.str,
(int ) table->s->table_name.length, table->s->table_name.str,
(int ) field_name.length, field_name.str,
(int ) expr.length(), expr.ptr());
}
/*
@ brief
Return possible keys for a field
@ details
Return bit map of keys over this field which can be used by the range
optimizer . For a field of a generic table such keys are all keys that starts
from this field . For a field of a materialized derived table / view such keys
are all keys in which this field takes a part . This is less restrictive as
keys for a materialized derived table / view are generated on the fly from
present fields , thus the case when a field for the beginning of a key is
absent is impossible .
@ return map of possible keys
*/
key_map Field::get_possible_keys()
{
DBUG_ASSERT(table->pos_in_table_list);
return (table->pos_in_table_list->is_materialized_derived() ?
part_of_key : key_start);
}
bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record)
{
MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->read_set);
bool rc;
if ((rc= validate_value_in_record(thd, record)))
{
// Get and report val_str() for the DEFAULT value
StringBuffer<MAX_FIELD_WIDTH> tmp;
val_str(&tmp, ptr_in_record(record));
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_INVALID_DEFAULT_VALUE_FOR_FIELD,
ER_THD(thd, ER_INVALID_DEFAULT_VALUE_FOR_FIELD),
ErrConvString(&tmp).ptr(), field_name.str);
}
dbug_tmp_restore_column_map(&table->read_set, old_map);
return rc;
}
/**
Find which reaction should be for IGNORE value .
*/
ignore_value_reaction find_ignore_reaction(THD *thd)
{
enum_sql_command com= thd->lex->sql_command;
// All insert-like commands
if (com == SQLCOM_INSERT || com == SQLCOM_REPLACE ||
com == SQLCOM_INSERT_SELECT || com == SQLCOM_REPLACE_SELECT ||
com == SQLCOM_LOAD)
{
return IGNORE_MEANS_DEFAULT;
}
// Update commands
if (com == SQLCOM_UPDATE || com == SQLCOM_UPDATE_MULTI)
{
return IGNORE_MEANS_FIELD_VALUE;
}
return IGNORE_MEANS_ERROR;
}
bool Field::save_in_field_default_value(bool view_error_processing)
{
THD *thd= table->in_use;
/*
TODO : MDEV - 19597 Refactor TABLE : : vers_update_fields ( ) via stored virtual columns
This condition will go away as well as other conditions with vers_sys_field ( ) .
*/
if (vers_sys_field())
{
if (flags & VERS_ROW_START)
set_time();
else
set_max();
return false ;
}
if (unlikely(flags & NO_DEFAULT_VALUE_FLAG &&
real_type() != MYSQL_TYPE_ENUM))
{
if (reset())
{
my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
ER_THD(thd, ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0 ));
return true ;
}
if (view_error_processing)
{
TABLE_LIST *view= table->pos_in_table_list->top_table();
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_VIEW_FIELD,
ER_THD(thd, ER_NO_DEFAULT_FOR_VIEW_FIELD),
view->view_db.str,
view->view_name.str);
}
else
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_FIELD,
ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD),
field_name.str);
}
return true ;
}
set_default();
return
!is_null() &&
validate_value_in_record_with_warn(thd, table->record[0 ]) &&
thd->is_error();
}
bool Field::save_in_field_ignore_value(bool view_error_processing)
{
enum_sql_command com= table->in_use->lex->sql_command;
// All insert-like commands
if (com == SQLCOM_INSERT || com == SQLCOM_REPLACE ||
com == SQLCOM_INSERT_SELECT || com == SQLCOM_REPLACE_SELECT ||
com == SQLCOM_LOAD)
return save_in_field_default_value(view_error_processing);
return 0 ; // ignore
}
void Field::register_field_in_read_map()
{
if (vcol_info)
{
Item *vcol_item= vcol_info->expr;
vcol_item->walk(&Item::register_field_in_read_map,
0 , WALK_SUBQUERY);
}
bitmap_set_bit(table->read_set, field_index);
}
LEX_STRING Field::val_lex_string_strmake(MEM_ROOT *mem)
{
StringBuffer<MAX_FIELD_WIDTH> str;
val_str(&str);
char *to= strmake_root(mem, str.ptr(), str.length());
return to ? LEX_STRING{to, str.length()} : LEX_STRING{NULL, 0 };
}
bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to)
{
StringBuffer<MAX_FIELD_WIDTH> str;
bool rc= false ;
THD *thd= get_thd();
Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
val_str(&str);
if (!(to->length= str.length()))
*to= empty_clex_str;
else if ((rc= !(to->str= strmake_root(mem_root, str.ptr(), str.length()))))
to->length= 0 ;
return rc;
}
void Field::print_key_value(String *out, uint32 length)
{
if (charset() == &my_charset_bin)
print_key_value_binary(out, ptr, length);
else
val_str(out);
}
void Field_string::print_key_value(String *out, uint32 length)
{
if (charset() == &my_charset_bin)
{
size_t len= field_charset()->lengthsp((const char *) ptr, length);
print_key_value_binary(out, ptr, static_cast <uint32>(len));
}
else
{
THD *thd= get_thd();
sql_mode_t sql_mode_backup= thd->variables.sql_mode;
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
val_str(out,out);
thd->variables.sql_mode= sql_mode_backup;
}
}
void Field_varstring::print_key_value(String *out, uint32 length)
{
if (charset() == &my_charset_bin)
print_key_value_binary(out, get_data(), get_length());
else
val_str(out,out);
}
void Field_blob::print_key_value(String *out, uint32 length)
{
if (charset() == &my_charset_bin)
{
uchar *blob;
memcpy(&blob, ptr+packlength, sizeof (uchar*));
print_key_value_binary(out, blob, get_length());
}
else
val_str(out, out);
}
/*
@ brief Print value of the key part
@ param
out Output string
key value of the key
length Length of field in bytes ,
excluding NULL flag and length bytes
*/
void
Field::print_key_part_value(String *out, const uchar* key, uint32 length)
{
StringBuffer<128 > tmp(system_charset_info);
uint null_byte= 0 ;
if (real_maybe_null())
{
/*
Byte 0 of key is the null - byte . If set , key is NULL .
Otherwise , print the key value starting immediately after the
null - byte
*/
if (*key)
{
out->append(NULL_clex_str);
return ;
}
null_byte++; // Skip null byte
}
set_key_image(key + null_byte, length);
print_key_value(&tmp, length);
if (charset() == &my_charset_bin)
out->append(tmp.ptr(), tmp.length(), tmp.charset());
else
tmp.print(out, system_charset_info);
}
void Field::print_key_value_binary(String *out, const uchar* key, uint32 length)
{
out->append_semi_hex((const char *)key, length, charset());
}
Virtual_column_info* Virtual_column_info::clone(THD *thd)
{
Virtual_column_info* dst= new (thd->mem_root) Virtual_column_info(*this );
if (!dst)
return NULL;
if (expr)
{
dst->expr= expr->deep_copy_with_checks(thd);
if (!dst->expr)
return NULL;
}
if (!thd->make_lex_string(&dst->name, name.str, name.length))
return NULL;
return dst;
};
Messung V0.5 in Prozent C=93 H=98 G=95
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.1.160Bemerkung:
(vorverarbeitet am 2026-10-08)
¤
*Bot Zugriff