mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
9a4a7cf184
Convertion from a floating point number to a string caused a crash. During rare circumstances a String object could crash when it was requested to allocate new memory. A crash could occcur in Field_double::val_str() because of a pointer referencing memory inside a String object which was of unknown size. And finally, the geometric collection should not accept arguments which are non geometric. mysql-test/r/gis.result: * Test cases change because we intercept the error behind the previous crashes much earlier. sql/field.cc: * It makes no sense to impose a lower limit on the length and not setting a upper limit will cause crashes later. sql/item_geofunc.h: * Disallow for binding with field- and item types which differ from MYSQL_TYPE_GEOMETRY types.
10424 lines
289 KiB
C++
10424 lines
289 KiB
C++
/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved.
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
|
|
/**
|
|
@file
|
|
|
|
@brief
|
|
This file implements classes defined in field.h
|
|
*/
|
|
|
|
#ifdef USE_PRAGMA_IMPLEMENTATION
|
|
#pragma implementation // gcc: Class implementation
|
|
#endif
|
|
|
|
#include "mysql_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 <m_ctype.h>
|
|
#include <errno.h>
|
|
#ifdef HAVE_FCONVERT
|
|
#include <floatingpoint.h>
|
|
#endif
|
|
|
|
// Maximum allowed exponent value for converting string to decimal
|
|
#define MAX_EXPONENT 1024
|
|
|
|
/*****************************************************************************
|
|
Instansiate templates and static variables
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
|
|
template class List<Create_field>;
|
|
template class List_iterator<Create_field>;
|
|
#endif
|
|
|
|
uchar Field_null::null[1]={1};
|
|
const char field_separator=',';
|
|
|
|
#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE 320
|
|
#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) ((LL(1) << min(arg, 4) * 8) - LL(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 DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index)))
|
|
|
|
/*
|
|
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 thia array.
|
|
*/
|
|
#define FIELDTYPE_TEAR_FROM (MYSQL_TYPE_BIT + 1)
|
|
#define FIELDTYPE_TEAR_TO (MYSQL_TYPE_NEWDECIMAL - 1)
|
|
#define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO))
|
|
inline int field_type2index (enum_field_types field_type)
|
|
{
|
|
return (field_type < FIELDTYPE_TEAR_FROM ?
|
|
field_type :
|
|
((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1);
|
|
}
|
|
|
|
|
|
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_DECIMAL, MYSQL_TYPE_DECIMAL,
|
|
//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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_INT24,
|
|
//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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_FLOAT, 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_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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_INT24,
|
|
//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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_INT24,
|
|
//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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_LONG,
|
|
//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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* MYSQL_TYPE_YEAR -> */
|
|
{
|
|
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
|
|
MYSQL_TYPE_DECIMAL, 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_VARCHAR, 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
|
|
},
|
|
/* 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_GEOMETRY
|
|
MYSQL_TYPE_TINY_BLOB, 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_GEOMETRY
|
|
MYSQL_TYPE_MEDIUM_BLOB, 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_GEOMETRY
|
|
MYSQL_TYPE_LONG_BLOB, 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_GEOMETRY
|
|
MYSQL_TYPE_BLOB, 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_GEOMETRY
|
|
MYSQL_TYPE_VARCHAR, 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_STRING
|
|
},
|
|
/* MYSQL_TYPE_GEOMETRY -> */
|
|
{
|
|
//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_GEOMETRY, 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_GEOMETRY
|
|
MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY
|
|
}
|
|
};
|
|
|
|
/**
|
|
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)
|
|
{
|
|
DBUG_ASSERT(a < FIELDTYPE_TEAR_FROM || a > FIELDTYPE_TEAR_TO);
|
|
DBUG_ASSERT(b < FIELDTYPE_TEAR_FROM || b > FIELDTYPE_TEAR_TO);
|
|
return field_types_merge_rules[field_type2index(a)]
|
|
[field_type2index(b)];
|
|
}
|
|
|
|
|
|
static Item_result field_types_result_type [FIELDTYPE_NUM]=
|
|
{
|
|
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
|
|
DECIMAL_RESULT, INT_RESULT,
|
|
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
|
|
INT_RESULT, INT_RESULT,
|
|
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
|
|
REAL_RESULT, REAL_RESULT,
|
|
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
|
|
STRING_RESULT, STRING_RESULT,
|
|
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
|
|
INT_RESULT, INT_RESULT,
|
|
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
|
|
STRING_RESULT, STRING_RESULT,
|
|
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
|
|
STRING_RESULT, INT_RESULT,
|
|
//MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
|
|
STRING_RESULT, STRING_RESULT,
|
|
//MYSQL_TYPE_BIT <16>-<245>
|
|
STRING_RESULT,
|
|
//MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
|
|
DECIMAL_RESULT, STRING_RESULT,
|
|
//MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
|
|
STRING_RESULT, STRING_RESULT,
|
|
//MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
|
|
STRING_RESULT, STRING_RESULT,
|
|
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
|
|
STRING_RESULT, STRING_RESULT,
|
|
//MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
|
|
STRING_RESULT, STRING_RESULT
|
|
};
|
|
|
|
|
|
/*
|
|
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);
|
|
}
|
|
|
|
|
|
/**
|
|
Detect Item_result by given field type of UNION merge result.
|
|
|
|
@param field_type given field type
|
|
|
|
@return
|
|
Item_result (type of internal MySQL expression result)
|
|
*/
|
|
|
|
Item_result Field::result_merge_type(enum_field_types field_type)
|
|
{
|
|
DBUG_ASSERT(field_type < FIELDTYPE_TEAR_FROM || field_type
|
|
> FIELDTYPE_TEAR_TO);
|
|
return field_types_result_type[field_type2index(field_type)];
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Static help functions
|
|
*****************************************************************************/
|
|
|
|
|
|
/**
|
|
Check whether a field type can be partially indexed by a key.
|
|
|
|
This is a static method, rather than a virtual function, because we need
|
|
to check the type of a non-Field in mysql_alter_table().
|
|
|
|
@param type field type
|
|
|
|
@retval
|
|
TRUE Type can have a prefixed key
|
|
@retval
|
|
FALSE Type can not have a prefixed key
|
|
*/
|
|
|
|
bool Field::type_can_have_key_part(enum enum_field_types type)
|
|
{
|
|
switch (type) {
|
|
case MYSQL_TYPE_VARCHAR:
|
|
case MYSQL_TYPE_TINY_BLOB:
|
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
case MYSQL_TYPE_LONG_BLOB:
|
|
case MYSQL_TYPE_BLOB:
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING:
|
|
case MYSQL_TYPE_GEOMETRY:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
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 char *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)
|
|
{
|
|
int diff;
|
|
if ((diff= (int) (field_length - value->length())) > 0)
|
|
{
|
|
bmove_upp((uchar*) value->ptr()+field_length,
|
|
(uchar*) value->ptr()+value->length(),
|
|
value->length());
|
|
bfill((uchar*) value->ptr(),diff,'0');
|
|
value->length(field_length);
|
|
(void) value->c_ptr_quick(); // Avoid warnings in purify
|
|
}
|
|
}
|
|
|
|
/**
|
|
Test if given number is a int.
|
|
|
|
@todo
|
|
Make this multi-byte-character safe
|
|
|
|
@param str String to test
|
|
@param length Length of 'str'
|
|
@param int_end Pointer to char after last used digit
|
|
@param cs Character set
|
|
|
|
@note
|
|
This is called after one has called strntoull10rnd() function.
|
|
|
|
@retval
|
|
0 OK
|
|
@retval
|
|
1 error: empty string or wrong integer.
|
|
@retval
|
|
2 error: garbage at the end of string.
|
|
*/
|
|
|
|
int Field_num::check_int(CHARSET_INFO *cs, const char *str, int length,
|
|
const char *int_end, int error)
|
|
{
|
|
/* Test if we get an empty string or wrong integer */
|
|
if (str == int_end || error == MY_ERRNO_EDOM)
|
|
{
|
|
char buff[128];
|
|
String tmp(buff, (uint32) sizeof(buff), system_charset_info);
|
|
tmp.copy(str, length, system_charset_info);
|
|
push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
|
|
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
|
|
"integer", tmp.c_ptr(), field_name,
|
|
(ulong) table->in_use->row_count);
|
|
return 1;
|
|
}
|
|
/* Test if we have garbage at the end of the given string. */
|
|
if (test_if_important_data(cs, int_end, str + length))
|
|
{
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
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, uint 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 (table->in_use->count_cuted_fields &&
|
|
check_int(cs, from, len, end, error))
|
|
return 1;
|
|
return 0;
|
|
|
|
out_of_range:
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
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(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
return 1;
|
|
}
|
|
if (op_result == E_DEC_TRUNCATED)
|
|
{
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
|
|
/* We return 0 here as this is not a critical issue */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef NOT_USED
|
|
static bool test_if_real(const char *str,int length, CHARSET_INFO *cs)
|
|
{
|
|
cs= system_charset_info; // QQ move test_if_real into CHARSET_INFO struct
|
|
|
|
while (length && my_isspace(cs,*str))
|
|
{ // Allow start space
|
|
length--; str++;
|
|
}
|
|
if (!length)
|
|
return 0;
|
|
if (*str == '+' || *str == '-')
|
|
{
|
|
length--; str++;
|
|
if (!length || !(my_isdigit(cs,*str) || *str == '.'))
|
|
return 0;
|
|
}
|
|
while (length && my_isdigit(cs,*str))
|
|
{
|
|
length--; str++;
|
|
}
|
|
if (!length)
|
|
return 1;
|
|
if (*str == '.')
|
|
{
|
|
length--; str++;
|
|
while (length && my_isdigit(cs,*str))
|
|
{
|
|
length--; str++;
|
|
}
|
|
}
|
|
if (!length)
|
|
return 1;
|
|
if (*str == 'E' || *str == 'e')
|
|
{
|
|
if (length < 3 || (str[1] != '+' && str[1] != '-') ||
|
|
!my_isdigit(cs,str[2]))
|
|
return 0;
|
|
length-=3;
|
|
str+=3;
|
|
while (length && my_isdigit(cs,*str))
|
|
{
|
|
length--; str++;
|
|
}
|
|
}
|
|
for (; length ; length--, str++)
|
|
{ // Allow end space
|
|
if (!my_isspace(cs,*str))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
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, my_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 char *field_name_arg)
|
|
:ptr(ptr_arg), null_ptr(null_ptr_arg),
|
|
table(0), orig_table(0), table_name(0),
|
|
field_name(field_name_arg),
|
|
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)
|
|
{
|
|
flags=null_ptr ? 0: NOT_NULL_FLAG;
|
|
comment.str= (char*) "";
|
|
comment.length=0;
|
|
field_index= 0;
|
|
}
|
|
|
|
|
|
void Field::hash(ulong *nr, ulong *nr2)
|
|
{
|
|
if (is_null())
|
|
{
|
|
*nr^= (*nr << 1) | 1;
|
|
}
|
|
else
|
|
{
|
|
uint len= pack_length();
|
|
CHARSET_INFO *cs= 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).
|
|
|
|
@param field_metadata Encoded size in field metadata
|
|
@param mflags Flags from the table map event for the table.
|
|
|
|
@retval 0 if this field's size is < the source field's size
|
|
@retval 1 if this field's size is >= the source field's size
|
|
*/
|
|
int Field::compatible_field_size(uint field_metadata,
|
|
const Relay_log_info *rli_arg __attribute__((unused)),
|
|
uint16 mflags __attribute__((unused)))
|
|
{
|
|
uint const source_size= pack_length_from_metadata(field_metadata);
|
|
uint const destination_size= row_pack_length();
|
|
return (source_size <= destination_size);
|
|
}
|
|
|
|
|
|
int Field::store(const char *to, uint length, CHARSET_INFO *cs,
|
|
enum_check_fields check_level)
|
|
{
|
|
int res;
|
|
enum_check_fields old_check_level= table->in_use->count_cuted_fields;
|
|
table->in_use->count_cuted_fields= check_level;
|
|
res= store(to, length, cs);
|
|
table->in_use->count_cuted_fields= old_check_level;
|
|
return res;
|
|
}
|
|
|
|
|
|
/**
|
|
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.
|
|
|
|
The value of <code>low_byte_first</code> is dependent on how the
|
|
packed data is going to be used: for local use, e.g., temporary
|
|
store on disk or in memory, use the native format since that is
|
|
faster. For data that is going to be transfered to other machines
|
|
(e.g., when writing data to the binary log), data should always be
|
|
stored in little-endian format.
|
|
|
|
@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.
|
|
|
|
@param low_byte_first
|
|
@c TRUE if integers should be stored little-endian, @c FALSE if
|
|
native format should be used. Note that for little-endian machines,
|
|
the value of this flag is a moot point since the native format is
|
|
little-endian.
|
|
*/
|
|
uchar *
|
|
Field::pack(uchar *to, const uchar *from, uint max_length,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
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
|
|
|
|
@param low_byte_first
|
|
If this flag is @c true, all composite entities (e.g., lengths)
|
|
should be unpacked in little-endian format; otherwise, the entities
|
|
are unpacked in native order.
|
|
|
|
@return New pointer into memory based on from + length of the data
|
|
*/
|
|
const uchar *
|
|
Field::unpack(uchar* to, const uchar *from, uint param_data,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
uint length=pack_length();
|
|
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()))
|
|
{
|
|
memcpy(to, from, length);
|
|
return from+length;
|
|
}
|
|
|
|
uint len= (param_data && (param_data < length)) ?
|
|
param_data : length;
|
|
|
|
memcpy(to, from, param_data > length ? length : 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_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;
|
|
field->org_table_name= orig_table->s->table_name.str;
|
|
}
|
|
else
|
|
field->org_table_name= field->db_name= "";
|
|
if (orig_table && orig_table->alias)
|
|
{
|
|
field->table_name= orig_table->alias;
|
|
field->org_col_name= field_name;
|
|
}
|
|
else
|
|
{
|
|
field->table_name= "";
|
|
field->org_col_name= "";
|
|
}
|
|
field->col_name= field_name;
|
|
field->charsetnr= charset()->number;
|
|
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(MYSQL_ERROR::WARN_LEVEL_WARN, 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_num::store_decimal(const my_decimal *val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int err= 0;
|
|
longlong i= convert_decimal2longlong(val, unsigned_flag, &err);
|
|
return 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_num::val_decimal(my_decimal *decimal_value)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
DBUG_ASSERT(result_type() == INT_RESULT);
|
|
longlong nr= val_int();
|
|
int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value);
|
|
return decimal_value;
|
|
}
|
|
|
|
|
|
Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
|
|
uchar null_bit_arg, utype unireg_check_arg,
|
|
const char *field_name_arg, CHARSET_INFO *charset_arg)
|
|
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
|
|
unireg_check_arg, field_name_arg)
|
|
{
|
|
field_charset= charset_arg;
|
|
if (charset_arg->state & MY_CS_BINSORT)
|
|
flags|=BINARY_FLAG;
|
|
field_derivation= DERIVATION_IMPLICIT;
|
|
}
|
|
|
|
|
|
void Field_num::make_field(Send_field *field)
|
|
{
|
|
Field::make_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;
|
|
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();
|
|
copy->field= this;
|
|
if (flags & BLOB_FLAG)
|
|
{
|
|
copy->type= CACHE_BLOB;
|
|
copy->length-= table->s->blob_ptr_size;
|
|
return copy->length;
|
|
}
|
|
else if (!zero_pack() &&
|
|
(type() == MYSQL_TYPE_STRING && copy->length >= 4 &&
|
|
copy->length < 256))
|
|
{
|
|
copy->type= CACHE_STRIPPED;
|
|
store_length= 2;
|
|
}
|
|
else
|
|
{
|
|
copy->type= 0;
|
|
store_length= 0;
|
|
}
|
|
return copy->length + store_length;
|
|
}
|
|
|
|
|
|
bool Field::get_date(MYSQL_TIME *ltime,uint fuzzydate)
|
|
{
|
|
char buff[40];
|
|
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
|
|
if (!(res=val_str(&tmp)) ||
|
|
str_to_datetime_with_warn(res->ptr(), res->length(),
|
|
ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool Field::get_time(MYSQL_TIME *ltime)
|
|
{
|
|
char buff[40];
|
|
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
|
|
if (!(res=val_str(&tmp)) ||
|
|
str_to_time_with_warn(res->ptr(), res->length(), ltime))
|
|
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(MYSQL_TIME *ltime, timestamp_type type_arg)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
char buff[MAX_DATE_STRING_REP_LENGTH];
|
|
uint length= (uint) my_TIME_to_str(ltime, buff);
|
|
return store(buff, length, &my_charset_bin);
|
|
}
|
|
|
|
|
|
bool Field::optimize_range(uint idx, uint part)
|
|
{
|
|
return test(table->file->index_flags(idx, part, 1) & HA_READ_RANGE);
|
|
}
|
|
|
|
|
|
Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table,
|
|
bool keep_type __attribute__((unused)))
|
|
{
|
|
Field *tmp;
|
|
if (!(tmp= (Field*) memdup_root(root,(char*) this,size_of())))
|
|
return 0;
|
|
|
|
if (tmp->table->maybe_null)
|
|
tmp->flags&= ~NOT_NULL_FLAG;
|
|
tmp->table= new_table;
|
|
tmp->key_start.init(0);
|
|
tmp->part_of_key.init(0);
|
|
tmp->part_of_sortkey.init(0);
|
|
tmp->unireg_check= Field::NONE;
|
|
tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
|
|
ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
|
|
tmp->reset_fields();
|
|
return tmp;
|
|
}
|
|
|
|
|
|
Field *Field::new_key_field(MEM_ROOT *root, struct st_table *new_table,
|
|
uchar *new_ptr, uchar *new_null_ptr,
|
|
uint new_null_bit)
|
|
{
|
|
Field *tmp;
|
|
if ((tmp= 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, struct st_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_null, a field that always return NULL
|
|
****************************************************************************/
|
|
|
|
void Field_null::sql_type(String &res) const
|
|
{
|
|
res.set_ascii(STRING_WITH_LEN("null"));
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
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(MYSQL_ERROR::WARN_LEVEL_WARN, 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, uint len, CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
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 table->in_use->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(MYSQL_ERROR::WARN_LEVEL_WARN, 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 (table->in_use->count_cuted_fields)
|
|
{
|
|
// Skip end spaces
|
|
for (;from != end && my_isspace(&my_charset_bin, *from); from++) ;
|
|
if (from != end) // If still something left, warn
|
|
{
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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=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=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 table->in_use->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 (table->in_use->count_cuted_fields && !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(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
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_warning(MYSQL_ERROR::WARN_LEVEL_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;
|
|
if (unsigned_flag && nr < 0)
|
|
{
|
|
overflow(1);
|
|
return 1;
|
|
}
|
|
|
|
if (!isfinite(nr)) // Handle infinity as special case
|
|
{
|
|
overflow(nr < 0.0);
|
|
return 1;
|
|
}
|
|
|
|
reg4 uint i;
|
|
size_t length;
|
|
uchar fyllchar,*to;
|
|
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
|
|
|
|
fyllchar = zerofill ? (char) '0' : (char) ' ';
|
|
#ifdef HAVE_SNPRINTF
|
|
buff[sizeof(buff)-1]=0; // Safety
|
|
snprintf(buff,sizeof(buff)-1, "%.*f",(int) dec,nr);
|
|
length= strlen(buff);
|
|
#else
|
|
length= sprintf(buff, "%.*f", dec, nr);
|
|
#endif
|
|
|
|
if (length > field_length)
|
|
{
|
|
overflow(nr < 0.0);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
to=ptr;
|
|
for (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;
|
|
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_bin);
|
|
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)",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 char *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);
|
|
}
|
|
|
|
|
|
Field_new_decimal::Field_new_decimal(uint32 len_arg,
|
|
bool maybe_null_arg,
|
|
const char *name,
|
|
uint8 dec_arg,
|
|
bool unsigned_arg)
|
|
:Field_num((uchar*) 0, len_arg,
|
|
maybe_null_arg ? (uchar*) "": 0, 0,
|
|
NONE, name, dec_arg, 0, 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);
|
|
}
|
|
|
|
|
|
Field *Field_new_decimal::create_from_item (Item *item)
|
|
{
|
|
uint8 dec= item->decimals;
|
|
uint8 intg= item->decimal_precision() - dec;
|
|
uint32 len= item->max_length;
|
|
|
|
DBUG_ASSERT (item->result_type() == DECIMAL_RESULT);
|
|
|
|
/*
|
|
Trying to put too many digits overall in a DECIMAL(prec,dec)
|
|
will always throw a warning. We must limit dec to
|
|
DECIMAL_MAX_SCALE however to prevent an assert() later.
|
|
*/
|
|
|
|
if (dec > 0)
|
|
{
|
|
signed int overflow;
|
|
|
|
dec= min(dec, DECIMAL_MAX_SCALE);
|
|
|
|
/*
|
|
If the value still overflows the field with the corrected dec,
|
|
we'll throw out decimals rather than integers. This is still
|
|
bad and of course throws a truncation warning.
|
|
+1: for decimal point
|
|
*/
|
|
|
|
const int required_length=
|
|
my_decimal_precision_to_length(intg + dec, dec,
|
|
item->unsigned_flag);
|
|
|
|
overflow= required_length - len;
|
|
|
|
if (overflow > 0)
|
|
dec= max(0, dec - overflow); // too long, discard fract
|
|
else
|
|
/* Corrected value fits. */
|
|
len= required_length;
|
|
}
|
|
return new Field_new_decimal(len, item->maybe_null, item->name,
|
|
dec, item->unsigned_flag);
|
|
}
|
|
|
|
|
|
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
|
|
|
|
@retval
|
|
0 ok
|
|
@retval
|
|
1 error
|
|
*/
|
|
|
|
bool Field_new_decimal::store_value(const my_decimal *decimal_value)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= 0;
|
|
DBUG_ENTER("Field_new_decimal::store_value");
|
|
#ifndef DBUG_OFF
|
|
{
|
|
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
|
|
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(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
decimal_value= &decimal_zero;
|
|
}
|
|
#ifndef DBUG_OFF
|
|
{
|
|
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
|
|
DBUG_PRINT("info", ("saving with precision %d scale: %d value %s",
|
|
(int)precision, (int)dec,
|
|
dbug_decimal_as_string(dbug_buff, decimal_value)));
|
|
}
|
|
#endif
|
|
|
|
if (warn_if_overflow(my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
|
|
decimal_value, ptr, precision, dec)))
|
|
{
|
|
my_decimal buff;
|
|
DBUG_PRINT("info", ("overflow"));
|
|
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);
|
|
}
|
|
|
|
|
|
int Field_new_decimal::store(const char *from, uint length,
|
|
CHARSET_INFO *charset_arg)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int err;
|
|
my_decimal decimal_value;
|
|
DBUG_ENTER("Field_new_decimal::store(char*)");
|
|
|
|
if ((err= str2my_decimal(E_DEC_FATAL_ERROR &
|
|
~(E_DEC_OVERFLOW | E_DEC_BAD_NUM),
|
|
from, length, charset_arg,
|
|
&decimal_value)) &&
|
|
table->in_use->abort_on_warning)
|
|
{
|
|
/* Because "from" is not NUL-terminated and we use %s in the ER() */
|
|
String from_as_str;
|
|
from_as_str.copy(from, length, &my_charset_bin);
|
|
|
|
push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_ERROR,
|
|
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
|
|
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
|
|
"decimal", from_as_str.c_ptr(), field_name,
|
|
(ulong) table->in_use->row_count);
|
|
|
|
DBUG_RETURN(err);
|
|
}
|
|
|
|
switch (err) {
|
|
case E_DEC_TRUNCATED:
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
|
|
break;
|
|
case E_DEC_OVERFLOW:
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
set_value_on_overflow(&decimal_value, decimal_value.sign());
|
|
break;
|
|
case E_DEC_BAD_NUM:
|
|
{
|
|
/* Because "from" is not NUL-terminated and we use %s in the ER() */
|
|
String from_as_str;
|
|
from_as_str.copy(from, length, &my_charset_bin);
|
|
|
|
push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
|
|
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
|
|
"decimal", from_as_str.c_ptr(), field_name,
|
|
(ulong) table->in_use->row_count);
|
|
my_decimal_set_zero(&decimal_value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifndef DBUG_OFF
|
|
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
|
|
DBUG_PRINT("enter", ("value: %s",
|
|
dbug_decimal_as_string(dbug_buff, &decimal_value)));
|
|
#endif
|
|
store_value(&decimal_value);
|
|
DBUG_RETURN(err);
|
|
}
|
|
|
|
|
|
/**
|
|
@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;
|
|
my_decimal decimal_value;
|
|
int err;
|
|
DBUG_ENTER("Field_new_decimal::store(double)");
|
|
|
|
err= double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr,
|
|
&decimal_value);
|
|
/*
|
|
TODO: fix following when double2my_decimal when double2decimal
|
|
will return E_DEC_TRUNCATED always correctly
|
|
*/
|
|
if (!err)
|
|
{
|
|
double nr2;
|
|
my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &nr2);
|
|
if (nr2 != nr)
|
|
err= E_DEC_TRUNCATED;
|
|
}
|
|
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 */
|
|
table->in_use->got_warning= 0;
|
|
}
|
|
if (store_value(&decimal_value))
|
|
err= 1;
|
|
else if (err && !table->in_use->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;
|
|
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 */
|
|
table->in_use->got_warning= 0;
|
|
}
|
|
if (store_value(&decimal_value))
|
|
err= 1;
|
|
else if (err && !table->in_use->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;
|
|
return store_value(decimal_value);
|
|
}
|
|
|
|
|
|
int Field_new_decimal::store_time(MYSQL_TIME *ltime, timestamp_type t_type)
|
|
{
|
|
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);
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
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 length __attribute__((unused)))
|
|
{
|
|
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::do_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);
|
|
}
|
|
|
|
|
|
/**
|
|
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).
|
|
|
|
@param field_metadata Encoded size in field metadata
|
|
|
|
@retval 0 if this field's size is < the source field's size
|
|
@retval 1 if this field's size is >= the source field's size
|
|
*/
|
|
int Field_new_decimal::compatible_field_size(uint field_metadata,
|
|
const Relay_log_info * __attribute__((unused)),
|
|
uint16 mflags __attribute__((unused)))
|
|
{
|
|
int compatible= 0;
|
|
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);
|
|
uint const destination_size= row_pack_length();
|
|
compatible= (source_size <= destination_size);
|
|
if (compatible)
|
|
compatible= (source_precision <= precision) &&
|
|
(source_decimal <= decimals());
|
|
return (compatible);
|
|
}
|
|
|
|
|
|
uint Field_new_decimal::is_equal(Create_field *new_field)
|
|
{
|
|
return ((new_field->sql_type == real_type()) &&
|
|
((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,
|
|
uint param_data,
|
|
bool low_byte_first)
|
|
{
|
|
if (param_data == 0)
|
|
return Field::unpack(to, from, param_data, low_byte_first);
|
|
|
|
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
|
|
memcpy(to, from, len); // Sizes are the same, just copy the data.
|
|
return from+len;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** tiny int
|
|
****************************************************************************/
|
|
|
|
int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
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;
|
|
int error= 0;
|
|
nr=rint(nr);
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0.0)
|
|
{
|
|
*ptr=0;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > 255.0)
|
|
{
|
|
*ptr=(char) 255;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
*ptr=(char) nr;
|
|
}
|
|
else
|
|
{
|
|
if (nr < -128.0)
|
|
{
|
|
*ptr= (char) -128;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > 127.0)
|
|
{
|
|
*ptr=127;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
*ptr=(char) (int) nr;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_tiny::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= 0;
|
|
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0 && !unsigned_val)
|
|
{
|
|
*ptr= 0;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if ((ulonglong) nr > (ulonglong) 255)
|
|
{
|
|
*ptr= (char) 255;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > 127)
|
|
{
|
|
*ptr=127;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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;
|
|
CHARSET_INFO *cs= &my_charset_bin;
|
|
uint length;
|
|
uint mlength=max(field_length+1,5*cs->mbmaxlen);
|
|
val_buffer->alloc(mlength);
|
|
char *to=(char*) val_buffer->ptr();
|
|
|
|
if (unsigned_flag)
|
|
length= (uint) cs->cset->long10_to_str(cs,to,mlength, 10,
|
|
(long) *ptr);
|
|
else
|
|
length= (uint) cs->cset->long10_to_str(cs,to,mlength,-10,
|
|
(long) *((signed char*) ptr));
|
|
|
|
val_buffer->length(length);
|
|
if (zerofill)
|
|
prepend_zeros(val_buffer);
|
|
return val_buffer;
|
|
}
|
|
|
|
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,uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int2store(ptr, store_tmp);
|
|
}
|
|
else
|
|
#endif
|
|
shortstore(ptr, (short) store_tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_short::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= 0;
|
|
int16 res;
|
|
nr=rint(nr);
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0)
|
|
{
|
|
res=0;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > (double) UINT_MAX16)
|
|
{
|
|
res=(int16) UINT_MAX16;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > (double) INT_MAX16)
|
|
{
|
|
res=INT_MAX16;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
res=(int16) (int) nr;
|
|
}
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int2store(ptr,res);
|
|
}
|
|
else
|
|
#endif
|
|
shortstore(ptr,res);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_short::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= 0;
|
|
int16 res;
|
|
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0L && !unsigned_val)
|
|
{
|
|
res=0;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if ((ulonglong) nr > (ulonglong) UINT_MAX16)
|
|
{
|
|
res=(int16) UINT_MAX16;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > (longlong) INT_MAX16)
|
|
{
|
|
res=INT_MAX16;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
res=(int16) nr;
|
|
}
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int2store(ptr,res);
|
|
}
|
|
else
|
|
#endif
|
|
shortstore(ptr,res);
|
|
return error;
|
|
}
|
|
|
|
|
|
double Field_short::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
short j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
j=sint2korr(ptr);
|
|
else
|
|
#endif
|
|
shortget(j,ptr);
|
|
return unsigned_flag ? (double) (unsigned short) j : (double) j;
|
|
}
|
|
|
|
longlong Field_short::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
short j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
j=sint2korr(ptr);
|
|
else
|
|
#endif
|
|
shortget(j,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;
|
|
CHARSET_INFO *cs= &my_charset_bin;
|
|
uint length;
|
|
uint mlength=max(field_length+1,7*cs->mbmaxlen);
|
|
val_buffer->alloc(mlength);
|
|
char *to=(char*) val_buffer->ptr();
|
|
short j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
j=sint2korr(ptr);
|
|
else
|
|
#endif
|
|
shortget(j,ptr);
|
|
|
|
if (unsigned_flag)
|
|
length=(uint) cs->cset->long10_to_str(cs, to, mlength, 10,
|
|
(long) (uint16) j);
|
|
else
|
|
length=(uint) cs->cset->long10_to_str(cs, to, mlength,-10, (long) j);
|
|
val_buffer->length(length);
|
|
if (zerofill)
|
|
prepend_zeros(val_buffer);
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
a=sint2korr(a_ptr);
|
|
b=sint2korr(b_ptr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
shortget(a,a_ptr);
|
|
shortget(b,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)))
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (!table->s->db_low_byte_first)
|
|
{
|
|
if (unsigned_flag)
|
|
to[0] = ptr[0];
|
|
else
|
|
to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */
|
|
to[1] = ptr[1];
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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,uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
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;
|
|
int error= 0;
|
|
nr=rint(nr);
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0)
|
|
{
|
|
int3store(ptr,0);
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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(MYSQL_ERROR::WARN_LEVEL_WARN, 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(MYSQL_ERROR::WARN_LEVEL_WARN, 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(MYSQL_ERROR::WARN_LEVEL_WARN, 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;
|
|
int error= 0;
|
|
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0 && !unsigned_val)
|
|
{
|
|
int3store(ptr,0);
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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(MYSQL_ERROR::WARN_LEVEL_WARN, 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(MYSQL_ERROR::WARN_LEVEL_WARN, 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(MYSQL_ERROR::WARN_LEVEL_WARN, 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;
|
|
CHARSET_INFO *cs= &my_charset_bin;
|
|
uint length;
|
|
uint mlength=max(field_length+1,10*cs->mbmaxlen);
|
|
val_buffer->alloc(mlength);
|
|
char *to=(char*) val_buffer->ptr();
|
|
long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
|
|
|
|
length=(uint) cs->cset->long10_to_str(cs,to,mlength,-10,j);
|
|
val_buffer->length(length);
|
|
if (zerofill)
|
|
prepend_zeros(val_buffer); /* purecov: inspected */
|
|
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,uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int4store(ptr, store_tmp);
|
|
}
|
|
else
|
|
#endif
|
|
longstore(ptr, store_tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_long::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
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(MYSQL_ERROR::WARN_LEVEL_WARN, 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 (error)
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int4store(ptr,res);
|
|
}
|
|
else
|
|
#endif
|
|
longstore(ptr,res);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_long::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= 0;
|
|
int32 res;
|
|
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0 && !unsigned_val)
|
|
{
|
|
res=0;
|
|
error= 1;
|
|
}
|
|
else if ((ulonglong) nr >= (LL(1) << 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 (error)
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int4store(ptr,res);
|
|
}
|
|
else
|
|
#endif
|
|
longstore(ptr,res);
|
|
return error;
|
|
}
|
|
|
|
|
|
double Field_long::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int32 j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
j=sint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(j,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->in_use == current_thd);
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
j=sint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(j,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;
|
|
CHARSET_INFO *cs= &my_charset_bin;
|
|
uint length;
|
|
uint mlength=max(field_length+1,12*cs->mbmaxlen);
|
|
val_buffer->alloc(mlength);
|
|
char *to=(char*) val_buffer->ptr();
|
|
int32 j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
j=sint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(j,ptr);
|
|
|
|
if (unsigned_flag)
|
|
length=cs->cset->long10_to_str(cs,to,mlength, 10,(long) (uint32)j);
|
|
else
|
|
length=cs->cset->long10_to_str(cs,to,mlength,-10,(long) j);
|
|
val_buffer->length(length);
|
|
if (zerofill)
|
|
prepend_zeros(val_buffer);
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
a=sint4korr(a_ptr);
|
|
b=sint4korr(b_ptr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
longget(a,a_ptr);
|
|
longget(b,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)))
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (!table->s->db_low_byte_first)
|
|
{
|
|
if (unsigned_flag)
|
|
to[0] = ptr[0];
|
|
else
|
|
to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */
|
|
to[1] = ptr[1];
|
|
to[2] = ptr[2];
|
|
to[3] = ptr[3];
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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,uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= 0;
|
|
char *end;
|
|
ulonglong tmp;
|
|
|
|
tmp= cs->cset->strntoull10rnd(cs,from,len,unsigned_flag,&end,&error);
|
|
if (error == MY_ERRNO_ERANGE)
|
|
{
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
else if (table->in_use->count_cuted_fields &&
|
|
check_int(cs, from, len, end, error))
|
|
error= 1;
|
|
else
|
|
error= 0;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int8store(ptr,tmp);
|
|
}
|
|
else
|
|
#endif
|
|
longlongstore(ptr,tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_longlong::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= 0;
|
|
longlong res;
|
|
|
|
nr= rint(nr);
|
|
if (unsigned_flag)
|
|
{
|
|
if (nr < 0)
|
|
{
|
|
res=0;
|
|
error= 1;
|
|
}
|
|
else if (nr >= (double) ULONGLONG_MAX)
|
|
{
|
|
res= ~(longlong) 0;
|
|
error= 1;
|
|
}
|
|
else
|
|
res=(longlong) double2ulonglong(nr);
|
|
}
|
|
else
|
|
{
|
|
if (nr <= (double) LONGLONG_MIN)
|
|
{
|
|
res= LONGLONG_MIN;
|
|
error= (nr < (double) LONGLONG_MIN);
|
|
}
|
|
else if (nr >= (double) (ulonglong) LONGLONG_MAX)
|
|
{
|
|
res= LONGLONG_MAX;
|
|
error= (nr > (double) LONGLONG_MAX);
|
|
}
|
|
else
|
|
res=(longlong) nr;
|
|
}
|
|
if (error)
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int8store(ptr,res);
|
|
}
|
|
else
|
|
#endif
|
|
longlongstore(ptr,res);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_longlong::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= 0;
|
|
|
|
if (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(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
error= 1;
|
|
}
|
|
}
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int8store(ptr,nr);
|
|
}
|
|
else
|
|
#endif
|
|
longlongstore(ptr,nr);
|
|
return error;
|
|
}
|
|
|
|
|
|
double Field_longlong::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
longlong j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
j=sint8korr(ptr);
|
|
}
|
|
else
|
|
#endif
|
|
longlongget(j,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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
j=sint8korr(ptr);
|
|
else
|
|
#endif
|
|
longlongget(j,ptr);
|
|
return j;
|
|
}
|
|
|
|
|
|
String *Field_longlong::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
CHARSET_INFO *cs= &my_charset_bin;
|
|
uint length;
|
|
uint mlength=max(field_length+1,22*cs->mbmaxlen);
|
|
val_buffer->alloc(mlength);
|
|
char *to=(char*) val_buffer->ptr();
|
|
longlong j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
j=sint8korr(ptr);
|
|
else
|
|
#endif
|
|
longlongget(j,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);
|
|
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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
a=sint8korr(a_ptr);
|
|
b=sint8korr(b_ptr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
longlongget(a,a_ptr);
|
|
longlongget(b,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)))
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (!table->s->db_low_byte_first)
|
|
{
|
|
if (unsigned_flag)
|
|
to[0] = ptr[0];
|
|
else
|
|
to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */
|
|
to[1] = ptr[1];
|
|
to[2] = ptr[2];
|
|
to[3] = ptr[3];
|
|
to[4] = ptr[4];
|
|
to[5] = ptr[5];
|
|
to[6] = ptr[6];
|
|
to[7] = ptr[7];
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
/*
|
|
Floating-point numbers
|
|
*/
|
|
|
|
uchar *
|
|
Field_real::pack(uchar *to, const uchar *from,
|
|
uint max_length, bool low_byte_first)
|
|
{
|
|
DBUG_ENTER("Field_real::pack");
|
|
DBUG_ASSERT(max_length >= pack_length());
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (low_byte_first != table->s->db_low_byte_first)
|
|
{
|
|
const uchar *dptr= from + pack_length();
|
|
while (dptr-- > from)
|
|
*to++ = *dptr;
|
|
DBUG_RETURN(to);
|
|
}
|
|
else
|
|
#endif
|
|
DBUG_RETURN(Field::pack(to, from, max_length, low_byte_first));
|
|
}
|
|
|
|
const uchar *
|
|
Field_real::unpack(uchar *to, const uchar *from,
|
|
uint param_data, bool low_byte_first)
|
|
{
|
|
DBUG_ENTER("Field_real::unpack");
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (low_byte_first != table->s->db_low_byte_first)
|
|
{
|
|
const uchar *dptr= from + pack_length();
|
|
while (dptr-- > from)
|
|
*to++ = *dptr;
|
|
DBUG_RETURN(from + pack_length());
|
|
}
|
|
else
|
|
#endif
|
|
DBUG_RETURN(Field::unpack(to, from, param_data, low_byte_first));
|
|
}
|
|
|
|
/****************************************************************************
|
|
single precision float
|
|
****************************************************************************/
|
|
|
|
int Field_float::store(const char *from,uint len,CHARSET_INFO *cs)
|
|
{
|
|
int error;
|
|
char *end;
|
|
double nr= my_strntod(cs,(char*) from,len,&end,&error);
|
|
if (error || (!len || ((uint) (end-from) != len &&
|
|
table->in_use->count_cuted_fields)))
|
|
{
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
(error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1);
|
|
error= error ? 1 : 2;
|
|
}
|
|
Field_float::store(nr);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_float::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= truncate(&nr, FLT_MAX);
|
|
float j= (float)nr;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float4store(ptr,j);
|
|
}
|
|
else
|
|
#endif
|
|
memcpy_fixed(ptr,(uchar*) &j,sizeof(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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float4get(j,ptr);
|
|
}
|
|
else
|
|
#endif
|
|
memcpy_fixed((uchar*) &j,ptr,sizeof(j));
|
|
return ((double) j);
|
|
}
|
|
|
|
longlong Field_float::val_int(void)
|
|
{
|
|
float j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float4get(j,ptr);
|
|
}
|
|
else
|
|
#endif
|
|
memcpy_fixed((uchar*) &j,ptr,sizeof(j));
|
|
return (longlong) rint(j);
|
|
}
|
|
|
|
|
|
String *Field_float::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
float nr;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float4get(nr,ptr);
|
|
}
|
|
else
|
|
#endif
|
|
memcpy_fixed((uchar*) &nr,ptr,sizeof(nr));
|
|
|
|
uint to_length=max(field_length,70);
|
|
val_buffer->alloc(to_length);
|
|
char *to=(char*) val_buffer->ptr();
|
|
|
|
if (dec >= NOT_FIXED_DEC)
|
|
{
|
|
sprintf(to,"%-*.*g",(int) field_length,FLT_DIG,nr);
|
|
to=strcend(to,' ');
|
|
*to=0;
|
|
}
|
|
else
|
|
{
|
|
#ifdef HAVE_FCONVERT
|
|
char buff[70],*pos=buff;
|
|
int decpt,sign,tmp_dec=dec;
|
|
|
|
VOID(sfconvert(&nr,tmp_dec,&decpt,&sign,buff));
|
|
if (sign)
|
|
{
|
|
*to++='-';
|
|
}
|
|
if (decpt < 0)
|
|
{ /* val_buffer is < 0 */
|
|
*to++='0';
|
|
if (!tmp_dec)
|
|
goto end;
|
|
*to++='.';
|
|
if (-decpt > tmp_dec)
|
|
decpt= - (int) tmp_dec;
|
|
tmp_dec=(uint) ((int) tmp_dec+decpt);
|
|
while (decpt++ < 0)
|
|
*to++='0';
|
|
}
|
|
else if (decpt == 0)
|
|
{
|
|
*to++= '0';
|
|
if (!tmp_dec)
|
|
goto end;
|
|
*to++='.';
|
|
}
|
|
else
|
|
{
|
|
while (decpt-- > 0)
|
|
*to++= *pos++;
|
|
if (!tmp_dec)
|
|
goto end;
|
|
*to++='.';
|
|
}
|
|
while (tmp_dec--)
|
|
*to++= *pos++;
|
|
#else
|
|
#ifdef HAVE_SNPRINTF
|
|
to[to_length-1]=0; // Safety
|
|
snprintf(to,to_length-1,"%.*f",dec,nr);
|
|
to=strend(to);
|
|
#else
|
|
to+= sprintf(to, "%.*f", dec, nr);
|
|
#endif
|
|
#endif
|
|
}
|
|
#ifdef HAVE_FCONVERT
|
|
end:
|
|
#endif
|
|
val_buffer->length((uint) (to-val_buffer->ptr()));
|
|
if (zerofill)
|
|
prepend_zeros(val_buffer);
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
float a,b;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float4get(a,a_ptr);
|
|
float4get(b,b_ptr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
memcpy_fixed(&a,a_ptr,sizeof(float));
|
|
memcpy_fixed(&b,b_ptr,sizeof(float));
|
|
}
|
|
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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float4get(nr,ptr);
|
|
}
|
|
else
|
|
#endif
|
|
memcpy_fixed(&nr,ptr,sizeof(float));
|
|
|
|
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_fixed(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::do_save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
*metadata_ptr= pack_length();
|
|
return 1;
|
|
}
|
|
|
|
|
|
void Field_float::sql_type(String &res) const
|
|
{
|
|
if (dec == NOT_FIXED_DEC)
|
|
{
|
|
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,uint len,CHARSET_INFO *cs)
|
|
{
|
|
int error;
|
|
char *end;
|
|
double nr= my_strntod(cs,(char*) from, len, &end, &error);
|
|
if (error || (!len || ((uint) (end-from) != len &&
|
|
table->in_use->count_cuted_fields)))
|
|
{
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
(error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1);
|
|
error= error ? 1 : 2;
|
|
}
|
|
Field_double::store(nr);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_double::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= truncate(&nr, DBL_MAX);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float8store(ptr,nr);
|
|
}
|
|
else
|
|
#endif
|
|
doublestore(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.
|
|
*/
|
|
|
|
int Field_real::truncate(double *nr, double max_value)
|
|
{
|
|
int error= 1;
|
|
double res= *nr;
|
|
|
|
if (isnan(res))
|
|
{
|
|
res= 0;
|
|
set_null();
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
goto end;
|
|
}
|
|
else if (unsigned_flag && res < 0)
|
|
{
|
|
res= 0;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
goto end;
|
|
}
|
|
|
|
if (!not_fixed)
|
|
{
|
|
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];
|
|
|
|
double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec];
|
|
res= floor(res) + tmp;
|
|
}
|
|
|
|
if (res < -max_value)
|
|
{
|
|
res= -max_value;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
}
|
|
else if (res > max_value)
|
|
{
|
|
res= max_value;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
}
|
|
else
|
|
error= 0;
|
|
|
|
end:
|
|
*nr= res;
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_real::store_decimal(const my_decimal *dm)
|
|
{
|
|
double dbl;
|
|
my_decimal2double(E_DEC_FATAL_ERROR, dm, &dbl);
|
|
return store(dbl);
|
|
}
|
|
|
|
double Field_double::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
double j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float8get(j,ptr);
|
|
}
|
|
else
|
|
#endif
|
|
doubleget(j,ptr);
|
|
return j;
|
|
}
|
|
|
|
longlong Field_double::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
double j;
|
|
longlong res;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float8get(j,ptr);
|
|
}
|
|
else
|
|
#endif
|
|
doubleget(j,ptr);
|
|
/* Check whether we fit into longlong range */
|
|
if (j <= (double) LONGLONG_MIN)
|
|
{
|
|
res= (longlong) LONGLONG_MIN;
|
|
goto warn;
|
|
}
|
|
if (j >= (double) (ulonglong) LONGLONG_MAX)
|
|
{
|
|
res= (longlong) LONGLONG_MAX;
|
|
goto warn;
|
|
}
|
|
return (longlong) rint(j);
|
|
|
|
warn:
|
|
{
|
|
char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
|
|
String tmp(buf, sizeof(buf), &my_charset_latin1), *str;
|
|
str= val_str(&tmp, 0);
|
|
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_TRUNCATED_WRONG_VALUE,
|
|
ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
|
|
str->c_ptr());
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
String *Field_double::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
double nr;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float8get(nr,ptr);
|
|
}
|
|
else
|
|
#endif
|
|
doubleget(nr,ptr);
|
|
|
|
uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE;
|
|
val_buffer->alloc(to_length);
|
|
char *to=(char*) val_buffer->ptr();
|
|
|
|
if (dec >= NOT_FIXED_DEC)
|
|
{
|
|
sprintf(to,"%-*.*g",(int) field_length,DBL_DIG,nr);
|
|
to=strcend(to,' ');
|
|
}
|
|
else
|
|
{
|
|
#ifdef HAVE_FCONVERT
|
|
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
|
|
char *pos= buff;
|
|
int decpt,sign,tmp_dec=dec;
|
|
|
|
VOID(fconvert(nr,tmp_dec,&decpt,&sign,buff));
|
|
if (sign)
|
|
{
|
|
*to++='-';
|
|
}
|
|
if (decpt < 0)
|
|
{ /* val_buffer is < 0 */
|
|
*to++='0';
|
|
if (!tmp_dec)
|
|
goto end;
|
|
*to++='.';
|
|
if (-decpt > tmp_dec)
|
|
decpt= - (int) tmp_dec;
|
|
tmp_dec=(uint) ((int) tmp_dec+decpt);
|
|
while (decpt++ < 0)
|
|
*to++='0';
|
|
}
|
|
else if (decpt == 0)
|
|
{
|
|
*to++= '0';
|
|
if (!tmp_dec)
|
|
goto end;
|
|
*to++='.';
|
|
}
|
|
else
|
|
{
|
|
while (decpt-- > 0)
|
|
*to++= *pos++;
|
|
if (!tmp_dec)
|
|
goto end;
|
|
*to++='.';
|
|
}
|
|
while (tmp_dec--)
|
|
*to++= *pos++;
|
|
#else
|
|
#ifdef HAVE_SNPRINTF
|
|
to[to_length-1]=0; // Safety
|
|
snprintf(to,to_length-1,"%.*f",dec,nr);
|
|
to=strend(to);
|
|
#else
|
|
to+= sprintf(to, "%.*f", dec, nr);
|
|
#endif
|
|
#endif
|
|
}
|
|
#ifdef HAVE_FCONVERT
|
|
end:
|
|
#endif
|
|
|
|
val_buffer->length((uint) (to-val_buffer->ptr()));
|
|
if (zerofill)
|
|
prepend_zeros(val_buffer);
|
|
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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float8get(a,a_ptr);
|
|
float8get(b,b_ptr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
doubleget(a, a_ptr);
|
|
doubleget(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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
float8get(nr,ptr);
|
|
}
|
|
else
|
|
#endif
|
|
doubleget(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::do_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 == NOT_FIXED_DEC)
|
|
{
|
|
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.
|
|
|
|
Up to one of timestamps columns in the table can be automatically
|
|
set on row update and/or have NOW() as default value.
|
|
TABLE::timestamp_field points to Field object for such timestamp with
|
|
auto-set-on-update. TABLE::time_stamp holds offset in record + 1 for this
|
|
field, and is used by handler code which performs updates required.
|
|
|
|
Actually SQL-99 says that we should allow niladic functions (like NOW())
|
|
as defaults for any field. Current limitations (only NOW() and only
|
|
for one TIMESTAMP field) 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())
|
|
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())
|
|
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 char *field_name_arg,
|
|
TABLE_SHARE *share,
|
|
CHARSET_INFO *cs)
|
|
:Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg,
|
|
unireg_check_arg, field_name_arg, cs)
|
|
{
|
|
/* For 4.0 MYD and 4.0 InnoDB compatibility */
|
|
flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
|
|
if (!share->timestamp_field && unireg_check != NONE)
|
|
{
|
|
/* This timestamp has auto-update */
|
|
share->timestamp_field= this;
|
|
flags|= TIMESTAMP_FLAG;
|
|
if (unireg_check != TIMESTAMP_DN_FIELD)
|
|
flags|= ON_UPDATE_NOW_FLAG;
|
|
}
|
|
}
|
|
|
|
|
|
Field_timestamp::Field_timestamp(bool maybe_null_arg,
|
|
const char *field_name_arg,
|
|
CHARSET_INFO *cs)
|
|
:Field_str((uchar*) 0, MAX_DATETIME_WIDTH,
|
|
maybe_null_arg ? (uchar*) "": 0, 0,
|
|
NONE, field_name_arg, cs)
|
|
{
|
|
/* For 4.0 MYD and 4.0 InnoDB compatibility */
|
|
flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
|
|
if (unireg_check != TIMESTAMP_DN_FIELD)
|
|
flags|= ON_UPDATE_NOW_FLAG;
|
|
}
|
|
|
|
|
|
/**
|
|
Get auto-set type for TIMESTAMP field.
|
|
|
|
Returns value indicating during which operations this TIMESTAMP field
|
|
should be auto-set to current timestamp.
|
|
*/
|
|
timestamp_auto_set_type Field_timestamp::get_auto_set_type() const
|
|
{
|
|
switch (unireg_check)
|
|
{
|
|
case TIMESTAMP_DN_FIELD:
|
|
return TIMESTAMP_AUTO_SET_ON_INSERT;
|
|
case TIMESTAMP_UN_FIELD:
|
|
return TIMESTAMP_AUTO_SET_ON_UPDATE;
|
|
case TIMESTAMP_OLD_FIELD:
|
|
/*
|
|
Although we can have several such columns in legacy tables this
|
|
function should be called only for first of them (i.e. the one
|
|
having auto-set property).
|
|
*/
|
|
DBUG_ASSERT(table->timestamp_field == this);
|
|
/* Fall-through */
|
|
case TIMESTAMP_DNUN_FIELD:
|
|
return TIMESTAMP_AUTO_SET_ON_BOTH;
|
|
default:
|
|
/*
|
|
Normally this function should not be called for TIMESTAMPs without
|
|
auto-set property.
|
|
*/
|
|
DBUG_ASSERT(0);
|
|
return TIMESTAMP_NO_AUTO_SET;
|
|
}
|
|
}
|
|
|
|
|
|
int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
MYSQL_TIME l_time;
|
|
my_time_t tmp= 0;
|
|
int error;
|
|
bool have_smth_to_conv;
|
|
my_bool in_dst_time_gap;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
|
|
/* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
|
|
have_smth_to_conv= (str_to_datetime(from, len, &l_time,
|
|
(thd->variables.sql_mode &
|
|
MODE_NO_ZERO_DATE) |
|
|
MODE_NO_ZERO_IN_DATE, &error) >
|
|
MYSQL_TIMESTAMP_ERROR);
|
|
|
|
if (error || !have_smth_to_conv)
|
|
{
|
|
error= 1;
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
|
|
from, len, MYSQL_TIMESTAMP_DATETIME, 1);
|
|
}
|
|
|
|
/* Only convert a correct date (not a zero date) */
|
|
if (have_smth_to_conv && l_time.month)
|
|
{
|
|
if (!(tmp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
|
|
{
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE,
|
|
from, len, MYSQL_TIMESTAMP_DATETIME, !error);
|
|
error= 1;
|
|
}
|
|
else if (in_dst_time_gap)
|
|
{
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_INVALID_TIMESTAMP,
|
|
from, len, MYSQL_TIMESTAMP_DATETIME, !error);
|
|
error= 1;
|
|
}
|
|
}
|
|
store_timestamp(tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_timestamp::store(double nr)
|
|
{
|
|
int error= 0;
|
|
if (nr < 0 || nr > 99991231235959.0)
|
|
{
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE,
|
|
nr, MYSQL_TIMESTAMP_DATETIME);
|
|
nr= 0; // Avoid overflow on buff
|
|
error= 1;
|
|
}
|
|
error|= Field_timestamp::store((longlong) rint(nr), FALSE);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_timestamp::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
MYSQL_TIME l_time;
|
|
my_time_t timestamp= 0;
|
|
int error;
|
|
my_bool in_dst_time_gap;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
|
|
/* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
|
|
longlong tmp= number_to_datetime(nr, &l_time, (thd->variables.sql_mode &
|
|
MODE_NO_ZERO_DATE) |
|
|
MODE_NO_ZERO_IN_DATE, &error);
|
|
if (tmp == LL(-1))
|
|
{
|
|
error= 2;
|
|
}
|
|
|
|
if (!error && tmp)
|
|
{
|
|
if (!(timestamp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
|
|
{
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE,
|
|
nr, MYSQL_TIMESTAMP_DATETIME, 1);
|
|
error= 1;
|
|
}
|
|
if (in_dst_time_gap)
|
|
{
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_INVALID_TIMESTAMP,
|
|
nr, MYSQL_TIMESTAMP_DATETIME, 1);
|
|
error= 1;
|
|
}
|
|
} else if (error)
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
WARN_DATA_TRUNCATED,
|
|
nr, MYSQL_TIMESTAMP_DATETIME, 1);
|
|
|
|
store_timestamp(timestamp);
|
|
return error;
|
|
}
|
|
|
|
double Field_timestamp::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
return (double) Field_timestamp::val_int();
|
|
}
|
|
|
|
longlong Field_timestamp::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
uint32 temp;
|
|
MYSQL_TIME time_tmp;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
|
|
thd->time_zone_used= 1;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
temp=uint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(temp,ptr);
|
|
|
|
if (temp == 0L) // No time
|
|
return(0); /* purecov: inspected */
|
|
|
|
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp);
|
|
|
|
return time_tmp.year * LL(10000000000) + time_tmp.month * LL(100000000) +
|
|
time_tmp.day * 1000000L + time_tmp.hour * 10000L +
|
|
time_tmp.minute * 100 + time_tmp.second;
|
|
}
|
|
|
|
|
|
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
uint32 temp, temp2;
|
|
MYSQL_TIME time_tmp;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
char *to;
|
|
|
|
val_buffer->alloc(field_length+1);
|
|
to= (char*) val_buffer->ptr();
|
|
val_buffer->length(field_length);
|
|
|
|
thd->time_zone_used= 1;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
temp=uint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(temp,ptr);
|
|
|
|
if (temp == 0L)
|
|
{ /* Zero time is "000000" */
|
|
val_ptr->set(STRING_WITH_LEN("0000-00-00 00:00:00"), &my_charset_bin);
|
|
return val_ptr;
|
|
}
|
|
val_buffer->set_charset(&my_charset_bin); // Safety
|
|
|
|
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp);
|
|
|
|
temp= time_tmp.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=time_tmp.month;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to++= '-';
|
|
temp=time_tmp.day;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to++= ' ';
|
|
temp=time_tmp.hour;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to++= ':';
|
|
temp=time_tmp.minute;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to++= ':';
|
|
temp=time_tmp.second;
|
|
temp2=temp/10; temp=temp-temp2*10;
|
|
*to++= (char) ('0'+(char) (temp2));
|
|
*to++= (char) ('0'+(char) (temp));
|
|
*to= 0;
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
bool Field_timestamp::get_date(MYSQL_TIME *ltime, uint fuzzydate)
|
|
{
|
|
long temp;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
thd->time_zone_used= 1;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
temp=uint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(temp,ptr);
|
|
if (temp == 0L)
|
|
{ /* Zero time is "000000" */
|
|
if (fuzzydate & TIME_NO_ZERO_DATE)
|
|
return 1;
|
|
bzero((char*) ltime,sizeof(*ltime));
|
|
}
|
|
else
|
|
{
|
|
thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool Field_timestamp::get_time(MYSQL_TIME *ltime)
|
|
{
|
|
return Field_timestamp::get_date(ltime,0);
|
|
}
|
|
|
|
|
|
bool Field_timestamp::send_binary(Protocol *protocol)
|
|
{
|
|
MYSQL_TIME tm;
|
|
Field_timestamp::get_date(&tm, 0);
|
|
return protocol->store(&tm);
|
|
}
|
|
|
|
|
|
int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
int32 a,b;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
{
|
|
a=sint4korr(a_ptr);
|
|
b=sint4korr(b_ptr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
longget(a,a_ptr);
|
|
longget(b,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)))
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (!table || !table->s->db_low_byte_first)
|
|
{
|
|
to[0] = ptr[0];
|
|
to[1] = ptr[1];
|
|
to[2] = ptr[2];
|
|
to[3] = ptr[3];
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
to[0] = ptr[3];
|
|
to[1] = ptr[2];
|
|
to[2] = ptr[1];
|
|
to[3] = ptr[0];
|
|
}
|
|
}
|
|
|
|
|
|
void Field_timestamp::sql_type(String &res) const
|
|
{
|
|
res.set_ascii(STRING_WITH_LEN("timestamp"));
|
|
}
|
|
|
|
|
|
void Field_timestamp::set_time()
|
|
{
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
long tmp= (long) thd->query_start();
|
|
set_notnull();
|
|
store_timestamp(tmp);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** time type
|
|
** In string context: HH:MM:SS
|
|
** In number context: HHMMSS
|
|
** Stored as a 3 byte unsigned int
|
|
****************************************************************************/
|
|
|
|
int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
|
|
{
|
|
MYSQL_TIME ltime;
|
|
long tmp;
|
|
int error= 0;
|
|
int warning;
|
|
|
|
if (str_to_time(from, len, <ime, &warning))
|
|
{
|
|
tmp=0L;
|
|
error= 2;
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
|
|
from, len, MYSQL_TIMESTAMP_TIME, 1);
|
|
}
|
|
else
|
|
{
|
|
if (warning & MYSQL_TIME_WARN_TRUNCATED)
|
|
{
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
WARN_DATA_TRUNCATED,
|
|
from, len, MYSQL_TIMESTAMP_TIME, 1);
|
|
error= 1;
|
|
}
|
|
if (warning & MYSQL_TIME_WARN_OUT_OF_RANGE)
|
|
{
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE,
|
|
from, len, MYSQL_TIMESTAMP_TIME, !error);
|
|
error= 1;
|
|
}
|
|
if (ltime.month)
|
|
ltime.day=0;
|
|
tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second);
|
|
}
|
|
|
|
if (ltime.neg)
|
|
tmp= -tmp;
|
|
int3store(ptr,tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_time::store_time(MYSQL_TIME *ltime, timestamp_type time_type)
|
|
{
|
|
long tmp= ((ltime->month ? 0 : ltime->day * 24L) + ltime->hour) * 10000L +
|
|
(ltime->minute * 100 + ltime->second);
|
|
if (ltime->neg)
|
|
tmp= -tmp;
|
|
return Field_time::store((longlong) tmp, FALSE);
|
|
}
|
|
|
|
|
|
int Field_time::store(double nr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
long tmp;
|
|
int error= 0;
|
|
if (nr > (double)TIME_MAX_VALUE)
|
|
{
|
|
tmp= TIME_MAX_VALUE;
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME);
|
|
error= 1;
|
|
}
|
|
else if (nr < (double)-TIME_MAX_VALUE)
|
|
{
|
|
tmp= -TIME_MAX_VALUE;
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME);
|
|
error= 1;
|
|
}
|
|
else
|
|
{
|
|
tmp=(long) floor(fabs(nr)); // Remove fractions
|
|
if (nr < 0)
|
|
tmp= -tmp;
|
|
if (tmp % 100 > 59 || tmp/100 % 100 > 59)
|
|
{
|
|
tmp=0;
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE, nr,
|
|
MYSQL_TIMESTAMP_TIME);
|
|
error= 1;
|
|
}
|
|
}
|
|
int3store(ptr,tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_time::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
long tmp;
|
|
int error= 0;
|
|
if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val)
|
|
{
|
|
tmp= -TIME_MAX_VALUE;
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE, nr,
|
|
MYSQL_TIMESTAMP_TIME, 1);
|
|
error= 1;
|
|
}
|
|
else if (nr > (longlong) TIME_MAX_VALUE || (nr < 0 && unsigned_val))
|
|
{
|
|
tmp= TIME_MAX_VALUE;
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE, nr,
|
|
MYSQL_TIMESTAMP_TIME, 1);
|
|
error= 1;
|
|
}
|
|
else
|
|
{
|
|
tmp=(long) nr;
|
|
if (tmp % 100 > 59 || tmp/100 % 100 > 59)
|
|
{
|
|
tmp=0;
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE, nr,
|
|
MYSQL_TIMESTAMP_TIME, 1);
|
|
error= 1;
|
|
}
|
|
}
|
|
int3store(ptr,tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
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 *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
MYSQL_TIME ltime;
|
|
val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
|
|
long tmp=(long) sint3korr(ptr);
|
|
ltime.neg= 0;
|
|
if (tmp < 0)
|
|
{
|
|
tmp= -tmp;
|
|
ltime.neg= 1;
|
|
}
|
|
ltime.day= (uint) 0;
|
|
ltime.hour= (uint) (tmp/10000);
|
|
ltime.minute= (uint) (tmp/100 % 100);
|
|
ltime.second= (uint) (tmp % 100);
|
|
make_time((DATE_TIME_FORMAT*) 0, <ime, val_buffer);
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
/**
|
|
@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, uint fuzzydate)
|
|
{
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
if (!(fuzzydate & TIME_FUZZY_DATE))
|
|
{
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE,
|
|
ER(ER_WARN_DATA_OUT_OF_RANGE), field_name,
|
|
thd->row_count);
|
|
return 1;
|
|
}
|
|
return Field_time::get_time(ltime);
|
|
}
|
|
|
|
|
|
bool Field_time::get_time(MYSQL_TIME *ltime)
|
|
{
|
|
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 tm;
|
|
Field_time::get_time(&tm);
|
|
tm.day= tm.hour/24; // Move hours to days
|
|
tm.hour-= tm.day*24;
|
|
return protocol->store_time(&tm);
|
|
}
|
|
|
|
|
|
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
|
|
{
|
|
res.set_ascii(STRING_WITH_LEN("time"));
|
|
}
|
|
|
|
/****************************************************************************
|
|
** 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, uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
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(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
|
|
return 1;
|
|
}
|
|
if (table->in_use->count_cuted_fields &&
|
|
(error= check_int(cs, from, len, end, error)))
|
|
{
|
|
if (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;
|
|
if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155)
|
|
{
|
|
*ptr= 0;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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;
|
|
}
|
|
|
|
|
|
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;
|
|
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)))
|
|
{
|
|
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());
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
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
|
|
****************************************************************************/
|
|
|
|
int Field_date::store(const char *from, uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
MYSQL_TIME l_time;
|
|
uint32 tmp;
|
|
int error;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
|
|
if (str_to_datetime(from, len, &l_time, TIME_FUZZY_DATE |
|
|
(thd->variables.sql_mode &
|
|
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
|
|
MODE_INVALID_DATES)),
|
|
&error) <= MYSQL_TIMESTAMP_ERROR)
|
|
{
|
|
tmp= 0;
|
|
error= 2;
|
|
}
|
|
else
|
|
tmp=(uint32) l_time.year*10000L + (uint32) (l_time.month*100+l_time.day);
|
|
|
|
if (error)
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
|
|
from, len, MYSQL_TIMESTAMP_DATE, 1);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
{
|
|
int4store(ptr,tmp);
|
|
}
|
|
else
|
|
#endif
|
|
longstore(ptr,tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_date::store(double nr)
|
|
{
|
|
longlong tmp;
|
|
if (nr >= 19000000000000.0 && nr <= 99991231235959.0)
|
|
nr=floor(nr/1000000.0); // Timestamp to date
|
|
if (nr < 0.0 || nr > 99991231.0)
|
|
{
|
|
tmp= LL(0);
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE,
|
|
nr, MYSQL_TIMESTAMP_DATE);
|
|
}
|
|
else
|
|
tmp= (longlong) rint(nr);
|
|
|
|
return Field_date::store(tmp, TRUE);
|
|
}
|
|
|
|
|
|
int Field_date::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
MYSQL_TIME not_used;
|
|
int error;
|
|
longlong initial_nr= nr;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
|
|
nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE |
|
|
(thd->variables.sql_mode &
|
|
(MODE_NO_ZERO_IN_DATE |
|
|
MODE_NO_ZERO_DATE |
|
|
MODE_INVALID_DATES))), &error);
|
|
|
|
if (nr == LL(-1))
|
|
{
|
|
nr= 0;
|
|
error= 2;
|
|
}
|
|
|
|
if (nr >= 19000000000000.0 && nr <= 99991231235959.0)
|
|
nr= (longlong) floor(nr/1000000.0); // Timestamp to date
|
|
|
|
if (error)
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
error == 2 ? ER_WARN_DATA_OUT_OF_RANGE :
|
|
WARN_DATA_TRUNCATED, initial_nr,
|
|
MYSQL_TIMESTAMP_DATETIME, 1);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
{
|
|
int4store(ptr, nr);
|
|
}
|
|
else
|
|
#endif
|
|
longstore(ptr, nr);
|
|
return error;
|
|
}
|
|
|
|
|
|
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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
j=sint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(j,ptr);
|
|
return (double) (uint32) j;
|
|
}
|
|
|
|
|
|
longlong Field_date::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int32 j;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
j=sint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(j,ptr);
|
|
return (longlong) (uint32) j;
|
|
}
|
|
|
|
|
|
String *Field_date::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
MYSQL_TIME ltime;
|
|
val_buffer->alloc(field_length);
|
|
int32 tmp;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
tmp=sint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(tmp,ptr);
|
|
ltime.neg= 0;
|
|
ltime.year= (int) ((uint32) tmp/10000L % 10000);
|
|
ltime.month= (int) ((uint32) tmp/100 % 100);
|
|
ltime.day= (int) ((uint32) tmp % 100);
|
|
make_date((DATE_TIME_FORMAT *) 0, <ime, val_buffer);
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
bool Field_date::get_time(MYSQL_TIME *ltime)
|
|
{
|
|
bzero((char *)ltime, sizeof(MYSQL_TIME));
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Field_date::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
int32 a,b;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
{
|
|
a=sint4korr(a_ptr);
|
|
b=sint4korr(b_ptr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
longget(a,a_ptr);
|
|
longget(b,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)))
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (!table || !table->s->db_low_byte_first)
|
|
{
|
|
to[0] = ptr[0];
|
|
to[1] = ptr[1];
|
|
to[2] = ptr[2];
|
|
to[3] = ptr[3];
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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
|
|
****************************************************************************/
|
|
|
|
/*
|
|
Store string into a date field
|
|
|
|
SYNOPSIS
|
|
Field_newdate::store()
|
|
from Date string
|
|
len Length of date field
|
|
cs Character set (not used)
|
|
|
|
RETURN
|
|
0 ok
|
|
1 Value was cut during conversion
|
|
2 Wrong date string
|
|
3 Datetime value that was cut (warning level NOTE)
|
|
This is used by opt_range.cc:get_mm_leaf(). Note that there is a
|
|
nearly-identical class Field_date doesn't ever return 3 from its
|
|
store function.
|
|
*/
|
|
|
|
int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
long tmp;
|
|
MYSQL_TIME l_time;
|
|
int error;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
enum enum_mysql_timestamp_type ret;
|
|
if ((ret= str_to_datetime(from, len, &l_time,
|
|
(TIME_FUZZY_DATE |
|
|
(thd->variables.sql_mode &
|
|
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
|
|
MODE_INVALID_DATES))),
|
|
&error)) <= MYSQL_TIMESTAMP_ERROR)
|
|
{
|
|
tmp= 0;
|
|
error= 2;
|
|
}
|
|
else
|
|
{
|
|
tmp= l_time.day + l_time.month*32 + l_time.year*16*32;
|
|
if (!error && (ret != MYSQL_TIMESTAMP_DATE) &&
|
|
(l_time.hour || l_time.minute || l_time.second || l_time.second_part))
|
|
error= 3; // Datetime was cut (note)
|
|
}
|
|
|
|
if (error)
|
|
set_datetime_warning(error == 3 ? MYSQL_ERROR::WARN_LEVEL_NOTE :
|
|
MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
WARN_DATA_TRUNCATED,
|
|
from, len, MYSQL_TIMESTAMP_DATE, 1);
|
|
|
|
int3store(ptr, tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_newdate::store(double nr)
|
|
{
|
|
if (nr < 0.0 || nr > 99991231235959.0)
|
|
{
|
|
int3store(ptr,(int32) 0);
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
WARN_DATA_TRUNCATED, nr, MYSQL_TIMESTAMP_DATE);
|
|
return 1;
|
|
}
|
|
return Field_newdate::store((longlong) rint(nr), FALSE);
|
|
}
|
|
|
|
|
|
int Field_newdate::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
MYSQL_TIME l_time;
|
|
longlong tmp;
|
|
int error;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
if (number_to_datetime(nr, &l_time,
|
|
(TIME_FUZZY_DATE |
|
|
(thd->variables.sql_mode &
|
|
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
|
|
MODE_INVALID_DATES))),
|
|
&error) == LL(-1))
|
|
{
|
|
tmp= 0L;
|
|
error= 2;
|
|
}
|
|
else
|
|
tmp= l_time.day + l_time.month*32 + l_time.year*16*32;
|
|
|
|
if (!error && l_time.time_type != MYSQL_TIMESTAMP_DATE &&
|
|
(l_time.hour || l_time.minute || l_time.second || l_time.second_part))
|
|
error= 3;
|
|
|
|
if (error)
|
|
set_datetime_warning(error == 3 ? MYSQL_ERROR::WARN_LEVEL_NOTE :
|
|
MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
error == 2 ?
|
|
ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED,
|
|
nr,MYSQL_TIMESTAMP_DATE, 1);
|
|
|
|
int3store(ptr,tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_newdate::store_time(MYSQL_TIME *ltime,timestamp_type time_type)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
long tmp;
|
|
int error= 0;
|
|
if (time_type == MYSQL_TIMESTAMP_DATE ||
|
|
time_type == MYSQL_TIMESTAMP_DATETIME)
|
|
{
|
|
tmp=ltime->year*16*32+ltime->month*32+ltime->day;
|
|
if (check_date(ltime, tmp != 0,
|
|
(TIME_FUZZY_DATE |
|
|
(current_thd->variables.sql_mode &
|
|
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
|
|
MODE_INVALID_DATES))), &error))
|
|
{
|
|
char buff[MAX_DATE_STRING_REP_LENGTH];
|
|
String str(buff, sizeof(buff), &my_charset_latin1);
|
|
tmp= 0;
|
|
make_date((DATE_TIME_FORMAT *) 0, ltime, &str);
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
|
|
str.ptr(), str.length(), MYSQL_TIMESTAMP_DATE, 1);
|
|
}
|
|
if (!error && ltime->time_type != MYSQL_TIMESTAMP_DATE &&
|
|
(ltime->hour || ltime->minute || ltime->second || ltime->second_part))
|
|
{
|
|
char buff[MAX_DATE_STRING_REP_LENGTH];
|
|
String str(buff, sizeof(buff), &my_charset_latin1);
|
|
make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str);
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_NOTE,
|
|
WARN_DATA_TRUNCATED,
|
|
str.ptr(), str.length(), MYSQL_TIMESTAMP_DATE, 1);
|
|
error= 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tmp=0;
|
|
error= 1;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
|
|
}
|
|
int3store(ptr,tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
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);
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
bool Field_newdate::get_date(MYSQL_TIME *ltime,uint fuzzydate)
|
|
{
|
|
uint32 tmp=(uint32) uint3korr(ptr);
|
|
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 ((!(fuzzydate & TIME_FUZZY_DATE) && (!ltime->month || !ltime->day)) ?
|
|
1 : 0);
|
|
}
|
|
|
|
|
|
bool Field_newdate::get_time(MYSQL_TIME *ltime)
|
|
{
|
|
return Field_newdate::get_date(ltime,0);
|
|
}
|
|
|
|
|
|
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"));
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** 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.
|
|
****************************************************************************/
|
|
|
|
int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
MYSQL_TIME time_tmp;
|
|
int error;
|
|
ulonglong tmp= 0;
|
|
enum enum_mysql_timestamp_type func_res;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
|
|
func_res= str_to_datetime(from, len, &time_tmp,
|
|
(TIME_FUZZY_DATE |
|
|
(thd->variables.sql_mode &
|
|
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
|
|
MODE_INVALID_DATES))),
|
|
&error);
|
|
if ((int) func_res > (int) MYSQL_TIMESTAMP_ERROR)
|
|
tmp= TIME_to_ulonglong_datetime(&time_tmp);
|
|
else
|
|
error= 1; // Fix if invalid zero date
|
|
|
|
if (error)
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE,
|
|
from, len, MYSQL_TIMESTAMP_DATETIME, 1);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
{
|
|
int8store(ptr,tmp);
|
|
}
|
|
else
|
|
#endif
|
|
longlongstore(ptr,tmp);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_datetime::store(double nr)
|
|
{
|
|
int error= 0;
|
|
if (nr < 0.0 || nr > 99991231235959.0)
|
|
{
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_WARN_DATA_OUT_OF_RANGE,
|
|
nr, MYSQL_TIMESTAMP_DATETIME);
|
|
nr= 0.0;
|
|
error= 1;
|
|
}
|
|
error|= Field_datetime::store((longlong) rint(nr), FALSE);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_datetime::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
MYSQL_TIME not_used;
|
|
int error;
|
|
longlong initial_nr= nr;
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
|
|
nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE |
|
|
(thd->variables.sql_mode &
|
|
(MODE_NO_ZERO_IN_DATE |
|
|
MODE_NO_ZERO_DATE |
|
|
MODE_INVALID_DATES))), &error);
|
|
|
|
if (nr == LL(-1))
|
|
{
|
|
nr= 0;
|
|
error= 2;
|
|
}
|
|
|
|
if (error)
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
error == 2 ? ER_WARN_DATA_OUT_OF_RANGE :
|
|
WARN_DATA_TRUNCATED, initial_nr,
|
|
MYSQL_TIMESTAMP_DATETIME, 1);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
{
|
|
int8store(ptr,nr);
|
|
}
|
|
else
|
|
#endif
|
|
longlongstore(ptr,nr);
|
|
return error;
|
|
}
|
|
|
|
|
|
int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
longlong tmp;
|
|
int error= 0;
|
|
/*
|
|
We don't perform range checking here since values stored in TIME
|
|
structure always fit into DATETIME range.
|
|
*/
|
|
if (time_type == MYSQL_TIMESTAMP_DATE ||
|
|
time_type == MYSQL_TIMESTAMP_DATETIME)
|
|
{
|
|
tmp=((ltime->year*10000L+ltime->month*100+ltime->day)*LL(1000000)+
|
|
(ltime->hour*10000L+ltime->minute*100+ltime->second));
|
|
if (check_date(ltime, tmp != 0,
|
|
(TIME_FUZZY_DATE |
|
|
(current_thd->variables.sql_mode &
|
|
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
|
|
MODE_INVALID_DATES))), &error))
|
|
{
|
|
char buff[MAX_DATE_STRING_REP_LENGTH];
|
|
String str(buff, sizeof(buff), &my_charset_latin1);
|
|
tmp= 0;
|
|
make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str);
|
|
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
|
|
str.ptr(), str.length(), MYSQL_TIMESTAMP_DATETIME,1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tmp=0;
|
|
error= 1;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
|
|
}
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
{
|
|
int8store(ptr,tmp);
|
|
}
|
|
else
|
|
#endif
|
|
longlongstore(ptr,tmp);
|
|
return error;
|
|
}
|
|
|
|
bool Field_datetime::send_binary(Protocol *protocol)
|
|
{
|
|
MYSQL_TIME tm;
|
|
Field_datetime::get_date(&tm, TIME_FUZZY_DATE);
|
|
return protocol->store(&tm);
|
|
}
|
|
|
|
|
|
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;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
j=sint8korr(ptr);
|
|
else
|
|
#endif
|
|
longlongget(j,ptr);
|
|
return j;
|
|
}
|
|
|
|
|
|
String *Field_datetime::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);
|
|
ulonglong tmp;
|
|
long part1,part2;
|
|
char *pos;
|
|
int part3;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
tmp=sint8korr(ptr);
|
|
else
|
|
#endif
|
|
longlongget(tmp,ptr);
|
|
|
|
/*
|
|
Avoid problem with slow longlong arithmetic and sprintf
|
|
*/
|
|
|
|
part1=(long) (tmp/LL(1000000));
|
|
part2=(long) (tmp - (ulonglong) part1*LL(1000000));
|
|
|
|
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);
|
|
return val_buffer;
|
|
}
|
|
|
|
bool Field_datetime::get_date(MYSQL_TIME *ltime, uint fuzzydate)
|
|
{
|
|
longlong tmp=Field_datetime::val_int();
|
|
uint32 part1,part2;
|
|
part1=(uint32) (tmp/LL(1000000));
|
|
part2=(uint32) (tmp - (ulonglong) part1*LL(1000000));
|
|
|
|
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 (!(fuzzydate & TIME_FUZZY_DATE) && (!ltime->month || !ltime->day)) ? 1 : 0;
|
|
}
|
|
|
|
bool Field_datetime::get_time(MYSQL_TIME *ltime)
|
|
{
|
|
return Field_datetime::get_date(ltime,0);
|
|
}
|
|
|
|
int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
longlong a,b;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table && table->s->db_low_byte_first)
|
|
{
|
|
a=sint8korr(a_ptr);
|
|
b=sint8korr(b_ptr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
longlongget(a,a_ptr);
|
|
longlongget(b,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)))
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (!table || !table->s->db_low_byte_first)
|
|
{
|
|
to[0] = ptr[0];
|
|
to[1] = ptr[1];
|
|
to[2] = ptr[2];
|
|
to[3] = ptr[3];
|
|
to[4] = ptr[4];
|
|
to[5] = ptr[5];
|
|
to[6] = ptr[6];
|
|
to[7] = ptr[7];
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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
|
|
{
|
|
res.set_ascii(STRING_WITH_LEN("datetime"));
|
|
}
|
|
|
|
/****************************************************************************
|
|
** 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()
|
|
field - Field
|
|
well_formed_error_pos - where not well formed data was first met
|
|
cannot_convert_error_pos - where a not-convertable character was first met
|
|
end - end of the string
|
|
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
|
|
*/
|
|
|
|
static bool
|
|
check_string_copy_error(Field_str *field,
|
|
const char *well_formed_error_pos,
|
|
const char *cannot_convert_error_pos,
|
|
const char *end,
|
|
CHARSET_INFO *cs)
|
|
{
|
|
const char *pos;
|
|
char tmp[32];
|
|
|
|
if (!(pos= well_formed_error_pos) &&
|
|
!(pos= cannot_convert_error_pos))
|
|
return FALSE;
|
|
|
|
convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
|
|
|
|
push_warning_printf(field->table->in_use,
|
|
field->table->in_use->abort_on_warning ?
|
|
MYSQL_ERROR::WARN_LEVEL_ERROR :
|
|
MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
|
|
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
|
|
"string", tmp, field->field_name,
|
|
(ulong) field->table->in_use->row_count);
|
|
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)
|
|
{
|
|
if ((pstr < end) && table->in_use->count_cuted_fields)
|
|
{
|
|
if (test_if_important_data(field_charset, pstr, end))
|
|
{
|
|
if (table->in_use->abort_on_warning)
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1);
|
|
else
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
|
|
return 2;
|
|
}
|
|
else if (count_spaces)
|
|
{ /* If we lost only spaces then produce a NOTE, not a WARNING */
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
|
|
return 2;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Copy a string and fill with space */
|
|
|
|
int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
uint copy_length;
|
|
const char *well_formed_error_pos;
|
|
const char *cannot_convert_error_pos;
|
|
const char *from_end_pos;
|
|
|
|
/* See the comment for Field_long::store(long long) */
|
|
DBUG_ASSERT(table->in_use == current_thd);
|
|
|
|
copy_length= well_formed_copy_nchars(field_charset,
|
|
(char*) ptr, field_length,
|
|
cs, from, length,
|
|
field_length / field_charset->mbmaxlen,
|
|
&well_formed_error_pos,
|
|
&cannot_convert_error_pos,
|
|
&from_end_pos);
|
|
|
|
/* 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);
|
|
|
|
if (check_string_copy_error(this, well_formed_error_pos,
|
|
cannot_convert_error_pos, from + length, cs))
|
|
return 2;
|
|
|
|
return report_if_important_data(from_end_pos, from + length, FALSE);
|
|
}
|
|
|
|
|
|
/**
|
|
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;
|
|
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
|
|
uint length;
|
|
uint local_char_length= field_length / charset()->mbmaxlen;
|
|
double anr= fabs(nr);
|
|
bool fractional= (anr != floor(anr));
|
|
int neg= (nr < 0.0) ? 1 : 0;
|
|
uint max_length;
|
|
int exp;
|
|
uint digits;
|
|
uint i;
|
|
|
|
/* Calculate the exponent from the 'e'-format conversion */
|
|
if (anr < 1.0 && anr > 0)
|
|
{
|
|
for (exp= 0; anr < 1e-100; exp-= 100, anr*= 1e100) ;
|
|
for (; anr < 1e-10; exp-= 10, anr*= 1e10) ;
|
|
for (i= 1; anr < 1 / log_10[i]; exp--, i++) ;
|
|
exp--;
|
|
}
|
|
else
|
|
{
|
|
for (exp= 0; anr > 1e100; exp+= 100, anr/= 1e100) ;
|
|
for (; anr > 1e10; exp+= 10, anr/= 1e10) ;
|
|
for (i= 1; anr > log_10[i]; exp++, i++) ;
|
|
}
|
|
|
|
max_length= local_char_length - neg;
|
|
|
|
/*
|
|
Since in sprintf("%g") precision means the number of significant digits,
|
|
calculate the maximum number of significant digits if the 'f'-format
|
|
would be used (+1 for decimal point if the number has a fractional part).
|
|
*/
|
|
digits= max(1, (int) max_length - fractional);
|
|
/*
|
|
If the exponent is negative, decrease digits by the number of leading zeros
|
|
after the decimal point that do not count as significant digits.
|
|
*/
|
|
if (exp < 0)
|
|
digits= max(1, (int) digits + exp);
|
|
/*
|
|
'e'-format is used only if the exponent is less than -4 or greater than or
|
|
equal to the precision. In this case we need to adjust the number of
|
|
significant digits to take "e+NN" + decimal point into account (hence -5).
|
|
We also have to reserve one additional character if abs(exp) >= 100.
|
|
*/
|
|
if (exp >= (int) digits || exp < -4)
|
|
digits= max(1, (int) (max_length - 5 - (exp >= 100 || exp <= -100)));
|
|
|
|
/* Limit precision to DBL_DIG to avoid garbage past significant digits */
|
|
set_if_smaller(digits, DBL_DIG);
|
|
|
|
length= (uint) sprintf(buff, "%-.*g", digits, nr);
|
|
|
|
#ifdef __WIN__
|
|
/*
|
|
Windows always zero-pads the exponent to 3 digits, we want to remove the
|
|
leading 0 to match the sprintf() output on other platforms.
|
|
*/
|
|
if ((exp >= (int) digits || exp < -4) && exp > -100 && exp < 100)
|
|
{
|
|
DBUG_ASSERT(length >= 6); /* 1e+NNN */
|
|
uint tmp= length - 3;
|
|
buff[tmp]= buff[tmp + 1];
|
|
tmp++;
|
|
buff[tmp]= buff[tmp + 1];
|
|
length--;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
+1 below is because "precision" in %g above means the
|
|
max. number of significant digits, not the output width.
|
|
Thus the width can be larger than number of significant digits by 1
|
|
(for decimal point)
|
|
the test for local_char_length < 5 is for extreme cases,
|
|
like inserting 500.0 in char(1)
|
|
*/
|
|
DBUG_ASSERT(local_char_length < 5 || length <= local_char_length+1);
|
|
return store(buff, length, charset());
|
|
}
|
|
|
|
|
|
uint Field::is_equal(Create_field *new_field)
|
|
{
|
|
return (new_field->sql_type == real_type());
|
|
}
|
|
|
|
|
|
uint Field_str::is_equal(Create_field *new_field)
|
|
{
|
|
if (field_flags_are_binary() != new_field->field_flags_are_binary())
|
|
return 0;
|
|
|
|
return ((new_field->sql_type == real_type()) &&
|
|
new_field->charset == field_charset &&
|
|
new_field->length == max_display_length());
|
|
}
|
|
|
|
|
|
int Field_string::store(longlong nr, bool unsigned_val)
|
|
{
|
|
char buff[64];
|
|
int l;
|
|
CHARSET_INFO *cs=charset();
|
|
l= (cs->cset->longlong10_to_str)(cs,buff,sizeof(buff),
|
|
unsigned_val ? 10 : -10, nr);
|
|
return Field_string::store(buff,(uint)l,cs);
|
|
}
|
|
|
|
|
|
int Field_longstr::store_decimal(const my_decimal *d)
|
|
{
|
|
char buff[DECIMAL_MAX_STR_LENGTH+1];
|
|
String str(buff, sizeof(buff), &my_charset_bin);
|
|
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);
|
|
}
|
|
|
|
|
|
double Field_string::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int error;
|
|
char *end;
|
|
CHARSET_INFO *cs= charset();
|
|
double result;
|
|
|
|
result= my_strntod(cs,(char*) ptr,field_length,&end,&error);
|
|
if (!table->in_use->no_errors &&
|
|
(error || (field_length != (uint32)(end - (char*) ptr) &&
|
|
!check_if_only_end_space(cs, end,
|
|
(char*) ptr + field_length))))
|
|
{
|
|
char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
|
|
String tmp(buf, sizeof(buf), cs);
|
|
tmp.copy((char*) ptr, field_length, cs);
|
|
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_TRUNCATED_WRONG_VALUE,
|
|
ER(ER_TRUNCATED_WRONG_VALUE),
|
|
"DOUBLE", tmp.c_ptr());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
longlong Field_string::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int error;
|
|
char *end;
|
|
CHARSET_INFO *cs= charset();
|
|
longlong result;
|
|
|
|
result= my_strntoll(cs, (char*) ptr,field_length,10,&end,&error);
|
|
if (!table->in_use->no_errors &&
|
|
(error || (field_length != (uint32)(end - (char*) ptr) &&
|
|
!check_if_only_end_space(cs, end,
|
|
(char*) ptr + field_length))))
|
|
{
|
|
char buf[LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE];
|
|
String tmp(buf, sizeof(buf), cs);
|
|
tmp.copy((char*) ptr, field_length, cs);
|
|
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_TRUNCATED_WRONG_VALUE,
|
|
ER(ER_TRUNCATED_WRONG_VALUE),
|
|
"INTEGER", tmp.c_ptr());
|
|
}
|
|
return 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->in_use == current_thd);
|
|
uint length;
|
|
if (table->in_use->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;
|
|
int err= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr, field_length,
|
|
charset(), decimal_value);
|
|
if (!table->in_use->no_errors && err)
|
|
{
|
|
char buf[DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE];
|
|
CHARSET_INFO *cs= charset();
|
|
String tmp(buf, sizeof(buf), cs);
|
|
tmp.copy((char*) ptr, field_length, cs);
|
|
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_TRUNCATED_WRONG_VALUE,
|
|
ER(ER_TRUNCATED_WRONG_VALUE),
|
|
"DECIMAL", tmp.c_ptr());
|
|
}
|
|
|
|
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,
|
|
param->field->real_type(),
|
|
param->field->row_pack_length()));
|
|
return param->field->row_pack_length() > 255;
|
|
}
|
|
#endif
|
|
|
|
int Field_string::compatible_field_size(uint field_metadata,
|
|
const Relay_log_info *rli_arg,
|
|
uint16 mflags __attribute__((unused)))
|
|
{
|
|
#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);
|
|
}
|
|
|
|
|
|
int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
|
|
{
|
|
uint a_len, b_len;
|
|
|
|
if (field_charset->mbmaxlen != 1)
|
|
{
|
|
uint 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,
|
|
0);
|
|
}
|
|
|
|
|
|
void Field_string::sort_string(uchar *to,uint length)
|
|
{
|
|
IF_DBUG(uint tmp=) my_strnxfrm(field_charset,
|
|
to, length,
|
|
ptr, field_length);
|
|
DBUG_ASSERT(tmp == length);
|
|
}
|
|
|
|
|
|
void Field_string::sql_type(String &res) const
|
|
{
|
|
THD *thd= table->in_use;
|
|
CHARSET_INFO *cs=res.charset();
|
|
ulong length;
|
|
|
|
length= cs->cset->snprintf(cs,(char*) res.ptr(),
|
|
res.alloced_length(), "%s(%d)",
|
|
((type() == MYSQL_TYPE_VAR_STRING &&
|
|
!thd->variables.new_mode) ?
|
|
(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,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
uint length= min(field_length,max_length);
|
|
uint local_char_length= max_length/field_charset->mbmaxlen;
|
|
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);
|
|
while (length && from[length-1] == field_charset->pad_char)
|
|
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::do_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,
|
|
uint param_data,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
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)
|
|
{
|
|
length= uint2korr(from);
|
|
from+= 2;
|
|
}
|
|
else
|
|
length= (uint) *from++;
|
|
|
|
memcpy(to, from, length);
|
|
// Pad the string with the pad character of the fields charset
|
|
bfill(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::do_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;
|
|
}
|
|
|
|
|
|
/*
|
|
Compare two packed keys
|
|
|
|
SYNOPSIS
|
|
pack_cmp()
|
|
a New key
|
|
b Original key
|
|
length Key length
|
|
insert_or_update 1 if this is an insert or update
|
|
|
|
RETURN
|
|
< 0 a < b
|
|
0 a = b
|
|
> 0 a > b
|
|
*/
|
|
|
|
int Field_string::pack_cmp(const uchar *a, const uchar *b, uint length,
|
|
my_bool insert_or_update)
|
|
{
|
|
uint a_length, b_length;
|
|
if (length > 255)
|
|
{
|
|
a_length= uint2korr(a);
|
|
b_length= uint2korr(b);
|
|
a+= 2;
|
|
b+= 2;
|
|
}
|
|
else
|
|
{
|
|
a_length= (uint) *a++;
|
|
b_length= (uint) *b++;
|
|
}
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
a, a_length,
|
|
b, b_length,
|
|
insert_or_update);
|
|
}
|
|
|
|
|
|
/**
|
|
Compare a packed key against row.
|
|
|
|
@param key Original key
|
|
@param length Key length. (May be less than field length)
|
|
@param insert_or_update 1 if this is an insert or update
|
|
|
|
@return
|
|
< 0 row < key
|
|
@return
|
|
0 row = key
|
|
@return
|
|
> 0 row > key
|
|
*/
|
|
|
|
int Field_string::pack_cmp(const uchar *key, uint length,
|
|
my_bool insert_or_update)
|
|
{
|
|
uint row_length, local_key_length;
|
|
uchar *end;
|
|
if (length > 255)
|
|
{
|
|
local_key_length= uint2korr(key);
|
|
key+= 2;
|
|
}
|
|
else
|
|
local_key_length= (uint) *key++;
|
|
|
|
/* Only use 'length' of key, not field_length */
|
|
end= ptr + length;
|
|
while (end > ptr && end[-1] == ' ')
|
|
end--;
|
|
row_length= (uint) (end - ptr);
|
|
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
ptr, row_length,
|
|
key, local_key_length,
|
|
insert_or_update);
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
uint 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 bytes;
|
|
}
|
|
|
|
|
|
Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
|
|
bool keep_type)
|
|
{
|
|
Field *field;
|
|
if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
|
|
field= Field::new_field(root, new_table, keep_type);
|
|
else if ((field= new 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 ::new_field. Here we alter the type of field, so ::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::do_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,uint length,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
uint copy_length;
|
|
const char *well_formed_error_pos;
|
|
const char *cannot_convert_error_pos;
|
|
const char *from_end_pos;
|
|
|
|
copy_length= well_formed_copy_nchars(field_charset,
|
|
(char*) ptr + length_bytes,
|
|
field_length,
|
|
cs, from, length,
|
|
field_length / field_charset->mbmaxlen,
|
|
&well_formed_error_pos,
|
|
&cannot_convert_error_pos,
|
|
&from_end_pos);
|
|
|
|
if (length_bytes == 1)
|
|
*ptr= (uchar) copy_length;
|
|
else
|
|
int2store(ptr, copy_length);
|
|
|
|
if (check_string_copy_error(this, well_formed_error_pos,
|
|
cannot_convert_error_pos, from + length, cs))
|
|
return 2;
|
|
|
|
return report_if_important_data(from_end_pos, from + length, TRUE);
|
|
}
|
|
|
|
|
|
int Field_varstring::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 Field_varstring::store(buff, length, field_charset);
|
|
}
|
|
|
|
|
|
double Field_varstring::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int not_used;
|
|
char *end_not_used;
|
|
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
return my_strntod(field_charset, (char*) ptr+length_bytes, length,
|
|
&end_not_used, ¬_used);
|
|
}
|
|
|
|
|
|
longlong Field_varstring::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int not_used;
|
|
char *end_not_used;
|
|
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
return my_strntoll(field_charset, (char*) ptr+length_bytes, length, 10,
|
|
&end_not_used, ¬_used);
|
|
}
|
|
|
|
String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
|
|
String *val_ptr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
val_ptr->set((const char*) ptr+length_bytes, length, field_charset);
|
|
return val_ptr;
|
|
}
|
|
|
|
|
|
my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr+length_bytes, length,
|
|
charset(), 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,0);
|
|
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)
|
|
{
|
|
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
uint 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), 0);
|
|
}
|
|
|
|
|
|
/**
|
|
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),
|
|
0);
|
|
}
|
|
|
|
|
|
void Field_varstring::sort_string(uchar *to,uint length)
|
|
{
|
|
uint tot_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
|
|
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]= tot_length;
|
|
else
|
|
mi_int2store(to+length-2, tot_length);
|
|
length-= length_bytes;
|
|
}
|
|
|
|
tot_length= my_strnxfrm(field_charset,
|
|
to, length, ptr + length_bytes,
|
|
tot_length);
|
|
DBUG_ASSERT(tot_length == 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;
|
|
}
|
|
|
|
|
|
void Field_varstring::sql_type(String &res) const
|
|
{
|
|
THD *thd= table->in_use;
|
|
CHARSET_INFO *cs=res.charset();
|
|
ulong length;
|
|
|
|
length= cs->cset->snprintf(cs,(char*) res.ptr(),
|
|
res.alloced_length(), "%s(%d)",
|
|
(has_charset() ? "varchar" : "varbinary"),
|
|
(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"));
|
|
}
|
|
|
|
|
|
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,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
uchar *
|
|
Field_varstring::pack_key(uchar *to, const uchar *key, uint max_length,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
uint length= length_bytes == 1 ? (uint) *key : uint2korr(key);
|
|
uint local_char_length= ((field_charset->mbmaxlen > 1) ?
|
|
max_length/field_charset->mbmaxlen : max_length);
|
|
key+= length_bytes;
|
|
if (length > local_char_length)
|
|
{
|
|
local_char_length= my_charpos(field_charset, key, key+length,
|
|
local_char_length);
|
|
set_if_smaller(length, local_char_length);
|
|
}
|
|
*to++= (char) (length & 255);
|
|
if (max_length > 255)
|
|
*to++= (char) (length >> 8);
|
|
if (length)
|
|
memcpy(to, key, length);
|
|
return to+length;
|
|
}
|
|
|
|
|
|
/**
|
|
Unpack a key into a record buffer.
|
|
|
|
A VARCHAR key has a maximum size of 64K-1.
|
|
In its packed form, the length field is one or two bytes long,
|
|
depending on 'max_length'.
|
|
|
|
@param to Pointer into the record buffer.
|
|
@param key Pointer to the packed key.
|
|
@param max_length Key length limit from key description.
|
|
|
|
@return
|
|
Pointer to end of 'key' (To the next key part if multi-segment key)
|
|
*/
|
|
|
|
const uchar *
|
|
Field_varstring::unpack_key(uchar *to, const uchar *key, uint max_length,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
/* get length of the blob key */
|
|
uint32 length= *key++;
|
|
if (max_length > 255)
|
|
length+= (*key++) << 8;
|
|
|
|
/* put the length into the record buffer */
|
|
if (length_bytes == 1)
|
|
*ptr= (uchar) length;
|
|
else
|
|
int2store(ptr, length);
|
|
memcpy(ptr + length_bytes, key, length);
|
|
return key + length;
|
|
}
|
|
|
|
/**
|
|
Create a packed key that will be used for storage in the index tree.
|
|
|
|
@param to Store packed key segment here
|
|
@param from Key segment (as given to index_read())
|
|
@param max_length Max length of key
|
|
|
|
@return
|
|
end of key storage
|
|
*/
|
|
|
|
uchar *
|
|
Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, uint max_length,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
/* Key length is always stored as 2 bytes */
|
|
uint length= uint2korr(from);
|
|
if (length > max_length)
|
|
length= max_length;
|
|
*to++= (char) (length & 255);
|
|
if (max_length > 255)
|
|
*to++= (char) (length >> 8);
|
|
if (length)
|
|
memcpy(to, from+HA_KEY_BLOB_LENGTH, 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,
|
|
uint param_data,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
uint length;
|
|
uint l_bytes= (param_data && (param_data < field_length)) ?
|
|
(param_data <= 255) ? 1 : 2 : length_bytes;
|
|
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)
|
|
memcpy(to+ length_bytes, from, length);
|
|
return from+length;
|
|
}
|
|
|
|
|
|
int Field_varstring::pack_cmp(const uchar *a, const uchar *b,
|
|
uint key_length_arg,
|
|
my_bool insert_or_update)
|
|
{
|
|
uint a_length, b_length;
|
|
if (key_length_arg > 255)
|
|
{
|
|
a_length=uint2korr(a); a+= 2;
|
|
b_length=uint2korr(b); b+= 2;
|
|
}
|
|
else
|
|
{
|
|
a_length= (uint) *a++;
|
|
b_length= (uint) *b++;
|
|
}
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
a, a_length,
|
|
b, b_length,
|
|
insert_or_update);
|
|
}
|
|
|
|
|
|
int Field_varstring::pack_cmp(const uchar *b, uint key_length_arg,
|
|
my_bool insert_or_update)
|
|
{
|
|
uchar *a= ptr+ length_bytes;
|
|
uint a_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
uint b_length;
|
|
uint local_char_length= ((field_charset->mbmaxlen > 1) ?
|
|
key_length_arg / field_charset->mbmaxlen :
|
|
key_length_arg);
|
|
|
|
if (key_length_arg > 255)
|
|
{
|
|
b_length=uint2korr(b); b+= HA_KEY_BLOB_LENGTH;
|
|
}
|
|
else
|
|
b_length= (uint) *b++;
|
|
|
|
if (a_length > local_char_length)
|
|
{
|
|
local_char_length= my_charpos(field_charset, a, a+a_length,
|
|
local_char_length);
|
|
set_if_smaller(a_length, local_char_length);
|
|
}
|
|
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
a, a_length,
|
|
b, b_length,
|
|
insert_or_update);
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
uint f_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
|
|
uint local_char_length= length / field_charset->mbmaxlen;
|
|
uchar *pos= ptr+length_bytes;
|
|
local_char_length= my_charpos(field_charset, pos, pos + f_length,
|
|
local_char_length);
|
|
set_if_smaller(f_length, local_char_length);
|
|
/* Key is always stored with 2 bytes */
|
|
int2store(buff,f_length);
|
|
memcpy(buff+HA_KEY_BLOB_LENGTH, pos, f_length);
|
|
if (f_length < length)
|
|
{
|
|
/*
|
|
Must clear this as we do a memcmp in opt_range.cc to detect
|
|
identical keys
|
|
*/
|
|
bzero(buff+HA_KEY_BLOB_LENGTH+f_length, (length-f_length));
|
|
}
|
|
return HA_KEY_BLOB_LENGTH+f_length;
|
|
}
|
|
|
|
|
|
void Field_varstring::set_key_image(const uchar *buff,uint length)
|
|
{
|
|
length= uint2korr(buff); // Real length is here
|
|
(void) Field_varstring::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::new_field(MEM_ROOT *root, struct st_table *new_table,
|
|
bool keep_type)
|
|
{
|
|
Field_varstring *res= (Field_varstring*) Field::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,
|
|
struct st_table *new_table,
|
|
uchar *new_ptr, uchar *new_null_ptr,
|
|
uint new_null_bit)
|
|
{
|
|
Field_varstring *res;
|
|
if ((res= (Field_varstring*) Field::new_key_field(root,
|
|
new_table,
|
|
new_ptr,
|
|
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->sql_type == real_type() &&
|
|
new_field->charset == field_charset)
|
|
{
|
|
if (new_field->length == max_display_length())
|
|
return IS_EQUAL_YES;
|
|
if (new_field->length > max_display_length() &&
|
|
((new_field->length <= 255 && max_display_length() <= 255) ||
|
|
(new_field->length > 255 && max_display_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);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** 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 char *field_name_arg,
|
|
TABLE_SHARE *share, uint blob_pack_length,
|
|
CHARSET_INFO *cs)
|
|
: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,
|
|
cs),
|
|
packlength(blob_pack_length)
|
|
{
|
|
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,
|
|
bool low_byte_first)
|
|
{
|
|
switch (i_packlength) {
|
|
case 1:
|
|
i_ptr[0]= (uchar) i_number;
|
|
break;
|
|
case 2:
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (low_byte_first)
|
|
{
|
|
int2store(i_ptr,(unsigned short) i_number);
|
|
}
|
|
else
|
|
#endif
|
|
shortstore(i_ptr,(unsigned short) i_number);
|
|
break;
|
|
case 3:
|
|
int3store(i_ptr,i_number);
|
|
break;
|
|
case 4:
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (low_byte_first)
|
|
{
|
|
int4store(i_ptr,i_number);
|
|
}
|
|
else
|
|
#endif
|
|
longstore(i_ptr,i_number);
|
|
}
|
|
}
|
|
|
|
|
|
uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg, bool low_byte_first)
|
|
{
|
|
switch (packlength_arg) {
|
|
case 1:
|
|
return (uint32) pos[0];
|
|
case 2:
|
|
{
|
|
uint16 tmp;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (low_byte_first)
|
|
tmp=sint2korr(pos);
|
|
else
|
|
#endif
|
|
shortget(tmp,pos);
|
|
return (uint32) tmp;
|
|
}
|
|
case 3:
|
|
return (uint32) uint3korr(pos);
|
|
case 4:
|
|
{
|
|
uint32 tmp;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (low_byte_first)
|
|
tmp=uint4korr(pos);
|
|
else
|
|
#endif
|
|
longget(tmp,pos);
|
|
return (uint32) tmp;
|
|
}
|
|
}
|
|
/* When expanding this, see also MAX_FIELD_BLOBLENGTH. */
|
|
return 0; // Impossible
|
|
}
|
|
|
|
|
|
/**
|
|
Put a blob length field into a record buffer.
|
|
|
|
Depending on the maximum length of a blob, its length field is
|
|
put into 1 to 4 bytes. This is a property of the blob object,
|
|
described by 'packlength'.
|
|
|
|
@param pos Pointer into the record buffer.
|
|
@param length The length value to put.
|
|
*/
|
|
|
|
void Field_blob::put_length(uchar *pos, uint32 length)
|
|
{
|
|
switch (packlength) {
|
|
case 1:
|
|
*pos= (char) length;
|
|
break;
|
|
case 2:
|
|
int2store(pos, length);
|
|
break;
|
|
case 3:
|
|
int3store(pos, length);
|
|
break;
|
|
case 4:
|
|
int4store(pos, length);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
uint copy_length, new_length;
|
|
const char *well_formed_error_pos;
|
|
const char *cannot_convert_error_pos;
|
|
const char *from_end_pos, *tmp;
|
|
char buff[STRING_BUFFER_USUAL_SIZE];
|
|
String tmpstr(buff,sizeof(buff), &my_charset_bin);
|
|
|
|
if (!length)
|
|
{
|
|
bzero(ptr,Field_blob::pack_length());
|
|
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.
|
|
*/
|
|
uint32 dummy_offset;
|
|
if (!String::needs_conversion(length, cs, field_charset, &dummy_offset))
|
|
{
|
|
Field_blob::store_length(length);
|
|
bmove(ptr+packlength,(char*) &from,sizeof(char*));
|
|
return 0;
|
|
}
|
|
if (tmpstr.copy(from, length, cs))
|
|
goto oom_error;
|
|
from= tmpstr.ptr();
|
|
}
|
|
|
|
new_length= min(max_data_length(), field_charset->mbmaxlen * length);
|
|
if (value.alloc(new_length))
|
|
goto oom_error;
|
|
|
|
|
|
if (f_is_hex_escape(flags))
|
|
{
|
|
copy_length= my_copy_with_hex_escaping(field_charset,
|
|
(char*) value.ptr(), new_length,
|
|
from, length);
|
|
Field_blob::store_length(copy_length);
|
|
tmp= value.ptr();
|
|
bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
|
|
return 0;
|
|
}
|
|
/*
|
|
"length" is OK as "nchars" argument to well_formed_copy_nchars as this
|
|
is never used to limit the length of the data. The cut of long data
|
|
is done with the new_length value.
|
|
*/
|
|
copy_length= well_formed_copy_nchars(field_charset,
|
|
(char*) value.ptr(), new_length,
|
|
cs, from, length,
|
|
length,
|
|
&well_formed_error_pos,
|
|
&cannot_convert_error_pos,
|
|
&from_end_pos);
|
|
|
|
Field_blob::store_length(copy_length);
|
|
tmp= value.ptr();
|
|
bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
|
|
|
|
if (check_string_copy_error(this, well_formed_error_pos,
|
|
cannot_convert_error_pos, from + length, cs))
|
|
return 2;
|
|
|
|
return report_if_important_data(from_end_pos, from + length, TRUE);
|
|
|
|
oom_error:
|
|
/* Fatal OOM error */
|
|
bzero(ptr,Field_blob::pack_length());
|
|
return -1;
|
|
}
|
|
|
|
|
|
int Field_blob::store(double nr)
|
|
{
|
|
CHARSET_INFO *cs=charset();
|
|
value.set_real(nr, 2, cs);
|
|
return Field_blob::store(value.ptr(),(uint) value.length(), cs);
|
|
}
|
|
|
|
|
|
int Field_blob::store(longlong nr, bool unsigned_val)
|
|
{
|
|
CHARSET_INFO *cs=charset();
|
|
value.set_int(nr, unsigned_val, cs);
|
|
return Field_blob::store(value.ptr(), (uint) value.length(), cs);
|
|
}
|
|
|
|
|
|
double Field_blob::val_real(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int not_used;
|
|
char *end_not_used, *blob;
|
|
uint32 length;
|
|
CHARSET_INFO *cs;
|
|
|
|
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
|
if (!blob)
|
|
return 0.0;
|
|
length= get_length(ptr);
|
|
cs= charset();
|
|
return my_strntod(cs, blob, length, &end_not_used, ¬_used);
|
|
}
|
|
|
|
|
|
longlong Field_blob::val_int(void)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
int not_used;
|
|
char *blob;
|
|
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
|
if (!blob)
|
|
return 0;
|
|
uint32 length=get_length(ptr);
|
|
return my_strntoll(charset(),blob,length,10,NULL,¬_used);
|
|
}
|
|
|
|
String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
|
|
String *val_ptr)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_READ;
|
|
char *blob;
|
|
memcpy_fixed(&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_fixed(&blob, ptr+packlength, sizeof(const uchar*));
|
|
if (!blob)
|
|
{
|
|
blob= "";
|
|
length= 0;
|
|
}
|
|
else
|
|
length= get_length(ptr);
|
|
|
|
str2my_decimal(E_DEC_FATAL_ERROR, blob, length, charset(),
|
|
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,
|
|
0);
|
|
}
|
|
|
|
|
|
int Field_blob::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
|
|
uint max_length)
|
|
{
|
|
uchar *blob1,*blob2;
|
|
memcpy_fixed(&blob1,a_ptr+packlength,sizeof(char*));
|
|
memcpy_fixed(&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_fixed(&a,a_ptr+packlength,sizeof(char*));
|
|
memcpy_fixed(&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,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)
|
|
{
|
|
uint32 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;
|
|
}
|
|
get_ptr(&blob);
|
|
gobj= Geometry::construct(&buffer, (char*) blob, 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*/
|
|
|
|
get_ptr(&blob);
|
|
uint 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 ((uint32) 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;
|
|
uint blob_length=get_length(ptr);
|
|
memcpy_fixed(&blob1,ptr+packlength,sizeof(char*));
|
|
CHARSET_INFO *cs= charset();
|
|
uint 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, 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));
|
|
}
|
|
|
|
|
|
/**
|
|
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::do_save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
*metadata_ptr= pack_length_no_ptr();
|
|
return 1;
|
|
}
|
|
|
|
|
|
uint32 Field_blob::sort_length() const
|
|
{
|
|
return (uint32) (current_thd->variables.max_sort_length +
|
|
(field_charset == &my_charset_bin ? 0 : packlength));
|
|
}
|
|
|
|
|
|
void Field_blob::sort_string(uchar *to,uint length)
|
|
{
|
|
uchar *blob;
|
|
uint blob_length=get_length();
|
|
|
|
if (!blob_length)
|
|
bzero(to,length);
|
|
else
|
|
{
|
|
if (field_charset == &my_charset_bin)
|
|
{
|
|
uchar *pos;
|
|
|
|
/*
|
|
Store length of blob last in blob to shorter blobs before longer blobs
|
|
*/
|
|
length-= packlength;
|
|
pos= to+length;
|
|
|
|
switch (packlength) {
|
|
case 1:
|
|
*pos= (char) blob_length;
|
|
break;
|
|
case 2:
|
|
mi_int2store(pos, blob_length);
|
|
break;
|
|
case 3:
|
|
mi_int3store(pos, blob_length);
|
|
break;
|
|
case 4:
|
|
mi_int4store(pos, blob_length);
|
|
break;
|
|
}
|
|
}
|
|
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
|
|
|
blob_length=my_strnxfrm(field_charset,
|
|
to, length, blob, blob_length);
|
|
DBUG_ASSERT(blob_length == length);
|
|
}
|
|
}
|
|
|
|
|
|
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, bool low_byte_first)
|
|
{
|
|
DBUG_ENTER("Field_blob::pack");
|
|
DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;"
|
|
" max_length: %u; low_byte_first: %d",
|
|
(ulong) to, (ulong) from,
|
|
max_length, low_byte_first));
|
|
DBUG_DUMP("record", from, table->s->reclength);
|
|
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, min(length, max_length), low_byte_first);
|
|
|
|
/*
|
|
Store the actual blob data, which will occupy 'length' bytes.
|
|
*/
|
|
if (length > 0)
|
|
{
|
|
get_ptr((uchar**) &from);
|
|
memcpy(to+packlength, from,length);
|
|
}
|
|
ptr=save; // Restore org row pointer
|
|
DBUG_DUMP("packed", to, packlength + length);
|
|
DBUG_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,
|
|
uint param_data,
|
|
bool low_byte_first)
|
|
{
|
|
DBUG_ENTER("Field_blob::unpack");
|
|
DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;"
|
|
" param_data: %u; low_byte_first: %d",
|
|
(ulong) to, (ulong) from, param_data, low_byte_first));
|
|
uint const master_packlength=
|
|
param_data > 0 ? param_data & 0xFF : packlength;
|
|
uint32 const length= get_length(from, master_packlength, low_byte_first);
|
|
DBUG_DUMP("packed", from, length + master_packlength);
|
|
bitmap_set_bit(table->write_set, field_index);
|
|
store(reinterpret_cast<const char*>(from) + master_packlength,
|
|
length, field_charset);
|
|
DBUG_DUMP("record", to, table->s->reclength);
|
|
DBUG_RETURN(from + master_packlength + length);
|
|
}
|
|
|
|
/* Keys for blobs are like keys on varchars */
|
|
|
|
int Field_blob::pack_cmp(const uchar *a, const uchar *b, uint key_length_arg,
|
|
my_bool insert_or_update)
|
|
{
|
|
uint a_length, b_length;
|
|
if (key_length_arg > 255)
|
|
{
|
|
a_length=uint2korr(a); a+=2;
|
|
b_length=uint2korr(b); b+=2;
|
|
}
|
|
else
|
|
{
|
|
a_length= (uint) *a++;
|
|
b_length= (uint) *b++;
|
|
}
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
a, a_length,
|
|
b, b_length,
|
|
insert_or_update);
|
|
}
|
|
|
|
|
|
int Field_blob::pack_cmp(const uchar *b, uint key_length_arg,
|
|
my_bool insert_or_update)
|
|
{
|
|
uchar *a;
|
|
uint a_length, b_length;
|
|
memcpy_fixed(&a,ptr+packlength,sizeof(char*));
|
|
if (!a)
|
|
return key_length_arg > 0 ? -1 : 0;
|
|
|
|
a_length= get_length(ptr);
|
|
if (key_length_arg > 255)
|
|
{
|
|
b_length= uint2korr(b); b+=2;
|
|
}
|
|
else
|
|
b_length= (uint) *b++;
|
|
return field_charset->coll->strnncollsp(field_charset,
|
|
a, a_length,
|
|
b, b_length,
|
|
insert_or_update);
|
|
}
|
|
|
|
/** Create a packed key that will be used for storage from a MySQL row. */
|
|
|
|
uchar *
|
|
Field_blob::pack_key(uchar *to, const uchar *from, uint max_length,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
uchar *save= ptr;
|
|
ptr= (uchar*) from;
|
|
uint32 length=get_length(); // Length of from string
|
|
uint local_char_length= ((field_charset->mbmaxlen > 1) ?
|
|
max_length/field_charset->mbmaxlen : max_length);
|
|
if (length)
|
|
get_ptr((uchar**) &from);
|
|
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);
|
|
*to++= (uchar) length;
|
|
if (max_length > 255) // 2 byte length
|
|
*to++= (uchar) (length >> 8);
|
|
memcpy(to, from, length);
|
|
ptr=save; // Restore org row pointer
|
|
return to+length;
|
|
}
|
|
|
|
|
|
/**
|
|
Unpack a blob key into a record buffer.
|
|
|
|
A blob key has a maximum size of 64K-1.
|
|
In its packed form, the length field is one or two bytes long,
|
|
depending on 'max_length'.
|
|
Depending on the maximum length of a blob, its length field is
|
|
put into 1 to 4 bytes. This is a property of the blob object,
|
|
described by 'packlength'.
|
|
Blobs are internally stored apart from the record buffer, which
|
|
contains a pointer to the blob buffer.
|
|
|
|
|
|
@param to Pointer into the record buffer.
|
|
@param from Pointer to the packed key.
|
|
@param max_length Key length limit from key description.
|
|
|
|
@return
|
|
Pointer into 'from' past the last byte copied from packed key.
|
|
*/
|
|
|
|
const uchar *
|
|
Field_blob::unpack_key(uchar *to, const uchar *from, uint max_length,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
/* get length of the blob key */
|
|
uint32 length= *from++;
|
|
if (max_length > 255)
|
|
length+= *from++ << 8;
|
|
|
|
/* put the length into the record buffer */
|
|
put_length(to, length);
|
|
|
|
/* put the address of the blob buffer or NULL */
|
|
if (length)
|
|
memcpy_fixed(to + packlength, &from, sizeof(from));
|
|
else
|
|
bzero(to + packlength, sizeof(from));
|
|
|
|
/* point to first byte of next field in 'from' */
|
|
return from + length;
|
|
}
|
|
|
|
|
|
/** Create a packed key that will be used for storage from a MySQL key. */
|
|
|
|
uchar *
|
|
Field_blob::pack_key_from_key_image(uchar *to, const uchar *from, uint max_length,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
uint length=uint2korr(from);
|
|
if (length > max_length)
|
|
length=max_length;
|
|
*to++= (char) (length & 255);
|
|
if (max_length > 255)
|
|
*to++= (char) (length >> 8);
|
|
if (length)
|
|
memcpy(to, from+HA_KEY_BLOB_LENGTH, length);
|
|
return to+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;
|
|
}
|
|
|
|
|
|
uint Field_blob::is_equal(Create_field *new_field)
|
|
{
|
|
if (field_flags_are_binary() != new_field->field_flags_are_binary())
|
|
return 0;
|
|
|
|
return ((new_field->sql_type == get_blob_type_from_length(max_data_length()))
|
|
&& new_field->charset == field_charset &&
|
|
new_field->pack_length == pack_length());
|
|
}
|
|
|
|
|
|
#ifdef HAVE_SPATIAL
|
|
|
|
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(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(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(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
|
|
return -1;
|
|
}
|
|
|
|
|
|
int Field_geom::store(const char *from, uint 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 + SIZEOF_STORED_DOUBLE*2)
|
|
goto err;
|
|
wkb_type= uint4korr(from + SRID_SIZE + 1);
|
|
if (wkb_type < (uint32) Geometry::wkb_point ||
|
|
wkb_type > (uint32) Geometry::wkb_last)
|
|
goto err;
|
|
Field_blob::store_length(length);
|
|
if (table->copy_blobs || length <= MAX_FIELD_WIDTH)
|
|
{ // Must make a copy
|
|
value.copy(from, length, cs);
|
|
from= value.ptr();
|
|
}
|
|
bmove(ptr + packlength, (char*) &from, sizeof(char*));
|
|
}
|
|
return 0;
|
|
|
|
err:
|
|
bzero(ptr, Field_blob::pack_length());
|
|
my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
|
|
ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
|
|
return -1;
|
|
}
|
|
|
|
#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)
|
|
{
|
|
switch (packlength) {
|
|
case 1: ptr[0]= (uchar) value; break;
|
|
case 2:
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int2store(ptr,(unsigned short) value);
|
|
}
|
|
else
|
|
#endif
|
|
shortstore(ptr,(unsigned short) value);
|
|
break;
|
|
case 3: int3store(ptr,(long) value); break;
|
|
case 4:
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int4store(ptr,value);
|
|
}
|
|
else
|
|
#endif
|
|
longstore(ptr,(long) value);
|
|
break;
|
|
case 8:
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
{
|
|
int8store(ptr,value);
|
|
}
|
|
else
|
|
#endif
|
|
longlongstore(ptr,value); break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
@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,uint length,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int err= 0;
|
|
uint32 not_used;
|
|
char buff[STRING_BUFFER_USUAL_SIZE];
|
|
String tmpstr(buff,sizeof(buff), &my_charset_bin);
|
|
|
|
/* Convert character set if necessary */
|
|
if (String::needs_conversion(length, cs, field_charset, ¬_used))
|
|
{
|
|
uint dummy_errors;
|
|
tmpstr.copy(from, length, cs, field_charset, &dummy_errors);
|
|
from= tmpstr.ptr();
|
|
length= tmpstr.length();
|
|
}
|
|
|
|
/* Remove end space */
|
|
length= 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(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
|
|
}
|
|
if (!table->in_use->count_cuted_fields)
|
|
err= 0;
|
|
}
|
|
else
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 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;
|
|
int error= 0;
|
|
if ((ulonglong) nr > typelib->count || nr == 0)
|
|
{
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
|
|
if (nr != 0 || table->in_use->count_cuted_fields)
|
|
{
|
|
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;
|
|
switch (packlength) {
|
|
case 1:
|
|
return (longlong) ptr[0];
|
|
case 2:
|
|
{
|
|
uint16 tmp;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
tmp=sint2korr(ptr);
|
|
else
|
|
#endif
|
|
shortget(tmp,ptr);
|
|
return (longlong) tmp;
|
|
}
|
|
case 3:
|
|
return (longlong) uint3korr(ptr);
|
|
case 4:
|
|
{
|
|
uint32 tmp;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
tmp=uint4korr(ptr);
|
|
else
|
|
#endif
|
|
longget(tmp,ptr);
|
|
return (longlong) tmp;
|
|
}
|
|
case 8:
|
|
{
|
|
longlong tmp;
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (table->s->db_low_byte_first)
|
|
tmp=sint8korr(ptr);
|
|
else
|
|
#endif
|
|
longlongget(tmp,ptr);
|
|
return tmp;
|
|
}
|
|
}
|
|
return 0; // impossible
|
|
}
|
|
|
|
|
|
/**
|
|
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::do_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::new_field(MEM_ROOT *root, struct st_table *new_table,
|
|
bool keep_type)
|
|
{
|
|
Field_enum *res= (Field_enum*) Field::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,uint length,CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
bool got_warning= 0;
|
|
int err= 0;
|
|
char *not_used;
|
|
uint not_used2;
|
|
uint32 not_used_offset;
|
|
char buff[STRING_BUFFER_USUAL_SIZE];
|
|
String tmpstr(buff,sizeof(buff), &my_charset_bin);
|
|
|
|
/* Convert character set if necessary */
|
|
if (String::needs_conversion(length, cs, field_charset, ¬_used_offset))
|
|
{
|
|
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(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
|
|
}
|
|
}
|
|
else if (got_warning)
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
|
|
store_type(tmp);
|
|
return err;
|
|
}
|
|
|
|
|
|
int Field_set::store(longlong nr, bool unsigned_val)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int error= 0;
|
|
ulonglong max_nr;
|
|
|
|
if (sizeof(ulonglong)*8 <= typelib->count)
|
|
max_nr= ULONGLONG_MAX;
|
|
else
|
|
max_nr= (ULL(1) << typelib->count) - 1;
|
|
|
|
if ((ulonglong) nr > max_nr)
|
|
{
|
|
nr&= max_nr;
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
|
|
error=1;
|
|
}
|
|
store_type((ulonglong) nr);
|
|
return error;
|
|
}
|
|
|
|
|
|
String *Field_set::val_str(String *val_buffer,
|
|
String *val_ptr __attribute__((unused)))
|
|
{
|
|
ulonglong tmp=(ulonglong) Field_enum::val_int();
|
|
uint bitnr=0;
|
|
|
|
val_buffer->length(0);
|
|
val_buffer->set_charset(field_charset);
|
|
while (tmp && bitnr < (uint) typelib->count)
|
|
{
|
|
if (tmp & 1)
|
|
{
|
|
if (val_buffer->length())
|
|
val_buffer->append(&field_separator, 1, &my_charset_latin1);
|
|
String str(typelib->type_names[bitnr],
|
|
typelib->type_lengths[bitnr],
|
|
field_charset);
|
|
val_buffer->append(str);
|
|
}
|
|
tmp>>=1;
|
|
bitnr++;
|
|
}
|
|
return val_buffer;
|
|
}
|
|
|
|
|
|
void Field_set::sql_type(String &res) const
|
|
{
|
|
char buffer[255];
|
|
String set_item(buffer, sizeof(buffer), res.charset());
|
|
|
|
res.length(0);
|
|
res.append(STRING_WITH_LEN("set("));
|
|
|
|
bool flag=0;
|
|
uint *len= typelib->type_lengths;
|
|
for (const char **pos= typelib->type_names; *pos; pos++, len++)
|
|
{
|
|
uint dummy_errors;
|
|
if (flag)
|
|
res.append(',');
|
|
/* convert to res.charset() == utf8, then quote */
|
|
set_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
|
|
append_unescaped(&res, set_item.ptr(), set_item.length());
|
|
flag= 1;
|
|
}
|
|
res.append(')');
|
|
}
|
|
|
|
/**
|
|
@retval
|
|
1 if the fields are equally defined
|
|
@retval
|
|
0 if the fields are unequally defined
|
|
*/
|
|
|
|
bool Field::eq_def(Field *field)
|
|
{
|
|
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(Field *field)
|
|
{
|
|
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->field_flags_are_binary() != field_flags_are_binary() ||
|
|
new_field->sql_type != real_type() ||
|
|
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;
|
|
}
|
|
|
|
|
|
/**
|
|
@return
|
|
returns 1 if the fields are equally defined
|
|
*/
|
|
bool Field_num::eq_def(Field *field)
|
|
{
|
|
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->sql_type == real_type()) &&
|
|
((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()));
|
|
}
|
|
|
|
|
|
/*
|
|
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 char *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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
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: 0x%lx",
|
|
bit_ofs, bit_len, (long) 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,
|
|
struct st_table *new_table,
|
|
uchar *new_ptr, uchar *new_null_ptr,
|
|
uint new_null_bit)
|
|
{
|
|
Field_bit *res;
|
|
if ((res= (Field_bit*) Field::new_key_field(root, new_table,
|
|
new_ptr, 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->sql_type == real_type() &&
|
|
new_field->length == max_display_length());
|
|
}
|
|
|
|
|
|
int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int delta;
|
|
|
|
for (; length && !*from; from++, length--) ; // skip left 0's
|
|
delta= 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 (table->in_use->really_abort_on_warning())
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1);
|
|
else
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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 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= min(pack_length(), sizeof(longlong));
|
|
ulonglong bits= val_int();
|
|
mi_int8store(buff,bits);
|
|
|
|
val_buffer->alloc(length);
|
|
memcpy_fixed((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;
|
|
}
|
|
return memcmp(a, b, field_length);
|
|
}
|
|
|
|
|
|
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, length);
|
|
}
|
|
|
|
|
|
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 data_length = min(length, bytes_in_rec);
|
|
memcpy(buff, ptr, data_length);
|
|
return 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::do_save_field_metadata(uchar *metadata_ptr)
|
|
{
|
|
/*
|
|
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;
|
|
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);
|
|
}
|
|
|
|
|
|
/**
|
|
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).
|
|
|
|
@param field_metadata Encoded size in field metadata
|
|
|
|
@retval 0 if this field's size is < the source field's size
|
|
@retval 1 if this field's size is >= the source field's size
|
|
*/
|
|
int Field_bit::compatible_field_size(uint field_metadata,
|
|
const Relay_log_info * __attribute__((unused)),
|
|
uint16 mflags)
|
|
{
|
|
uint from_bit_len= 8 * (field_metadata >> 8) + (field_metadata & 0xff);
|
|
uint to_bit_len= max_display_length();
|
|
|
|
/*
|
|
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;
|
|
}
|
|
|
|
return from_bit_len <= to_bit_len;
|
|
}
|
|
|
|
|
|
|
|
void Field_bit::sql_type(String &res) const
|
|
{
|
|
CHARSET_INFO *cs= res.charset();
|
|
ulong length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
|
|
"bit(%d)", (int) field_length);
|
|
res.length((uint) length);
|
|
}
|
|
|
|
|
|
uchar *
|
|
Field_bit::pack(uchar *to, const uchar *from, uint max_length,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
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= 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, uint param_data,
|
|
bool low_byte_first __attribute__((unused)))
|
|
{
|
|
uint const from_len= (param_data >> 8U) & 0x00ff;
|
|
uint const from_bit_len= param_data & 0x00ff;
|
|
/*
|
|
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 (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);
|
|
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 new_len= (field_length + 7) / 8;
|
|
char *value= (char *)my_alloca(new_len);
|
|
bzero(value, new_len);
|
|
uint len= from_len + ((from_bit_len > 0) ? 1 : 0);
|
|
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);
|
|
return from + len;
|
|
}
|
|
|
|
|
|
void Field_bit::set_default()
|
|
{
|
|
if (bit_len > 0)
|
|
{
|
|
my_ptrdiff_t const offset= table->s->default_values - table->record[0];
|
|
uchar bits= get_rec_bits(bit_ptr + offset, bit_ofs, bit_len);
|
|
set_rec_bits(bits, bit_ptr, bit_ofs, bit_len);
|
|
}
|
|
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 char *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, uint length, CHARSET_INFO *cs)
|
|
{
|
|
ASSERT_COLUMN_MARKED_FOR_WRITE;
|
|
int delta;
|
|
uchar bits= (uchar) (field_length & 7);
|
|
|
|
for (; length && !*from; from++, length--) ; // skip left 0's
|
|
delta= 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 (table->in_use->really_abort_on_warning())
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1);
|
|
else
|
|
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 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();
|
|
ulong length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
|
|
"bit(%d)", (int) field_length);
|
|
res.length((uint) length);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
Handling of field and Create_field
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
Convert create_field::length from number of characters to number of bytes.
|
|
*/
|
|
|
|
void Create_field::create_length_to_internal_length(void)
|
|
{
|
|
switch (sql_type) {
|
|
case MYSQL_TYPE_TINY_BLOB:
|
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
case MYSQL_TYPE_LONG_BLOB:
|
|
case MYSQL_TYPE_BLOB:
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING:
|
|
case MYSQL_TYPE_VARCHAR:
|
|
length*= charset->mbmaxlen;
|
|
key_length= length;
|
|
pack_length= calc_pack_length(sql_type, length);
|
|
break;
|
|
case MYSQL_TYPE_ENUM:
|
|
case MYSQL_TYPE_SET:
|
|
/* Pack_length already calculated in sql_parse.cc */
|
|
length*= charset->mbmaxlen;
|
|
key_length= pack_length;
|
|
break;
|
|
case MYSQL_TYPE_BIT:
|
|
if (f_bit_as_char(pack_flag))
|
|
{
|
|
key_length= pack_length= ((length + 7) & ~7) / 8;
|
|
}
|
|
else
|
|
{
|
|
pack_length= length / 8;
|
|
/* We need one extra byte to store the bits we save among the null bits */
|
|
key_length= pack_length + test(length & 7);
|
|
}
|
|
break;
|
|
case MYSQL_TYPE_NEWDECIMAL:
|
|
key_length= pack_length=
|
|
my_decimal_get_binary_size(my_decimal_length_to_precision(length,
|
|
decimals,
|
|
flags &
|
|
UNSIGNED_FLAG),
|
|
decimals);
|
|
break;
|
|
default:
|
|
key_length= pack_length= calc_pack_length(sql_type, length);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Init for a tmp table field. To be extended if need be.
|
|
*/
|
|
void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
|
|
uint32 length_arg, uint32 decimals_arg,
|
|
bool maybe_null, bool is_unsigned)
|
|
{
|
|
field_name= "";
|
|
sql_type= sql_type_arg;
|
|
char_length= length= length_arg;;
|
|
unireg_check= Field::NONE;
|
|
interval= 0;
|
|
charset= &my_charset_bin;
|
|
geom_type= Field::GEOM_GEOMETRY;
|
|
pack_flag= (FIELDFLAG_NUMBER |
|
|
((decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT) |
|
|
(maybe_null ? FIELDFLAG_MAYBE_NULL : 0) |
|
|
(is_unsigned ? 0 : FIELDFLAG_DECIMAL));
|
|
}
|
|
|
|
|
|
/**
|
|
Initialize field definition for create.
|
|
|
|
@param thd Thread handle
|
|
@param fld_name Field name
|
|
@param fld_type Field type
|
|
@param fld_length Field length
|
|
@param fld_decimals Decimal (if any)
|
|
@param fld_type_modifier Additional type information
|
|
@param fld_default_value Field default value (if any)
|
|
@param fld_on_update_value The value of ON UPDATE clause
|
|
@param fld_comment Field comment
|
|
@param fld_change Field change
|
|
@param fld_interval_list Interval list (if any)
|
|
@param fld_charset Field charset
|
|
@param fld_geom_type Field geometry type (if any)
|
|
|
|
@retval
|
|
FALSE on success
|
|
@retval
|
|
TRUE on error
|
|
*/
|
|
|
|
bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
|
|
char *fld_length, char *fld_decimals,
|
|
uint fld_type_modifier, Item *fld_default_value,
|
|
Item *fld_on_update_value, LEX_STRING *fld_comment,
|
|
char *fld_change, List<String> *fld_interval_list,
|
|
CHARSET_INFO *fld_charset, uint fld_geom_type)
|
|
{
|
|
uint sign_len, allowed_type_modifier= 0;
|
|
ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
|
|
|
|
DBUG_ENTER("Create_field::init()");
|
|
|
|
field= 0;
|
|
field_name= fld_name;
|
|
def= fld_default_value;
|
|
flags= fld_type_modifier;
|
|
unireg_check= (fld_type_modifier & AUTO_INCREMENT_FLAG ?
|
|
Field::NEXT_NUMBER : Field::NONE);
|
|
decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0;
|
|
if (decimals >= NOT_FIXED_DEC)
|
|
{
|
|
my_error(ER_TOO_BIG_SCALE, MYF(0), decimals, fld_name,
|
|
NOT_FIXED_DEC-1);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
|
|
sql_type= fld_type;
|
|
length= 0;
|
|
change= fld_change;
|
|
interval= 0;
|
|
pack_length= key_length= 0;
|
|
charset= fld_charset;
|
|
geom_type= (Field::geometry_type) fld_geom_type;
|
|
interval_list.empty();
|
|
|
|
comment= *fld_comment;
|
|
/*
|
|
Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and
|
|
it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP.
|
|
*/
|
|
if (!fld_default_value && !(fld_type_modifier & AUTO_INCREMENT_FLAG) &&
|
|
(fld_type_modifier & NOT_NULL_FLAG) && fld_type != MYSQL_TYPE_TIMESTAMP)
|
|
flags|= NO_DEFAULT_VALUE_FLAG;
|
|
|
|
if (fld_length != NULL)
|
|
{
|
|
errno= 0;
|
|
length= strtoul(fld_length, NULL, 10);
|
|
if ((errno != 0) || (length > MAX_FIELD_BLOBLENGTH))
|
|
{
|
|
my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name, MAX_FIELD_BLOBLENGTH);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
|
|
if (length == 0)
|
|
fld_length= NULL; /* purecov: inspected */
|
|
}
|
|
|
|
sign_len= fld_type_modifier & UNSIGNED_FLAG ? 0 : 1;
|
|
|
|
switch (fld_type) {
|
|
case MYSQL_TYPE_TINY:
|
|
if (!fld_length)
|
|
length= MAX_TINYINT_WIDTH+sign_len;
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
break;
|
|
case MYSQL_TYPE_SHORT:
|
|
if (!fld_length)
|
|
length= MAX_SMALLINT_WIDTH+sign_len;
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
break;
|
|
case MYSQL_TYPE_INT24:
|
|
if (!fld_length)
|
|
length= MAX_MEDIUMINT_WIDTH+sign_len;
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
break;
|
|
case MYSQL_TYPE_LONG:
|
|
if (!fld_length)
|
|
length= MAX_INT_WIDTH+sign_len;
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
break;
|
|
case MYSQL_TYPE_LONGLONG:
|
|
if (!fld_length)
|
|
length= MAX_BIGINT_WIDTH;
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
break;
|
|
case MYSQL_TYPE_NULL:
|
|
break;
|
|
case MYSQL_TYPE_NEWDECIMAL:
|
|
my_decimal_trim(&length, &decimals);
|
|
if (length > DECIMAL_MAX_PRECISION)
|
|
{
|
|
my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
|
|
DECIMAL_MAX_PRECISION);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
if (length < decimals)
|
|
{
|
|
my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
length=
|
|
my_decimal_precision_to_length(length, decimals,
|
|
fld_type_modifier & UNSIGNED_FLAG);
|
|
pack_length=
|
|
my_decimal_get_binary_size(length, decimals);
|
|
break;
|
|
case MYSQL_TYPE_VARCHAR:
|
|
/*
|
|
Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table
|
|
if they don't have a default value
|
|
*/
|
|
max_field_charlength= MAX_FIELD_VARCHARLENGTH;
|
|
break;
|
|
case MYSQL_TYPE_STRING:
|
|
break;
|
|
case MYSQL_TYPE_BLOB:
|
|
case MYSQL_TYPE_TINY_BLOB:
|
|
case MYSQL_TYPE_LONG_BLOB:
|
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
case MYSQL_TYPE_GEOMETRY:
|
|
if (fld_default_value)
|
|
{
|
|
/* Allow empty as default value. */
|
|
String str,*res;
|
|
res= fld_default_value->val_str(&str);
|
|
/*
|
|
A default other than '' is always an error, and any non-NULL
|
|
specified default is an error in strict mode.
|
|
*/
|
|
if (res->length() || (thd->variables.sql_mode &
|
|
(MODE_STRICT_TRANS_TABLES |
|
|
MODE_STRICT_ALL_TABLES)))
|
|
{
|
|
my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0),
|
|
fld_name); /* purecov: inspected */
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Otherwise a default of '' is just a warning.
|
|
*/
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_BLOB_CANT_HAVE_DEFAULT,
|
|
ER(ER_BLOB_CANT_HAVE_DEFAULT),
|
|
fld_name);
|
|
}
|
|
def= 0;
|
|
}
|
|
flags|= BLOB_FLAG;
|
|
break;
|
|
case MYSQL_TYPE_YEAR:
|
|
if (!fld_length || length != 2)
|
|
length= 4; /* Default length */
|
|
flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
|
|
break;
|
|
case MYSQL_TYPE_FLOAT:
|
|
/* change FLOAT(precision) to FLOAT or DOUBLE */
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
if (fld_length && !fld_decimals)
|
|
{
|
|
uint tmp_length= length;
|
|
if (tmp_length > PRECISION_FOR_DOUBLE)
|
|
{
|
|
my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
else if (tmp_length > PRECISION_FOR_FLOAT)
|
|
{
|
|
sql_type= MYSQL_TYPE_DOUBLE;
|
|
length= MAX_DOUBLE_STR_LENGTH;
|
|
}
|
|
else
|
|
length= MAX_FLOAT_STR_LENGTH;
|
|
decimals= NOT_FIXED_DEC;
|
|
break;
|
|
}
|
|
if (!fld_length && !fld_decimals)
|
|
{
|
|
length= MAX_FLOAT_STR_LENGTH;
|
|
decimals= NOT_FIXED_DEC;
|
|
}
|
|
if (length < decimals &&
|
|
decimals != NOT_FIXED_DEC)
|
|
{
|
|
my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
break;
|
|
case MYSQL_TYPE_DOUBLE:
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
if (!fld_length && !fld_decimals)
|
|
{
|
|
length= DBL_DIG+7;
|
|
decimals= NOT_FIXED_DEC;
|
|
}
|
|
if (length < decimals &&
|
|
decimals != NOT_FIXED_DEC)
|
|
{
|
|
my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
break;
|
|
case MYSQL_TYPE_TIMESTAMP:
|
|
if (fld_length == NULL)
|
|
{
|
|
length= MAX_DATETIME_WIDTH;
|
|
}
|
|
else if (length != MAX_DATETIME_WIDTH)
|
|
{
|
|
/*
|
|
We support only even TIMESTAMP lengths less or equal than 14
|
|
and 19 as length of 4.1 compatible representation. Silently
|
|
shrink it to MAX_DATETIME_COMPRESSED_WIDTH.
|
|
*/
|
|
DBUG_ASSERT(MAX_DATETIME_COMPRESSED_WIDTH < UINT_MAX);
|
|
if (length != UINT_MAX) /* avoid overflow; is safe because of min() */
|
|
length= ((length+1)/2)*2;
|
|
length= min(length, MAX_DATETIME_COMPRESSED_WIDTH);
|
|
}
|
|
flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
|
|
/*
|
|
Since we silently rewrite down to MAX_DATETIME_COMPRESSED_WIDTH bytes,
|
|
the parser should not raise errors unless bizzarely large.
|
|
*/
|
|
max_field_charlength= UINT_MAX;
|
|
|
|
if (fld_default_value)
|
|
{
|
|
/* Grammar allows only NOW() value for ON UPDATE clause */
|
|
if (fld_default_value->type() == Item::FUNC_ITEM &&
|
|
((Item_func*)fld_default_value)->functype() == Item_func::NOW_FUNC)
|
|
{
|
|
unireg_check= (fld_on_update_value ? Field::TIMESTAMP_DNUN_FIELD:
|
|
Field::TIMESTAMP_DN_FIELD);
|
|
/*
|
|
We don't need default value any longer moreover it is dangerous.
|
|
Everything handled by unireg_check further.
|
|
*/
|
|
def= 0;
|
|
}
|
|
else
|
|
unireg_check= (fld_on_update_value ? Field::TIMESTAMP_UN_FIELD:
|
|
Field::NONE);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
If we have default TIMESTAMP NOT NULL column without explicit DEFAULT
|
|
or ON UPDATE values then for the sake of compatiblity we should treat
|
|
this column as having DEFAULT NOW() ON UPDATE NOW() (when we don't
|
|
have another TIMESTAMP column with auto-set option before this one)
|
|
or DEFAULT 0 (in other cases).
|
|
So here we are setting TIMESTAMP_OLD_FIELD only temporary, and will
|
|
replace this value by TIMESTAMP_DNUN_FIELD or NONE later when
|
|
information about all TIMESTAMP fields in table will be availiable.
|
|
|
|
If we have TIMESTAMP NULL column without explicit DEFAULT value
|
|
we treat it as having DEFAULT NULL attribute.
|
|
*/
|
|
unireg_check= (fld_on_update_value ? Field::TIMESTAMP_UN_FIELD :
|
|
(flags & NOT_NULL_FLAG ? Field::TIMESTAMP_OLD_FIELD :
|
|
Field::NONE));
|
|
}
|
|
break;
|
|
case MYSQL_TYPE_DATE:
|
|
/* Old date type. */
|
|
if (protocol_version != PROTOCOL_VERSION-1)
|
|
sql_type= MYSQL_TYPE_NEWDATE;
|
|
/* fall trough */
|
|
case MYSQL_TYPE_NEWDATE:
|
|
length= MAX_DATE_WIDTH;
|
|
break;
|
|
case MYSQL_TYPE_TIME:
|
|
length= 10;
|
|
break;
|
|
case MYSQL_TYPE_DATETIME:
|
|
length= MAX_DATETIME_WIDTH;
|
|
break;
|
|
case MYSQL_TYPE_SET:
|
|
{
|
|
pack_length= get_set_pack_length(fld_interval_list->elements);
|
|
|
|
List_iterator<String> it(*fld_interval_list);
|
|
String *tmp;
|
|
while ((tmp= it++))
|
|
interval_list.push_back(tmp);
|
|
/*
|
|
Set fake length to 1 to pass the below conditions.
|
|
Real length will be set in mysql_prepare_table()
|
|
when we know the character set of the column
|
|
*/
|
|
length= 1;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_ENUM:
|
|
{
|
|
/* Should be safe. */
|
|
pack_length= get_enum_pack_length(fld_interval_list->elements);
|
|
|
|
List_iterator<String> it(*fld_interval_list);
|
|
String *tmp;
|
|
while ((tmp= it++))
|
|
interval_list.push_back(tmp);
|
|
length= 1; /* See comment for MYSQL_TYPE_SET above. */
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
DBUG_ASSERT(0); /* Impossible. */
|
|
break;
|
|
case MYSQL_TYPE_BIT:
|
|
{
|
|
if (!fld_length)
|
|
length= 1;
|
|
if (length > MAX_BIT_FIELD_LENGTH)
|
|
{
|
|
my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name,
|
|
MAX_BIT_FIELD_LENGTH);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
pack_length= (length + 7) / 8;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_DECIMAL:
|
|
DBUG_ASSERT(0); /* Was obsolete */
|
|
}
|
|
/* Remember the value of length */
|
|
char_length= length;
|
|
|
|
if (!(flags & BLOB_FLAG) &&
|
|
((length > max_field_charlength && fld_type != MYSQL_TYPE_SET &&
|
|
fld_type != MYSQL_TYPE_ENUM &&
|
|
(fld_type != MYSQL_TYPE_VARCHAR || fld_default_value)) ||
|
|
((length == 0) &&
|
|
fld_type != MYSQL_TYPE_STRING &&
|
|
fld_type != MYSQL_TYPE_VARCHAR && fld_type != MYSQL_TYPE_GEOMETRY)))
|
|
{
|
|
my_error((fld_type == MYSQL_TYPE_VAR_STRING ||
|
|
fld_type == MYSQL_TYPE_VARCHAR ||
|
|
fld_type == MYSQL_TYPE_STRING) ? ER_TOO_BIG_FIELDLENGTH :
|
|
ER_TOO_BIG_DISPLAYWIDTH,
|
|
MYF(0),
|
|
fld_name, max_field_charlength); /* purecov: inspected */
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
fld_type_modifier&= AUTO_INCREMENT_FLAG;
|
|
if ((~allowed_type_modifier) & fld_type_modifier)
|
|
{
|
|
my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
|
|
switch (fld_type) {
|
|
case MYSQL_TYPE_DATE:
|
|
case MYSQL_TYPE_NEWDATE:
|
|
case MYSQL_TYPE_TIME:
|
|
case MYSQL_TYPE_DATETIME:
|
|
case MYSQL_TYPE_TIMESTAMP:
|
|
charset= &my_charset_bin;
|
|
flags|= BINCMP_FLAG;
|
|
default: break;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/*
|
|
Make a field from the .frm file info
|
|
*/
|
|
|
|
uint32 calc_pack_length(enum_field_types type,uint32 length)
|
|
{
|
|
switch (type) {
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING:
|
|
case MYSQL_TYPE_DECIMAL: return (length);
|
|
case MYSQL_TYPE_VARCHAR: return (length + (length < 256 ? 1: 2));
|
|
case MYSQL_TYPE_YEAR:
|
|
case MYSQL_TYPE_TINY : return 1;
|
|
case MYSQL_TYPE_SHORT : return 2;
|
|
case MYSQL_TYPE_INT24:
|
|
case MYSQL_TYPE_NEWDATE:
|
|
case MYSQL_TYPE_TIME: return 3;
|
|
case MYSQL_TYPE_TIMESTAMP:
|
|
case MYSQL_TYPE_DATE:
|
|
case MYSQL_TYPE_LONG : return 4;
|
|
case MYSQL_TYPE_FLOAT : return sizeof(float);
|
|
case MYSQL_TYPE_DOUBLE: return sizeof(double);
|
|
case MYSQL_TYPE_DATETIME:
|
|
case MYSQL_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */
|
|
case MYSQL_TYPE_NULL : return 0;
|
|
case MYSQL_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr;
|
|
case MYSQL_TYPE_BLOB: return 2+portable_sizeof_char_ptr;
|
|
case MYSQL_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr;
|
|
case MYSQL_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr;
|
|
case MYSQL_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr;
|
|
case MYSQL_TYPE_SET:
|
|
case MYSQL_TYPE_ENUM:
|
|
case MYSQL_TYPE_NEWDECIMAL:
|
|
abort(); return 0; // This shouldn't happen
|
|
case MYSQL_TYPE_BIT: return length / 8;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
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, uchar *ptr, uint32 field_length,
|
|
uchar *null_pos, uchar null_bit,
|
|
uint pack_flag,
|
|
enum_field_types field_type,
|
|
CHARSET_INFO *field_charset,
|
|
Field::geometry_type geom_type,
|
|
Field::utype unireg_check,
|
|
TYPELIB *interval,
|
|
const char *field_name)
|
|
{
|
|
uchar *UNINIT_VAR(bit_ptr);
|
|
uchar UNINIT_VAR(bit_offset);
|
|
if (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;
|
|
}
|
|
|
|
switch (field_type) {
|
|
case MYSQL_TYPE_DATE:
|
|
case MYSQL_TYPE_NEWDATE:
|
|
case MYSQL_TYPE_TIME:
|
|
case MYSQL_TYPE_DATETIME:
|
|
case MYSQL_TYPE_TIMESTAMP:
|
|
field_charset= &my_charset_bin;
|
|
default: break;
|
|
}
|
|
|
|
if (f_is_alpha(pack_flag))
|
|
{
|
|
if (!f_is_packed(pack_flag))
|
|
{
|
|
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 Field_string(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
field_charset);
|
|
if (field_type == MYSQL_TYPE_VARCHAR)
|
|
return new Field_varstring(ptr,field_length,
|
|
HA_VARCHAR_PACKLENGTH(field_length),
|
|
null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
share,
|
|
field_charset);
|
|
return 0; // Error
|
|
}
|
|
|
|
uint pack_length=calc_pack_length((enum_field_types)
|
|
f_packtype(pack_flag),
|
|
field_length);
|
|
|
|
#ifdef HAVE_SPATIAL
|
|
if (f_is_geom(pack_flag))
|
|
return new Field_geom(ptr,null_pos,null_bit,
|
|
unireg_check, field_name, share,
|
|
pack_length, geom_type);
|
|
#endif
|
|
if (f_is_blob(pack_flag))
|
|
return new 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 Field_enum(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
pack_length, interval, field_charset);
|
|
else
|
|
return new Field_set(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name,
|
|
pack_length, interval, field_charset);
|
|
}
|
|
}
|
|
|
|
switch (field_type) {
|
|
case MYSQL_TYPE_DECIMAL:
|
|
return new 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 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:
|
|
return new Field_float(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_DOUBLE:
|
|
return new Field_double(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_TINY:
|
|
return new 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 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 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 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:
|
|
return new 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:
|
|
return new Field_timestamp(ptr,field_length, null_pos, null_bit,
|
|
unireg_check, field_name, share,
|
|
field_charset);
|
|
case MYSQL_TYPE_YEAR:
|
|
return new Field_year(ptr,field_length,null_pos,null_bit,
|
|
unireg_check, field_name);
|
|
case MYSQL_TYPE_DATE:
|
|
return new Field_date(ptr,null_pos,null_bit,
|
|
unireg_check, field_name, field_charset);
|
|
case MYSQL_TYPE_NEWDATE:
|
|
return new Field_newdate(ptr,null_pos,null_bit,
|
|
unireg_check, field_name, field_charset);
|
|
case MYSQL_TYPE_TIME:
|
|
return new Field_time(ptr,null_pos,null_bit,
|
|
unireg_check, field_name, field_charset);
|
|
case MYSQL_TYPE_DATETIME:
|
|
return new Field_datetime(ptr,null_pos,null_bit,
|
|
unireg_check, field_name, field_charset);
|
|
case MYSQL_TYPE_NULL:
|
|
return new Field_null(ptr, field_length, unireg_check, field_name,
|
|
field_charset);
|
|
case MYSQL_TYPE_BIT:
|
|
return f_bit_as_char(pack_flag) ?
|
|
new Field_bit_as_char(ptr, field_length, null_pos, null_bit,
|
|
unireg_check, field_name) :
|
|
new Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr,
|
|
bit_offset, unireg_check, field_name);
|
|
|
|
default: // Impossible (Wrong version)
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Create a field suitable for create of table. */
|
|
|
|
Create_field::Create_field(Field *old_field,Field *orig_field)
|
|
{
|
|
field= old_field;
|
|
field_name=change=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();
|
|
sql_type= old_field->real_type();
|
|
charset= old_field->charset(); // May be NULL ptr
|
|
comment= old_field->comment;
|
|
decimals= old_field->decimals();
|
|
|
|
/* Fix if the original table had 4 byte pointer blobs */
|
|
if (flags & BLOB_FLAG)
|
|
pack_length= (pack_length- old_field->table->s->blob_ptr_size +
|
|
portable_sizeof_char_ptr);
|
|
|
|
switch (sql_type) {
|
|
case MYSQL_TYPE_BLOB:
|
|
switch (pack_length - portable_sizeof_char_ptr) {
|
|
case 1: sql_type= MYSQL_TYPE_TINY_BLOB; break;
|
|
case 2: sql_type= MYSQL_TYPE_BLOB; break;
|
|
case 3: sql_type= MYSQL_TYPE_MEDIUM_BLOB; break;
|
|
default: sql_type= MYSQL_TYPE_LONG_BLOB; break;
|
|
}
|
|
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)
|
|
sql_type= MYSQL_TYPE_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;
|
|
break;
|
|
#ifdef HAVE_SPATIAL
|
|
case MYSQL_TYPE_GEOMETRY:
|
|
geom_type= ((Field_geom*)old_field)->geom_type;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (flags & (ENUM_FLAG | SET_FLAG))
|
|
interval= ((Field_enum*) old_field)->typelib;
|
|
else
|
|
interval=0;
|
|
def=0;
|
|
char_length= length;
|
|
|
|
if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
|
|
old_field->ptr && orig_field &&
|
|
(sql_type != MYSQL_TYPE_TIMESTAMP || /* set def only if */
|
|
old_field->table->timestamp_field != old_field || /* timestamp field */
|
|
unireg_check == Field::TIMESTAMP_UN_FIELD)) /* has default val */
|
|
{
|
|
char buff[MAX_FIELD_WIDTH];
|
|
String tmp(buff,sizeof(buff), charset);
|
|
my_ptrdiff_t diff;
|
|
|
|
/* Get the value from default_values */
|
|
diff= (my_ptrdiff_t) (orig_field->table->s->default_values-
|
|
orig_field->table->record[0]);
|
|
orig_field->move_field_offset(diff); // Points now at default_values
|
|
if (!orig_field->is_real_null())
|
|
{
|
|
char buff[MAX_FIELD_WIDTH], *pos;
|
|
String tmp(buff, sizeof(buff), charset), *res;
|
|
res= orig_field->val_str(&tmp);
|
|
pos= (char*) sql_strmake(res->ptr(), res->length());
|
|
def= new Item_string(pos, res->length(), charset);
|
|
}
|
|
orig_field->move_field_offset(-diff); // Back to record[0]
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
maximum possible display length for blob.
|
|
|
|
@return
|
|
length
|
|
*/
|
|
|
|
uint32 Field_blob::max_display_length()
|
|
{
|
|
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) 4294967295U;
|
|
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 cuted_increment - whenever we should increase cut fields count or not
|
|
|
|
@note
|
|
This function won't produce warning and increase cut fields counter
|
|
if count_cuted_fields == CHECK_FIELD_IGNORE for current thread.
|
|
|
|
if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes.
|
|
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(MYSQL_ERROR::enum_warning_level level, uint code,
|
|
int cuted_increment)
|
|
{
|
|
/*
|
|
If this field was created only for type conversion purposes it
|
|
will have table == NULL.
|
|
*/
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
if (thd->count_cuted_fields)
|
|
{
|
|
thd->cuted_fields+= cuted_increment;
|
|
push_warning_printf(thd, level, code, ER(code), field_name,
|
|
thd->row_count);
|
|
return 0;
|
|
}
|
|
return level >= MYSQL_ERROR::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 str_length length of string 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.
|
|
*/
|
|
|
|
void
|
|
Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code,
|
|
const char *str, uint str_length,
|
|
timestamp_type ts_type, int cuted_increment)
|
|
{
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
if ((thd->really_abort_on_warning() &&
|
|
level >= MYSQL_ERROR::WARN_LEVEL_WARN) ||
|
|
set_warning(level, code, cuted_increment))
|
|
make_truncated_value_warning(thd, level, str, str_length, ts_type,
|
|
field_name);
|
|
}
|
|
|
|
|
|
/**
|
|
Produce warning or note about integer datetime value saved into field.
|
|
|
|
@param level level of message (Note/Warning/Error)
|
|
@param code error code of message to be produced
|
|
@param nr numeric 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.
|
|
*/
|
|
|
|
void
|
|
Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code,
|
|
longlong nr, timestamp_type ts_type,
|
|
int cuted_increment)
|
|
{
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
if (thd->really_abort_on_warning() ||
|
|
set_warning(level, code, cuted_increment))
|
|
{
|
|
char str_nr[22];
|
|
char *str_end= longlong10_to_str(nr, str_nr, -10);
|
|
make_truncated_value_warning(thd, level, str_nr, (uint) (str_end - str_nr),
|
|
ts_type, field_name);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Produce warning or note about double datetime data saved into field.
|
|
|
|
@param level level of message (Note/Warning/Error)
|
|
@param code error code of message to be produced
|
|
@param nr double value which we tried to save
|
|
@param ts_type type of datetime value (datetime/date/time)
|
|
|
|
@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.
|
|
*/
|
|
|
|
void
|
|
Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code,
|
|
double nr, timestamp_type ts_type)
|
|
{
|
|
THD *thd= table ? table->in_use : current_thd;
|
|
if (thd->really_abort_on_warning() ||
|
|
set_warning(level, code, 1))
|
|
{
|
|
/* DBL_DIG is enough to print '-[digits].E+###' */
|
|
char str_nr[DBL_DIG + 8];
|
|
uint str_len= sprintf(str_nr, "%g", nr);
|
|
make_truncated_value_warning(thd, level, str_nr, str_len, ts_type,
|
|
field_name);
|
|
}
|
|
}
|