mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
11356 lines
323 KiB
C++
11356 lines
323 KiB
C++
/*
|
|
Copyright (c) 2000, 2017, Oracle and/or its affiliates.
|
|
Copyright (c) 2008, 2017, 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-1301 USA
|
|
*/
|
|
|
|
/**
|
|
@file
|
|
|
|
@brief
|
|
This file implements classes defined in field.h
|
|
*/
|
|
|
|
#ifdef USE_PRAGMA_IMPLEMENTATION
|
|
#pragma implementation // gcc: Class implementation
|
|
#endif
|
|
|
|
#include "mariadb.h"
|
|
#include "sql_priv.h"
|
|
#include "sql_select.h"
|
|
#include "rpl_rli.h" // Pull in Relay_log_info
|
|
#include "slave.h" // Pull in rpl_master_has_bug()
|
|
#include "strfunc.h" // find_type2, find_set
|
|
#include "sql_time.h" // str_to_datetime_with_warn,
|
|
// str_to_time_with_warn,
|
|
// TIME_to_timestamp,
|
|
// make_time, make_date,
|
|
// make_truncated_value_warning
|
|
#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
|
|
|
|
/*****************************************************************************
|
|
Instansiate templates and static variables
|
|
*****************************************************************************/
|
|
|
|
static const char *zero_timestamp="0000-00-00 00:00:00.000000";
|
|
LEX_CSTRING temp_lex_str= {STRING_WITH_LEN("temp")};
|
|
|
|
uchar Field_null::null[1]={1};
|
|
const char field_separator=',';
|
|
|
|
#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE FLOATING_POINT_BUFFER
|
|
#define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128
|
|
#define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128
|
|
#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
|
|
((ulong) ((1LL << MY_MIN(arg, 4) * 8) - 1))
|
|
|
|
#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index)))
|
|
#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(is_stat_field || !table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || (table->vcol_set && bitmap_is_set(table->vcol_set, field_index))))
|
|
|
|
#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 skiped
|
|
following #defines describe that gap and how to canculate number of fields
|
|
and index of field in this array.
|
|
*/
|
|
const int FIELDTYPE_TEAR_FROM= (MYSQL_TYPE_BIT + 1);
|
|
const int FIELDTYPE_TEAR_TO= (MYSQL_TYPE_NEWDECIMAL - 1);
|
|
const int FIELDTYPE_LAST= 254;
|
|
const int FIELDTYPE_NUM= FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST -
|
|
FIELDTYPE_TEAR_TO);
|
|
|
|
static inline int field_type2index (enum_field_types field_type)
|
|
{
|
|
DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM ||
|
|
real_type_to_type(field_type) > FIELDTYPE_TEAR_TO);
|
|
DBUG_ASSERT(field_type <= FIELDTYPE_LAST);
|
|
field_type= real_type_to_type(field_type);
|
|
if (field_type < FIELDTYPE_TEAR_FROM)
|
|
return field_type;
|
|
return FIELDTYPE_TEAR_FROM + (field_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
|
|
}
|
|
};
|
|
|
|
/**
|
|
Return type of which can carry value of both given types in UNION result.
|
|
|
|
@param a type for merging
|
|
@param b type for merging
|
|
|
|
@return
|
|
type of field
|
|
*/
|
|
|
|
enum_field_types Field::field_type_merge(enum_field_types a,
|
|
enum_field_types b)
|
|
{
|
|
return field_types_merge_rules[field_type2index(a)]
|
|
[field_type2index(b)];
|
|
}
|
|
|
|
const Type_handler *
|
|
Type_handler::aggregate_for_result_traditional(const Type_handler *a,
|
|
const Type_handler *b)
|
|
{
|
|
enum_field_types ta= a->real_field_type();
|
|
enum_field_types tb= b->real_field_type();
|
|
return
|
|
Type_handler::get_handler_by_real_type(Field::field_type_merge(ta, tb));
|
|
}
|
|
|
|
|
|
/*
|
|
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->cset->scan(cs, 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
|
|
*****************************************************************************/
|
|
|
|
|
|
void Field::make_sort_key(uchar *buff,uint length)
|
|
{
|
|
if (maybe_null())
|
|
{
|
|
if (is_null())
|
|
{
|
|
bzero(buff, length + 1);
|
|
return;
|
|
}
|
|
*buff++= 1;
|
|
}
|
|
sort_string(buff, length);
|
|
}
|
|
|
|
|
|
/**
|
|
@brief
|
|
Determine the relative position of the field value in a numeric interval
|
|
|
|
@details
|
|
The function returns a double number between 0.0 and 1.0 as the relative
|
|
position of the value of the this field in the numeric interval of [min,max].
|
|
If the value is not in the interval the the function returns 0.0 when
|
|
the value is less than min, and, 1.0 when the value is greater than max.
|
|
|
|
@param min value of the left end of the interval
|
|
@param max value of the right end of the interval
|
|
|
|
@return
|
|
relative position of the field value in the numeric interval [min,max]
|
|
*/
|
|
|
|
double Field::pos_in_interval_val_real(Field *min, Field *max)
|
|
{
|
|
double n, d;
|
|
n= val_real() - min->val_real();
|
|
if (n < 0)
|
|
return 0.0;
|
|
d= max->val_real() - min->val_real();
|
|
if (d <= 0)
|
|
return 1.0;
|
|
return MY_MIN(n/d, 1.0);
|
|
}
|
|
|
|
|
|
static
|
|
inline ulonglong char_prefix_to_ulonglong(uchar *src)
|
|
{
|
|
uint sz= sizeof(ulonglong);
|
|
for (uint i= 0; i < sz/2; i++)
|
|
{
|
|
uchar tmp= src[i];
|
|
src[i]= src[sz-1-i];
|
|
src[sz-1-i]= tmp;
|
|
}
|
|
return uint8korr(src);
|
|
}
|
|
|
|
/*
|
|
Compute res = a - b, without losing precision and taking care that these are
|
|
unsigned numbers.
|
|
*/
|
|
static inline double safe_substract(ulonglong a, ulonglong b)
|
|
{
|
|
return (a > b)? double(a - b) : -double(b - a);
|
|
}
|
|
|
|
|
|
/**
|
|
@brief
|
|
Determine the relative position of the field value in a string interval
|
|
|
|
@details
|
|
The function returns a double number between 0.0 and 1.0 as the relative
|
|
position of the value of the this field in the string interval of [min,max].
|
|
If the value is not in the interval the the function returns 0.0 when
|
|
the value is less than min, and, 1.0 when the value is greater than max.
|
|
|
|
@note
|
|
To calculate the relative position of the string value v in the interval
|
|
[min, max] the function first converts the beginning of these three
|
|
strings v, min, max into the strings that are used for byte comparison.
|
|
For each string not more sizeof(ulonglong) first bytes are taken
|
|
from the result of conversion. Then these bytes are interpreted as the
|
|
big-endian representation of an ulonglong integer. The values of these
|
|
integer numbers obtained for the strings v, min, max are used to calculate
|
|
the position of v in [min,max] in the same way is it's done for numeric
|
|
fields (see Field::pos_in_interval_val_real).
|
|
|
|
@todo
|
|
Improve the procedure for the case when min and max have the same
|
|
beginning
|
|
|
|
@param min value of the left end of the interval
|
|
@param max value of the right end of the interval
|
|
|
|
@return
|
|
relative position of the field value in the string interval [min,max]
|
|
*/
|
|
|
|
double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
|
|
{
|
|
uchar mp_prefix[sizeof(ulonglong)];
|
|
uchar minp_prefix[sizeof(ulonglong)];
|
|
uchar maxp_prefix[sizeof(ulonglong)];
|
|
ulonglong mp, minp, maxp;
|
|
my_strnxfrm(charset(), mp_prefix, sizeof(mp),
|
|
ptr + data_offset,
|
|
data_length());
|
|
my_strnxfrm(charset(), minp_prefix, sizeof(minp),
|
|
min->ptr + data_offset,
|
|
min->data_length());
|
|
my_strnxfrm(charset(), maxp_prefix, sizeof(maxp),
|
|
max->ptr + data_offset,
|
|
max->data_length());
|
|
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;
|
|
}
|
|
|
|
|
|
/*
|
|
This handles all numeric and BIT data types.
|
|
*/
|
|
bool 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;
|
|
}
|
|
|
|
|
|
/*
|
|
This handles all numeric and BIT data types.
|
|
*/
|
|
bool 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;
|
|
}
|
|
|
|
|
|
/*
|
|
This covers all numeric types, BIT
|
|
*/
|
|
bool 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;
|
|
}
|
|
|
|
|
|
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) && !(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_prepare_func_item(value, 1)))
|
|
goto error;
|
|
|
|
/*
|
|
expr_item is now fixed, it's safe to call cmp_type()
|
|
*/
|
|
if (expr_item->cmp_type() == ROW_RESULT)
|
|
{
|
|
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
|
|
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);
|
|
}
|
|
|
|
|
|
/**
|
|
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,
|
|
uint8 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/**
|
|
Contruct 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 guranteed 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 retuned
|
|
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()
|
|
- my_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;
|
|
}
|
|
|
|
|
|
/*
|
|
Conver 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->cset->strntoull10rnd(cs, 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 0;
|
|
|
|
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= my_strntod(cs,(char*) str, length, &end, error);
|
|
if (unlikely(*error))
|
|
{
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
*error= 1;
|
|
}
|
|
else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
|
|
check_edom_and_truncation("double", str == end,
|
|
cs, str, length, end))
|
|
*error= 1;
|
|
return nr;
|
|
}
|
|
|
|
|
|
/**
|
|
Process decimal library return codes and issue warnings for overflow and
|
|
truncation.
|
|
|
|
@param op_result decimal library return code (E_DEC_* see include/decimal.h)
|
|
|
|
@retval
|
|
1 there was overflow
|
|
@retval
|
|
0 no error or some other errors except overflow
|
|
*/
|
|
|
|
int Field::warn_if_overflow(int op_result)
|
|
{
|
|
if (op_result == E_DEC_OVERFLOW)
|
|
{
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
return 1;
|
|
}
|
|
if (op_result == E_DEC_TRUNCATED)
|
|
{
|
|
set_note(WARN_DATA_TRUNCATED, 1);
|
|
/* We return 0 here as this is not a critical issue */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Interpret field value as an integer but return the result as a string.
|
|
|
|
This is used for printing bit_fields as numbers while debugging.
|
|
*/
|
|
|
|
String *Field::val_int_as_str(String *val_buffer, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_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->cset->longlong10_to_str)(cs, (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), invisible(VISIBLE),
|
|
null_ptr(null_ptr_arg), table(0), orig_table(0),
|
|
table_name(0), field_name(*field_name_arg), option_list(0),
|
|
option_struct(0), key_start(0), part_of_key(0),
|
|
part_of_key_not_clustered(0), part_of_sortkey(0),
|
|
unireg_check(unireg_check_arg), 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;
|
|
is_stat_field= FALSE;
|
|
cond_selectivity= 1.0;
|
|
next_equal_field= NULL;
|
|
}
|
|
|
|
|
|
void Field::hash(ulong *nr, ulong *nr2)
|
|
{
|
|
if (is_null())
|
|
{
|
|
*nr^= (*nr << 1) | 1;
|
|
}
|
|
else
|
|
{
|
|
uint len= pack_length();
|
|
CHARSET_INFO *cs= sort_charset();
|
|
cs->coll->hash_sort(cs, ptr, len, nr, nr2);
|
|
}
|
|
}
|
|
|
|
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_binary(Protocol *protocol)
|
|
{
|
|
char buff[MAX_FIELD_WIDTH];
|
|
String tmp(buff,sizeof(buff),charset());
|
|
val_str(&tmp);
|
|
return protocol->store(tmp.ptr(), tmp.length(), tmp.charset());
|
|
}
|
|
|
|
|
|
/**
|
|
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,
|
|
Relay_log_info *rli_arg __attribute__((unused)),
|
|
uint16 mflags __attribute__((unused)),
|
|
int *order_var)
|
|
{
|
|
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)
|
|
{
|
|
int res;
|
|
THD *thd= get_thd();
|
|
enum_check_fields old_check_level= thd->count_cuted_fields;
|
|
thd->count_cuted_fields= check_level;
|
|
res= store(to, length, cs);
|
|
thd->count_cuted_fields= old_check_level;
|
|
return res;
|
|
}
|
|
|
|
|
|
int Field::store_timestamp(my_time_t ts, ulong sec_part)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
THD *thd= get_thd();
|
|
thd->timestamp_to_TIME(<ime, ts, sec_part, 0);
|
|
return store_time_dec(<ime, decimals());
|
|
}
|
|
|
|
/**
|
|
Pack the field into a format suitable for storage and transfer.
|
|
|
|
To implement packing functionality, only the virtual function
|
|
should be overridden. The other functions are just convenience
|
|
functions and hence should not be overridden.
|
|
|
|
@note The default method for packing fields just copy the raw bytes
|
|
of the record into the destination, but never more than
|
|
<code>max_length</code> characters.
|
|
|
|
@param to
|
|
Pointer to memory area where representation of field should be put.
|
|
|
|
@param from
|
|
Pointer to memory area where record representation of field is
|
|
stored.
|
|
|
|
@param max_length
|
|
Maximum length of the field, as given in the column definition. For
|
|
example, for <code>CHAR(1000)</code>, the <code>max_length</code>
|
|
is 1000. This information is sometimes needed to decide how to pack
|
|
the data.
|
|
|
|
*/
|
|
uchar *
|
|
Field::pack(uchar *to, const uchar *from, uint max_length)
|
|
{
|
|
uint32 length= pack_length();
|
|
set_if_smaller(length, max_length);
|
|
memcpy(to, from, length);
|
|
return to+length;
|
|
}
|
|
|
|
/**
|
|
Unpack a field from row data.
|
|
|
|
This method is used to unpack a field from a master whose size of
|
|
the field is less than that of the slave.
|
|
|
|
The <code>param_data</code> parameter is a two-byte integer (stored
|
|
in the least significant 16 bits of the unsigned integer) usually
|
|
consisting of two parts: the real type in the most significant byte
|
|
and a original pack length in the least significant byte.
|
|
|
|
The exact layout of the <code>param_data</code> field is given by
|
|
the <code>Table_map_log_event::save_field_metadata()</code>.
|
|
|
|
This is the default method for unpacking a field. It just copies
|
|
the memory block in byte order (of original pack length bytes or
|
|
length of field, whichever is smaller).
|
|
|
|
@param to Destination of the data
|
|
@param from Source of the data
|
|
@param param_data Real type and original pack length of the field
|
|
data
|
|
|
|
@return New pointer into memory based on from + length of the data
|
|
@return 0 if wrong data
|
|
*/
|
|
const uchar *
|
|
Field::unpack(uchar* to, const uchar *from, const uchar *from_end,
|
|
uint param_data)
|
|
{
|
|
uint length=pack_length(), len;
|
|
int from_type= 0;
|
|
/*
|
|
If from length is > 255, it has encoded data in the upper bits. Need
|
|
to mask it out.
|
|
*/
|
|
if (param_data > 255)
|
|
{
|
|
from_type= (param_data & 0xff00) >> 8U; // real_type.
|
|
param_data= param_data & 0x00ff; // length.
|
|
}
|
|
|
|
if ((param_data == 0) ||
|
|
(length == param_data) ||
|
|
(from_type != real_type()))
|
|
{
|
|
if (from + length > from_end)
|
|
return 0; // Error in data
|
|
|
|
memcpy(to, from, length);
|
|
return from+length;
|
|
}
|
|
|
|
len= (param_data && (param_data < length)) ? param_data : length;
|
|
|
|
if (from + len > from_end)
|
|
return 0; // Error in data
|
|
|
|
memcpy(to, from, len);
|
|
return from+len;
|
|
}
|
|
|
|
|
|
my_decimal *Field::val_decimal(my_decimal *decimal)
|
|
{
|
|
/* This never have to be called */
|
|
DBUG_ASSERT(0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
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.str;
|
|
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.str;
|
|
}
|
|
else
|
|
field->org_table_name= field->db_name= "";
|
|
if (orig_table && orig_table->alias.ptr())
|
|
{
|
|
field->table_name= orig_table->alias.ptr();
|
|
field->org_col_name= field_name;
|
|
}
|
|
else
|
|
{
|
|
field->table_name= "";
|
|
field->org_col_name= empty_clex_str;
|
|
}
|
|
field->col_name= field_name;
|
|
field->length=field_length;
|
|
field->type=type();
|
|
field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
|
|
field->decimals= 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Conversion from decimal to longlong with checking overflow and
|
|
setting correct value (min/max) in case of overflow.
|
|
|
|
@param val value which have to be converted
|
|
@param unsigned_flag type of integer in which we convert val
|
|
@param err variable to pass error code
|
|
|
|
@return
|
|
value converted from val
|
|
*/
|
|
longlong Field::convert_decimal2longlong(const my_decimal *val,
|
|
bool unsigned_flag, int *err)
|
|
{
|
|
longlong i;
|
|
if (unsigned_flag)
|
|
{
|
|
if (val->sign())
|
|
{
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
i= 0;
|
|
*err= 1;
|
|
}
|
|
else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR &
|
|
~E_DEC_OVERFLOW &
|
|
~E_DEC_TRUNCATED),
|
|
val, TRUE, &i)))
|
|
{
|
|
i= ~(longlong) 0;
|
|
*err= 1;
|
|
}
|
|
}
|
|
else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR &
|
|
~E_DEC_OVERFLOW &
|
|
~E_DEC_TRUNCATED),
|
|
val, FALSE, &i)))
|
|
{
|
|
i= (val->sign() ? LONGLONG_MIN : LONGLONG_MAX);
|
|
*err= 1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
/**
|
|
Storing decimal in integer fields.
|
|
|
|
@param val value for storing
|
|
|
|
@note
|
|
This method is used by all integer fields, real/decimal redefine it
|
|
|
|
@retval
|
|
0 OK
|
|
@retval
|
|
!=0 error
|
|
*/
|
|
|
|
int Field_int::store_decimal(const my_decimal *val)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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,ulonglong fuzzydate)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
longlong nr= val_int();
|
|
bool neg= !(flags & UNSIGNED_FLAG) && nr < 0;
|
|
return int_to_datetime_with_warn(neg, neg ? -nr : nr, ltime, fuzzydate,
|
|
field_name.str);
|
|
}
|
|
|
|
|
|
bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
field_charset= collation.collation;
|
|
if (collation.collation->state & MY_CS_BINSORT)
|
|
flags|=BINARY_FLAG;
|
|
field_derivation= collation.derivation;
|
|
field_repertoire= collation.repertoire;
|
|
}
|
|
|
|
|
|
bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const
|
|
{
|
|
/*
|
|
Can't guarantee uniqueness when comparing a CHAR/VARCHAR/TEXT,
|
|
BINARY/VARBINARY/BLOB, ENUM,SET columns to an item with cmp_type()
|
|
of INT_RESULT, DOUBLE_RESULT, DECIMAL_RESULT or TIME_RESULT.
|
|
Example:
|
|
SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01'
|
|
return non-unuque values, e.g. '2001-01-01' and '2001-01-01x'.
|
|
*/
|
|
if (!field_charset->coll->propagate(field_charset, 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(field_charset, field_derivation, repertoire());
|
|
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_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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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 *ltime,ulonglong fuzzydate)
|
|
{
|
|
char buff[40];
|
|
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
|
|
if (!(res=val_str(&tmp)) ||
|
|
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
|
|
ltime, fuzzydate))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
char buff[MAX_DATE_STRING_REP_LENGTH];
|
|
uint length= (uint) my_TIME_to_str(ltime, buff, dec);
|
|
/* Avoid conversion when field character set is ASCII compatible */
|
|
return store(buff, length, (charset()->state & MY_CS_NONASCII) ?
|
|
&my_charset_latin1 : charset());
|
|
}
|
|
|
|
|
|
bool Field::optimize_range(uint idx, uint part) const
|
|
{
|
|
return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE);
|
|
}
|
|
|
|
|
|
Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table,
|
|
bool keep_type __attribute__((unused)))
|
|
{
|
|
Field *tmp;
|
|
if (!(tmp= (Field*) memdup_root(root,(char*) this,size_of())))
|
|
return 0;
|
|
|
|
if (tmp->table->maybe_null)
|
|
tmp->flags&= ~NOT_NULL_FLAG;
|
|
tmp->table= new_table;
|
|
tmp->key_start.init(0);
|
|
tmp->part_of_key.init(0);
|
|
tmp->part_of_sortkey.init(0);
|
|
/*
|
|
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 |
|
|
VERS_SYS_START_FLAG | VERS_SYS_END_FLAG |
|
|
VERS_UPDATE_UNVERSIONED_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;
|
|
}
|
|
|
|
|
|
/* 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,
|
|
bool stat_flag)
|
|
{
|
|
Field *tmp;
|
|
if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
|
|
{
|
|
tmp->init(new_table);
|
|
tmp->move_field_offset(diff);
|
|
}
|
|
tmp->is_stat_field= stat_flag;
|
|
return tmp;
|
|
}
|
|
|
|
|
|
Field *Field::clone(MEM_ROOT *root, my_ptrdiff_t diff)
|
|
{
|
|
Field *tmp;
|
|
if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
|
|
{
|
|
tmp->move_field_offset(diff);
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
int Field::set_default()
|
|
{
|
|
if (default_value)
|
|
{
|
|
Query_arena backup_arena;
|
|
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());
|
|
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"));
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Field_row, e.g. for ROW-type SP variables
|
|
****************************************************************************/
|
|
|
|
Field_row::~Field_row()
|
|
{
|
|
delete m_table;
|
|
}
|
|
|
|
|
|
bool Field_row::sp_prepare_and_store_item(THD *thd, Item **value)
|
|
{
|
|
DBUG_ENTER("Field_row::sp_prepare_and_store_item");
|
|
|
|
if (value[0]->type() == Item::NULL_ITEM)
|
|
{
|
|
/*
|
|
We're in a auto-generated sp_inst_set, to assign
|
|
the explicit default NULL value to a ROW variable.
|
|
*/
|
|
m_table->set_all_fields_to_null();
|
|
DBUG_RETURN(false);
|
|
}
|
|
|
|
/**
|
|
- In case if we're assigning a ROW variable from another ROW variable,
|
|
value[0] points to Item_splocal. sp_fix_func_item() will return the
|
|
fixed underlying Item_field pointing to Field_row.
|
|
- In case if we're assigning from a ROW() value, src and value[0] will
|
|
point to the same Item_row.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
DBUG_RETURN(m_table->sp_set_all_fields_from_item(thd, src));
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Functions for the Field_decimal class
|
|
This is an number stored as a pre-space (or pre-zero) string
|
|
****************************************************************************/
|
|
|
|
int
|
|
Field_decimal::reset(void)
|
|
{
|
|
Field_decimal::store(STRING_WITH_LEN("0"),&my_charset_bin);
|
|
return 0;
|
|
}
|
|
|
|
void Field_decimal::overflow(bool negative)
|
|
{
|
|
uint len=field_length;
|
|
uchar *to=ptr, filler= '9';
|
|
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
if (negative)
|
|
{
|
|
if (!unsigned_flag)
|
|
{
|
|
/* Put - sign as a first digit so we'll have -999..999 or 999..999 */
|
|
*to++ = '-';
|
|
len--;
|
|
}
|
|
else
|
|
{
|
|
filler= '0'; // Fill up with 0
|
|
if (!zerofill)
|
|
{
|
|
/*
|
|
Handle unsigned integer without zerofill, in which case
|
|
the number should be of format ' 0' or ' 0.000'
|
|
*/
|
|
uint whole_part=field_length- (dec ? dec+2 : 1);
|
|
// Fill with spaces up to the first digit
|
|
bfill(to, whole_part, ' ');
|
|
to+= whole_part;
|
|
len-= whole_part;
|
|
// The main code will also handle the 0 before the decimal point
|
|
}
|
|
}
|
|
}
|
|
bfill(to, len, filler);
|
|
if (dec)
|
|
ptr[field_length-dec-1]='.';
|
|
return;
|
|
}
|
|
|
|
|
|
int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
char buff[STRING_BUFFER_USUAL_SIZE];
|
|
String tmp(buff,sizeof(buff), &my_charset_bin);
|
|
const uchar *from= (uchar*) from_arg;
|
|
|
|
/* Convert character set if the old one is multi uchar */
|
|
if (cs->mbmaxlen > 1)
|
|
{
|
|
uint dummy_errors;
|
|
tmp.copy((char*) from, len, cs, &my_charset_bin, &dummy_errors);
|
|
from= (uchar*) tmp.ptr();
|
|
len= tmp.length();
|
|
}
|
|
|
|
const uchar *end= from+len;
|
|
/* The pointer where the field value starts (i.e., "where to write") */
|
|
uchar *to= ptr;
|
|
uint tmp_dec, tmp_uint;
|
|
/*
|
|
The sign of the number : will be 0 (means positive but sign not
|
|
specified), '+' or '-'
|
|
*/
|
|
uchar sign_char=0;
|
|
/* The pointers where prezeros start and stop */
|
|
const uchar *pre_zeros_from, *pre_zeros_end;
|
|
/* The pointers where digits at the left of '.' start and stop */
|
|
const uchar *int_digits_from, *int_digits_end;
|
|
/* The pointers where digits at the right of '.' start and stop */
|
|
const uchar *frac_digits_from, *frac_digits_end;
|
|
/* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */
|
|
char expo_sign_char=0;
|
|
uint exponent=0; // value of the exponent
|
|
/*
|
|
Pointers used when digits move from the left of the '.' to the
|
|
right of the '.' (explained below)
|
|
*/
|
|
const uchar *UNINIT_VAR(int_digits_tail_from);
|
|
/* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */
|
|
uint UNINIT_VAR(int_digits_added_zeros);
|
|
/*
|
|
Pointer used when digits move from the right of the '.' to the left
|
|
of the '.'
|
|
*/
|
|
const uchar *UNINIT_VAR(frac_digits_head_end);
|
|
/* Number of 0 that need to be added at the right of the '.' (for 1E-3) */
|
|
uint UNINIT_VAR(frac_digits_added_zeros);
|
|
uchar *pos,*tmp_left_pos,*tmp_right_pos;
|
|
/* Pointers that are used as limits (begin and end of the field buffer) */
|
|
uchar *left_wall,*right_wall;
|
|
uchar tmp_char;
|
|
/*
|
|
To remember if get_thd()->cuted_fields has already been incremented,
|
|
to do that only once
|
|
*/
|
|
bool is_cuted_fields_incr=0;
|
|
|
|
/*
|
|
There are three steps in this function :
|
|
- parse the input string
|
|
- modify the position of digits around the decimal dot '.'
|
|
according to the exponent value (if specified)
|
|
- write the formatted number
|
|
*/
|
|
|
|
if ((tmp_dec=dec))
|
|
tmp_dec++;
|
|
|
|
/* skip pre-space */
|
|
while (from != end && my_isspace(&my_charset_bin,*from))
|
|
from++;
|
|
if (from == end)
|
|
{
|
|
set_warning(WARN_DATA_TRUNCATED, 1);
|
|
is_cuted_fields_incr=1;
|
|
}
|
|
else if (*from == '+' || *from == '-') // Found some sign ?
|
|
{
|
|
sign_char= *from++;
|
|
/*
|
|
We allow "+" for unsigned decimal unless defined different
|
|
Both options allowed as one may wish not to have "+" for unsigned numbers
|
|
because of data processing issues
|
|
*/
|
|
if (unsigned_flag)
|
|
{
|
|
if (sign_char=='-')
|
|
{
|
|
Field_decimal::overflow(1);
|
|
return 1;
|
|
}
|
|
/*
|
|
Defining this will not store "+" for unsigned decimal type even if
|
|
it is passed in numeric string. This will make some tests to fail
|
|
*/
|
|
#ifdef DONT_ALLOW_UNSIGNED_PLUS
|
|
else
|
|
sign_char=0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
pre_zeros_from= from;
|
|
for (; from!=end && *from == '0'; from++) ; // Read prezeros
|
|
pre_zeros_end=int_digits_from=from;
|
|
/* Read non zero digits at the left of '.'*/
|
|
for (; from != end && my_isdigit(&my_charset_bin, *from) ; from++) ;
|
|
int_digits_end=from;
|
|
if (from!=end && *from == '.') // Some '.' ?
|
|
from++;
|
|
frac_digits_from= from;
|
|
/* Read digits at the right of '.' */
|
|
for (;from!=end && my_isdigit(&my_charset_bin, *from); from++) ;
|
|
frac_digits_end=from;
|
|
// Some exponentiation symbol ?
|
|
if (from != end && (*from == 'e' || *from == 'E'))
|
|
{
|
|
from++;
|
|
if (from != end && (*from == '+' || *from == '-')) // Some exponent sign ?
|
|
expo_sign_char= *from++;
|
|
else
|
|
expo_sign_char= '+';
|
|
/*
|
|
Read digits of the exponent and compute its value. We must care about
|
|
'exponent' overflow, because as unsigned arithmetic is "modulo", big
|
|
exponents will become small (e.g. 1e4294967296 will become 1e0, and the
|
|
field will finally contain 1 instead of its max possible value).
|
|
*/
|
|
for (;from!=end && my_isdigit(&my_charset_bin, *from); from++)
|
|
{
|
|
exponent=10*exponent+(*from-'0');
|
|
if (exponent>MAX_EXPONENT)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
We only have to generate warnings if count_cuted_fields is set.
|
|
This is to avoid extra checks of the number when they are not needed.
|
|
Even if this flag is not set, it's OK to increment warnings, if
|
|
it makes the code easer to read.
|
|
*/
|
|
|
|
if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
|
|
{
|
|
// Skip end spaces
|
|
for (;from != end && my_isspace(&my_charset_bin, *from); from++) ;
|
|
if (from != end) // If still something left, warn
|
|
{
|
|
set_warning(WARN_DATA_TRUNCATED, 1);
|
|
is_cuted_fields_incr=1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Now "move" digits around the decimal dot according to the exponent value,
|
|
and add necessary zeros.
|
|
Examples :
|
|
- 1E+3 : needs 3 more zeros at the left of '.' (int_digits_added_zeros=3)
|
|
- 1E-3 : '1' moves at the right of '.', and 2 more zeros are needed
|
|
between '.' and '1'
|
|
- 1234.5E-3 : '234' moves at the right of '.'
|
|
These moves are implemented with pointers which point at the begin
|
|
and end of each moved segment. Examples :
|
|
- 1234.5E-3 : before the code below is executed, the int_digits part is
|
|
from '1' to '4' and the frac_digits part from '5' to '5'. After the code
|
|
below, the int_digits part is from '1' to '1', the frac_digits_head
|
|
part is from '2' to '4', and the frac_digits part from '5' to '5'.
|
|
- 1234.5E3 : before the code below is executed, the int_digits part is
|
|
from '1' to '4' and the frac_digits part from '5' to '5'. After the code
|
|
below, the int_digits part is from '1' to '4', the int_digits_tail
|
|
part is from '5' to '5', the frac_digits part is empty, and
|
|
int_digits_added_zeros=2 (to make 1234500).
|
|
*/
|
|
|
|
/*
|
|
Below tmp_uint cannot overflow with small enough MAX_EXPONENT setting,
|
|
as int_digits_added_zeros<=exponent<4G and
|
|
(int_digits_end-int_digits_from)<=max_allowed_packet<=2G and
|
|
(frac_digits_from-int_digits_tail_from)<=max_allowed_packet<=2G
|
|
*/
|
|
|
|
if (!expo_sign_char)
|
|
tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from);
|
|
else if (expo_sign_char == '-')
|
|
{
|
|
tmp_uint=MY_MIN(exponent,(uint)(int_digits_end-int_digits_from));
|
|
frac_digits_added_zeros=exponent-tmp_uint;
|
|
int_digits_end -= tmp_uint;
|
|
frac_digits_head_end=int_digits_end+tmp_uint;
|
|
tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from);
|
|
}
|
|
else // (expo_sign_char=='+')
|
|
{
|
|
tmp_uint=MY_MIN(exponent,(uint)(frac_digits_end-frac_digits_from));
|
|
int_digits_added_zeros=exponent-tmp_uint;
|
|
int_digits_tail_from=frac_digits_from;
|
|
frac_digits_from=frac_digits_from+tmp_uint;
|
|
/*
|
|
We "eat" the heading zeros of the
|
|
int_digits.int_digits_tail.int_digits_added_zeros concatenation
|
|
(for example 0.003e3 must become 3 and not 0003)
|
|
*/
|
|
if (int_digits_from == int_digits_end)
|
|
{
|
|
/*
|
|
There was nothing in the int_digits part, so continue
|
|
eating int_digits_tail zeros
|
|
*/
|
|
for (; int_digits_tail_from != frac_digits_from &&
|
|
*int_digits_tail_from == '0'; int_digits_tail_from++) ;
|
|
if (int_digits_tail_from == frac_digits_from)
|
|
{
|
|
// there were only zeros in int_digits_tail too
|
|
int_digits_added_zeros=0;
|
|
}
|
|
}
|
|
tmp_uint= (uint) (tmp_dec+(int_digits_end-int_digits_from)+
|
|
(uint)(frac_digits_from-int_digits_tail_from)+
|
|
int_digits_added_zeros);
|
|
}
|
|
|
|
/*
|
|
Now write the formated 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_cutted_fields, we may also want
|
|
to know if some non-zero tail of these parts will
|
|
be truncated (for example, 0.002->0.00 will generate a warning,
|
|
while 0.000->0.00 will not)
|
|
(and 0E1000000000 will not, while 1E-1000000000 will)
|
|
*/
|
|
|
|
pos=to+(uint)(field_length-tmp_dec); // Calculate post to '.'
|
|
right_wall=to+field_length;
|
|
if (pos != right_wall)
|
|
*pos++='.';
|
|
|
|
if (expo_sign_char == '-')
|
|
{
|
|
while (frac_digits_added_zeros-- > 0)
|
|
{
|
|
if (pos == right_wall)
|
|
{
|
|
if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
|
|
!is_cuted_fields_incr)
|
|
break; // Go on below to see if we lose non zero digits
|
|
return 0;
|
|
}
|
|
*pos++='0';
|
|
}
|
|
while (int_digits_end != frac_digits_head_end)
|
|
{
|
|
tmp_char= *int_digits_end++;
|
|
if (pos == right_wall)
|
|
{
|
|
if (tmp_char != '0') // Losing a non zero digit ?
|
|
{
|
|
if (!is_cuted_fields_incr)
|
|
set_warning(WARN_DATA_TRUNCATED, 1);
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
*pos++= tmp_char;
|
|
}
|
|
}
|
|
|
|
for (;frac_digits_from!=frac_digits_end;)
|
|
{
|
|
tmp_char= *frac_digits_from++;
|
|
if (pos == right_wall)
|
|
{
|
|
if (tmp_char != '0') // Losing a non zero digit ?
|
|
{
|
|
if (!is_cuted_fields_incr)
|
|
{
|
|
/*
|
|
This is a note, not a warning, as we don't want to abort
|
|
when we cut decimals in strict mode
|
|
*/
|
|
set_note(WARN_DATA_TRUNCATED, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
*pos++= tmp_char;
|
|
}
|
|
|
|
while (pos != right_wall)
|
|
*pos++='0'; // Fill with zeros at right of '.'
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Field_decimal::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
char buff[22];
|
|
uint length, int_part;
|
|
char fyllchar;
|
|
uchar *to;
|
|
|
|
if (nr < 0 && unsigned_flag && !unsigned_val)
|
|
{
|
|
overflow(1);
|
|
return 1;
|
|
}
|
|
length= (uint) (longlong10_to_str(nr,buff,unsigned_val ? 10 : -10) - buff);
|
|
int_part= field_length- (dec ? dec+1 : 0);
|
|
|
|
if (length > int_part)
|
|
{
|
|
overflow(!unsigned_val && nr < 0L); /* purecov: inspected */
|
|
return 1;
|
|
}
|
|
|
|
fyllchar = zerofill ? (char) '0' : (char) ' ';
|
|
to= ptr;
|
|
for (uint i=int_part-length ; i-- > 0 ;)
|
|
*to++ = fyllchar;
|
|
memcpy(to,buff,length);
|
|
if (dec)
|
|
{
|
|
to[length]='.';
|
|
bfill(to+length+1,dec,'0');
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
double Field_decimal::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int not_used;
|
|
char *end_not_used;
|
|
return my_strntod(&my_charset_bin, (char*) ptr, field_length, &end_not_used,
|
|
¬_used);
|
|
}
|
|
|
|
longlong Field_decimal::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int not_used;
|
|
if (unsigned_flag)
|
|
return my_strntoull(&my_charset_bin, (char*) ptr, field_length, 10, NULL,
|
|
¬_used);
|
|
return my_strntoll(&my_charset_bin, (char*) ptr, field_length, 10, NULL,
|
|
¬_used);
|
|
}
|
|
|
|
|
|
String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
|
|
String *val_ptr)
|
|
{
|
|
ASSERT_COLUMN_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 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_new_decimal
|
|
****************************************************************************/
|
|
|
|
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,
|
|
uint8 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, dec_arg, zero_arg, unsigned_arg)
|
|
{
|
|
precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
|
|
set_if_smaller(precision, DECIMAL_MAX_PRECISION);
|
|
DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
|
|
(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_decimal2binary().
|
|
|
|
@retval
|
|
0 ok
|
|
@retval
|
|
1 error
|
|
*/
|
|
|
|
bool Field_new_decimal::store_value(const my_decimal *decimal_value,
|
|
int *native_error)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int error= 0;
|
|
DBUG_ENTER("Field_new_decimal::store_value");
|
|
#ifndef DBUG_OFF
|
|
{
|
|
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;
|
|
}
|
|
#ifndef DBUG_OFF
|
|
{
|
|
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= my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
|
|
decimal_value, ptr, precision, dec);
|
|
|
|
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());
|
|
my_decimal2binary(E_DEC_FATAL_ERROR, &buff, 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)
|
|
{
|
|
ASSERT_COLUMN_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 alreay 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);
|
|
}
|
|
}
|
|
|
|
#ifndef DBUG_OFF
|
|
char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
|
|
DBUG_PRINT("enter", ("value: %s",
|
|
dbug_decimal_as_string(dbug_buff, &decimal_value)));
|
|
#endif
|
|
int err2;
|
|
if (store_value(&decimal_value, &err2))
|
|
DBUG_RETURN(1);
|
|
|
|
/*
|
|
E_DEC_TRUNCATED means minor truncation, a note should be enough:
|
|
- in err: str2my_decimal() truncated '1e-1000000000000' to 0.0
|
|
- in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2)
|
|
Also, we send a note if a string had some trailing spaces: '1.12 '
|
|
*/
|
|
if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
|
|
(err == E_DEC_TRUNCATED ||
|
|
err2 == E_DEC_TRUNCATED ||
|
|
end < from + length))
|
|
set_note(WARN_DATA_TRUNCATED, 1);
|
|
DBUG_RETURN(0);
|
|
}
|
|
|
|
|
|
/**
|
|
@todo
|
|
Fix following when double2my_decimal when double2decimal
|
|
will return E_DEC_TRUNCATED always correctly
|
|
*/
|
|
|
|
int Field_new_decimal::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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));
|
|
}
|
|
|
|
|
|
double Field_new_decimal::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
double dbl;
|
|
my_decimal decimal_value;
|
|
my_decimal2double(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), &dbl);
|
|
return dbl;
|
|
}
|
|
|
|
|
|
longlong Field_new_decimal::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
longlong i;
|
|
my_decimal decimal_value;
|
|
my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
|
|
unsigned_flag, &i);
|
|
return i;
|
|
}
|
|
|
|
|
|
my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
|
|
{
|
|
ASSERT_COLUMN_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);
|
|
}
|
|
|
|
|
|
String *Field_new_decimal::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
my_decimal decimal_value;
|
|
uint fixed_precision= zerofill ? precision : 0;
|
|
my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
|
|
fixed_precision, dec, '0', val_buffer);
|
|
val_buffer->set_charset(&my_charset_numeric);
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
bool Field_new_decimal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
|
|
{
|
|
my_decimal value;
|
|
return decimal_to_datetime_with_warn(val_decimal(&value),
|
|
ltime, fuzzydate, field_name.str);
|
|
}
|
|
|
|
|
|
int Field_new_decimal::cmp(const uchar *a,const uchar*b)
|
|
{
|
|
return memcmp(a, b, bin_size);
|
|
}
|
|
|
|
|
|
void Field_new_decimal::sort_string(uchar *buff,
|
|
uint)
|
|
{
|
|
memcpy(buff, ptr, bin_size);
|
|
}
|
|
|
|
|
|
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
|
|
*/
|
|
int Field_new_decimal::save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
*metadata_ptr= precision;
|
|
*(metadata_ptr + 1)= decimals();
|
|
return 2;
|
|
}
|
|
|
|
|
|
/**
|
|
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)
|
|
{
|
|
uint const source_precision= (field_metadata >> 8U) & 0x00ff;
|
|
uint const source_decimal= field_metadata & 0x00ff;
|
|
uint const source_size= my_decimal_get_binary_size(source_precision,
|
|
source_decimal);
|
|
return (source_size);
|
|
}
|
|
|
|
|
|
bool Field_new_decimal::compatible_field_size(uint field_metadata,
|
|
Relay_log_info * __attribute__((unused)),
|
|
uint16 mflags __attribute__((unused)),
|
|
int *order_var)
|
|
{
|
|
uint const source_precision= (field_metadata >> 8U) & 0x00ff;
|
|
uint const source_decimal= field_metadata & 0x00ff;
|
|
int order= compare(source_precision, precision);
|
|
*order_var= order != 0 ? order : compare(source_decimal, dec);
|
|
return true;
|
|
}
|
|
|
|
|
|
uint Field_new_decimal::is_equal(Create_field *new_field)
|
|
{
|
|
return ((new_field->type_handler() == type_handler()) &&
|
|
((new_field->flags & UNSIGNED_FLAG) ==
|
|
(uint) (flags & UNSIGNED_FLAG)) &&
|
|
((new_field->flags & AUTO_INCREMENT_FLAG) ==
|
|
(uint) (flags & AUTO_INCREMENT_FLAG)) &&
|
|
(new_field->length == max_display_length()) &&
|
|
(new_field->decimals == dec));
|
|
}
|
|
|
|
|
|
/**
|
|
Unpack a decimal field from row data.
|
|
|
|
This method is used to unpack a decimal or numeric field from a master
|
|
whose size of the field is less than that of the slave.
|
|
|
|
@param to Destination of the data
|
|
@param from Source of the data
|
|
@param param_data Precision (upper) and decimal (lower) values
|
|
|
|
@return New pointer into memory based on from + length of the data
|
|
*/
|
|
const uchar *
|
|
Field_new_decimal::unpack(uchar* to, const uchar *from, const uchar *from_end,
|
|
uint param_data)
|
|
{
|
|
if (param_data == 0)
|
|
return Field::unpack(to, from, from_end, param_data);
|
|
|
|
uint from_precision= (param_data & 0xff00) >> 8U;
|
|
uint from_decimal= param_data & 0x00ff;
|
|
uint length=pack_length();
|
|
uint from_pack_len= my_decimal_get_binary_size(from_precision, from_decimal);
|
|
uint len= (param_data && (from_pack_len < length)) ?
|
|
from_pack_len : length;
|
|
if ((from_pack_len && (from_pack_len < length)) ||
|
|
(from_precision < precision) ||
|
|
(from_decimal < decimals()))
|
|
{
|
|
/*
|
|
If the master's data is smaller than the slave, we need to convert
|
|
the binary to decimal then resize the decimal converting it back to
|
|
a decimal and write that to the raw data buffer.
|
|
*/
|
|
decimal_digit_t dec_buf[DECIMAL_MAX_PRECISION];
|
|
decimal_t dec_val;
|
|
dec_val.len= from_precision;
|
|
dec_val.buf= dec_buf;
|
|
/*
|
|
Note: bin2decimal does not change the length of the field. So it is
|
|
just the first step the resizing operation. The second step does the
|
|
resizing using the precision and decimals from the slave.
|
|
*/
|
|
bin2decimal((uchar *)from, &dec_val, from_precision, from_decimal);
|
|
decimal2bin(&dec_val, to, precision, decimals());
|
|
}
|
|
else
|
|
{
|
|
if (from + len > from_end)
|
|
return 0; // Wrong data
|
|
memcpy(to, from, len); // Sizes are the same, just copy the data.
|
|
}
|
|
return from+len;
|
|
}
|
|
|
|
|
|
Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx,
|
|
Item *const_item)
|
|
{
|
|
if (flags & ZEROFILL_FLAG)
|
|
return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item);
|
|
switch (ctx.subst_constraint()) {
|
|
case IDENTITY_SUBST:
|
|
if (const_item->field_type() != MYSQL_TYPE_NEWDECIMAL ||
|
|
const_item->decimal_scale() != decimals())
|
|
{
|
|
my_decimal *val, val_buffer, val_buffer2;
|
|
if (!(val= const_item->val_decimal(&val_buffer)))
|
|
{
|
|
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_round(E_DEC_FATAL_ERROR, val, decimals(), true, &val_buffer2);
|
|
return new (thd->mem_root) Item_decimal(thd, field_name.str,
|
|
&val_buffer2,
|
|
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);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** tiny int
|
|
****************************************************************************/
|
|
|
|
int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int tmp= unsigned_flag ? (int) ptr[0] :
|
|
(int) ((signed char*) ptr)[0];
|
|
return (double) tmp;
|
|
}
|
|
|
|
|
|
longlong Field_tiny::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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)))
|
|
{
|
|
ASSERT_COLUMN_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_binary(Protocol *protocol)
|
|
{
|
|
return protocol->store_tiny((longlong) (int8) ptr[0]);
|
|
}
|
|
|
|
int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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 */
|
|
}
|
|
|
|
void Field_tiny::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs=res.charset();
|
|
res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
|
|
"tinyint(%d)",(int) field_length));
|
|
add_zerofill_and_unsigned(res);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Field type short int (2 byte)
|
|
****************************************************************************/
|
|
|
|
int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int error= 0;
|
|
int16 res;
|
|
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0L && !unsigned_val)
|
|
{
|
|
res=0;
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if ((ulonglong) nr > (ulonglong) UINT_MAX16)
|
|
{
|
|
res=(int16) UINT_MAX16;
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
res=(int16) (uint16) nr;
|
|
}
|
|
else
|
|
{
|
|
if (nr < 0 && unsigned_val)
|
|
nr= UINT_MAX16+1; // Generate overflow
|
|
|
|
if (nr < INT_MIN16)
|
|
{
|
|
res=INT_MIN16;
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > (longlong) INT_MAX16)
|
|
{
|
|
res=INT_MAX16;
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
res=(int16) nr;
|
|
}
|
|
int2store(ptr,res);
|
|
return error;
|
|
}
|
|
|
|
|
|
double Field_short::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
short j;
|
|
j=sint2korr(ptr);
|
|
return unsigned_flag ? (double) (unsigned short) j : (double) j;
|
|
}
|
|
|
|
longlong Field_short::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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)))
|
|
{
|
|
ASSERT_COLUMN_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_binary(Protocol *protocol)
|
|
{
|
|
return protocol->store_short(Field_short::val_int());
|
|
}
|
|
|
|
|
|
int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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];
|
|
}
|
|
|
|
void Field_short::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs=res.charset();
|
|
res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
|
|
"smallint(%d)",(int) field_length));
|
|
add_zerofill_and_unsigned(res);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Field type medium int (3 byte)
|
|
****************************************************************************/
|
|
|
|
int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int error= 0;
|
|
nr=rint(nr);
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0)
|
|
{
|
|
int3store(ptr,0);
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr >= (double) (long) (1L << 24))
|
|
{
|
|
uint32 tmp=(uint32) (1L << 24)-1L;
|
|
int3store(ptr,tmp);
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
int3store(ptr,(uint32) nr);
|
|
}
|
|
else
|
|
{
|
|
if (nr < (double) INT_MIN24)
|
|
{
|
|
long tmp=(long) INT_MIN24;
|
|
int3store(ptr,tmp);
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > (double) INT_MAX24)
|
|
{
|
|
long tmp=(long) INT_MAX24;
|
|
int3store(ptr,tmp);
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
int3store(ptr,(long) nr);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_medium::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int error= 0;
|
|
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0 && !unsigned_val)
|
|
{
|
|
int3store(ptr,0);
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24))
|
|
{
|
|
long tmp= (long) (1L << 24)-1L;
|
|
int3store(ptr,tmp);
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
int3store(ptr,(uint32) nr);
|
|
}
|
|
else
|
|
{
|
|
if (nr < 0 && unsigned_val)
|
|
nr= (ulonglong) (long) (1L << 24); // Generate overflow
|
|
|
|
if (nr < (longlong) INT_MIN24)
|
|
{
|
|
long tmp= (long) INT_MIN24;
|
|
int3store(ptr,tmp);
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > (longlong) INT_MAX24)
|
|
{
|
|
long tmp=(long) INT_MAX24;
|
|
int3store(ptr,tmp);
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
int3store(ptr,(long) nr);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
|
|
double Field_medium::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
|
|
return (double) j;
|
|
}
|
|
|
|
|
|
longlong Field_medium::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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)))
|
|
{
|
|
ASSERT_COLUMN_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->cset->long10_to_str(cs, 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_binary(Protocol *protocol)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
return protocol->store_long(Field_medium::val_int());
|
|
}
|
|
|
|
|
|
int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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];
|
|
}
|
|
|
|
|
|
void Field_medium::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs=res.charset();
|
|
res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
|
|
"mediumint(%d)",(int) field_length));
|
|
add_zerofill_and_unsigned(res);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** long int
|
|
****************************************************************************/
|
|
|
|
int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
long store_tmp;
|
|
int error;
|
|
longlong rnd;
|
|
|
|
error= get_int(cs, from, len, &rnd, UINT_MAX32, INT_MIN32, INT_MAX32);
|
|
store_tmp= unsigned_flag ? (long) (ulonglong) rnd : (long) rnd;
|
|
int4store(ptr, store_tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_long::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int error= 0;
|
|
int32 res;
|
|
nr=rint(nr);
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0)
|
|
{
|
|
res=0;
|
|
error= 1;
|
|
}
|
|
else if (nr > (double) UINT_MAX32)
|
|
{
|
|
res= UINT_MAX32;
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
res=(int32) (ulong) nr;
|
|
}
|
|
else
|
|
{
|
|
if (nr < (double) INT_MIN32)
|
|
{
|
|
res=(int32) INT_MIN32;
|
|
error= 1;
|
|
}
|
|
else if (nr > (double) INT_MAX32)
|
|
{
|
|
res=(int32) INT_MAX32;
|
|
error= 1;
|
|
}
|
|
else
|
|
res=(int32) (longlong) nr;
|
|
}
|
|
if (unlikely(error))
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
|
|
int4store(ptr,res);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_long::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int error= 0;
|
|
int32 res;
|
|
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0 && !unsigned_val)
|
|
{
|
|
res=0;
|
|
error= 1;
|
|
}
|
|
else if ((ulonglong) nr >= (1LL << 32))
|
|
{
|
|
res=(int32) (uint32) ~0L;
|
|
error= 1;
|
|
}
|
|
else
|
|
res=(int32) (uint32) nr;
|
|
}
|
|
else
|
|
{
|
|
if (nr < 0 && unsigned_val)
|
|
nr= ((longlong) INT_MAX32) + 1; // Generate overflow
|
|
if (nr < (longlong) INT_MIN32)
|
|
{
|
|
res=(int32) INT_MIN32;
|
|
error= 1;
|
|
}
|
|
else if (nr > (longlong) INT_MAX32)
|
|
{
|
|
res=(int32) INT_MAX32;
|
|
error= 1;
|
|
}
|
|
else
|
|
res=(int32) nr;
|
|
}
|
|
if (unlikely(error))
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
|
|
int4store(ptr,res);
|
|
return error;
|
|
}
|
|
|
|
|
|
double Field_long::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int32 j;
|
|
j=sint4korr(ptr);
|
|
return unsigned_flag ? (double) (uint32) j : (double) j;
|
|
}
|
|
|
|
longlong Field_long::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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)))
|
|
{
|
|
ASSERT_COLUMN_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_binary(Protocol *protocol)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
return protocol->store_long(Field_long::val_int());
|
|
}
|
|
|
|
int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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];
|
|
}
|
|
|
|
|
|
void Field_long::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs=res.charset();
|
|
res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
|
|
"int(%d)",(int) field_length));
|
|
add_zerofill_and_unsigned(res);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Field type longlong int (8 bytes)
|
|
****************************************************************************/
|
|
|
|
int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int error= 0;
|
|
char *end;
|
|
ulonglong tmp;
|
|
|
|
tmp= cs->cset->strntoull10rnd(cs,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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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->cset->longlong10_to_str)(cs,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_binary(Protocol *protocol)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag);
|
|
}
|
|
|
|
|
|
int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs=res.charset();
|
|
res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
|
|
"bigint(%d)",(int) field_length));
|
|
add_zerofill_and_unsigned(res);
|
|
}
|
|
|
|
void Field_longlong::set_max()
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
set_notnull();
|
|
int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX);
|
|
}
|
|
|
|
bool Field_longlong::is_max()
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
if (unsigned_flag)
|
|
{
|
|
ulonglong j;
|
|
j= uint8korr(ptr);
|
|
return j == ULONGLONG_MAX;
|
|
}
|
|
longlong j;
|
|
j= sint8korr(ptr);
|
|
return j == LONGLONG_MAX;
|
|
}
|
|
|
|
/*
|
|
Floating-point numbers
|
|
*/
|
|
|
|
/****************************************************************************
|
|
single precision float
|
|
****************************************************************************/
|
|
|
|
int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs)
|
|
{
|
|
int error;
|
|
Field_float::store(get_double(from, len, cs, &error));
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_float::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
float j;
|
|
float4get(j,ptr);
|
|
return ((double) j);
|
|
}
|
|
|
|
longlong Field_float::val_int(void)
|
|
{
|
|
float j;
|
|
float4get(j,ptr);
|
|
return (longlong) rint(j);
|
|
}
|
|
|
|
|
|
String *Field_float::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
|
|
float nr;
|
|
float4get(nr,ptr);
|
|
|
|
uint to_length= 70;
|
|
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_FLOAT, to_length - 1, to, NULL);
|
|
else
|
|
{
|
|
/*
|
|
We are safe here because the buffer length is 70, and
|
|
fabs(float) < 10^39, dec < FLOATING_POINT_DECIMALS. So the resulting string
|
|
will be not longer than 69 chars + terminating '\0'.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
|
|
int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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_binary(Protocol *protocol)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
return protocol->store((float) Field_float::val_real(), dec, (String*) 0);
|
|
}
|
|
|
|
|
|
/**
|
|
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
|
|
*/
|
|
int Field_float::save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
*metadata_ptr= pack_length();
|
|
return 1;
|
|
}
|
|
|
|
|
|
void Field_float::sql_type(String &res) const
|
|
{
|
|
if (dec >= FLOATING_POINT_DECIMALS)
|
|
{
|
|
res.set_ascii(STRING_WITH_LEN("float"));
|
|
}
|
|
else
|
|
{
|
|
CHARSET_INFO *cs= res.charset();
|
|
res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
|
|
"float(%d,%d)",(int) field_length,dec));
|
|
}
|
|
add_zerofill_and_unsigned(res);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
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)
|
|
{
|
|
ASSERT_COLUMN_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, uint 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;
|
|
max_value= 1.0;
|
|
for (; order > step; order-= step)
|
|
max_value*= log_10[step];
|
|
max_value*= log_10[order];
|
|
max_value-= 1.0 / log_10[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_decimal(const my_decimal *dm)
|
|
{
|
|
double dbl;
|
|
my_decimal2double(E_DEC_FATAL_ERROR, dm, &dbl);
|
|
return store(dbl);
|
|
}
|
|
|
|
int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
|
|
{
|
|
return store(TIME_to_double(ltime));
|
|
}
|
|
|
|
|
|
double Field_double::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value);
|
|
return decimal_value;
|
|
}
|
|
|
|
|
|
bool Field_real::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
double nr= val_real();
|
|
return double_to_datetime_with_warn(nr, ltime, fuzzydate, 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;
|
|
}
|
|
|
|
|
|
String *Field_double::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ASSERT_COLUMN_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_binary(Protocol *protocol)
|
|
{
|
|
return protocol->store((double) Field_double::val_real(), dec, (String*) 0);
|
|
}
|
|
|
|
|
|
int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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
|
|
*/
|
|
int Field_double::save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
*metadata_ptr= pack_length();
|
|
return 1;
|
|
}
|
|
|
|
|
|
void Field_double::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs=res.charset();
|
|
if (dec >= FLOATING_POINT_DECIMALS)
|
|
{
|
|
res.set_ascii(STRING_WITH_LEN("double"));
|
|
}
|
|
else
|
|
{
|
|
res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
|
|
"double(%d,%d)",(int) field_length,dec));
|
|
}
|
|
add_zerofill_and_unsigned(res);
|
|
}
|
|
|
|
|
|
/**
|
|
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 automaticly updated on insert.
|
|
*/
|
|
flags|= TIMESTAMP_FLAG;
|
|
if (unireg_check != TIMESTAMP_DN_FIELD)
|
|
flags|= ON_UPDATE_NOW_FLAG;
|
|
}
|
|
}
|
|
|
|
|
|
int Field_timestamp::save_in_field(Field *to)
|
|
{
|
|
ulong sec_part;
|
|
my_time_t ts= get_timestamp(&sec_part);
|
|
return to->store_timestamp(ts, sec_part);
|
|
}
|
|
|
|
my_time_t Field_timestamp::get_timestamp(const uchar *pos,
|
|
ulong *sec_part) const
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
*sec_part= 0;
|
|
return sint4korr(pos);
|
|
}
|
|
|
|
|
|
int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
|
|
const ErrConv *str,
|
|
int was_cut,
|
|
bool have_smth_to_conv)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
uint error = 0;
|
|
my_time_t timestamp;
|
|
|
|
if (MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) || !have_smth_to_conv)
|
|
{
|
|
error= 1;
|
|
set_datetime_warning(WARN_DATA_TRUNCATED,
|
|
str, MYSQL_TIMESTAMP_DATETIME, 1);
|
|
}
|
|
else if (MYSQL_TIME_WARN_HAVE_NOTES(was_cut))
|
|
{
|
|
error= 3;
|
|
set_datetime_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED,
|
|
str, MYSQL_TIMESTAMP_DATETIME, 1);
|
|
}
|
|
/* Only convert a correct date (not a zero date) */
|
|
if (have_smth_to_conv && l_time->month)
|
|
{
|
|
uint conversion_error;
|
|
timestamp= TIME_to_timestamp(thd, l_time, &conversion_error);
|
|
if (timestamp == 0 && l_time->second_part == 0)
|
|
conversion_error= ER_WARN_DATA_OUT_OF_RANGE;
|
|
if (unlikely(conversion_error))
|
|
{
|
|
set_datetime_warning(conversion_error,
|
|
str, MYSQL_TIMESTAMP_DATETIME, !error);
|
|
error= 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
timestamp= 0;
|
|
l_time->second_part= 0;
|
|
}
|
|
store_TIME(timestamp, l_time->second_part);
|
|
return error;
|
|
}
|
|
|
|
|
|
static bool
|
|
copy_or_convert_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
|
|
{
|
|
if (from->time_type == MYSQL_TIMESTAMP_TIME)
|
|
return time_to_datetime(thd, from, to);
|
|
*to= *from;
|
|
return false;
|
|
}
|
|
|
|
|
|
sql_mode_t Field_timestamp::sql_mode_for_timestamp(THD *thd) const
|
|
{
|
|
// We don't want to store invalid or fuzzy datetime values in TIMESTAMP
|
|
return (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 unused;
|
|
ErrConvTime str(ltime);
|
|
THD *thd= get_thd();
|
|
MYSQL_TIME l_time;
|
|
bool valid= !copy_or_convert_to_datetime(thd, ltime, &l_time) &&
|
|
!check_date(&l_time, pack_time(&l_time) != 0,
|
|
sql_mode_for_timestamp(thd), &unused);
|
|
return store_TIME_with_warning(thd, &l_time, &str, false, valid);
|
|
}
|
|
|
|
|
|
int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs)
|
|
{
|
|
MYSQL_TIME l_time;
|
|
MYSQL_TIME_STATUS status;
|
|
bool have_smth_to_conv;
|
|
ErrConvString str(from, len, cs);
|
|
THD *thd= get_thd();
|
|
|
|
have_smth_to_conv= !str_to_datetime(cs, from, len, &l_time,
|
|
sql_mode_for_timestamp(thd), &status);
|
|
return store_TIME_with_warning(thd, &l_time, &str,
|
|
status.warnings, have_smth_to_conv);
|
|
}
|
|
|
|
|
|
int Field_timestamp::store(double nr)
|
|
{
|
|
MYSQL_TIME l_time;
|
|
int error;
|
|
ErrConvDouble str(nr);
|
|
THD *thd= get_thd();
|
|
|
|
longlong tmp= double_to_datetime(nr, &l_time, sql_mode_for_timestamp(thd),
|
|
&error);
|
|
return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
|
|
}
|
|
|
|
|
|
int Field_timestamp::store(longlong nr, bool unsigned_val)
|
|
{
|
|
MYSQL_TIME l_time;
|
|
int error;
|
|
ErrConvInteger str(nr, unsigned_val);
|
|
THD *thd= get_thd();
|
|
|
|
longlong tmp= number_to_datetime(nr, 0, &l_time, sql_mode_for_timestamp(thd),
|
|
&error);
|
|
return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
|
|
}
|
|
|
|
|
|
int Field_timestamp::store_timestamp(my_time_t ts, ulong sec_part)
|
|
{
|
|
store_TIME(ts, sec_part);
|
|
if (ts == 0 && sec_part == 0 &&
|
|
get_thd()->variables.sql_mode & 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, MYSQL_TIMESTAMP_DATETIME, 1);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
double Field_timestamp::val_real(void)
|
|
{
|
|
return (double) Field_timestamp::val_int();
|
|
}
|
|
|
|
|
|
longlong Field_timestamp::val_int(void)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
if (get_date(<ime, TIME_NO_ZERO_DATE))
|
|
return 0;
|
|
|
|
return ltime.year * 10000000000LL + ltime.month * 100000000LL +
|
|
ltime.day * 1000000L + ltime.hour * 10000L +
|
|
ltime.minute * 100 + ltime.second;
|
|
}
|
|
|
|
|
|
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
uint32 temp, temp2;
|
|
uint dec;
|
|
char *to;
|
|
|
|
val_buffer->alloc(field_length+1);
|
|
to= (char*) val_buffer->ptr();
|
|
val_buffer->length(field_length);
|
|
|
|
if (get_date(<ime, TIME_NO_ZERO_DATE))
|
|
{ /* Zero time is "000000" */
|
|
val_ptr->set(zero_timestamp, field_length, &my_charset_numeric);
|
|
return val_ptr;
|
|
}
|
|
val_buffer->set_charset(&my_charset_numeric); // Safety
|
|
|
|
temp= ltime.year % 100;
|
|
if (temp < YY_PART_YEAR - 1)
|
|
{
|
|
*to++= '2';
|
|
*to++= '0';
|
|
}
|
|
else
|
|
{
|
|
*to++= '1';
|
|
*to++= '9';
|
|
}
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to++= '-';
|
|
temp=ltime.month;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to++= '-';
|
|
temp=ltime.day;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to++= ' ';
|
|
temp=ltime.hour;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to++= ':';
|
|
temp=ltime.minute;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to++= ':';
|
|
temp=ltime.second;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to= 0;
|
|
val_buffer->set_charset(&my_charset_numeric);
|
|
|
|
if ((dec= decimals()))
|
|
{
|
|
ulong sec_part= (ulong) sec_part_shift(ltime.second_part, dec);
|
|
char *buf= const_cast<char*>(val_buffer->ptr() + MAX_DATETIME_WIDTH);
|
|
for (int i= dec; i > 0; i--, sec_part/= 10)
|
|
buf[i]= (char)(sec_part % 10) + '0';
|
|
buf[0]= '.';
|
|
buf[dec + 1]= 0;
|
|
}
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
bool
|
|
Field_timestamp::validate_value_in_record(THD *thd, const uchar *record) const
|
|
{
|
|
DBUG_ASSERT(!is_null_in_record(record));
|
|
ulong sec_part;
|
|
return !get_timestamp(ptr_in_record(record), &sec_part) && !sec_part &&
|
|
(sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != 0;
|
|
}
|
|
|
|
|
|
bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong 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_timestamp::send_binary(Protocol *protocol)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
Field_timestamp::get_date(<ime, 0);
|
|
return protocol->store(<ime, 0);
|
|
}
|
|
|
|
|
|
int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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_timestamp::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_timestamp::sql_type(String &res) const
|
|
{
|
|
if (!decimals())
|
|
{
|
|
res.set_ascii(STRING_WITH_LEN("timestamp"));
|
|
return;
|
|
}
|
|
CHARSET_INFO *cs=res.charset();
|
|
res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
|
|
"timestamp(%u)", decimals()));
|
|
}
|
|
|
|
|
|
int Field_timestamp::set_time()
|
|
{
|
|
set_notnull();
|
|
store_TIME(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
|
|
|
|
static void store_lowendian(ulonglong num, uchar *to, uint bytes)
|
|
{
|
|
switch(bytes) {
|
|
case 1: *to= (uchar)num; break;
|
|
case 2: int2store(to, num); break;
|
|
case 3: int3store(to, num); break;
|
|
case 4: int4store(to, num); break;
|
|
case 8: int8store(to, num); break;
|
|
default: DBUG_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static longlong read_lowendian(const uchar *from, uint bytes)
|
|
{
|
|
switch(bytes) {
|
|
case 1: return from[0];
|
|
case 2: return uint2korr(from);
|
|
case 3: return uint3korr(from);
|
|
case 4: return uint4korr(from);
|
|
case 8: return sint8korr(from);
|
|
default: DBUG_ASSERT(0); return 0;
|
|
}
|
|
}
|
|
|
|
void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part)
|
|
{
|
|
mi_int4store(ptr, timestamp);
|
|
store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes(dec));
|
|
}
|
|
|
|
my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos,
|
|
ulong *sec_part) const
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
*sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes(dec)), dec);
|
|
return mi_uint4korr(pos);
|
|
}
|
|
|
|
double Field_timestamp_with_dec::val_real(void)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
if (get_date(<ime, TIME_NO_ZERO_DATE))
|
|
return 0;
|
|
|
|
return ltime.year * 1e10 + ltime.month * 1e8 +
|
|
ltime.day * 1e6 + ltime.hour * 1e4 +
|
|
ltime.minute * 1e2 + ltime.second + ltime.second_part*1e-6;
|
|
}
|
|
|
|
my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
get_date(<ime, 0);
|
|
return TIME_to_my_decimal(<ime, d);
|
|
}
|
|
|
|
int Field_timestamp::store_decimal(const my_decimal *d)
|
|
{
|
|
ulonglong nr;
|
|
ulong sec_part;
|
|
int error;
|
|
MYSQL_TIME ltime;
|
|
longlong tmp;
|
|
THD *thd= get_thd();
|
|
ErrConvDecimal str(d);
|
|
|
|
if (my_decimal2seconds(d, &nr, &sec_part))
|
|
{
|
|
tmp= -1;
|
|
error= 2;
|
|
}
|
|
else
|
|
tmp= number_to_datetime(nr, sec_part, <ime, sql_mode_for_timestamp(thd),
|
|
&error);
|
|
return store_TIME_with_warning(thd, <ime, &str, error, tmp != -1);
|
|
}
|
|
|
|
int Field_timestamp_with_dec::set_time()
|
|
{
|
|
THD *thd= get_thd();
|
|
set_notnull();
|
|
// Avoid writing microseconds into binlog for FSP=0
|
|
store_TIME(thd->query_start(), decimals() ? thd->query_start_sec_part() : 0);
|
|
return 0;
|
|
}
|
|
|
|
bool Field_timestamp_with_dec::send_binary(Protocol *protocol)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
Field_timestamp::get_date(<ime, 0);
|
|
return protocol->store(<ime, dec);
|
|
}
|
|
|
|
|
|
int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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_TIME(my_time_t timestamp, ulong sec_part)
|
|
{
|
|
struct timeval tm;
|
|
tm.tv_sec= timestamp;
|
|
tm.tv_usec= sec_part;
|
|
my_timeval_trunc(&tm, dec);
|
|
my_timestamp_to_binary(&tm, ptr, dec);
|
|
}
|
|
|
|
void Field_timestampf::set_max()
|
|
{
|
|
DBUG_ENTER("Field_timestampf::set_max");
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS);
|
|
|
|
set_notnull();
|
|
mi_int4store(ptr, TIMESTAMP_MAX_VALUE);
|
|
mi_int3store(ptr + 4, TIME_MAX_SECOND_PART);
|
|
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
bool Field_timestampf::is_max()
|
|
{
|
|
DBUG_ENTER("Field_timestampf::is_max");
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
|
|
DBUG_RETURN(mi_sint4korr(ptr) == TIMESTAMP_MAX_VALUE &&
|
|
mi_sint3korr(ptr + 4) == TIME_MAX_SECOND_PART);
|
|
}
|
|
|
|
my_time_t Field_timestampf::get_timestamp(const uchar *pos,
|
|
ulong *sec_part) const
|
|
{
|
|
struct timeval tm;
|
|
my_timestamp_from_binary(&tm, pos, dec);
|
|
*sec_part= tm.tv_usec;
|
|
return tm.tv_sec;
|
|
}
|
|
|
|
|
|
/*************************************************************/
|
|
uint Field_temporal::is_equal(Create_field *new_field)
|
|
{
|
|
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,
|
|
timestamp_type ts_type)
|
|
{
|
|
/*
|
|
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, ts_type, 1);
|
|
if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
|
|
set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, ts_type, 1);
|
|
}
|
|
|
|
|
|
/*
|
|
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_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime,
|
|
const ErrConv *str,
|
|
int was_cut,
|
|
int have_smth_to_conv)
|
|
{
|
|
Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN;
|
|
int ret= 2;
|
|
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
|
|
if (was_cut == 0 && have_smth_to_conv == 0) // special case: zero date
|
|
{
|
|
was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
|
|
}
|
|
else if (!have_smth_to_conv)
|
|
{
|
|
bzero(ltime, sizeof(*ltime));
|
|
was_cut= MYSQL_TIME_WARN_TRUNCATED;
|
|
ret= 1;
|
|
}
|
|
else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) &&
|
|
(MYSQL_TIME_WARN_HAVE_NOTES(was_cut) ||
|
|
(type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_DATE &&
|
|
(ltime->hour || ltime->minute || ltime->second || ltime->second_part))))
|
|
{
|
|
trunc_level= Sql_condition::WARN_LEVEL_NOTE;
|
|
was_cut|= MYSQL_TIME_WARN_TRUNCATED;
|
|
ret= 3;
|
|
}
|
|
set_warnings(trunc_level, str, was_cut,
|
|
type_handler()->mysql_timestamp_type());
|
|
store_TIME(ltime);
|
|
return was_cut ? ret : 0;
|
|
}
|
|
|
|
|
|
int Field_temporal_with_date::store(const char *from, size_t len, CHARSET_INFO *cs)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
MYSQL_TIME_STATUS status;
|
|
THD *thd= get_thd();
|
|
ErrConvString str(from, len, cs);
|
|
bool func_res= !str_to_datetime(cs, from, len, <ime,
|
|
sql_mode_for_dates(thd),
|
|
&status);
|
|
return store_TIME_with_warning(<ime, &str, status.warnings, func_res);
|
|
}
|
|
|
|
|
|
int Field_temporal_with_date::store(double nr)
|
|
{
|
|
int error= 0;
|
|
MYSQL_TIME ltime;
|
|
THD *thd= get_thd();
|
|
ErrConvDouble str(nr);
|
|
|
|
longlong tmp= double_to_datetime(nr, <ime,
|
|
(uint) sql_mode_for_dates(thd), &error);
|
|
return store_TIME_with_warning(<ime, &str, error, tmp != -1);
|
|
}
|
|
|
|
|
|
int Field_temporal_with_date::store(longlong nr, bool unsigned_val)
|
|
{
|
|
int error;
|
|
MYSQL_TIME ltime;
|
|
longlong tmp;
|
|
THD *thd= get_thd();
|
|
ErrConvInteger str(nr, unsigned_val);
|
|
|
|
tmp= number_to_datetime(nr, 0, <ime, sql_mode_for_dates(thd), &error);
|
|
|
|
return store_TIME_with_warning(<ime, &str, error, tmp != -1);
|
|
}
|
|
|
|
|
|
int Field_temporal_with_date::store_time_dec(const MYSQL_TIME *ltime, uint dec)
|
|
{
|
|
int error= 0, have_smth_to_conv= 1;
|
|
ErrConvTime str(ltime);
|
|
MYSQL_TIME l_time;
|
|
|
|
if (copy_or_convert_to_datetime(get_thd(), ltime, &l_time))
|
|
{
|
|
/*
|
|
Set have_smth_to_conv and error in a way to have
|
|
store_TIME_with_warning do bzero().
|
|
*/
|
|
have_smth_to_conv= false;
|
|
error= MYSQL_TIME_WARN_OUT_OF_RANGE;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
We don't perform range checking here since values stored in TIME
|
|
structure always fit into DATETIME range.
|
|
*/
|
|
have_smth_to_conv= !check_date(&l_time, pack_time(&l_time) != 0,
|
|
sql_mode_for_dates(get_thd()), &error);
|
|
}
|
|
return store_TIME_with_warning(&l_time, &str, error, have_smth_to_conv);
|
|
}
|
|
|
|
|
|
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), sql_mode_for_dates(thd));
|
|
}
|
|
|
|
|
|
my_decimal *Field_temporal::val_decimal(my_decimal *d)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
if (get_date(<ime, 0))
|
|
{
|
|
bzero(<ime, sizeof(ltime));
|
|
ltime.time_type= type_handler()->mysql_timestamp_type();
|
|
}
|
|
return TIME_to_my_decimal(<ime, d);
|
|
}
|
|
|
|
|
|
bool Field_temporal::can_optimize_keypart_ref(const Item_bool_func *cond,
|
|
const Item *value) const
|
|
{
|
|
return true; // Field is of TIME_RESULT, which supersedes everything else.
|
|
}
|
|
|
|
|
|
bool Field_temporal::can_optimize_group_min_max(const Item_bool_func *cond,
|
|
const Item *const_item) const
|
|
{
|
|
return true; // Field is of TIME_RESULT, which supersedes everything else.
|
|
}
|
|
|
|
|
|
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 dt(thd, const_item, 0);
|
|
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.get_mysql_time(),
|
|
decimals());
|
|
}
|
|
break;
|
|
case ANY_SUBST:
|
|
if (!is_temporal_type_with_date(const_item->field_type()))
|
|
{
|
|
Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES);
|
|
if (!dt.is_valid_datetime())
|
|
return NULL;
|
|
return new (thd->mem_root)
|
|
Item_datetime_literal_for_invalid_dates(thd, dt.get_mysql_time(),
|
|
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
|
|
****************************************************************************/
|
|
int Field_time::store_TIME_with_warning(MYSQL_TIME *ltime,
|
|
const ErrConv *str,
|
|
int was_cut,
|
|
int have_smth_to_conv)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
|
|
if (!have_smth_to_conv)
|
|
{
|
|
bzero(ltime, sizeof(*ltime));
|
|
store_TIME(ltime);
|
|
set_warnings(Sql_condition::WARN_LEVEL_WARN, str, MYSQL_TIME_WARN_TRUNCATED);
|
|
return 1;
|
|
}
|
|
if (ltime->year != 0 || ltime->month != 0)
|
|
{
|
|
ltime->year= ltime->month= ltime->day= 0;
|
|
was_cut|= MYSQL_TIME_NOTE_TRUNCATED;
|
|
}
|
|
my_time_trunc(ltime, decimals());
|
|
store_TIME(ltime);
|
|
if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) &&
|
|
MYSQL_TIME_WARN_HAVE_NOTES(was_cut))
|
|
{
|
|
set_warnings(Sql_condition::WARN_LEVEL_NOTE, str,
|
|
was_cut | MYSQL_TIME_WARN_TRUNCATED);
|
|
return 3;
|
|
}
|
|
set_warnings(Sql_condition::WARN_LEVEL_WARN, str, was_cut);
|
|
return was_cut ? 2 : 0;
|
|
}
|
|
|
|
|
|
void Field_time::store_TIME(const MYSQL_TIME *ltime)
|
|
{
|
|
DBUG_ASSERT(ltime->year == 0);
|
|
DBUG_ASSERT(ltime->month == 0);
|
|
long tmp= (ltime->day*24L+ltime->hour)*10000L +
|
|
(ltime->minute*100+ltime->second);
|
|
if (ltime->neg)
|
|
tmp= -tmp;
|
|
int3store(ptr,tmp);
|
|
}
|
|
|
|
int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
MYSQL_TIME_STATUS status;
|
|
ErrConvString str(from, len, cs);
|
|
bool have_smth_to_conv=
|
|
!str_to_time(cs, from, len, <ime, sql_mode_for_dates(get_thd()),
|
|
&status);
|
|
|
|
return store_TIME_with_warning(<ime, &str,
|
|
status.warnings, have_smth_to_conv);
|
|
}
|
|
|
|
|
|
/**
|
|
subtract a given number of days from DATETIME, return TIME
|
|
|
|
optimized version of calc_time_diff()
|
|
|
|
@note it might generate TIME values outside of the valid TIME range!
|
|
*/
|
|
static void calc_datetime_days_diff(MYSQL_TIME *ltime, long days)
|
|
{
|
|
long daydiff= calc_daynr(ltime->year, ltime->month, ltime->day) - days;
|
|
ltime->year= ltime->month= 0;
|
|
if (daydiff >=0 )
|
|
{
|
|
ltime->day= daydiff;
|
|
ltime->time_type= MYSQL_TIMESTAMP_TIME;
|
|
}
|
|
else
|
|
{
|
|
longlong timediff= ((((daydiff * 24LL +
|
|
ltime->hour) * 60LL +
|
|
ltime->minute) * 60LL +
|
|
ltime->second) * 1000000LL +
|
|
ltime->second_part);
|
|
unpack_time(timediff, ltime, MYSQL_TIMESTAMP_TIME);
|
|
}
|
|
}
|
|
|
|
|
|
int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec)
|
|
{
|
|
MYSQL_TIME l_time= *ltime;
|
|
ErrConvTime str(ltime);
|
|
int was_cut= 0;
|
|
|
|
if (curdays && l_time.time_type != MYSQL_TIMESTAMP_TIME)
|
|
calc_datetime_days_diff(&l_time, curdays);
|
|
|
|
int have_smth_to_conv= !check_time_range(&l_time, decimals(), &was_cut);
|
|
return store_TIME_with_warning(&l_time, &str, was_cut, have_smth_to_conv);
|
|
}
|
|
|
|
|
|
int Field_time::store(double nr)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
ErrConvDouble str(nr);
|
|
int was_cut;
|
|
bool neg= nr < 0;
|
|
if (neg)
|
|
nr= -nr;
|
|
int have_smth_to_conv= !number_to_time(neg, (ulonglong) nr,
|
|
(ulong)((nr - floor(nr)) * TIME_SECOND_PART_FACTOR),
|
|
<ime, &was_cut);
|
|
|
|
return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv);
|
|
}
|
|
|
|
|
|
int Field_time::store(longlong nr, bool unsigned_val)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
ErrConvInteger str(nr, unsigned_val);
|
|
int was_cut;
|
|
if (nr < 0 && unsigned_val)
|
|
nr= 99991231235959LL + 1;
|
|
int have_smth_to_conv= !number_to_time(nr < 0,
|
|
(ulonglong) (nr < 0 ? -nr : nr),
|
|
0, <ime, &was_cut);
|
|
|
|
return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv);
|
|
}
|
|
|
|
|
|
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_time::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
uint32 j= (uint32) uint3korr(ptr);
|
|
return (double) j;
|
|
}
|
|
|
|
longlong Field_time::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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)))
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
MYSQL_TIME ltime;
|
|
get_date(<ime, TIME_TIME_ONLY);
|
|
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(ulonglong fuzzydate)
|
|
{
|
|
if (!(fuzzydate & TIME_TIME_ONLY) && (fuzzydate & 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_time::get_date(MYSQL_TIME *ltime, ulonglong 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;
|
|
}
|
|
|
|
|
|
bool Field_time::send_binary(Protocol *protocol)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
get_date(<ime, TIME_TIME_ONLY);
|
|
return protocol->store_time(<ime, decimals());
|
|
}
|
|
|
|
|
|
int Field_time::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
int32 a,b;
|
|
a=(int32) sint3korr(a_ptr);
|
|
b=(int32) sint3korr(b_ptr);
|
|
return (a < b) ? -1 : (a > b) ? 1 : 0;
|
|
}
|
|
|
|
void Field_time::sort_string(uchar *to,uint length __attribute__((unused)))
|
|
{
|
|
to[0] = (uchar) (ptr[2] ^ 128);
|
|
to[1] = ptr[1];
|
|
to[2] = ptr[0];
|
|
}
|
|
|
|
void Field_time::sql_type(String &res) const
|
|
{
|
|
if (decimals() == 0)
|
|
{
|
|
res.set_ascii(STRING_WITH_LEN("time"));
|
|
return;
|
|
}
|
|
CHARSET_INFO *cs= res.charset();
|
|
res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
|
|
"time(%d)", decimals()));
|
|
}
|
|
|
|
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)
|
|
{
|
|
ulonglong nr;
|
|
ulong sec_part;
|
|
ErrConvDecimal str(d);
|
|
MYSQL_TIME ltime;
|
|
int was_cut;
|
|
bool neg= my_decimal2seconds(d, &nr, &sec_part);
|
|
|
|
int have_smth_to_conv= !number_to_time(neg, nr, sec_part, <ime, &was_cut);
|
|
|
|
return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv);
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
switch (ctx.subst_constraint()) {
|
|
case ANY_SUBST:
|
|
if (const_item->field_type() != MYSQL_TYPE_TIME)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
// Get the value of const_item with conversion from DATETIME to TIME
|
|
ulonglong fuzzydate= Time::comparison_flags_for_get_date();
|
|
if (const_item->get_time_with_conversion(thd, <ime, fuzzydate))
|
|
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, <ime,
|
|
ltime.second_part ?
|
|
TIME_SECOND_PART_DIGITS :
|
|
0);
|
|
}
|
|
break;
|
|
case IDENTITY_SUBST:
|
|
if (const_item->field_type() != MYSQL_TYPE_TIME ||
|
|
const_item->decimals != decimals())
|
|
{
|
|
MYSQL_TIME ltime;
|
|
if (const_item->get_time_with_conversion(thd, <ime, TIME_TIME_ONLY))
|
|
return NULL;
|
|
/*
|
|
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.
|
|
*/
|
|
return new (thd->mem_root) Item_time_literal(thd, <ime, decimals());
|
|
}
|
|
break;
|
|
}
|
|
return const_item;
|
|
}
|
|
|
|
|
|
longlong Field_time_with_dec::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
MYSQL_TIME ltime;
|
|
get_date(<ime, TIME_TIME_ONLY);
|
|
longlong val= TIME_to_ulonglong_time(<ime);
|
|
return ltime.neg ? -val : val;
|
|
}
|
|
|
|
double Field_time_with_dec::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
MYSQL_TIME ltime;
|
|
get_date(<ime, TIME_TIME_ONLY);
|
|
return TIME_to_double(<ime);
|
|
}
|
|
|
|
bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong 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)
|
|
{
|
|
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, ulonglong 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;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** 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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
char *end;
|
|
int error;
|
|
longlong nr= cs->cset->strntoull10rnd(cs, 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 (get_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)
|
|
{
|
|
ASSERT_COLUMN_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 dec_arg)
|
|
{
|
|
ErrConvTime str(ltime);
|
|
if (Field_year::store(ltime->year, 0))
|
|
return 1;
|
|
|
|
set_datetime_warning(WARN_DATA_TRUNCATED, &str, ltime->time_type, 1);
|
|
return 0;
|
|
}
|
|
|
|
bool Field_year::send_binary(Protocol *protocol)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
DBUG_ASSERT(field_length == 2 || field_length == 4);
|
|
int tmp= (int) ptr[0];
|
|
if (field_length != 4)
|
|
tmp%=100; // Return last 2 char
|
|
else if (tmp)
|
|
tmp+=1900;
|
|
return (longlong) tmp;
|
|
}
|
|
|
|
|
|
String *Field_year::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
DBUG_ASSERT(field_length < 5);
|
|
val_buffer->alloc(5);
|
|
val_buffer->length(field_length);
|
|
char *to=(char*) val_buffer->ptr();
|
|
sprintf(to,field_length == 2 ? "%02d" : "%04d",(int) Field_year::val_int());
|
|
val_buffer->set_charset(&my_charset_numeric);
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
bool Field_year::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
|
|
{
|
|
int tmp= (int) ptr[0];
|
|
if (tmp || field_length != 4)
|
|
tmp+= 1900;
|
|
return int_to_datetime_with_warn(false, tmp * 10000,
|
|
ltime, fuzzydate, 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));
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** date type
|
|
** In string context: YYYY-MM-DD
|
|
** In number context: YYYYMMDD
|
|
** Stored as a 4 byte unsigned int
|
|
****************************************************************************/
|
|
|
|
void Field_date::store_TIME(MYSQL_TIME *ltime)
|
|
{
|
|
uint tmp= ltime->year*10000L + ltime->month*100+ltime->day;
|
|
int4store(ptr,tmp);
|
|
}
|
|
|
|
bool Field_date::send_binary(Protocol *protocol)
|
|
{
|
|
longlong tmp= Field_date::val_int();
|
|
MYSQL_TIME tm;
|
|
tm.year= (uint32) tmp/10000L % 10000;
|
|
tm.month= (uint32) tmp/100 % 100;
|
|
tm.day= (uint32) tmp % 100;
|
|
return protocol->store_date(&tm);
|
|
}
|
|
|
|
|
|
double Field_date::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int32 j;
|
|
j=sint4korr(ptr);
|
|
return (double) (uint32) j;
|
|
}
|
|
|
|
|
|
longlong Field_date::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int32 j;
|
|
j=sint4korr(ptr);
|
|
return (longlong) (uint32) j;
|
|
}
|
|
|
|
|
|
bool Field_date::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
|
|
ulonglong fuzzydate) const
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int32 tmp= sint4korr(pos);
|
|
ltime->year= (int) ((uint32) tmp/10000L % 10000);
|
|
ltime->month= (int) ((uint32) tmp/100 % 100);
|
|
ltime->day= (int) ((uint32) tmp % 100);
|
|
ltime->time_type= MYSQL_TIMESTAMP_DATE;
|
|
ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0;
|
|
return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
|
|
}
|
|
|
|
|
|
String *Field_date::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
MYSQL_TIME ltime;
|
|
get_TIME(<ime, ptr, 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)
|
|
{
|
|
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(MYSQL_TIME *ltime)
|
|
{
|
|
uint tmp= ltime->year*16*32 + ltime->month*32+ltime->day;
|
|
int3store(ptr,tmp);
|
|
}
|
|
|
|
|
|
bool Field_newdate::send_binary(Protocol *protocol)
|
|
{
|
|
MYSQL_TIME tm;
|
|
Field_newdate::get_date(&tm,0);
|
|
return protocol->store_date(&tm);
|
|
}
|
|
|
|
|
|
double Field_newdate::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
return (double) Field_newdate::val_int();
|
|
}
|
|
|
|
|
|
longlong Field_newdate::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
ulong j= uint3korr(ptr);
|
|
j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L;
|
|
return (longlong) j;
|
|
}
|
|
|
|
|
|
String *Field_newdate::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
val_buffer->alloc(field_length);
|
|
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,
|
|
ulonglong fuzzydate) const
|
|
{
|
|
ASSERT_COLUMN_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);
|
|
}
|
|
|
|
|
|
int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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()))
|
|
{
|
|
// Get the value of const_item with conversion from TIME to DATETIME
|
|
Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES);
|
|
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.get_mysql_time(),
|
|
dt.get_mysql_time()->
|
|
second_part ?
|
|
TIME_SECOND_PART_DIGITS : 0);
|
|
return new (thd->mem_root)
|
|
Item_date_literal_for_invalid_dates(thd, Date(&dt).get_mysql_time());
|
|
}
|
|
break;
|
|
case IDENTITY_SUBST:
|
|
if (const_item->field_type() != MYSQL_TYPE_DATE)
|
|
{
|
|
Date d(thd, const_item, 0);
|
|
if (!d.is_valid_date())
|
|
return NULL;
|
|
return new (thd->mem_root) Item_date_literal(thd, d.get_mysql_time());
|
|
}
|
|
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_datetime::store_TIME(MYSQL_TIME *ltime)
|
|
{
|
|
ulonglong tmp= TIME_to_ulonglong_datetime(ltime);
|
|
int8store(ptr,tmp);
|
|
}
|
|
|
|
bool Field_datetime::send_binary(Protocol *protocol)
|
|
{
|
|
MYSQL_TIME tm;
|
|
Field_datetime::get_date(&tm, 0);
|
|
return protocol->store(&tm, 0);
|
|
}
|
|
|
|
|
|
double Field_datetime::val_real(void)
|
|
{
|
|
return (double) Field_datetime::val_int();
|
|
}
|
|
|
|
longlong Field_datetime::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
longlong j;
|
|
j=sint8korr(ptr);
|
|
return j;
|
|
}
|
|
|
|
|
|
String *Field_datetime::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
val_buffer->alloc(field_length);
|
|
val_buffer->length(field_length);
|
|
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
ulonglong tmp;
|
|
long part1,part2;
|
|
char *pos;
|
|
int part3;
|
|
|
|
tmp= Field_datetime::val_int();
|
|
|
|
/*
|
|
Avoid problem with slow longlong arithmetic and sprintf
|
|
*/
|
|
|
|
part1=(long) (tmp/1000000LL);
|
|
part2=(long) (tmp - (ulonglong) part1*1000000LL);
|
|
|
|
pos=(char*) val_buffer->ptr() + MAX_DATETIME_WIDTH;
|
|
*pos--=0;
|
|
*pos--= (char) ('0'+(char) (part2%10)); part2/=10;
|
|
*pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10);
|
|
*pos--= ':';
|
|
*pos--= (char) ('0'+(char) (part3%10)); part3/=10;
|
|
*pos--= (char) ('0'+(char) (part3%10)); part3/=10;
|
|
*pos--= ':';
|
|
*pos--= (char) ('0'+(char) (part3%10)); part3/=10;
|
|
*pos--= (char) ('0'+(char) part3);
|
|
*pos--= ' ';
|
|
*pos--= (char) ('0'+(char) (part1%10)); part1/=10;
|
|
*pos--= (char) ('0'+(char) (part1%10)); part1/=10;
|
|
*pos--= '-';
|
|
*pos--= (char) ('0'+(char) (part1%10)); part1/=10;
|
|
*pos--= (char) ('0'+(char) (part1%10)); part3= (int) (part1/10);
|
|
*pos--= '-';
|
|
*pos--= (char) ('0'+(char) (part3%10)); part3/=10;
|
|
*pos--= (char) ('0'+(char) (part3%10)); part3/=10;
|
|
*pos--= (char) ('0'+(char) (part3%10)); part3/=10;
|
|
*pos=(char) ('0'+(char) part3);
|
|
val_buffer->set_charset(&my_charset_numeric);
|
|
return val_buffer;
|
|
}
|
|
|
|
bool Field_datetime::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
|
|
ulonglong fuzzydate) const
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
longlong tmp= sint8korr(pos);
|
|
uint32 part1,part2;
|
|
part1=(uint32) (tmp/1000000LL);
|
|
part2=(uint32) (tmp - (ulonglong) part1*1000000LL);
|
|
|
|
ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
|
|
ltime->neg= 0;
|
|
ltime->second_part= 0;
|
|
ltime->second= (int) (part2%100);
|
|
ltime->minute= (int) (part2/100%100);
|
|
ltime->hour= (int) (part2/10000);
|
|
ltime->day= (int) (part1%100);
|
|
ltime->month= (int) (part1/100%100);
|
|
ltime->year= (int) (part1/10000);
|
|
return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
|
|
}
|
|
|
|
|
|
int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
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_datetime::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];
|
|
}
|
|
|
|
|
|
void Field_datetime::sql_type(String &res) const
|
|
{
|
|
if (decimals() == 0)
|
|
{
|
|
res.set_ascii(STRING_WITH_LEN("datetime"));
|
|
return;
|
|
}
|
|
CHARSET_INFO *cs= res.charset();
|
|
res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
|
|
"datetime(%u)", decimals()));
|
|
}
|
|
|
|
|
|
int Field_datetime::set_time()
|
|
{
|
|
THD *thd= table->in_use;
|
|
MYSQL_TIME now_time;
|
|
thd->variables.time_zone->gmt_sec_to_TIME(&now_time, thd->query_start());
|
|
now_time.second_part= thd->query_start_sec_part();
|
|
set_notnull();
|
|
store_TIME(&now_time);
|
|
thd->time_zone_used= 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime)
|
|
{
|
|
ulonglong packed= sec_part_shift(pack_time(ltime), dec);
|
|
store_bigendian(packed, ptr, Field_datetime_hires::pack_length());
|
|
}
|
|
|
|
int Field_temporal_with_date::store_decimal(const my_decimal *d)
|
|
{
|
|
ulonglong nr;
|
|
ulong sec_part;
|
|
int error;
|
|
MYSQL_TIME ltime;
|
|
longlong tmp;
|
|
THD *thd= get_thd();
|
|
ErrConvDecimal str(d);
|
|
|
|
if (my_decimal2seconds(d, &nr, &sec_part))
|
|
{
|
|
tmp= -1;
|
|
error= 2;
|
|
}
|
|
else
|
|
tmp= number_to_datetime(nr, sec_part, <ime, sql_mode_for_dates(thd),
|
|
&error);
|
|
|
|
return store_TIME_with_warning(<ime, &str, error, tmp != -1);
|
|
}
|
|
|
|
bool Field_datetime_with_dec::send_binary(Protocol *protocol)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
get_date(<ime, 0);
|
|
return protocol->store(<ime, dec);
|
|
}
|
|
|
|
|
|
double Field_datetime_with_dec::val_real(void)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
get_date(<ime, 0);
|
|
return TIME_to_double(<ime);
|
|
}
|
|
|
|
longlong Field_datetime_with_dec::val_int(void)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
get_date(<ime, 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, 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,
|
|
ulonglong fuzzydate) const
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
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(MYSQL_TIME *ltime)
|
|
{
|
|
my_time_trunc(ltime, decimals());
|
|
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,
|
|
ulonglong fuzzydate) const
|
|
{
|
|
ASSERT_COLUMN_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);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** 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;
|
|
|
|
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= get_thd();
|
|
if ((pstr < end) && 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;
|
|
}
|
|
|
|
|
|
/* Copy a string and fill with space */
|
|
|
|
int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_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_length / field_charset->mbmaxlen,
|
|
false, ©_length);
|
|
|
|
/* Append spaces if the string was shorter than the field. */
|
|
if (copy_length < field_length)
|
|
field_charset->cset->fill(field_charset,(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->cset->longlong10_to_str)(field_charset,
|
|
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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
|
|
uint local_char_length= MY_MIN(sizeof(buff),
|
|
field_length / field_charset->mbmaxlen);
|
|
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);
|
|
}
|
|
|
|
uint Field::is_equal(Create_field *new_field)
|
|
{
|
|
return new_field->type_handler() == type_handler();
|
|
}
|
|
|
|
|
|
uint Field_str::is_equal(Create_field *new_field)
|
|
{
|
|
return new_field->type_handler() == type_handler() &&
|
|
new_field->charset == field_charset &&
|
|
new_field->length == max_display_length();
|
|
}
|
|
|
|
|
|
int Field_longstr::store_decimal(const my_decimal *d)
|
|
{
|
|
char buff[DECIMAL_MAX_STR_LENGTH+1];
|
|
String str(buff, sizeof(buff), &my_charset_numeric);
|
|
my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str);
|
|
return store(str.ptr(), str.length(), str.charset());
|
|
}
|
|
|
|
uint32 Field_longstr::max_data_length() const
|
|
{
|
|
return field_length + (field_length > 255 ? 2 : 1);
|
|
}
|
|
|
|
|
|
bool
|
|
Field_longstr::cmp_to_string_with_same_collation(const Item_bool_func *cond,
|
|
const Item *item) const
|
|
{
|
|
return item->cmp_type() == STRING_RESULT &&
|
|
charset() == cond->compare_collation();
|
|
}
|
|
|
|
|
|
bool
|
|
Field_longstr::cmp_to_string_with_stricter_collation(const Item_bool_func *cond,
|
|
const Item *item) const
|
|
{
|
|
return item->cmp_type() == STRING_RESULT &&
|
|
(charset() == cond->compare_collation() ||
|
|
cond->compare_collation()->state & MY_CS_BINSORT);
|
|
}
|
|
|
|
|
|
bool Field_longstr::can_optimize_keypart_ref(const Item_bool_func *cond,
|
|
const Item *item) const
|
|
{
|
|
DBUG_ASSERT(cmp_type() == STRING_RESULT);
|
|
return cmp_to_string_with_stricter_collation(cond, item);
|
|
}
|
|
|
|
|
|
bool 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);
|
|
}
|
|
|
|
|
|
bool 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);
|
|
}
|
|
|
|
|
|
bool 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_string::charset() == &my_charset_bin)
|
|
{ }
|
|
|
|
|
|
double Field_string::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
THD *thd= get_thd();
|
|
return Converter_strntod_with_warn(get_thd(),
|
|
Warn_filter_string(thd, this),
|
|
Field_string::charset(),
|
|
(const char *) ptr,
|
|
field_length).result();
|
|
}
|
|
|
|
|
|
longlong Field_string::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
THD *thd= get_thd();
|
|
return Converter_strntoll_with_warn(thd, Warn_filter_string(thd, this),
|
|
Field_string::charset(),
|
|
(const char *) ptr,
|
|
field_length).result();
|
|
}
|
|
|
|
|
|
String *Field_string::val_str(String *val_buffer __attribute__((unused)),
|
|
String *val_ptr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
/* See the comment for Field_long::store(long long) */
|
|
DBUG_ASSERT(!table || table->in_use == current_thd);
|
|
size_t length;
|
|
if (get_thd()->variables.sql_mode &
|
|
MODE_PAD_CHAR_TO_FULL_LENGTH)
|
|
length= my_charpos(field_charset, ptr, ptr + field_length,
|
|
field_length / field_charset->mbmaxlen);
|
|
else
|
|
length= field_charset->cset->lengthsp(field_charset, (const char*) ptr,
|
|
field_length);
|
|
val_ptr->set((const char*) ptr, length, field_charset);
|
|
return val_ptr;
|
|
}
|
|
|
|
|
|
my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
THD *thd= get_thd();
|
|
Converter_str2my_decimal_with_warn(thd,
|
|
Warn_filter_string(thd, this),
|
|
E_DEC_FATAL_ERROR,
|
|
Field_string::charset(),
|
|
(const char *) ptr,
|
|
field_length, decimal_value);
|
|
return decimal_value;
|
|
}
|
|
|
|
|
|
struct Check_field_param {
|
|
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,
|
|
Relay_log_info *rli_arg,
|
|
uint16 mflags __attribute__((unused)),
|
|
int *order_var)
|
|
{
|
|
#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)
|
|
{
|
|
size_t a_len, b_len;
|
|
|
|
if (field_charset->mbmaxlen != 1)
|
|
{
|
|
size_t char_len= field_length/field_charset->mbmaxlen;
|
|
a_len= my_charpos(field_charset, a_ptr, a_ptr + field_length, char_len);
|
|
b_len= my_charpos(field_charset, b_ptr, b_ptr + field_length, char_len);
|
|
}
|
|
else
|
|
a_len= b_len= field_length;
|
|
/*
|
|
We have to remove end space to be able to compare multi-byte-characters
|
|
like in latin_de 'ae' and 0xe4
|
|
*/
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
a_ptr, a_len,
|
|
b_ptr, b_len);
|
|
}
|
|
|
|
|
|
void Field_string::sort_string(uchar *to,uint length)
|
|
{
|
|
IF_DBUG(size_t tmp= ,)
|
|
field_charset->coll->strnxfrm(field_charset,
|
|
to, length,
|
|
char_length() *
|
|
field_charset->strxfrm_multiply,
|
|
ptr, field_length,
|
|
MY_STRXFRM_PAD_WITH_SPACE |
|
|
MY_STRXFRM_PAD_TO_MAXLEN);
|
|
DBUG_ASSERT(tmp == length);
|
|
}
|
|
|
|
|
|
void Field_string::sql_type(String &res) const
|
|
{
|
|
THD *thd= table->in_use;
|
|
CHARSET_INFO *cs=res.charset();
|
|
size_t length;
|
|
|
|
length= cs->cset->snprintf(cs,(char*) res.ptr(),
|
|
res.alloced_length(), "%s(%d)",
|
|
(type() == MYSQL_TYPE_VAR_STRING ?
|
|
(has_charset() ? "varchar" : "varbinary") :
|
|
(has_charset() ? "char" : "binary")),
|
|
(int) field_length / charset()->mbmaxlen);
|
|
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"));
|
|
}
|
|
|
|
|
|
uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length)
|
|
{
|
|
size_t length= MY_MIN(field_length,max_length);
|
|
size_t local_char_length= max_length/field_charset->mbmaxlen;
|
|
DBUG_PRINT("debug", ("Packing field '%s' - length: %zu ", field_name.str,
|
|
length));
|
|
|
|
if (length > local_char_length)
|
|
local_char_length= my_charpos(field_charset, from, from+length,
|
|
local_char_length);
|
|
set_if_smaller(length, local_char_length);
|
|
|
|
/*
|
|
TODO: change charset interface to add a new function that does
|
|
the following or add a flag to lengthsp to do it itself
|
|
(this is for not packing padding adding bytes in BINARY
|
|
fields).
|
|
*/
|
|
if (field_charset->mbmaxlen == 1)
|
|
{
|
|
while (length && from[length-1] == field_charset->pad_char)
|
|
length --;
|
|
}
|
|
else
|
|
length= field_charset->cset->lengthsp(field_charset, (const char*) from, length);
|
|
|
|
// Length always stored little-endian
|
|
*to++= (uchar) length;
|
|
if (field_length > 255)
|
|
*to++= (uchar) (length >> 8);
|
|
|
|
// Store the actual bytes of the string
|
|
memcpy(to, from, length);
|
|
return to+length;
|
|
}
|
|
|
|
|
|
/**
|
|
Unpack a string field from row data.
|
|
|
|
This method is used to unpack a string field from a master whose size
|
|
of the field is less than that of the slave. Note that there can be a
|
|
variety of field types represented with this class. Certain types like
|
|
ENUM or SET are processed differently. Hence, the upper byte of the
|
|
@c param_data argument contains the result of field->real_type() from
|
|
the master.
|
|
|
|
@note For information about how the length is packed, see @c
|
|
Field_string::save_field_metadata
|
|
|
|
@param to Destination of the data
|
|
@param from Source of the data
|
|
@param param_data Real type (upper) and length (lower) values
|
|
|
|
@return New pointer into memory based on from + length of the data
|
|
*/
|
|
const uchar *
|
|
Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end,
|
|
uint param_data)
|
|
{
|
|
uint from_length, length;
|
|
|
|
/*
|
|
Compute the declared length of the field on the master. This is
|
|
used to decide if one or two bytes should be read as length.
|
|
*/
|
|
if (param_data)
|
|
from_length= (((param_data >> 4) & 0x300) ^ 0x300) + (param_data & 0x00ff);
|
|
else
|
|
from_length= field_length;
|
|
|
|
DBUG_PRINT("debug",
|
|
("param_data: 0x%x, field_length: %u, from_length: %u",
|
|
param_data, field_length, from_length));
|
|
/*
|
|
Compute the actual length of the data by reading one or two bits
|
|
(depending on the declared field length on the master).
|
|
*/
|
|
if (from_length > 255)
|
|
{
|
|
if (from + 2 > from_end)
|
|
return 0;
|
|
length= uint2korr(from);
|
|
from+= 2;
|
|
}
|
|
else
|
|
{
|
|
if (from + 1 > from_end)
|
|
return 0;
|
|
length= (uint) *from++;
|
|
}
|
|
if (from + length > from_end || length > field_length)
|
|
return 0;
|
|
|
|
memcpy(to, from, length);
|
|
// Pad the string with the pad character of the fields charset
|
|
field_charset->cset->fill(field_charset, (char*) to + length, field_length - length, field_charset->pad_char);
|
|
return from+length;
|
|
}
|
|
|
|
|
|
/**
|
|
Save the field metadata for string fields.
|
|
|
|
Saves the real type in the first byte and the field length in the
|
|
second byte of the field metadata array at index of *metadata_ptr and
|
|
*(metadata_ptr + 1).
|
|
|
|
@note In order to be able to handle lengths exceeding 255 and be
|
|
backwards-compatible with pre-5.1.26 servers, an extra two bits of
|
|
the length has been added to the metadata in such a way that if
|
|
they are set, a new unrecognized type is generated. This will
|
|
cause pre-5.1-26 servers to stop due to a field type mismatch,
|
|
while new servers will be able to extract the extra bits. If the
|
|
length is <256, there will be no difference and both a new and an
|
|
old server will be able to handle it.
|
|
|
|
@note The extra two bits are added to bits 13 and 14 of the
|
|
parameter data (with 1 being the least siginficant bit and 16 the
|
|
most significant bit of the word) by xoring the extra length bits
|
|
with the real type. Since all allowable types have 0xF as most
|
|
significant bits of the metadata word, lengths <256 will not affect
|
|
the real type at all, while all other values will result in a
|
|
non-existant 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
|
|
*/
|
|
int Field_string::save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
DBUG_ASSERT(field_length < 1024);
|
|
DBUG_ASSERT((real_type() & 0xF0) == 0xF0);
|
|
DBUG_PRINT("debug", ("field_length: %u, real_type: %u",
|
|
field_length, real_type()));
|
|
*metadata_ptr= (real_type() ^ ((field_length & 0x300) >> 4));
|
|
*(metadata_ptr + 1)= field_length & 0xFF;
|
|
return 2;
|
|
}
|
|
|
|
|
|
uint Field_string::packed_col_length(const uchar *data_ptr, uint length)
|
|
{
|
|
if (length > 255)
|
|
return uint2korr(data_ptr)+2;
|
|
return (uint) *data_ptr + 1;
|
|
}
|
|
|
|
|
|
uint Field_string::max_packed_col_length(uint max_length)
|
|
{
|
|
return (max_length > 255 ? 2 : 1)+max_length;
|
|
}
|
|
|
|
|
|
uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg)
|
|
{
|
|
size_t bytes = my_charpos(field_charset, (char*) ptr,
|
|
(char*) ptr + field_length,
|
|
length / field_charset->mbmaxlen);
|
|
memcpy(buff, ptr, bytes);
|
|
if (bytes < length)
|
|
field_charset->cset->fill(field_charset, (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(new_table);
|
|
/*
|
|
Normally orig_table is different from table only if field was
|
|
created via ::make_new_field. Here we alter the type of field,
|
|
so ::make_new_field is not applicable. But we still need to
|
|
preserve the original field metadata for the client-server
|
|
protocol.
|
|
*/
|
|
field->orig_table= orig_table;
|
|
}
|
|
return field;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
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;
|
|
|
|
/**
|
|
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
|
|
*/
|
|
int Field_varstring::save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
DBUG_ASSERT(field_length <= 65535);
|
|
int2store((char*)metadata_ptr, field_length);
|
|
return 2;
|
|
}
|
|
|
|
int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_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_length / field_charset->mbmaxlen,
|
|
true, ©_length);
|
|
|
|
store_length(copy_length);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
double Field_varstring::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
THD *thd= get_thd();
|
|
return Converter_strntod_with_warn(thd, Warn_filter(thd),
|
|
Field_varstring::charset(),
|
|
(const char *) get_data(),
|
|
get_length()).result();
|
|
}
|
|
|
|
|
|
longlong Field_varstring::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
THD *thd= get_thd();
|
|
Converter_str2my_decimal_with_warn(thd, Warn_filter(thd),
|
|
E_DEC_FATAL_ERROR,
|
|
Field_varstring::charset(),
|
|
(const char *) get_data(),
|
|
get_length(), decimal_value);
|
|
return decimal_value;
|
|
|
|
}
|
|
|
|
|
|
int Field_varstring::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
|
|
uint max_len)
|
|
{
|
|
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, max_len);
|
|
set_if_smaller(b_length, max_len);
|
|
diff= field_charset->coll->strnncollsp(field_charset,
|
|
a_ptr+
|
|
length_bytes,
|
|
a_length,
|
|
b_ptr+
|
|
length_bytes,
|
|
b_length);
|
|
return diff;
|
|
}
|
|
|
|
|
|
/**
|
|
@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)
|
|
{
|
|
size_t length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
size_t local_char_length= max_key_length / field_charset->mbmaxlen;
|
|
|
|
local_char_length= my_charpos(field_charset, ptr + length_bytes,
|
|
ptr + length_bytes + length, local_char_length);
|
|
set_if_smaller(length, local_char_length);
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
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)
|
|
{
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
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;
|
|
}
|
|
|
|
#ifndef DBUG_OFF
|
|
size_t rc=
|
|
#endif
|
|
field_charset->coll->strnxfrm(field_charset, to, length,
|
|
char_length() * field_charset->strxfrm_multiply,
|
|
(const uchar*) buf.ptr(), buf.length(),
|
|
MY_STRXFRM_PAD_WITH_SPACE |
|
|
MY_STRXFRM_PAD_TO_MAXLEN);
|
|
DBUG_ASSERT(rc == length);
|
|
}
|
|
|
|
|
|
enum ha_base_keytype Field_varstring::key_type() const
|
|
{
|
|
enum ha_base_keytype res;
|
|
|
|
if (binary())
|
|
res= length_bytes == 1 ? HA_KEYTYPE_VARBINARY1 : HA_KEYTYPE_VARBINARY2;
|
|
else
|
|
res= length_bytes == 1 ? HA_KEYTYPE_VARTEXT1 : HA_KEYTYPE_VARTEXT2;
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
Compressed columns need one extra byte to store the compression method.
|
|
This byte is invisible to the end user, but not for the storage engine.
|
|
*/
|
|
|
|
void Field_varstring::sql_type(String &res) const
|
|
{
|
|
THD *thd= table->in_use;
|
|
CHARSET_INFO *cs=res.charset();
|
|
size_t length;
|
|
|
|
length= cs->cset->snprintf(cs,(char*) res.ptr(),
|
|
res.alloced_length(), "%s(%d)",
|
|
(has_charset() ? "varchar" : "varbinary"),
|
|
(int) field_length / charset()->mbmaxlen -
|
|
MY_TEST(compression_method()));
|
|
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"));
|
|
}
|
|
|
|
|
|
uint32 Field_varstring::data_length()
|
|
{
|
|
return length_bytes == 1 ? (uint32) *ptr : uint2korr(ptr);
|
|
}
|
|
|
|
/*
|
|
Functions to create a packed row.
|
|
Here the number of length bytes are depending on the given max_length
|
|
*/
|
|
|
|
uchar *Field_varstring::pack(uchar *to, const uchar *from, uint max_length)
|
|
{
|
|
uint length= length_bytes == 1 ? (uint) *from : uint2korr(from);
|
|
set_if_smaller(max_length, field_length);
|
|
if (length > max_length)
|
|
length=max_length;
|
|
|
|
/* Length always stored little-endian */
|
|
*to++= length & 0xFF;
|
|
if (max_length > 255)
|
|
*to++= (length >> 8) & 0xFF;
|
|
|
|
/* Store bytes of string */
|
|
if (length > 0)
|
|
memcpy(to, from+length_bytes, length);
|
|
return to+length;
|
|
}
|
|
|
|
|
|
/**
|
|
Unpack a varstring field from row data.
|
|
|
|
This method is used to unpack a varstring field from a master
|
|
whose size of the field is less than that of the slave.
|
|
|
|
@note
|
|
The string length is always packed little-endian.
|
|
|
|
@param to Destination of the data
|
|
@param from Source of the data
|
|
@param param_data Length bytes from the master's field data
|
|
|
|
@return New pointer into memory based on from + length of the data
|
|
*/
|
|
const uchar *
|
|
Field_varstring::unpack(uchar *to, const uchar *from, const uchar *from_end,
|
|
uint param_data)
|
|
{
|
|
uint length;
|
|
uint l_bytes= (param_data && (param_data < field_length)) ?
|
|
(param_data <= 255) ? 1 : 2 : length_bytes;
|
|
|
|
if (from + l_bytes > from_end)
|
|
return 0; // Error in data
|
|
|
|
if (l_bytes == 1)
|
|
{
|
|
to[0]= *from++;
|
|
length= to[0];
|
|
if (length_bytes == 2)
|
|
to[1]= 0;
|
|
}
|
|
else /* l_bytes == 2 */
|
|
{
|
|
length= uint2korr(from);
|
|
to[0]= *from++;
|
|
to[1]= *from++;
|
|
}
|
|
if (length)
|
|
{
|
|
if (from + length > from_end || length > field_length)
|
|
return 0; // Error in data
|
|
memcpy(to+ length_bytes, from, length);
|
|
}
|
|
return from+length;
|
|
}
|
|
|
|
|
|
uint Field_varstring::packed_col_length(const uchar *data_ptr, uint length)
|
|
{
|
|
if (length > 255)
|
|
return uint2korr(data_ptr)+2;
|
|
return (uint) *data_ptr + 1;
|
|
}
|
|
|
|
|
|
uint Field_varstring::max_packed_col_length(uint max_length)
|
|
{
|
|
return (max_length > 255 ? 2 : 1)+max_length;
|
|
}
|
|
|
|
uint Field_varstring::get_key_image(uchar *buff, uint length,
|
|
imagetype type_arg)
|
|
{
|
|
String val;
|
|
uint local_char_length;
|
|
my_bitmap_map *old_map;
|
|
|
|
old_map= dbug_tmp_use_all_columns(table, table->read_set);
|
|
val_str(&val, &val);
|
|
dbug_tmp_restore_column_map(table->read_set, old_map);
|
|
|
|
local_char_length= val.charpos(length / field_charset->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)
|
|
{
|
|
uint32 a_length,b_length;
|
|
|
|
if (length_bytes == 1)
|
|
{
|
|
a_length= (uint) *a_ptr;
|
|
b_length= (uint) *b_ptr;
|
|
}
|
|
else
|
|
{
|
|
a_length= uint2korr(a_ptr);
|
|
b_length= uint2korr(b_ptr);
|
|
}
|
|
set_if_smaller(a_length, max_length);
|
|
set_if_smaller(b_length, max_length);
|
|
if (a_length != b_length)
|
|
return 1;
|
|
return memcmp(a_ptr+length_bytes, b_ptr+length_bytes, a_length);
|
|
}
|
|
|
|
|
|
Field *Field_varstring::make_new_field(MEM_ROOT *root, TABLE *new_table,
|
|
bool keep_type)
|
|
{
|
|
Field_varstring *res= (Field_varstring*) Field::make_new_field(root,
|
|
new_table,
|
|
keep_type);
|
|
if (res)
|
|
res->length_bytes= length_bytes;
|
|
return res;
|
|
}
|
|
|
|
|
|
Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table,
|
|
uchar *new_ptr, uint32 length,
|
|
uchar *new_null_ptr, uint new_null_bit)
|
|
{
|
|
Field_varstring *res;
|
|
if ((res= (Field_varstring*) Field::new_key_field(root, new_table,
|
|
new_ptr, length,
|
|
new_null_ptr, new_null_bit)))
|
|
{
|
|
/* Keys length prefixes are always packed with 2 bytes */
|
|
res->length_bytes= 2;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
uint Field_varstring::is_equal(Create_field *new_field)
|
|
{
|
|
if (new_field->type_handler() == type_handler() &&
|
|
new_field->charset == field_charset &&
|
|
!new_field->compression_method() == !compression_method())
|
|
{
|
|
if (new_field->length == field_length)
|
|
return IS_EQUAL_YES;
|
|
if (new_field->length > field_length &&
|
|
((new_field->length <= 255 && field_length <= 255) ||
|
|
(new_field->length > 255 && field_length > 255)))
|
|
return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length
|
|
}
|
|
return IS_EQUAL_NO;
|
|
}
|
|
|
|
|
|
void Field_varstring::hash(ulong *nr, ulong *nr2)
|
|
{
|
|
if (is_null())
|
|
{
|
|
*nr^= (*nr << 1) | 1;
|
|
}
|
|
else
|
|
{
|
|
uint len= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
CHARSET_INFO *cs= charset();
|
|
cs->coll->hash_sort(cs, ptr + length_bytes, len, nr, nr2);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Compress field
|
|
|
|
@param[out] to destination buffer for compressed data
|
|
@param[in] to_length size of to
|
|
@param[in] from data to compress
|
|
@param[in] length from length
|
|
@param[in] max_length truncate `from' to this length
|
|
@param[out] out_length compessed data length
|
|
@param[in] cs from character set
|
|
@param[in] nchars copy no more than "nchars" characters
|
|
|
|
In worst case (no compression performed) storage requirement is increased by
|
|
1 byte to store header. If it exceeds field length, normal data truncation is
|
|
performed.
|
|
|
|
Generic compressed header format (1 byte):
|
|
|
|
Bits 1-4: method specific bits
|
|
Bits 5-8: compression method
|
|
|
|
If compression method is 0 then header is immediately followed by
|
|
uncompressed data.
|
|
|
|
If compression method is zlib:
|
|
|
|
Bits 1-3: number of bytes occupied by original data length
|
|
Bits 4: true if zlib wrapper not present
|
|
Bits 5-8: store 8 (zlib)
|
|
|
|
Header is immediately followed by original data length,
|
|
followed by compressed data.
|
|
*/
|
|
|
|
int Field_longstr::compress(char *to, uint to_length,
|
|
const char *from, uint length,
|
|
uint max_length,
|
|
uint *out_length,
|
|
CHARSET_INFO *cs, size_t nchars)
|
|
{
|
|
THD *thd= get_thd();
|
|
char *buf;
|
|
uint buf_length;
|
|
int rc= 0;
|
|
|
|
if (String::needs_conversion_on_storage(length, cs, field_charset) ||
|
|
max_length < length)
|
|
{
|
|
set_if_smaller(max_length, static_cast<ulonglong>(field_charset->mbmaxlen) * length + 1);
|
|
if (!(buf= (char*) my_malloc(max_length, MYF(MY_WME))))
|
|
{
|
|
*out_length= 0;
|
|
return -1;
|
|
}
|
|
|
|
rc= well_formed_copy_with_check(buf, max_length, cs, from, length,
|
|
nchars, true, &buf_length);
|
|
}
|
|
else
|
|
{
|
|
buf= const_cast<char*>(from);
|
|
buf_length= length;
|
|
}
|
|
|
|
if (buf_length == 0)
|
|
*out_length= 0;
|
|
else if (buf_length >= thd->variables.column_compression_threshold &&
|
|
(*out_length= compression_method()->compress(thd, to, buf, buf_length)))
|
|
status_var_increment(thd->status_var.column_compressions);
|
|
else
|
|
{
|
|
/* Store uncompressed */
|
|
to[0]= 0;
|
|
if (buf_length < to_length)
|
|
memcpy(to + 1, buf, buf_length);
|
|
else
|
|
{
|
|
/* Storing string at blob capacity, e.g. 255 bytes string to TINYBLOB. */
|
|
rc= well_formed_copy_with_check(to + 1, to_length - 1, cs, from, length,
|
|
nchars, true, &buf_length);
|
|
}
|
|
*out_length= buf_length + 1;
|
|
}
|
|
|
|
if (buf != from)
|
|
my_free(buf);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
Memory is allocated only when original data was actually compressed.
|
|
Otherwise val_ptr points at data located immediately after header.
|
|
|
|
Data can be stored uncompressed if data was shorter than threshold
|
|
or compressed data was longer than original data.
|
|
*/
|
|
|
|
String *Field_longstr::uncompress(String *val_buffer, String *val_ptr,
|
|
const uchar *from, uint from_length)
|
|
{
|
|
if (from_length)
|
|
{
|
|
uchar method= (*from & 0xF0) >> 4;
|
|
|
|
/* Uncompressed data */
|
|
if (!method)
|
|
{
|
|
val_ptr->set((const char*) from + 1, from_length - 1, field_charset);
|
|
return val_ptr;
|
|
}
|
|
|
|
if (compression_methods[method].uncompress)
|
|
{
|
|
if (!compression_methods[method].uncompress(val_buffer, from, from_length,
|
|
field_length))
|
|
{
|
|
val_buffer->set_charset(field_charset);
|
|
status_var_increment(get_thd()->status_var.column_decompressions);
|
|
return val_buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
It would be better to return 0 in case of errors, but to take the
|
|
safer route, let's return a zero string and let the general
|
|
handler catch the error.
|
|
*/
|
|
val_ptr->set("", 0, field_charset);
|
|
return val_ptr;
|
|
}
|
|
|
|
|
|
int Field_varstring_compressed::store(const char *from, size_t length,
|
|
CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_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;
|
|
}
|
|
|
|
|
|
String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
return uncompress(val_buffer, val_ptr, get_data(), get_length());
|
|
}
|
|
|
|
|
|
double Field_varstring_compressed::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
THD *thd= get_thd();
|
|
String buf;
|
|
val_str(&buf, &buf);
|
|
return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset,
|
|
buf.ptr(), buf.length()).result();
|
|
}
|
|
|
|
|
|
longlong Field_varstring_compressed::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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_max(const uchar *a_ptr, const uchar *b_ptr,
|
|
uint max_len)
|
|
{
|
|
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);
|
|
|
|
if (a.length() > max_len)
|
|
a.length(max_len);
|
|
if (b.length() > max_len)
|
|
b.length(max_len);
|
|
|
|
return sortcmp(&a, &b, field_charset);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** blob type
|
|
** A blob is saved as a length and a pointer. The length is stored in the
|
|
** packlength slot and may be from 1-4.
|
|
****************************************************************************/
|
|
|
|
Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
|
|
enum utype unireg_check_arg,
|
|
const LEX_CSTRING *field_name_arg,
|
|
TABLE_SHARE *share, uint blob_pack_length,
|
|
const DTCollation &collation)
|
|
:Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
|
|
null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
|
|
collation),
|
|
packlength(blob_pack_length)
|
|
{
|
|
DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently
|
|
flags|= BLOB_FLAG;
|
|
share->blob_fields++;
|
|
/* TODO: why do not fill table->s->blob_field array here? */
|
|
}
|
|
|
|
|
|
void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number)
|
|
{
|
|
store_lowendian(i_number, i_ptr, i_packlength);
|
|
}
|
|
|
|
|
|
uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) const
|
|
{
|
|
return (uint32)read_lowendian(pos, packlength_arg);
|
|
}
|
|
|
|
|
|
/**
|
|
Copy a value from another BLOB field of the same character set.
|
|
This method is used by Copy_field, e.g. during ALTER TABLE.
|
|
*/
|
|
int Field_blob::copy_value(Field_blob *from)
|
|
{
|
|
DBUG_ASSERT(field_charset == from->charset());
|
|
DBUG_ASSERT(!compression_method() == !from->compression_method());
|
|
int rc= 0;
|
|
uint32 length= from->get_length();
|
|
uchar *data= from->get_ptr();
|
|
if (packlength < from->packlength)
|
|
{
|
|
set_if_smaller(length, Field_blob::max_data_length());
|
|
length= (uint32) Well_formed_prefix(field_charset,
|
|
(const char *) data, length).length();
|
|
rc= report_if_important_data((const char *) data + length,
|
|
(const char *) data + from->get_length(),
|
|
true);
|
|
}
|
|
store_length(length);
|
|
bmove(ptr + packlength, (uchar*) &data, sizeof(char*));
|
|
return rc;
|
|
}
|
|
|
|
|
|
int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_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;
|
|
}
|
|
|
|
if (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= (size_t)MY_MIN(UINT_MAX,table->in_use->variables.group_concat_max_len);
|
|
if (new_length > copy_length)
|
|
{
|
|
new_length= Well_formed_prefix(cs,
|
|
from, copy_length, new_length).length();
|
|
table->blob_storage->set_truncated_value(true);
|
|
}
|
|
if (!(tmp= table->blob_storage->store(from, new_length)))
|
|
goto oom_error;
|
|
|
|
Field_blob::store_length(new_length);
|
|
bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
If the 'from' address is in the range of the temporary 'value'-
|
|
object we need to copy the content to a different location or it will be
|
|
invalidated when the 'value'-object is reallocated to make room for
|
|
the new character set.
|
|
*/
|
|
if (from >= value.ptr() && from <= value.ptr()+value.length())
|
|
{
|
|
/*
|
|
If content of the 'from'-address is cached in the 'value'-object
|
|
it is possible that the content needs a character conversion.
|
|
*/
|
|
if (!String::needs_conversion_on_storage(length, cs, field_charset))
|
|
{
|
|
Field_blob::store_length(length);
|
|
bmove(ptr + packlength, &from, sizeof(char*));
|
|
return 0;
|
|
}
|
|
if (tmpstr.copy(from, length, cs))
|
|
goto oom_error;
|
|
from= tmpstr.ptr();
|
|
}
|
|
|
|
new_length= MY_MIN(max_data_length(), field_charset->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);
|
|
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;
|
|
}
|
|
|
|
|
|
double Field_blob::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
char *blob;
|
|
memcpy(&blob, ptr+packlength, sizeof(char*));
|
|
if (!blob)
|
|
return 0.0;
|
|
THD *thd= get_thd();
|
|
return Converter_strntod_with_warn(thd, Warn_filter(thd),
|
|
Field_blob::charset(),
|
|
blob, get_length(ptr)).result();
|
|
}
|
|
|
|
|
|
longlong Field_blob::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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,
|
|
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)
|
|
{
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
a, a_length, b, b_length);
|
|
}
|
|
|
|
|
|
int Field_blob::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
|
|
uint max_length)
|
|
{
|
|
uchar *blob1,*blob2;
|
|
memcpy(&blob1, a_ptr+packlength, sizeof(char*));
|
|
memcpy(&blob2, b_ptr+packlength, sizeof(char*));
|
|
uint a_len= get_length(a_ptr), b_len= get_length(b_ptr);
|
|
set_if_smaller(a_len, max_length);
|
|
set_if_smaller(b_len, max_length);
|
|
return Field_blob::cmp(blob1,a_len,blob2,b_len);
|
|
}
|
|
|
|
|
|
int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
|
|
uint32 max_length)
|
|
{
|
|
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;
|
|
diff=memcmp(a,b,MY_MIN(a_length,b_length));
|
|
return diff ? diff : (int) (a_length - b_length);
|
|
}
|
|
|
|
|
|
/* The following is used only when comparing a key */
|
|
|
|
uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg)
|
|
{
|
|
size_t blob_length= get_length(ptr);
|
|
uchar *blob;
|
|
|
|
#ifdef HAVE_SPATIAL
|
|
if (type_arg == itMBR)
|
|
{
|
|
const char *dummy;
|
|
MBR mbr;
|
|
Geometry_buffer buffer;
|
|
Geometry *gobj;
|
|
const uint image_length= SIZEOF_STORED_DOUBLE*4;
|
|
|
|
if (blob_length < SRID_SIZE)
|
|
{
|
|
bzero(buff, image_length);
|
|
return image_length;
|
|
}
|
|
blob= get_ptr();
|
|
gobj= Geometry::construct(&buffer, (char*) blob, (uint32)blob_length);
|
|
if (!gobj || gobj->get_mbr(&mbr, &dummy))
|
|
bzero(buff, image_length);
|
|
else
|
|
{
|
|
float8store(buff, mbr.xmin);
|
|
float8store(buff+8, mbr.xmax);
|
|
float8store(buff+16, mbr.ymin);
|
|
float8store(buff+24, mbr.ymax);
|
|
}
|
|
return image_length;
|
|
}
|
|
#endif /*HAVE_SPATIAL*/
|
|
|
|
blob= get_ptr();
|
|
size_t local_char_length= length / field_charset->mbmaxlen;
|
|
local_char_length= my_charpos(field_charset, 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);
|
|
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)
|
|
{
|
|
uchar *blob1;
|
|
size_t blob_length=get_length(ptr);
|
|
memcpy(&blob1, ptr+packlength, sizeof(char*));
|
|
CHARSET_INFO *cs= charset();
|
|
size_t local_char_length= max_key_length / cs->mbmaxlen;
|
|
local_char_length= my_charpos(cs, 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)
|
|
{
|
|
return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a),
|
|
b+HA_KEY_BLOB_LENGTH, uint2korr(b));
|
|
}
|
|
|
|
|
|
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
|
|
*/
|
|
int Field_blob::save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
DBUG_ENTER("Field_blob::save_field_metadata");
|
|
*metadata_ptr= pack_length_no_ptr();
|
|
DBUG_PRINT("debug", ("metadata: %u (pack_length_no_ptr)", *metadata_ptr));
|
|
DBUG_RETURN(1);
|
|
}
|
|
|
|
|
|
uint32 Field_blob::sort_length() const
|
|
{
|
|
return (uint32) (get_thd()->variables.max_sort_length +
|
|
(field_charset == &my_charset_bin ? 0 : packlength));
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
#ifndef DBUG_OFF
|
|
size_t rc=
|
|
#endif
|
|
field_charset->coll->strnxfrm(field_charset, to, length, length,
|
|
(const uchar*) buf.ptr(), buf.length(),
|
|
MY_STRXFRM_PAD_WITH_SPACE |
|
|
MY_STRXFRM_PAD_TO_MAXLEN);
|
|
DBUG_ASSERT(rc == length);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Return the data type handler, according to packlength.
|
|
Implemented in field.cc rather than in field.h
|
|
to avoid exporting type_handler_xxx with MYSQL_PLUGIN_IMPORT.
|
|
*/
|
|
const Type_handler *Field_blob::type_handler() const
|
|
{
|
|
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"));
|
|
else
|
|
{
|
|
res.append(STRING_WITH_LEN("text"));
|
|
}
|
|
}
|
|
|
|
uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
|
|
{
|
|
uchar *save= ptr;
|
|
ptr= (uchar*) from;
|
|
uint32 length=get_length(); // Length of from string
|
|
|
|
/*
|
|
Store max length, which will occupy packlength bytes. If the max
|
|
length given is smaller than the actual length of the blob, we
|
|
just store the initial bytes of the blob.
|
|
*/
|
|
store_length(to, packlength, MY_MIN(length, max_length));
|
|
|
|
/*
|
|
Store the actual blob data, which will occupy 'length' bytes.
|
|
*/
|
|
if (length > 0)
|
|
{
|
|
from= get_ptr();
|
|
memcpy(to+packlength, from,length);
|
|
}
|
|
ptr=save; // Restore org row pointer
|
|
return to+packlength+length;
|
|
}
|
|
|
|
|
|
/**
|
|
Unpack a blob field from row data.
|
|
|
|
This method is used to unpack a blob field from a master whose size of
|
|
the field is less than that of the slave. Note: This method is included
|
|
to satisfy inheritance rules, but is not needed for blob fields. It
|
|
simply is used as a pass-through to the original unpack() method for
|
|
blob fields.
|
|
|
|
@param to Destination of the data
|
|
@param from Source of the data
|
|
@param param_data @c TRUE if base types should be stored in little-
|
|
endian format, @c FALSE if native format should
|
|
be used.
|
|
|
|
@return New pointer into memory based on from + length of the data
|
|
*/
|
|
|
|
const uchar *Field_blob::unpack(uchar *to, const uchar *from,
|
|
const uchar *from_end, uint param_data)
|
|
{
|
|
DBUG_ENTER("Field_blob::unpack");
|
|
DBUG_PRINT("enter", ("to: %p; from: %p; param_data: %u",
|
|
to, from, param_data));
|
|
uint const master_packlength=
|
|
param_data > 0 ? param_data & 0xFF : packlength;
|
|
if (from + master_packlength > from_end)
|
|
DBUG_RETURN(0); // Error in data
|
|
uint32 const length= get_length(from, master_packlength);
|
|
DBUG_DUMP("packed", from, length + master_packlength);
|
|
if (from + master_packlength + length > from_end)
|
|
DBUG_RETURN(0);
|
|
set_ptr(length, const_cast<uchar*> (from) + master_packlength);
|
|
DBUG_RETURN(from + master_packlength + length);
|
|
}
|
|
|
|
|
|
uint Field_blob::packed_col_length(const uchar *data_ptr, uint length)
|
|
{
|
|
if (length > 255)
|
|
return uint2korr(data_ptr)+2;
|
|
return (uint) *data_ptr + 1;
|
|
}
|
|
|
|
|
|
uint Field_blob::max_packed_col_length(uint max_length)
|
|
{
|
|
return (max_length > 255 ? 2 : 1)+max_length;
|
|
}
|
|
|
|
|
|
/*
|
|
Blob fields are regarded equal if they have same character set,
|
|
same blob store length and if either both are compressed or both are
|
|
uncompressed.
|
|
The logic for compression is that we don't have to uncompress and compress
|
|
again an already compressed field just because compression method changes.
|
|
*/
|
|
|
|
uint Field_blob::is_equal(Create_field *new_field)
|
|
{
|
|
return new_field->type_handler() == type_handler() &&
|
|
new_field->charset == field_charset &&
|
|
new_field->pack_length == pack_length() &&
|
|
!new_field->compression_method() == !compression_method();
|
|
}
|
|
|
|
|
|
int Field_blob_compressed::store(const char *from, size_t length,
|
|
CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
uint compressed_length;
|
|
uint max_length= max_data_length();
|
|
uint to_length= (uint) MY_MIN(max_length,
|
|
field_charset->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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
return uncompress(val_buffer, val_ptr, get_ptr(), get_length());
|
|
}
|
|
|
|
|
|
double Field_blob_compressed::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
THD *thd= get_thd();
|
|
String buf;
|
|
val_str(&buf, &buf);
|
|
return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset,
|
|
buf.ptr(), buf.length()).result();
|
|
}
|
|
|
|
|
|
longlong Field_blob_compressed::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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();
|
|
}
|
|
|
|
|
|
#ifdef HAVE_SPATIAL
|
|
/* Values 1-40 reserved for 1-byte options,
|
|
41-80 for 2-byte options,
|
|
81-120 for 4-byte options,
|
|
121-160 for 8-byte options,
|
|
other - varied length in next 1-3 bytes.
|
|
*/
|
|
enum extra2_gis_field_options {
|
|
FIELDGEOM_END=0,
|
|
FIELDGEOM_STORAGE_MODEL=1,
|
|
FIELDGEOM_PRECISION=2,
|
|
FIELDGEOM_SCALE=3,
|
|
FIELDGEOM_SRID=81,
|
|
};
|
|
|
|
|
|
uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields)
|
|
{
|
|
uint image_size= 0;
|
|
List_iterator<Create_field> it(create_fields);
|
|
Create_field *field;
|
|
while ((field= it++))
|
|
{
|
|
if (field->real_field_type() != MYSQL_TYPE_GEOMETRY)
|
|
continue;
|
|
if (buff)
|
|
{
|
|
uchar *cbuf= buff + image_size;
|
|
|
|
cbuf[0]= FIELDGEOM_STORAGE_MODEL;
|
|
cbuf[1]= (uchar) Field_geom::GEOM_STORAGE_WKB;
|
|
|
|
cbuf[2]= FIELDGEOM_PRECISION;
|
|
cbuf[3]= (uchar) field->length;
|
|
|
|
cbuf[4]= FIELDGEOM_SCALE;
|
|
cbuf[5]= (uchar) field->decimals;
|
|
|
|
cbuf[6]= FIELDGEOM_SRID;
|
|
int4store(cbuf + 7, ((uint32) field->srid));
|
|
|
|
cbuf[11]= FIELDGEOM_END;
|
|
}
|
|
image_size+= 12;
|
|
}
|
|
|
|
return image_size;
|
|
}
|
|
|
|
|
|
uint gis_field_options_read(const uchar *buf, size_t buf_len,
|
|
Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid)
|
|
{
|
|
const uchar *buf_end= buf + buf_len;
|
|
const uchar *cbuf= buf;
|
|
int option_id;
|
|
|
|
*precision= *scale= *srid= 0;
|
|
*st_type= Field_geom::GEOM_STORAGE_WKB;
|
|
|
|
if (!buf) /* can only happen with the old FRM file */
|
|
goto end_of_record;
|
|
|
|
while (cbuf < buf_end)
|
|
{
|
|
switch ((option_id= *(cbuf++)))
|
|
{
|
|
case FIELDGEOM_STORAGE_MODEL:
|
|
*st_type= (Field_geom::storage_type) cbuf[0];
|
|
break;
|
|
case FIELDGEOM_PRECISION:
|
|
*precision= cbuf[0];
|
|
break;
|
|
case FIELDGEOM_SCALE:
|
|
*scale= cbuf[0];
|
|
break;
|
|
case FIELDGEOM_SRID:
|
|
*srid= uint4korr(cbuf);
|
|
break;
|
|
case FIELDGEOM_END:
|
|
goto end_of_record;
|
|
}
|
|
if (option_id > 0 && option_id <= 40)
|
|
cbuf+= 1;
|
|
else if (option_id > 40 && option_id <= 80)
|
|
cbuf+= 2;
|
|
else if (option_id > 80 && option_id <= 120)
|
|
cbuf+= 4;
|
|
else if (option_id > 120 && option_id <= 160)
|
|
cbuf+= 8;
|
|
else /* > 160 and <=255 */
|
|
cbuf+= cbuf[0] ? 1 + cbuf[0] : 3 + uint2korr(cbuf+1);
|
|
}
|
|
|
|
end_of_record:
|
|
return (uint)(cbuf - buf);
|
|
}
|
|
|
|
|
|
|
|
void Field_geom::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs= &my_charset_latin1;
|
|
switch (geom_type)
|
|
{
|
|
case GEOM_POINT:
|
|
res.set(STRING_WITH_LEN("point"), cs);
|
|
break;
|
|
case GEOM_LINESTRING:
|
|
res.set(STRING_WITH_LEN("linestring"), cs);
|
|
break;
|
|
case GEOM_POLYGON:
|
|
res.set(STRING_WITH_LEN("polygon"), cs);
|
|
break;
|
|
case GEOM_MULTIPOINT:
|
|
res.set(STRING_WITH_LEN("multipoint"), cs);
|
|
break;
|
|
case GEOM_MULTILINESTRING:
|
|
res.set(STRING_WITH_LEN("multilinestring"), cs);
|
|
break;
|
|
case GEOM_MULTIPOLYGON:
|
|
res.set(STRING_WITH_LEN("multipolygon"), cs);
|
|
break;
|
|
case GEOM_GEOMETRYCOLLECTION:
|
|
res.set(STRING_WITH_LEN("geometrycollection"), cs);
|
|
break;
|
|
default:
|
|
res.set(STRING_WITH_LEN("geometry"), cs);
|
|
}
|
|
}
|
|
|
|
|
|
int Field_geom::store(double nr)
|
|
{
|
|
my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
|
|
ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
|
|
return -1;
|
|
}
|
|
|
|
|
|
int Field_geom::store(longlong nr, bool unsigned_val)
|
|
{
|
|
my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
|
|
ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
|
|
return -1;
|
|
}
|
|
|
|
|
|
int Field_geom::store_decimal(const my_decimal *)
|
|
{
|
|
my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
|
|
ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
|
|
return -1;
|
|
}
|
|
|
|
|
|
int Field_geom::store(const char *from, size_t length, CHARSET_INFO *cs)
|
|
{
|
|
if (!length)
|
|
bzero(ptr, Field_blob::pack_length());
|
|
else
|
|
{
|
|
if (from == Geometry::bad_geometry_data.ptr())
|
|
goto err;
|
|
// Check given WKB
|
|
uint32 wkb_type;
|
|
if (length < SRID_SIZE + WKB_HEADER_SIZE + 4)
|
|
goto err;
|
|
wkb_type= uint4korr(from + SRID_SIZE + 1);
|
|
if (wkb_type < (uint32) Geometry::wkb_point ||
|
|
wkb_type > (uint32) Geometry::wkb_last)
|
|
goto err;
|
|
|
|
if (geom_type != Field::GEOM_GEOMETRY &&
|
|
geom_type != Field::GEOM_GEOMETRYCOLLECTION &&
|
|
(uint32) geom_type != wkb_type)
|
|
{
|
|
my_error(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, MYF(0),
|
|
Geometry::ci_collection[geom_type]->m_name.str,
|
|
Geometry::ci_collection[wkb_type]->m_name.str,
|
|
field_name.str,
|
|
(ulong) table->in_use->get_stmt_da()->
|
|
current_row_for_warning());
|
|
goto err_exit;
|
|
}
|
|
|
|
Field_blob::store_length(length);
|
|
if ((table->copy_blobs || length <= MAX_FIELD_WIDTH) &&
|
|
from != value.ptr())
|
|
{ // Must make a copy
|
|
value.copy(from, length, cs);
|
|
from= value.ptr();
|
|
}
|
|
bmove(ptr + packlength, &from, sizeof(char*));
|
|
}
|
|
return 0;
|
|
|
|
err:
|
|
my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
|
|
ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
|
|
err_exit:
|
|
bzero(ptr, Field_blob::pack_length());
|
|
return -1;
|
|
}
|
|
|
|
Field::geometry_type Field_geom::geometry_type_merge(geometry_type a,
|
|
geometry_type b)
|
|
{
|
|
if (a == b)
|
|
return a;
|
|
return Field::GEOM_GEOMETRY;
|
|
}
|
|
|
|
|
|
uint Field_geom::is_equal(Create_field *new_field)
|
|
{
|
|
return new_field->type_handler() == type_handler() &&
|
|
/*
|
|
- Allow ALTER..INPLACE to supertype (GEOMETRY),
|
|
e.g. POINT to GEOMETRY or POLYGON to GEOMETRY.
|
|
- Allow ALTER..INPLACE to the same geometry type: POINT -> POINT
|
|
*/
|
|
(new_field->geom_type == geom_type ||
|
|
new_field->geom_type == GEOM_GEOMETRY);
|
|
}
|
|
|
|
|
|
bool Field_geom::can_optimize_range(const Item_bool_func *cond,
|
|
const Item *item,
|
|
bool is_eq_func) const
|
|
{
|
|
return item->cmp_type() == STRING_RESULT;
|
|
}
|
|
|
|
|
|
bool Field_geom::load_data_set_no_data(THD *thd, bool fixed_format)
|
|
{
|
|
return Field_geom::load_data_set_null(thd);
|
|
}
|
|
|
|
|
|
bool Field_geom::load_data_set_null(THD *thd)
|
|
{
|
|
Field_blob::reset();
|
|
if (!maybe_null())
|
|
{
|
|
my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field_name.str,
|
|
thd->get_stmt_da()->current_row_for_warning());
|
|
return true;
|
|
}
|
|
set_null();
|
|
set_has_explicit_value(); // Do not auto-update this field
|
|
return false;
|
|
}
|
|
|
|
|
|
#endif /*HAVE_SPATIAL*/
|
|
|
|
/****************************************************************************
|
|
** 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.
|
|
****************************************************************************/
|
|
|
|
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)
|
|
{
|
|
ASSERT_COLUMN_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->cset->lengthsp(field_charset, from, length);
|
|
uint tmp=find_type2(typelib, from, length, field_charset);
|
|
if (!tmp)
|
|
{
|
|
if (length < 6) // Can't be more than 99999 enums
|
|
{
|
|
/* This is for reading numbers with LOAD DATA INFILE */
|
|
char *end;
|
|
tmp=(uint) my_strntoul(cs,from,length,10,&end,&err);
|
|
if (err || end != from+length || tmp > typelib->count)
|
|
{
|
|
tmp=0;
|
|
set_warning(WARN_DATA_TRUNCATED, 1);
|
|
err= 1;
|
|
}
|
|
if ((get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) && !length)
|
|
err= 0;
|
|
}
|
|
else
|
|
{
|
|
set_warning(WARN_DATA_TRUNCATED, 1);
|
|
err= 1;
|
|
}
|
|
}
|
|
store_type((ulonglong) tmp);
|
|
return err;
|
|
}
|
|
|
|
|
|
int Field_enum::store(double nr)
|
|
{
|
|
return Field_enum::store((longlong) nr, FALSE);
|
|
}
|
|
|
|
|
|
int Field_enum::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
return read_lowendian(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
|
|
*/
|
|
int Field_enum::save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
*metadata_ptr= real_type();
|
|
*(metadata_ptr + 1)= pack_length();
|
|
return 2;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
uchar *old= ptr;
|
|
ptr= (uchar*) a_ptr;
|
|
ulonglong a=Field_enum::val_int();
|
|
ptr= (uchar*) b_ptr;
|
|
ulonglong b=Field_enum::val_int();
|
|
ptr= old;
|
|
return (a < b) ? -1 : (a > b) ? 1 : 0;
|
|
}
|
|
|
|
void Field_enum::sort_string(uchar *to,uint length __attribute__((unused)))
|
|
{
|
|
ulonglong value=Field_enum::val_int();
|
|
to+=packlength-1;
|
|
for (uint i=0 ; i < packlength ; i++)
|
|
{
|
|
*to-- = (uchar) (value & 255);
|
|
value>>=8;
|
|
}
|
|
}
|
|
|
|
|
|
void Field_enum::sql_type(String &res) const
|
|
{
|
|
char buffer[255];
|
|
String enum_item(buffer, sizeof(buffer), res.charset());
|
|
|
|
res.length(0);
|
|
res.append(STRING_WITH_LEN("enum("));
|
|
|
|
bool flag=0;
|
|
uint *len= typelib->type_lengths;
|
|
for (const char **pos= typelib->type_names; *pos; pos++, len++)
|
|
{
|
|
uint dummy_errors;
|
|
if (flag)
|
|
res.append(',');
|
|
/* convert to res.charset() == utf8, then quote */
|
|
enum_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
|
|
append_unescaped(&res, enum_item.ptr(), enum_item.length());
|
|
flag= 1;
|
|
}
|
|
res.append(')');
|
|
}
|
|
|
|
|
|
Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table,
|
|
bool keep_type)
|
|
{
|
|
Field_enum *res= (Field_enum*) Field::make_new_field(root, new_table,
|
|
keep_type);
|
|
if (res)
|
|
res->typelib= copy_typelib(root, typelib);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
set type.
|
|
This is a string which can have a collection of different values.
|
|
Each string value is separated with a ','.
|
|
For example "One,two,five"
|
|
If one uses this string in a number context one gets the bits as a longlong
|
|
number.
|
|
*/
|
|
|
|
|
|
int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_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=my_strntoull(cs,from,length,10,&end,&err);
|
|
if (err || end != from+length ||
|
|
tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1))
|
|
{
|
|
tmp=0;
|
|
set_warning(WARN_DATA_TRUNCATED, 1);
|
|
err= 1;
|
|
}
|
|
}
|
|
else if (got_warning)
|
|
set_warning(WARN_DATA_TRUNCATED, 1);
|
|
store_type(tmp);
|
|
return err;
|
|
}
|
|
|
|
|
|
int Field_set::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int error= 0;
|
|
ulonglong max_nr;
|
|
|
|
if (sizeof(ulonglong)*8 <= typelib->count)
|
|
max_nr= ULONGLONG_MAX;
|
|
else
|
|
max_nr= (1ULL << typelib->count) - 1;
|
|
|
|
if ((ulonglong) nr > max_nr)
|
|
{
|
|
nr&= max_nr;
|
|
set_warning(WARN_DATA_TRUNCATED, 1);
|
|
error=1;
|
|
}
|
|
store_type((ulonglong) nr);
|
|
return error;
|
|
}
|
|
|
|
|
|
String *Field_set::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ulonglong tmp=(ulonglong) Field_enum::val_int();
|
|
uint bitnr=0;
|
|
|
|
/*
|
|
Some callers expect *val_buffer to contain the result,
|
|
so we assign to it, rather than doing 'return &empty_set_string.
|
|
*/
|
|
*val_buffer= empty_set_string;
|
|
if (tmp == 0)
|
|
{
|
|
return val_buffer;
|
|
}
|
|
|
|
val_buffer->set_charset(field_charset);
|
|
val_buffer->length(0);
|
|
|
|
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(')');
|
|
}
|
|
|
|
/**
|
|
@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, TYPELIB *t1, TYPELIB *t2)
|
|
{
|
|
for (uint i= 0; i < t1->count; i++)
|
|
if (my_strnncoll(charset,
|
|
(const uchar*) t1->type_names[i],
|
|
t1->type_lengths[i],
|
|
(const uchar*) 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
|
|
{
|
|
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 IS_EQUAL_YES if fields are compatible.
|
|
IS_EQUAL_NO otherwise.
|
|
*/
|
|
|
|
uint Field_enum::is_equal(Create_field *new_field)
|
|
{
|
|
TYPELIB *values= new_field->interval;
|
|
|
|
/*
|
|
The fields are compatible if they have the same flags,
|
|
type, charset and have the same underlying length.
|
|
*/
|
|
if (new_field->type_handler() != type_handler() ||
|
|
new_field->charset != field_charset ||
|
|
new_field->pack_length != pack_length())
|
|
return IS_EQUAL_NO;
|
|
|
|
/*
|
|
Changing the definition of an ENUM or SET column by adding a new
|
|
enumeration or set members to the end of the list of valid member
|
|
values only alters table metadata and not table data.
|
|
*/
|
|
if (typelib->count > values->count)
|
|
return IS_EQUAL_NO;
|
|
|
|
/* Check whether there are modification before the end. */
|
|
if (! compare_type_names(field_charset, typelib, new_field->interval))
|
|
return IS_EQUAL_NO;
|
|
|
|
return IS_EQUAL_YES;
|
|
}
|
|
|
|
|
|
uchar *Field_enum::pack(uchar *to, const uchar *from, uint max_length)
|
|
{
|
|
DBUG_ENTER("Field_enum::pack");
|
|
DBUG_PRINT("debug", ("packlength: %d", packlength));
|
|
DBUG_DUMP("from", from, packlength);
|
|
DBUG_RETURN(pack_int(to, from, packlength));
|
|
}
|
|
|
|
const uchar *Field_enum::unpack(uchar *to, const uchar *from,
|
|
const uchar *from_end, uint param_data)
|
|
{
|
|
DBUG_ENTER("Field_enum::unpack");
|
|
DBUG_PRINT("debug", ("packlength: %d", packlength));
|
|
DBUG_DUMP("from", from, packlength);
|
|
DBUG_RETURN(unpack_int(to, from, from_end, packlength));
|
|
}
|
|
|
|
|
|
/**
|
|
@return
|
|
returns 1 if the fields are equally defined
|
|
*/
|
|
bool Field_num::eq_def(const Field *field) const
|
|
{
|
|
if (!Field::eq_def(field))
|
|
return 0;
|
|
Field_num *from_num= (Field_num*) field;
|
|
|
|
if (unsigned_flag != from_num->unsigned_flag ||
|
|
(zerofill && !from_num->zerofill && !zero_pack()) ||
|
|
dec != from_num->dec)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
Check whether two numeric fields can be considered 'equal' for table
|
|
alteration purposes. Fields are equal if they are of the same type
|
|
and retain the same pack length.
|
|
*/
|
|
|
|
uint Field_num::is_equal(Create_field *new_field)
|
|
{
|
|
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->pack_length == pack_length()));
|
|
}
|
|
|
|
|
|
bool Field_enum::can_optimize_range(const Item_bool_func *cond,
|
|
const Item *item,
|
|
bool is_eq_func) const
|
|
{
|
|
return item->cmp_type() != TIME_RESULT;
|
|
}
|
|
|
|
|
|
bool Field_enum::can_optimize_keypart_ref(const Item_bool_func *cond,
|
|
const Item *item) const
|
|
{
|
|
switch (item->cmp_type())
|
|
{
|
|
case TIME_RESULT:
|
|
return false;
|
|
case INT_RESULT:
|
|
case DECIMAL_RESULT:
|
|
case REAL_RESULT:
|
|
return true;
|
|
case STRING_RESULT:
|
|
return charset() == cond->compare_collation();
|
|
case ROW_RESULT:
|
|
DBUG_ASSERT(0);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
Bit field.
|
|
|
|
We store the first 0 - 6 uneven bits among the null bits
|
|
at the start of the record. The rest bytes are stored in
|
|
the record itself.
|
|
|
|
For example:
|
|
|
|
CREATE TABLE t1 (a int, b bit(17), c bit(21) not null, d bit(8));
|
|
We would store data as follows in the record:
|
|
|
|
Byte Bit
|
|
1 7 - reserve for delete
|
|
6 - null bit for 'a'
|
|
5 - null bit for 'b'
|
|
4 - first (high) bit of 'b'
|
|
3 - first (high) bit of 'c'
|
|
2 - second bit of 'c'
|
|
1 - third bit of 'c'
|
|
0 - forth bit of 'c'
|
|
2 7 - firth bit of 'c'
|
|
6 - null bit for 'd'
|
|
3 - 6 four bytes for 'a'
|
|
7 - 8 two bytes for 'b'
|
|
9 - 10 two bytes for 'c'
|
|
11 one byte for 'd'
|
|
*/
|
|
|
|
Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
|
|
uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
|
|
enum utype unireg_check_arg,
|
|
const LEX_CSTRING *field_name_arg)
|
|
: Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
|
|
unireg_check_arg, field_name_arg),
|
|
bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7),
|
|
bytes_in_rec(len_arg / 8)
|
|
{
|
|
DBUG_ENTER("Field_bit::Field_bit");
|
|
DBUG_PRINT("enter", ("ptr_arg: %p, null_ptr_arg: %p, len_arg: %u, bit_len: %u, bytes_in_rec: %u",
|
|
ptr_arg, null_ptr_arg, len_arg, bit_len, bytes_in_rec));
|
|
flags|= UNSIGNED_FLAG;
|
|
/*
|
|
Ensure that Field::eq() can distinguish between two different bit fields.
|
|
(two bit fields that are not null, may have same ptr and null_ptr)
|
|
*/
|
|
if (!null_ptr_arg)
|
|
null_bit= bit_ofs_arg;
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
|
|
void Field_bit::hash(ulong *nr, ulong *nr2)
|
|
{
|
|
if (is_null())
|
|
{
|
|
*nr^= (*nr << 1) | 1;
|
|
}
|
|
else
|
|
{
|
|
CHARSET_INFO *cs= &my_charset_bin;
|
|
longlong value= Field_bit::val_int();
|
|
uchar tmp[8];
|
|
mi_int8store(tmp,value);
|
|
cs->coll->hash_sort(cs, tmp, 8, nr, nr2);
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
uint Field_bit::is_equal(Create_field *new_field)
|
|
{
|
|
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)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int delta;
|
|
|
|
for (; length && !*from; from++, length--) // skip left 0's
|
|
;
|
|
delta= (int)(bytes_in_rec - length);
|
|
|
|
if (delta < -1 ||
|
|
(delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) ||
|
|
(!bit_len && delta < 0))
|
|
{
|
|
set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len);
|
|
memset(ptr, 0xff, bytes_in_rec);
|
|
if (get_thd()->really_abort_on_warning())
|
|
set_warning(ER_DATA_TOO_LONG, 1);
|
|
else
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
return 1;
|
|
}
|
|
/* delta is >= -1 here */
|
|
if (delta > 0)
|
|
{
|
|
if (bit_len)
|
|
clr_rec_bits(bit_ptr, bit_ofs, bit_len);
|
|
bzero(ptr, delta);
|
|
memcpy(ptr + delta, from, length);
|
|
}
|
|
else if (delta == 0)
|
|
{
|
|
if (bit_len)
|
|
clr_rec_bits(bit_ptr, bit_ofs, bit_len);
|
|
memcpy(ptr, from, length);
|
|
}
|
|
else
|
|
{
|
|
if (bit_len)
|
|
{
|
|
set_rec_bits((uchar) *from, bit_ptr, bit_ofs, bit_len);
|
|
from++;
|
|
}
|
|
memcpy(ptr, from, bytes_in_rec);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Field_bit::store(double nr)
|
|
{
|
|
return Field_bit::store((longlong) nr, FALSE);
|
|
}
|
|
|
|
|
|
int Field_bit::store(longlong nr, bool unsigned_val)
|
|
{
|
|
char buf[8];
|
|
|
|
mi_int8store(buf, nr);
|
|
return store(buf, 8, NULL);
|
|
}
|
|
|
|
|
|
int Field_bit::store_decimal(const my_decimal *val)
|
|
{
|
|
int err= 0;
|
|
longlong i= convert_decimal2longlong(val, 1, &err);
|
|
return MY_TEST(err | store(i, TRUE));
|
|
}
|
|
|
|
|
|
double Field_bit::val_real(void)
|
|
{
|
|
return (double) Field_bit::val_int();
|
|
}
|
|
|
|
|
|
longlong Field_bit::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_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)))
|
|
{
|
|
ASSERT_COLUMN_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)
|
|
{
|
|
ASSERT_COLUMN_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_max(const uchar *a, const uchar *b, uint max_len)
|
|
{
|
|
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 length)
|
|
{
|
|
if (bit_len)
|
|
{
|
|
int flag;
|
|
uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len);
|
|
if ((flag= (int) (bits - *str)))
|
|
return flag;
|
|
str++;
|
|
length--;
|
|
}
|
|
return memcmp(ptr, str, bytes_in_rec);
|
|
}
|
|
|
|
|
|
int Field_bit::cmp_offset(uint 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, imagetype type_arg)
|
|
{
|
|
if (bit_len)
|
|
{
|
|
uchar bits= get_rec_bits(bit_ptr, 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;
|
|
}
|
|
|
|
|
|
/**
|
|
Save the field metadata for bit fields.
|
|
|
|
Saves the bit length in the first byte and bytes in record 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
|
|
*/
|
|
int Field_bit::save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
DBUG_ENTER("Field_bit::save_field_metadata");
|
|
DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d",
|
|
bit_len, bytes_in_rec));
|
|
/*
|
|
Since this class and Field_bit_as_char have different ideas of
|
|
what should be stored here, we compute the values of the metadata
|
|
explicitly using the field_length.
|
|
*/
|
|
metadata_ptr[0]= field_length % 8;
|
|
metadata_ptr[1]= field_length / 8;
|
|
DBUG_RETURN(2);
|
|
}
|
|
|
|
|
|
/**
|
|
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)
|
|
{
|
|
uint const from_len= (field_metadata >> 8U) & 0x00ff;
|
|
uint const from_bit_len= field_metadata & 0x00ff;
|
|
uint const source_size= from_len + ((from_bit_len > 0) ? 1 : 0);
|
|
return (source_size);
|
|
}
|
|
|
|
|
|
bool
|
|
Field_bit::compatible_field_size(uint field_metadata,
|
|
Relay_log_info * __attribute__((unused)),
|
|
uint16 mflags,
|
|
int *order_var)
|
|
{
|
|
DBUG_ENTER("Field_bit::compatible_field_size");
|
|
DBUG_ASSERT((field_metadata >> 16) == 0);
|
|
uint from_bit_len=
|
|
8 * (field_metadata >> 8) + (field_metadata & 0xff);
|
|
uint to_bit_len= max_display_length();
|
|
DBUG_PRINT("debug", ("from_bit_len: %u, to_bit_len: %u",
|
|
from_bit_len, to_bit_len));
|
|
/*
|
|
If the bit length exact flag is clear, we are dealing with an old
|
|
master, so we allow some less strict behaviour if replicating by
|
|
moving both bit lengths to an even multiple of 8.
|
|
|
|
We do this by computing the number of bytes to store the field
|
|
instead, and then compare the result.
|
|
*/
|
|
if (!(mflags & Table_map_log_event::TM_BIT_LEN_EXACT_F)) {
|
|
from_bit_len= (from_bit_len + 7) / 8;
|
|
to_bit_len= (to_bit_len + 7) / 8;
|
|
}
|
|
|
|
*order_var= compare(from_bit_len, to_bit_len);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
|
|
|
|
|
|
void Field_bit::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs= res.charset();
|
|
size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
|
|
"bit(%d)", (int) field_length);
|
|
res.length(length);
|
|
}
|
|
|
|
|
|
uchar *
|
|
Field_bit::pack(uchar *to, const uchar *from, uint max_length)
|
|
{
|
|
DBUG_ASSERT(max_length > 0);
|
|
uint length;
|
|
if (bit_len > 0)
|
|
{
|
|
/*
|
|
We have the following:
|
|
|
|
ptr Points into a field in record R1
|
|
from Points to a field in a record R2
|
|
bit_ptr Points to the byte (in the null bytes) that holds the
|
|
odd bits of R1
|
|
from_bitp Points to the byte that holds the odd bits of R2
|
|
|
|
We have the following:
|
|
|
|
ptr - bit_ptr = from - from_bitp
|
|
|
|
We want to isolate 'from_bitp', so this gives:
|
|
|
|
ptr - bit_ptr - from = - from_bitp
|
|
- ptr + bit_ptr + from = from_bitp
|
|
bit_ptr + from - ptr = from_bitp
|
|
*/
|
|
uchar bits= get_rec_bits(bit_ptr + (from - ptr), bit_ofs, bit_len);
|
|
*to++= bits;
|
|
}
|
|
length= MY_MIN(bytes_in_rec, max_length - (bit_len > 0));
|
|
memcpy(to, from, length);
|
|
return to + length;
|
|
}
|
|
|
|
|
|
/**
|
|
Unpack a bit field from row data.
|
|
|
|
This method is used to unpack a bit field from a master whose size
|
|
of the field is less than that of the slave.
|
|
|
|
@param to Destination of the data
|
|
@param from Source of the data
|
|
@param param_data Bit length (upper) and length (lower) values
|
|
|
|
@return New pointer into memory based on from + length of the data
|
|
*/
|
|
const uchar *
|
|
Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end,
|
|
uint param_data)
|
|
{
|
|
DBUG_ENTER("Field_bit::unpack");
|
|
DBUG_PRINT("enter", ("to: %p, from: %p, param_data: 0x%x",
|
|
to, from, param_data));
|
|
DBUG_PRINT("debug", ("bit_ptr: %p, bit_len: %u, bit_ofs: %u",
|
|
bit_ptr, bit_len, bit_ofs));
|
|
uint const from_len= (param_data >> 8U) & 0x00ff;
|
|
uint const from_bit_len= param_data & 0x00ff;
|
|
DBUG_PRINT("debug", ("from_len: %u, from_bit_len: %u",
|
|
from_len, from_bit_len));
|
|
/*
|
|
If the parameter data is zero (i.e., undefined), or if the master
|
|
and slave have the same sizes, then use the old unpack() method.
|
|
*/
|
|
if (param_data == 0 ||
|
|
((from_bit_len == bit_len) && (from_len == bytes_in_rec)))
|
|
{
|
|
if (from + bytes_in_rec + MY_TEST(bit_len) > from_end)
|
|
return 0; // Error in data
|
|
|
|
if (bit_len > 0)
|
|
{
|
|
/*
|
|
set_rec_bits is a macro, don't put the post-increment in the
|
|
argument since that might cause strange side-effects.
|
|
|
|
For the choice of the second argument, see the explanation for
|
|
Field_bit::pack().
|
|
*/
|
|
set_rec_bits(*from, bit_ptr + (to - ptr), bit_ofs, bit_len);
|
|
from++;
|
|
}
|
|
memcpy(to, from, bytes_in_rec);
|
|
DBUG_RETURN(from + bytes_in_rec);
|
|
}
|
|
|
|
/*
|
|
We are converting a smaller bit field to a larger one here.
|
|
To do that, we first need to construct a raw value for the original
|
|
bit value stored in the from buffer. Then that needs to be converted
|
|
to the larger field then sent to store() for writing to the field.
|
|
Lastly the odd bits need to be masked out if the bytes_in_rec > 0.
|
|
Otherwise stray bits can cause spurious values.
|
|
*/
|
|
|
|
uint len= from_len + ((from_bit_len > 0) ? 1 : 0);
|
|
uint new_len= (field_length + 7) / 8;
|
|
|
|
if (from + len > from_end || new_len < len)
|
|
return 0; // Error in data
|
|
|
|
char *value= (char *)my_alloca(new_len);
|
|
bzero(value, new_len);
|
|
|
|
memcpy(value + (new_len - len), from, len);
|
|
/*
|
|
Mask out the unused bits in the partial byte.
|
|
TODO: Add code to the master to always mask these bits and remove
|
|
the following.
|
|
*/
|
|
if ((from_bit_len > 0) && (from_len > 0))
|
|
value[new_len - len]= value[new_len - len] & ((1U << from_bit_len) - 1);
|
|
bitmap_set_bit(table->write_set,field_index);
|
|
store(value, new_len, system_charset_info);
|
|
my_afree(value);
|
|
DBUG_RETURN(from + len);
|
|
}
|
|
|
|
|
|
int Field_bit::set_default()
|
|
{
|
|
if (bit_len > 0)
|
|
{
|
|
my_ptrdiff_t const col_offset= table->s->default_values - table->record[0];
|
|
uchar bits= get_rec_bits(bit_ptr + col_offset, bit_ofs, bit_len);
|
|
set_rec_bits(bits, bit_ptr, bit_ofs, bit_len);
|
|
}
|
|
return Field::set_default();
|
|
}
|
|
|
|
/*
|
|
Bit field support for non-MyISAM tables.
|
|
*/
|
|
|
|
Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg,
|
|
uchar *null_ptr_arg, uchar null_bit_arg,
|
|
enum utype unireg_check_arg,
|
|
const LEX_CSTRING *field_name_arg)
|
|
:Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0,
|
|
unireg_check_arg, field_name_arg)
|
|
{
|
|
flags|= UNSIGNED_FLAG;
|
|
bit_len= 0;
|
|
bytes_in_rec= (len_arg + 7) / 8;
|
|
}
|
|
|
|
|
|
int Field_bit_as_char::store(const char *from, size_t length, CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
|
|
int delta;
|
|
uchar bits= (uchar) (field_length & 7);
|
|
|
|
for (; length && !*from; from++, length--) // skip left 0's
|
|
;
|
|
delta= (int)(bytes_in_rec - length);
|
|
|
|
if (delta < 0 ||
|
|
(delta == 0 && bits && (uint) (uchar) *from >= (uint) (1 << bits)))
|
|
{
|
|
memset(ptr, 0xff, bytes_in_rec);
|
|
if (bits)
|
|
*ptr&= ((1 << bits) - 1); /* set first uchar */
|
|
if (get_thd()->really_abort_on_warning())
|
|
set_warning(ER_DATA_TOO_LONG, 1);
|
|
else
|
|
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
return 1;
|
|
}
|
|
bzero(ptr, delta);
|
|
memcpy(ptr + delta, from, length);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void Field_bit_as_char::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs= res.charset();
|
|
size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
|
|
"bit(%d)", (int) field_length);
|
|
res.length(length);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
Handling of field and Create_field
|
|
*****************************************************************************/
|
|
|
|
bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
|
|
bool reuse_interval_list_values)
|
|
{
|
|
DBUG_ENTER("Column_definition::create_interval_from_interval_list");
|
|
DBUG_ASSERT(!interval);
|
|
if (!(interval= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB))))
|
|
DBUG_RETURN(true); // EOM
|
|
|
|
List_iterator<String> it(interval_list);
|
|
StringBuffer<64> conv;
|
|
char comma_buf[5]; /* 5 bytes for 'filename' charset */
|
|
DBUG_ASSERT(sizeof(comma_buf) >= charset->mbmaxlen);
|
|
int comma_length= charset->cset->wc_mb(charset, ',',
|
|
(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,
|
|
&interval->type_names,
|
|
sizeof(char*) * (interval_list.elements + 1),
|
|
&interval->type_lengths,
|
|
sizeof(uint) * (interval_list.elements + 1),
|
|
NullS))
|
|
goto err; // EOM
|
|
|
|
interval->name= "";
|
|
interval->count= interval_list.elements;
|
|
|
|
for (uint i= 0; i < interval->count; 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->cset->lengthsp(charset, value.str, value.length);
|
|
((char*) value.str)[value.length]= '\0';
|
|
|
|
if (real_field_type() == MYSQL_TYPE_SET)
|
|
{
|
|
if (charset->coll->instr(charset, 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;
|
|
}
|
|
}
|
|
interval->type_names[i]= value.str;
|
|
interval->type_lengths[i]= (uint)value.length;
|
|
}
|
|
interval->type_names[interval->count]= 0; // End marker
|
|
interval->type_lengths[interval->count]= 0;
|
|
interval_list.empty(); // Don't need interval_list anymore
|
|
DBUG_RETURN(false);
|
|
err:
|
|
interval= NULL; // Avoid having both non-empty interval_list and interval
|
|
DBUG_RETURN(true);
|
|
}
|
|
|
|
|
|
bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root,
|
|
bool reuse_interval_list_values)
|
|
{
|
|
DBUG_ENTER("Column_definition::prepare_interval_field");
|
|
DBUG_ASSERT(real_field_type() == MYSQL_TYPE_ENUM ||
|
|
real_field_type() == MYSQL_TYPE_SET);
|
|
/*
|
|
Interval values are either in "interval" or in "interval_list",
|
|
but not in both at the same time, and are not empty at the same time.
|
|
- Values are in "interval_list" when we're coming from the parser
|
|
in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}.
|
|
- Values are in "interval" when we're in ALTER TABLE.
|
|
|
|
In a corner case with an empty set like SET(''):
|
|
- after the parser we have interval_list.elements==1
|
|
- in ALTER TABLE we have a non-NULL interval with interval->count==1,
|
|
with interval->type_names[0]=="" and interval->type_lengths[0]==0.
|
|
So the assert is still valid for this corner case.
|
|
|
|
ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible,
|
|
as the parser requires at least one element, so for a ENUM or SET field it
|
|
should never happen that both internal_list.elements and interval are 0.
|
|
*/
|
|
DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0));
|
|
|
|
/*
|
|
Create typelib from interval_list, and if necessary
|
|
convert strings from client character set to the
|
|
column character set.
|
|
*/
|
|
if (interval_list.elements &&
|
|
create_interval_from_interval_list(mem_root,
|
|
reuse_interval_list_values))
|
|
DBUG_RETURN(true);
|
|
|
|
if (!reuse_interval_list_values)
|
|
{
|
|
/*
|
|
We're initializing from an existing table or view Field_enum
|
|
(e.g. for a %TYPE variable) rather than from the parser.
|
|
The constructor Column_definition(THD*,Field*,Field*) has already
|
|
copied the TYPELIB pointer from the original Field_enum.
|
|
Now we need to make a permanent copy of that TYPELIB,
|
|
as the original field can be freed before the end of the life
|
|
cycle of "this".
|
|
*/
|
|
DBUG_ASSERT(interval);
|
|
if (!(interval= copy_typelib(mem_root, interval)))
|
|
DBUG_RETURN(true);
|
|
}
|
|
prepare_interval_field_calc_length();
|
|
DBUG_RETURN(false);
|
|
}
|
|
|
|
|
|
void Column_definition::set_attributes(const Lex_field_type_st &type,
|
|
CHARSET_INFO *cs)
|
|
{
|
|
DBUG_ASSERT(type_handler() == &type_handler_null);
|
|
DBUG_ASSERT(charset == &my_charset_bin || charset == NULL);
|
|
DBUG_ASSERT(length == 0);
|
|
DBUG_ASSERT(decimals == 0);
|
|
|
|
set_handler(type.type_handler());
|
|
charset= cs;
|
|
|
|
if (type.length())
|
|
{
|
|
int err;
|
|
length= my_strtoll10(type.length(), NULL, &err);
|
|
if (err)
|
|
length= ~0ULL; // safety
|
|
}
|
|
|
|
if (type.dec())
|
|
decimals= (uint) atoi(type.dec());
|
|
}
|
|
|
|
|
|
void Column_definition::create_length_to_internal_length_bit()
|
|
{
|
|
if (f_bit_as_char(pack_flag))
|
|
{
|
|
key_length= pack_length= ((length + 7) & ~7) / 8;
|
|
}
|
|
else
|
|
{
|
|
pack_length= (uint) length / 8;
|
|
/* We need one extra byte to store the bits we save among the null bits */
|
|
key_length= pack_length + MY_TEST(length & 7);
|
|
}
|
|
}
|
|
|
|
|
|
void Column_definition::create_length_to_internal_length_newdecimal()
|
|
{
|
|
key_length= pack_length=
|
|
my_decimal_get_binary_size(my_decimal_length_to_precision((uint) length,
|
|
decimals,
|
|
flags &
|
|
UNSIGNED_FLAG),
|
|
decimals);
|
|
}
|
|
|
|
|
|
bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name,
|
|
enum_vcol_info_type type)
|
|
|
|
{
|
|
bool ret;
|
|
Item::vcol_func_processor_result res;
|
|
|
|
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
|
|
*/
|
|
res.errors= 0;
|
|
ret= vcol->expr->walk(&Item::check_vcol_func_processor, 0, &res);
|
|
vcol->flags= res.errors;
|
|
|
|
uint filter= VCOL_IMPOSSIBLE;
|
|
if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT)
|
|
filter|= VCOL_NOT_STRICTLY_DETERMINISTIC;
|
|
if (type == VCOL_GENERATED_VIRTUAL)
|
|
filter|= VCOL_NOT_VIRTUAL;
|
|
|
|
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), static_cast<ulonglong>(decimals),
|
|
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), static_cast<ulonglong>(decimals),
|
|
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), length, 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), length, field_name.str,
|
|
MAX_DATETIME_PRECISION);
|
|
return true;
|
|
}
|
|
length+= int_part_length + (length ? 1 : 0);
|
|
return false;
|
|
}
|
|
|
|
|
|
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_field_type(real_field_type());
|
|
if (check_expression(vcol_info, &field_name, vcol_info->stored_in_db
|
|
? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL))
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
|
|
if (check_constraint &&
|
|
check_expression(check_constraint, &field_name, VCOL_CHECK_FIELD))
|
|
DBUG_RETURN(1);
|
|
|
|
if (default_value)
|
|
{
|
|
Item *def_expr= default_value->expr;
|
|
if (check_expression(default_value, &field_name, VCOL_DEFAULT))
|
|
DBUG_RETURN(TRUE);
|
|
|
|
/* Constant's are stored in the 'empty_record', except for blobs */
|
|
if (def_expr->basic_const_item())
|
|
{
|
|
if (def_expr->type() == Item::NULL_ITEM)
|
|
{
|
|
default_value= 0;
|
|
if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG)
|
|
{
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
|
|
DBUG_RETURN(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (default_value && (flags & AUTO_INCREMENT_FLAG))
|
|
{
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
|
|
DBUG_RETURN(1);
|
|
}
|
|
|
|
if (default_value && !default_value->expr->basic_const_item() &&
|
|
mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME &&
|
|
default_value->expr->type() == Item::FUNC_ITEM)
|
|
{
|
|
/*
|
|
Special case: NOW() for TIMESTAMP and DATETIME fields are handled
|
|
as in MariaDB 10.1 by marking them in unireg_check.
|
|
*/
|
|
Item_func *fn= static_cast<Item_func*>(default_value->expr);
|
|
if (fn->functype() == Item_func::NOW_FUNC &&
|
|
(fn->decimals == 0 || fn->decimals >= length))
|
|
{
|
|
default_value= 0;
|
|
unireg_check= Field::TIMESTAMP_DN_FIELD;
|
|
}
|
|
}
|
|
|
|
if (on_update)
|
|
{
|
|
if (mysql_timestamp_type() != MYSQL_TIMESTAMP_DATETIME ||
|
|
on_update->decimals < length)
|
|
{
|
|
my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name.str);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
unireg_check= unireg_check == Field::NONE ? Field::TIMESTAMP_UN_FIELD
|
|
: Field::TIMESTAMP_DNUN_FIELD;
|
|
}
|
|
else if (flags & AUTO_INCREMENT_FLAG)
|
|
unireg_check= Field::NEXT_NUMBER;
|
|
|
|
if (type_handler()->Column_definition_fix_attributes(this))
|
|
DBUG_RETURN(true);
|
|
|
|
/* Remember the value of length */
|
|
char_length= (uint)length;
|
|
|
|
/*
|
|
Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and
|
|
it is NOT NULL, not an AUTO_INCREMENT field.
|
|
We need to do this check here and in mysql_create_prepare_table() as
|
|
sp_head::fill_field_definition() calls this function.
|
|
*/
|
|
if (!default_value && unireg_check == Field::NONE && (flags & NOT_NULL_FLAG))
|
|
{
|
|
/*
|
|
TIMESTAMP columns get implicit DEFAULT value when
|
|
explicit_defaults_for_timestamp is not set.
|
|
*/
|
|
if ((opt_explicit_defaults_for_timestamp ||
|
|
!is_timestamp_type()) && !vers_sys_field())
|
|
{
|
|
flags|= NO_DEFAULT_VALUE_FLAG;
|
|
}
|
|
}
|
|
|
|
|
|
if ((flags & AUTO_INCREMENT_FLAG) &&
|
|
!type_handler()->type_can_have_auto_increment_attribute())
|
|
{
|
|
my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
|
|
DBUG_RETURN(FALSE); /* success */
|
|
}
|
|
|
|
enum_field_types get_blob_type_from_length(ulong length)
|
|
{
|
|
enum_field_types type;
|
|
if (length < 256)
|
|
type= MYSQL_TYPE_TINY_BLOB;
|
|
else if (length < 65536)
|
|
type= MYSQL_TYPE_BLOB;
|
|
else if (length < 256L*256L*256L)
|
|
type= MYSQL_TYPE_MEDIUM_BLOB;
|
|
else
|
|
type= MYSQL_TYPE_LONG_BLOB;
|
|
return type;
|
|
}
|
|
|
|
|
|
uint pack_length_to_packflag(uint type)
|
|
{
|
|
switch (type) {
|
|
case 1: return f_settype((uint) MYSQL_TYPE_TINY);
|
|
case 2: return f_settype((uint) MYSQL_TYPE_SHORT);
|
|
case 3: return f_settype((uint) MYSQL_TYPE_INT24);
|
|
case 4: return f_settype((uint) MYSQL_TYPE_LONG);
|
|
case 8: return f_settype((uint) MYSQL_TYPE_LONGLONG);
|
|
}
|
|
return 0; // This shouldn't happen
|
|
}
|
|
|
|
|
|
Field *make_field(TABLE_SHARE *share,
|
|
MEM_ROOT *mem_root,
|
|
uchar *ptr, uint32 field_length,
|
|
uchar *null_pos, uchar null_bit,
|
|
uint pack_flag,
|
|
const Type_handler *handler,
|
|
CHARSET_INFO *field_charset,
|
|
Field::geometry_type geom_type, uint srid,
|
|
Field::utype unireg_check,
|
|
TYPELIB *interval,
|
|
const LEX_CSTRING *field_name,
|
|
uint32 flags)
|
|
{
|
|
uchar *UNINIT_VAR(bit_ptr);
|
|
uchar UNINIT_VAR(bit_offset);
|
|
|
|
DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
|
|
handler->name().ptr(), field_length, interval,
|
|
FLAGSTR(pack_flag, FIELDFLAG_BINARY),
|
|
FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
|
|
FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
|
|
FLAGSTR(pack_flag, FIELDFLAG_PACK),
|
|
FLAGSTR(pack_flag, FIELDFLAG_BLOB)));
|
|
|
|
if (handler == &type_handler_row)
|
|
{
|
|
DBUG_ASSERT(field_length == 0);
|
|
DBUG_ASSERT(f_maybe_null(pack_flag));
|
|
return new (mem_root) Field_row(ptr, field_name);
|
|
}
|
|
|
|
if (handler->real_field_type() == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
|
|
{
|
|
bit_ptr= null_pos;
|
|
bit_offset= null_bit;
|
|
if (f_maybe_null(pack_flag)) // if null field
|
|
{
|
|
bit_ptr+= (null_bit == 7); // shift bit_ptr and bit_offset
|
|
bit_offset= (bit_offset + 1) & 7;
|
|
}
|
|
}
|
|
|
|
if (!f_maybe_null(pack_flag))
|
|
{
|
|
null_pos=0;
|
|
null_bit=0;
|
|
}
|
|
else
|
|
{
|
|
null_bit= ((uchar) 1) << null_bit;
|
|
}
|
|
|
|
|
|
if (f_is_alpha(pack_flag))
|
|
{
|
|
if (!f_is_packed(pack_flag))
|
|
{
|
|
enum_field_types field_type= handler->real_field_type();
|
|
if (field_type == MYSQL_TYPE_STRING ||
|
|
field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string
|
|
field_type == MYSQL_TYPE_VAR_STRING)
|
|
return new (mem_root)
|
|
Field_string(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
field_charset);
|
|
if (field_type == MYSQL_TYPE_VARCHAR)
|
|
{
|
|
if (unireg_check == Field::TMYSQL_COMPRESSED)
|
|
return new (mem_root)
|
|
Field_varstring_compressed(
|
|
ptr, field_length,
|
|
HA_VARCHAR_PACKLENGTH(field_length),
|
|
null_pos, null_bit,
|
|
unireg_check, field_name,
|
|
share, field_charset, zlib_compression_method);
|
|
|
|
return new (mem_root)
|
|
Field_varstring(ptr,field_length,
|
|
HA_VARCHAR_PACKLENGTH(field_length),
|
|
null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
share,
|
|
field_charset);
|
|
}
|
|
return 0; // Error
|
|
}
|
|
|
|
// MYSQL_TYPE_VAR_STRING is handled above
|
|
DBUG_ASSERT(f_packtype(pack_flag) != MYSQL_TYPE_VAR_STRING);
|
|
const Type_handler *tmp;
|
|
tmp= Type_handler::get_handler_by_real_type((enum_field_types)
|
|
f_packtype(pack_flag));
|
|
uint pack_length= tmp->calc_pack_length(field_length);
|
|
|
|
#ifdef HAVE_SPATIAL
|
|
if (f_is_geom(pack_flag))
|
|
{
|
|
status_var_increment(current_thd->status_var.feature_gis);
|
|
return new (mem_root)
|
|
Field_geom(ptr,null_pos,null_bit,
|
|
unireg_check, field_name, share,
|
|
pack_length, geom_type, srid);
|
|
}
|
|
#endif
|
|
if (f_is_blob(pack_flag))
|
|
{
|
|
if (unireg_check == Field::TMYSQL_COMPRESSED)
|
|
return new (mem_root)
|
|
Field_blob_compressed(ptr, null_pos, null_bit,
|
|
unireg_check, field_name, share,
|
|
pack_length, field_charset, zlib_compression_method);
|
|
|
|
return new (mem_root)
|
|
Field_blob(ptr,null_pos,null_bit,
|
|
unireg_check, field_name, share,
|
|
pack_length, field_charset);
|
|
}
|
|
if (interval)
|
|
{
|
|
if (f_is_enum(pack_flag))
|
|
return new (mem_root)
|
|
Field_enum(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
pack_length, interval, field_charset);
|
|
else
|
|
return new (mem_root)
|
|
Field_set(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
pack_length, interval, field_charset);
|
|
}
|
|
}
|
|
|
|
switch (handler->real_field_type()) {
|
|
case MYSQL_TYPE_DECIMAL:
|
|
return new (mem_root)
|
|
Field_decimal(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
f_decimals(pack_flag),
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag) == 0);
|
|
case MYSQL_TYPE_NEWDECIMAL:
|
|
return new (mem_root)
|
|
Field_new_decimal(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
f_decimals(pack_flag),
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag) == 0);
|
|
case MYSQL_TYPE_FLOAT:
|
|
{
|
|
int decimals= f_decimals(pack_flag);
|
|
if (decimals == FLOATING_POINT_DECIMALS)
|
|
decimals= NOT_FIXED_DEC;
|
|
return new (mem_root)
|
|
Field_float(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
decimals,
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag)== 0);
|
|
}
|
|
case MYSQL_TYPE_DOUBLE:
|
|
{
|
|
int decimals= f_decimals(pack_flag);
|
|
if (decimals == FLOATING_POINT_DECIMALS)
|
|
decimals= NOT_FIXED_DEC;
|
|
return new (mem_root)
|
|
Field_double(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
decimals,
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag)== 0);
|
|
}
|
|
case MYSQL_TYPE_TINY:
|
|
return new (mem_root)
|
|
Field_tiny(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag) == 0);
|
|
case MYSQL_TYPE_SHORT:
|
|
return new (mem_root)
|
|
Field_short(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag) == 0);
|
|
case MYSQL_TYPE_INT24:
|
|
return new (mem_root)
|
|
Field_medium(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag) == 0);
|
|
case MYSQL_TYPE_LONG:
|
|
return new (mem_root)
|
|
Field_long(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag) == 0);
|
|
case MYSQL_TYPE_LONGLONG:
|
|
if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG))
|
|
{
|
|
return new (mem_root)
|
|
Field_vers_trx_id(ptr, field_length, null_pos, null_bit,
|
|
unireg_check, field_name,
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag) == 0);
|
|
}
|
|
else
|
|
{
|
|
return new (mem_root)
|
|
Field_longlong(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
f_is_zerofill(pack_flag) != 0,
|
|
f_is_dec(pack_flag) == 0);
|
|
}
|
|
case MYSQL_TYPE_TIMESTAMP:
|
|
{
|
|
uint dec= field_length > MAX_DATETIME_WIDTH ?
|
|
field_length - MAX_DATETIME_WIDTH - 1: 0;
|
|
return new_Field_timestamp(mem_root, ptr, null_pos, null_bit, unireg_check,
|
|
field_name, share, dec);
|
|
}
|
|
case MYSQL_TYPE_TIMESTAMP2:
|
|
{
|
|
uint dec= field_length > MAX_DATETIME_WIDTH ?
|
|
field_length - MAX_DATETIME_WIDTH - 1: 0;
|
|
return new (mem_root)
|
|
Field_timestampf(ptr, null_pos, null_bit, unireg_check,
|
|
field_name, share, dec);
|
|
}
|
|
case MYSQL_TYPE_YEAR:
|
|
return new (mem_root)
|
|
Field_year(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name);
|
|
case MYSQL_TYPE_DATE:
|
|
return new (mem_root)
|
|
Field_date(ptr,null_pos,null_bit,
|
|
unireg_check, field_name);
|
|
case MYSQL_TYPE_NEWDATE:
|
|
return new (mem_root)
|
|
Field_newdate(ptr,null_pos,null_bit,
|
|
unireg_check, field_name);
|
|
case MYSQL_TYPE_TIME:
|
|
{
|
|
uint dec= field_length > MIN_TIME_WIDTH ?
|
|
field_length - MIN_TIME_WIDTH - 1: 0;
|
|
return new_Field_time(mem_root, ptr, null_pos, null_bit, unireg_check,
|
|
field_name, dec);
|
|
}
|
|
case MYSQL_TYPE_TIME2:
|
|
{
|
|
uint dec= field_length > MIN_TIME_WIDTH ?
|
|
field_length - MIN_TIME_WIDTH - 1: 0;
|
|
return new (mem_root)
|
|
Field_timef(ptr, null_pos, null_bit, unireg_check,
|
|
field_name, dec);
|
|
}
|
|
case MYSQL_TYPE_DATETIME:
|
|
{
|
|
uint dec= field_length > MAX_DATETIME_WIDTH ?
|
|
field_length - MAX_DATETIME_WIDTH - 1: 0;
|
|
return new_Field_datetime(mem_root, ptr, null_pos, null_bit, unireg_check,
|
|
field_name, dec);
|
|
}
|
|
case MYSQL_TYPE_DATETIME2:
|
|
{
|
|
uint dec= field_length > MAX_DATETIME_WIDTH ?
|
|
field_length - MAX_DATETIME_WIDTH - 1: 0;
|
|
return new (mem_root)
|
|
Field_datetimef(ptr, null_pos, null_bit, unireg_check,
|
|
field_name, dec);
|
|
}
|
|
case MYSQL_TYPE_NULL:
|
|
return new (mem_root)
|
|
Field_null(ptr, field_length, unireg_check, field_name,
|
|
field_charset);
|
|
case MYSQL_TYPE_BIT:
|
|
return (f_bit_as_char(pack_flag) ?
|
|
new (mem_root)
|
|
Field_bit_as_char(ptr, field_length, null_pos, null_bit,
|
|
unireg_check, field_name) :
|
|
new (mem_root)
|
|
Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr,
|
|
bit_offset, unireg_check, field_name));
|
|
|
|
default: // Impossible (Wrong version)
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const
|
|
{
|
|
return item->type() == Item::DATE_ITEM;
|
|
}
|
|
|
|
|
|
/** Create a field suitable for create of table. */
|
|
|
|
Column_definition::Column_definition(THD *thd, Field *old_field,
|
|
Field *orig_field)
|
|
{
|
|
on_update= NULL;
|
|
field_name= old_field->field_name;
|
|
length= old_field->field_length;
|
|
flags= old_field->flags;
|
|
unireg_check=old_field->unireg_check;
|
|
pack_length=old_field->pack_length();
|
|
key_length= old_field->key_length();
|
|
set_handler(old_field->type_handler());
|
|
charset= old_field->charset(); // May be NULL ptr
|
|
comment= old_field->comment;
|
|
decimals= old_field->decimals();
|
|
vcol_info= old_field->vcol_info;
|
|
option_list= old_field->option_list;
|
|
pack_flag= 0;
|
|
compression_method_ptr= 0;
|
|
versioning= VERSIONING_NOT_SET;
|
|
invisible= old_field->invisible;
|
|
|
|
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;
|
|
}
|
|
|
|
switch (real_field_type()) {
|
|
case MYSQL_TYPE_TINY_BLOB:
|
|
case MYSQL_TYPE_BLOB:
|
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
case MYSQL_TYPE_LONG_BLOB:
|
|
length/= charset->mbmaxlen;
|
|
key_length/= charset->mbmaxlen;
|
|
break;
|
|
case MYSQL_TYPE_STRING:
|
|
/* Change CHAR -> VARCHAR if dynamic record length */
|
|
if (old_field->type() == MYSQL_TYPE_VAR_STRING)
|
|
set_handler(&type_handler_varchar);
|
|
/* fall through */
|
|
|
|
case MYSQL_TYPE_ENUM:
|
|
case MYSQL_TYPE_SET:
|
|
case MYSQL_TYPE_VARCHAR:
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
/* This is corrected in create_length_to_internal_length */
|
|
length= (length+charset->mbmaxlen-1) / charset->mbmaxlen -
|
|
MY_TEST(old_field->compression_method());
|
|
break;
|
|
#ifdef HAVE_SPATIAL
|
|
case MYSQL_TYPE_GEOMETRY:
|
|
geom_type= ((Field_geom*)old_field)->geom_type;
|
|
srid= ((Field_geom*)old_field)->srid;
|
|
break;
|
|
#endif
|
|
case MYSQL_TYPE_YEAR:
|
|
if (length != 4)
|
|
{
|
|
char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
|
|
my_snprintf(buff, sizeof(buff), "YEAR(%llu)", length);
|
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
|
|
ER_WARN_DEPRECATED_SYNTAX,
|
|
ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
|
|
buff, "YEAR(4)");
|
|
}
|
|
break;
|
|
case MYSQL_TYPE_FLOAT:
|
|
case MYSQL_TYPE_DOUBLE:
|
|
/*
|
|
Floating points are stored with FLOATING_POINT_DECIMALS but internally
|
|
in MariaDB used with NOT_FIXED_DEC, which is >= FLOATING_POINT_DECIMALS.
|
|
*/
|
|
if (decimals >= FLOATING_POINT_DECIMALS)
|
|
decimals= NOT_FIXED_DEC;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (flags & (ENUM_FLAG | SET_FLAG))
|
|
interval= ((Field_enum*) old_field)->typelib;
|
|
else
|
|
interval=0;
|
|
|
|
interval_list.empty(); // prepare_interval_field() needs this
|
|
|
|
char_length= (uint)length;
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
void
|
|
Column_definition::redefine_stage1_common(const Column_definition *dup_field,
|
|
const handler *file,
|
|
const Schema_specification_st *schema)
|
|
{
|
|
set_handler(dup_field->type_handler());
|
|
default_value= dup_field->default_value;
|
|
charset= dup_field->charset ? dup_field->charset :
|
|
schema->default_table_charset;
|
|
length= dup_field->char_length;
|
|
pack_length= dup_field->pack_length;
|
|
key_length= dup_field->key_length;
|
|
decimals= dup_field->decimals;
|
|
unireg_check= dup_field->unireg_check;
|
|
flags= dup_field->flags;
|
|
interval= dup_field->interval;
|
|
vcol_info= dup_field->vcol_info;
|
|
invisible= dup_field->invisible;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
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::octet_length();
|
|
}
|
|
|
|
|
|
uint32 Field_blob::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 explicitely
|
|
|
|
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)
|
|
{
|
|
enum enum_field_types sql_type= real_field_type();
|
|
/* We can't use f_is_blob here as pack_flag is not yet set */
|
|
if (sql_type == MYSQL_TYPE_VARCHAR || sql_type == MYSQL_TYPE_TINY_BLOB ||
|
|
sql_type == MYSQL_TYPE_BLOB || sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
|
|
sql_type == MYSQL_TYPE_LONG_BLOB)
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str);
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
maximum possible display length for blob.
|
|
|
|
@return
|
|
length
|
|
*/
|
|
|
|
uint32 Field_blob::max_display_length() const
|
|
{
|
|
switch (packlength)
|
|
{
|
|
case 1:
|
|
return 255 * field_charset->mbmaxlen;
|
|
case 2:
|
|
return 65535 * field_charset->mbmaxlen;
|
|
case 3:
|
|
return 16777215 * field_charset->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
|
|
|
|
@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) const
|
|
{
|
|
/*
|
|
If this field was created only for type conversion purposes it
|
|
will have table == NULL.
|
|
*/
|
|
THD *thd= get_thd();
|
|
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,
|
|
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,
|
|
timestamp_type ts_type, int cuted_increment)
|
|
const
|
|
{
|
|
THD *thd= get_thd();
|
|
if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN)
|
|
make_truncated_value_warning(thd, level, str, ts_type, 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();
|
|
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, field_name.str,
|
|
static_cast<ulong>(thd->get_stmt_da()->
|
|
current_row_for_warning()));
|
|
}
|
|
|
|
|
|
/*
|
|
@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);
|
|
}
|
|
|
|
|
|
/**
|
|
Mark the field as having an explicit default value.
|
|
|
|
@param value if available, the value that the field is being set to
|
|
|
|
@note
|
|
Fields that have an explicit default value should not be updated
|
|
automatically via the DEFAULT or ON UPDATE functions. The functions
|
|
that deal with data change functionality (INSERT/UPDATE/LOAD),
|
|
determine if there is an explicit value for each field before performing
|
|
the data change, and call this method to mark the field.
|
|
|
|
If the 'value' parameter is NULL, then the field is marked unconditionally
|
|
as having an explicit value. If 'value' is not NULL, then it can be further
|
|
analyzed to check if it really should count as a value.
|
|
*/
|
|
|
|
bool Field::set_explicit_default(Item *value)
|
|
{
|
|
if (value->type() == Item::DEFAULT_VALUE_ITEM &&
|
|
!((Item_default_value*)value)->arg)
|
|
return false;
|
|
set_has_explicit_value();
|
|
return true;
|
|
}
|
|
|
|
|
|
bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record)
|
|
{
|
|
my_bitmap_map *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;
|
|
}
|
|
|
|
|
|
bool Field::save_in_field_default_value(bool view_error_processing)
|
|
{
|
|
THD *thd= table->in_use;
|
|
|
|
if (unlikely(flags & NO_DEFAULT_VALUE_FLAG &&
|
|
real_type() != MYSQL_TYPE_ENUM))
|
|
{
|
|
if (reset())
|
|
{
|
|
my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
|
|
ER_THD(thd, ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
|
|
return true;
|
|
}
|
|
|
|
if (view_error_processing)
|
|
{
|
|
TABLE_LIST *view= table->pos_in_table_list->top_table();
|
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_NO_DEFAULT_FOR_VIEW_FIELD,
|
|
ER_THD(thd, ER_NO_DEFAULT_FOR_VIEW_FIELD),
|
|
view->view_db.str,
|
|
view->view_name.str);
|
|
}
|
|
else
|
|
{
|
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_NO_DEFAULT_FOR_FIELD,
|
|
ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD),
|
|
field_name.str);
|
|
}
|
|
return true;
|
|
}
|
|
set_default();
|
|
return
|
|
!is_null() &&
|
|
validate_value_in_record_with_warn(thd, table->record[0]) &&
|
|
thd->is_error();
|
|
}
|
|
|
|
|
|
bool Field::save_in_field_ignore_value(bool view_error_processing)
|
|
{
|
|
enum_sql_command com= table->in_use->lex->sql_command;
|
|
// All insert-like commands
|
|
if (com == SQLCOM_INSERT || com == SQLCOM_REPLACE ||
|
|
com == SQLCOM_INSERT_SELECT || com == SQLCOM_REPLACE_SELECT ||
|
|
com == SQLCOM_LOAD)
|
|
return save_in_field_default_value(view_error_processing);
|
|
return 0; // ignore
|
|
}
|
|
|
|
|
|
void Field::register_field_in_read_map()
|
|
{
|
|
if (vcol_info)
|
|
{
|
|
Item *vcol_item= vcol_info->expr;
|
|
vcol_item->walk(&Item::register_field_in_read_map, 1, 0);
|
|
}
|
|
bitmap_set_bit(table->read_set, field_index);
|
|
}
|
|
|
|
|
|
bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to)
|
|
{
|
|
StringBuffer<MAX_FIELD_WIDTH> str;
|
|
bool rc= false;
|
|
THD *thd= get_thd();
|
|
sql_mode_t sql_mode_backup= thd->variables.sql_mode;
|
|
thd->variables.sql_mode&= ~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;
|
|
|
|
thd->variables.sql_mode= sql_mode_backup;
|
|
return rc;
|
|
}
|