diff options
Diffstat (limited to 'sql/field.cc')
-rw-r--r-- | sql/field.cc | 11730 |
1 files changed, 11730 insertions, 0 deletions
diff --git a/sql/field.cc b/sql/field.cc new file mode 100644 index 00000000..e94d5c19 --- /dev/null +++ b/sql/field.cc @@ -0,0 +1,11730 @@ +/* + Copyright (c) 2000, 2017, Oracle and/or its affiliates. + Copyright (c) 2008, 2022, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA +*/ + +/** + @file + + @brief + This file implements classes defined in field.h +*/ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include "mariadb.h" +#include "sql_priv.h" +#include "sql_select.h" +#include "rpl_rli.h" // Pull in Relay_log_info +#include "slave.h" // Pull in rpl_master_has_bug() +#include "strfunc.h" // find_type2, find_set +#include "tztime.h" // struct Time_zone +#include "filesort.h" // change_double_for_sort +#include "log_event.h" // class Table_map_log_event +#include <m_ctype.h> + +// Maximum allowed exponent value for converting string to decimal +#define MAX_EXPONENT 1024 + +/***************************************************************************** + Instantiate templates and static variables +*****************************************************************************/ + +static const char *zero_timestamp="0000-00-00 00:00:00.000000"; +LEX_CSTRING temp_lex_str= {STRING_WITH_LEN("temp")}; + +uchar Field_null::null[1]={1}; +const char field_separator=','; + +#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE FLOATING_POINT_BUFFER +#define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128 +#define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128 +#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \ + ((ulong) ((1LL << MY_MIN(arg, 4) * 8) - 1)) + +// Column marked for read or the field set to read out of record[0] +bool Field::marked_for_read() const +{ + return !table || + (!table->read_set || + bitmap_is_set(table->read_set, field_index) || + (!(ptr >= table->record[0] && + ptr < table->record[0] + table->s->reclength))); +} + +/* + The name of this function is a bit misleading as in 10.4 we don't + have to test anymore if the field is computed. Instead we mark + changed fields with DBUG_FIX_WRITE_SET() in table.cc +*/ + +bool Field::marked_for_write_or_computed() const +{ + return (!table || + (!table->write_set || + bitmap_is_set(table->write_set, field_index) || + (!(ptr >= table->record[0] && + ptr < table->record[0] + table->s->reclength)))); +} + + +#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "") + +/* + Rules for merging different types of fields in UNION + + NOTE: to avoid 256*256 table, gap in table types numeration is skipped + following #defines describe that gap and how to canculate number of fields + and index of field in this array. +*/ +const int FIELDTYPE_TEAR_FROM= (MYSQL_TYPE_BIT + 1); +const int FIELDTYPE_TEAR_TO= (MYSQL_TYPE_NEWDECIMAL - 1); +const int FIELDTYPE_LAST= 254; +const int FIELDTYPE_NUM= FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST - + FIELDTYPE_TEAR_TO); + +static inline int merge_type2index(enum_field_types merge_type) +{ + DBUG_ASSERT(merge_type < FIELDTYPE_TEAR_FROM || + merge_type > FIELDTYPE_TEAR_TO); + DBUG_ASSERT(merge_type <= FIELDTYPE_LAST); + if (merge_type < FIELDTYPE_TEAR_FROM) + return merge_type; + return FIELDTYPE_TEAR_FROM + (merge_type - FIELDTYPE_TEAR_TO) - 1; +} + + +/** + Implements data type merge rules for the built-in traditional data types. + Used for operations such as: + - UNION + - CASE and its abbreviations COALESCE, IF, IFNULL + - LEAST/GREATEST + + Given Fields A and B of real_types a and b, we find the result type of + COALESCE(A, B) by querying: + field_types_merge_rules[field_type_to_index(a)][field_type_to_index(b)]. +*/ +static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= +{ + /* MYSQL_TYPE_DECIMAL -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_TINY -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_TINY, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_SHORT -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_SHORT, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_SHORT, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_SHORT, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_LONG -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_LONG, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_LONG, MYSQL_TYPE_LONG, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_LONG, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONG, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_LONG, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_FLOAT -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_FLOAT, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_FLOAT, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_FLOAT, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_FLOAT, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_DOUBLE -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_NULL -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONGLONG, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_TIME, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_BIT, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_ENUM, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_TIMESTAMP -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_TIMESTAMP, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_LONGLONG -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_LONGLONG, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONGLONG, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONGLONG, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_LONGLONG, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_INT24 -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_INT24, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_INT24, MYSQL_TYPE_LONG, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_INT24, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_INT24, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_DATE -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_TIME -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIME, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_DATETIME -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_YEAR -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_YEAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_YEAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_NEWDATE -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_VARCHAR -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_VARCHAR + }, + /* MYSQL_TYPE_BIT -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_BIT, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_BIT, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_NEWDECIMAL -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_NEWDECIMAL, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_ENUM -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_ENUM, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_SET -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_SET, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + }, + /* MYSQL_TYPE_TINY_BLOB -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_STRING + MYSQL_TYPE_TINY_BLOB + }, + /* MYSQL_TYPE_MEDIUM_BLOB -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, + //MYSQL_TYPE_STRING + MYSQL_TYPE_MEDIUM_BLOB + }, + /* MYSQL_TYPE_LONG_BLOB -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_STRING + MYSQL_TYPE_LONG_BLOB + }, + /* MYSQL_TYPE_BLOB -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_BLOB, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, + //MYSQL_TYPE_STRING + MYSQL_TYPE_BLOB + }, + /* MYSQL_TYPE_VAR_STRING -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_VARCHAR + }, + /* MYSQL_TYPE_STRING -> */ + { + //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY + MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, + //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG + MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, + //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE + MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, + //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, + //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 + MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, + //MYSQL_TYPE_DATE MYSQL_TYPE_TIME + MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, + //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR + MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, + //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR + MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_BIT <16>-<245> + MYSQL_TYPE_STRING, + //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM + MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, + //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_STRING, MYSQL_TYPE_TINY_BLOB, + //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, + //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING + } +}; + + +const Type_handler * +Type_handler::aggregate_for_result_traditional(const Type_handler *a, + const Type_handler *b) +{ + if (a == b) + { + /* + If two traditional handlers are equal, quickly return "a". + Some handlers (e.g. Type_handler_bool) pretend to be traditional, + but in fact they are not traditional in full extent, they are + only sub-types for now (and don't have a corresponding Field_xxx yet). + Here we preserve such handlers during aggregation. + As a result, COALESCE(true,true) preserves the "boolean" data type. + + Need to do this conversion for deprecated data types, + similar to what field_type_merge_rules[][] does. + */ + switch (a->field_type()) { + case MYSQL_TYPE_DECIMAL: return &type_handler_newdecimal; + case MYSQL_TYPE_DATE: return &type_handler_newdate; + case MYSQL_TYPE_VAR_STRING: return &type_handler_varchar; + default: break; + } + return a; + } + enum_field_types ta= a->traditional_merge_field_type(); + enum_field_types tb= b->traditional_merge_field_type(); + enum_field_types res= field_types_merge_rules[merge_type2index(ta)] + [merge_type2index(tb)]; + return Type_handler::get_handler_by_real_type(res); +} + + +bool Field::check_assignability_from(const Type_handler *from, + bool ignore) const +{ + /* + Using type_handler_for_item_field() here to get the data type handler + on both sides. This is needed to make sure aggregation for Field + works the same way with how Item_field aggregates for UNION or CASE, + so these statements: + SELECT a FROM t1 UNION SELECT b FROM t1; // Item_field vs Item_field + UPDATE t1 SET a=b; // Field vs Item_field + either both return "Illegal parameter data types" or both pass + the data type compatibility test. + For MariaDB standard data types, using type_handler_for_item_field() + turns ENUM/SET into just CHAR. + */ + Type_handler_hybrid_field_type th(type_handler()-> + type_handler_for_item_field()); + if (th.aggregate_for_result(from->type_handler_for_item_field())) + { + bool error= (!ignore && get_thd()->is_strict_mode()) || + (type_handler()->is_scalar_type() != from->is_scalar_type()); + /* + Display fully qualified column name for table columns. + Display non-qualified names for other things, + e.g. SP variables, SP return values, SP and CURSOR parameters. + */ + if (table->s->db.str && table->s->table_name.str) + my_printf_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, + "Cannot cast '%s' as '%s' in assignment of %`s.%`s.%`s", + MYF(error ? 0 : ME_WARNING), + from->name().ptr(), type_handler()->name().ptr(), + table->s->db.str, table->s->table_name.str, + field_name.str); + else + my_printf_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, + "Cannot cast '%s' as '%s' in assignment of %`s", + MYF(error ? 0 : ME_WARNING), + from->name().ptr(), type_handler()->name().ptr(), + field_name.str); + return error; + } + return false; +} + + +/* + Test if the given string contains important data: + not spaces for character string, + or any data for binary string. + + SYNOPSIS + test_if_important_data() + cs Character set + str String to test + strend String end + + RETURN + FALSE - If string does not have important data + TRUE - If string has some important data +*/ + +static bool +test_if_important_data(CHARSET_INFO *cs, const char *str, const char *strend) +{ + if (cs != &my_charset_bin) + str+= cs->scan(str, strend, MY_SEQ_SPACES); + return (str < strend); +} + + +/** + Function to compare two unsigned integers for their relative order. + Used below. In an anonymous namespace to not clash with definitions + in other files. + */ + +CPP_UNNAMED_NS_START + +int compare(unsigned int a, unsigned int b) +{ + if (a < b) + return -1; + if (b < a) + return 1; + return 0; +} + +CPP_UNNAMED_NS_END + + +/***************************************************************************** + Static help functions +*****************************************************************************/ + +/* + @brief + Create a fixed size sort key part + + @param buff buffer where values are written + @param length fixed size of the sort column +*/ + +void Field::make_sort_key_part(uchar *buff,uint length) +{ + if (maybe_null()) + { + if (is_null()) + { + bzero(buff, length + 1); + return; + } + *buff++= 1; + } + sort_string(buff, length); +} + + +/* + @brief + Create a packed sort key part + + @param buff buffer where values are written + @param sort_field sort column structure + + @retval + length of the bytes written, does not include the NULL bytes +*/ +uint +Field::make_packed_sort_key_part(uchar *buff, + const SORT_FIELD_ATTR *sort_field) +{ + if (maybe_null()) + { + if (is_null()) + { + *buff++= 0; + return 0; // For NULL values don't write any data + } + *buff++=1; + } + sort_string(buff, sort_field->original_length); + return sort_field->original_length; +} + + +uint +Field_longstr::make_packed_sort_key_part(uchar *buff, + const SORT_FIELD_ATTR *sort_field) +{ + if (maybe_null()) + { + if (is_null()) + { + *buff++= 0; + return 0; // For NULL values don't write any data + } + *buff++=1; + } + uchar *end= pack_sort_string(buff, sort_field); + return (uint) (end-buff); +} + + +uchar* +Field_longstr::pack_sort_string(uchar *to, const SORT_FIELD_ATTR *sort_field) +{ + StringBuffer<LONGLONG_BUFFER_SIZE+1> buf; + val_str(&buf, &buf); + return to + sort_field->pack_sort_string(to, &buf, field_charset()); +} + + +/** + @brief + Determine the relative position of the field value in a numeric interval + + @details + The function returns a double number between 0.0 and 1.0 as the relative + position of the value of the this field in the numeric interval of [min,max]. + If the value is not in the interval the the function returns 0.0 when + the value is less than min, and, 1.0 when the value is greater than max. + + @param min value of the left end of the interval + @param max value of the right end of the interval + + @return + relative position of the field value in the numeric interval [min,max] +*/ + +double pos_in_interval_for_double(double midp_val, double min_val, + double max_val) +{ + double n, d; + n= midp_val - min_val; + if (n < 0) + return 0.0; + d= max_val - min_val; + if (d <= 0) + return 1.0; + return MY_MIN(n/d, 1.0); +} + + +double Field::pos_in_interval_val_real(Field *min, Field *max) +{ + return pos_in_interval_for_double(val_real(), min->val_real(), + max->val_real()); +} + + +static +inline ulonglong char_prefix_to_ulonglong(uchar *src) +{ + uint sz= sizeof(ulonglong); + for (uint i= 0; i < sz/2; i++) + { + uchar tmp= src[i]; + src[i]= src[sz-1-i]; + src[sz-1-i]= tmp; + } + return uint8korr(src); +} + +/* + Compute res = a - b, without losing precision and taking care that these are + unsigned numbers. +*/ +static inline double safe_substract(ulonglong a, ulonglong b) +{ + return (a > b)? double(a - b) : -double(b - a); +} + + +/** + @brief + Determine the relative position of the field value in a string interval + + @details + The function returns a double number between 0.0 and 1.0 as the relative + position of the value of the this field in the string interval of [min,max]. + If the value is not in the interval the the function returns 0.0 when + the value is less than min, and, 1.0 when the value is greater than max. + + @note + To calculate the relative position of the string value v in the interval + [min, max] the function first converts the beginning of these three + strings v, min, max into the strings that are used for byte comparison. + For each string not more sizeof(ulonglong) first bytes are taken + from the result of conversion. Then these bytes are interpreted as the + big-endian representation of an ulonglong integer. The values of these + integer numbers obtained for the strings v, min, max are used to calculate + the position of v in [min,max] in the same way is it's done for numeric + fields (see Field::pos_in_interval_val_real). + + @todo + Improve the procedure for the case when min and max have the same + beginning + + @param min value of the left end of the interval + @param max value of the right end of the interval + + @return + relative position of the field value in the string interval [min,max] +*/ + +double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset) +{ + return pos_in_interval_for_string(charset(), + ptr + data_offset, data_length(), + min->ptr + data_offset, min->data_length(), + max->ptr + data_offset, max->data_length() + ); +} + + +double pos_in_interval_for_string(CHARSET_INFO *cset, + const uchar *midp_val, uint32 midp_len, + const uchar *min_val, uint32 min_len, + const uchar *max_val, uint32 max_len) +{ + uchar mp_prefix[sizeof(ulonglong)]; + uchar minp_prefix[sizeof(ulonglong)]; + uchar maxp_prefix[sizeof(ulonglong)]; + ulonglong mp, minp, maxp; + + cset->strnxfrm(mp_prefix, sizeof(mp), midp_val, midp_len); + cset->strnxfrm(minp_prefix, sizeof(minp), min_val, min_len); + cset->strnxfrm(maxp_prefix, sizeof(maxp), max_val, max_len); + + mp= char_prefix_to_ulonglong(mp_prefix); + minp= char_prefix_to_ulonglong(minp_prefix); + maxp= char_prefix_to_ulonglong(maxp_prefix); + + double n, d; + n= safe_substract(mp, minp); + if (n < 0) + return 0.0; + d= safe_substract(maxp, minp); + if (d <= 0) + return 1.0; + return MY_MIN(n/d, 1.0); +} + + +bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const +{ + DBUG_ASSERT(cmp_type() != STRING_RESULT); // For STRING_RESULT see Field_str + /* + We use result_type() rather than cmp_type() in the below condition, + because it covers a special case that string literals guarantee uniqueness + for temporal columns, so the query: + WHERE temporal_column='string' + cannot return multiple distinct temporal values. + + TODO: perhaps we could allow INT/DECIMAL/DOUBLE types for temporal items. + */ + return result_type() == item->result_type(); +} + + +/** + Check whether a field item can be substituted for an equal item + + @details + The function checks whether a substitution of a field item for + an equal item is valid. + + @param arg *arg != NULL <-> the field is in the context + where substitution for an equal item is valid + + @note + The following statement is not always true: + @n + x=y => F(x)=F(x/y). + @n + This means substitution of an item for an equal item not always + yields an equavalent condition. Here's an example: + @code + 'a'='a ' + (LENGTH('a')=1) != (LENGTH('a ')=2) + @endcode + Such a substitution is surely valid if either the substituted + field is not of a STRING type or if it is an argument of + a comparison predicate. + + @retval + TRUE substitution is valid + @retval + FALSE otherwise +*/ + +bool Field::can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal) +{ + DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT); + DBUG_ASSERT(cmp_type() != STRING_RESULT); + switch (ctx.subst_constraint()) { + case ANY_SUBST: + /* + Disable const propagation for items used in different comparison contexts. + This must be done because, for example, Item_hex_string->val_int() is not + the same as (Item_hex_string->val_str() in BINARY column)->val_int(). + We cannot simply disable the replacement in a particular context ( + e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since + Items don't know the context they are in and there are functions like + IF (<hex_string>, 'yes', 'no'). + */ + return ctx.compare_type_handler() == item_equal->compare_type_handler(); + case IDENTITY_SUBST: + return true; + } + return false; +} + + +bool Field::cmp_is_done_using_type_handler_of_this(const Item_bool_func *cond, + const Item *item) const +{ + /* + We could eventually take comparison_type_handler() from cond, + instead of calculating it again. But only some descendants of + Item_bool_func has this method. So this needs some hierarchy changes. + Another option is to pass "class Context" to this method. + */ + Type_handler_hybrid_field_type cmp(type_handler_for_comparison()); + return !cmp.aggregate_for_comparison(item->type_handler_for_comparison()) && + cmp.type_handler() == type_handler_for_comparison(); +} + + +/* + This handles all numeric and BIT data types. +*/ +Data_type_compatibility +Field::can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const +{ + DBUG_ASSERT(cmp_type() != STRING_RESULT); + DBUG_ASSERT(cmp_type() != TIME_RESULT); + return item->cmp_type() != TIME_RESULT ? + Data_type_compatibility::OK : + Data_type_compatibility::INCOMPATIBLE_DATA_TYPE; +} + + +/* + This handles all numeric and BIT data types. +*/ +Data_type_compatibility +Field::can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const +{ + DBUG_ASSERT(cmp_type() != STRING_RESULT); + DBUG_ASSERT(cmp_type() != TIME_RESULT); + return const_item->cmp_type() != TIME_RESULT ? + Data_type_compatibility::OK : + Data_type_compatibility::INCOMPATIBLE_DATA_TYPE; +} + + +/* + This covers all numeric types, BIT +*/ +Data_type_compatibility +Field::can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const +{ + DBUG_ASSERT(cmp_type() != TIME_RESULT); // Handled in Field_temporal + DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants + return item->cmp_type() != TIME_RESULT ? + Data_type_compatibility::OK : + Data_type_compatibility::INCOMPATIBLE_DATA_TYPE; +} + + +int Field::store_hex_hybrid(const char *str, size_t length) +{ + DBUG_ASSERT(result_type() != STRING_RESULT); + ulonglong nr; + + if (length > 8) + { + nr= flags & UNSIGNED_FLAG ? ULONGLONG_MAX : LONGLONG_MAX; + goto warn; + } + nr= (ulonglong) longlong_from_hex_hybrid(str, length); + if ((length == 8) && !(flags & UNSIGNED_FLAG) && (nr > LONGLONG_MAX)) + { + nr= LONGLONG_MAX; + goto warn; + } + return store((longlong) nr, true); // Assume hex numbers are unsigned + +warn: + if (!store((longlong) nr, true)) + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + return 1; +} + + +/** + If a field does not have a corresponding data, it's behavior can vary: + - In case of the fixed file format + it's set to the default value for the data type, + such as 0 for numbers or '' for strings. + - In case of a non-fixed format + it's set to NULL for nullable fields, and + it's set to the default value for the data type for NOT NULL fields. + This seems to be by design. +*/ +bool Field::load_data_set_no_data(THD *thd, bool fixed_format) +{ + reset(); // Do not use the DEFAULT value + if (fixed_format) + { + set_notnull(); + /* + We're loading a fixed format file, e.g.: + LOAD DATA INFILE 't1.txt' INTO TABLE t1 FIELDS TERMINATED BY ''; + Suppose the file ended unexpectedly and no data was provided for an + auto-increment column in the current row. + Historically, if sql_mode=NO_AUTO_VALUE_ON_ZERO, then the column value + is set to 0 in such case (the next auto_increment value is not used). + This behaviour was introduced by the fix for "bug#12053" in mysql-4.1. + Note, loading a delimited file works differently: + "no data" is not converted to 0 on NO_AUTO_VALUE_ON_ZERO: + it's considered as equal to setting the column to NULL, + which is then replaced to the next auto_increment value. + This difference seems to be intentional. + */ + if (this == table->next_number_field) + table->auto_increment_field_not_null= true; + } + set_has_explicit_value(); // Do not auto-update this field + return false; +} + + +bool Field::load_data_set_null(THD *thd) +{ + reset(); + set_null(); + if (!maybe_null()) + { + if (this != table->next_number_field) + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_NULL_TO_NOTNULL, 1); + } + set_has_explicit_value(); // Do not auto-update this field + return false; +} + + +void Field::load_data_set_value(const char *pos, uint length, + CHARSET_INFO *cs) +{ + /* + Mark field as not null, we should do this for each row because of + restore_record... + */ + set_notnull(); + if (this == table->next_number_field) + table->auto_increment_field_not_null= true; + store(pos, length, cs); + set_has_explicit_value(); // Do not auto-update this field +} + + +bool Field::sp_prepare_and_store_item(THD *thd, Item **value) +{ + DBUG_ENTER("Field::sp_prepare_and_store_item"); + DBUG_ASSERT(value); + + Item *expr_item; + + if (!(expr_item= thd->sp_fix_func_item_for_assignment(this, value))) + goto error; + + /* Save the value in the field. Convert the value if needed. */ + + expr_item->save_in_field(this, 0); + + if (likely(!thd->is_error())) + DBUG_RETURN(false); + +error: + /* + In case of error during evaluation, leave the result field set to NULL. + Sic: we can't do it in the beginning of the function because the + result field might be needed for its own re-evaluation, e.g. case of + set x = x + 1; + */ + set_null(); + DBUG_ASSERT(thd->is_error()); + DBUG_RETURN(true); +} + + +void Field::error_generated_column_function_is_not_allowed(THD *thd, + bool error) const +{ + StringBuffer<64> tmp; + vcol_info->expr->print(&tmp, (enum_query_type) + (QT_TO_SYSTEM_CHARSET | + QT_ITEM_IDENT_SKIP_DB_NAMES | + QT_ITEM_IDENT_SKIP_TABLE_NAMES)); + my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, + MYF(error ? 0 : ME_WARNING), + tmp.c_ptr_safe(), vcol_info->get_vcol_type_name(), + const_cast<const char*>(field_name.str)); +} + + +/* + Check if an indexed or a persistent virtual column depends on sql_mode flags + that it cannot handle. + See sql_mode.h for details. +*/ +bool Field::check_vcol_sql_mode_dependency(THD *thd, vcol_init_mode mode) const +{ + DBUG_ASSERT(vcol_info); + if ((flags & PART_KEY_FLAG) != 0 || stored_in_db()) + { + Sql_mode_dependency valdep= vcol_info->expr->value_depends_on_sql_mode(); + sql_mode_t cnvdep= conversion_depends_on_sql_mode(thd, vcol_info->expr); + Sql_mode_dependency dep= + (valdep | Sql_mode_dependency(0, cnvdep)) & + Sql_mode_dependency(~0, ~can_handle_sql_mode_dependency_on_store()); + if (dep) + { + bool error= (mode & VCOL_INIT_DEPENDENCY_FAILURE_IS_ERROR) != 0; + error_generated_column_function_is_not_allowed(thd, error); + dep.push_dependency_warnings(thd); + return error; + } + } + return false; +} + + +bool Field::make_empty_rec_store_default_value(THD *thd, Item *item) +{ + DBUG_ASSERT(!(flags & BLOB_FLAG)); + int res= item->save_in_field(this, true); + return res != 0 && res != 3; +} + + +/** + Numeric fields base class constructor. +*/ +Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, + decimal_digits_t dec_arg, bool zero_arg, bool unsigned_arg) + :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg), + dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg) +{ + if (zerofill) + flags|=ZEROFILL_FLAG; + if (unsigned_flag) + flags|=UNSIGNED_FLAG; +} + + +void Field_num::prepend_zeros(String *value) const +{ + int diff; + if ((diff= (int) (field_length - value->length())) > 0) + { + const bool error= value->realloc(field_length); + if (likely(!error)) + { + bmove_upp((uchar*) value->ptr()+field_length, + (uchar*) value->ptr()+value->length(), + value->length()); + bfill((uchar*) value->ptr(),diff,'0'); + value->length(field_length); + } + } +} + + +sql_mode_t Field_num::can_handle_sql_mode_dependency_on_store() const +{ + return MODE_PAD_CHAR_TO_FULL_LENGTH; +} + + +Item *Field_num::get_equal_zerofill_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + switch (ctx.subst_constraint()) { + case IDENTITY_SUBST: + return NULL; // Not safe to propagate if not in comparison. See MDEV-8369. + case ANY_SUBST: + break; + } + DBUG_ASSERT(const_item->const_item()); + DBUG_ASSERT(ctx.compare_type_handler()->cmp_type() != STRING_RESULT); + return const_item; +} + + +/** +Construct warning parameters using thd->no_errors + to determine whether to generate or suppress warnings. + We can get here in a query like this: + SELECT COUNT(@@basedir); + from Item_func_get_system_var::update_null_value(). +*/ +Value_source::Warn_filter::Warn_filter(const THD *thd) + :m_want_warning_edom(!thd->no_errors), + m_want_note_truncated_spaces(!thd->no_errors) +{ } + + +/** + Check string-to-number conversion and produce a warning if + - could not convert any digits (EDOM-alike error) + - found garbage at the end of the string + - found trailing spaces (a note) + See also Field_num::check_edom_and_truncation() for a similar function. + + @param thd - the thread + @param filter - which warnings/notes are allowed + @param type - name of the data type (e.g. "INTEGER", "DECIMAL", "DOUBLE") + @param cs - character set of the original string + @param str - the original string + @param end - the end of the string + + Unlike Field_num::check_edom_and_truncation(), this function does not + distinguish between EDOM and truncation and reports the same warning for + both cases. Perhaps we should eventually print different warnings, to make + the explicit CAST work closer to the implicit cast in Field_xxx::store(). +*/ +void +Value_source::Converter_string_to_number::check_edom_and_truncation(THD *thd, + Warn_filter filter, + const char *type, + CHARSET_INFO *cs, + const char *str, + size_t length) const +{ + DBUG_ASSERT(str <= m_end_of_num); + DBUG_ASSERT(m_end_of_num <= str + length); + if (m_edom || (m_end_of_num < str + length && + !check_if_only_end_space(cs, m_end_of_num, str + length))) + { + // EDOM or important trailing data truncation + if (filter.want_warning_edom()) + { + /* + We can use err.ptr() here as ErrConvString is guaranteed to put an + end \0 here. + */ + THD *wthd= thd ? thd : current_thd; + push_warning_printf(wthd, Sql_condition::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type, + ErrConvString(str, length, cs).ptr()); + } + } + else if (m_end_of_num < str + length) + { + // Unimportant trailing data (spaces) truncation + if (filter.want_note_truncated_spaces()) + { + THD *wthd= thd ? thd : current_thd; + push_warning_printf(wthd, Sql_condition::WARN_LEVEL_NOTE, + ER_TRUNCATED_WRONG_VALUE, + ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type, + ErrConvString(str, length, cs).ptr()); + } + } +} + + +/** + Check a string-to-number conversion routine result and generate warnings + in case when it: + - could not convert any digits + - found garbage at the end of the string. + + @param type Data type name (e.g. "decimal", "integer", "double") + @param edom Indicates that the string-to-number routine returned + an error code equivalent to EDOM (value out of domain), + i.e. the string fully consisted of garbage and the + conversion routine could not get any digits from it. + @param str The original string + @param length Length of 'str' + @param cs Character set + @param end Pointer to char after last used digit + + @note + This is called after one has called one of the following functions: + - strntoull10rnd() + - strntod() + - str2my_decimal() + + @retval + 0 OK + @retval + 1 error: could not scan any digits (EDOM), + e.g. empty string, or garbage. + @retval + 2 error: scanned some digits, + but then found garbage at the end of the string. +*/ + + +int Field_num::check_edom_and_important_data_truncation(const char *type, + bool edom, + CHARSET_INFO *cs, + const char *str, size_t length, + const char *end) +{ + /* Test if we get an empty string or garbage */ + if (edom) + { + ErrConvString err(str, length, cs); + set_warning_truncated_wrong_value(type, err.ptr()); + return 1; + } + /* Test if we have garbage at the end of the given string. */ + if (test_if_important_data(cs, end, str + length)) + { + set_warning(WARN_DATA_TRUNCATED, 1); + return 2; + } + return 0; +} + + +int Field_num::check_edom_and_truncation(const char *type, bool edom, + CHARSET_INFO *cs, + const char *str, size_t length, + const char *end) +{ + int rc= check_edom_and_important_data_truncation(type, edom, + cs, str, length, end); + if (!rc && end < str + length) + set_note(WARN_DATA_TRUNCATED, 1); + return rc; +} + + +/* + Convert a string to an integer then check bounds. + + SYNOPSIS + Field_num::get_int + cs Character set + from String to convert + len Length of the string + rnd OUT longlong value + unsigned_max max unsigned value + signed_min min signed value + signed_max max signed value + + DESCRIPTION + The function calls strntoull10rnd() to get an integer value then + check bounds and errors returned. In case of any error a warning + is raised. + + RETURN + 0 ok + 1 error +*/ + +bool Field_num::get_int(CHARSET_INFO *cs, const char *from, size_t len, + longlong *rnd, ulonglong unsigned_max, + longlong signed_min, longlong signed_max) +{ + char *end; + int error; + + *rnd= (longlong) cs->strntoull10rnd(from, len, + unsigned_flag, &end, + &error); + if (unsigned_flag) + { + + if ((((ulonglong) *rnd > unsigned_max) && + (*rnd= (longlong) unsigned_max)) || + error == MY_ERRNO_ERANGE) + { + goto out_of_range; + } + } + else + { + if (*rnd < signed_min) + { + *rnd= signed_min; + goto out_of_range; + } + else if (*rnd > signed_max) + { + *rnd= signed_max; + goto out_of_range; + } + } + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && + check_int(cs, from, len, end, error)) + return 1; + + return error && get_thd()->count_cuted_fields == CHECK_FIELD_EXPRESSION; + +out_of_range: + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + return 1; +} + + +double Field_real::get_double(const char *str, size_t length, CHARSET_INFO *cs, + int *error) +{ + char *end; + double nr= cs->strntod((char*) str, length, &end, error); + if (unlikely(*error)) + { + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + *error= 1; + } + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && + check_edom_and_truncation("double", str == end, + cs, str, length, end)) + *error= 1; + return nr; +} + + +/** + Process decimal library return codes and issue warnings for overflow and + truncation. + + @param op_result decimal library return code (E_DEC_* see include/decimal.h) + + @retval + 1 there was overflow + @retval + 0 no error or some other errors except overflow +*/ + +int Field::warn_if_overflow(int op_result) +{ + if (op_result == E_DEC_OVERFLOW) + { + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + return 1; + } + if (op_result == E_DEC_TRUNCATED) + { + set_note(WARN_DATA_TRUNCATED, 1); + /* We return 0 here as this is not a critical issue */ + } + return 0; +} + + +/** + Interpret field value as an integer but return the result as a string. + + This is used for printing bit_fields as numbers while debugging. +*/ + +String *Field::val_int_as_str(String *val_buffer, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_read()); + CHARSET_INFO *cs= &my_charset_bin; + uint length; + longlong value= val_int(); + + if (val_buffer->alloc(MY_INT64_NUM_DECIMAL_DIGITS)) + return 0; + length= (uint) (cs->longlong10_to_str)((char*) val_buffer->ptr(), + MY_INT64_NUM_DECIMAL_DIGITS, + unsigned_val ? 10 : -10, + value); + val_buffer->length(length); + return val_buffer; +} + + +/// This is used as a table name when the table structure is not set up +Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, + uchar null_bit_arg, + utype unireg_check_arg, const LEX_CSTRING *field_name_arg) + :ptr(ptr_arg), + null_ptr(null_ptr_arg), table(0), orig_table(0), + table_name(0), field_name(*field_name_arg), option_list(0), + option_struct(0), key_start(0), part_of_key(0), + part_of_key_not_clustered(0), part_of_sortkey(0), + unireg_check(unireg_check_arg), invisible(VISIBLE), field_length(length_arg), + null_bit(null_bit_arg), is_created_from_null_item(FALSE), + read_stats(NULL), collected_stats(0), vcol_info(0), check_constraint(0), + default_value(0) +{ + flags=null_ptr ? 0: NOT_NULL_FLAG; + comment.str= (char*) ""; + comment.length=0; + field_index= 0; + cond_selectivity= 1.0; + next_equal_field= NULL; +} + + +void Field::hash_not_null(Hasher *hasher) +{ + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(!is_null()); + hasher->add(sort_charset(), ptr, pack_length()); +} + +size_t +Field::do_last_null_byte() const +{ + DBUG_ASSERT(null_ptr == NULL || null_ptr >= table->record[0]); + if (null_ptr) + return (size_t) (null_ptr - table->record[0]) + 1; + return LAST_NULL_BYTE_UNDEF; +} + + +void Field::copy_from_tmp(int row_offset) +{ + memcpy(ptr,ptr+row_offset,pack_length()); + if (null_ptr) + { + *null_ptr= (uchar) ((null_ptr[0] & (uchar) ~(uint) null_bit) | + (null_ptr[row_offset] & (uchar) null_bit)); + } +} + + +bool Field::send(Protocol *protocol) +{ + StringBuffer<MAX_FIELD_WIDTH> tmp(charset()); + val_str(&tmp); + return protocol->store(tmp.ptr(), tmp.length(), tmp.charset()); +} + + +bool Field_num::send_numeric_zerofill_str(Protocol_text *protocol, + protocol_send_type_t send_type) +{ + DBUG_ASSERT(marked_for_read()); + StringBuffer<MAX_FIELD_WIDTH> tmp(&my_charset_latin1); + val_str(&tmp); + return protocol->store_numeric_zerofill_str(tmp.ptr(), + tmp.length(), + send_type); +} + + +/** + Check to see if field size is compatible with destination. + + This method is used in row-based replication to verify that the + slave's field size is less than or equal to the master's field + size. The encoded field metadata (from the master or source) is + decoded and compared to the size of this field (the slave or + destination). + + @note + + The comparison is made so that if the source data (from the master) + is less than the target data (on the slave), -1 is returned in @c + <code>*order_var</code>. This implies that a conversion is + necessary, but that it is lossy and can result in truncation of the + value. + + If the source data is strictly greater than the target data, 1 is + returned in <code>*order_var</code>. This implies that the source + type can is contained in the target type and that a conversion is + necessary but is non-lossy. + + If no conversion is required to fit the source type in the target + type, 0 is returned in <code>*order_var</code>. + + @param field_metadata Encoded size in field metadata + @param mflags Flags from the table map event for the table. + @param order_var Pointer to variable where the order + between the source field and this field + will be returned. + + @return @c true if this field's size is compatible with the + master's field size, @c false otherwise. +*/ +bool Field::compatible_field_size(uint field_metadata, + const Relay_log_info *rli_arg __attribute__((unused)), + uint16 mflags __attribute__((unused)), + int *order_var) const +{ + uint const source_size= pack_length_from_metadata(field_metadata); + uint const destination_size= row_pack_length(); + DBUG_PRINT("debug", ("real_type: %d, source_size: %u, destination_size: %u", + real_type(), source_size, destination_size)); + *order_var = compare(source_size, destination_size); + return true; +} + + +int Field::store(const char *to, size_t length, CHARSET_INFO *cs, + enum_check_fields check_level) +{ + Check_level_instant_set tmp_level(get_thd(), check_level); + return store(to, length, cs); +} + + +int Field::store_text(const char *to, size_t length, CHARSET_INFO *cs, + enum_check_fields check_level) +{ + Check_level_instant_set tmp_level(get_thd(), check_level); + return store_text(to, length, cs); +} + + +int Field::store_timestamp_dec(const timeval &ts, uint dec) +{ + return store_time_dec(Datetime(get_thd(), ts).get_mysql_time(), dec); +} + + +int Field::store_to_statistical_minmax_field(Field *field, String *val) +{ + val_str(val); + size_t length= Well_formed_prefix(val->charset(), val->ptr(), + MY_MIN(val->length(), field->field_length)).length(); + return field->store(val->ptr(), length, &my_charset_bin); +} + + +int Field::store_from_statistical_minmax_field(Field *stat_field, String *str, + MEM_ROOT *mem) +{ + stat_field->val_str(str); + return store_text(str->ptr(), str->length(), &my_charset_bin); +} + + +/* + Same as above, but store the string in the statistics mem_root to make it + easy to free everything by just freeing the mem_root. +*/ + +int Field_blob::store_from_statistical_minmax_field(Field *stat_field, + String *str, + MEM_ROOT *mem) +{ + String *tmp= stat_field->val_str(str); + uchar *ptr; + if (!(ptr= (uchar*) memdup_root(mem, tmp->ptr(), tmp->length()))) + { + set_ptr((uint32) 0, NULL); + return 1; + } + set_ptr(tmp->length(), ptr); + return 0; +} + + +/** + Pack the field into a format suitable for storage and transfer. + + To implement packing functionality, only the virtual function + should be overridden. The other functions are just convenience + functions and hence should not be overridden. + + @note The default method for packing fields just copy the raw bytes + of the record into the destination, but never more than + <code>max_length</code> characters. + + @param to + Pointer to memory area where representation of field should be put. + + @param from + Pointer to memory area where record representation of field is + stored. + + @param max_length + Maximum length of the field, as given in the column definition. For + example, for <code>CHAR(1000)</code>, the <code>max_length</code> + is 1000. This information is sometimes needed to decide how to pack + the data. + +*/ +uchar * +Field::pack(uchar *to, const uchar *from, uint max_length) +{ + uint32 length= pack_length(); + set_if_smaller(length, max_length); + memcpy(to, from, length); + return to+length; +} + +/** + Unpack a field from row data. + + This method is used to unpack a field from a master whose size of + the field is less than that of the slave. + + The <code>param_data</code> parameter is a two-byte integer (stored + in the least significant 16 bits of the unsigned integer) usually + consisting of two parts: the real type in the most significant byte + and a original pack length in the least significant byte. + + The exact layout of the <code>param_data</code> field is given by + the <code>Table_map_log_event::save_field_metadata()</code>. + + This is the default method for unpacking a field. It just copies + the memory block in byte order (of original pack length bytes or + length of field, whichever is smaller). + + @param to Destination of the data + @param from Source of the data + @param param_data Real type and original pack length of the field + data + + @return New pointer into memory based on from + length of the data + @return 0 if wrong data +*/ +const uchar * +Field::unpack(uchar* to, const uchar *from, const uchar *from_end, + uint param_data) +{ + uint length=pack_length(), len; + int from_type= 0; + /* + If from length is > 255, it has encoded data in the upper bits. Need + to mask it out. + */ + if (param_data > 255) + { + from_type= (param_data & 0xff00) >> 8U; // real_type. + param_data= param_data & 0x00ff; // length. + } + + if ((param_data == 0) || + (length == param_data) || + (from_type != real_type())) + { + if (from + length > from_end) + return 0; // Error in data + + memcpy(to, from, length); + return from+length; + } + + len= (param_data && (param_data < length)) ? param_data : length; + + if (from + len > from_end) + return 0; // Error in data + + memcpy(to, from, len); + return from+len; +} + + +void Field_num::add_zerofill_and_unsigned(String &res) const +{ + if (unsigned_flag) + res.append(STRING_WITH_LEN(" unsigned")); + if (zerofill) + res.append(STRING_WITH_LEN(" zerofill")); +} + + +void Field::make_send_field(Send_field *field) +{ + if (orig_table && orig_table->s->db.str && *orig_table->s->db.str) + { + field->db_name= orig_table->s->db; + if (orig_table->pos_in_table_list && + orig_table->pos_in_table_list->schema_table) + field->org_table_name= Lex_cstring_strlen(orig_table->pos_in_table_list-> + schema_table->table_name); + else + field->org_table_name= orig_table->s->table_name; + } + else + field->org_table_name= field->db_name= empty_clex_str; + if (orig_table && orig_table->alias.ptr()) + { + orig_table->alias.get_value(&field->table_name); + field->org_col_name= field_name; + } + else + { + field->table_name= empty_clex_str; + field->org_col_name= empty_clex_str; + } + field->col_name= field_name; + field->length=field_length; + field->set_handler(type_handler()); + field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags; + field->decimals= 0; +} + + +/** + Conversion from decimal to longlong with checking overflow and + setting correct value (min/max) in case of overflow. + + @param val value which have to be converted + @param unsigned_flag type of integer in which we convert val + @param err variable to pass error code + + @return + value converted from val +*/ +longlong Field::convert_decimal2longlong(const my_decimal *val, + bool unsigned_flag, int *err) +{ + longlong i; + if (unsigned_flag) + { + if (val->sign()) + { + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + i= 0; + *err= 1; + } + else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR & + ~E_DEC_OVERFLOW & + ~E_DEC_TRUNCATED), + val, TRUE, &i))) + { + i= ~(longlong) 0; + *err= 1; + } + } + else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR & + ~E_DEC_OVERFLOW & + ~E_DEC_TRUNCATED), + val, FALSE, &i))) + { + i= (val->sign() ? LONGLONG_MIN : LONGLONG_MAX); + *err= 1; + } + return i; +} + + +/** + Storing decimal in integer fields. + + @param val value for storing + + @note + This method is used by all integer fields, real/decimal redefine it + + @retval + 0 OK + @retval + !=0 error +*/ + +int Field_int::store_decimal(const my_decimal *val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int err= 0; + longlong i= convert_decimal2longlong(val, unsigned_flag, &err); + return MY_TEST(err | store(i, unsigned_flag)); +} + + +/** + Return decimal value of integer field. + + @param decimal_value buffer for storing decimal value + + @note + This method is used by all integer fields, real/decimal redefine it. + All longlong values fit in our decimal buffer which cal store 8*9=72 + digits of integer number + + @return + pointer to decimal buffer with value of field +*/ + +my_decimal* Field_int::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(marked_for_read()); + longlong nr= val_int(); + int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value); + return decimal_value; +} + + +bool Field_int::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) +{ + DBUG_ASSERT(marked_for_read()); + Longlong_hybrid nr(val_int(), (flags & UNSIGNED_FLAG)); + return int_to_datetime_with_warn(get_thd(), nr, ltime, + fuzzydate, table->s, field_name.str); +} + + +bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id) +{ + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(ltime); + if (!table || !table->s) + return true; + DBUG_ASSERT(table->versioned(VERS_TRX_ID) || + (table->versioned() && table->s->table_category == TABLE_CATEGORY_TEMPORARY)); + if (!trx_id) + return true; + + THD *thd= get_thd(); + DBUG_ASSERT(thd); + if (trx_id == ULONGLONG_MAX) + { + thd->variables.time_zone->gmt_sec_to_TIME(ltime, TIMESTAMP_MAX_VALUE); + ltime->second_part= TIME_MAX_SECOND_PART; + return false; + } + if (cached == trx_id) + { + *ltime= cache; + return false; + } + + TR_table trt(thd); + bool found= trt.query(trx_id); + if (found) + { + trt[TR_table::FLD_COMMIT_TS]->get_date(&cache, fuzzydate); + *ltime= cache; + cached= trx_id; + return false; + } + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_NO_TRX_ID, ER_THD(thd, ER_VERS_NO_TRX_ID), + (longlong) trx_id); + return true; +} + + +Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, + const DTCollation &collation) + :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg) +{ + m_collation= collation; + if (collation.collation->state & MY_CS_BINSORT) + flags|=BINARY_FLAG; +} + + +bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const +{ + /* + Can't guarantee uniqueness when comparing a CHAR/VARCHAR/TEXT, + BINARY/VARBINARY/BLOB, ENUM,SET columns to an item with cmp_type() + of INT_RESULT, DOUBLE_RESULT, DECIMAL_RESULT or TIME_RESULT. + Example: + SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01' + return non-unuque values, e.g. '2001-01-01' and '2001-01-01x'. + */ + if (!field_charset()->propagate(0, 0) || + item->cmp_type() != STRING_RESULT) + return false; + /* + Can't guarantee uniqueness when comparing to + an item of a different collation. + Example: + SELECT * FROM t1 + WHERE latin1_bin_column = _latin1'A' COLLATE latin1_swedish_ci + return non-unique values 'a' and 'A'. + */ + DTCollation tmp(dtcollation()); + return !tmp.aggregate(item->collation) && tmp.collation == field_charset(); +} + + +bool Field_str::can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal) +{ + DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() == STRING_RESULT); + switch (ctx.subst_constraint()) { + case ANY_SUBST: + return ctx.compare_type_handler() == item_equal->compare_type_handler() && + (ctx.compare_type_handler()->cmp_type() != STRING_RESULT || + ctx.compare_collation() == item_equal->compare_collation()); + case IDENTITY_SUBST: + return ((charset()->state & MY_CS_BINSORT) && + (charset()->state & MY_CS_NOPAD)); + } + return false; +} + + +void Field_str::change_charset(const DTCollation &new_cs) +{ + if (!has_charset()) + return; + + field_length= (field_length * new_cs.collation->mbmaxlen) / + m_collation.collation->mbmaxlen; + m_collation= new_cs; +} + + +void Field_num::make_send_field(Send_field *field) +{ + Field::make_send_field(field); + field->decimals= dec; +} + +/** + Decimal representation of Field_str. + + @param d value for storing + + @note + Field_str is the base class for fields like Field_enum, + Field_date and some similar. Some dates use fraction and also + string value should be converted to floating point value according + our rules, so we use double to store value of decimal in string. + + @todo + use decimal2string? + + @retval + 0 OK + @retval + !=0 error +*/ + +int Field_str::store_decimal(const my_decimal *d) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + double val; + /* TODO: use decimal2string? */ + int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR & + ~E_DEC_OVERFLOW, d, &val)); + return err | store(val); +} + + +my_decimal *Field_str::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(marked_for_read()); + longlong nr= val_int(); + int2my_decimal(E_DEC_FATAL_ERROR, nr, 0, decimal_value); + return decimal_value; +} + + +uint Field::fill_cache_field(CACHE_FIELD *copy) +{ + uint store_length; + copy->str= ptr; + copy->length= pack_length_in_rec(); + copy->field= this; + if (flags & BLOB_FLAG) + { + copy->type= CACHE_BLOB; + copy->length-= portable_sizeof_char_ptr; + return copy->length; + } + else if (!zero_pack() && + (type() == MYSQL_TYPE_STRING && copy->length >= 4 && + copy->length < 256)) + { + copy->type= CACHE_STRIPPED; /* Remove end space */ + store_length= 2; + } + else if (type() == MYSQL_TYPE_VARCHAR) + { + copy->type= pack_length()-row_pack_length() == 1 ? CACHE_VARSTR1: + CACHE_VARSTR2; + store_length= 0; + } + else + { + copy->type= 0; + store_length= 0; + } + return copy->length + store_length; +} + + +bool Field::get_date(MYSQL_TIME *to, date_mode_t mode) +{ + StringBuffer<40> tmp; + Temporal::Warn_push warn(get_thd(), nullptr, nullptr, nullptr, to, mode); + Temporal_hybrid *t= new(to) Temporal_hybrid(get_thd(), &warn, + val_str(&tmp), mode); + return !t->is_valid_temporal(); +} + + +longlong Field::val_datetime_packed(THD *thd) +{ + MYSQL_TIME ltime, tmp; + if (get_date(<ime, Datetime::Options_cmp(thd))) + return 0; + if (ltime.time_type != MYSQL_TIMESTAMP_TIME) + return pack_time(<ime); + if (time_to_datetime_with_warn(thd, <ime, &tmp, TIME_CONV_NONE)) + return 0; + return pack_time(&tmp); +} + + +longlong Field::val_time_packed(THD *thd) +{ + MYSQL_TIME ltime; + Time::Options_cmp opt(thd); + if (get_date(<ime, opt)) + return 0; + if (ltime.time_type == MYSQL_TIMESTAMP_TIME) + return pack_time(<ime); + // Conversion from DATETIME or DATE to TIME is needed + return Time(thd, <ime, opt).to_packed(); +} + + +/** + This is called when storing a date in a string. + + @note + Needs to be changed if/when we want to support different time formats. +*/ + +int Field::store_time_dec(const MYSQL_TIME *ltime, uint dec) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + char buff[MAX_DATE_STRING_REP_LENGTH]; + uint length= (uint) my_TIME_to_str(ltime, buff, dec); + /* Avoid conversion when field character set is ASCII compatible */ + return store(buff, length, (charset()->state & MY_CS_NONASCII) ? + &my_charset_latin1 : charset()); +} + + +bool Field::optimize_range(uint idx, uint part) const +{ + return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE); +} + + +Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type __attribute__((unused))) +{ + Field *tmp; + if (!(tmp= (Field*) memdup_root(root,(char*) this,size_of()))) + return 0; + + if (tmp->table->maybe_null) + tmp->flags&= ~NOT_NULL_FLAG; + tmp->table= new_table; + tmp->key_start.init(0); + tmp->part_of_key.init(0); + tmp->part_of_sortkey.init(0); + tmp->read_stats= NULL; + /* + TODO: it is not clear why this method needs to reset unireg_check. + Try not to reset it, or explain why it needs to be reset. + */ + tmp->unireg_check= Field::NONE; + tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | + ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); + tmp->reset_fields(); + tmp->invisible= VISIBLE; + return tmp; +} + + +Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit) +{ + Field *tmp; + if ((tmp= make_new_field(root, new_table, table == new_table))) + { + tmp->ptr= new_ptr; + tmp->null_ptr= new_null_ptr; + tmp->null_bit= new_null_bit; + } + return tmp; +} + + +/** + Create field for temporary table from given field. + + @param thd Thread handler + @param table Temporary table + @param maybe_null_arg If the result field should be NULL-able, + even if the original field is NOT NULL, e.g. for: + - OUTER JOIN fields + - WITH ROLLUP fields + - arguments of aggregate functions, e.g. SUM(column1) + @retval NULL, on error + @retval pointer to the new field created, on success. +*/ + +Field *Field::create_tmp_field(MEM_ROOT *mem_root, TABLE *new_table, + bool maybe_null_arg) +{ + Field *new_field; + + if ((new_field= make_new_field(mem_root, new_table, new_table == table))) + { + new_field->init_for_tmp_table(this, new_table); + new_field->flags|= flags & NO_DEFAULT_VALUE_FLAG; + if (maybe_null_arg) + new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join + } + return new_field; +} + + +/* This is used to generate a field in TABLE from TABLE_SHARE */ + +Field *Field::clone(MEM_ROOT *root, TABLE *new_table) +{ + Field *tmp; + if ((tmp= (Field*) memdup_root(root,(char*) this,size_of()))) + { + tmp->init(new_table); + tmp->move_field_offset((my_ptrdiff_t) (new_table->record[0] - + new_table->s->default_values)); + } + return tmp; +} + + +Field *Field::clone(MEM_ROOT *root, TABLE *new_table, my_ptrdiff_t diff) +{ + Field *tmp; + if ((tmp= (Field*) memdup_root(root,(char*) this,size_of()))) + { + if (new_table) + tmp->init(new_table); + tmp->move_field_offset(diff); + } + return tmp; +} + + +int Field::set_default() +{ + if (default_value) + { + Query_arena backup_arena; + /* + TODO: this may impose memory leak until table flush. + See comment in + TABLE::update_virtual_fields(handler *, enum_vcol_update_mode). + */ + table->in_use->set_n_backup_active_arena(table->expr_arena, &backup_arena); + int rc= default_value->expr->save_in_field(this, 0); + table->in_use->restore_active_arena(table->expr_arena, &backup_arena); + return rc; + } + /* Copy constant value stored in s->default_values */ + my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values - + table->record[0]); + memcpy(ptr, ptr + l_offset, pack_length_in_rec()); + if (maybe_null_in_table()) + *null_ptr= ((*null_ptr & (uchar) ~null_bit) | + (null_ptr[l_offset] & null_bit)); + return 0; +} + + +/**************************************************************************** + Field_null, a field that always return NULL +****************************************************************************/ + +void Field_null::sql_type(String &res) const +{ + res.set_ascii(STRING_WITH_LEN("null")); +} + + +bool Field_null::is_equal(const Column_definition &new_field) const +{ + DBUG_ASSERT(!compression_method()); + return new_field.type_handler() == type_handler() && + new_field.charset == field_charset() && + new_field.length == max_display_length(); +} + + +/**************************************************************************** + Field_row, e.g. for ROW-type SP variables +****************************************************************************/ + +Field_row::~Field_row() +{ + delete m_table; +} + + +bool Field_row::sp_prepare_and_store_item(THD *thd, Item **value) +{ + DBUG_ENTER("Field_row::sp_prepare_and_store_item"); + + if (value[0]->type() == Item::NULL_ITEM) + { + /* + We're in a auto-generated sp_inst_set, to assign + the explicit default NULL value to a ROW variable. + */ + m_table->set_all_fields_to_null(); + DBUG_RETURN(false); + } + + /** + - In case if we're assigning a ROW variable from another ROW variable, + value[0] points to Item_splocal. sp_fix_func_item() will return the + fixed underlying Item_field pointing to Field_row. + - In case if we're assigning from a ROW() value, src and value[0] will + point to the same Item_row. + - In case if we're assigning from a subselect, src and value[0] also + point to the same Item_singlerow_subselect. + */ + Item *src; + if (!(src= thd->sp_fix_func_item(value)) || + src->cmp_type() != ROW_RESULT || + src->cols() != m_table->s->fields) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), m_table->s->fields); + m_table->set_all_fields_to_null(); + DBUG_RETURN(true); + } + + src->bring_value(); + DBUG_RETURN(m_table->sp_set_all_fields_from_item(thd, src)); +} + + +/**************************************************************************** + Functions for the Field_decimal class + This is an number stored as a pre-space (or pre-zero) string +****************************************************************************/ + +int +Field_decimal::reset(void) +{ + Field_decimal::store(STRING_WITH_LEN("0"),&my_charset_bin); + return 0; +} + +void Field_decimal::overflow(bool negative) +{ + uint len=field_length; + uchar *to=ptr, filler= '9'; + + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + if (negative) + { + if (!unsigned_flag) + { + /* Put - sign as a first digit so we'll have -999..999 or 999..999 */ + *to++ = '-'; + len--; + } + else + { + filler= '0'; // Fill up with 0 + if (!zerofill) + { + /* + Handle unsigned integer without zerofill, in which case + the number should be of format ' 0' or ' 0.000' + */ + uint whole_part=field_length- (dec ? dec+2 : 1); + // Fill with spaces up to the first digit + bfill(to, whole_part, ' '); + to+= whole_part; + len-= whole_part; + // The main code will also handle the 0 before the decimal point + } + } + } + bfill(to, len, filler); + if (dec) + ptr[field_length-dec-1]='.'; + return; +} + + +int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff,sizeof(buff), &my_charset_bin); + const uchar *from= (uchar*) from_arg; + + /* Convert character set if the old one is multi uchar */ + if (cs->mbmaxlen > 1) + { + uint dummy_errors; + tmp.copy((char*) from, len, cs, &my_charset_bin, &dummy_errors); + from= (uchar*) tmp.ptr(); + len= tmp.length(); + } + + const uchar *end= from+len; + /* The pointer where the field value starts (i.e., "where to write") */ + uchar *to= ptr; + uint tmp_dec, tmp_uint; + /* + The sign of the number : will be 0 (means positive but sign not + specified), '+' or '-' + */ + uchar sign_char=0; + /* The pointers where prezeros start and stop */ + const uchar *pre_zeros_from, *pre_zeros_end; + /* The pointers where digits at the left of '.' start and stop */ + const uchar *int_digits_from, *int_digits_end; + /* The pointers where digits at the right of '.' start and stop */ + const uchar *frac_digits_from, *frac_digits_end; + /* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */ + char expo_sign_char=0; + uint exponent=0; // value of the exponent + /* + Pointers used when digits move from the left of the '.' to the + right of the '.' (explained below) + */ + const uchar *UNINIT_VAR(int_digits_tail_from); + /* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */ + uint UNINIT_VAR(int_digits_added_zeros); + /* + Pointer used when digits move from the right of the '.' to the left + of the '.' + */ + const uchar *UNINIT_VAR(frac_digits_head_end); + /* Number of 0 that need to be added at the right of the '.' (for 1E-3) */ + uint UNINIT_VAR(frac_digits_added_zeros); + uchar *pos,*tmp_left_pos,*tmp_right_pos; + /* Pointers that are used as limits (begin and end of the field buffer) */ + uchar *left_wall,*right_wall; + uchar tmp_char; + /* + To remember if get_thd()->cuted_fields has already been incremented, + to do that only once + */ + bool is_cuted_fields_incr=0; + + /* + There are three steps in this function : + - parse the input string + - modify the position of digits around the decimal dot '.' + according to the exponent value (if specified) + - write the formatted number + */ + + if ((tmp_dec=dec)) + tmp_dec++; + + /* skip pre-space */ + while (from != end && my_isspace(&my_charset_bin,*from)) + from++; + if (from == end) + { + set_warning(WARN_DATA_TRUNCATED, 1); + is_cuted_fields_incr=1; + } + else if (*from == '+' || *from == '-') // Found some sign ? + { + sign_char= *from++; + /* + We allow "+" for unsigned decimal unless defined different + Both options allowed as one may wish not to have "+" for unsigned numbers + because of data processing issues + */ + if (unsigned_flag) + { + if (sign_char=='-') + { + Field_decimal::overflow(1); + return 1; + } + /* + Defining this will not store "+" for unsigned decimal type even if + it is passed in numeric string. This will make some tests to fail + */ +#ifdef DONT_ALLOW_UNSIGNED_PLUS + else + sign_char=0; +#endif + } + } + + pre_zeros_from= from; + for (; from!=end && *from == '0'; from++) ; // Read prezeros + pre_zeros_end=int_digits_from=from; + /* Read non zero digits at the left of '.'*/ + for (; from != end && my_isdigit(&my_charset_bin, *from) ; from++) ; + int_digits_end=from; + if (from!=end && *from == '.') // Some '.' ? + from++; + frac_digits_from= from; + /* Read digits at the right of '.' */ + for (;from!=end && my_isdigit(&my_charset_bin, *from); from++) ; + frac_digits_end=from; + // Some exponentiation symbol ? + if (from != end && (*from == 'e' || *from == 'E')) + { + from++; + if (from != end && (*from == '+' || *from == '-')) // Some exponent sign ? + expo_sign_char= *from++; + else + expo_sign_char= '+'; + /* + Read digits of the exponent and compute its value. We must care about + 'exponent' overflow, because as unsigned arithmetic is "modulo", big + exponents will become small (e.g. 1e4294967296 will become 1e0, and the + field will finally contain 1 instead of its max possible value). + */ + for (;from!=end && my_isdigit(&my_charset_bin, *from); from++) + { + exponent=10*exponent+(*from-'0'); + if (exponent>MAX_EXPONENT) + break; + } + } + + /* + We only have to generate warnings if count_cuted_fields is set. + This is to avoid extra checks of the number when they are not needed. + Even if this flag is not set, it's OK to increment warnings, if + it makes the code easier to read. + */ + + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) + { + // Skip end spaces + for (;from != end && my_isspace(&my_charset_bin, *from); from++) ; + if (from != end) // If still something left, warn + { + set_warning(WARN_DATA_TRUNCATED, 1); + is_cuted_fields_incr=1; + } + } + + /* + Now "move" digits around the decimal dot according to the exponent value, + and add necessary zeros. + Examples : + - 1E+3 : needs 3 more zeros at the left of '.' (int_digits_added_zeros=3) + - 1E-3 : '1' moves at the right of '.', and 2 more zeros are needed + between '.' and '1' + - 1234.5E-3 : '234' moves at the right of '.' + These moves are implemented with pointers which point at the begin + and end of each moved segment. Examples : + - 1234.5E-3 : before the code below is executed, the int_digits part is + from '1' to '4' and the frac_digits part from '5' to '5'. After the code + below, the int_digits part is from '1' to '1', the frac_digits_head + part is from '2' to '4', and the frac_digits part from '5' to '5'. + - 1234.5E3 : before the code below is executed, the int_digits part is + from '1' to '4' and the frac_digits part from '5' to '5'. After the code + below, the int_digits part is from '1' to '4', the int_digits_tail + part is from '5' to '5', the frac_digits part is empty, and + int_digits_added_zeros=2 (to make 1234500). + */ + + /* + Below tmp_uint cannot overflow with small enough MAX_EXPONENT setting, + as int_digits_added_zeros<=exponent<4G and + (int_digits_end-int_digits_from)<=max_allowed_packet<=2G and + (frac_digits_from-int_digits_tail_from)<=max_allowed_packet<=2G + */ + + if (!expo_sign_char) + tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from); + else if (expo_sign_char == '-') + { + tmp_uint=MY_MIN(exponent,(uint)(int_digits_end-int_digits_from)); + frac_digits_added_zeros=exponent-tmp_uint; + int_digits_end -= tmp_uint; + frac_digits_head_end=int_digits_end+tmp_uint; + tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from); + } + else // (expo_sign_char=='+') + { + tmp_uint=MY_MIN(exponent,(uint)(frac_digits_end-frac_digits_from)); + int_digits_added_zeros=exponent-tmp_uint; + int_digits_tail_from=frac_digits_from; + frac_digits_from=frac_digits_from+tmp_uint; + /* + We "eat" the heading zeros of the + int_digits.int_digits_tail.int_digits_added_zeros concatenation + (for example 0.003e3 must become 3 and not 0003) + */ + if (int_digits_from == int_digits_end) + { + /* + There was nothing in the int_digits part, so continue + eating int_digits_tail zeros + */ + for (; int_digits_tail_from != frac_digits_from && + *int_digits_tail_from == '0'; int_digits_tail_from++) ; + if (int_digits_tail_from == frac_digits_from) + { + // there were only zeros in int_digits_tail too + int_digits_added_zeros=0; + } + } + tmp_uint= (uint) (tmp_dec+(int_digits_end-int_digits_from)+ + (uint)(frac_digits_from-int_digits_tail_from)+ + int_digits_added_zeros); + } + + /* + Now write the formatted number + + First the digits of the int_% parts. + Do we have enough room to write these digits ? + If the sign is defined and '-', we need one position for it + */ + + if (field_length < tmp_uint + (int) (sign_char == '-')) + { + // too big number, change to max or min number + Field_decimal::overflow(sign_char == '-'); + return 1; + } + + /* + Tmp_left_pos is the position where the leftmost digit of + the int_% parts will be written + */ + tmp_left_pos=pos=to+(uint)(field_length-tmp_uint); + + // Write all digits of the int_% parts + while (int_digits_from != int_digits_end) + *pos++ = *int_digits_from++ ; + + if (expo_sign_char == '+') + { + while (int_digits_tail_from != frac_digits_from) + *pos++= *int_digits_tail_from++; + while (int_digits_added_zeros-- >0) + *pos++= '0'; + } + /* + Note the position where the rightmost digit of the int_% parts has been + written (this is to later check if the int_% parts contained nothing, + meaning an extra 0 is needed). + */ + tmp_right_pos=pos; + + /* + Step back to the position of the leftmost digit of the int_% parts, + to write sign and fill with zeros or blanks or prezeros. + */ + pos=tmp_left_pos-1; + if (zerofill) + { + left_wall=to-1; + while (pos > left_wall) // Fill with zeros + *pos--='0'; + } + else + { + left_wall=to+(sign_char != 0)-1; + if (!expo_sign_char) // If exponent was specified, ignore prezeros + { + for (;pos > left_wall && pre_zeros_from !=pre_zeros_end; + pre_zeros_from++) + *pos--= '0'; + } + if (pos == tmp_right_pos-1) + *pos--= '0'; // no 0 has ever been written, so write one + left_wall= to-1; + if (sign_char && pos != left_wall) + { + /* Write sign if possible (it is if sign is '-') */ + *pos--= sign_char; + } + while (pos != left_wall) + *pos--=' '; //fill with blanks + } + + /* + Write digits of the frac_% parts ; + Depending on get_thd()->count_cuted_fields, we may also want + to know if some non-zero tail of these parts will + be truncated (for example, 0.002->0.00 will generate a warning, + while 0.000->0.00 will not) + (and 0E1000000000 will not, while 1E-1000000000 will) + */ + + pos=to+(uint)(field_length-tmp_dec); // Calculate post to '.' + right_wall=to+field_length; + if (pos != right_wall) + *pos++='.'; + + if (expo_sign_char == '-') + { + while (frac_digits_added_zeros-- > 0) + { + if (pos == right_wall) + { + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && + !is_cuted_fields_incr) + break; // Go on below to see if we lose non zero digits + return 0; + } + *pos++='0'; + } + while (int_digits_end != frac_digits_head_end) + { + tmp_char= *int_digits_end++; + if (pos == right_wall) + { + if (tmp_char != '0') // Losing a non zero digit ? + { + if (!is_cuted_fields_incr) + set_warning(WARN_DATA_TRUNCATED, 1); + return 0; + } + continue; + } + *pos++= tmp_char; + } + } + + for (;frac_digits_from!=frac_digits_end;) + { + tmp_char= *frac_digits_from++; + if (pos == right_wall) + { + if (tmp_char != '0') // Losing a non zero digit ? + { + if (!is_cuted_fields_incr) + { + /* + This is a note, not a warning, as we don't want to abort + when we cut decimals in strict mode + */ + set_note(WARN_DATA_TRUNCATED, 1); + } + return 0; + } + continue; + } + *pos++= tmp_char; + } + + while (pos != right_wall) + *pos++='0'; // Fill with zeros at right of '.' + return 0; +} + + +int Field_decimal::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + if (unsigned_flag && nr < 0) + { + overflow(1); + return 1; + } + + if (!std::isfinite(nr)) // Handle infinity as special case + { + overflow(nr < 0.0); + return 1; + } + + size_t length; + uchar fyllchar,*to; + char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; + + fyllchar = zerofill ? (char) '0' : (char) ' '; + length= my_fcvt(nr, dec, buff, NULL); + + if (length > field_length) + { + overflow(nr < 0.0); + return 1; + } + else + { + to=ptr; + for (size_t i=field_length-length ; i-- > 0 ;) + *to++ = fyllchar; + memcpy(to,buff,length); + return 0; + } +} + + +int Field_decimal::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + char buff[22]; + uint length, int_part; + char fyllchar; + uchar *to; + + if (nr < 0 && unsigned_flag && !unsigned_val) + { + overflow(1); + return 1; + } + length= (uint) (longlong10_to_str(nr,buff,unsigned_val ? 10 : -10) - buff); + int_part= field_length- (dec ? dec+1 : 0); + + if (length > int_part) + { + overflow(!unsigned_val && nr < 0L); /* purecov: inspected */ + return 1; + } + + fyllchar = zerofill ? (char) '0' : (char) ' '; + to= ptr; + for (uint i=int_part-length ; i-- > 0 ;) + *to++ = fyllchar; + memcpy(to,buff,length); + if (dec) + { + to[length]='.'; + bfill(to+length+1,dec,'0'); + } + return 0; +} + + +double Field_decimal::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + int not_used; + char *end_not_used; + return my_charset_bin.strntod((char*) ptr, field_length, &end_not_used, ¬_used); +} + +longlong Field_decimal::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + int not_used; + if (unsigned_flag) + return my_charset_bin.strntoull((char*) ptr, field_length, 10, NULL, ¬_used); + return my_charset_bin.strntoll((char*) ptr, field_length, 10, NULL, ¬_used); +} + + +String *Field_decimal::val_str(String *val_buffer __attribute__((unused)), + String *val_ptr) +{ + DBUG_ASSERT(marked_for_read()); + uchar *str; + size_t tmp_length; + + for (str=ptr ; *str == ' ' ; str++) ; + val_ptr->set_charset(&my_charset_numeric); + tmp_length= (size_t) (str-ptr); + if (field_length < tmp_length) // Error in data + val_ptr->length(0); + else + val_ptr->set_ascii((const char*) str, field_length-tmp_length); + return val_ptr; +} + +/** + Should be able to handle at least the following fixed decimal formats: + 5.00 , -1.0, 05, -05, +5 with optional pre/end space +*/ + +int Field_decimal::cmp(const uchar *a_ptr,const uchar *b_ptr) const +{ + const uchar *end; + int swap=0; + /* First remove prefixes '0', ' ', and '-' */ + for (end=a_ptr+field_length; + a_ptr != end && + (*a_ptr == *b_ptr || + ((my_isspace(&my_charset_bin,*a_ptr) || *a_ptr == '+' || + *a_ptr == '0') && + (my_isspace(&my_charset_bin,*b_ptr) || *b_ptr == '+' || + *b_ptr == '0'))); + a_ptr++,b_ptr++) + { + if (*a_ptr == '-') // If both numbers are negative + swap= -1 ^ 1; // Swap result + } + if (a_ptr == end) + return 0; + if (*a_ptr == '-') + return -1; + if (*b_ptr == '-') + return 1; + + while (a_ptr != end) + { + if (*a_ptr++ != *b_ptr++) + return swap ^ (a_ptr[-1] < b_ptr[-1] ? -1 : 1); // compare digits + } + return 0; +} + + +void Field_decimal::sort_string(uchar *to,uint length) +{ + uchar *str,*end; + for (str=ptr,end=ptr+length; + str != end && + ((my_isspace(&my_charset_bin,*str) || *str == '+' || + *str == '0')) ; + str++) + *to++=' '; + if (str == end) + return; /* purecov: inspected */ + + if (*str == '-') + { + *to++=1; // Smaller than any number + str++; + while (str != end) + if (my_isdigit(&my_charset_bin,*str)) + *to++= (char) ('9' - *str++); + else + *to++= *str++; + } + else memcpy(to,str,(uint) (end-str)); +} + + +void Field_decimal::sql_type(String &res) const +{ + CHARSET_INFO *cs=res.charset(); + uint tmp=field_length; + if (!unsigned_flag) + tmp--; + if (dec) + tmp--; + res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), + "decimal(%d,%d)/*old*/",tmp,dec)); + add_zerofill_and_unsigned(res); +} + + +Field *Field_decimal::make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type) +{ + if (keep_type) + return Field_real::make_new_field(root, new_table, keep_type); + + Field *field= new (root) Field_new_decimal(NULL, field_length, + maybe_null() ? (uchar*) "" : 0, 0, + NONE, &field_name, + dec, flags & ZEROFILL_FLAG, + unsigned_flag); + if (field) + field->init_for_make_new_field(new_table, orig_table); + return field; +} + + +/**************************************************************************** +** Field_new_decimal +****************************************************************************/ + +static decimal_digits_t get_decimal_precision(uint len, decimal_digits_t dec, + bool unsigned_val) +{ + uint precision= my_decimal_length_to_precision(len, dec, unsigned_val); + return (decimal_digits_t) MY_MIN(precision, DECIMAL_MAX_PRECISION); +} + +Field_new_decimal::Field_new_decimal(uchar *ptr_arg, + uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, + decimal_digits_t dec_arg,bool zero_arg, + bool unsigned_arg) + :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + MY_MIN(dec_arg, DECIMAL_MAX_SCALE), zero_arg, unsigned_arg) +{ + precision= get_decimal_precision(len_arg, dec_arg, unsigned_arg); + DBUG_ASSERT(precision <= DECIMAL_MAX_PRECISION); + DBUG_ASSERT(dec <= DECIMAL_MAX_SCALE); + bin_size= my_decimal_get_binary_size(precision, dec); +} + + +int Field_new_decimal::reset(void) +{ + store_value(&decimal_zero); + return 0; +} + + +/** + Generate max/min decimal value in case of overflow. + + @param decimal_value buffer for value + @param sign sign of value which caused overflow +*/ + +void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, + bool sign) +{ + DBUG_ENTER("Field_new_decimal::set_value_on_overflow"); + max_my_decimal(decimal_value, precision, decimals()); + if (sign) + { + if (unsigned_flag) + my_decimal_set_zero(decimal_value); + else + decimal_value->sign(TRUE); + } + DBUG_VOID_RETURN; +} + + +/** + Store decimal value in the binary buffer. + + Checks if decimal_value fits into field size. + If it does, stores the decimal in the buffer using binary format. + Otherwise sets maximal number that can be stored in the field. + + @param decimal_value my_decimal + @param [OUT] native_error the error returned by my_decimal::to_binary(). + + @retval + 0 ok + @retval + 1 error +*/ + +bool Field_new_decimal::store_value(const my_decimal *decimal_value, + int *native_error) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + DBUG_ENTER("Field_new_decimal::store_value"); +#ifdef DBUG_TRACE + { + char dbug_buff[DECIMAL_MAX_STR_LENGTH+2]; + DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, decimal_value))); + } +#endif + + /* check that we do not try to write negative value in unsigned field */ + if (unsigned_flag && decimal_value->sign()) + { + DBUG_PRINT("info", ("unsigned overflow")); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + decimal_value= &decimal_zero; + } +#ifdef DBUG_TRACE + { + char dbug_buff[DECIMAL_MAX_STR_LENGTH+2]; + DBUG_PRINT("info", ("saving with precision %d scale: %d value %s", + (int)precision, (int)dec, + dbug_decimal_as_string(dbug_buff, decimal_value))); + } +#endif + + *native_error= decimal_value->to_binary(ptr, precision, dec, + E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW); + + if (unlikely(*native_error == E_DEC_OVERFLOW)) + { + my_decimal buff; + DBUG_PRINT("info", ("overflow")); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + set_value_on_overflow(&buff, decimal_value->sign()); + buff.to_binary(ptr, precision, dec); + error= 1; + } + DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr, + bin_size);); + DBUG_RETURN(error); +} + + +bool Field_new_decimal::store_value(const my_decimal *decimal_value) +{ + int native_error; + bool rc= store_value(decimal_value, &native_error); + if (unlikely(!rc && native_error == E_DEC_TRUNCATED)) + set_note(WARN_DATA_TRUNCATED, 1); + return rc; +} + + +int Field_new_decimal::store(const char *from, size_t length, + CHARSET_INFO *charset_arg) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + my_decimal decimal_value; + THD *thd= get_thd(); + DBUG_ENTER("Field_new_decimal::store(char*)"); + + const char *end; + int err= str2my_decimal(E_DEC_FATAL_ERROR & + ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM), + from, length, charset_arg, + &decimal_value, &end); + + if (err == E_DEC_OVERFLOW) // Too many digits (>81) in the integer part + { + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + if (!thd->abort_on_warning) + { + set_value_on_overflow(&decimal_value, decimal_value.sign()); + store_decimal(&decimal_value); + } + DBUG_RETURN(1); + } + + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) + { + if (check_edom_and_important_data_truncation("decimal", + err && err != E_DEC_TRUNCATED, + charset_arg, + from, length, end)) + { + if (!thd->abort_on_warning) + { + if (err && err != E_DEC_TRUNCATED) + { + /* + If check_decimal() failed because of EDOM-alike error, + (e.g. E_DEC_BAD_NUM), we have to initialize decimal_value to zero. + Note: if check_decimal() failed because of truncation, + decimal_value is already properly initialized. + */ + my_decimal_set_zero(&decimal_value); + /* + TODO: check str2my_decimal() with HF. It seems to do + decimal_make_zero() on fatal errors, so my_decimal_set_zero() + is probably not needed here. + */ + } + store_decimal(&decimal_value); + } + DBUG_RETURN(1); + } + } + +#ifdef DBUG_TRACE + char dbug_buff[DECIMAL_MAX_STR_LENGTH+2]; + DBUG_PRINT("enter", ("value: %s", + dbug_decimal_as_string(dbug_buff, &decimal_value))); +#endif + int err2; + if (store_value(&decimal_value, &err2)) + DBUG_RETURN(1); + + /* + E_DEC_TRUNCATED means minor truncation, a note should be enough: + - in err: str2my_decimal() truncated '1e-1000000000000' to 0.0 + - in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2) + Also, we send a note if a string had some trailing spaces: '1.12 ' + */ + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION && + (err == E_DEC_TRUNCATED || + err2 == E_DEC_TRUNCATED || + end < from + length)) + set_note(WARN_DATA_TRUNCATED, 1); + DBUG_RETURN(0); +} + + +/** + @todo + Fix following when double2my_decimal when double2decimal + will return E_DEC_TRUNCATED always correctly +*/ + +int Field_new_decimal::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + my_decimal decimal_value; + int err; + THD *thd= get_thd(); + DBUG_ENTER("Field_new_decimal::store(double)"); + + err= double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr, + &decimal_value); + if (err) + { + if (check_overflow(err)) + set_value_on_overflow(&decimal_value, decimal_value.sign()); + /* Only issue a warning if store_value doesn't issue an warning */ + thd->got_warning= 0; + } + if (store_value(&decimal_value)) + err= 1; + else if (err && !thd->got_warning) + err= warn_if_overflow(err); + DBUG_RETURN(err); +} + + +int Field_new_decimal::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + my_decimal decimal_value; + int err; + + if ((err= int2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, + nr, unsigned_val, &decimal_value))) + { + if (check_overflow(err)) + set_value_on_overflow(&decimal_value, decimal_value.sign()); + /* Only issue a warning if store_value doesn't issue an warning */ + get_thd()->got_warning= 0; + } + if (store_value(&decimal_value)) + err= 1; + else if (err && !get_thd()->got_warning) + err= warn_if_overflow(err); + return err; +} + + +int Field_new_decimal::store_decimal(const my_decimal *decimal_value) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + return store_value(decimal_value); +} + + +int Field_new_decimal::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) +{ + my_decimal decimal_value; + return store_value(date2my_decimal(ltime, &decimal_value)); +} + + +my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(marked_for_read()); + DBUG_ENTER("Field_new_decimal::val_decimal"); + binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value, + precision, dec); + DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr, + bin_size);); + DBUG_RETURN(decimal_value); +} + + +int Field_new_decimal::cmp(const uchar *a,const uchar*b) const +{ + return memcmp(a, b, bin_size); +} + + +void Field_new_decimal::sort_string(uchar *buff, uint length) +{ + memcpy(buff, ptr, length); +} + + +void Field_new_decimal::sql_type(String &str) const +{ + CHARSET_INFO *cs= str.charset(); + str.length(cs->cset->snprintf(cs, (char*) str.ptr(), str.alloced_length(), + "decimal(%d,%d)", precision, (int)dec)); + add_zerofill_and_unsigned(str); +} + + +/** + Save the field metadata for new decimal fields. + + Saves the precision in the first byte and decimals() in the second + byte of the field metadata array at index of *metadata_ptr and + *(metadata_ptr + 1). + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ + +Binlog_type_info Field_new_decimal::binlog_type_info() const +{ + DBUG_ASSERT(Field_new_decimal::type() == binlog_type()); + return Binlog_type_info(Field_new_decimal::type(), precision + + (decimals() << 8), 2, binlog_signedness()); +} + + +/** + Returns the number of bytes field uses in row-based replication + row packed size. + + This method is used in row-based replication to determine the number + of bytes that the field consumes in the row record format. This is + used to skip fields in the master that do not exist on the slave. + + @param field_metadata Encoded size in field metadata + + @returns The size of the field based on the field metadata. +*/ +uint Field_new_decimal::pack_length_from_metadata(uint field_metadata) const +{ + uint const source_precision= (field_metadata >> 8U) & 0x00ff; + uint const source_decimal= field_metadata & 0x00ff; + uint const source_size= my_decimal_get_binary_size(source_precision, + source_decimal); + return (source_size); +} + + +bool Field_new_decimal::compatible_field_size(uint field_metadata, + const Relay_log_info * __attribute__((unused)), + uint16 mflags __attribute__((unused)), + int *order_var) const +{ + uint const source_precision= (field_metadata >> 8U) & 0x00ff; + uint const source_decimal= field_metadata & 0x00ff; + int order= compare(source_precision, precision); + *order_var= order != 0 ? order : compare(source_decimal, dec); + return true; +} + + +bool Field_new_decimal::is_equal(const Column_definition &new_field) const +{ + return ((new_field.type_handler() == type_handler()) && + ((new_field.flags & UNSIGNED_FLAG) == + (uint) (flags & UNSIGNED_FLAG)) && + ((new_field.flags & AUTO_INCREMENT_FLAG) <= + (uint) (flags & AUTO_INCREMENT_FLAG)) && + (new_field.length == max_display_length()) && + (new_field.decimals == dec)); +} + + +/** + Unpack a decimal field from row data. + + This method is used to unpack a decimal or numeric field from a master + whose size of the field is less than that of the slave. + + @param to Destination of the data + @param from Source of the data + @param param_data Precision (upper) and decimal (lower) values + + @return New pointer into memory based on from + length of the data +*/ +const uchar * +Field_new_decimal::unpack(uchar* to, const uchar *from, const uchar *from_end, + uint param_data) +{ + if (param_data == 0) + return Field::unpack(to, from, from_end, param_data); + + uint from_precision= (param_data & 0xff00) >> 8U; + uint from_decimal= param_data & 0x00ff; + uint length=pack_length(); + uint from_pack_len= my_decimal_get_binary_size(from_precision, from_decimal); + uint len= (param_data && (from_pack_len < length)) ? + from_pack_len : length; + if ((from_pack_len && (from_pack_len < length)) || + (from_precision < precision) || + (from_decimal < decimals())) + { + /* + If the master's data is smaller than the slave, we need to convert + the binary to decimal then resize the decimal converting it back to + a decimal and write that to the raw data buffer. + */ + decimal_digit_t dec_buf[DECIMAL_MAX_PRECISION]; + decimal_t dec_val; + dec_val.len= from_precision; + dec_val.buf= dec_buf; + /* + Note: bin2decimal does not change the length of the field. So it is + just the first step the resizing operation. The second step does the + resizing using the precision and decimals from the slave. + */ + bin2decimal((uchar *)from, &dec_val, from_precision, from_decimal); + decimal2bin(&dec_val, to, precision, decimals()); + } + else + { + if (from + len > from_end) + return 0; // Wrong data + memcpy(to, from, len); // Sizes are the same, just copy the data. + } + return from+len; +} + + +Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + if (flags & ZEROFILL_FLAG) + return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item); + switch (ctx.subst_constraint()) { + case IDENTITY_SUBST: + if (const_item->field_type() != MYSQL_TYPE_NEWDECIMAL || + const_item->decimal_scale() != decimals()) + { + VDec val(const_item); + if (val.is_null()) + { + DBUG_ASSERT(0); + return const_item; + } + /* + Truncate or extend the decimal value to the scale of the field. + See comments about truncation in the same place in + Field_time::get_equal_const_item(). + */ + my_decimal tmp; + val.round_to(&tmp, decimals(), TRUNCATE); + return new (thd->mem_root) Item_decimal(thd, field_name.str, &tmp, + decimals(), field_length); + } + break; + case ANY_SUBST: + break; + } + return const_item; +} + + +int Field_int::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) +{ + longlong v= TIME_to_ulonglong(ltime); + if (ltime->neg == 0) + return store(v, true); + return store(-v, false); +} + + +void Field_int::sql_type(String &res) const +{ + CHARSET_INFO *cs=res.charset(); + Name name= type_handler()->type_handler_signed()->name(); + res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), + "%.*s(%d)", (int) name.length(), name.ptr(), + (int) field_length)); + add_zerofill_and_unsigned(res); +} + + +/**************************************************************************** +** tiny int +****************************************************************************/ + +int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error; + longlong rnd; + + error= get_int(cs, from, len, &rnd, 255, -128, 127); + ptr[0]= unsigned_flag ? (char) (ulonglong) rnd : (char) rnd; + return error; +} + + +int Field_tiny::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + nr=rint(nr); + if (unsigned_flag) + { + if (nr < 0.0) + { + *ptr=0; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (nr > 255.0) + { + *ptr= (uchar) 255; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + *ptr= (uchar) nr; + } + else + { + if (nr < -128.0) + { + *ptr= (uchar) -128; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (nr > 127.0) + { + *ptr=127; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + *ptr=(uchar) (int) nr; + } + return error; +} + + +int Field_tiny::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + + if (unsigned_flag) + { + if (nr < 0 && !unsigned_val) + { + *ptr= 0; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if ((ulonglong) nr > (ulonglong) 255) + { + *ptr= (char) 255; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + *ptr=(char) nr; + } + else + { + if (nr < 0 && unsigned_val) + nr= 256; // Generate overflow + if (nr < -128) + { + *ptr= (char) -128; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (nr > 127) + { + *ptr=127; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + *ptr=(char) nr; + } + return error; +} + + +double Field_tiny::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + int tmp= unsigned_flag ? (int) ptr[0] : + (int) ((signed char*) ptr)[0]; + return (double) tmp; +} + + +longlong Field_tiny::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + int tmp= unsigned_flag ? (int) ptr[0] : + (int) ((signed char*) ptr)[0]; + return (longlong) tmp; +} + + +String *Field_tiny::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + DBUG_ASSERT(marked_for_read()); + long nr= unsigned_flag ? (long) ptr[0] : (long) ((signed char*) ptr)[0]; + return val_str_from_long(val_buffer, 5, -10, nr); +} + +bool Field_tiny::send(Protocol *protocol) +{ + DBUG_ASSERT(marked_for_read()); + Protocol_text *txt; + if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol))) + return send_numeric_zerofill_str(txt, PROTOCOL_SEND_TINY); + return protocol->store_tiny(Field_tiny::val_int()); +} + + +int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + signed char a,b; + a=(signed char) a_ptr[0]; b= (signed char) b_ptr[0]; + if (unsigned_flag) + return ((uchar) a < (uchar) b) ? -1 : ((uchar) a > (uchar) b) ? 1 : 0; + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +void Field_tiny::sort_string(uchar *to,uint length __attribute__((unused))) +{ + if (unsigned_flag) + *to= *ptr; + else + to[0] = (char) (ptr[0] ^ (uchar) 128); /* Revers signbit */ +} + +/**************************************************************************** + Field type short int (2 byte) +****************************************************************************/ + +int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int store_tmp; + int error; + longlong rnd; + + error= get_int(cs, from, len, &rnd, UINT_MAX16, INT_MIN16, INT_MAX16); + store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd; + int2store(ptr, store_tmp); + return error; +} + + +int Field_short::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + int16 res; + nr=rint(nr); + if (unsigned_flag) + { + if (nr < 0) + { + res=0; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (nr > (double) UINT_MAX16) + { + res=(int16) UINT_MAX16; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + res=(int16) (uint16) nr; + } + else + { + if (nr < (double) INT_MIN16) + { + res=INT_MIN16; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (nr > (double) INT_MAX16) + { + res=INT_MAX16; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + res=(int16) (int) nr; + } + int2store(ptr,res); + return error; +} + + +int Field_short::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + int16 res; + + if (unsigned_flag) + { + if (nr < 0L && !unsigned_val) + { + res=0; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if ((ulonglong) nr > (ulonglong) UINT_MAX16) + { + res=(int16) UINT_MAX16; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + res=(int16) (uint16) nr; + } + else + { + if (nr < 0 && unsigned_val) + nr= UINT_MAX16+1; // Generate overflow + + if (nr < INT_MIN16) + { + res=INT_MIN16; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (nr > (longlong) INT_MAX16) + { + res=INT_MAX16; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + res=(int16) nr; + } + int2store(ptr,res); + return error; +} + + +double Field_short::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + short j; + j=sint2korr(ptr); + return unsigned_flag ? (double) (unsigned short) j : (double) j; +} + +longlong Field_short::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + short j; + j=sint2korr(ptr); + return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j; +} + + +String *Field_short::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + DBUG_ASSERT(marked_for_read()); + short j= sint2korr(ptr); + long nr= unsigned_flag ? (long) (unsigned short) j : (long) j; + return val_str_from_long(val_buffer, 7, -10, nr); +} + + +bool Field_short::send(Protocol *protocol) +{ + DBUG_ASSERT(marked_for_read()); + Protocol_text *txt; + if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol))) + return send_numeric_zerofill_str(txt, PROTOCOL_SEND_SHORT); + return protocol->store_short(Field_short::val_int()); +} + + +int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + short a,b; + a=sint2korr(a_ptr); + b=sint2korr(b_ptr); + + if (unsigned_flag) + return ((unsigned short) a < (unsigned short) b) ? -1 : + ((unsigned short) a > (unsigned short) b) ? 1 : 0; + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +void Field_short::sort_string(uchar *to,uint length __attribute__((unused))) +{ + if (unsigned_flag) + to[0] = ptr[1]; + else + to[0] = (char) (ptr[1] ^ 128); /* Revers signbit */ + to[1] = ptr[0]; +} + +/**************************************************************************** + Field type medium int (3 byte) +****************************************************************************/ + +int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int store_tmp; + int error; + longlong rnd; + + error= get_int(cs, from, len, &rnd, UINT_MAX24, INT_MIN24, INT_MAX24); + store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd; + int3store(ptr, store_tmp); + return error; +} + + +int Field_medium::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + nr=rint(nr); + if (unsigned_flag) + { + if (nr < 0) + { + int3store(ptr,0); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (nr >= (double) (long) (1L << 24)) + { + uint32 tmp=(uint32) (1L << 24)-1L; + int3store(ptr,tmp); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + int3store(ptr,(uint32) nr); + } + else + { + if (nr < (double) INT_MIN24) + { + long tmp=(long) INT_MIN24; + int3store(ptr,tmp); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (nr > (double) INT_MAX24) + { + long tmp=(long) INT_MAX24; + int3store(ptr,tmp); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + int3store(ptr,(long) nr); + } + return error; +} + + +int Field_medium::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + + if (unsigned_flag) + { + if (nr < 0 && !unsigned_val) + { + int3store(ptr,0); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24)) + { + long tmp= (long) (1L << 24)-1L; + int3store(ptr,tmp); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + int3store(ptr,(uint32) nr); + } + else + { + if (nr < 0 && unsigned_val) + nr= (ulonglong) (long) (1L << 24); // Generate overflow + + if (nr < (longlong) INT_MIN24) + { + long tmp= (long) INT_MIN24; + int3store(ptr,tmp); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (nr > (longlong) INT_MAX24) + { + long tmp=(long) INT_MAX24; + int3store(ptr,tmp); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + int3store(ptr,(long) nr); + } + return error; +} + + +double Field_medium::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); + return (double) j; +} + + +longlong Field_medium::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); + return (longlong) j; +} + + +String *Field_medium::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + DBUG_ASSERT(marked_for_read()); + long nr= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); + return val_str_from_long(val_buffer, 10, -10, nr); +} + + +String *Field_int::val_str_from_long(String *val_buffer, + uint max_char_length, + int radix, long nr) +{ + CHARSET_INFO *cs= &my_charset_numeric; + uint length; + uint mlength= MY_MAX(field_length + 1, max_char_length * cs->mbmaxlen); + val_buffer->alloc(mlength); + char *to=(char*) val_buffer->ptr(); + length= (uint) cs->long10_to_str(to, mlength, radix, nr); + val_buffer->length(length); + if (zerofill) + prepend_zeros(val_buffer); /* purecov: inspected */ + val_buffer->set_charset(cs); + return val_buffer; +} + + +bool Field_medium::send(Protocol *protocol) +{ + DBUG_ASSERT(marked_for_read()); + Protocol_text *txt; + if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol))) + return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG); + return protocol->store_long(Field_medium::val_int()); +} + + +int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + long a,b; + if (unsigned_flag) + { + a=uint3korr(a_ptr); + b=uint3korr(b_ptr); + } + else + { + a=sint3korr(a_ptr); + b=sint3korr(b_ptr); + } + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +void Field_medium::sort_string(uchar *to,uint length __attribute__((unused))) +{ + if (unsigned_flag) + to[0] = ptr[2]; + else + to[0] = (uchar) (ptr[2] ^ 128); /* Revers signbit */ + to[1] = ptr[1]; + to[2] = ptr[0]; +} + + +/**************************************************************************** +** long int +****************************************************************************/ + +int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + long store_tmp; + int error; + longlong rnd; + + error= get_int(cs, from, len, &rnd, UINT_MAX32, INT_MIN32, INT_MAX32); + store_tmp= unsigned_flag ? (long) (ulonglong) rnd : (long) rnd; + int4store(ptr, store_tmp); + return error; +} + + +int Field_long::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + int32 res; + nr=rint(nr); + if (unsigned_flag) + { + if (nr < 0) + { + res=0; + error= 1; + } + else if (nr > (double) UINT_MAX32) + { + res= UINT_MAX32; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else + res=(int32) (ulong) nr; + } + else + { + if (nr < (double) INT_MIN32) + { + res=(int32) INT_MIN32; + error= 1; + } + else if (nr > (double) INT_MAX32) + { + res=(int32) INT_MAX32; + error= 1; + } + else + res=(int32) (longlong) nr; + } + if (unlikely(error)) + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + + int4store(ptr,res); + return error; +} + + +int Field_long::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + int32 res; + + if (unsigned_flag) + { + if (nr < 0 && !unsigned_val) + { + res=0; + error= 1; + } + else if ((ulonglong) nr >= (1LL << 32)) + { + res=(int32) (uint32) ~0L; + error= 1; + } + else + res=(int32) (uint32) nr; + } + else + { + if (nr < 0 && unsigned_val) + nr= ((longlong) INT_MAX32) + 1; // Generate overflow + if (nr < (longlong) INT_MIN32) + { + res=(int32) INT_MIN32; + error= 1; + } + else if (nr > (longlong) INT_MAX32) + { + res=(int32) INT_MAX32; + error= 1; + } + else + res=(int32) nr; + } + if (unlikely(error)) + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + + int4store(ptr,res); + return error; +} + + +double Field_long::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + int32 j; + j=sint4korr(ptr); + return unsigned_flag ? (double) (uint32) j : (double) j; +} + +longlong Field_long::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + int32 j; + /* See the comment in Field_long::store(long long) */ + DBUG_ASSERT(!table || table->in_use == current_thd); + j=sint4korr(ptr); + return unsigned_flag ? (longlong) (uint32) j : (longlong) j; +} + + +String *Field_long::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + DBUG_ASSERT(marked_for_read()); + long nr= unsigned_flag ? (long) uint4korr(ptr) : sint4korr(ptr); + return val_str_from_long(val_buffer, 12, unsigned_flag ? 10 : -10, nr); +} + + +bool Field_long::send(Protocol *protocol) +{ + DBUG_ASSERT(marked_for_read()); + Protocol_text *txt; + if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol))) + return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG); + return protocol->store_long(Field_long::val_int()); +} + + +int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + int32 a,b; + a=sint4korr(a_ptr); + b=sint4korr(b_ptr); + if (unsigned_flag) + return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +void Field_long::sort_string(uchar *to,uint length __attribute__((unused))) +{ + if (unsigned_flag) + to[0] = ptr[3]; + else + to[0] = (char) (ptr[3] ^ 128); /* Revers signbit */ + to[1] = ptr[2]; + to[2] = ptr[1]; + to[3] = ptr[0]; +} + + +/**************************************************************************** + Field type longlong int (8 bytes) +****************************************************************************/ + +int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + char *end; + ulonglong tmp; + + tmp= cs->strntoull10rnd(from, len, unsigned_flag, &end, &error); + if (unlikely(error == MY_ERRNO_ERANGE)) + { + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && + check_int(cs, from, len, end, error)) + error= 1; + else + error= 0; + int8store(ptr,tmp); + return error; +} + + +int Field_longlong::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + Converter_double_to_longlong conv(nr, unsigned_flag); + + if (unlikely(conv.error())) + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + + int8store(ptr, conv.result()); + return conv.error(); +} + + +int Field_longlong::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + + if (unlikely(nr < 0)) // Only possible error + { + /* + if field is unsigned and value is signed (< 0) or + if field is signed and value is unsigned we have an overflow + */ + if (unsigned_flag != unsigned_val) + { + nr= unsigned_flag ? (ulonglong) 0 : (ulonglong) LONGLONG_MAX; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; + } + } + + int8store(ptr,nr); + return error; +} + + +double Field_longlong::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + longlong j; + j=sint8korr(ptr); + /* The following is open coded to avoid a bug in gcc 3.3 */ + if (unsigned_flag) + { + ulonglong tmp= (ulonglong) j; + return ulonglong2double(tmp); + } + return (double) j; +} + + +longlong Field_longlong::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + longlong j; + j=sint8korr(ptr); + return j; +} + + +String *Field_longlong::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + CHARSET_INFO *cs= &my_charset_numeric; + uint length; + uint mlength=MY_MAX(field_length+1,22*cs->mbmaxlen); + val_buffer->alloc(mlength); + char *to=(char*) val_buffer->ptr(); + longlong j; + j=sint8korr(ptr); + + length=(uint) (cs->longlong10_to_str)(to, mlength, + unsigned_flag ? 10 : -10, j); + val_buffer->length(length); + if (zerofill) + prepend_zeros(val_buffer); + val_buffer->set_charset(cs); + return val_buffer; +} + + +bool Field_longlong::send(Protocol *protocol) +{ + DBUG_ASSERT(marked_for_read()); + Protocol_text *txt; + if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol))) + return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONGLONG); + return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag); +} + + +int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + longlong a,b; + a=sint8korr(a_ptr); + b=sint8korr(b_ptr); + if (unsigned_flag) + return ((ulonglong) a < (ulonglong) b) ? -1 : + ((ulonglong) a > (ulonglong) b) ? 1 : 0; + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +void Field_longlong::sort_string(uchar *to,uint length __attribute__((unused))) +{ + if (unsigned_flag) + to[0] = ptr[7]; + else + to[0] = (char) (ptr[7] ^ 128); /* Revers signbit */ + to[1] = ptr[6]; + to[2] = ptr[5]; + to[3] = ptr[4]; + to[4] = ptr[3]; + to[5] = ptr[2]; + to[6] = ptr[1]; + to[7] = ptr[0]; +} + + +void Field_longlong::set_max() +{ + DBUG_ASSERT(marked_for_write_or_computed()); + set_notnull(); + int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX); +} + +bool Field_longlong::is_max() +{ + DBUG_ASSERT(marked_for_read()); + if (unsigned_flag) + { + ulonglong j; + j= uint8korr(ptr); + return j == ULONGLONG_MAX; + } + longlong j; + j= sint8korr(ptr); + return j == LONGLONG_MAX; +} + +/* + Floating-point numbers + */ + +/**************************************************************************** + single precision float +****************************************************************************/ + +Field_float::Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, + decimal_digits_t dec_arg, + bool zero_arg, bool unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), + zero_arg, unsigned_arg) +{ +} + +Field_float::Field_float(uint32 len_arg, bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, + decimal_digits_t dec_arg) + :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, + NONE, field_name_arg, + (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), + 0, 0) +{ +} + + +int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs) +{ + int error; + Field_float::store(get_double(from, len, cs, &error)); + return error; +} + + +int Field_float::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= truncate_double(&nr, field_length, + not_fixed ? NOT_FIXED_DEC : dec, + unsigned_flag, FLT_MAX); + if (unlikely(error)) + { + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + if (error < 0) // Wrong double value + { + error= 1; + set_null(); + } + } + float j= (float)nr; + + float4store(ptr,j); + return error; +} + + +int Field_float::store(longlong nr, bool unsigned_val) +{ + return Field_float::store(unsigned_val ? ulonglong2double((ulonglong) nr) : + (double) nr); +} + + +double Field_float::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + float j; + float4get(j,ptr); + return ((double) j); +} + +longlong Field_float::val_int(void) +{ + float j; + float4get(j,ptr); + return Converter_double_to_longlong(j, false).result(); +} + + +String *Field_float::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH); + + if (Float(ptr).to_string(val_buffer, dec)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return val_buffer; + } + + if (zerofill) + prepend_zeros(val_buffer); + return val_buffer; +} + + +int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + float a,b; + float4get(a,a_ptr); + float4get(b,b_ptr); + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +#define FLT_EXP_DIG (sizeof(float)*8-FLT_MANT_DIG) + +void Field_float::sort_string(uchar *to,uint length __attribute__((unused))) +{ + float nr; + float4get(nr,ptr); + + uchar *tmp= to; + if (nr == (float) 0.0) + { /* Change to zero string */ + tmp[0]=(uchar) 128; + bzero((char*) tmp+1,sizeof(nr)-1); + } + else + { +#ifdef WORDS_BIGENDIAN + memcpy(tmp, &nr, sizeof(nr)); +#else + tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0]; +#endif + if (tmp[0] & 128) /* Negative */ + { /* make complement */ + uint i; + for (i=0 ; i < sizeof(nr); i++) + tmp[i]= (uchar) (tmp[i] ^ (uchar) 255); + } + else + { + ushort exp_part=(((ushort) tmp[0] << 8) | (ushort) tmp[1] | + (ushort) 32768); + exp_part+= (ushort) 1 << (16-1-FLT_EXP_DIG); + tmp[0]= (uchar) (exp_part >> 8); + tmp[1]= (uchar) exp_part; + } + } +} + + +bool Field_float::send(Protocol *protocol) +{ + DBUG_ASSERT(marked_for_read()); + Protocol_text *txt; + if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol))) + return send_numeric_zerofill_str(txt, PROTOCOL_SEND_FLOAT); + return protocol->store_float((float) Field_float::val_real(), dec); +} + + +/** + Save the field metadata for float fields. + + Saves the pack length in the first byte. + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +Binlog_type_info Field_float::binlog_type_info() const +{ + DBUG_ASSERT(Field_float::type() == binlog_type()); + return Binlog_type_info(Field_float::type(), pack_length(), 1, + binlog_signedness()); +} + + +/**************************************************************************** + double precision floating point numbers +****************************************************************************/ + +Field_double::Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, + decimal_digits_t dec_arg, + bool zero_arg, bool unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), + zero_arg, unsigned_arg) +{ +} + +Field_double::Field_double(uint32 len_arg, bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, + decimal_digits_t dec_arg) + :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, + NONE, field_name_arg, + (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), + 0, 0) +{ +} + +Field_double::Field_double(uint32 len_arg, bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, + decimal_digits_t dec_arg, bool not_fixed_arg) + :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, + NONE, field_name_arg, + (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), + 0, 0) +{ + not_fixed= not_fixed_arg; +} + +int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs) +{ + int error; + Field_double::store(get_double(from, len, cs, &error)); + return error; +} + + +int Field_double::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= truncate_double(&nr, field_length, + not_fixed ? NOT_FIXED_DEC : dec, + unsigned_flag, DBL_MAX); + if (unlikely(error)) + { + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + if (error < 0) // Wrong double value + { + error= 1; + set_null(); + } + } + + float8store(ptr,nr); + return error; +} + + +int Field_double::store(longlong nr, bool unsigned_val) +{ + return Field_double::store(unsigned_val ? ulonglong2double((ulonglong) nr) : + (double) nr); +} + +/* + If a field has fixed length, truncate the double argument pointed to by 'nr' + appropriately. + Also ensure that the argument is within [-max_value; max_value] range. + + return + 0 ok + -1 Illegal double value + 1 Value was truncated +*/ + +int truncate_double(double *nr, uint field_length, decimal_digits_t dec, + bool unsigned_flag, double max_value) +{ + int error= 0; + double res= *nr; + + if (std::isnan(res)) + { + *nr= 0; + return -1; + } + else if (unsigned_flag && res < 0) + { + *nr= 0; + return 1; + } + + if (dec < FLOATING_POINT_DECIMALS) + { + uint order= field_length - dec; + uint step= array_elements(log_10) - 1; + double max_value_by_dec= 1.0; + for (; order > step; order-= step) + max_value_by_dec*= log_10[step]; + max_value_by_dec*= log_10[order]; + max_value_by_dec-= 1.0 / log_10[dec]; + set_if_smaller(max_value, max_value_by_dec); + + /* Check for infinity so we don't get NaN in calculations */ + if (!std::isinf(res)) + { + double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec]; + res= floor(res) + tmp; + } + } + + if (res < -max_value) + { + res= -max_value; + error= 1; + } + else if (res > max_value) + { + res= max_value; + error= 1; + } + + *nr= res; + return error; +} + +/* + Convert double to longlong / ulonglong. + If double is outside of the supported range, + adjust m_result and set m_error. + + @param nr Number to convert + @param unsigned_flag true if result is unsigned +*/ + +Value_source:: +Converter_double_to_longlong::Converter_double_to_longlong(double nr, + bool unsigned_flag) + :m_error(false) +{ + nr= rint(nr); + if (unsigned_flag) + { + if (nr < 0) + { + m_result= 0; + m_error= true; + } + else if (nr >= (double) ULONGLONG_MAX) + { + m_result= ~(longlong) 0; + m_error= true; + } + else + m_result= (longlong) double2ulonglong(nr); + } + else + { + if (nr <= (double) LONGLONG_MIN) + { + m_result= LONGLONG_MIN; + m_error= (nr < (double) LONGLONG_MIN); + } + else if (nr >= (double) (ulonglong) LONGLONG_MAX) + { + m_result= LONGLONG_MAX; + m_error= (nr > (double) LONGLONG_MAX); + } + else + m_result= (longlong) nr; + } +} + + +void Value_source:: +Converter_double_to_longlong::push_warning(THD *thd, + double nr, + bool unsigned_flag) +{ + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_DATA_OVERFLOW, ER_THD(thd, ER_DATA_OVERFLOW), + ErrConvDouble(nr).ptr(), + unsigned_flag ? "UNSIGNED INT" : "INT"); +} + + +int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint) +{ + return store(TIME_to_double(ltime)); +} + + +double Field_double::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + double j; + float8get(j,ptr); + return j; +} + + +longlong Field_double::val_int_from_real(bool want_unsigned_result) +{ + Converter_double_to_longlong conv(val_real(), want_unsigned_result); + if (unlikely(!want_unsigned_result && conv.error())) + conv.push_warning(get_thd(), Field_double::val_real(), false); + return conv.result(); +} + + +my_decimal *Field_real::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(marked_for_read()); + double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value); + return decimal_value; +} + + +bool Field_real::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) +{ + DBUG_ASSERT(marked_for_read()); + double nr= val_real(); + return double_to_datetime_with_warn(get_thd(), nr, ltime, fuzzydate, + table->s, field_name.str); +} + + +Item *Field_real::get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + if (flags & ZEROFILL_FLAG) + return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item); + switch (ctx.subst_constraint()) { + case IDENTITY_SUBST: + if (const_item->decimal_scale() != Field_real::decimals()) + { + double val= const_item->val_real(); + return new (thd->mem_root) Item_float(thd, val, Field_real::decimals()); + } + break; + case ANY_SUBST: + break; + } + return const_item; +} + + +void Field_real::sql_type(String &res) const +{ + const Name name= type_handler()->name(); + if (dec >= FLOATING_POINT_DECIMALS) + { + res.set_ascii(name.ptr(), name.length()); + } + else + { + CHARSET_INFO *cs= res.charset(); + res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), + "%.*s(%d,%d)", (int) name.length(), name.ptr(), + (int) field_length,dec)); + } + add_zerofill_and_unsigned(res); +} + + +String *Field_double::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH); + double nr; + float8get(nr,ptr); + + uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE; + if (val_buffer->alloc(to_length)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return val_buffer; + } + + char *to=(char*) val_buffer->ptr(); + size_t len; + + if (dec >= FLOATING_POINT_DECIMALS) + len= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, to_length - 1, to, NULL); + else + len= my_fcvt(nr, dec, to, NULL); + + val_buffer->length((uint) len); + if (zerofill) + prepend_zeros(val_buffer); + val_buffer->set_charset(&my_charset_numeric); + return val_buffer; +} + +bool Field_double::send(Protocol *protocol) +{ + DBUG_ASSERT(marked_for_read()); + Protocol_text *txt; + if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol))) + return send_numeric_zerofill_str(txt, PROTOCOL_SEND_DOUBLE); + return protocol->store_double(Field_double::val_real(), dec); +} + + +int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + double a,b; + float8get(a,a_ptr); + float8get(b,b_ptr); + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + + +#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG) + +/* The following should work for IEEE */ + +void Field_double::sort_string(uchar *to,uint length __attribute__((unused))) +{ + double nr; + float8get(nr,ptr); + change_double_for_sort(nr, to); +} + + +/** + Save the field metadata for double fields. + + Saves the pack length in the first byte of the field metadata array + at index of *metadata_ptr. + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +Binlog_type_info Field_double::binlog_type_info() const +{ + DBUG_ASSERT(Field_double::type() == binlog_type()); + return Binlog_type_info(Field_double::type(), pack_length(), 1, + binlog_signedness()); +} + + +/** + TIMESTAMP type holds datetime values in range from 1970-01-01 00:00:01 UTC to + 2038-01-01 00:00:00 UTC stored as number of seconds since Unix + Epoch in UTC. + + Actually SQL-99 says that we should allow niladic functions (like NOW()) + as defaults for any field. The current limitation (only NOW() and only + for TIMESTAMP and DATETIME fields) are because of restricted binary .frm + format and should go away in the future. + + Also because of this limitation of binary .frm format we use 5 different + unireg_check values with TIMESTAMP field to distinguish various cases of + DEFAULT or ON UPDATE values. These values are: + + TIMESTAMP_OLD_FIELD - old timestamp, if there was not any fields with + auto-set-on-update (or now() as default) in this table before, then this + field has NOW() as default and is updated when row changes, else it is + field which has 0 as default value and is not automatically updated. + TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update + automatically (TIMESTAMP DEFAULT NOW()), not used in Field since 10.2.2 + TIMESTAMP_UN_FIELD - field which is set on update automatically but has not + NOW() as default (but it may has 0 or some other const timestamp as + default) (TIMESTAMP ON UPDATE NOW()). + TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on + update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW()), not used in Field since 10.2.2 + NONE - field which is not auto-set on update with some other than NOW() + default value (TIMESTAMP DEFAULT 0). + + Note that TIMESTAMP_OLD_FIELDs are never created explicitly now, they are + left only for preserving ability to read old tables. Such fields replaced + with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE. This is + because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for + "TIMESTAMP DEFAULT 'Const'" field. (Old timestamps allowed such + specification too but ignored default value for first timestamp, which of + course is non-standard.) In most cases user won't notice any change, only + exception is different behavior of old/new timestamps during ALTER TABLE. + */ + +Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, + TABLE_SHARE *share) + :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg) +{ + /* For 4.0 MYD and 4.0 InnoDB compatibility */ + flags|= UNSIGNED_FLAG; + if (unireg_check != NONE) + { + /* + We mark the flag with TIMESTAMP_FLAG to indicate to the client that + this field will be automatically updated on insert. + */ + flags|= TIMESTAMP_FLAG; + if (unireg_check != TIMESTAMP_DN_FIELD) + flags|= ON_UPDATE_NOW_FLAG; + } +} + + +sql_mode_t +Field_timestamp::conversion_depends_on_sql_mode(THD *thd, Item *expr) const +{ + return expr->datetime_precision(thd) > decimals() ? + MODE_TIME_ROUND_FRACTIONAL : 0; +} + + +int Field_timestamp::save_in_field(Field *to) +{ + ulong sec_part; + my_time_t ts= get_timestamp(&sec_part); + return to->store_timestamp_dec(Timeval(ts, sec_part), decimals()); +} + +my_time_t Field_timestamp0::get_timestamp(const uchar *pos, + ulong *sec_part) const +{ + DBUG_ASSERT(marked_for_read()); + *sec_part= 0; + return sint4korr(pos); +} + + +bool Field_timestamp0::val_native(Native *to) +{ + DBUG_ASSERT(marked_for_read()); + my_time_t sec= (my_time_t) sint4korr(ptr); + return Timestamp_or_zero_datetime(Timestamp(sec, 0), sec == 0). + to_native(to, 0); +} + + +int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt, + const ErrConv *str, int was_cut) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + static const Timestamp zero(0, 0); + + // Handle totally bad values + if (!dt->is_valid_datetime()) + { + set_datetime_warning(WARN_DATA_TRUNCATED, str, "datetime", 1); + store_TIMESTAMP(zero); + return 1; + } + + // Handle values that do not need DATETIME to TIMESTAMP conversion + if (!dt->get_mysql_time()->month) + { + /* + Zero date is allowed by the current sql_mode. Store zero timestamp. + Return success or a warning about non-fatal truncation, e.g.: + INSERT INTO t1 (ts) VALUES ('0000-00-00 00:00:00 some tail'); + */ + store_TIMESTAMP(zero); + return store_TIME_return_code_with_warnings(was_cut, str, "datetime"); + } + + // Convert DATETIME to TIMESTAMP + uint conversion_error; + const MYSQL_TIME *l_time= dt->get_mysql_time(); + my_time_t timestamp= TIME_to_timestamp(thd, l_time, &conversion_error); + if (timestamp == 0 && l_time->second_part == 0) + { + set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, "datetime", 1); + store_TIMESTAMP(zero); + return 1; // date was fine but pointed to a DST gap + } + + // Store the value + DBUG_ASSERT(!dt->fraction_remainder(decimals())); + store_TIMESTAMP(Timestamp(timestamp, l_time->second_part)); + + // Calculate return value and send warnings if needed + if (unlikely(conversion_error)) // e.g. DATETIME in the DST gap + { + set_datetime_warning(conversion_error, str, "datetime", 1); + return 1; + } + return store_TIME_return_code_with_warnings(was_cut, str, "datetime"); +} + + +date_conv_mode_t Timestamp::sql_mode_for_timestamp(THD *thd) +{ + // We don't want to store invalid or fuzzy datetime values in TIMESTAMP + return date_conv_mode_t((thd->variables.sql_mode & MODE_NO_ZERO_DATE) | + MODE_NO_ZERO_IN_DATE); +} + + +int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec) +{ + int warn; + ErrConvTime str(ltime); + THD *thd= get_thd(); + Datetime dt(thd, &warn, ltime, Timestamp::DatetimeOptions(thd), decimals()); + return store_TIME_with_warning(thd, &dt, &str, warn); +} + + +int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs) +{ + ErrConvString str(from, len, cs); + THD *thd= get_thd(); + MYSQL_TIME_STATUS st; + Datetime dt(thd, &st, from, len, cs, Timestamp::DatetimeOptions(thd), decimals()); + return store_TIME_with_warning(thd, &dt, &str, st.warnings); +} + + +int Field_timestamp::store(double nr) +{ + int error; + ErrConvDouble str(nr); + THD *thd= get_thd(); + Datetime dt(thd, &error, nr, Timestamp::DatetimeOptions(thd), decimals()); + return store_TIME_with_warning(thd, &dt, &str, error); +} + + +int Field_timestamp::store(longlong nr, bool unsigned_val) +{ + int error; + Longlong_hybrid tmp(nr, unsigned_val); + ErrConvInteger str(tmp); + THD *thd= get_thd(); + Datetime dt(&error, tmp, Timestamp::DatetimeOptions(thd)); + return store_TIME_with_warning(thd, &dt, &str, error); +} + + +int Field_timestamp::store_timestamp_dec(const timeval &ts, uint dec) +{ + int warn= 0; + time_round_mode_t mode= Datetime::default_round_mode(get_thd()); + store_TIMESTAMP(Timestamp(ts).round(decimals(), mode, &warn)); + if (warn) + { + /* + We're here if rounding would overflow outside of the supported TIMESTAMP + range, so truncation happened instead: + CREATE TABLE t1 (a TIMESTAMP(6)); + INSERT INTO t1 VALUES ('maximum-possible-timestamp.999999'); + ALTER TABLE t1 MODIFY a TIMESTAMP(5); + SELECT * FROM t1; --> 'maximum-possible-timestamp.99999' (5 digits) + Raise a warning, like DATETIME does for '9999-12-31 23:59:59.999999'. + */ + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + } + if (ts.tv_sec == 0 && ts.tv_usec == 0 && + get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE) + return zero_time_stored_return_code_with_warning(); + return 0; +} + + +int Field_timestamp::zero_time_stored_return_code_with_warning() +{ + if (get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE) + { + ErrConvString s( + STRING_WITH_LEN("0000-00-00 00:00:00.000000") - (decimals() ? 6 - decimals() : 7), + system_charset_info); + set_datetime_warning(WARN_DATA_TRUNCATED, &s, "datetime", 1); + return 1; + } + return 0; + +} + + +int Field_timestamp::store_native(const Native &value) +{ + if (!value.length()) // Zero datetime + { + reset(); + return zero_time_stored_return_code_with_warning(); + } + /* + The exact second precision is not important here. + Field_timestamp*::store_timestamp_dec() do not use the "dec" parameter. + Passing TIME_SECOND_PART_DIGITS is OK. + */ + return store_timestamp_dec(Timestamp(value).tv(), TIME_SECOND_PART_DIGITS); +} + + +longlong Field_timestamp::val_int(void) +{ + MYSQL_TIME ltime; + if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd()))) + return 0; + + return ltime.year * 10000000000LL + ltime.month * 100000000LL + + ltime.day * 1000000L + ltime.hour * 10000L + + ltime.minute * 100 + ltime.second; +} + + +String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) +{ + MYSQL_TIME ltime; + uint32 temp, temp2; + uint dec; + char *to; + + val_buffer->alloc(field_length+1); + to= (char*) val_buffer->ptr(); + val_buffer->length(field_length); + + if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd()))) + { /* Zero time is "000000" */ + val_ptr->set(zero_timestamp, field_length, &my_charset_numeric); + return val_ptr; + } + val_buffer->set_charset(&my_charset_numeric); // Safety + + temp= ltime.year % 100; + if (temp < YY_PART_YEAR - 1) + { + *to++= '2'; + *to++= '0'; + } + else + { + *to++= '1'; + *to++= '9'; + } + temp2=temp/10; temp=temp-temp2*10; + *to++= (char) ('0'+(char) (temp2)); + *to++= (char) ('0'+(char) (temp)); + *to++= '-'; + temp=ltime.month; + temp2=temp/10; temp=temp-temp2*10; + *to++= (char) ('0'+(char) (temp2)); + *to++= (char) ('0'+(char) (temp)); + *to++= '-'; + temp=ltime.day; + temp2=temp/10; temp=temp-temp2*10; + *to++= (char) ('0'+(char) (temp2)); + *to++= (char) ('0'+(char) (temp)); + *to++= ' '; + temp=ltime.hour; + temp2=temp/10; temp=temp-temp2*10; + *to++= (char) ('0'+(char) (temp2)); + *to++= (char) ('0'+(char) (temp)); + *to++= ':'; + temp=ltime.minute; + temp2=temp/10; temp=temp-temp2*10; + *to++= (char) ('0'+(char) (temp2)); + *to++= (char) ('0'+(char) (temp)); + *to++= ':'; + temp=ltime.second; + temp2=temp/10; temp=temp-temp2*10; + *to++= (char) ('0'+(char) (temp2)); + *to++= (char) ('0'+(char) (temp)); + *to= 0; + val_buffer->set_charset(&my_charset_numeric); + + if ((dec= decimals())) + { + ulong sec_part= (ulong) sec_part_shift(ltime.second_part, dec); + char *buf= const_cast<char*>(val_buffer->ptr() + MAX_DATETIME_WIDTH); + for (int i= dec; i > 0; i--, sec_part/= 10) + buf[i]= (char)(sec_part % 10) + '0'; + buf[0]= '.'; + buf[dec + 1]= 0; + } + return val_buffer; +} + + +bool +Field_timestamp::validate_value_in_record(THD *thd, const uchar *record) const +{ + DBUG_ASSERT(!is_null_in_record(record)); + ulong sec_part; + return !get_timestamp(ptr_in_record(record), &sec_part) && !sec_part && + bool(sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != false; +} + + +bool Field_timestamp::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) +{ + ulong sec_part; + my_time_t ts= get_timestamp(&sec_part); + return get_thd()->timestamp_to_TIME(ltime, ts, sec_part, fuzzydate); +} + + +bool Field_timestamp0::send(Protocol *protocol) +{ + MYSQL_TIME ltime; + Field_timestamp0::get_date(<ime, date_mode_t(0)); + return protocol->store_datetime(<ime, 0); +} + + +int Field_timestamp0::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + int32 a,b; + a=sint4korr(a_ptr); + b=sint4korr(b_ptr); + return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; +} + + +void Field_timestamp0::sort_string(uchar *to,uint length __attribute__((unused))) +{ + to[0] = ptr[3]; + to[1] = ptr[2]; + to[2] = ptr[1]; + to[3] = ptr[0]; +} + + +int Field_timestamp0::set_time() +{ + set_notnull(); + store_TIMESTAMP(Timestamp(get_thd()->query_start(), 0)); + return 0; +} + + +bool Field_timestamp::load_data_set_no_data(THD *thd, bool fixed_format) +{ + if (!maybe_null()) + { + /* + Timestamp fields that are NOT NULL are autoupdated if there is no + corresponding value in the data file. + */ + set_time(); + set_has_explicit_value(); + return false; + } + return Field::load_data_set_no_data(thd, fixed_format); +} + + +bool Field_timestamp::load_data_set_null(THD *thd) +{ + if (!maybe_null()) + { + /* + Timestamp fields that are NOT NULL are autoupdated if there is no + corresponding value in the data file. + */ + set_time(); + } + else + { + reset(); + set_null(); + } + set_has_explicit_value(); // Do not auto-update this field + return false; +} + + +#ifdef NOT_USED +static void store_native(ulonglong num, uchar *to, uint bytes) +{ + switch(bytes) { + case 1: *to= (uchar)num; break; + case 2: shortstore(to, (ushort)num); break; + case 3: int3store(to, num); /* Sic!*/ break; + case 4: longstore(to, (ulong)num); break; + case 8: longlongstore(to, num); break; + default: DBUG_ASSERT(0); + } +} + +static longlong read_native(const uchar *from, uint bytes) +{ + switch(bytes) { + case 1: return from[0]; + case 2: { uint16 tmp; shortget(tmp, from); return tmp; } + case 3: return uint3korr(from); + case 4: { uint32 tmp; longget(tmp, from); return tmp; } + case 8: { longlong tmp; longlongget(tmp, from); return tmp; } + default: DBUG_ASSERT(0); return 0; + } +} +#endif + + +void Field_timestamp_hires::store_TIMEVAL(const timeval &tv) +{ + mi_int4store(ptr, tv.tv_sec); + store_bigendian(sec_part_shift(tv.tv_usec, dec), ptr+4, sec_part_bytes(dec)); +} + +my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos, + ulong *sec_part) const +{ + DBUG_ASSERT(marked_for_read()); + *sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes(dec)), dec); + return mi_uint4korr(pos); +} + + +bool Field_timestamp_hires::val_native(Native *to) +{ + DBUG_ASSERT(marked_for_read()); + struct timeval tm; + tm.tv_sec= mi_uint4korr(ptr); + tm.tv_usec= (ulong) sec_part_unshift(read_bigendian(ptr+4, sec_part_bytes(dec)), dec); + return Timestamp_or_zero_datetime(Timestamp(tm), tm.tv_sec == 0). + to_native(to, dec); +} + + +double Field_timestamp_with_dec::val_real(void) +{ + MYSQL_TIME ltime; + if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd()))) + return 0; + + return ltime.year * 1e10 + ltime.month * 1e8 + + ltime.day * 1e6 + ltime.hour * 1e4 + + ltime.minute * 1e2 + ltime.second + ltime.second_part*1e-6; +} + +my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d) +{ + MYSQL_TIME ltime; + get_date(<ime, date_mode_t(0)); + return TIME_to_my_decimal(<ime, d); +} + +int Field_timestamp::store_decimal(const my_decimal *d) +{ + int error; + THD *thd= get_thd(); + ErrConvDecimal str(d); + Datetime dt(thd, &error, d, Timestamp::DatetimeOptions(thd), decimals()); + return store_TIME_with_warning(thd, &dt, &str, error); +} + +int Field_timestamp_with_dec::set_time() +{ + THD *thd= get_thd(); + set_notnull(); + // Avoid writing microseconds into binlog for FSP=0 + ulong msec= decimals() ? thd->query_start_sec_part() : 0; + store_TIMESTAMP(Timestamp(thd->query_start(), msec).trunc(decimals())); + return 0; +} + +bool Field_timestamp_with_dec::send(Protocol *protocol) +{ + MYSQL_TIME ltime; + Field_timestamp::get_date(<ime, date_mode_t(0)); + return protocol->store_datetime(<ime, dec); +} + + +int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + int32 a,b; + ulong a_sec_part, b_sec_part; + a= mi_uint4korr(a_ptr); + a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes(dec)); + b= mi_uint4korr(b_ptr); + b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes(dec)); + return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : + a_sec_part < b_sec_part ? -1 : a_sec_part > b_sec_part ? 1 : 0; +} + + +void Field_timestamp_with_dec::make_send_field(Send_field *field) +{ + Field::make_send_field(field); + field->decimals= dec; +} + + +/************************************************************* +** MySQL-5.6 compatible TIMESTAMP(N) +**************************************************************/ + +void Field_timestampf::store_TIMEVAL(const timeval &tm) +{ + my_timestamp_to_binary(&tm, ptr, dec); +} + +void Field_timestampf::set_max() +{ + DBUG_ENTER("Field_timestampf::set_max"); + DBUG_ASSERT(marked_for_write_or_computed()); + DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS); + + set_notnull(); + mi_int4store(ptr, TIMESTAMP_MAX_VALUE); + mi_int3store(ptr + 4, TIME_MAX_SECOND_PART); + + DBUG_VOID_RETURN; +} + +bool Field_timestampf::is_max() +{ + DBUG_ENTER("Field_timestampf::is_max"); + DBUG_ASSERT(marked_for_read()); + + DBUG_RETURN(mi_sint4korr(ptr) == TIMESTAMP_MAX_VALUE && + mi_sint3korr(ptr + 4) == TIME_MAX_SECOND_PART); +} + +my_time_t Field_timestampf::get_timestamp(const uchar *pos, + ulong *sec_part) const +{ + struct timeval tm; + my_timestamp_from_binary(&tm, pos, dec); + *sec_part= tm.tv_usec; + return tm.tv_sec; +} + + +bool Field_timestampf::val_native(Native *to) +{ + DBUG_ASSERT(marked_for_read()); + // Check if it's '0000-00-00 00:00:00' rather than a real timestamp + if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) + { + to->length(0); + return false; + } + return Field::val_native(to); +} + +Binlog_type_info Field_timestampf::binlog_type_info() const +{ + return Binlog_type_info(Field_timestampf::binlog_type(), decimals(), 1); +} + + +/*************************************************************/ +sql_mode_t Field_temporal::can_handle_sql_mode_dependency_on_store() const +{ + return MODE_PAD_CHAR_TO_FULL_LENGTH; +} + + +bool Field_temporal::is_equal(const Column_definition &new_field) const +{ + return new_field.type_handler() == type_handler() && + new_field.length == max_display_length(); +} + + +void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level, + const ErrConv *str, int was_cut, + const char *typestr) +{ + /* + error code logic: + MYSQL_TIME_WARN_TRUNCATED means that the value was not a date/time at all. + it will be stored as zero date/time. + MYSQL_TIME_WARN_OUT_OF_RANGE means that the value was a date/time, + that is, it was parsed as such, but the value was invalid. + + Also, MYSQL_TIME_WARN_TRUNCATED is used when storing a DATETIME in + a DATE field and non-zero time part is thrown away. + */ + if (was_cut & MYSQL_TIME_WARN_TRUNCATED) + set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, str, typestr, 1); + if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE) + set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, typestr, 1); +} + + +void Field_temporal::sql_type_dec_comment(String &res, + const Name &name, + uint dec, + const Name &comment) const +{ + CHARSET_INFO *cs=res.charset(); + res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + "%.*s(%u)%s%.*s%s", + (uint) name.length(), name.ptr(), + dec, + comment.length() ? " /* " : "", + (uint) comment.length(), comment.ptr(), + comment.length() ? " */" : "")); +} + + +void Field_temporal::sql_type_comment(String &res, + const Name &name, + const Name &comment) const +{ + CHARSET_INFO *cs=res.charset(); + res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + "%.*s%s%.*s%s", + (uint) name.length(), name.ptr(), + comment.length() ? " /* " : "", + (uint) comment.length(), comment.ptr(), + comment.length() ? " */" : "")); +} + + +const Name & Field_temporal::type_version_mysql56() +{ + DBUG_EXECUTE_IF("sql_type", return Type_handler::version_mysql56(); ); + static Name none(NULL, 0); + return none; +} + + +/* + Store string into a date/time field + + RETURN + 0 ok + 1 Value was cut during conversion + 2 value was out of range + 3 Datetime value that was cut (warning level NOTE) + This is used by opt_range.cc:get_mm_leaf(). +*/ +int Field_datetime::store_TIME_with_warning(const Datetime *dt, + const ErrConv *str, + int was_cut) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + // Handle totally bad values + if (!dt->is_valid_datetime()) + return store_invalid_with_warning(str, was_cut, "datetime"); + // Store the value + DBUG_ASSERT(!dt->fraction_remainder(decimals())); + store_datetime(*dt); + // Caclulate return value and send warnings if needed + return store_TIME_return_code_with_warnings(was_cut, str, "datetime"); +} + + +int Field_datetime::store(const char *from, size_t len, CHARSET_INFO *cs) +{ + MYSQL_TIME_STATUS st; + ErrConvString str(from, len, cs); + THD *thd= get_thd(); + Datetime dt(thd, &st, from, len, cs, Datetime::Options(thd), decimals()); + return store_TIME_with_warning(&dt, &str, st.warnings); +} + +int Field_datetime::store(double nr) +{ + int error; + ErrConvDouble str(nr); + THD *thd= get_thd(); + Datetime dt(thd, &error, nr, Datetime::Options(thd), decimals()); + return store_TIME_with_warning(&dt, &str, error); +} + + +int Field_datetime::store(longlong nr, bool unsigned_val) +{ + int error; + Longlong_hybrid tmp(nr, unsigned_val); + ErrConvInteger str(tmp); + THD *thd= get_thd(); + Datetime dt(&error, tmp, Datetime::Options(thd)); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_datetime::store_time_dec(const MYSQL_TIME *ltime, uint dec) +{ + int error; + ErrConvTime str(ltime); + THD *thd= get_thd(); + Datetime dt(thd, &error, ltime, Datetime::Options(thd), decimals()); + return store_TIME_with_warning(&dt, &str, error); +} + + +int Field_datetime::store_decimal(const my_decimal *d) +{ + int error; + ErrConvDecimal str(d); + THD *thd= get_thd(); + Datetime tm(thd, &error, d, Datetime::Options(thd), decimals()); + return store_TIME_with_warning(&tm, &str, error); +} + + +bool +Field_temporal_with_date::validate_value_in_record(THD *thd, + const uchar *record) const +{ + DBUG_ASSERT(!is_null_in_record(record)); + MYSQL_TIME ltime; + return get_TIME(<ime, ptr_in_record(record), Datetime::Options(thd)); +} + + +my_decimal *Field_temporal::val_decimal(my_decimal *d) +{ + MYSQL_TIME ltime; + if (get_date(<ime, date_mode_t(0))) + { + bzero(<ime, sizeof(ltime)); + ltime.time_type= type_handler()->mysql_timestamp_type(); + } + return TIME_to_my_decimal(<ime, d); +} + + +Data_type_compatibility +Field_temporal::can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *value) const +{ + // Field is of TIME_RESULT, which supersedes everything else. + return Data_type_compatibility::OK; +} + + +Data_type_compatibility +Field_temporal::can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const +{ + // Field is of TIME_RESULT, which supersedes everything else. + return Data_type_compatibility::OK; +} + + +Item *Field_temporal::get_equal_const_item_datetime(THD *thd, + const Context &ctx, + Item *const_item) +{ + switch (ctx.subst_constraint()) { + case IDENTITY_SUBST: + if ((const_item->field_type() != MYSQL_TYPE_DATETIME && + const_item->field_type() != MYSQL_TYPE_TIMESTAMP) || + const_item->decimals != decimals()) + { + Datetime::Options opt(TIME_CONV_NONE, thd); + Datetime dt(thd, const_item, opt, decimals()); + if (!dt.is_valid_datetime()) + return NULL; + /* + See comments about truncation in the same place in + Field_time::get_equal_const_item(). + */ + return new (thd->mem_root) Item_datetime_literal(thd, &dt, decimals()); + } + break; + case ANY_SUBST: + if (!is_temporal_type_with_date(const_item->field_type())) + { + Datetime dt= type_handler()->field_type() == MYSQL_TYPE_TIMESTAMP ? + Datetime(thd, const_item, Timestamp::DatetimeOptions(thd)) : + Datetime(thd, const_item, Datetime::Options_cmp(thd)); + if (!dt.is_valid_datetime()) + return NULL; + return new (thd->mem_root) + Item_datetime_literal_for_invalid_dates(thd, &dt, + dt.get_mysql_time()-> + second_part ? + TIME_SECOND_PART_DIGITS : 0); + } + break; + } + return const_item; +} + + +/**************************************************************************** +** time type +** In string context: HH:MM:SS +** In number context: HHMMSS +** Stored as a 3 byte unsigned int +****************************************************************************/ +sql_mode_t +Field_time::conversion_depends_on_sql_mode(THD *thd, Item *expr) const +{ + return expr->time_precision(thd) > decimals() ? + MODE_TIME_ROUND_FRACTIONAL : 0; +} + + +int Field_time::store_TIME_with_warning(const Time *t, + const ErrConv *str, int warn) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + // Handle totally bad values + if (!t->is_valid_time()) + return store_invalid_with_warning(str, warn, "time"); + // Store the value + DBUG_ASSERT(!t->fraction_remainder(decimals())); + store_TIME(*t); + // Calculate return value and send warnings if needed + return store_TIME_return_code_with_warnings(warn, str, "time"); +} + + +void Field_time0::store_TIME(const MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ltime->year == 0); + DBUG_ASSERT(ltime->month == 0); + long tmp= (ltime->day*24L+ltime->hour)*10000L + + (ltime->minute*100+ltime->second); + if (ltime->neg) + tmp= -tmp; + int3store(ptr,tmp); +} + +int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs) +{ + ErrConvString str(from, len, cs); + MYSQL_TIME_STATUS st; + THD *thd= get_thd(); + /* + Unlike number-to-time conversion, we need to additionally pass + MODE_NO_ZERO_DATE here (if it presents in the current sql_mode): + SET sql_mode='STRICT_ALL_TABLES,NO_ZERO_DATE'; + INSERT INTO t1 VALUES ('0000-00-00 00:00:00'); -- error + INSERT INTO t1 VALUES (0); -- ok + In the first INSERT we have a zero date. + In the second INSERT we don't have a zero date (it is just a zero time). + */ + Time::Options opt(sql_mode_for_dates(thd), thd); + Time tm(thd, &st, from, len, cs, opt, decimals()); + return store_TIME_with_warning(&tm, &str, st.warnings); +} + + +int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec) +{ + ErrConvTime str(ltime); + int warn; + Time tm(&warn, ltime, curdays, Time::Options(get_thd()), decimals()); + return store_TIME_with_warning(&tm, &str, warn); +} + + +int Field_time::store(double nr) +{ + ErrConvDouble str(nr); + int was_cut; + Time tm(get_thd(), &was_cut, nr, Time::Options(get_thd()), decimals()); + return store_TIME_with_warning(&tm, &str, was_cut); +} + + +int Field_time::store(longlong nr, bool unsigned_val) +{ + Longlong_hybrid tmp(nr, unsigned_val); + ErrConvInteger str(tmp); + int was_cut; + THD *thd= get_thd(); + /* + Need fractional digit truncation if nr overflows to '838:59:59.999999'. + The constructor used below will always truncate (never round). + We don't need to care to overwrite the default session rounding mode + from HALF_UP to TRUNCATE. + */ + Time tm(thd, &was_cut, tmp, Time::Options(thd), decimals()); + return store_TIME_with_warning(&tm, &str, was_cut); +} + + +void Field_time::set_curdays(THD *thd) +{ + MYSQL_TIME ltime; + set_current_date(thd, <ime); + curdays= calc_daynr(ltime.year, ltime.month, ltime.day); +} + + +Field *Field_time::new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit) +{ + THD *thd= get_thd(); + Field_time *res= + (Field_time*) Field::new_key_field(root, new_table, new_ptr, length, + new_null_ptr, new_null_bit); + if (!(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) && res) + res->set_curdays(thd); + return res; +} + + +double Field_time0::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + uint32 j= (uint32) uint3korr(ptr); + return (double) j; +} + +longlong Field_time0::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + return (longlong) sint3korr(ptr); +} + + +/** + @note + This function is multi-byte safe as the result string is always of type + my_charset_bin +*/ + +String *Field_time::val_str(String *str, + String *unused __attribute__((unused))) +{ + DBUG_ASSERT(marked_for_read()); + MYSQL_TIME ltime; + get_date(<ime, Datetime::Options(TIME_TIME_ONLY, get_thd())); + str->alloc(field_length + 1); + str->length(my_time_to_str(<ime, const_cast<char*>(str->ptr()), decimals())); + str->set_charset(&my_charset_numeric); + return str; +} + + +bool Field_time::check_zero_in_date_with_warn(date_mode_t fuzzydate) +{ + date_conv_mode_t tmp= date_conv_mode_t(fuzzydate); + if (!(tmp & TIME_TIME_ONLY) && (tmp & TIME_NO_ZERO_IN_DATE)) + { + THD *thd= get_thd(); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name.str, + thd->get_stmt_da()->current_row_for_warning()); + return true; + } + return false; +} + + +/** + @note + Normally we would not consider 'time' as a valid date, but we allow + get_date() here to be able to do things like + DATE_FORMAT(time, "%l.%i %p") +*/ + +bool Field_time0::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) +{ + if (check_zero_in_date_with_warn(fuzzydate)) + return true; + long tmp=(long) sint3korr(ptr); + ltime->neg=0; + if (tmp < 0) + { + ltime->neg= 1; + tmp=-tmp; + } + ltime->year= ltime->month= ltime->day= 0; + ltime->hour= (int) (tmp/10000); + tmp-=ltime->hour*10000; + ltime->minute= (int) tmp/100; + ltime->second= (int) tmp % 100; + ltime->second_part=0; + ltime->time_type= MYSQL_TIMESTAMP_TIME; + return 0; +} + + +int Field_time::store_native(const Native &value) +{ + Time t(value); + DBUG_ASSERT(t.is_valid_time()); + store_TIME(t); + return 0; +} + + +bool Field_time::val_native(Native *to) +{ + MYSQL_TIME ltime; + get_date(<ime, date_mode_t(0)); + int warn; + return Time(&warn, <ime, 0).to_native(to, decimals()); +} + + +bool Field_time::send(Protocol *protocol) +{ + MYSQL_TIME ltime; + get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd())); + return protocol->store_time(<ime, decimals()); +} + + +int Field_time0::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + int32 a,b; + a=(int32) sint3korr(a_ptr); + b=(int32) sint3korr(b_ptr); + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +void Field_time0::sort_string(uchar *to,uint length __attribute__((unused))) +{ + to[0] = (uchar) (ptr[2] ^ 128); + to[1] = ptr[1]; + to[2] = ptr[0]; +} + + +int Field_time_hires::reset() +{ + store_bigendian(zero_point, ptr, Field_time_hires::pack_length()); + return 0; +} + + +void Field_time_hires::store_TIME(const MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ltime->year == 0); + DBUG_ASSERT(ltime->month == 0); + ulonglong packed= sec_part_shift(pack_time(ltime), dec) + zero_point; + store_bigendian(packed, ptr, Field_time_hires::pack_length()); +} + +int Field_time::store_decimal(const my_decimal *d) +{ + ErrConvDecimal str(d); + int was_cut; + Time tm(get_thd(), &was_cut, d, Time::Options(get_thd()), decimals()); + return store_TIME_with_warning(&tm, &str, was_cut); +} + + +bool Field_time::can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal) +{ + DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT); + switch (ctx.subst_constraint()) { + case ANY_SUBST: + /* + A TIME field in a DATETIME comparison can be substituted to + Item_equal with TIME comparison. + + SET timestamp=UNIX_TIMESTAMP('2015-08-30 10:20:30'); + CREATE OR REPLACE TABLE t1 (a TIME); + INSERT INTO t1 VALUES ('00:00:00'),('00:00:01'); + SELECT * FROM t1 WHERE a>=TIMESTAMP'2015-08-30 00:00:00' + AND a='00:00:00'; + + The above query can be simplified to: + SELECT * FROM t1 WHERE TIME'00:00:00'>=TIMESTAMP'2015-08-30 00:00:00' + AND a='00:00:00'; + And further to: + SELECT * FROM t1 WHERE a=TIME'00:00:00'; + */ + if (ctx.compare_type_handler() == &type_handler_datetime && + item_equal->compare_type_handler() == &type_handler_time) + return true; + return ctx.compare_type_handler() == item_equal->compare_type_handler(); + case IDENTITY_SUBST: + return true; + } + return false; +} + + +Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + /* + Old mode conversion from DATETIME with non-zero YYYYMMDD part + to TIME works very inconsistently. Possible variants: + - truncate the YYYYMMDD part + - add (MM*33+DD)*24 to hours + - add (MM*31+DD)*24 to hours + Let's disallow propagation of DATETIME with non-zero YYYYMMDD + as an equal constant for a TIME field. + */ + Time::datetime_to_time_mode_t mode= + (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) ? + Time::DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY : + Time::DATETIME_TO_TIME_MINUS_CURRENT_DATE; + + switch (ctx.subst_constraint()) { + case ANY_SUBST: + if (const_item->field_type() != MYSQL_TYPE_TIME) + { + // Get the value of const_item with conversion from DATETIME to TIME + Time tm(get_thd(), const_item, Time::Options_cmp(thd, mode)); + if (!tm.is_valid_time()) + return NULL; + /* + Replace a DATE/DATETIME constant to a TIME constant: + WHERE LENGTH(time_column)=8 + AND time_column=TIMESTAMP'2015-08-30 10:20:30'; + to: + WHERE LENGTH(time_column)=10 + AND time_column=TIME'10:20:30' + + (assuming CURRENT_DATE is '2015-08-30' + */ + return new (thd->mem_root) Item_time_literal(thd, &tm, + tm.get_mysql_time()-> + second_part ? + TIME_SECOND_PART_DIGITS : + 0); + } + break; + case IDENTITY_SUBST: + if (const_item->field_type() != MYSQL_TYPE_TIME || + const_item->decimals != decimals()) + { + /* + Note, the value returned in "ltime" can have more fractional + digits that decimals(). The Item_time_literal constructor will + truncate these digits. We could disallow propagation is such + cases, but it's still useful (and safe) to optimize: + WHERE time0_column='00:00:00.123' AND LENGTH(a)=12 + to + WHERE time0_column='00:00:00.123' AND LENGTH(TIME'00:00:00')=12 + and then to + WHERE FALSE + The original WHERE would do the full table scan (in case of no keys). + The optimized WHERE will return with "Impossible WHERE", without + having to do the full table scan. + */ + Time tm(thd, const_item, Time::Options(TIME_TIME_ONLY, thd, mode), + decimals()); + if (!tm.is_valid_time()) + return NULL; + return new (thd->mem_root) Item_time_literal(thd, &tm, decimals()); + } + break; + } + return const_item; +} + + +longlong Field_time_with_dec::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + MYSQL_TIME ltime; + get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd())); + longlong val= TIME_to_ulonglong_time(<ime); + return ltime.neg ? -val : val; +} + +double Field_time_with_dec::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + MYSQL_TIME ltime; + get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd())); + return TIME_to_double(<ime); +} + +bool Field_time_hires::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) +{ + if (check_zero_in_date_with_warn(fuzzydate)) + return true; + uint32 len= pack_length(); + longlong packed= read_bigendian(ptr, len); + + packed= sec_part_unshift(packed - zero_point, dec); + + unpack_time(packed, ltime, MYSQL_TIMESTAMP_TIME); + return false; +} + + +int Field_time_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + ulonglong a=read_bigendian(a_ptr, Field_time_hires::pack_length()); + ulonglong b=read_bigendian(b_ptr, Field_time_hires::pack_length()); + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +void Field_time_hires::sort_string(uchar *to,uint length __attribute__((unused))) +{ + DBUG_ASSERT(length == Field_time_hires::pack_length()); + memcpy(to, ptr, length); + to[0]^= 128; +} + +void Field_time_with_dec::make_send_field(Send_field *field) +{ + Field::make_send_field(field); + field->decimals= dec; +} + +/**************************************************************************** +** time type with fsp (MySQL-5.6 version) +** In string context: HH:MM:SS.FFFFFF +** In number context: HHMMSS.FFFFFF +****************************************************************************/ + +int Field_timef::reset() +{ + my_time_packed_to_binary(0, ptr, dec); + return 0; +} + +void Field_timef::store_TIME(const MYSQL_TIME *ltime) +{ + longlong tmp= TIME_to_longlong_time_packed(ltime); + my_time_packed_to_binary(tmp, ptr, dec); +} + +bool Field_timef::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) +{ + if (check_zero_in_date_with_warn(fuzzydate)) + return true; + longlong tmp= my_time_packed_from_binary(ptr, dec); + TIME_from_longlong_time_packed(ltime, tmp); + return false; +} +Binlog_type_info Field_timef::binlog_type_info() const +{ + return Binlog_type_info(Field_timef::binlog_type(), decimals(), 1); +} + + +longlong Field_timef::val_time_packed(THD *thd) +{ + DBUG_ASSERT(marked_for_read()); + longlong tmp= my_time_packed_from_binary(ptr, dec); + MYSQL_TIME ltime; + TIME_from_longlong_time_packed(<ime, tmp); + return pack_time(<ime); +} + + +int Field_timef::store_native(const Native &value) +{ + DBUG_ASSERT(value.length() == my_time_binary_length(dec)); + DBUG_ASSERT(Time(value).is_valid_time()); + memcpy(ptr, value.ptr(), value.length()); + return 0; +} + + +bool Field_timef::val_native(Native *to) +{ + uint32 binlen= my_time_binary_length(dec); + return to->copy((const char*) ptr, binlen); +} + + +/**************************************************************************** +** year type +** Save in a byte the year 0, 1901->2155 +** Can handle 2 byte or 4 byte years! +****************************************************************************/ + +int Field_year::store(const char *from, size_t len,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + THD *thd= get_thd(); + char *end; + int error; + longlong nr= cs->strntoull10rnd(from, len, 0, &end, &error); + + if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155 || + error == MY_ERRNO_ERANGE) + { + *ptr=0; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + return 1; + } + + if (thd->count_cuted_fields <= CHECK_FIELD_EXPRESSION && error == MY_ERRNO_EDOM) + { + *ptr= 0; + return 1; + } + + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION && + (error= check_int(cs, from, len, end, error))) + { + if (unlikely(error == 1) /* empty or incorrect string */) + { + *ptr= 0; + return 1; + } + error= 1; + } + + if (nr != 0 || len != 4) + { + if (nr < YY_PART_YEAR) + nr+=100; // 2000 - 2069 + else if (nr > 1900) + nr-= 1900; + } + *ptr= (char) (uchar) nr; + return error; +} + + +int Field_year::store(double nr) +{ + if (nr < 0.0 || nr > 2155.0) + { + (void) Field_year::store((longlong) -1, FALSE); + return 1; + } + return Field_year::store((longlong) nr, FALSE); +} + + +int Field_year::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155) + { + *ptr= 0; + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + return 1; + } + if (nr != 0 || field_length != 4) // 0000 -> 0; 00 -> 2000 + { + if (nr < YY_PART_YEAR) + nr+=100; // 2000 - 2069 + else if (nr > 1900) + nr-= 1900; + } + *ptr= (char) (uchar) nr; + return 0; +} + + +int Field_year::store_time_dec(const MYSQL_TIME *ltime, uint) +{ + ErrConvTime str(ltime); + if (Field_year::store(ltime->year, 0)) + return 1; + + const char *typestr= Temporal::type_name_by_timestamp_type(ltime->time_type); + set_datetime_warning(WARN_DATA_TRUNCATED, &str, typestr, 1); + return 0; +} + +bool Field_year::send(Protocol *protocol) +{ + DBUG_ASSERT(marked_for_read()); + Protocol_text *txt; + if ((txt= dynamic_cast<Protocol_text*>(protocol))) + return send_numeric_zerofill_str(txt, PROTOCOL_SEND_SHORT); + ulonglong tmp= Field_year::val_int(); + return protocol->store_short(tmp); +} + + +double Field_year::val_real(void) +{ + return (double) Field_year::val_int(); +} + + +longlong Field_year::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(field_length == 2 || field_length == 4); + int tmp= (int) ptr[0]; + if (field_length != 4) + tmp%=100; // Return last 2 char + else if (tmp) + tmp+=1900; + return (longlong) tmp; +} + + +String *Field_year::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + DBUG_ASSERT(field_length < 5); + val_buffer->alloc(5); + val_buffer->length(field_length); + char *to=(char*) val_buffer->ptr(); + sprintf(to,field_length == 2 ? "%02d" : "%04d",(int) Field_year::val_int()); + val_buffer->set_charset(&my_charset_numeric); + return val_buffer; +} + + +bool Field_year::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) +{ + int tmp= (int) ptr[0]; + if (tmp || field_length != 4) + tmp+= 1900; + return int_to_datetime_with_warn(get_thd(), + Longlong_hybrid(tmp * 10000, true), + ltime, fuzzydate, table->s, field_name.str); +} + + +void Field_year::sql_type(String &res) const +{ + CHARSET_INFO *cs=res.charset(); + res.length(cs->cset->snprintf(cs,(char*)res.ptr(),res.alloced_length(), + "year(%d)",(int) field_length)); +} + + +/*****************************************************************************/ + +int Field_date_common::store_TIME_with_warning(const Datetime *dt, + const ErrConv *str, + int was_cut) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + // Handle totally bad values + if (!dt->is_valid_datetime()) + return store_invalid_with_warning(str, was_cut, "date"); + // Store the value + if (!dt->hhmmssff_is_zero()) + was_cut|= MYSQL_TIME_NOTE_TRUNCATED; + store_datetime(*dt); + // Caclulate return value and send warnings if needed + return store_TIME_return_code_with_warnings(was_cut, str, "date"); +} + +int Field_date_common::store(const char *from, size_t len, CHARSET_INFO *cs) +{ + MYSQL_TIME_STATUS st; + ErrConvString str(from, len, cs); + THD *thd= get_thd(); + Datetime dt(thd, &st, from, len, cs, Date::Options(thd), 0); + return store_TIME_with_warning(&dt, &str, st.warnings); +} + +int Field_date_common::store(double nr) +{ + int error; + ErrConvDouble str(nr); + THD *thd= get_thd(); + Datetime dt(thd, &error, nr, Date::Options(thd), 0); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_date_common::store(longlong nr, bool unsigned_val) +{ + int error; + Longlong_hybrid tmp(nr, unsigned_val); + ErrConvInteger str(tmp); + THD *thd= get_thd(); + Datetime dt(&error, tmp, Date::Options(thd)); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_date_common::store_time_dec(const MYSQL_TIME *ltime, uint dec) +{ + int error; + ErrConvTime str(ltime); + THD *thd= get_thd(); + Datetime dt(thd, &error, ltime, Date::Options(thd), 0); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_date_common::store_decimal(const my_decimal *d) +{ + int error; + ErrConvDecimal str(d); + THD *thd= get_thd(); + Datetime tm(thd, &error, d, Date::Options(thd), 0); + return store_TIME_with_warning(&tm, &str, error); +} + + +/**************************************************************************** +** date type +** In string context: YYYY-MM-DD +** In number context: YYYYMMDD +** Stored as a 4 byte unsigned int +****************************************************************************/ + +void Field_date::store_TIME(const MYSQL_TIME *ltime) +{ + uint tmp= ltime->year*10000L + ltime->month*100+ltime->day; + int4store(ptr,tmp); +} + +bool Field_date::send(Protocol *protocol) +{ + longlong tmp= Field_date::val_int(); + MYSQL_TIME tm; + tm.year= (uint32) tmp/10000L % 10000; + tm.month= (uint32) tmp/100 % 100; + tm.day= (uint32) tmp % 100; + return protocol->store_date(&tm); +} + + +double Field_date::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + int32 j; + j=sint4korr(ptr); + return (double) (uint32) j; +} + + +longlong Field_date::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + int32 j; + j=sint4korr(ptr); + return (longlong) (uint32) j; +} + + +bool Field_date::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + date_mode_t fuzzydate) const +{ + DBUG_ASSERT(marked_for_read()); + int32 tmp= sint4korr(pos); + ltime->year= (int) ((uint32) tmp/10000L % 10000); + ltime->month= (int) ((uint32) tmp/100 % 100); + ltime->day= (int) ((uint32) tmp % 100); + ltime->time_type= MYSQL_TIMESTAMP_DATE; + ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0; + return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate); +} + + +String *Field_date::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + MYSQL_TIME ltime; + get_TIME(<ime, ptr, date_mode_t(0)); + val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); + uint length= (uint) my_date_to_str(<ime, + const_cast<char*>(val_buffer->ptr())); + val_buffer->length(length); + val_buffer->set_charset(&my_charset_numeric); + + return val_buffer; +} + + +int Field_date::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + int32 a,b; + a=sint4korr(a_ptr); + b=sint4korr(b_ptr); + return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; +} + + +void Field_date::sort_string(uchar *to,uint length __attribute__((unused))) +{ + to[0] = ptr[3]; + to[1] = ptr[2]; + to[2] = ptr[1]; + to[3] = ptr[0]; +} + +void Field_date::sql_type(String &res) const +{ + res.set_ascii(STRING_WITH_LEN("date")); +} + + +/**************************************************************************** +** The new date type +** This is identical to the old date type, but stored on 3 bytes instead of 4 +** In number context: YYYYMMDD +****************************************************************************/ + +void Field_newdate::store_TIME(const MYSQL_TIME *ltime) +{ + uint tmp= ltime->year*16*32 + ltime->month*32+ltime->day; + int3store(ptr,tmp); +} + + +bool Field_newdate::send(Protocol *protocol) +{ + MYSQL_TIME tm; + Field_newdate::get_date(&tm, date_mode_t(0)); + return protocol->store_date(&tm); +} + + +double Field_newdate::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + return (double) Field_newdate::val_int(); +} + + +longlong Field_newdate::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + ulong j= uint3korr(ptr); + j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L; + return (longlong) j; +} + + +String *Field_newdate::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + DBUG_ASSERT(marked_for_read()); + val_buffer->alloc(field_length+1); + val_buffer->length(field_length); + uint32 tmp=(uint32) uint3korr(ptr); + int part; + char *pos=(char*) val_buffer->ptr()+10; + + /* Open coded to get more speed */ + *pos--=0; // End NULL + part=(int) (tmp & 31); + *pos--= (char) ('0'+part%10); + *pos--= (char) ('0'+part/10); + *pos--= '-'; + part=(int) (tmp >> 5 & 15); + *pos--= (char) ('0'+part%10); + *pos--= (char) ('0'+part/10); + *pos--= '-'; + part=(int) (tmp >> 9); + *pos--= (char) ('0'+part%10); part/=10; + *pos--= (char) ('0'+part%10); part/=10; + *pos--= (char) ('0'+part%10); part/=10; + *pos= (char) ('0'+part); + val_buffer->set_charset(&my_charset_numeric); + return val_buffer; +} + + +bool Field_newdate::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + date_mode_t fuzzydate) const +{ + DBUG_ASSERT(marked_for_read()); + uint32 tmp=(uint32) uint3korr(pos); + ltime->day= tmp & 31; + ltime->month= (tmp >> 5) & 15; + ltime->year= (tmp >> 9); + ltime->time_type= MYSQL_TIMESTAMP_DATE; + ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0; + return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate); +} + + +longlong Field_newdate::val_datetime_packed(THD *thd) +{ + MYSQL_TIME ltime; + Field_newdate::get_date(<ime, date_mode_t(0)); + return pack_time(<ime); +} + + +int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + uint32 a,b; + a=(uint32) uint3korr(a_ptr); + b=(uint32) uint3korr(b_ptr); + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + + +void Field_newdate::sort_string(uchar *to,uint length __attribute__((unused))) +{ + to[0] = ptr[2]; + to[1] = ptr[1]; + to[2] = ptr[0]; +} + + +void Field_newdate::sql_type(String &res) const +{ + res.set_ascii(STRING_WITH_LEN("date")); +} + + +Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + switch (ctx.subst_constraint()) { + case ANY_SUBST: + if (!is_temporal_type_with_date(const_item->field_type())) + { + /* + DATE is compared to DATETIME-alike non-temporal values + (such as VARCHAR, DECIMAL) as DATETIME, e.g.: + WHERE date_column=20010101235959.0000009 + So here we convert the constant to DATETIME normally. + In case if TIME_ROUND_FRACTIONAL is enabled, nanoseconds will round. + */ + Datetime dt(thd, const_item, Datetime::Options_cmp(thd)); + if (!dt.is_valid_datetime()) + return NULL; + /* + Replace the constant to a DATE or DATETIME constant. + Example: + WHERE LENGTH(date_column)=10 + AND date_column=TIME'10:20:30'; + to: + WHERE LENGTH(date_column)=10 + AND date_column=TIMESTAMP'2015-08-30 10:20:30' + + (assuming CURRENT_DATE is '2015-08-30' + */ + if (!dt.hhmmssff_is_zero()) + return new (thd->mem_root) + Item_datetime_literal_for_invalid_dates(thd, &dt, + dt.get_mysql_time()-> + second_part ? + TIME_SECOND_PART_DIGITS : 0); + Date d(&dt); + return new (thd->mem_root) Item_date_literal_for_invalid_dates(thd, &d); + } + break; + case IDENTITY_SUBST: + if (const_item->field_type() != MYSQL_TYPE_DATE) + { + /* + DATE is compared to non-temporal as DATETIME. + We need to convert to DATETIME first, taking into account the + current session rounding mode (even though this is IDENTITY_SUBSTS!), + then convert the result to DATE. + */ + Datetime dt(thd, const_item, Datetime::Options(TIME_CONV_NONE, thd)); + if (!dt.is_valid_datetime()) + return NULL; + Date d(&dt); + return new (thd->mem_root) Item_date_literal(thd, &d); + } + break; + } + return const_item; +} + + +/**************************************************************************** +** datetime type +** In string context: YYYY-MM-DD HH:MM:DD +** In number context: YYYYMMDDHHMMDD +** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int. +****************************************************************************/ + +void Field_datetime0::store_TIME(const MYSQL_TIME *ltime) +{ + ulonglong tmp= TIME_to_ulonglong_datetime(ltime); + int8store(ptr,tmp); +} + + +sql_mode_t +Field_datetime::conversion_depends_on_sql_mode(THD *thd, Item *expr) const +{ + return expr->datetime_precision(thd) > decimals() ? + MODE_TIME_ROUND_FRACTIONAL : 0; +} + + +bool Field_datetime0::send(Protocol *protocol) +{ + MYSQL_TIME tm; + Field_datetime0::get_date(&tm, date_mode_t(0)); + return protocol->store_datetime(&tm, 0); +} + + +longlong Field_datetime0::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + longlong j; + j=sint8korr(ptr); + return j; +} + + +String *Field_datetime0::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + val_buffer->alloc(field_length+1); + val_buffer->length(field_length); + + DBUG_ASSERT(marked_for_read()); + ulonglong tmp; + long part1,part2; + char *pos; + int part3; + + tmp= Field_datetime0::val_int(); + + /* + Avoid problem with slow longlong arithmetic and sprintf + */ + + part1=(long) (tmp/1000000LL); + part2=(long) (tmp - (ulonglong) part1*1000000LL); + + pos=(char*) val_buffer->ptr() + MAX_DATETIME_WIDTH; + *pos--=0; + *pos--= (char) ('0'+(char) (part2%10)); part2/=10; + *pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10); + *pos--= ':'; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= ':'; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) part3); + *pos--= ' '; + *pos--= (char) ('0'+(char) (part1%10)); part1/=10; + *pos--= (char) ('0'+(char) (part1%10)); part1/=10; + *pos--= '-'; + *pos--= (char) ('0'+(char) (part1%10)); part1/=10; + *pos--= (char) ('0'+(char) (part1%10)); part3= (int) (part1/10); + *pos--= '-'; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos=(char) ('0'+(char) part3); + val_buffer->set_charset(&my_charset_numeric); + return val_buffer; +} + +bool Field_datetime0::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + date_mode_t fuzzydate) const +{ + DBUG_ASSERT(marked_for_read()); + longlong tmp= sint8korr(pos); + uint32 part1,part2; + part1=(uint32) (tmp/1000000LL); + part2=(uint32) (tmp - (ulonglong) part1*1000000LL); + + ltime->time_type= MYSQL_TIMESTAMP_DATETIME; + ltime->neg= 0; + ltime->second_part= 0; + ltime->second= (int) (part2%100); + ltime->minute= (int) (part2/100%100); + ltime->hour= (int) (part2/10000); + ltime->day= (int) (part1%100); + ltime->month= (int) (part1/100%100); + ltime->year= (int) (part1/10000); + return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate); +} + + +int Field_datetime0::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + longlong a,b; + a=sint8korr(a_ptr); + b=sint8korr(b_ptr); + return ((ulonglong) a < (ulonglong) b) ? -1 : + ((ulonglong) a > (ulonglong) b) ? 1 : 0; +} + +void Field_datetime0::sort_string(uchar *to,uint length __attribute__((unused))) +{ + to[0] = ptr[7]; + to[1] = ptr[6]; + to[2] = ptr[5]; + to[3] = ptr[4]; + to[4] = ptr[3]; + to[5] = ptr[2]; + to[6] = ptr[1]; + to[7] = ptr[0]; +} + + +int Field_datetime::set_time() +{ + THD *thd= table->in_use; + set_notnull(); + // Here we always truncate (not round), no matter what sql_mode is + if (decimals()) + store_datetime(Datetime(thd, Timeval(thd->query_start(), + thd->query_start_sec_part()) + ).trunc(decimals())); + else + store_datetime(Datetime(thd, Timeval(thd->query_start(), 0))); + return 0; +} + + +void Field_datetime_hires::store_TIME(const MYSQL_TIME *ltime) +{ + ulonglong packed= sec_part_shift(pack_time(ltime), dec); + store_bigendian(packed, ptr, Field_datetime_hires::pack_length()); +} + +bool Field_datetime_with_dec::send(Protocol *protocol) +{ + MYSQL_TIME ltime; + get_date(<ime, date_mode_t(0)); + return protocol->store_datetime(<ime, dec); +} + + +double Field_datetime_with_dec::val_real(void) +{ + MYSQL_TIME ltime; + get_date(<ime, date_mode_t(0)); + return TIME_to_double(<ime); +} + +longlong Field_datetime_with_dec::val_int(void) +{ + MYSQL_TIME ltime; + get_date(<ime, date_mode_t(0)); + return TIME_to_ulonglong_datetime(<ime); +} + + +String *Field_datetime_with_dec::val_str(String *str, + String *unused __attribute__((unused))) +{ + MYSQL_TIME ltime; + get_date(<ime, date_mode_t(0)); + str->alloc(field_length+1); + str->length(field_length); + my_datetime_to_str(<ime, (char*) str->ptr(), dec); + str->set_charset(&my_charset_numeric); + return str; +} + + +bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + date_mode_t fuzzydate) const +{ + DBUG_ASSERT(marked_for_read()); + ulonglong packed= read_bigendian(pos, Field_datetime_hires::pack_length()); + unpack_time(sec_part_unshift(packed, dec), ltime, MYSQL_TIMESTAMP_DATETIME); + return validate_MMDD(packed, ltime->month, ltime->day, fuzzydate); +} + + +int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length()); + ulonglong b=read_bigendian(b_ptr, Field_datetime_hires::pack_length()); + return a < b ? -1 : a > b ? 1 : 0; +} + +void Field_datetime_with_dec::make_send_field(Send_field *field) +{ + Field::make_send_field(field); + field->decimals= dec; +} + + +/**************************************************************************** +** MySQL-5.6 compatible DATETIME(N) +** +****************************************************************************/ +int Field_datetimef::reset() +{ + my_datetime_packed_to_binary(0, ptr, dec); + return 0; +} + +void Field_datetimef::store_TIME(const MYSQL_TIME *ltime) +{ + longlong tmp= TIME_to_longlong_datetime_packed(ltime); + my_datetime_packed_to_binary(tmp, ptr, dec); +} + +bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + date_mode_t fuzzydate) const +{ + DBUG_ASSERT(marked_for_read()); + longlong tmp= my_datetime_packed_from_binary(pos, dec); + TIME_from_longlong_datetime_packed(ltime, tmp); + return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate); +} +Binlog_type_info Field_datetimef::binlog_type_info() const +{ + return Binlog_type_info(Field_datetimef::binlog_type(), decimals(), 1); +} + +longlong Field_datetimef::val_datetime_packed(THD *thd) +{ + DBUG_ASSERT(marked_for_read()); + longlong tmp= my_datetime_packed_from_binary(ptr, dec); + MYSQL_TIME ltime; + TIME_from_longlong_datetime_packed(<ime, tmp); + return pack_time(<ime); +} + + +/**************************************************************************** +** string type +** A string may be varchar or binary +****************************************************************************/ + +/* + Report "not well formed" or "cannot convert" error + after storing a character string info a field. + + SYNOPSIS + check_string_copy_error() + copier - the conversion status + end - the very end of the source string + that was just copied + cs - character set of the string + + NOTES + As of version 5.0 both cases return the same error: + + "Invalid string value: 'xxx' for column 't' at row 1" + + Future versions will possibly introduce a new error message: + + "Cannot convert character string: 'xxx' for column 't' at row 1" + + RETURN + FALSE - If errors didn't happen + TRUE - If an error happened +*/ + +bool +Field_longstr::check_string_copy_error(const String_copier *copier, + const char *end, + CHARSET_INFO *cs) +{ + const char *pos; + char tmp[32]; + + if (likely(!(pos= copier->most_important_error_pos()))) + return FALSE; + + /* Ignore errors from internal expressions */ + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) + { + DBUG_ASSERT(sizeof(tmp) >= convert_to_printable_required_length(6)); + convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6); + set_warning_truncated_wrong_value("string", tmp); + } + return TRUE; +} + + +/* + Check if we lost any important data and send a truncation error/warning + + SYNOPSIS + Field_longstr::report_if_important_data() + pstr - Truncated rest of string + end - End of truncated string + count_spaces - Treat traling spaces as important data + + RETURN VALUES + 0 - None was truncated (or we don't count cut fields) + 2 - Some bytes was truncated + + NOTE + Check if we lost any important data (anything in a binary string, + or any non-space in others). If only trailing spaces was lost, + send a truncation note, otherwise send a truncation error. + Silently ignore traling spaces if the count_space parameter is FALSE. +*/ + +int +Field_longstr::report_if_important_data(const char *pstr, const char *end, + bool count_spaces) +{ + THD *thd; + if ((pstr < end) && + (thd= get_thd())->count_cuted_fields > CHECK_FIELD_EXPRESSION) + { + if (test_if_important_data(field_charset(), pstr, end)) + { + if (thd->abort_on_warning) + set_warning(ER_DATA_TOO_LONG, 1); + else + set_warning(WARN_DATA_TRUNCATED, 1); + return 2; + } + else if (count_spaces) + { + /* If we lost only spaces then produce a NOTE, not a WARNING */ + set_note(WARN_DATA_TRUNCATED, 1); + return 2; + } + } + return 0; +} + + +/* + This is JSON specific. + We should eventually add Field_json_varchar and Field_json_blob + and move make_send_field() to the new classes. +*/ +void Field_longstr::make_send_field(Send_field *field) +{ + Field_str::make_send_field(field); + if (check_constraint) + { + /* + Append the format that is implicitly implied by the CHECK CONSTRAINT. + For example: + CREATE TABLE t1 (js longtext DEFAULT NULL CHECK (json_valid(a))); + SELECT j FROM t1; + will add "format=json" to the extended type info metadata for t1.js. + */ + check_constraint->expr->set_format_by_check_constraint(field); + } +} + + +/* + An optimized version that uses less stack than Field::send(). +*/ +bool Field_longstr::send(Protocol *protocol) +{ + String tmp; + val_str(&tmp, &tmp); + /* + Ensure this function is only used with classes that do not allocate + memory in val_str() + */ + DBUG_ASSERT(tmp.alloced_length() == 0); + return protocol->store(tmp.ptr(), tmp.length(), tmp.charset()); +} + + +const Type_handler *Field_string::type_handler() const +{ + if (is_var_string()) + return &type_handler_var_string; + /* + This is a temporary solution and will be fixed soon (in 10.9?). + Type_handler_string_json will provide its own Field_string_json. + */ + if (Type_handler_json_common::has_json_valid_constraint(this)) + return &type_handler_string_json; + return &type_handler_string; +} + + /* Copy a string and fill with space */ + +int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + uint copy_length; + int rc; + + /* See the comment for Field_long::store(long long) */ + DBUG_ASSERT(!table || table->in_use == current_thd); + + rc= well_formed_copy_with_check((char*) ptr, field_length, + cs, from, length, + Field_string::char_length(), + false, ©_length); + + /* Append spaces if the string was shorter than the field. */ + if (copy_length < field_length) + field_charset()->fill((char*) ptr + copy_length, + field_length - copy_length, + field_charset()->pad_char); + + return rc; +} + + +int Field_str::store(longlong nr, bool unsigned_val) +{ + char buff[64]; + uint length; + length= (uint) (field_charset()->longlong10_to_str)(buff, sizeof(buff), + (unsigned_val ? 10: -10), + nr); + return store(buff, length, field_charset()); +} + + +/** + Store double value in Field_string or Field_varstring. + + Pretty prints double number into field_length characters buffer. + + @param nr number +*/ + +int Field_str::store(double nr) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; + uint local_char_length= MY_MIN(sizeof(buff), Field_str::char_length()); + size_t length= 0; + my_bool error= (local_char_length == 0); + + // my_gcvt() requires width > 0, and we may have a CHAR(0) column. + if (likely(!error)) + length= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, local_char_length, buff, &error); + + if (unlikely(error)) + { + if (get_thd()->abort_on_warning) + set_warning(ER_DATA_TOO_LONG, 1); + else + set_warning(WARN_DATA_TRUNCATED, 1); + } + return store(buff, (uint)length, &my_charset_numeric); +} + +bool Field_string::is_equal(const Column_definition &new_field) const +{ + DBUG_ASSERT(!compression_method()); + return new_field.type_handler() == type_handler() && + new_field.char_length == char_length() && + new_field.charset == field_charset() && + new_field.length == max_display_length(); +} + + +int Field_longstr::store_decimal(const my_decimal *d) +{ + StringBuffer<DECIMAL_MAX_STR_LENGTH+1> str; + d->to_string(&str); + return store(str.ptr(), str.length(), str.charset()); +} + +uint32 Field_longstr::max_data_length() const +{ + return field_length + (field_length > 255 ? 2 : 1); +} + + +Data_type_compatibility +Field_longstr::cmp_to_string_with_same_collation(const Item_bool_func *cond, + const Item *item) const +{ + return !cmp_is_done_using_type_handler_of_this(cond, item) ? + Data_type_compatibility::INCOMPATIBLE_DATA_TYPE : + charset() != cond->compare_collation() ? + Data_type_compatibility::INCOMPATIBLE_COLLATION : + Data_type_compatibility::OK; +} + + +Data_type_compatibility +Field_longstr::cmp_to_string_with_stricter_collation(const Item_bool_func *cond, + const Item *item) const +{ + return !cmp_is_done_using_type_handler_of_this(cond, item) ? + Data_type_compatibility::INCOMPATIBLE_DATA_TYPE : + (charset() != cond->compare_collation() && + !(cond->compare_collation()->state & MY_CS_BINSORT) && + !Utf8_narrow::should_do_narrowing(this, cond->compare_collation())) ? + Data_type_compatibility::INCOMPATIBLE_COLLATION : + Data_type_compatibility::OK; +} + + +Data_type_compatibility +Field_longstr::can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const +{ + DBUG_ASSERT(cmp_type() == STRING_RESULT); + /* + So, we have an equality: tbl.string_key = 'abc' + + The comparison is the string comparison. Can we use index lookups to + find matching rows? We can do that when: + - The comparison uses the same collation as tbl.string_key + - the comparison uses binary collation, while tbl.string_key + uses some other collation. + In this case, we will find matches in some collation. For example, for + 'abc' we may find 'abc', 'ABC', and 'äbc'. + But we're certain that will find the row with the identical binary, 'abc'. + */ + return cmp_to_string_with_stricter_collation(cond, item); +} + + +Data_type_compatibility +Field_longstr::can_optimize_hash_join(const Item_bool_func *cond, + const Item *item) const +{ + DBUG_ASSERT(cmp_type() == STRING_RESULT); + return cmp_to_string_with_same_collation(cond, item); +} + + +Data_type_compatibility +Field_longstr::can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const +{ + /* + Can't use indexes when comparing a string to a number or a date + Don't use an index when comparing strings of different collations. + */ + DBUG_ASSERT(cmp_type() == STRING_RESULT); + return cmp_to_string_with_same_collation(cond, const_item); +} + + +Data_type_compatibility +Field_longstr::can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const +{ + return is_eq_func ? + cmp_to_string_with_stricter_collation(cond, item) : + cmp_to_string_with_same_collation(cond, item); +} + + +/** + This overrides the default behavior of the parent constructor + Warn_filter(thd) to suppress notes about trailing spaces in case of CHAR(N), + as they are truncated during val_str(). + We still do want truncation notes in case of BINARY(N), + as trailing spaces are not truncated in val_str(). +*/ +Field_string::Warn_filter_string::Warn_filter_string(const THD *thd, + const Field_string *field) + :Warn_filter(!thd->no_errors, + !thd->no_errors && + field->field_charset() == &my_charset_bin) +{ } + + +double Field_string::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + return Converter_strntod_with_warn(get_thd(), + Warn_filter_string(thd, this), + Field_string::charset(), + (const char *) ptr, + field_length).result(); +} + + +longlong Field_string::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + return Converter_strntoll_with_warn(thd, Warn_filter_string(thd, this), + Field_string::charset(), + (const char *) ptr, + field_length).result(); +} + + +sql_mode_t Field_string::value_depends_on_sql_mode() const +{ + return has_charset() ? MODE_PAD_CHAR_TO_FULL_LENGTH : sql_mode_t(0); +}; + + +sql_mode_t Field_string::can_handle_sql_mode_dependency_on_store() const +{ + return has_charset() ? MODE_PAD_CHAR_TO_FULL_LENGTH : sql_mode_t(0); +} + + +String *Field_string::val_str(String *val_buffer __attribute__((unused)), + String *val_ptr) +{ + DBUG_ASSERT(marked_for_read()); + /* See the comment for Field_long::store(long long) */ + DBUG_ASSERT(!table || table->in_use == current_thd); + size_t length; + if (get_thd()->variables.sql_mode & + MODE_PAD_CHAR_TO_FULL_LENGTH) + length= field_charset()->charpos(ptr, ptr + field_length, + Field_string::char_length()); + else + length= field_charset()->lengthsp((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) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + Converter_str2my_decimal_with_warn(thd, + Warn_filter_string(thd, this), + E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM, + Field_string::charset(), + (const char *) ptr, + field_length, decimal_value); + return decimal_value; +} + + +struct Check_field_param { + const Field *field; +}; + +#ifdef HAVE_REPLICATION +static bool +check_field_for_37426(const void *param_arg) +{ + Check_field_param *param= (Check_field_param*) param_arg; + DBUG_ASSERT(param->field->real_type() == MYSQL_TYPE_STRING); + DBUG_PRINT("debug", ("Field %s - type: %d, size: %d", + param->field->field_name.str, + param->field->real_type(), + param->field->row_pack_length())); + return param->field->row_pack_length() > 255; +} +#endif + +bool +Field_string::compatible_field_size(uint field_metadata, + const Relay_log_info *rli_arg, + uint16 mflags __attribute__((unused)), + int *order_var) const +{ +#ifdef HAVE_REPLICATION + const Check_field_param check_param = { this }; + if (rpl_master_has_bug(rli_arg, 37426, TRUE, + check_field_for_37426, &check_param)) + return FALSE; // Not compatible field sizes +#endif + return Field::compatible_field_size(field_metadata, rli_arg, mflags, order_var); +} + + +int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + return field_charset()->coll->strnncollsp_nchars(field_charset(), + a_ptr, field_length, + b_ptr, field_length, + Field_string::char_length(), + MY_STRNNCOLLSP_NCHARS_EMULATE_TRIMMED_TRAILING_SPACES); +} + + +void Field_string::sort_string(uchar *to,uint length) +{ +#ifdef DBUG_ASSERT_EXISTS + size_t tmp= +#endif + field_charset()->strnxfrm(to, length, + char_length() * field_charset()->strxfrm_multiply, + ptr, field_length, + MY_STRXFRM_PAD_WITH_SPACE | + MY_STRXFRM_PAD_TO_MAXLEN); + DBUG_ASSERT(tmp == length); +} + + +void Field_string::sql_type(String &res) const +{ + THD *thd= table->in_use; + CHARSET_INFO *cs=res.charset(); + size_t length; + + length= cs->cset->snprintf(cs,(char*) res.ptr(), + res.alloced_length(), "%s(%d)%s", + (type() == MYSQL_TYPE_VAR_STRING ? + (has_charset() ? "varchar" : "varbinary") : + (has_charset() ? "char" : "binary")), + (int) field_length / charset()->mbmaxlen, + type() == MYSQL_TYPE_VAR_STRING ? "/*old*/" : ""); + res.length(length); + if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && + has_charset() && (charset()->state & MY_CS_BINSORT)) + res.append(STRING_WITH_LEN(" binary")); +} + +/** + For fields which are associated with character sets their length is provided + in octets and their character set information is also provided as part of + type information. + + @param res String which contains filed type and length. +*/ +void Field_string::sql_rpl_type(String *res) const +{ + if (Field_string::has_charset()) + { + CHARSET_INFO *cs= res->charset(); + DBUG_ASSERT(cs->mbminlen == 1); + size_t length= cs->cset->snprintf(cs, (char*) res->ptr(), + res->alloced_length(), + "char(%u octets) character set %s", + field_length, + charset()->cs_name.str); + res->length(length); + } + else + Field_string::sql_type(*res); + } + +uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) +{ + DBUG_PRINT("debug", ("Packing field '%s'", field_name.str)); + return StringPack(field_charset(), field_length).pack(to, from, max_length); +} + + +/** + Unpack a string field from row data. + + This method is used to unpack a string field from a master whose size + of the field is less than that of the slave. Note that there can be a + variety of field types represented with this class. Certain types like + ENUM or SET are processed differently. Hence, the upper byte of the + @c param_data argument contains the result of field->real_type() from + the master. + + @note For information about how the length is packed, see @c + Field_string::save_field_metadata + + @param to Destination of the data + @param from Source of the data + @param param_data Real type (upper) and length (lower) values + + @return New pointer into memory based on from + length of the data +*/ +const uchar * +Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end, + uint param_data) +{ + return StringPack(field_charset(), field_length).unpack(to, from, from_end, + param_data); +} + + +/** + Save the field metadata for string fields. + + Saves the real type in the first byte and the field length in the + second byte of the field metadata array at index of *metadata_ptr and + *(metadata_ptr + 1). + + @note In order to be able to handle lengths exceeding 255 and be + backwards-compatible with pre-5.1.26 servers, an extra two bits of + the length has been added to the metadata in such a way that if + they are set, a new unrecognized type is generated. This will + cause pre-5.1-26 servers to stop due to a field type mismatch, + while new servers will be able to extract the extra bits. If the + length is <256, there will be no difference and both a new and an + old server will be able to handle it. + + @note The extra two bits are added to bits 13 and 14 of the + parameter data (with 1 being the least siginficant bit and 16 the + most significant bit of the word) by xoring the extra length bits + with the real type. Since all allowable types have 0xF as most + significant bits of the metadata word, lengths <256 will not affect + the real type at all, while all other values will result in a + non-existent type in the range 17-244. + + @see Field_string::unpack + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ + +Binlog_type_info_fixed_string::Binlog_type_info_fixed_string(uchar type_code, + uint32 octets, + CHARSET_INFO *cs) + :Binlog_type_info(type_code, 0, 2, cs) +{ + DBUG_ASSERT(octets < 1024); + DBUG_ASSERT((type_code & 0xF0) == 0xF0); + DBUG_PRINT("debug", ("octets: %u, type_code: %u", octets, type_code)); + m_metadata= (type_code ^ ((octets & 0x300) >> 4)) + + (((uint)(octets & 0xFF)) << 8); +} + + +Binlog_type_info Field_string::binlog_type_info() const +{ + DBUG_ASSERT(Field_string::type() == binlog_type()); + return Binlog_type_info_fixed_string(Field_string::binlog_type(), + field_length, charset()); +} + + +uint Field_string::packed_col_length(const uchar *data_ptr, uint length) +{ + return StringPack::packed_col_length(data_ptr, length); +} + + +uint Field_string::max_packed_col_length(uint max_length) +{ + return StringPack::max_packed_col_length(max_length); +} + + +uint Field_string::get_key_image(uchar *buff, uint length, const uchar *ptr_arg, + imagetype type_arg) const +{ + size_t bytes= field_charset()->charpos((char*) ptr_arg, + (char*) ptr_arg + field_length, + length / mbmaxlen()); + memcpy(buff, ptr_arg, bytes); + if (bytes < length) + field_charset()->fill((char*) buff + bytes, + length - bytes, + field_charset()->pad_char); + return (uint)bytes; +} + + +Field *Field_string::make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type) +{ + Field *field; + if (type() != MYSQL_TYPE_VAR_STRING || keep_type) + field= Field::make_new_field(root, new_table, keep_type); + else if ((field= new (root) Field_varstring(field_length, maybe_null(), + &field_name, + new_table->s, charset()))) + { + /* + Old VARCHAR field which should be modified to a VARCHAR on copy + This is done to ensure that ALTER TABLE will convert old VARCHAR fields + to now VARCHAR fields. + */ + field->init_for_make_new_field(new_table, orig_table); + } + return field; +} + + +en_fieldtype Field_string::tmp_engine_column_type(bool use_packed_rows) const +{ + return field_length >= MIN_STRING_LENGTH_TO_PACK_ROWS ? FIELD_SKIP_ENDSPACE : + FIELD_NORMAL; +} + +/**************************************************************************** + VARCHAR type + Data in field->ptr is stored as: + 1 or 2 bytes length-prefix-header (from Field_varstring::length_bytes) + data + + NOTE: + When VARCHAR is stored in a key (for handler::index_read() etc) it's always + stored with a 2 byte prefix. (Just like blob keys). + + Normally length_bytes is calculated as (field_length < 256 : 1 ? 2) + The exception is if there is a prefix key field that is part of a long + VARCHAR, in which case field_length for this may be 1 but the length_bytes + is 2. +****************************************************************************/ + +const uint Field_varstring::MAX_SIZE= UINT_MAX16; + + +const Type_handler *Field_varstring::type_handler() const +{ + /* + This is a temporary solution and will be fixed soon (in 10.9?). + Type_handler_varchar_json will provide its own Field_varstring_json + and Field_varstring_compressed_json + */ + if (Type_handler_json_common::has_json_valid_constraint(this)) + return &type_handler_varchar_json; + return &type_handler_varchar; +} + + +/** + Save the field metadata for varstring fields. + + Saves the field length in the first byte. Note: may consume + 2 bytes. Caller must ensure second byte is contiguous with + first byte (e.g. array index 0,1). + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +Binlog_type_info Field_varstring::binlog_type_info() const +{ + DBUG_ASSERT(Field_varstring::type() == binlog_type()); + return Binlog_type_info(Field_varstring::type(), field_length, 2, charset()); +} + + +bool Field_varstring::memcpy_field_possible(const Field *from) const +{ + return (Field_str::memcpy_field_possible(from) && + !compression_method() == !from->compression_method() && + length_bytes == ((Field_varstring*) from)->length_bytes && + (table->file && !(table->file->ha_table_flags() & + HA_RECORD_MUST_BE_CLEAN_ON_WRITE))); +} + + +int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + uint copy_length; + int rc; + + rc= well_formed_copy_with_check((char*) get_data(), field_length, + cs, from, length, + Field_varstring::char_length(), + true, ©_length); + + store_length(copy_length); + + return rc; +} + + +double Field_varstring::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + return Converter_strntod_with_warn(thd, Warn_filter(thd), + Field_varstring::charset(), + (const char *) get_data(), + get_length()).result(); +} + + +longlong Field_varstring::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), + Field_varstring::charset(), + (const char *) get_data(), + get_length()).result(); +} + + +String *Field_varstring::val_str(String *val_buffer __attribute__((unused)), + String *val_ptr) +{ + DBUG_ASSERT(marked_for_read()); + val_ptr->set((const char*) get_data(), get_length(), field_charset()); + return val_ptr; +} + + +my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + Converter_str2my_decimal_with_warn(thd, Warn_filter(thd), + E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM, + Field_varstring::charset(), + (const char *) get_data(), + get_length(), decimal_value); + return decimal_value; + +} + + +/* + An optimized version that uses less stack and less temporary + variable initialization than Field_longstr::send() +*/ +bool Field_varstring::send(Protocol *protocol) +{ + return protocol->store((const char *) get_data(), get_length(), + field_charset()); +} + + +#ifdef HAVE_MEM_CHECK + +/* + Mark the unused part of the varstring as defined. + + This function is only used be Unique when calculating statistics. + + The marking is needed as we write the whole tree to disk in case of + overflows. For using or comparing values the undefined value part + is never used. We could also use bzero() here, but it would be + slower in production environments. + This function is tested by main.stat_tables-enospc +*/ + +void Field_varstring::mark_unused_memory_as_defined() +{ + uint used_length __attribute__((unused)) = get_length(); + MEM_MAKE_DEFINED(get_data() + used_length, field_length - used_length); +} +#endif + + +int Field_varstring::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + uint a_length, b_length; + int diff; + + if (length_bytes == 1) + { + a_length= (uint) *a_ptr; + b_length= (uint) *b_ptr; + } + else + { + a_length= uint2korr(a_ptr); + b_length= uint2korr(b_ptr); + } + set_if_smaller(a_length, field_length); + set_if_smaller(b_length, field_length); + diff= field_charset()->strnncollsp(a_ptr + length_bytes, a_length, + b_ptr + length_bytes, b_length); + return diff; +} + + +int Field_varstring::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr, + size_t prefix_char_len) const +{ + /* avoid more expensive strnncollsp_nchars() if possible */ + if (prefix_char_len * field_charset()->mbmaxlen == + table->field[field_index]->field_length) + return Field_varstring::cmp(a_ptr, b_ptr); + + size_t a_length, b_length; + + if (length_bytes == 1) + { + a_length= *a_ptr; + b_length= *b_ptr; + } + else + { + a_length= uint2korr(a_ptr); + b_length= uint2korr(b_ptr); + } + return field_charset()->coll->strnncollsp_nchars(field_charset(), + a_ptr + length_bytes, + a_length, + b_ptr + length_bytes, + b_length, + prefix_char_len, + 0); +} + + +/** + @note + varstring and blob keys are ALWAYS stored with a 2 byte length prefix +*/ + +int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length) const +{ + size_t length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); + size_t local_char_length= max_key_length / mbmaxlen(); + + local_char_length= field_charset()->charpos(ptr + length_bytes, + ptr + length_bytes + length, + local_char_length); + set_if_smaller(length, local_char_length); + return field_charset()->strnncollsp(ptr + length_bytes, + length, + key_ptr + HA_KEY_BLOB_LENGTH, + uint2korr(key_ptr)); +} + + +/** + Compare to key segments (always 2 byte length prefix). + + @note + This is used only to compare key segments created for index_read(). + (keys are created and compared in key.cc) +*/ + +int Field_varstring::key_cmp(const uchar *a,const uchar *b) const +{ + return field_charset()->strnncollsp(a + HA_KEY_BLOB_LENGTH, uint2korr(a), + b + HA_KEY_BLOB_LENGTH, uint2korr(b)); +} + + +void Field_varstring::sort_string(uchar *to,uint length) +{ + String buf; + + val_str(&buf, &buf); + + if (field_charset() == &my_charset_bin) + { + /* Store length last in high-byte order to sort longer strings first */ + if (length_bytes == 1) + to[length - 1]= buf.length(); + else + mi_int2store(to + length - 2, buf.length()); + length-= length_bytes; + } + +#ifdef DBUG_ASSERT_EXISTS + size_t rc= +#endif + field_charset()->strnxfrm(to, length, + char_length() * field_charset()->strxfrm_multiply, + (const uchar *) buf.ptr(), buf.length(), + MY_STRXFRM_PAD_WITH_SPACE | + MY_STRXFRM_PAD_TO_MAXLEN); + DBUG_ASSERT(rc == length); +} + + +enum ha_base_keytype Field_varstring::key_type() const +{ + enum ha_base_keytype res; + + if (binary()) + res= length_bytes == 1 ? HA_KEYTYPE_VARBINARY1 : HA_KEYTYPE_VARBINARY2; + else + res= length_bytes == 1 ? HA_KEYTYPE_VARTEXT1 : HA_KEYTYPE_VARTEXT2; + return res; +} + + +/* + Compressed columns need one extra byte to store the compression method. + This byte is invisible to the end user, but not for the storage engine. +*/ + +void Field_varstring::sql_type(String &res) const +{ + THD *thd= table->in_use; + CHARSET_INFO *cs=res.charset(); + size_t length; + + length= cs->cset->snprintf(cs,(char*) res.ptr(), + res.alloced_length(), "%s(%u)", + (has_charset() ? "varchar" : "varbinary"), + (uint) char_length()); + res.length(length); + if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && + has_charset() && (charset()->state & MY_CS_BINSORT)) + res.append(STRING_WITH_LEN(" binary")); +} + +/** + For fields which are associated with character sets their length is provided + in octets and their character set information is also provided as part of + type information. + + @param res String which contains filed type and length. +*/ +void Field_varstring::sql_rpl_type(String *res) const +{ + if (Field_varstring::has_charset()) + { + CHARSET_INFO *cs= res->charset(); + DBUG_ASSERT(cs->mbminlen == 1); + size_t length= cs->cset->snprintf(cs, (char*) res->ptr(), + res->alloced_length(), + "varchar(%u octets) character set %s", + field_length, + charset()->cs_name.str); + res->length(length); + } + else + Field_varstring::sql_type(*res); +} + + +uint32 Field_varstring::data_length() +{ + return length_bytes == 1 ? (uint32) *ptr : uint2korr(ptr); +} + +/* + Functions to create a packed row. + Here the number of length bytes are depending on the given max_length +*/ + +uchar *Field_varstring::pack(uchar *to, const uchar *from, uint max_length) +{ + uint length= length_bytes == 1 ? (uint) *from : uint2korr(from); + set_if_smaller(max_length, field_length); + if (length > max_length) + length=max_length; + + /* Length always stored little-endian */ + *to++= length & 0xFF; + if (max_length > 255) + *to++= (length >> 8) & 0xFF; + + /* Store bytes of string */ + if (length > 0) + memcpy(to, from+length_bytes, length); + return to+length; +} + + +/** + Unpack a varstring field from row data. + + This method is used to unpack a varstring field from a master + whose size of the field is less than that of the slave. + + @note + The string length is always packed little-endian. + + @param to Destination of the data + @param from Source of the data + @param param_data Length bytes from the master's field data + + @return New pointer into memory based on from + length of the data +*/ +const uchar * +Field_varstring::unpack(uchar *to, const uchar *from, const uchar *from_end, + uint param_data) +{ + uint length; + uint l_bytes= (param_data && (param_data < field_length)) ? + (param_data <= 255) ? 1 : 2 : length_bytes; + + if (from + l_bytes > from_end) + return 0; // Error in data + + if (l_bytes == 1) + { + to[0]= *from++; + length= to[0]; + if (length_bytes == 2) + to[1]= 0; + } + else /* l_bytes == 2 */ + { + length= uint2korr(from); + to[0]= *from++; + to[1]= *from++; + } + if (length) + { + if (from + length > from_end || length > field_length) + return 0; // Error in data + memcpy(to+ length_bytes, from, length); + } + return from+length; +} + + +uint Field_varstring::packed_col_length(const uchar *data_ptr, uint length) +{ + if (length > 255) + return uint2korr(data_ptr)+2; + return (uint) *data_ptr + 1; +} + + +uint Field_varstring::max_packed_col_length(uint max_length) +{ + return (max_length > 255 ? 2 : 1)+max_length; +} + +void Field_varstring::val_str_from_ptr(String *val, const uchar *ptr) const +{ + val->set((const char*) get_data(ptr), get_length(ptr), field_charset()); +} + +uint Field_varstring::get_key_image(uchar *buff, uint length, + const uchar *ptr_arg, + imagetype type_arg) const +{ + String val; + val_str_from_ptr(&val, ptr_arg); + + uint local_char_length= val.charpos(length / mbmaxlen()); + if (local_char_length < val.length()) + val.length(local_char_length); + /* Key is always stored with 2 bytes */ + int2store(buff, val.length()); + memcpy(buff + HA_KEY_BLOB_LENGTH, val.ptr(), val.length()); + if (val.length() < length) + { + /* + Must clear this as we do a memcmp in opt_range.cc to detect + identical keys + */ + memset(buff + HA_KEY_BLOB_LENGTH + val.length(), 0, length - val.length()); + } + return HA_KEY_BLOB_LENGTH + val.length(); +} + + +void Field_varstring::set_key_image(const uchar *buff,uint length) +{ + length= uint2korr(buff); // Real length is here + (void) store((const char*) buff + HA_KEY_BLOB_LENGTH, length, field_charset()); +} + + +int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, + uint32 max_length) const +{ + uint32 a_length,b_length; + + if (length_bytes == 1) + { + a_length= (uint) *a_ptr; + b_length= (uint) *b_ptr; + } + else + { + a_length= uint2korr(a_ptr); + b_length= uint2korr(b_ptr); + } + set_if_smaller(a_length, max_length); + set_if_smaller(b_length, max_length); + if (a_length != b_length) + return 1; + return memcmp(a_ptr+length_bytes, b_ptr+length_bytes, a_length); +} + + +Field *Field_varstring::make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type) +{ + Field_varstring *res= (Field_varstring*) Field::make_new_field(root, + new_table, + keep_type); + if (res) + res->length_bytes= length_bytes; + return res; +} + + +Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit) +{ + Field_varstring *res; + if ((res= (Field_varstring*) Field::new_key_field(root, new_table, + new_ptr, length, + new_null_ptr, new_null_bit))) + { + /* Keys length prefixes are always packed with 2 bytes */ + res->length_bytes= 2; + } + return res; +} + +bool Field_varstring::is_equal(const Column_definition &new_field) const +{ + return new_field.type_handler() == type_handler() && + new_field.length == field_length && + new_field.char_length == char_length() && + !new_field.compression_method() == !compression_method() && + new_field.charset == field_charset(); +} + + +void Field_varstring::hash_not_null(Hasher *hasher) +{ + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(!is_null()); + uint len= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); + hasher->add(charset(), ptr + length_bytes, len); +} + + +/** + Compress field + + @param[out] to destination buffer for compressed data + @param[in] to_length size of to + @param[in] from data to compress + @param[in] length from length + @param[in] max_length truncate `from' to this length + @param[out] out_length compessed data length + @param[in] cs from character set + @param[in] nchars copy no more than "nchars" characters + + In worst case (no compression performed) storage requirement is increased by + 1 byte to store header. If it exceeds field length, normal data truncation is + performed. + + Generic compressed header format (1 byte): + + Bits 1-4: method specific bits + Bits 5-8: compression method + + If compression method is 0 then header is immediately followed by + uncompressed data. + + If compression method is zlib: + + Bits 1-3: number of bytes occupied by original data length + Bits 4: true if zlib wrapper not present + Bits 5-8: store 8 (zlib) + + Header is immediately followed by original data length, + followed by compressed data. +*/ + +int Field_longstr::compress(char *to, uint to_length, + const char *from, uint length, + uint max_length, + uint *out_length, + CHARSET_INFO *cs, size_t nchars) +{ + THD *thd= get_thd(); + char *buf; + uint buf_length; + int rc= 0; + + if (String::needs_conversion_on_storage(length, cs, field_charset()) || + max_length < length) + { + set_if_smaller(max_length, static_cast<ulonglong>(mbmaxlen()) * length + 1); + if (!(buf= (char*) my_malloc(PSI_INSTRUMENT_ME, max_length, MYF(MY_WME)))) + { + *out_length= 0; + return -1; + } + + rc= well_formed_copy_with_check(buf, max_length, cs, from, length, + nchars, true, &buf_length); + } + else + { + buf= const_cast<char*>(from); + buf_length= length; + } + + if (buf_length == 0) + *out_length= 0; + else if (buf_length >= thd->variables.column_compression_threshold && + (*out_length= compression_method()->compress(thd, to, buf, buf_length))) + status_var_increment(thd->status_var.column_compressions); + else + { + /* Store uncompressed */ + to[0]= 0; + if (buf_length < to_length) + memcpy(to + 1, buf, buf_length); + else + { + /* Storing string at blob capacity, e.g. 255 bytes string to TINYBLOB. */ + rc= well_formed_copy_with_check(to + 1, to_length - 1, cs, from, length, + nchars, true, &buf_length); + } + *out_length= buf_length + 1; + } + + if (buf != from) + my_free(buf); + return rc; +} + + +/* + Memory is allocated only when original data was actually compressed. + Otherwise val_ptr points at data located immediately after header. + + Data can be stored uncompressed if data was shorter than threshold + or compressed data was longer than original data. +*/ + +String *Field_longstr::uncompress(String *val_buffer, String *val_ptr, + const uchar *from, uint from_length) const +{ + if (from_length) + { + uchar method= (*from & 0xF0) >> 4; + + /* Uncompressed data */ + if (!method) + { + val_ptr->set((const char*) from + 1, from_length - 1, field_charset()); + return val_ptr; + } + + if (compression_methods[method].uncompress) + { + if (!compression_methods[method].uncompress(val_buffer, from, from_length, + field_length)) + { + val_buffer->set_charset(field_charset()); + status_var_increment(get_thd()->status_var.column_decompressions); + return val_buffer; + } + } + } + + /* + It would be better to return 0 in case of errors, but to take the + safer route, let's return a zero string and let the general + handler catch the error. + */ + val_ptr->set("", 0, field_charset()); + return val_ptr; +} + + +int Field_varstring_compressed::store(const char *from, size_t length, + CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + uint compressed_length; + int rc= compress((char*) get_data(), field_length, from, (uint) length, + Field_varstring_compressed::max_display_length(), + &compressed_length, cs, + Field_varstring_compressed::char_length()); + store_length(compressed_length); + return rc; +} + +void Field_varstring_compressed::val_str_from_ptr(String *val, const uchar *ptr) const +{ + uncompress(val, val, get_data(ptr), get_length(ptr)); +} + + +String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr) +{ + DBUG_ASSERT(marked_for_read()); + return uncompress(val_buffer, val_ptr, get_data(), get_length()); +} + + +double Field_varstring_compressed::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset(), + buf.ptr(), buf.length()).result(); +} + + +longlong Field_varstring_compressed::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(), + buf.ptr(), buf.length()).result(); +} + + +int Field_varstring_compressed::cmp(const uchar *a_ptr, + const uchar *b_ptr) const +{ + String a, b; + uint a_length, b_length; + + if (length_bytes == 1) + { + a_length= (uint) *a_ptr; + b_length= (uint) *b_ptr; + } + else + { + a_length= uint2korr(a_ptr); + b_length= uint2korr(b_ptr); + } + + uncompress(&a, &a, a_ptr + length_bytes, a_length); + uncompress(&b, &b, b_ptr + length_bytes, b_length); + + return sortcmp(&a, &b, field_charset()); +} + + +Binlog_type_info Field_varstring_compressed::binlog_type_info() const +{ + return Binlog_type_info(Field_varstring_compressed::binlog_type(), + field_length, 2, charset()); +} + + +/**************************************************************************** +** blob type +** A blob is saved as a length and a pointer. The length is stored in the +** packlength slot and may be from 1-4. +****************************************************************************/ + +Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, + TABLE_SHARE *share, uint blob_pack_length, + const DTCollation &collation) + :Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length), + null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, + collation), + packlength(blob_pack_length) +{ + DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently + flags|= BLOB_FLAG; + share->blob_fields++; + /* TODO: why do not fill table->s->blob_field array here? */ +} + + +void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number) +{ + store_lowendian(i_number, i_ptr, i_packlength); +} + + +uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) const +{ + return (uint32)read_lowendian(pos, packlength_arg); +} + + +/** + Copy a value from another BLOB field of the same character set. + This method is used by Copy_field, e.g. during ALTER TABLE. +*/ +int Field_blob::copy_value(Field_blob *from) +{ + DBUG_ASSERT(field_charset() == from->charset()); + DBUG_ASSERT(!compression_method() == !from->compression_method()); + int rc= 0; + uint32 length= from->get_length(); + uchar *data= from->get_ptr(); + if (packlength < from->packlength) + { + set_if_smaller(length, Field_blob::max_data_length()); + length= (uint32) Well_formed_prefix(field_charset(), + (const char *) data, length).length(); + rc= report_if_important_data((const char *) data + length, + (const char *) data + from->get_length(), + true); + } + store_length(length); + bmove(ptr + packlength, (uchar*) &data, sizeof(char*)); + return rc; +} + + +int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + size_t copy_length, new_length; + uint copy_len; + char *tmp; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmpstr(buff,sizeof(buff), &my_charset_bin); + int rc; + + if (!length) + { + bzero(ptr,Field_blob::pack_length()); + return 0; + } + + /* + For min/max fields of statistical data 'table' is set to NULL. + It could not be otherwise as this data is shared by many instances + of the same base table. + */ + + if (table && table->blob_storage) // GROUP_CONCAT with ORDER BY | DISTINCT + { + DBUG_ASSERT(!f_is_hex_escape(flags)); + DBUG_ASSERT(field_charset() == cs); + DBUG_ASSERT(length <= max_data_length()); + + new_length= length; + copy_length= table->in_use->variables.group_concat_max_len; + if (new_length > copy_length) + { + new_length= Well_formed_prefix(cs, + from, copy_length, new_length).length(); + table->blob_storage->set_truncated_value(true); + } + if (!(tmp= table->blob_storage->store(from, new_length))) + goto oom_error; + + Field_blob::store_length(new_length); + bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*)); + return 0; + } + + /* + If the 'from' address is in the range of the temporary 'value'- + object we need to copy the content to a different location or it will be + invalidated when the 'value'-object is reallocated to make room for + the new character set. + */ + if (from >= value.ptr() && from <= value.ptr()+value.length()) + { + /* + If content of the 'from'-address is cached in the 'value'-object + it is possible that the content needs a character conversion. + */ + if (!String::needs_conversion_on_storage(length, cs, field_charset())) + { + Field_blob::store_length(length); + bmove(ptr + packlength, &from, sizeof(char*)); + return 0; + } + if (tmpstr.copy(from, length, cs)) + goto oom_error; + from= tmpstr.ptr(); + } + + new_length= MY_MIN(max_data_length(), mbmaxlen() * length); + if (value.alloc(new_length)) + goto oom_error; + tmp= const_cast<char*>(value.ptr()); + + if (f_is_hex_escape(flags)) + { + copy_length= my_copy_with_hex_escaping(field_charset(), + tmp, new_length, + from, length); + Field_blob::store_length(copy_length); + bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*)); + return 0; + } + rc= well_formed_copy_with_check((char*) value.ptr(), (uint) new_length, + cs, from, length, + length, true, ©_len); + value.length(copy_len); + Field_blob::store_length(copy_len); + bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*)); + + return rc; + +oom_error: + /* Fatal OOM error */ + bzero(ptr,Field_blob::pack_length()); + return -1; +} + + +void Field_blob::hash_not_null(Hasher *hasher) +{ + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(!is_null()); + char *blob; + memcpy(&blob, ptr + packlength, sizeof(char*)); + if (blob) + hasher->add(Field_blob::charset(), blob, get_length(ptr)); +} + + +double Field_blob::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + char *blob; + memcpy(&blob, ptr+packlength, sizeof(char*)); + if (!blob) + return 0.0; + THD *thd= get_thd(); + return Converter_strntod_with_warn(thd, Warn_filter(thd), + Field_blob::charset(), + blob, get_length(ptr)).result(); +} + + +longlong Field_blob::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + char *blob; + memcpy(&blob, ptr+packlength, sizeof(char*)); + if (!blob) + return 0; + THD *thd= get_thd(); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), + Field_blob::charset(), + blob, get_length(ptr)).result(); +} + + +String *Field_blob::val_str(String *val_buffer __attribute__((unused)), + String *val_ptr) +{ + DBUG_ASSERT(marked_for_read()); + char *blob; + memcpy(&blob, ptr+packlength, sizeof(char*)); + if (!blob) + val_ptr->set("",0,charset()); // A bit safer than ->length(0) + else + val_ptr->set((const char*) blob,get_length(ptr),charset()); + return val_ptr; +} + + +my_decimal *Field_blob::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(marked_for_read()); + const char *blob; + size_t length; + memcpy(&blob, ptr+packlength, sizeof(const uchar*)); + if (!blob) + { + blob= ""; + length= 0; + } + else + length= get_length(ptr); + + THD *thd= get_thd(); + Converter_str2my_decimal_with_warn(thd, Warn_filter(thd), + E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM, + Field_blob::charset(), + blob, length, decimal_value); + return decimal_value; +} + + +int Field_blob::cmp(const uchar *a,uint32 a_length, const uchar *b, + uint32 b_length) const +{ + return field_charset()->strnncollsp(a, a_length, b, b_length); +} + + +int Field_blob::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + uchar *blob1,*blob2; + memcpy(&blob1, a_ptr+packlength, sizeof(char*)); + memcpy(&blob2, b_ptr+packlength, sizeof(char*)); + size_t a_len= get_length(a_ptr), b_len= get_length(b_ptr); + return cmp(blob1, (uint32)a_len, blob2, (uint32)b_len); +} + + +int Field_blob::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr, + size_t prefix_char_len) const +{ + uchar *blob1,*blob2; + memcpy(&blob1, a_ptr+packlength, sizeof(char*)); + memcpy(&blob2, b_ptr+packlength, sizeof(char*)); + size_t a_len= get_length(a_ptr), b_len= get_length(b_ptr); + return field_charset()->coll->strnncollsp_nchars(field_charset(), + blob1, a_len, + blob2, b_len, + prefix_char_len, + 0); +} + + +int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, + uint32 max_length) const +{ + char *a,*b; + uint diff; + uint32 a_length,b_length; + memcpy(&a, a_ptr+packlength, sizeof(char*)); + memcpy(&b, b_ptr+packlength, sizeof(char*)); + a_length=get_length(a_ptr); + if (a_length > max_length) + a_length=max_length; + b_length=get_length(b_ptr); + if (b_length > max_length) + b_length=max_length; + if (uint32 len= MY_MIN(a_length,b_length)) + diff= memcmp(a,b,len); + else + diff= 0; + return diff ? diff : (int) (a_length - b_length); +} + + +/* The following is used only when comparing a key */ + +uint Field_blob::get_key_image_itRAW(const uchar *ptr_arg, uchar *buff, + uint length) const +{ + size_t blob_length= get_length(ptr_arg); + const uchar *blob= get_ptr(ptr_arg); + size_t local_char_length= length / mbmaxlen(); + local_char_length= field_charset()->charpos(blob, blob + blob_length, + local_char_length); + set_if_smaller(blob_length, local_char_length); + + if (length > blob_length) + { + /* + Must clear this as we do a memcmp in opt_range.cc to detect + identical keys + */ + bzero(buff+HA_KEY_BLOB_LENGTH+blob_length, (length-blob_length)); + length=(uint) blob_length; + } + int2store(buff,length); + if (length) + memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length); + return HA_KEY_BLOB_LENGTH+length; +} + + +void Field_blob::set_key_image(const uchar *buff,uint length) +{ + length= uint2korr(buff); + (void) Field_blob::store((const char*) buff+HA_KEY_BLOB_LENGTH, length, + field_charset()); +} + + +int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length) const +{ + uchar *blob1; + size_t blob_length=get_length(ptr); + memcpy(&blob1, ptr+packlength, sizeof(char*)); + CHARSET_INFO *cs= charset(); + size_t local_char_length= max_key_length / cs->mbmaxlen; + local_char_length= cs->charpos(blob1, blob1+blob_length, + local_char_length); + set_if_smaller(blob_length, local_char_length); + return Field_blob::cmp(blob1, (uint32)blob_length, + key_ptr+HA_KEY_BLOB_LENGTH, + uint2korr(key_ptr)); +} + +int Field_blob::key_cmp(const uchar *a,const uchar *b) const +{ + return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a), + b+HA_KEY_BLOB_LENGTH, uint2korr(b)); +} + + +Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit) +{ + Field_varstring *res= new (root) Field_varstring(new_ptr, length, 2, + new_null_ptr, + new_null_bit, Field::NONE, + &field_name, + table->s, charset()); + res->init(new_table); + return res; +} + + +/** + Save the field metadata for blob fields. + + Saves the pack length in the first byte of the field metadata array + at index of *metadata_ptr. + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +Binlog_type_info Field_blob::binlog_type_info() const +{ + DBUG_ASSERT(Field_blob::type() == binlog_type()); + return Binlog_type_info(Field_blob::type(), pack_length_no_ptr(), 1, + charset()); +} + + +uint32 Field_blob::sort_length() const +{ + return packlength == 4 ? + UINT_MAX32 : + (uint32) field_length + sort_suffix_length(); +} + + +uint32 Field_blob::sort_suffix_length() const +{ + return field_charset() == &my_charset_bin ? packlength : 0; +} + + +void Field_blob::sort_string(uchar *to,uint length) +{ + String buf; + + val_str(&buf, &buf); + if (!buf.length() && field_charset()->pad_char == 0) + bzero(to,length); + else + { + if (field_charset() == &my_charset_bin) + { + /* + Store length of blob last in blob to shorter blobs before longer blobs + */ + length-= packlength; + store_bigendian(buf.length(), to + length, packlength); + } + +#ifdef DBUG_ASSERT_EXISTS + size_t rc= +#endif + field_charset()->strnxfrm(to, length, length, + (const uchar *) buf.ptr(), buf.length(), + MY_STRXFRM_PAD_WITH_SPACE | + MY_STRXFRM_PAD_TO_MAXLEN); + DBUG_ASSERT(rc == length); + } +} + + +/* + Return the data type handler, according to packlength. + Implemented in field.cc rather than in field.h + to avoid exporting type_handler_xxx with MYSQL_PLUGIN_IMPORT. +*/ +const Type_handler *Field_blob::type_handler() const +{ + /* + This is a temporary solution and will be fixed soon (in 10.9?). + Type_handler_*blob_json will provide its own Field_blob_json + and Field_blob_compressed_json. + */ + if (Type_handler_json_common::has_json_valid_constraint(this)) + return Type_handler_json_common:: + json_blob_type_handler_by_length_bytes(packlength); + + switch (packlength) { + case 1: return &type_handler_tiny_blob; + case 2: return &type_handler_blob; + case 3: return &type_handler_medium_blob; + } + return &type_handler_long_blob; +} + + +void Field_blob::sql_type(String &res) const +{ + const char *str; + uint length; + switch (packlength) { + default: str="tiny"; length=4; break; + case 2: str=""; length=0; break; + case 3: str="medium"; length= 6; break; + case 4: str="long"; length=4; break; + } + res.set_ascii(str,length); + if (charset() == &my_charset_bin) + { + res.append(STRING_WITH_LEN("blob")); + if (packlength == 2 && (get_thd()->variables.sql_mode & MODE_ORACLE)) + res.append(STRING_WITH_LEN("(65535)")); + } + else + { + res.append(STRING_WITH_LEN("text")); + } +} + +uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length) +{ + uint32 length=get_length(from, packlength); // Length of from string + + /* + Store max length, which will occupy packlength bytes. If the max + length given is smaller than the actual length of the blob, we + just store the initial bytes of the blob. + */ + store_length(to, packlength, MY_MIN(length, max_length)); + + /* + Store the actual blob data, which will occupy 'length' bytes. + */ + if (length > 0) + { + from= get_ptr(from); + memcpy(to+packlength, from,length); + } + return to+packlength+length; +} + + +/** + Unpack a blob field from row data. + + This method is used to unpack a blob field from a master whose size of + the field is less than that of the slave. Note: This method is included + to satisfy inheritance rules, but is not needed for blob fields. It + simply is used as a pass-through to the original unpack() method for + blob fields. + + @param to Destination of the data + @param from Source of the data + @param param_data @c TRUE if base types should be stored in little- + endian format, @c FALSE if native format should + be used. + + @return New pointer into memory based on from + length of the data +*/ + +const uchar *Field_blob::unpack(uchar *to, const uchar *from, + const uchar *from_end, uint param_data) +{ + DBUG_ENTER("Field_blob::unpack"); + DBUG_PRINT("enter", ("to: %p; from: %p; param_data: %u", + to, from, param_data)); + uint const master_packlength= + param_data > 0 ? param_data & 0xFF : packlength; + if (from + master_packlength > from_end) + DBUG_RETURN(0); // Error in data + uint32 const length= get_length(from, master_packlength); + DBUG_DUMP("packed", from, length + master_packlength); + if (from + master_packlength + length > from_end) + DBUG_RETURN(0); + set_ptr(length, const_cast<uchar*> (from) + master_packlength); + DBUG_RETURN(from + master_packlength + length); +} + + +uint Field_blob::packed_col_length(const uchar *data_ptr, uint length) +{ + if (length > 255) + return uint2korr(data_ptr)+2; + return (uint) *data_ptr + 1; +} + + +uint Field_blob::max_packed_col_length(uint max_length) +{ + return (max_length > 255 ? 2 : 1)+max_length; +} + + +/* + Blob fields are regarded equal if they have same character set, + same blob store length and if either both are compressed or both are + uncompressed. + The logic for compression is that we don't have to uncompress and compress + again an already compressed field just because compression method changes. +*/ + +bool Field_blob::is_equal(const Column_definition &new_field) const +{ + return new_field.type_handler() == type_handler() && + !new_field.compression_method() == !compression_method() && + new_field.pack_length == pack_length() && + new_field.charset == field_charset(); +} + + +void Field_blob::make_send_field(Send_field *field) +{ + /* + Historically all BLOB variant Fields are displayed as MYSQL_TYPE_BLOB + in the result set metadata. Note, Item can work differently and + display the exact BLOB type, such as + MYSQL_TYPE_{TINY_BLOB|BLOB|MEDIUM_BLOB|LONG_BLOB}. + QQ: this should be made consistent eventually. + */ + Field_longstr::make_send_field(field); + field->set_handler(&type_handler_blob); +} + + +bool Field_blob::make_empty_rec_store_default_value(THD *thd, Item *item) +{ + DBUG_ASSERT(flags & BLOB_FLAG); + int res= item->save_in_field(this, true); + DBUG_ASSERT(res != 3); // Field_blob never returns 3 + if (res) + return true; // E.g. truncation happened + reset(); // Clear the pointer to a String, it should not be written to frm + return false; +} + + +int Field_blob_compressed::store(const char *from, size_t length, + CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + uint compressed_length; + uint max_length= max_data_length(); + uint to_length= (uint) MY_MIN(max_length, mbmaxlen() * length + 1); + String tmp(from, length, cs); + int rc; + + if (from >= value.ptr() && from <= value.end() && tmp.copy(from, length, cs)) + goto oom; + + if (value.alloc(to_length)) + goto oom; + + rc= compress((char*) value.ptr(), to_length, tmp.ptr(), (uint) length, + max_length, &compressed_length, cs, (uint) length); + set_ptr(compressed_length, (uchar*) value.ptr()); + return rc; + +oom: + set_ptr((uint32) 0, NULL); + return -1; +} + + +String *Field_blob_compressed::val_str(String *val_buffer, String *val_ptr) +{ + DBUG_ASSERT(marked_for_read()); + return uncompress(val_buffer, val_ptr, get_ptr(), get_length()); +} + + +double Field_blob_compressed::val_real(void) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset(), + buf.ptr(), buf.length()).result(); +} + + +longlong Field_blob_compressed::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(), + buf.ptr(), buf.length()).result(); +} + +Binlog_type_info Field_blob_compressed::binlog_type_info() const +{ + return Binlog_type_info(Field_blob_compressed::binlog_type(), + pack_length_no_ptr(), 1, charset()); +} + +/**************************************************************************** +** enum type. +** This is a string which only can have a selection of different values. +** If one uses this string in a number context one gets the type number. +****************************************************************************/ + +sql_mode_t Field_enum::can_handle_sql_mode_dependency_on_store() const +{ + return MODE_PAD_CHAR_TO_FULL_LENGTH; +} + + +enum ha_base_keytype Field_enum::key_type() const +{ + switch (packlength) { + default: return HA_KEYTYPE_BINARY; + case 2: return HA_KEYTYPE_USHORT_INT; + case 3: return HA_KEYTYPE_UINT24; + case 4: return HA_KEYTYPE_ULONG_INT; + case 8: return HA_KEYTYPE_ULONGLONG; + } +} + +void Field_enum::store_type(ulonglong value) +{ + store_lowendian(value, ptr, packlength); +} + + +/** + @note + Storing a empty string in a enum field gives a warning + (if there isn't a empty value in the enum) +*/ + +int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int err= 0; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmpstr(buff,sizeof(buff), &my_charset_bin); + + /* Convert character set if necessary */ + if (String::needs_conversion_on_storage(length, cs, field_charset())) + { + uint dummy_errors; + tmpstr.copy(from, length, cs, field_charset(), &dummy_errors); + from= tmpstr.ptr(); + length= tmpstr.length(); + } + + /* Remove end space */ + length= (uint) field_charset()->lengthsp(from, length); + uint tmp=find_type2(typelib, from, length, field_charset()); + if (!tmp) + { + if (length < 6) // Can't be more than 99999 enums + { + /* This is for reading numbers with LOAD DATA INFILE */ + char *end; + tmp=(uint) cs->strntoul(from,length,10,&end,&err); + if (err || end != from+length || tmp > typelib->count) + { + tmp=0; + set_warning(WARN_DATA_TRUNCATED, 1); + err= 1; + } + if ((get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) && !length) + err= 0; + } + else + { + set_warning(WARN_DATA_TRUNCATED, 1); + err= 1; + } + } + store_type((ulonglong) tmp); + return err; +} + + +int Field_enum::store(double nr) +{ + return Field_enum::store((longlong) nr, FALSE); +} + + +int Field_enum::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + if ((ulonglong) nr > typelib->count || nr == 0) + { + set_warning(WARN_DATA_TRUNCATED, 1); + if (nr != 0 || get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) + { + nr= 0; + error= 1; + } + } + store_type((ulonglong) (uint) nr); + return error; +} + + +double Field_enum::val_real(void) +{ + return (double) Field_enum::val_int(); +} + + +longlong Field_enum::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + return val_int(ptr); +} + +longlong Field_enum::val_int(const uchar *real_ptr) const +{ + return read_lowendian(real_ptr, packlength); +} + +/** + Save the field metadata for enum fields. + + Saves the real type in the first byte and the pack length in the + second byte of the field metadata array at index of *metadata_ptr and + *(metadata_ptr + 1). + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +Binlog_type_info Field_enum::binlog_type_info() const +{ + DBUG_ASSERT(Field_enum::type() == binlog_type()); + return Binlog_type_info(Field_enum::type(), real_type() + (pack_length() << 8), + 2, charset(), (TYPELIB *)get_typelib(), NULL); +} + + +String *Field_enum::val_str(String *val_buffer __attribute__((unused)), + String *val_ptr) +{ + uint tmp=(uint) Field_enum::val_int(); + if (!tmp || tmp > typelib->count) + val_ptr->set("", 0, field_charset()); + else + val_ptr->set((const char*) typelib->type_names[tmp-1], + typelib->type_lengths[tmp-1], + field_charset()); + return val_ptr; +} + +int Field_enum::cmp(const uchar *a_ptr, const uchar *b_ptr) const +{ + ulonglong a=Field_enum::val_int(a_ptr); + ulonglong b=Field_enum::val_int(b_ptr); + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +void Field_enum::sort_string(uchar *to,uint length __attribute__((unused))) +{ + ulonglong value=Field_enum::val_int(); + to+=packlength-1; + for (uint i=0 ; i < packlength ; i++) + { + *to-- = (uchar) (value & 255); + value>>=8; + } +} + + +void Field_enum::sql_type(String &res) const +{ + char buffer[255]; + String enum_item(buffer, sizeof(buffer), res.charset()); + + res.length(0); + res.append(STRING_WITH_LEN("enum(")); + + bool flag=0; + uint *len= typelib->type_lengths; + for (const char **pos= typelib->type_names; *pos; pos++, len++) + { + uint dummy_errors; + if (flag) + res.append(','); + /* convert to res.charset() == utf8, then quote */ + enum_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors); + append_unescaped(&res, enum_item.ptr(), enum_item.length()); + flag= 1; + } + res.append(')'); +} + + +Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type) +{ + Field_enum *res= (Field_enum*) Field::make_new_field(root, new_table, + keep_type); + if (res) + res->typelib= copy_typelib(root, typelib); + return res; +} + + +/* + set type. + This is a string which can have a collection of different values. + Each string value is separated with a ','. + For example "One,two,five" + If one uses this string in a number context one gets the bits as a longlong + number. +*/ + + +int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + bool got_warning= 0; + int err= 0; + char *not_used; + uint not_used2; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmpstr(buff,sizeof(buff), &my_charset_bin); + + /* Convert character set if necessary */ + if (String::needs_conversion_on_storage(length, cs, field_charset())) + { + uint dummy_errors; + tmpstr.copy(from, length, cs, field_charset(), &dummy_errors); + from= tmpstr.ptr(); + length= tmpstr.length(); + } + ulonglong tmp= find_set(typelib, from, length, field_charset(), + ¬_used, ¬_used2, &got_warning); + if (!tmp && length && length < 22) + { + /* This is for reading numbers with LOAD DATA INFILE */ + char *end; + tmp= cs->strntoull(from, length, 10, &end, &err); + if (err || end != from + length) + { + set_warning(WARN_DATA_TRUNCATED, 1); + store_type(0); + return 1; + } + return Field_set::store((longlong) tmp, true/*unsigned*/); + } + else if (got_warning) + set_warning(WARN_DATA_TRUNCATED, 1); + store_type(tmp); + return err; +} + + +int Field_set::store(longlong nr, bool unsigned_val) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int error= 0; + ulonglong max_nr; + + if (sizeof(ulonglong)*8 <= typelib->count) + max_nr= ULONGLONG_MAX; + else + max_nr= (1ULL << typelib->count) - 1; + + if ((ulonglong) nr > max_nr) + { + nr&= max_nr; + set_warning(WARN_DATA_TRUNCATED, 1); + error=1; + } + store_type((ulonglong) nr); + return error; +} + + +String *Field_set::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + ulonglong tmp=(ulonglong) Field_enum::val_int(); + uint bitnr=0; + + val_buffer->set_charset(field_charset()); + val_buffer->length(0); + + while (tmp && bitnr < (uint) typelib->count) + { + if (tmp & 1) + { + if (val_buffer->length()) + val_buffer->append(&field_separator, 1, &my_charset_latin1); + String str(typelib->type_names[bitnr], typelib->type_lengths[bitnr], + field_charset()); + val_buffer->append(str); + } + tmp>>=1; + bitnr++; + } + return val_buffer; +} + + +void Field_set::sql_type(String &res) const +{ + char buffer[255]; + String set_item(buffer, sizeof(buffer), res.charset()); + + res.length(0); + res.append(STRING_WITH_LEN("set(")); + + bool flag=0; + uint *len= typelib->type_lengths; + for (const char **pos= typelib->type_names; *pos; pos++, len++) + { + uint dummy_errors; + if (flag) + res.append(','); + /* convert to res.charset() == utf8, then quote */ + set_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors); + append_unescaped(&res, set_item.ptr(), set_item.length()); + flag= 1; + } + res.append(')'); +} + +Binlog_type_info Field_set::binlog_type_info() const +{ + DBUG_ASSERT(Field_set::type() == binlog_type()); + return Binlog_type_info(Field_set::type(), real_type() + + (pack_length() << 8), 2, charset(), NULL, (TYPELIB *)get_typelib()); +} + +/** + @retval + 1 if the fields are equally defined + @retval + 0 if the fields are unequally defined +*/ + +bool Field::eq_def(const Field *field) const +{ + if (real_type() != field->real_type() || charset() != field->charset() || + pack_length() != field->pack_length()) + return 0; + return 1; +} + + +/** + Compare the first t1::count type names. + + @return TRUE if the type names of t1 match those of t2. FALSE otherwise. +*/ + +static bool compare_type_names(CHARSET_INFO *charset, const TYPELIB *t1, + const TYPELIB *t2) +{ + for (uint i= 0; i < t1->count; i++) + if (charset->strnncoll(t1->type_names[i], t1->type_lengths[i], + t2->type_names[i], t2->type_lengths[i])) + return FALSE; + return TRUE; +} + +/** + @return + returns 1 if the fields are equally defined +*/ + +bool Field_enum::eq_def(const Field *field) const +{ + const TYPELIB *values; + + if (!Field::eq_def(field)) + return FALSE; + + values= ((Field_enum*) field)->typelib; + + /* Definition must be strictly equal. */ + if (typelib->count != values->count) + return FALSE; + + return compare_type_names(field_charset(), typelib, values); +} + + +/** + Check whether two fields can be considered 'equal' for table + alteration purposes. Fields are equal if they retain the same + pack length and if new members are added to the end of the list. + + @return true if fields are compatible. + false otherwise. +*/ + +bool Field_enum::is_equal(const Column_definition &new_field) const +{ + const TYPELIB *values= new_field.interval; + + /* + The fields are compatible if they have the same flags, + type, charset and have the same underlying length. + */ + if (new_field.type_handler() != type_handler() || + new_field.charset != field_charset() || + new_field.pack_length != pack_length()) + return false; + + /* + Changing the definition of an ENUM or SET column by adding a new + enumeration or set members to the end of the list of valid member + values only alters table metadata and not table data. + */ + if (typelib->count > values->count) + return false; + + /* Check whether there are modification before the end. */ + if (! compare_type_names(field_charset(), typelib, new_field.interval)) + return false; + + return true; +} + + +uchar *Field_enum::pack(uchar *to, const uchar *from, uint max_length) +{ + DBUG_ENTER("Field_enum::pack"); + DBUG_PRINT("debug", ("packlength: %d", packlength)); + DBUG_DUMP("from", from, packlength); + DBUG_RETURN(pack_int(to, from, packlength)); +} + +const uchar *Field_enum::unpack(uchar *to, const uchar *from, + const uchar *from_end, uint param_data) +{ + DBUG_ENTER("Field_enum::unpack"); + DBUG_PRINT("debug", ("packlength: %d", packlength)); + DBUG_DUMP("from", from, packlength); + DBUG_RETURN(unpack_int(to, from, from_end, packlength)); +} + + +/** + @return + returns 1 if the fields are equally defined +*/ +bool Field_num::eq_def(const Field *field) const +{ + if (!Field::eq_def(field)) + return 0; + Field_num *from_num= (Field_num*) field; + + if (unsigned_flag != from_num->unsigned_flag || + (zerofill && !from_num->zerofill && !zero_pack()) || + dec != from_num->dec) + return 0; + return 1; +} + + +/** + Check whether two numeric fields can be considered 'equal' for table + alteration purposes. Fields are equal if they are of the same type + and retain the same pack length. +*/ + +bool Field_num::is_equal(const Column_definition &new_field) const +{ + if (((new_field.flags & UNSIGNED_FLAG) != (flags & UNSIGNED_FLAG)) || + ((new_field.flags & AUTO_INCREMENT_FLAG) > (flags & AUTO_INCREMENT_FLAG))) + return false; + + const Type_handler *th= type_handler(), *new_th = new_field.type_handler(); + + if (th == new_th && new_field.pack_length == pack_length()) + return true; + /* FIXME: Test and consider returning true for the following: + TINYINT UNSIGNED to BIT(8) + SMALLINT UNSIGNED to BIT(16) + MEDIUMINT UNSIGNED to BIT(24) + INT UNSIGNED to BIT(32) + BIGINT UNSIGNED to BIT(64) + + BIT(1..7) to TINYINT, or BIT(1..8) to TINYINT UNSIGNED + BIT(9..15) to SMALLINT, or BIT(9..16) to SMALLINT UNSIGNED + BIT(17..23) to MEDIUMINT, or BIT(17..24) to MEDIUMINT UNSIGNED + BIT(25..31) to INT, or BIT(25..32) to INT UNSIGNED + BIT(57..63) to BIGINT, or BIT(57..64) to BIGINT UNSIGNED + + Note: InnoDB stores integers in big-endian format, and BIT appears + to use big-endian format. For storage engines that use little-endian + format for integers, we can only return true for the TINYINT + conversion. */ + + return false; +} + + +Data_type_compatibility +Field_enum::can_optimize_range_or_keypart_ref(const Item_bool_func *cond, + const Item *item) const +{ + switch (item->cmp_type()) + { + case TIME_RESULT: + return Data_type_compatibility::INCOMPATIBLE_DATA_TYPE; + case INT_RESULT: + case DECIMAL_RESULT: + case REAL_RESULT: + return Data_type_compatibility::OK; + case STRING_RESULT: + return charset() == cond->compare_collation() ? + Data_type_compatibility::OK : + Data_type_compatibility::INCOMPATIBLE_COLLATION; + case ROW_RESULT: + DBUG_ASSERT(0); + break; + } + return Data_type_compatibility::INCOMPATIBLE_DATA_TYPE; +} + + +/* + Bit field. + + We store the first 0 - 6 uneven bits among the null bits + at the start of the record. The rest bytes are stored in + the record itself. + + For example: + + CREATE TABLE t1 (a int, b bit(17), c bit(21) not null, d bit(8)); + We would store data as follows in the record: + + Byte Bit + 1 7 - reserve for delete + 6 - null bit for 'a' + 5 - null bit for 'b' + 4 - first (high) bit of 'b' + 3 - first (high) bit of 'c' + 2 - second bit of 'c' + 1 - third bit of 'c' + 0 - forth bit of 'c' + 2 7 - firth bit of 'c' + 6 - null bit for 'd' + 3 - 6 four bytes for 'a' + 7 - 8 two bytes for 'b' + 9 - 10 two bytes for 'c' + 11 one byte for 'd' +*/ + +Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg) + : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg), + bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7), + bytes_in_rec(len_arg / 8) +{ + DBUG_ENTER("Field_bit::Field_bit"); + DBUG_PRINT("enter", ("ptr_arg: %p, null_ptr_arg: %p, len_arg: %u, bit_len: %u, bytes_in_rec: %u", + ptr_arg, null_ptr_arg, len_arg, bit_len, bytes_in_rec)); + flags|= UNSIGNED_FLAG; + /* + Ensure that Field::eq() can distinguish between two different bit fields. + (two bit fields that are not null, may have same ptr and null_ptr) + */ + if (!null_ptr_arg) + null_bit= bit_ofs_arg; + DBUG_VOID_RETURN; +} + + +const DTCollation & Field_bit::dtcollation() const +{ + static DTCollation tmp(&my_charset_bin, + DERIVATION_IMPLICIT, MY_REPERTOIRE_UNICODE30); + return tmp; +} + + +/* + This method always calculates hash over 8 bytes. + This is different from how the HEAP engine calculate hash: + HEAP takes into account the actual octet size, so say for BIT(18) + it calculates hash over three bytes only: + - the incomplete byte with bits 16..17 + - the two full bytes with bits 0..15 + See hp_rec_hashnr(), hp_hashnr() for details. + + The HEAP way is more efficient, especially for short lengths. + Let's consider fixing Field_bit eventually to do it in the HEAP way, + with proper measures to upgrade partitioned tables easy. +*/ +void Field_bit::hash_not_null(Hasher *hasher) +{ + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(!is_null()); + longlong value= Field_bit::val_int(); + uchar tmp[8]; + mi_int8store(tmp,value); + hasher->add(&my_charset_bin, tmp, 8); +} + + +size_t +Field_bit::do_last_null_byte() const +{ + /* + Code elsewhere is assuming that bytes are 8 bits, so I'm using + that value instead of the correct one: CHAR_BIT. + + REFACTOR SUGGESTION (Matz): Change to use the correct number of + bits. On systems with CHAR_BIT > 8 (not very common), the storage + will lose the extra bits. + */ + DBUG_PRINT("test", ("bit_ofs: %d, bit_len: %d bit_ptr: %p", + bit_ofs, bit_len, bit_ptr)); + uchar *result; + if (bit_len == 0) + result= null_ptr; + else if (bit_ofs + bit_len > 8) + result= bit_ptr + 1; + else + result= bit_ptr; + + if (result) + return (size_t) (result - table->record[0]) + 1; + return LAST_NULL_BYTE_UNDEF; +} + + +Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit) +{ + Field_bit *res; + if ((res= (Field_bit*) Field::new_key_field(root, new_table, new_ptr, length, + new_null_ptr, new_null_bit))) + { + /* Move bits normally stored in null_pointer to new_ptr */ + res->bit_ptr= new_ptr; + res->bit_ofs= 0; + if (bit_len) + res->ptr++; // Store rest of data here + } + return res; +} + + +bool Field_bit::is_equal(const Column_definition &new_field) const +{ + return new_field.type_handler() == type_handler() && + new_field.length == max_display_length(); +} + + +int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int delta; + + for (; length && !*from; from++, length--) // skip left 0's + ; + delta= (int)(bytes_in_rec - length); + + if (delta < -1 || + (delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) || + (!bit_len && delta < 0)) + { + set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len); + memset(ptr, 0xff, bytes_in_rec); + if (get_thd()->really_abort_on_warning()) + set_warning(ER_DATA_TOO_LONG, 1); + else + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + return 1; + } + /* delta is >= -1 here */ + if (delta > 0) + { + if (bit_len) + clr_rec_bits(bit_ptr, bit_ofs, bit_len); + bzero(ptr, delta); + memcpy(ptr + delta, from, length); + } + else if (delta == 0) + { + if (bit_len) + clr_rec_bits(bit_ptr, bit_ofs, bit_len); + memcpy(ptr, from, length); + } + else + { + if (bit_len) + { + set_rec_bits((uchar) *from, bit_ptr, bit_ofs, bit_len); + from++; + } + memcpy(ptr, from, bytes_in_rec); + } + return 0; +} + + +int Field_bit::store(double nr) +{ + return Field_bit::store((longlong) nr, FALSE); +} + + +int Field_bit::store(longlong nr, bool unsigned_val) +{ + char buf[8]; + + mi_int8store(buf, nr); + return store(buf, 8, NULL); +} + + +int Field_bit::store_decimal(const my_decimal *val) +{ + int err= 0; + longlong i= convert_decimal2longlong(val, 1, &err); + return MY_TEST(err | store(i, TRUE)); +} + + +double Field_bit::val_real(void) +{ + return (double) Field_bit::val_int(); +} + + +longlong Field_bit::val_int(void) +{ + DBUG_ASSERT(marked_for_read()); + ulonglong bits= 0; + if (bit_len) + { + bits= get_rec_bits(bit_ptr, bit_ofs, bit_len); + bits<<= (bytes_in_rec * 8); + } + + switch (bytes_in_rec) { + case 0: return bits; + case 1: return bits | (ulonglong) ptr[0]; + case 2: return bits | mi_uint2korr(ptr); + case 3: return bits | mi_uint3korr(ptr); + case 4: return bits | mi_uint4korr(ptr); + case 5: return bits | mi_uint5korr(ptr); + case 6: return bits | mi_uint6korr(ptr); + case 7: return bits | mi_uint7korr(ptr); + default: return mi_uint8korr(ptr + bytes_in_rec - sizeof(longlong)); + } +} + + +String *Field_bit::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + DBUG_ASSERT(marked_for_read()); + char buff[sizeof(longlong)]; + uint length= MY_MIN(pack_length(), sizeof(longlong)); + ulonglong bits= val_int(); + mi_int8store(buff,bits); + + val_buffer->alloc(length); + memcpy((char *) val_buffer->ptr(), buff+8-length, length); + val_buffer->length(length); + val_buffer->set_charset(&my_charset_bin); + return val_buffer; +} + + +my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value) +{ + DBUG_ASSERT(marked_for_read()); + int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 1, deciaml_value); + return deciaml_value; +} + + +/* + Compare two bit fields using pointers within the record. + SYNOPSIS + cmp_max() + a Pointer to field->ptr in first record + b Pointer to field->ptr in second record + max_len Maximum length used in index + DESCRIPTION + This method is used from key_rec_cmp used by merge sorts used + by partitioned index read and later other similar places. + The a and b pointer must be pointers to the field in a record + (not the table->record[0] necessarily) +*/ +int Field_bit::cmp_prefix(const uchar *a, const uchar *b, + size_t prefix_char_len) const +{ + my_ptrdiff_t a_diff= a - ptr; + my_ptrdiff_t b_diff= b - ptr; + if (bit_len) + { + int flag; + uchar bits_a= get_rec_bits(bit_ptr+a_diff, bit_ofs, bit_len); + uchar bits_b= get_rec_bits(bit_ptr+b_diff, bit_ofs, bit_len); + if ((flag= (int) (bits_a - bits_b))) + return flag; + } + if (!bytes_in_rec) + return 0; + return memcmp(a, b, bytes_in_rec); +} + + +int Field_bit::key_cmp(const uchar *str, uint) const +{ + if (bit_len) + { + int flag; + uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len); + if ((flag= (int) (bits - *str))) + return flag; + str++; + } + return memcmp(ptr, str, bytes_in_rec); +} + + +int Field_bit::cmp_offset(my_ptrdiff_t row_offset) +{ + if (bit_len) + { + int flag; + uchar bits_a= get_rec_bits(bit_ptr, bit_ofs, bit_len); + uchar bits_b= get_rec_bits(bit_ptr + row_offset, bit_ofs, bit_len); + if ((flag= (int) (bits_a - bits_b))) + return flag; + } + return memcmp(ptr, ptr + row_offset, bytes_in_rec); +} + + +uint Field_bit::get_key_image(uchar *buff, uint length, const uchar *ptr_arg, imagetype type_arg) const +{ + if (bit_len) + { + const uchar *bit_ptr_for_arg= ptr_arg + (bit_ptr - ptr); + uchar bits= get_rec_bits(bit_ptr_for_arg, bit_ofs, bit_len); + *buff++= bits; + length--; + } + uint tmp_data_length = MY_MIN(length, bytes_in_rec); + memcpy(buff, ptr, tmp_data_length); + return tmp_data_length + 1; +} + + +/** + Returns the number of bytes field uses in row-based replication + row packed size. + + This method is used in row-based replication to determine the number + of bytes that the field consumes in the row record format. This is + used to skip fields in the master that do not exist on the slave. + + @param field_metadata Encoded size in field metadata + + @returns The size of the field based on the field metadata. +*/ +uint Field_bit::pack_length_from_metadata(uint field_metadata) const +{ + uint const from_len= (field_metadata >> 8U) & 0x00ff; + uint const from_bit_len= field_metadata & 0x00ff; + uint const source_size= from_len + ((from_bit_len > 0) ? 1 : 0); + return (source_size); +} + + +bool +Field_bit::compatible_field_size(uint field_metadata, + const Relay_log_info * __attribute__((unused)), + uint16 mflags, + int *order_var) const +{ + DBUG_ENTER("Field_bit::compatible_field_size"); + DBUG_ASSERT((field_metadata >> 16) == 0); + uint from_bit_len= + 8 * (field_metadata >> 8) + (field_metadata & 0xff); + uint to_bit_len= max_display_length(); + DBUG_PRINT("debug", ("from_bit_len: %u, to_bit_len: %u", + from_bit_len, to_bit_len)); + /* + If the bit length exact flag is clear, we are dealing with an old + master, so we allow some less strict behaviour if replicating by + moving both bit lengths to an even multiple of 8. + + We do this by computing the number of bytes to store the field + instead, and then compare the result. + */ + if (!(mflags & Table_map_log_event::TM_BIT_LEN_EXACT_F)) { + from_bit_len= (from_bit_len + 7) / 8; + to_bit_len= (to_bit_len + 7) / 8; + } + + *order_var= compare(from_bit_len, to_bit_len); + DBUG_RETURN(TRUE); +} + + + +void Field_bit::sql_type(String &res) const +{ + CHARSET_INFO *cs= res.charset(); + size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + "bit(%d)", (int) field_length); + res.length(length); +} + + +uchar * +Field_bit::pack(uchar *to, const uchar *from, uint max_length) +{ + DBUG_ASSERT(max_length > 0); + uint length; + if (bit_len > 0) + { + /* + We have the following: + + ptr Points into a field in record R1 + from Points to a field in a record R2 + bit_ptr Points to the byte (in the null bytes) that holds the + odd bits of R1 + from_bitp Points to the byte that holds the odd bits of R2 + + We have the following: + + ptr - bit_ptr = from - from_bitp + + We want to isolate 'from_bitp', so this gives: + + ptr - bit_ptr - from = - from_bitp + - ptr + bit_ptr + from = from_bitp + bit_ptr + from - ptr = from_bitp + */ + uchar bits= get_rec_bits(bit_ptr + (from - ptr), bit_ofs, bit_len); + *to++= bits; + } + length= MY_MIN(bytes_in_rec, max_length - (bit_len > 0)); + memcpy(to, from, length); + return to + length; +} + + +/** + Unpack a bit field from row data. + + This method is used to unpack a bit field from a master whose size + of the field is less than that of the slave. + + @param to Destination of the data + @param from Source of the data + @param param_data Bit length (upper) and length (lower) values + + @return New pointer into memory based on from + length of the data +*/ +const uchar * +Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end, + uint param_data) +{ + DBUG_ENTER("Field_bit::unpack"); + DBUG_PRINT("enter", ("to: %p, from: %p, param_data: 0x%x", + to, from, param_data)); + DBUG_PRINT("debug", ("bit_ptr: %p, bit_len: %u, bit_ofs: %u", + bit_ptr, bit_len, bit_ofs)); + uint const from_len= (param_data >> 8U) & 0x00ff; + uint const from_bit_len= param_data & 0x00ff; + DBUG_PRINT("debug", ("from_len: %u, from_bit_len: %u", + from_len, from_bit_len)); + /* + If the parameter data is zero (i.e., undefined), or if the master + and slave have the same sizes, then use the old unpack() method. + */ + if (param_data == 0 || + ((from_bit_len == bit_len) && (from_len == bytes_in_rec))) + { + if (from + bytes_in_rec + MY_TEST(bit_len) > from_end) + return 0; // Error in data + + if (bit_len > 0) + { + /* + set_rec_bits is a macro, don't put the post-increment in the + argument since that might cause strange side-effects. + + For the choice of the second argument, see the explanation for + Field_bit::pack(). + */ + set_rec_bits(*from, bit_ptr + (to - ptr), bit_ofs, bit_len); + from++; + } + memcpy(to, from, bytes_in_rec); + DBUG_RETURN(from + bytes_in_rec); + } + + /* + We are converting a smaller bit field to a larger one here. + To do that, we first need to construct a raw value for the original + bit value stored in the from buffer. Then that needs to be converted + to the larger field then sent to store() for writing to the field. + Lastly the odd bits need to be masked out if the bytes_in_rec > 0. + Otherwise stray bits can cause spurious values. + */ + + uint len= from_len + ((from_bit_len > 0) ? 1 : 0); + uint new_len= (field_length + 7) / 8; + + if (from + len > from_end || new_len < len) + return 0; // Error in data + + char *value= (char *)my_alloca(new_len); + bzero(value, new_len); + + memcpy(value + (new_len - len), from, len); + /* + Mask out the unused bits in the partial byte. + TODO: Add code to the master to always mask these bits and remove + the following. + */ + if ((from_bit_len > 0) && (from_len > 0)) + value[new_len - len]= value[new_len - len] & ((1U << from_bit_len) - 1); + bitmap_set_bit(table->write_set,field_index); + store(value, new_len, system_charset_info); + my_afree(value); + DBUG_RETURN(from + len); +} + + +int Field_bit::set_default() +{ + if (bit_len > 0) + { + my_ptrdiff_t const col_offset= table->s->default_values - table->record[0]; + uchar bits= get_rec_bits(bit_ptr + col_offset, bit_ofs, bit_len); + set_rec_bits(bits, bit_ptr, bit_ofs, bit_len); + } + return Field::set_default(); +} + +/* + Bit field support for non-MyISAM tables. +*/ + +Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg) + :Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0, + unireg_check_arg, field_name_arg) +{ + flags|= UNSIGNED_FLAG; + bit_len= 0; + bytes_in_rec= (len_arg + 7) / 8; +} + + +int Field_bit_as_char::store(const char *from, size_t length, CHARSET_INFO *cs) +{ + DBUG_ASSERT(marked_for_write_or_computed()); + int delta; + uchar bits= (uchar) (field_length & 7); + + for (; length && !*from; from++, length--) // skip left 0's + ; + delta= (int)(bytes_in_rec - length); + + if (delta < 0 || + (delta == 0 && bits && (uint) (uchar) *from >= (uint) (1 << bits))) + { + memset(ptr, 0xff, bytes_in_rec); + if (bits) + *ptr&= ((1 << bits) - 1); /* set first uchar */ + if (get_thd()->really_abort_on_warning()) + set_warning(ER_DATA_TOO_LONG, 1); + else + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + return 1; + } + bzero(ptr, delta); + memcpy(ptr + delta, from, length); + return 0; +} + + +void Field_bit_as_char::sql_type(String &res) const +{ + CHARSET_INFO *cs= res.charset(); + size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + "bit(%d)", (int) field_length); + res.length(length); +} + + +/***************************************************************************** + Handling of field and Create_field +*****************************************************************************/ + +bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root, + bool reuse_interval_list_values) +{ + DBUG_ENTER("Column_definition::create_interval_from_interval_list"); + DBUG_ASSERT(!interval); + TYPELIB *tmpint; + if (!(interval= tmpint= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB)))) + DBUG_RETURN(true); // EOM + + List_iterator<String> it(interval_list); + StringBuffer<64> conv; + char comma_buf[5]; /* 5 bytes for 'filename' charset */ + DBUG_ASSERT(sizeof(comma_buf) >= charset->mbmaxlen); + int comma_length= charset->wc_mb(',', + (uchar*) comma_buf, + (uchar*) comma_buf + sizeof(comma_buf)); + DBUG_ASSERT(comma_length >= 0 && comma_length <= (int) sizeof(comma_buf)); + + if (!multi_alloc_root(mem_root, + &tmpint->type_names, + sizeof(char*) * (interval_list.elements + 1), + &tmpint->type_lengths, + sizeof(uint) * (interval_list.elements + 1), + NullS)) + goto err; // EOM + + tmpint->name= ""; + tmpint->count= interval_list.elements; + + for (uint i= 0; i < interval_list.elements; i++) + { + uint32 dummy; + String *tmp= it++; + LEX_CSTRING value; + if (String::needs_conversion(tmp->length(), tmp->charset(), + charset, &dummy)) + { + uint cnv_errs; + conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), charset, &cnv_errs); + value.str= strmake_root(mem_root, conv.ptr(), conv.length()); + value.length= conv.length(); + } + else + { + value.str= reuse_interval_list_values ? tmp->ptr() : + strmake_root(mem_root, + tmp->ptr(), + tmp->length()); + value.length= tmp->length(); + } + if (!value.str) + goto err; // EOM + + // Strip trailing spaces. + value.length= charset->lengthsp(value.str, value.length); + ((char*) value.str)[value.length]= '\0'; + + if (real_field_type() == MYSQL_TYPE_SET) + { + if (charset->instr(value.str, value.length, + comma_buf, comma_length, NULL, 0)) + { + ErrConvString err(tmp); + my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr()); + goto err; + } + } + tmpint->type_names[i]= value.str; + tmpint->type_lengths[i]= (uint)value.length; + } + tmpint->type_names[interval_list.elements]= 0; // End marker + tmpint->type_lengths[interval_list.elements]= 0; + interval_list.empty(); // Don't need interval_list anymore + DBUG_RETURN(false); +err: + interval= NULL; // Avoid having both non-empty interval_list and interval + DBUG_RETURN(true); +} + + +bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root, + bool reuse_interval_list_values) +{ + DBUG_ENTER("Column_definition::prepare_interval_field"); + DBUG_ASSERT(real_field_type() == MYSQL_TYPE_ENUM || + real_field_type() == MYSQL_TYPE_SET); + /* + Interval values are either in "interval" or in "interval_list", + but not in both at the same time, and are not empty at the same time. + - Values are in "interval_list" when we're coming from the parser + in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}. + - Values are in "interval" when we're in ALTER TABLE. + + In a corner case with an empty set like SET(''): + - after the parser we have interval_list.elements==1 + - in ALTER TABLE we have a non-NULL interval with interval->count==1, + with interval->type_names[0]=="" and interval->type_lengths[0]==0. + So the assert is still valid for this corner case. + + ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible, + as the parser requires at least one element, so for a ENUM or SET field it + should never happen that both internal_list.elements and interval are 0. + */ + DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0)); + + /* + Create typelib from interval_list, and if necessary + convert strings from client character set to the + column character set. + */ + if (interval_list.elements && + create_interval_from_interval_list(mem_root, + reuse_interval_list_values)) + DBUG_RETURN(true); + + if (!reuse_interval_list_values) + { + /* + We're initializing from an existing table or view Field_enum + (e.g. for a %TYPE variable) rather than from the parser. + The constructor Column_definition(THD*,Field*,Field*) has already + copied the TYPELIB pointer from the original Field_enum. + Now we need to make a permanent copy of that TYPELIB, + as the original field can be freed before the end of the life + cycle of "this". + */ + DBUG_ASSERT(interval); + if (!(interval= copy_typelib(mem_root, interval))) + DBUG_RETURN(true); + } + prepare_interval_field_calc_length(); + DBUG_RETURN(false); +} + + +bool Column_definition::set_attributes(THD *thd, + const Lex_field_type_st &def, + column_definition_type_t type) +{ + DBUG_ASSERT(type_handler() == &type_handler_null); + DBUG_ASSERT(length == 0); + DBUG_ASSERT(decimals == 0); + + set_handler(def.type_handler()); + return type_handler()->Column_definition_set_attributes(thd, this, + def, type); +} + + +void +Column_definition_attributes::set_length_and_dec(const Lex_length_and_dec_st + &type) +{ + if (type.has_explicit_length()) + length= type.length_overflowed() ? (ulonglong) UINT_MAX32 + 1 : + (ulonglong) type.length(); + + if (type.has_explicit_dec()) + decimals= type.dec(); +} + + +void Column_definition::create_length_to_internal_length_bit() +{ + if (f_bit_as_char(pack_flag)) + { + pack_length= ((length + 7) & ~7) / 8; + } + else + { + pack_length= (uint) length / 8; + } +} + + +void Column_definition::create_length_to_internal_length_newdecimal() +{ + DBUG_ASSERT(length < UINT_MAX32); + decimal_digit_t prec= get_decimal_precision((uint)length, decimals, + flags & UNSIGNED_FLAG); + pack_length= my_decimal_get_binary_size(prec, decimals); +} + + +bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name, + enum_vcol_info_type type, Alter_info *alter_info) + +{ + bool ret; + Item::vcol_func_processor_result res; + res.alter_info= alter_info; + + if (!vcol->name.length) + vcol->name= *name; + + /* + Walk through the Item tree checking if all items are valid + to be part of the virtual column + */ + ret= vcol->expr->walk(&Item::check_vcol_func_processor, 0, &res); + vcol->flags= res.errors; + + uint filter= VCOL_IMPOSSIBLE; + if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT) + filter|= VCOL_NOT_STRICTLY_DETERMINISTIC; + if (type != VCOL_DEFAULT) + filter|= VCOL_NEXTVAL; + + if (unlikely(ret || (res.errors & filter))) + { + my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name, + vcol_type_name(type), name->str); + return TRUE; + } + /* + Safe to call before fix_fields as long as vcol's don't include sub + queries (which is now checked in check_vcol_func_processor) + */ + if (vcol->expr->check_cols(1)) + return TRUE; + return FALSE; +} + + +bool Column_definition::check_length(uint mysql_errno, uint limit) const +{ + if (length <= limit) + return false; + my_error(mysql_errno, MYF(0), field_name.str, static_cast<ulong>(limit)); + return true; +} + + +bool Column_definition::fix_attributes_int(uint default_length) +{ + if (length) + return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH); + length= default_length; + return false; +} + + +bool Column_definition::fix_attributes_real(uint default_length) +{ + /* change FLOAT(precision) to FLOAT or DOUBLE */ + if (!length && !decimals) + { + length= default_length; + decimals= NOT_FIXED_DEC; + } + if (length < decimals && decimals != NOT_FIXED_DEC) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str); + return true; + } + if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), + field_name.str, static_cast<uint>(FLOATING_POINT_DECIMALS-1)); + return true; + } + return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH); +} + + +bool Column_definition::fix_attributes_decimal() +{ + if (decimals >= NOT_FIXED_DEC) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), + field_name.str, static_cast<uint>(NOT_FIXED_DEC - 1)); + return true; + } + my_decimal_trim(&length, &decimals); + if (length > DECIMAL_MAX_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), field_name.str, + DECIMAL_MAX_PRECISION); + return true; + } + if (length < decimals) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str); + return true; + } + length= my_decimal_precision_to_length((uint) length, decimals, + flags & UNSIGNED_FLAG); + pack_length= my_decimal_get_binary_size((uint) length, decimals); + return false; +} + + +bool Column_definition::fix_attributes_bit() +{ + if (!length) + length= 1; + pack_length= ((uint) length + 7) / 8; + return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_BIT_FIELD_LENGTH); +} + + +bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length) +{ + if (length > MAX_DATETIME_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), field_name.str, + MAX_DATETIME_PRECISION); + return true; + } + decimals= (uint) length; + length+= int_part_length + (length ? 1 : 0); + return false; +} + + +bool Column_definition::validate_check_constraint(THD *thd) +{ + return check_constraint && + check_expression(check_constraint, &field_name, VCOL_CHECK_FIELD); +} + + +bool Column_definition::check(THD *thd) +{ + DBUG_ENTER("Column_definition::check"); + + /* Initialize data for a computed field */ + if (vcol_info) + { + DBUG_ASSERT(vcol_info->expr); + vcol_info->set_handler(type_handler()); + if (check_expression(vcol_info, &field_name, vcol_info->stored_in_db + ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL)) + DBUG_RETURN(TRUE); + } + + if (type_handler()->Column_definition_validate_check_constraint(thd, this)) + DBUG_RETURN(TRUE); + + if (default_value) + { + Item *def_expr= default_value->expr; + if (check_expression(default_value, &field_name, VCOL_DEFAULT)) + DBUG_RETURN(TRUE); + + /* Constant's are stored in the 'empty_record', except for blobs */ + if (def_expr->basic_const_item()) + { + if (def_expr->type() == Item::NULL_ITEM) + { + default_value= 0; + if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); + DBUG_RETURN(1); + } + } + } + } + + if (default_value && (flags & AUTO_INCREMENT_FLAG)) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); + DBUG_RETURN(1); + } + + if (default_value && !default_value->expr->basic_const_item() && + mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME && + default_value->expr->type() == Item::FUNC_ITEM) + { + /* + Special case: NOW() for TIMESTAMP and DATETIME fields are handled + as in MariaDB 10.1 by marking them in unireg_check. + */ + Item_func *fn= static_cast<Item_func*>(default_value->expr); + if (fn->functype() == Item_func::NOW_FUNC && + (fn->decimals == 0 || fn->decimals >= length)) + { + default_value= 0; + unireg_check= Field::TIMESTAMP_DN_FIELD; + } + } + + if (on_update) + { + if (mysql_timestamp_type() != MYSQL_TIMESTAMP_DATETIME || + on_update->decimals < length) + { + my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name.str); + DBUG_RETURN(TRUE); + } + unireg_check= unireg_check == Field::NONE ? Field::TIMESTAMP_UN_FIELD + : Field::TIMESTAMP_DNUN_FIELD; + } + else if (flags & AUTO_INCREMENT_FLAG) + unireg_check= Field::NEXT_NUMBER; + + if (type_handler()->Column_definition_fix_attributes(this)) + DBUG_RETURN(true); + + /* Remember the value of length */ + char_length= (uint)length; + + /* + Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and + it is NOT NULL, not an AUTO_INCREMENT field. + We need to do this check here and in mysql_create_prepare_table() as + sp_head::fill_field_definition() calls this function. + */ + if (!default_value && unireg_check == Field::NONE && (flags & NOT_NULL_FLAG)) + { + /* + TIMESTAMP columns get implicit DEFAULT value when + explicit_defaults_for_timestamp is not set. + */ + if (((thd->variables.option_bits & OPTION_EXPLICIT_DEF_TIMESTAMP) || + !is_timestamp_type()) && !vers_sys_field()) + { + flags|= NO_DEFAULT_VALUE_FLAG; + } + } + + + if ((flags & AUTO_INCREMENT_FLAG) && + !type_handler()->type_can_have_auto_increment_attribute()) + { + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str); + DBUG_RETURN(TRUE); + } + + DBUG_RETURN(FALSE); /* success */ +} + +enum_field_types get_blob_type_from_length(ulong length) +{ + enum_field_types type; + if (length < 256) + type= MYSQL_TYPE_TINY_BLOB; + else if (length < 65536) + type= MYSQL_TYPE_BLOB; + else if (length < 256L*256L*256L) + type= MYSQL_TYPE_MEDIUM_BLOB; + else + type= MYSQL_TYPE_LONG_BLOB; + return type; +} + + +uint pack_length_to_packflag(uint type) +{ + switch (type) { + case 1: return f_settype((uint) MYSQL_TYPE_TINY); + case 2: return f_settype((uint) MYSQL_TYPE_SHORT); + case 3: return f_settype((uint) MYSQL_TYPE_INT24); + case 4: return f_settype((uint) MYSQL_TYPE_LONG); + case 8: return f_settype((uint) MYSQL_TYPE_LONGLONG); + } + return 0; // This shouldn't happen +} + + +uint Column_definition_attributes::pack_flag_to_pack_length() const +{ + uint type= f_packtype(pack_flag); // 0..15 + DBUG_ASSERT(type < 16); + switch (type) { + case MYSQL_TYPE_TINY: return 1; + case MYSQL_TYPE_SHORT: return 2; + case MYSQL_TYPE_LONG: return 4; + case MYSQL_TYPE_LONGLONG: return 8; + case MYSQL_TYPE_INT24: return 3; + } + return 0; // This should not happen +} + + +Field *Column_definition_attributes::make_field(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const Record_addr *rec, + const Type_handler *handler, + const LEX_CSTRING *field_name, + uint32 flags) + const +{ + DBUG_ASSERT(length <= UINT_MAX32); + DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s", + handler->name().ptr(), (uint) length, interval, + FLAGSTR(pack_flag, FIELDFLAG_BINARY), + FLAGSTR(pack_flag, FIELDFLAG_INTERVAL), + FLAGSTR(pack_flag, FIELDFLAG_NUMBER), + FLAGSTR(pack_flag, FIELDFLAG_PACK), + FLAGSTR(pack_flag, FIELDFLAG_BLOB))); + + Record_addr addr(rec->ptr(), f_maybe_null(pack_flag) ? rec->null() : + Bit_addr()); + /* + Special code for the BIT-alike data types + who store data bits together with NULL-bits. + */ + Bit_addr bit(rec->null()); + if (f_maybe_null(pack_flag)) + bit.inc(); + return handler->make_table_field_from_def(share, mem_root, field_name, + addr, bit, this, flags); +} + + +bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const +{ + return item->is_of_type(Item::CONST_ITEM, TIME_RESULT); +} + + +Column_definition_attributes::Column_definition_attributes(const Field *field) + :length(field->character_octet_length() / field->charset()->mbmaxlen), + interval(NULL), + charset(field->charset()), // May be NULL ptr + srid(0), + pack_flag(0), + decimals(field->decimals()), + unireg_check(field->unireg_check) +{} + + +Column_definition_attributes:: + Column_definition_attributes(const Type_all_attributes &attr) + :length(attr.max_length), + interval(attr.get_typelib()), + charset(attr.collation.collation), + srid(0), + pack_flag(attr.unsigned_flag ? 0 : FIELDFLAG_DECIMAL), + decimals(attr.decimals), + unireg_check(Field::NONE) +{} + + +/** Create a field suitable for create of table. */ + +Column_definition::Column_definition(THD *thd, Field *old_field, + Field *orig_field) + :Column_definition_attributes(old_field) +{ + srid= 0; + on_update= NULL; + field_name= old_field->field_name; + flags= old_field->flags; + pack_length=old_field->pack_length(); + set_handler(old_field->type_handler()); + comment= old_field->comment; + vcol_info= old_field->vcol_info; + option_list= old_field->option_list; + explicitly_nullable= !(old_field->flags & NOT_NULL_FLAG); + compression_method_ptr= 0; + versioning= VERSIONING_NOT_SET; + invisible= old_field->invisible; + interval_list.empty(); // prepare_interval_field() needs this + char_length= (uint) length; + + if (orig_field) + { + default_value= orig_field->default_value; + check_constraint= orig_field->check_constraint; + if (orig_field->unireg_check == Field::TMYSQL_COMPRESSED) + { + unireg_check= Field::TMYSQL_COMPRESSED; + compression_method_ptr= zlib_compression_method; + } + } + else + { + default_value= 0; + check_constraint= 0; + } + + type_handler()->Column_definition_reuse_fix_attributes(thd, this, old_field); + + /* + Copy the default (constant/function) from the column object orig_field, if + supplied. We do this if all these conditions are met: + + - The column allows a default. + + - The column type is not a BLOB type (as BLOB's doesn't have constant + defaults) + + - The original column (old_field) was properly initialized with a record + buffer pointer. + + - The column didn't have a default expression + */ + if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && + old_field->ptr != NULL && orig_field != NULL) + { + if (orig_field->unireg_check != Field::NEXT_NUMBER) + unireg_check= orig_field->unireg_check; + + /* Get the value from default_values */ + const uchar *dv= orig_field->table->s->default_values; + if (!default_value && !orig_field->is_null_in_record(dv)) + { + StringBuffer<MAX_FIELD_WIDTH> tmp(charset); + String *res= orig_field->val_str(&tmp, orig_field->ptr_in_record(dv)); + char *pos= (char*) thd->strmake(res->ptr(), res->length()); + default_value= new (thd->mem_root) Virtual_column_info(); + default_value->expr= + new (thd->mem_root) Item_string(thd, pos, res->length(), charset); + default_value->utf8= 0; + } + } +} + + +/** + The common part for data type redefinition: + CREATE TABLE t1 (a INT) AS SELECT a FROM t2; + See Type_handler::Column_definition_redefine_stage1() + for data type specific code. + + @param this - The field definition corresponding to the expression + in the "AS SELECT.." part. + + @param dup_field - The field definition from the "CREATE TABLE (...)" part. + It has already underwent prepare_stage1(), so + must be fully initialized: + -- dup_field->charset is set and BINARY + sorting style is applied, see find_bin_collation(). + + @param file - The table handler +*/ +void +Column_definition::redefine_stage1_common(const Column_definition *dup_field, + const handler *file) +{ + set_handler(dup_field->type_handler()); + default_value= dup_field->default_value; + DBUG_ASSERT(dup_field->charset); // Set by prepare_stage1() + charset= dup_field->charset; + length= dup_field->char_length; + pack_length= dup_field->pack_length; + decimals= dup_field->decimals; + unireg_check= dup_field->unireg_check; + flags= dup_field->flags; + interval= dup_field->interval; + vcol_info= dup_field->vcol_info; + invisible= dup_field->invisible; + check_constraint= dup_field->check_constraint; + comment= dup_field->comment; + option_list= dup_field->option_list; + versioning= dup_field->versioning; +} + + + +/** + maximum possible character length for blob. + + This method is used in Item_field::set_field to calculate + max_length for Item. + + For example: + CREATE TABLE t2 SELECT CONCAT(tinyblob_utf8_column) FROM t1; + must create a "VARCHAR(255) CHARACTER SET utf8" column. + + @return + length +*/ + +uint32 Field_blob::char_length() const +{ + return Field_blob::character_octet_length(); +} + + +uint32 Field_blob::character_octet_length() const +{ + switch (packlength) + { + case 1: + return 255; + case 2: + return 65535; + case 3: + return 16777215; + case 4: + return (uint32) UINT_MAX32; + default: + DBUG_ASSERT(0); // we should never go here + return 0; + } +} + + +/** + Makes a clone of this object for ALTER/CREATE TABLE + + @param mem_root MEM_ROOT where to clone the field +*/ + +Create_field *Create_field::clone(MEM_ROOT *mem_root) const +{ + Create_field *res= new (mem_root) Create_field(*this); + return res; +} + +/** + Return true if default is an expression that must be saved explicitly + + This is: + - Not basic constants + - If field is a BLOB (Which doesn't support normal DEFAULT) +*/ + +bool Column_definition::has_default_expression() +{ + return (default_value && + (!default_value->expr->basic_const_item() || + (flags & BLOB_FLAG))); +} + + +bool Column_definition::set_compressed(const char *method) +{ + if (!method || !strcmp(method, zlib_compression_method->name)) + { + unireg_check= Field::TMYSQL_COMPRESSED; + compression_method_ptr= zlib_compression_method; + return false; + } + my_error(ER_UNKNOWN_COMPRESSION_METHOD, MYF(0), method); + return true; +} + + +bool Column_definition::set_compressed_deprecated(THD *thd, const char *method) +{ + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_DEPRECATED_SYNTAX, + ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX), + "<data type> <character set clause> ... COMPRESSED...", + "'<data type> COMPRESSED... <character set clause> ...'"); + return set_compressed(method); +} + + +bool +Column_definition::set_compressed_deprecated_column_attribute(THD *thd, + const char *pos, + const char *method) +{ + if (compression_method_ptr) + { + /* + Compression method has already been set, e.g.: + a VARCHAR(10) COMPRESSED DEFAULT 10 COMPRESSED + */ + thd->parse_error(ER_SYNTAX_ERROR, pos); + return true; + } + enum enum_field_types sql_type= real_field_type(); + /* We can't use f_is_blob here as pack_flag is not yet set */ + if (sql_type == MYSQL_TYPE_VARCHAR || sql_type == MYSQL_TYPE_TINY_BLOB || + sql_type == MYSQL_TYPE_BLOB || sql_type == MYSQL_TYPE_MEDIUM_BLOB || + sql_type == MYSQL_TYPE_LONG_BLOB) + return set_compressed_deprecated(thd, method); + else + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str); + return true; +} + + +bool Column_definition::check_vcol_for_key(THD *thd) const +{ + if (vcol_info && (vcol_info->flags & VCOL_NOT_STRICTLY_DETERMINISTIC)) + { + /* use check_expression() to report an error */ + check_expression(vcol_info, &field_name, VCOL_GENERATED_STORED); + DBUG_ASSERT(thd->is_error()); + return true; + } + return false; +} + + +Send_field::Send_field(THD *thd, Item *item) +{ + item->make_send_field(thd, this); + normalize(); +} + + +/** + maximum possible display length for blob. + + @return + length +*/ + +uint32 Field_blob::max_display_length() const +{ + switch (packlength) + { + case 1: + return 255 * mbmaxlen(); + case 2: + return 65535 * mbmaxlen(); + case 3: + return 16777215 * mbmaxlen(); + case 4: + return (uint32) UINT_MAX32; + default: + DBUG_ASSERT(0); // we should never go here + return 0; + } +} + + +/***************************************************************************** + Warning handling +*****************************************************************************/ + +/** +* Produce warning or note about data saved into field. + + @param level - level of message (Note/Warning/Error) + @param code - error code of message to be produced + @param cut_increment - whenever we should increase cut fields count + @current_row - current row number + + @note + This function won't produce warning or notes or increase cut fields counter + if count_cuted_fields == CHECK_FIELD_IGNORE or CHECK_FIELD_EXPRESSION + for the current thread. + + This allows us to avoid notes in optimisation, like + convert_constant_item(). + + @retval + 1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE + @retval + 0 otherwise +*/ + +bool +Field::set_warning(Sql_condition::enum_warning_level level, uint code, + int cut_increment, ulong current_row) const +{ + /* + If this field was created only for type conversion purposes it + will have table == NULL. + */ + THD *thd= get_thd(); + + /* + In INPLACE ALTER, server can't know which row has generated + the warning, so the value of current row is supplied by the engine. + */ + if (current_row) + thd->get_stmt_da()->reset_current_row_for_warning(current_row); + + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) + { + thd->cuted_fields+= cut_increment; + push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str, + current_row ? current_row + : thd->get_stmt_da()->current_row_for_warning()); + return 0; + } + return level >= Sql_condition::WARN_LEVEL_WARN; +} + + +/** + Produce warning or note about datetime string data saved into field. + + @param level level of message (Note/Warning/Error) + @param code error code of message to be produced + @param str string value which we tried to save + @param ts_type type of datetime value (datetime/date/time) + @param cuted_increment whenever we should increase cut fields count or not + + @note + This function will always produce some warning but won't increase cut + fields counter if count_cuted_fields ==FIELD_CHECK_IGNORE for current + thread. + + See also bug#2336 + +*/ + +void Field::set_datetime_warning(Sql_condition::enum_warning_level level, + uint code, const ErrConv *str, + const char *typestr, int cuted_increment) + const +{ + THD *thd= get_thd(); + if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN) + { + /* + field_name.str can be NULL when field is not in the select list: + SET SESSION SQL_MODE= 'STRICT_ALL_TABLES,NO_ZERO_DATE'; + CREATE OR REPLACE TABLE t2 SELECT 1 AS f FROM t1 GROUP BY FROM_DAYS(d); + Can't call push_warning_truncated_value_for_field() directly here, + as it expect a non-NULL name. + */ + thd->push_warning_wrong_or_truncated_value(level, false, typestr, + str->ptr(), + table->s->db.str, + table->s->table_name.str, + field_name.str); + } + else + set_warning(level, code, cuted_increment); +} + + +void Field::set_warning_truncated_wrong_value(const char *type_arg, + const char *value) +{ + THD *thd= get_thd(); + const char *db_name; + const char *table_name; + /* + table has in the past been 0 in case of wrong calls when processing + statistics tables. Let's protect against that. + */ + DBUG_ASSERT(table); + + db_name= (table && table->s->db.str) ? table->s->db.str : ""; + table_name= (table && table->s->table_name.str) ? + table->s->table_name.str : ""; + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, + ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), + type_arg, value, db_name, table_name, field_name.str, + static_cast<ulong>(thd->get_stmt_da()-> + current_row_for_warning())); +} + + +/* + Give warning for unusable key + + Note that the caller is responsible to call it only under ther + right note_verbosity level +*/ + +void Field::raise_note_cannot_use_key_part(THD *thd, + uint keynr, uint part, + const LEX_CSTRING &op, + Item *value, + Data_type_compatibility reason) + const +{ + StringBuffer<128> value_buffer; + const LEX_CSTRING keyname= table->s->key_info[keynr].name; + size_t value_length; + + value->print(&value_buffer, QT_EXPLAIN); + value_length= Well_formed_prefix(value_buffer.charset(), + value_buffer.ptr(), + MY_MIN(value_buffer.length(), 64)).length(); + /* + We must use c_ptr() here for the 'T' argument as it only works with + zero terminated strings. + */ + switch (reason){ + case Data_type_compatibility::INCOMPATIBLE_COLLATION: + { + const LEX_CSTRING colf(charset()->coll_name); + const LEX_CSTRING colv(value->collation.collation->coll_name); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, + "Cannot use key %`.*s part[%u] for lookup: " + "%`.*s.%`.*s.%`.*s of collation %`.*s " + "%.*s \"%.*T\" of collation %`.*s", + (int) keyname.length, keyname.str, + part, + (int) table->s->db.length, table->s->db.str, + (int) table->s->table_name.length, + table->s->table_name.str, + (int) field_name.length, field_name.str, + (int) colf.length, colf.str, + (int) op.length, op.str, + (int) value_length, value_buffer.c_ptr_safe(), + (int) colv.length, colv.str); + } + break; + case Data_type_compatibility::OK: + DBUG_ASSERT(0); + /* fall through */ + case Data_type_compatibility::INCOMPATIBLE_DATA_TYPE: + { + const LEX_CSTRING dtypef(type_handler()->name().lex_cstring()); + const LEX_CSTRING dtypev(value->type_handler()->name().lex_cstring()); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, + "Cannot use key %`.*s part[%u] for lookup: " + "%`.*s.%`.*s.%`.*s of type %`.*s " + "%.*s \"%.*T\" of type %`.*s", + (int) keyname.length, keyname.str, + part, + (int) table->s->db.length, table->s->db.str, + (int) table->s->table_name.length, + table->s->table_name.str, + (int) field_name.length, field_name.str, + (int) dtypef.length, dtypef.str, + (int) op.length, op.str, + (int) value_length, value_buffer.c_ptr_safe(), + (int) dtypev.length, dtypev.str); + } + break; + } +} + + +/* + Give warning for unusable key + + Note that the caller is responsible to call it only under ther + right note_verbosity level +*/ + +void Field::raise_note_key_become_unused(THD *thd, const String &expr) const +{ + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR, + "Cannot use key parts with %`.*s.%`.*s.%`.*s " + "in the rewritten condition: %`.*s", + (int) table->s->db.length, table->s->db.str, + (int) table->s->table_name.length, table->s->table_name.str, + (int) field_name.length, field_name.str, + (int) expr.length(), expr.ptr()); +} + + +/* + @brief + Return possible keys for a field + + @details + Return bit map of keys over this field which can be used by the range + optimizer. For a field of a generic table such keys are all keys that starts + from this field. For a field of a materialized derived table/view such keys + are all keys in which this field takes a part. This is less restrictive as + keys for a materialized derived table/view are generated on the fly from + present fields, thus the case when a field for the beginning of a key is + absent is impossible. + + @return map of possible keys +*/ + +key_map Field::get_possible_keys() +{ + DBUG_ASSERT(table->pos_in_table_list); + return (table->pos_in_table_list->is_materialized_derived() ? + part_of_key : key_start); +} + + +bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record) +{ + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->read_set); + bool rc; + if ((rc= validate_value_in_record(thd, record))) + { + // Get and report val_str() for the DEFAULT value + StringBuffer<MAX_FIELD_WIDTH> tmp; + val_str(&tmp, ptr_in_record(record)); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_DEFAULT_VALUE_FOR_FIELD, + ER_THD(thd, ER_INVALID_DEFAULT_VALUE_FOR_FIELD), + ErrConvString(&tmp).ptr(), field_name.str); + } + dbug_tmp_restore_column_map(&table->read_set, old_map); + return rc; +} + + +bool Field::save_in_field_default_value(bool view_error_processing) +{ + THD *thd= table->in_use; + + /* + TODO: MDEV-19597 Refactor TABLE::vers_update_fields() via stored virtual columns + This condition will go away as well as other conditions with vers_sys_field(). + */ + if (vers_sys_field()) + { + if (flags & VERS_ROW_START) + set_time(); + else + set_max(); + return false; + } + + if (unlikely(flags & NO_DEFAULT_VALUE_FLAG && + real_type() != MYSQL_TYPE_ENUM)) + { + if (reset()) + { + my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, + ER_THD(thd, ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); + return true; + } + + if (view_error_processing) + { + TABLE_LIST *view= table->pos_in_table_list->top_table(); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_NO_DEFAULT_FOR_VIEW_FIELD, + ER_THD(thd, ER_NO_DEFAULT_FOR_VIEW_FIELD), + view->view_db.str, + view->view_name.str); + } + else + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_NO_DEFAULT_FOR_FIELD, + ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD), + field_name.str); + } + return true; + } + set_default(); + return + !is_null() && + validate_value_in_record_with_warn(thd, table->record[0]) && + thd->is_error(); +} + + +bool Field::save_in_field_ignore_value(bool view_error_processing) +{ + enum_sql_command com= table->in_use->lex->sql_command; + // All insert-like commands + if (com == SQLCOM_INSERT || com == SQLCOM_REPLACE || + com == SQLCOM_INSERT_SELECT || com == SQLCOM_REPLACE_SELECT || + com == SQLCOM_LOAD) + return save_in_field_default_value(view_error_processing); + return 0; // ignore +} + + +void Field::register_field_in_read_map() +{ + if (vcol_info) + { + Item *vcol_item= vcol_info->expr; + vcol_item->walk(&Item::register_field_in_read_map, 1, 0); + } + bitmap_set_bit(table->read_set, field_index); +} + + +bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to) +{ + StringBuffer<MAX_FIELD_WIDTH> str; + bool rc= false; + THD *thd= get_thd(); + Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH); + + val_str(&str); + if (!(to->length= str.length())) + *to= empty_clex_str; + else if ((rc= !(to->str= strmake_root(mem_root, str.ptr(), str.length())))) + to->length= 0; + + return rc; +} + + +void Field::print_key_value(String *out, uint32 length) +{ + if (charset() == &my_charset_bin) + print_key_value_binary(out, ptr, length); + else + val_str(out); +} + + +void Field_string::print_key_value(String *out, uint32 length) +{ + if (charset() == &my_charset_bin) + { + size_t len= field_charset()->lengthsp((const char*) ptr, length); + print_key_value_binary(out, ptr, static_cast<uint32>(len)); + } + else + { + THD *thd= get_thd(); + sql_mode_t sql_mode_backup= thd->variables.sql_mode; + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + val_str(out,out); + thd->variables.sql_mode= sql_mode_backup; + } +} + + +void Field_varstring::print_key_value(String *out, uint32 length) +{ + if (charset() == &my_charset_bin) + print_key_value_binary(out, get_data(), get_length()); + else + val_str(out,out); +} + + +void Field_blob::print_key_value(String *out, uint32 length) +{ + if (charset() == &my_charset_bin) + { + uchar *blob; + memcpy(&blob, ptr+packlength, sizeof(uchar*)); + print_key_value_binary(out, blob, get_length()); + } + else + val_str(out, out); +} + + +/* + @brief Print value of the key part + + @param + out Output string + key value of the key + length Length of field in bytes, + excluding NULL flag and length bytes +*/ + + +void +Field::print_key_part_value(String *out, const uchar* key, uint32 length) +{ + StringBuffer<128> tmp(system_charset_info); + uint null_byte= 0; + if (real_maybe_null()) + { + /* + Byte 0 of key is the null-byte. If set, key is NULL. + Otherwise, print the key value starting immediately after the + null-byte + */ + if (*key) + { + out->append(NULL_clex_str); + return; + } + null_byte++; // Skip null byte + } + + set_key_image(key + null_byte, length); + print_key_value(&tmp, length); + if (charset() == &my_charset_bin) + out->append(tmp.ptr(), tmp.length(), tmp.charset()); + else + tmp.print(out, system_charset_info); +} + + +void Field::print_key_value_binary(String *out, const uchar* key, uint32 length) +{ + out->append_semi_hex((const char*)key, length, charset()); +} + + +Virtual_column_info* Virtual_column_info::clone(THD *thd) +{ + Virtual_column_info* dst= new (thd->mem_root) Virtual_column_info(*this); + if (!dst) + return NULL; + if (expr) + { + dst->expr= expr->build_clone(thd); + if (!dst->expr) + return NULL; + } + if (!thd->make_lex_string(&dst->name, name.str, name.length)) + return NULL; + return dst; +}; |