summaryrefslogtreecommitdiffstats
path: root/sql/field.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sql/field.cc11393
1 files changed, 11393 insertions, 0 deletions
diff --git a/sql/field.cc b/sql/field.cc
new file mode 100644
index 00000000..71ca27c5
--- /dev/null
+++ b/sql/field.cc
@@ -0,0 +1,11393 @@
+/*
+ Copyright (c) 2000, 2017, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2020, 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);
+}
+
+
+/*
+ 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 Field::pos_in_interval_val_real(Field *min, Field *max)
+{
+ double n, d;
+ n= val_real() - min->val_real();
+ if (n < 0)
+ return 0.0;
+ d= max->val_real() - min->val_real();
+ if (d <= 0)
+ return 1.0;
+ return MY_MIN(n/d, 1.0);
+}
+
+
+static
+inline ulonglong char_prefix_to_ulonglong(uchar *src)
+{
+ uint sz= sizeof(ulonglong);
+ for (uint i= 0; i < sz/2; i++)
+ {
+ uchar tmp= src[i];
+ src[i]= src[sz-1-i];
+ src[sz-1-i]= tmp;
+ }
+ return uint8korr(src);
+}
+
+/*
+ Compute res = a - b, without losing precision and taking care that these are
+ unsigned numbers.
+*/
+static inline double safe_substract(ulonglong a, ulonglong b)
+{
+ return (a > b)? double(a - b) : -double(b - a);
+}
+
+
+/**
+ @brief
+ Determine the relative position of the field value in a string interval
+
+ @details
+ The function returns a double number between 0.0 and 1.0 as the relative
+ position of the value of the this field in the string interval of [min,max].
+ If the value is not in the interval the the function returns 0.0 when
+ the value is less than min, and, 1.0 when the value is greater than max.
+
+ @note
+ To calculate the relative position of the string value v in the interval
+ [min, max] the function first converts the beginning of these three
+ strings v, min, max into the strings that are used for byte comparison.
+ For each string not more sizeof(ulonglong) first bytes are taken
+ from the result of conversion. Then these bytes are interpreted as the
+ big-endian representation of an ulonglong integer. The values of these
+ integer numbers obtained for the strings v, min, max are used to calculate
+ the position of v in [min,max] in the same way is it's done for numeric
+ fields (see Field::pos_in_interval_val_real).
+
+ @todo
+ Improve the procedure for the case when min and max have the same
+ beginning
+
+ @param min value of the left end of the interval
+ @param max value of the right end of the interval
+
+ @return
+ relative position of the field value in the string interval [min,max]
+*/
+
+double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
+{
+ uchar mp_prefix[sizeof(ulonglong)];
+ uchar minp_prefix[sizeof(ulonglong)];
+ uchar maxp_prefix[sizeof(ulonglong)];
+ ulonglong mp, minp, maxp;
+ charset()->strnxfrm(mp_prefix, sizeof(mp),
+ ptr + data_offset,
+ data_length());
+ charset()->strnxfrm(minp_prefix, sizeof(minp),
+ min->ptr + data_offset,
+ min->data_length());
+ charset()->strnxfrm(maxp_prefix, sizeof(maxp),
+ max->ptr + data_offset,
+ max->data_length());
+ mp= char_prefix_to_ulonglong(mp_prefix);
+ minp= char_prefix_to_ulonglong(minp_prefix);
+ maxp= char_prefix_to_ulonglong(maxp_prefix);
+ double n, d;
+ n= safe_substract(mp, minp);
+ if (n < 0)
+ return 0.0;
+ d= safe_substract(maxp, minp);
+ if (d <= 0)
+ return 1.0;
+ return MY_MIN(n/d, 1.0);
+}
+
+
+bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const
+{
+ DBUG_ASSERT(cmp_type() != STRING_RESULT); // For STRING_RESULT see Field_str
+ /*
+ We use result_type() rather than cmp_type() in the below condition,
+ because it covers a special case that string literals guarantee uniqueness
+ for temporal columns, so the query:
+ WHERE temporal_column='string'
+ cannot return multiple distinct temporal values.
+
+ TODO: perhaps we could allow INT/DECIMAL/DOUBLE types for temporal items.
+ */
+ return result_type() == item->result_type();
+}
+
+
+/**
+ Check whether a field item can be substituted for an equal item
+
+ @details
+ The function checks whether a substitution of a field item for
+ an equal item is valid.
+
+ @param arg *arg != NULL <-> the field is in the context
+ where substitution for an equal item is valid
+
+ @note
+ The following statement is not always true:
+ @n
+ x=y => F(x)=F(x/y).
+ @n
+ This means substitution of an item for an equal item not always
+ yields an equavalent condition. Here's an example:
+ @code
+ 'a'='a '
+ (LENGTH('a')=1) != (LENGTH('a ')=2)
+ @endcode
+ Such a substitution is surely valid if either the substituted
+ field is not of a STRING type or if it is an argument of
+ a comparison predicate.
+
+ @retval
+ TRUE substitution is valid
+ @retval
+ FALSE otherwise
+*/
+
+bool Field::can_be_substituted_to_equal_item(const Context &ctx,
+ const Item_equal *item_equal)
+{
+ DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT);
+ DBUG_ASSERT(cmp_type() != STRING_RESULT);
+ switch (ctx.subst_constraint()) {
+ case ANY_SUBST:
+ /*
+ Disable const propagation for items used in different comparison contexts.
+ This must be done because, for example, Item_hex_string->val_int() is not
+ the same as (Item_hex_string->val_str() in BINARY column)->val_int().
+ We cannot simply disable the replacement in a particular context (
+ e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since
+ Items don't know the context they are in and there are functions like
+ IF (<hex_string>, 'yes', 'no').
+ */
+ return ctx.compare_type_handler() == item_equal->compare_type_handler();
+ case IDENTITY_SUBST:
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ This handles all numeric and BIT data types.
+*/
+bool Field::can_optimize_keypart_ref(const Item_bool_func *cond,
+ const Item *item) const
+{
+ DBUG_ASSERT(cmp_type() != STRING_RESULT);
+ DBUG_ASSERT(cmp_type() != TIME_RESULT);
+ return item->cmp_type() != TIME_RESULT;
+}
+
+
+/*
+ This handles all numeric and BIT data types.
+*/
+bool Field::can_optimize_group_min_max(const Item_bool_func *cond,
+ const Item *const_item) const
+{
+ DBUG_ASSERT(cmp_type() != STRING_RESULT);
+ DBUG_ASSERT(cmp_type() != TIME_RESULT);
+ return const_item->cmp_type() != TIME_RESULT;
+}
+
+
+/*
+ This covers all numeric types, BIT
+*/
+bool Field::can_optimize_range(const Item_bool_func *cond,
+ const Item *item,
+ bool is_eq_func) const
+{
+ DBUG_ASSERT(cmp_type() != TIME_RESULT); // Handled in Field_temporal
+ DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants
+ return item->cmp_type() != TIME_RESULT;
+}
+
+
+int Field::store_hex_hybrid(const char *str, size_t length)
+{
+ DBUG_ASSERT(result_type() != STRING_RESULT);
+ ulonglong nr;
+
+ if (length > 8)
+ {
+ nr= flags & UNSIGNED_FLAG ? ULONGLONG_MAX : LONGLONG_MAX;
+ goto warn;
+ }
+ nr= (ulonglong) longlong_from_hex_hybrid(str, length);
+ if ((length == 8) && !(flags & UNSIGNED_FLAG) && (nr > LONGLONG_MAX))
+ {
+ nr= LONGLONG_MAX;
+ goto warn;
+ }
+ return store((longlong) nr, true); // Assume hex numbers are unsigned
+
+warn:
+ if (!store((longlong) nr, true))
+ set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ return 1;
+}
+
+
+/**
+ If a field does not have a corresponding data, it's behavior can vary:
+ - In case of the fixed file format
+ it's set to the default value for the data type,
+ such as 0 for numbers or '' for strings.
+ - In case of a non-fixed format
+ it's set to NULL for nullable fields, and
+ it's set to the default value for the data type for NOT NULL fields.
+ This seems to be by design.
+*/
+bool Field::load_data_set_no_data(THD *thd, bool fixed_format)
+{
+ reset(); // Do not use the DEFAULT value
+ if (fixed_format)
+ {
+ set_notnull();
+ /*
+ We're loading a fixed format file, e.g.:
+ LOAD DATA INFILE 't1.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
+ Suppose the file ended unexpectedly and no data was provided for an
+ auto-increment column in the current row.
+ Historically, if sql_mode=NO_AUTO_VALUE_ON_ZERO, then the column value
+ is set to 0 in such case (the next auto_increment value is not used).
+ This behaviour was introduced by the fix for "bug#12053" in mysql-4.1.
+ Note, loading a delimited file works differently:
+ "no data" is not converted to 0 on NO_AUTO_VALUE_ON_ZERO:
+ it's considered as equal to setting the column to NULL,
+ which is then replaced to the next auto_increment value.
+ This difference seems to be intentional.
+ */
+ if (this == table->next_number_field)
+ table->auto_increment_field_not_null= true;
+ }
+ set_has_explicit_value(); // Do not auto-update this field
+ return false;
+}
+
+
+bool Field::load_data_set_null(THD *thd)
+{
+ reset();
+ set_null();
+ if (!maybe_null())
+ {
+ if (this != table->next_number_field)
+ set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_NULL_TO_NOTNULL, 1);
+ }
+ set_has_explicit_value(); // Do not auto-update this field
+ return false;
+}
+
+
+void Field::load_data_set_value(const char *pos, uint length,
+ CHARSET_INFO *cs)
+{
+ /*
+ Mark field as not null, we should do this for each row because of
+ restore_record...
+ */
+ set_notnull();
+ if (this == table->next_number_field)
+ table->auto_increment_field_not_null= true;
+ store(pos, length, cs);
+ set_has_explicit_value(); // Do not auto-update this field
+}
+
+
+bool Field::sp_prepare_and_store_item(THD *thd, Item **value)
+{
+ DBUG_ENTER("Field::sp_prepare_and_store_item");
+ DBUG_ASSERT(value);
+
+ Item *expr_item;
+
+ if (!(expr_item= thd->sp_prepare_func_item(value, 1)))
+ goto error;
+
+ /*
+ expr_item is now fixed, it's safe to call cmp_type()
+ */
+ if (expr_item->cmp_type() == ROW_RESULT)
+ {
+ my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
+ goto error;
+ }
+
+ /* Save the value in the field. Convert the value if needed. */
+
+ expr_item->save_in_field(this, 0);
+
+ if (likely(!thd->is_error()))
+ DBUG_RETURN(false);
+
+error:
+ /*
+ In case of error during evaluation, leave the result field set to NULL.
+ Sic: we can't do it in the beginning of the function because the
+ result field might be needed for its own re-evaluation, e.g. case of
+ set x = x + 1;
+ */
+ set_null();
+ DBUG_ASSERT(thd->is_error());
+ DBUG_RETURN(true);
+}
+
+
+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,
+ uint8 dec_arg, bool zero_arg, bool unsigned_arg)
+ :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg),
+ dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg)
+{
+ if (zerofill)
+ flags|=ZEROFILL_FLAG;
+ if (unsigned_flag)
+ flags|=UNSIGNED_FLAG;
+}
+
+
+void Field_num::prepend_zeros(String *value) const
+{
+ int diff;
+ if ((diff= (int) (field_length - value->length())) > 0)
+ {
+ const bool error= value->realloc(field_length);
+ if (likely(!error))
+ {
+ bmove_upp((uchar*) value->ptr()+field_length,
+ (uchar*) value->ptr()+value->length(),
+ value->length());
+ bfill((uchar*) value->ptr(),diff,'0');
+ value->length(field_length);
+ }
+ }
+}
+
+
+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 0;
+
+out_of_range:
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ return 1;
+}
+
+
+double Field_real::get_double(const char *str, size_t length, CHARSET_INFO *cs,
+ int *error)
+{
+ char *end;
+ double nr= 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), invisible(VISIBLE),
+ null_ptr(null_ptr_arg), table(0), orig_table(0),
+ table_name(0), field_name(*field_name_arg), option_list(0),
+ option_struct(0), key_start(0), part_of_key(0),
+ part_of_key_not_clustered(0), part_of_sortkey(0),
+ unireg_check(unireg_check_arg), field_length(length_arg),
+ null_bit(null_bit_arg), is_created_from_null_item(FALSE),
+ read_stats(NULL), collected_stats(0), vcol_info(0), check_constraint(0),
+ default_value(0)
+{
+ flags=null_ptr ? 0: NOT_NULL_FLAG;
+ comment.str= (char*) "";
+ comment.length=0;
+ field_index= 0;
+ cond_selectivity= 1.0;
+ next_equal_field= NULL;
+}
+
+
+void Field::hash(ulong *nr, ulong *nr2)
+{
+ if (is_null())
+ {
+ *nr^= (*nr << 1) | 1;
+ }
+ else
+ {
+ uint len= pack_length();
+ sort_charset()->hash_sort(ptr, len, nr, nr2);
+ }
+}
+
+size_t
+Field::do_last_null_byte() const
+{
+ DBUG_ASSERT(null_ptr == NULL || null_ptr >= table->record[0]);
+ if (null_ptr)
+ return (size_t) (null_ptr - table->record[0]) + 1;
+ return LAST_NULL_BYTE_UNDEF;
+}
+
+
+void Field::copy_from_tmp(int row_offset)
+{
+ memcpy(ptr,ptr+row_offset,pack_length());
+ if (null_ptr)
+ {
+ *null_ptr= (uchar) ((null_ptr[0] & (uchar) ~(uint) null_bit) |
+ (null_ptr[row_offset] & (uchar) null_bit));
+ }
+}
+
+
+bool Field::send(Protocol *protocol)
+{
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff),charset());
+ val_str(&tmp);
+ return protocol->store(tmp.ptr(), tmp.length(), tmp.charset());
+}
+
+
+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)
+{
+ stat_field->val_str(str);
+ return store_text(str->ptr(), str->length(), &my_charset_bin);
+}
+
+
+/**
+ 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_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(&ltime, Datetime::Options_cmp(thd)))
+ return 0;
+ if (ltime.time_type != MYSQL_TIMESTAMP_TIME)
+ return pack_time(&ltime);
+ if (time_to_datetime_with_warn(thd, &ltime, &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(&ltime, opt))
+ return 0;
+ if (ltime.time_type == MYSQL_TIMESTAMP_TIME)
+ return pack_time(&ltime);
+ // Conversion from DATETIME or DATE to TIME is needed
+ return Time(thd, &ltime, 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);
+ /*
+ TODO: it is not clear why this method needs to reset unireg_check.
+ Try not to reset it, or explain why it needs to be reset.
+ */
+ tmp->unireg_check= Field::NONE;
+ tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
+ ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG |
+ VERS_SYS_START_FLAG | VERS_SYS_END_FLAG |
+ VERS_UPDATE_UNVERSIONED_FLAG);
+ tmp->reset_fields();
+ tmp->invisible= VISIBLE;
+ return tmp;
+}
+
+
+Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table,
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit)
+{
+ Field *tmp;
+ if ((tmp= make_new_field(root, new_table, table == new_table)))
+ {
+ tmp->ptr= new_ptr;
+ tmp->null_ptr= new_null_ptr;
+ tmp->null_bit= new_null_bit;
+ }
+ return tmp;
+}
+
+
+/**
+ 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;
+ 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.
+ */
+ Item *src;
+ if (!(src= thd->sp_fix_func_item(value)) ||
+ src->cmp_type() != ROW_RESULT ||
+ src->cols() != m_table->s->fields)
+ {
+ my_error(ER_OPERAND_COLUMNS, MYF(0), m_table->s->fields);
+ m_table->set_all_fields_to_null();
+ DBUG_RETURN(true);
+ }
+
+ DBUG_RETURN(m_table->sp_set_all_fields_from_item(thd, src));
+}
+
+
+/****************************************************************************
+ Functions for the Field_decimal class
+ This is an number stored as a pre-space (or pre-zero) string
+****************************************************************************/
+
+int
+Field_decimal::reset(void)
+{
+ Field_decimal::store(STRING_WITH_LEN("0"),&my_charset_bin);
+ return 0;
+}
+
+void Field_decimal::overflow(bool negative)
+{
+ uint len=field_length;
+ uchar *to=ptr, filler= '9';
+
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ if (negative)
+ {
+ if (!unsigned_flag)
+ {
+ /* Put - sign as a first digit so we'll have -999..999 or 999..999 */
+ *to++ = '-';
+ len--;
+ }
+ else
+ {
+ filler= '0'; // Fill up with 0
+ if (!zerofill)
+ {
+ /*
+ Handle unsigned integer without zerofill, in which case
+ the number should be of format ' 0' or ' 0.000'
+ */
+ uint whole_part=field_length- (dec ? dec+2 : 1);
+ // Fill with spaces up to the first digit
+ bfill(to, whole_part, ' ');
+ to+= whole_part;
+ len-= whole_part;
+ // The main code will also handle the 0 before the decimal point
+ }
+ }
+ }
+ bfill(to, len, filler);
+ if (dec)
+ ptr[field_length-dec-1]='.';
+ return;
+}
+
+
+int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs)
+{
+ 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, &not_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, &not_used);
+ return my_charset_bin.strntoll((char*) ptr, field_length, 10, NULL, &not_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 uint get_decimal_precision(uint len, uint8 dec, bool unsigned_val)
+{
+ uint precision= my_decimal_length_to_precision(len, dec, unsigned_val);
+ return 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,
+ uint8 dec_arg,bool zero_arg,
+ bool unsigned_arg)
+ :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg)
+{
+ precision= get_decimal_precision(len_arg, dec_arg, unsigned_arg);
+ DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
+ (dec <= DECIMAL_MAX_SCALE));
+ bin_size= my_decimal_get_binary_size(precision, dec);
+}
+
+
+int Field_new_decimal::reset(void)
+{
+ store_value(&decimal_zero);
+ return 0;
+}
+
+
+/**
+ Generate max/min decimal value in case of overflow.
+
+ @param decimal_value buffer for value
+ @param sign sign of value which caused overflow
+*/
+
+void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
+ bool sign)
+{
+ DBUG_ENTER("Field_new_decimal::set_value_on_overflow");
+ max_my_decimal(decimal_value, precision, decimals());
+ if (sign)
+ {
+ if (unsigned_flag)
+ my_decimal_set_zero(decimal_value);
+ else
+ decimal_value->sign(TRUE);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Store decimal value in the binary buffer.
+
+ Checks if decimal_value fits into field size.
+ If it does, stores the decimal in the buffer using binary format.
+ Otherwise sets maximal number that can be stored in the field.
+
+ @param decimal_value my_decimal
+ @param [OUT] native_error the error returned by my_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");
+#ifndef DBUG_OFF
+ {
+ char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
+ DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, decimal_value)));
+ }
+#endif
+
+ /* check that we do not try to write negative value in unsigned field */
+ if (unsigned_flag && decimal_value->sign())
+ {
+ DBUG_PRINT("info", ("unsigned overflow"));
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ decimal_value= &decimal_zero;
+ }
+#ifndef DBUG_OFF
+ {
+ char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
+ DBUG_PRINT("info", ("saving with precision %d scale: %d value %s",
+ (int)precision, (int)dec,
+ dbug_decimal_as_string(dbug_buff, decimal_value)));
+ }
+#endif
+
+ *native_error= 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);
+ }
+ }
+
+#ifndef DBUG_OFF
+ char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
+ DBUG_PRINT("enter", ("value: %s",
+ dbug_decimal_as_string(dbug_buff, &decimal_value)));
+#endif
+ int err2;
+ if (store_value(&decimal_value, &err2))
+ DBUG_RETURN(1);
+
+ /*
+ E_DEC_TRUNCATED means minor truncation, a note should be enough:
+ - in err: str2my_decimal() truncated '1e-1000000000000' to 0.0
+ - in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2)
+ Also, we send a note if a string had some trailing spaces: '1.12 '
+ */
+ if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
+ (err == E_DEC_TRUNCATED ||
+ err2 == E_DEC_TRUNCATED ||
+ end < from + length))
+ set_note(WARN_DATA_TRUNCATED, 1);
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @todo
+ Fix following when double2my_decimal when double2decimal
+ will return E_DEC_TRUNCATED always correctly
+*/
+
+int Field_new_decimal::store(double nr)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ my_decimal decimal_value;
+ int err;
+ THD *thd= get_thd();
+ DBUG_ENTER("Field_new_decimal::store(double)");
+
+ err= double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr,
+ &decimal_value);
+ if (err)
+ {
+ if (check_overflow(err))
+ set_value_on_overflow(&decimal_value, decimal_value.sign());
+ /* Only issue a warning if store_value doesn't issue an warning */
+ thd->got_warning= 0;
+ }
+ if (store_value(&decimal_value))
+ err= 1;
+ else if (err && !thd->got_warning)
+ err= warn_if_overflow(err);
+ DBUG_RETURN(err);
+}
+
+
+int Field_new_decimal::store(longlong nr, bool unsigned_val)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ my_decimal decimal_value;
+ int err;
+
+ if ((err= int2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
+ nr, unsigned_val, &decimal_value)))
+ {
+ if (check_overflow(err))
+ set_value_on_overflow(&decimal_value, decimal_value.sign());
+ /* Only issue a warning if store_value doesn't issue an warning */
+ get_thd()->got_warning= 0;
+ }
+ if (store_value(&decimal_value))
+ err= 1;
+ else if (err && !get_thd()->got_warning)
+ err= warn_if_overflow(err);
+ return err;
+}
+
+
+int Field_new_decimal::store_decimal(const my_decimal *decimal_value)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ return store_value(decimal_value);
+}
+
+
+int Field_new_decimal::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
+{
+ my_decimal decimal_value;
+ return store_value(date2my_decimal(ltime, &decimal_value));
+}
+
+
+my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
+{
+ DBUG_ASSERT(marked_for_read());
+ DBUG_ENTER("Field_new_decimal::val_decimal");
+ binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value,
+ precision, dec);
+ DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
+ bin_size););
+ DBUG_RETURN(decimal_value);
+}
+
+
+int Field_new_decimal::cmp(const uchar *a,const uchar*b) const
+{
+ return memcmp(a, b, bin_size);
+}
+
+
+void Field_new_decimal::sort_string(uchar *buff, uint length)
+{
+ memcpy(buff, ptr, length);
+}
+
+
+void Field_new_decimal::sql_type(String &str) const
+{
+ CHARSET_INFO *cs= str.charset();
+ str.length(cs->cset->snprintf(cs, (char*) str.ptr(), str.alloced_length(),
+ "decimal(%d,%d)", precision, (int)dec));
+ add_zerofill_and_unsigned(str);
+}
+
+
+/**
+ Save the field metadata for new decimal fields.
+
+ Saves the precision in the first byte and decimals() in the second
+ byte of the field metadata array at index of *metadata_ptr and
+ *(metadata_ptr + 1).
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+
+Binlog_type_info Field_new_decimal::binlog_type_info() const
+{
+ DBUG_ASSERT(Field_new_decimal::type() == binlog_type());
+ return Binlog_type_info(Field_new_decimal::type(), precision +
+ (decimals() << 8), 2, binlog_signedness());
+}
+
+
+/**
+ Returns the number of bytes field uses in row-based replication
+ row packed size.
+
+ This method is used in row-based replication to determine the number
+ of bytes that the field consumes in the row record format. This is
+ used to skip fields in the master that do not exist on the slave.
+
+ @param field_metadata Encoded size in field metadata
+
+ @returns The size of the field based on the field metadata.
+*/
+uint Field_new_decimal::pack_length_from_metadata(uint field_metadata) const
+{
+ uint const source_precision= (field_metadata >> 8U) & 0x00ff;
+ uint const source_decimal= field_metadata & 0x00ff;
+ uint const source_size= my_decimal_get_binary_size(source_precision,
+ source_decimal);
+ return (source_size);
+}
+
+
+bool Field_new_decimal::compatible_field_size(uint field_metadata,
+ const Relay_log_info * __attribute__((unused)),
+ uint16 mflags __attribute__((unused)),
+ int *order_var) const
+{
+ uint const source_precision= (field_metadata >> 8U) & 0x00ff;
+ uint const source_decimal= field_metadata & 0x00ff;
+ int order= compare(source_precision, precision);
+ *order_var= order != 0 ? order : compare(source_decimal, dec);
+ return true;
+}
+
+
+bool Field_new_decimal::is_equal(const Column_definition &new_field) const
+{
+ return ((new_field.type_handler() == type_handler()) &&
+ ((new_field.flags & UNSIGNED_FLAG) ==
+ (uint) (flags & UNSIGNED_FLAG)) &&
+ ((new_field.flags & AUTO_INCREMENT_FLAG) <=
+ (uint) (flags & AUTO_INCREMENT_FLAG)) &&
+ (new_field.length == max_display_length()) &&
+ (new_field.decimals == dec));
+}
+
+
+/**
+ Unpack a decimal field from row data.
+
+ This method is used to unpack a decimal or numeric field from a master
+ whose size of the field is less than that of the slave.
+
+ @param to Destination of the data
+ @param from Source of the data
+ @param param_data Precision (upper) and decimal (lower) values
+
+ @return New pointer into memory based on from + length of the data
+*/
+const uchar *
+Field_new_decimal::unpack(uchar* to, const uchar *from, const uchar *from_end,
+ uint param_data)
+{
+ if (param_data == 0)
+ return Field::unpack(to, from, from_end, param_data);
+
+ uint from_precision= (param_data & 0xff00) >> 8U;
+ uint from_decimal= param_data & 0x00ff;
+ uint length=pack_length();
+ uint from_pack_len= my_decimal_get_binary_size(from_precision, from_decimal);
+ uint len= (param_data && (from_pack_len < length)) ?
+ from_pack_len : length;
+ if ((from_pack_len && (from_pack_len < length)) ||
+ (from_precision < precision) ||
+ (from_decimal < decimals()))
+ {
+ /*
+ If the master's data is smaller than the slave, we need to convert
+ the binary to decimal then resize the decimal converting it back to
+ a decimal and write that to the raw data buffer.
+ */
+ decimal_digit_t dec_buf[DECIMAL_MAX_PRECISION];
+ decimal_t dec_val;
+ dec_val.len= from_precision;
+ dec_val.buf= dec_buf;
+ /*
+ Note: bin2decimal does not change the length of the field. So it is
+ just the first step the resizing operation. The second step does the
+ resizing using the precision and decimals from the slave.
+ */
+ bin2decimal((uchar *)from, &dec_val, from_precision, from_decimal);
+ decimal2bin(&dec_val, to, precision, decimals());
+ }
+ else
+ {
+ if (from + len > from_end)
+ return 0; // Wrong data
+ memcpy(to, from, len); // Sizes are the same, just copy the data.
+ }
+ return from+len;
+}
+
+
+Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx,
+ Item *const_item)
+{
+ if (flags & ZEROFILL_FLAG)
+ return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item);
+ switch (ctx.subst_constraint()) {
+ case IDENTITY_SUBST:
+ if (const_item->field_type() != MYSQL_TYPE_NEWDECIMAL ||
+ const_item->decimal_scale() != decimals())
+ {
+ VDec val(const_item);
+ if (val.is_null())
+ {
+ DBUG_ASSERT(0);
+ return const_item;
+ }
+ /*
+ Truncate or extend the decimal value to the scale of the field.
+ See comments about truncation in the same place in
+ Field_time::get_equal_const_item().
+ */
+ my_decimal tmp;
+ val.round_to(&tmp, decimals(), TRUNCATE);
+ return new (thd->mem_root) Item_decimal(thd, field_name.str, &tmp,
+ decimals(), field_length);
+ }
+ break;
+ case ANY_SUBST:
+ break;
+ }
+ return const_item;
+}
+
+
+int Field_int::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
+{
+ longlong v= TIME_to_ulonglong(ltime);
+ if (ltime->neg == 0)
+ return store(v, true);
+ return store(-v, false);
+}
+
+
+void Field_int::sql_type(String &res) const
+{
+ CHARSET_INFO *cs=res.charset();
+ Name name= type_handler()->type_handler_signed()->name();
+ res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
+ "%.*s(%d)", (int) name.length(), name.ptr(),
+ (int) field_length));
+ add_zerofill_and_unsigned(res);
+}
+
+
+/****************************************************************************
+** tiny int
+****************************************************************************/
+
+int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error;
+ longlong rnd;
+
+ error= get_int(cs, from, len, &rnd, 255, -128, 127);
+ ptr[0]= unsigned_flag ? (char) (ulonglong) rnd : (char) rnd;
+ return error;
+}
+
+
+int Field_tiny::store(double nr)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+ nr=rint(nr);
+ if (unsigned_flag)
+ {
+ if (nr < 0.0)
+ {
+ *ptr=0;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (nr > 255.0)
+ {
+ *ptr= (uchar) 255;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ *ptr= (uchar) nr;
+ }
+ else
+ {
+ if (nr < -128.0)
+ {
+ *ptr= (uchar) -128;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (nr > 127.0)
+ {
+ *ptr=127;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ *ptr=(uchar) (int) nr;
+ }
+ return error;
+}
+
+
+int Field_tiny::store(longlong nr, bool unsigned_val)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+
+ if (unsigned_flag)
+ {
+ if (nr < 0 && !unsigned_val)
+ {
+ *ptr= 0;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if ((ulonglong) nr > (ulonglong) 255)
+ {
+ *ptr= (char) 255;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ *ptr=(char) nr;
+ }
+ else
+ {
+ if (nr < 0 && unsigned_val)
+ nr= 256; // Generate overflow
+ if (nr < -128)
+ {
+ *ptr= (char) -128;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (nr > 127)
+ {
+ *ptr=127;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ *ptr=(char) nr;
+ }
+ return error;
+}
+
+
+double Field_tiny::val_real(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ int tmp= unsigned_flag ? (int) ptr[0] :
+ (int) ((signed char*) ptr)[0];
+ return (double) tmp;
+}
+
+
+longlong Field_tiny::val_int(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ int tmp= unsigned_flag ? (int) ptr[0] :
+ (int) ((signed char*) ptr)[0];
+ return (longlong) tmp;
+}
+
+
+String *Field_tiny::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ DBUG_ASSERT(marked_for_read());
+ long nr= unsigned_flag ? (long) ptr[0] : (long) ((signed char*) ptr)[0];
+ return val_str_from_long(val_buffer, 5, -10, nr);
+}
+
+bool Field_tiny::send(Protocol *protocol)
+{
+ DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_TINY);
+ return protocol->store_tiny(Field_tiny::val_int());
+}
+
+
+int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr) const
+{
+ signed char a,b;
+ a=(signed char) a_ptr[0]; b= (signed char) b_ptr[0];
+ if (unsigned_flag)
+ return ((uchar) a < (uchar) b) ? -1 : ((uchar) a > (uchar) b) ? 1 : 0;
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_tiny::sort_string(uchar *to,uint length __attribute__((unused)))
+{
+ if (unsigned_flag)
+ *to= *ptr;
+ else
+ to[0] = (char) (ptr[0] ^ (uchar) 128); /* Revers signbit */
+}
+
+/****************************************************************************
+ Field type short int (2 byte)
+****************************************************************************/
+
+int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int store_tmp;
+ int error;
+ longlong rnd;
+
+ error= get_int(cs, from, len, &rnd, UINT_MAX16, INT_MIN16, INT_MAX16);
+ store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd;
+ int2store(ptr, store_tmp);
+ return error;
+}
+
+
+int Field_short::store(double nr)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+ int16 res;
+ nr=rint(nr);
+ if (unsigned_flag)
+ {
+ if (nr < 0)
+ {
+ res=0;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (nr > (double) UINT_MAX16)
+ {
+ res=(int16) UINT_MAX16;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ res=(int16) (uint16) nr;
+ }
+ else
+ {
+ if (nr < (double) INT_MIN16)
+ {
+ res=INT_MIN16;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (nr > (double) INT_MAX16)
+ {
+ res=INT_MAX16;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ res=(int16) (int) nr;
+ }
+ int2store(ptr,res);
+ return error;
+}
+
+
+int Field_short::store(longlong nr, bool unsigned_val)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+ int16 res;
+
+ if (unsigned_flag)
+ {
+ if (nr < 0L && !unsigned_val)
+ {
+ res=0;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if ((ulonglong) nr > (ulonglong) UINT_MAX16)
+ {
+ res=(int16) UINT_MAX16;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ res=(int16) (uint16) nr;
+ }
+ else
+ {
+ if (nr < 0 && unsigned_val)
+ nr= UINT_MAX16+1; // Generate overflow
+
+ if (nr < INT_MIN16)
+ {
+ res=INT_MIN16;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (nr > (longlong) INT_MAX16)
+ {
+ res=INT_MAX16;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ res=(int16) nr;
+ }
+ int2store(ptr,res);
+ return error;
+}
+
+
+double Field_short::val_real(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ short j;
+ j=sint2korr(ptr);
+ return unsigned_flag ? (double) (unsigned short) j : (double) j;
+}
+
+longlong Field_short::val_int(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ short j;
+ j=sint2korr(ptr);
+ return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j;
+}
+
+
+String *Field_short::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ DBUG_ASSERT(marked_for_read());
+ short j= sint2korr(ptr);
+ long nr= unsigned_flag ? (long) (unsigned short) j : (long) j;
+ return val_str_from_long(val_buffer, 7, -10, nr);
+}
+
+
+bool Field_short::send(Protocol *protocol)
+{
+ DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_SHORT);
+ return protocol->store_short(Field_short::val_int());
+}
+
+
+int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr) const
+{
+ short a,b;
+ a=sint2korr(a_ptr);
+ b=sint2korr(b_ptr);
+
+ if (unsigned_flag)
+ return ((unsigned short) a < (unsigned short) b) ? -1 :
+ ((unsigned short) a > (unsigned short) b) ? 1 : 0;
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_short::sort_string(uchar *to,uint length __attribute__((unused)))
+{
+ if (unsigned_flag)
+ to[0] = ptr[1];
+ else
+ to[0] = (char) (ptr[1] ^ 128); /* Revers signbit */
+ to[1] = ptr[0];
+}
+
+/****************************************************************************
+ Field type medium int (3 byte)
+****************************************************************************/
+
+int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int store_tmp;
+ int error;
+ longlong rnd;
+
+ error= get_int(cs, from, len, &rnd, UINT_MAX24, INT_MIN24, INT_MAX24);
+ store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd;
+ int3store(ptr, store_tmp);
+ return error;
+}
+
+
+int Field_medium::store(double nr)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+ nr=rint(nr);
+ if (unsigned_flag)
+ {
+ if (nr < 0)
+ {
+ int3store(ptr,0);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (nr >= (double) (long) (1L << 24))
+ {
+ uint32 tmp=(uint32) (1L << 24)-1L;
+ int3store(ptr,tmp);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ int3store(ptr,(uint32) nr);
+ }
+ else
+ {
+ if (nr < (double) INT_MIN24)
+ {
+ long tmp=(long) INT_MIN24;
+ int3store(ptr,tmp);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (nr > (double) INT_MAX24)
+ {
+ long tmp=(long) INT_MAX24;
+ int3store(ptr,tmp);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ int3store(ptr,(long) nr);
+ }
+ return error;
+}
+
+
+int Field_medium::store(longlong nr, bool unsigned_val)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+
+ if (unsigned_flag)
+ {
+ if (nr < 0 && !unsigned_val)
+ {
+ int3store(ptr,0);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24))
+ {
+ long tmp= (long) (1L << 24)-1L;
+ int3store(ptr,tmp);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ int3store(ptr,(uint32) nr);
+ }
+ else
+ {
+ if (nr < 0 && unsigned_val)
+ nr= (ulonglong) (long) (1L << 24); // Generate overflow
+
+ if (nr < (longlong) INT_MIN24)
+ {
+ long tmp= (long) INT_MIN24;
+ int3store(ptr,tmp);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (nr > (longlong) INT_MAX24)
+ {
+ long tmp=(long) INT_MAX24;
+ int3store(ptr,tmp);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ int3store(ptr,(long) nr);
+ }
+ return error;
+}
+
+
+double Field_medium::val_real(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
+ return (double) j;
+}
+
+
+longlong Field_medium::val_int(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
+ return (longlong) j;
+}
+
+
+String *Field_medium::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ DBUG_ASSERT(marked_for_read());
+ long nr= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
+ return val_str_from_long(val_buffer, 10, -10, nr);
+}
+
+
+String *Field_int::val_str_from_long(String *val_buffer,
+ uint max_char_length,
+ int radix, long nr)
+{
+ CHARSET_INFO *cs= &my_charset_numeric;
+ uint length;
+ uint mlength= MY_MAX(field_length + 1, max_char_length * cs->mbmaxlen);
+ val_buffer->alloc(mlength);
+ char *to=(char*) val_buffer->ptr();
+ length= (uint) cs->long10_to_str(to, mlength, radix, nr);
+ val_buffer->length(length);
+ if (zerofill)
+ prepend_zeros(val_buffer); /* purecov: inspected */
+ val_buffer->set_charset(cs);
+ return val_buffer;
+}
+
+
+bool Field_medium::send(Protocol *protocol)
+{
+ DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG);
+ return protocol->store_long(Field_medium::val_int());
+}
+
+
+int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr) const
+{
+ long a,b;
+ if (unsigned_flag)
+ {
+ a=uint3korr(a_ptr);
+ b=uint3korr(b_ptr);
+ }
+ else
+ {
+ a=sint3korr(a_ptr);
+ b=sint3korr(b_ptr);
+ }
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_medium::sort_string(uchar *to,uint length __attribute__((unused)))
+{
+ if (unsigned_flag)
+ to[0] = ptr[2];
+ else
+ to[0] = (uchar) (ptr[2] ^ 128); /* Revers signbit */
+ to[1] = ptr[1];
+ to[2] = ptr[0];
+}
+
+
+/****************************************************************************
+** long int
+****************************************************************************/
+
+int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ long store_tmp;
+ int error;
+ longlong rnd;
+
+ error= get_int(cs, from, len, &rnd, UINT_MAX32, INT_MIN32, INT_MAX32);
+ store_tmp= unsigned_flag ? (long) (ulonglong) rnd : (long) rnd;
+ int4store(ptr, store_tmp);
+ return error;
+}
+
+
+int Field_long::store(double nr)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+ int32 res;
+ nr=rint(nr);
+ if (unsigned_flag)
+ {
+ if (nr < 0)
+ {
+ res=0;
+ error= 1;
+ }
+ else if (nr > (double) UINT_MAX32)
+ {
+ res= UINT_MAX32;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else
+ res=(int32) (ulong) nr;
+ }
+ else
+ {
+ if (nr < (double) INT_MIN32)
+ {
+ res=(int32) INT_MIN32;
+ error= 1;
+ }
+ else if (nr > (double) INT_MAX32)
+ {
+ res=(int32) INT_MAX32;
+ error= 1;
+ }
+ else
+ res=(int32) (longlong) nr;
+ }
+ if (unlikely(error))
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+
+ int4store(ptr,res);
+ return error;
+}
+
+
+int Field_long::store(longlong nr, bool unsigned_val)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+ int32 res;
+
+ if (unsigned_flag)
+ {
+ if (nr < 0 && !unsigned_val)
+ {
+ res=0;
+ error= 1;
+ }
+ else if ((ulonglong) nr >= (1LL << 32))
+ {
+ res=(int32) (uint32) ~0L;
+ error= 1;
+ }
+ else
+ res=(int32) (uint32) nr;
+ }
+ else
+ {
+ if (nr < 0 && unsigned_val)
+ nr= ((longlong) INT_MAX32) + 1; // Generate overflow
+ if (nr < (longlong) INT_MIN32)
+ {
+ res=(int32) INT_MIN32;
+ error= 1;
+ }
+ else if (nr > (longlong) INT_MAX32)
+ {
+ res=(int32) INT_MAX32;
+ error= 1;
+ }
+ else
+ res=(int32) nr;
+ }
+ if (unlikely(error))
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+
+ int4store(ptr,res);
+ return error;
+}
+
+
+double Field_long::val_real(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ int32 j;
+ j=sint4korr(ptr);
+ return unsigned_flag ? (double) (uint32) j : (double) j;
+}
+
+longlong Field_long::val_int(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ int32 j;
+ /* See the comment in Field_long::store(long long) */
+ DBUG_ASSERT(!table || table->in_use == current_thd);
+ j=sint4korr(ptr);
+ return unsigned_flag ? (longlong) (uint32) j : (longlong) j;
+}
+
+
+String *Field_long::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ DBUG_ASSERT(marked_for_read());
+ long nr= unsigned_flag ? (long) uint4korr(ptr) : sint4korr(ptr);
+ return val_str_from_long(val_buffer, 12, unsigned_flag ? 10 : -10, nr);
+}
+
+
+bool Field_long::send(Protocol *protocol)
+{
+ DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG);
+ return protocol->store_long(Field_long::val_int());
+}
+
+
+int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr) const
+{
+ int32 a,b;
+ a=sint4korr(a_ptr);
+ b=sint4korr(b_ptr);
+ if (unsigned_flag)
+ return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_long::sort_string(uchar *to,uint length __attribute__((unused)))
+{
+ if (unsigned_flag)
+ to[0] = ptr[3];
+ else
+ to[0] = (char) (ptr[3] ^ 128); /* Revers signbit */
+ to[1] = ptr[2];
+ to[2] = ptr[1];
+ to[3] = ptr[0];
+}
+
+
+/****************************************************************************
+ Field type longlong int (8 bytes)
+****************************************************************************/
+
+int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+ char *end;
+ ulonglong tmp;
+
+ tmp= cs->strntoull10rnd(from, len, unsigned_flag, &end, &error);
+ if (unlikely(error == MY_ERRNO_ERANGE))
+ {
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
+ check_int(cs, from, len, end, error))
+ error= 1;
+ else
+ error= 0;
+ int8store(ptr,tmp);
+ return error;
+}
+
+
+int Field_longlong::store(double nr)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ Converter_double_to_longlong conv(nr, unsigned_flag);
+
+ if (unlikely(conv.error()))
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+
+ int8store(ptr, conv.result());
+ return conv.error();
+}
+
+
+int Field_longlong::store(longlong nr, bool unsigned_val)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= 0;
+
+ if (unlikely(nr < 0)) // Only possible error
+ {
+ /*
+ if field is unsigned and value is signed (< 0) or
+ if field is signed and value is unsigned we have an overflow
+ */
+ if (unsigned_flag != unsigned_val)
+ {
+ nr= unsigned_flag ? (ulonglong) 0 : (ulonglong) LONGLONG_MAX;
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ }
+
+ int8store(ptr,nr);
+ return error;
+}
+
+
+double Field_longlong::val_real(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ longlong j;
+ j=sint8korr(ptr);
+ /* The following is open coded to avoid a bug in gcc 3.3 */
+ if (unsigned_flag)
+ {
+ ulonglong tmp= (ulonglong) j;
+ return ulonglong2double(tmp);
+ }
+ return (double) j;
+}
+
+
+longlong Field_longlong::val_int(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ longlong j;
+ j=sint8korr(ptr);
+ return j;
+}
+
+
+String *Field_longlong::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ CHARSET_INFO *cs= &my_charset_numeric;
+ uint length;
+ uint mlength=MY_MAX(field_length+1,22*cs->mbmaxlen);
+ val_buffer->alloc(mlength);
+ char *to=(char*) val_buffer->ptr();
+ longlong j;
+ j=sint8korr(ptr);
+
+ length=(uint) (cs->longlong10_to_str)(to, mlength,
+ unsigned_flag ? 10 : -10, j);
+ val_buffer->length(length);
+ if (zerofill)
+ prepend_zeros(val_buffer);
+ val_buffer->set_charset(cs);
+ return val_buffer;
+}
+
+
+bool Field_longlong::send(Protocol *protocol)
+{
+ DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONGLONG);
+ return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag);
+}
+
+
+int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr) const
+{
+ longlong a,b;
+ a=sint8korr(a_ptr);
+ b=sint8korr(b_ptr);
+ if (unsigned_flag)
+ return ((ulonglong) a < (ulonglong) b) ? -1 :
+ ((ulonglong) a > (ulonglong) b) ? 1 : 0;
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_longlong::sort_string(uchar *to,uint length __attribute__((unused)))
+{
+ if (unsigned_flag)
+ to[0] = ptr[7];
+ else
+ to[0] = (char) (ptr[7] ^ 128); /* Revers signbit */
+ to[1] = ptr[6];
+ to[2] = ptr[5];
+ to[3] = ptr[4];
+ to[4] = ptr[3];
+ to[5] = ptr[2];
+ to[6] = ptr[1];
+ to[7] = ptr[0];
+}
+
+
+void Field_longlong::set_max()
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ set_notnull();
+ int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX);
+}
+
+bool Field_longlong::is_max()
+{
+ DBUG_ASSERT(marked_for_read());
+ if (unsigned_flag)
+ {
+ ulonglong j;
+ j= uint8korr(ptr);
+ return j == ULONGLONG_MAX;
+ }
+ longlong j;
+ j= sint8korr(ptr);
+ return j == LONGLONG_MAX;
+}
+
+/*
+ Floating-point numbers
+ */
+
+/****************************************************************************
+ single precision float
+****************************************************************************/
+
+int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs)
+{
+ int error;
+ Field_float::store(get_double(from, len, cs, &error));
+ return error;
+}
+
+
+int Field_float::store(double nr)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= truncate_double(&nr, field_length,
+ not_fixed ? NOT_FIXED_DEC : dec,
+ unsigned_flag, FLT_MAX);
+ if (unlikely(error))
+ {
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ if (error < 0) // Wrong double value
+ {
+ error= 1;
+ set_null();
+ }
+ }
+ float j= (float)nr;
+
+ float4store(ptr,j);
+ return error;
+}
+
+
+int Field_float::store(longlong nr, bool unsigned_val)
+{
+ return Field_float::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
+ (double) nr);
+}
+
+
+double Field_float::val_real(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ float j;
+ float4get(j,ptr);
+ return ((double) j);
+}
+
+longlong Field_float::val_int(void)
+{
+ float j;
+ float4get(j,ptr);
+ return Converter_double_to_longlong(j, false).result();
+}
+
+
+String *Field_float::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ DBUG_ASSERT(marked_for_read());
+ DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
+
+ if (Float(ptr).to_string(val_buffer, dec))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return val_buffer;
+ }
+
+ if (zerofill)
+ prepend_zeros(val_buffer);
+ return val_buffer;
+}
+
+
+int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr) const
+{
+ float a,b;
+ float4get(a,a_ptr);
+ float4get(b,b_ptr);
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+#define FLT_EXP_DIG (sizeof(float)*8-FLT_MANT_DIG)
+
+void Field_float::sort_string(uchar *to,uint length __attribute__((unused)))
+{
+ float nr;
+ float4get(nr,ptr);
+
+ uchar *tmp= to;
+ if (nr == (float) 0.0)
+ { /* Change to zero string */
+ tmp[0]=(uchar) 128;
+ bzero((char*) tmp+1,sizeof(nr)-1);
+ }
+ else
+ {
+#ifdef WORDS_BIGENDIAN
+ memcpy(tmp, &nr, sizeof(nr));
+#else
+ tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0];
+#endif
+ if (tmp[0] & 128) /* Negative */
+ { /* make complement */
+ uint i;
+ for (i=0 ; i < sizeof(nr); i++)
+ tmp[i]= (uchar) (tmp[i] ^ (uchar) 255);
+ }
+ else
+ {
+ ushort exp_part=(((ushort) tmp[0] << 8) | (ushort) tmp[1] |
+ (ushort) 32768);
+ exp_part+= (ushort) 1 << (16-1-FLT_EXP_DIG);
+ tmp[0]= (uchar) (exp_part >> 8);
+ tmp[1]= (uchar) exp_part;
+ }
+ }
+}
+
+
+bool Field_float::send(Protocol *protocol)
+{
+ DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_FLOAT);
+ return protocol->store_float((float) Field_float::val_real(), dec);
+}
+
+
+/**
+ Save the field metadata for float fields.
+
+ Saves the pack length in the first byte.
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+Binlog_type_info Field_float::binlog_type_info() const
+{
+ DBUG_ASSERT(Field_float::type() == binlog_type());
+ return Binlog_type_info(Field_float::type(), pack_length(), 1,
+ binlog_signedness());
+}
+
+
+/****************************************************************************
+ double precision floating point numbers
+****************************************************************************/
+
+int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs)
+{
+ int error;
+ Field_double::store(get_double(from, len, cs, &error));
+ return error;
+}
+
+
+int Field_double::store(double nr)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ int error= truncate_double(&nr, field_length,
+ not_fixed ? NOT_FIXED_DEC : dec,
+ unsigned_flag, DBL_MAX);
+ if (unlikely(error))
+ {
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
+ if (error < 0) // Wrong double value
+ {
+ error= 1;
+ set_null();
+ }
+ }
+
+ float8store(ptr,nr);
+ return error;
+}
+
+
+int Field_double::store(longlong nr, bool unsigned_val)
+{
+ return Field_double::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
+ (double) nr);
+}
+
+/*
+ If a field has fixed length, truncate the double argument pointed to by 'nr'
+ appropriately.
+ Also ensure that the argument is within [-max_value; max_value] range.
+
+ return
+ 0 ok
+ -1 Illegal double value
+ 1 Value was truncated
+*/
+
+int truncate_double(double *nr, uint field_length, uint dec,
+ bool unsigned_flag, double max_value)
+{
+ int error= 0;
+ double res= *nr;
+
+ if (std::isnan(res))
+ {
+ *nr= 0;
+ return -1;
+ }
+ else if (unsigned_flag && res < 0)
+ {
+ *nr= 0;
+ return 1;
+ }
+
+ if (dec < FLOATING_POINT_DECIMALS)
+ {
+ uint order= field_length - dec;
+ uint step= array_elements(log_10) - 1;
+ 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 dec_arg)
+{
+ 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(&ltime, 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(&ltime, 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(&ltime, date_mode_t(0));
+ return protocol->store(&ltime, 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(&ltime, 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(&ltime, date_mode_t(0));
+ return TIME_to_my_decimal(&ltime, 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(&ltime, date_mode_t(0));
+ return protocol->store(&ltime, 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(&ltime, ptr_in_record(record), Datetime::Options(thd));
+}
+
+
+my_decimal *Field_temporal::val_decimal(my_decimal *d)
+{
+ MYSQL_TIME ltime;
+ if (get_date(&ltime, date_mode_t(0)))
+ {
+ bzero(&ltime, sizeof(ltime));
+ ltime.time_type= type_handler()->mysql_timestamp_type();
+ }
+ return TIME_to_my_decimal(&ltime, d);
+}
+
+
+bool Field_temporal::can_optimize_keypart_ref(const Item_bool_func *cond,
+ const Item *value) const
+{
+ return true; // Field is of TIME_RESULT, which supersedes everything else.
+}
+
+
+bool Field_temporal::can_optimize_group_min_max(const Item_bool_func *cond,
+ const Item *const_item) const
+{
+ return true; // Field is of TIME_RESULT, which supersedes everything else.
+}
+
+
+Item *Field_temporal::get_equal_const_item_datetime(THD *thd,
+ const Context &ctx,
+ Item *const_item)
+{
+ switch (ctx.subst_constraint()) {
+ case IDENTITY_SUBST:
+ if ((const_item->field_type() != MYSQL_TYPE_DATETIME &&
+ const_item->field_type() != MYSQL_TYPE_TIMESTAMP) ||
+ const_item->decimals != decimals())
+ {
+ Datetime::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, &ltime);
+ 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(&ltime, Datetime::Options(TIME_TIME_ONLY, get_thd()));
+ str->alloc(field_length + 1);
+ str->length(my_time_to_str(&ltime, 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(&ltime, date_mode_t(0));
+ int warn;
+ return Time(&warn, &ltime, 0).to_native(to, decimals());
+}
+
+
+bool Field_time::send(Protocol *protocol)
+{
+ MYSQL_TIME ltime;
+ get_date(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
+ return protocol->store_time(&ltime, 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(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
+ longlong val= TIME_to_ulonglong_time(&ltime);
+ return ltime.neg ? -val : val;
+}
+
+double Field_time_with_dec::val_real(void)
+{
+ DBUG_ASSERT(marked_for_read());
+ MYSQL_TIME ltime;
+ get_date(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
+ return TIME_to_double(&ltime);
+}
+
+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(&ltime, tmp);
+ return pack_time(&ltime);
+}
+
+
+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 dec_arg)
+{
+ 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(&ltime, ptr, date_mode_t(0));
+ val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
+ uint length= (uint) my_date_to_str(&ltime,
+ 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);
+ 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(&ltime, date_mode_t(0));
+ return pack_time(&ltime);
+}
+
+
+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(&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);
+ 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(&ltime, date_mode_t(0));
+ return protocol->store(&ltime, dec);
+}
+
+
+double Field_datetime_with_dec::val_real(void)
+{
+ MYSQL_TIME ltime;
+ get_date(&ltime, date_mode_t(0));
+ return TIME_to_double(&ltime);
+}
+
+longlong Field_datetime_with_dec::val_int(void)
+{
+ MYSQL_TIME ltime;
+ get_date(&ltime, date_mode_t(0));
+ return TIME_to_ulonglong_datetime(&ltime);
+}
+
+
+String *Field_datetime_with_dec::val_str(String *str,
+ String *unused __attribute__((unused)))
+{
+ MYSQL_TIME ltime;
+ get_date(&ltime, date_mode_t(0));
+ str->alloc(field_length+1);
+ str->length(field_length);
+ my_datetime_to_str(&ltime, (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(&ltime, tmp);
+ return pack_time(&ltime);
+}
+
+
+/****************************************************************************
+** 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());
+}
+
+
+ /* 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, &copy_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);
+}
+
+
+bool
+Field_longstr::cmp_to_string_with_same_collation(const Item_bool_func *cond,
+ const Item *item) const
+{
+ return item->cmp_type() == STRING_RESULT &&
+ charset() == cond->compare_collation();
+}
+
+
+bool
+Field_longstr::cmp_to_string_with_stricter_collation(const Item_bool_func *cond,
+ const Item *item) const
+{
+ return item->cmp_type() == STRING_RESULT &&
+ (charset() == cond->compare_collation() ||
+ cond->compare_collation()->state & MY_CS_BINSORT);
+}
+
+
+bool Field_longstr::can_optimize_keypart_ref(const Item_bool_func *cond,
+ const Item *item) const
+{
+ DBUG_ASSERT(cmp_type() == STRING_RESULT);
+ return cmp_to_string_with_stricter_collation(cond, item);
+}
+
+
+bool Field_longstr::can_optimize_hash_join(const Item_bool_func *cond,
+ const Item *item) const
+{
+ DBUG_ASSERT(cmp_type() == STRING_RESULT);
+ return cmp_to_string_with_same_collation(cond, item);
+}
+
+
+bool Field_longstr::can_optimize_group_min_max(const Item_bool_func *cond,
+ const Item *const_item) const
+{
+ /*
+ Can't use indexes when comparing a string to a number or a date
+ Don't use an index when comparing strings of different collations.
+ */
+ DBUG_ASSERT(cmp_type() == STRING_RESULT);
+ return cmp_to_string_with_same_collation(cond, const_item);
+}
+
+
+bool Field_longstr::can_optimize_range(const Item_bool_func *cond,
+ const Item *item,
+ bool is_eq_func) const
+{
+ return is_eq_func ?
+ cmp_to_string_with_stricter_collation(cond, item) :
+ cmp_to_string_with_same_collation(cond, item);
+}
+
+
+/**
+ This overrides the default behavior of the parent constructor
+ Warn_filter(thd) to suppress notes about trailing spaces in case of CHAR(N),
+ as they are truncated during val_str().
+ We still do want truncation notes in case of BINARY(N),
+ as trailing spaces are not truncated in val_str().
+*/
+Field_string::Warn_filter_string::Warn_filter_string(const THD *thd,
+ const Field_string *field)
+ :Warn_filter(!thd->no_errors,
+ !thd->no_errors &&
+ field->field_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,
+ 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
+{
+ size_t a_len, b_len;
+
+ if (mbmaxlen() != 1)
+ {
+ size_t char_len= Field_string::char_length();
+ a_len= field_charset()->charpos(a_ptr, a_ptr + field_length, char_len);
+ b_len= field_charset()->charpos(b_ptr, b_ptr + field_length, char_len);
+ }
+ else
+ a_len= b_len= field_length;
+ /*
+ We have to remove end space to be able to compare multi-byte-characters
+ like in latin_de 'ae' and 0xe4
+ */
+ return field_charset()->strnncollsp(a_ptr, a_len,
+ b_ptr, b_len);
+}
+
+
+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
+{
+ CHARSET_INFO *cs=charset();
+ if (Field_string::has_charset())
+ {
+ size_t length= cs->cset->snprintf(cs, (char*) res->ptr(),
+ res->alloced_length(),
+ "char(%u octets) character set %s",
+ field_length,
+ charset()->csname);
+ 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;
+
+/**
+ 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, &copy_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,
+ 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
+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;
+}
+
+
+static int cmp_str_prefix(const uchar *ua, size_t alen, const uchar *ub,
+ size_t blen, size_t prefix, CHARSET_INFO *cs)
+{
+ const char *a= (char*)ua, *b= (char*)ub;
+ MY_STRCOPY_STATUS status;
+ prefix/= cs->mbmaxlen;
+ alen= cs->cset->well_formed_char_length(cs, a, a + alen, prefix, &status);
+ blen= cs->cset->well_formed_char_length(cs, b, b + blen, prefix, &status);
+ return cs->coll->strnncollsp(cs, ua, alen, ub, blen);
+}
+
+
+
+int Field_varstring::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
+ size_t prefix_len) const
+{
+ /* avoid expensive well_formed_char_length if possible */
+ if (prefix_len == 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 cmp_str_prefix(a_ptr+length_bytes, a_length, b_ptr+length_bytes,
+ b_length, prefix_len, field_charset());
+}
+
+
+/**
+ @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
+{
+ CHARSET_INFO *cs=charset();
+ if (Field_varstring::has_charset())
+ {
+ size_t length= cs->cset->snprintf(cs, (char*) res->ptr(),
+ res->alloced_length(),
+ "varchar(%u octets) character set %s",
+ field_length,
+ charset()->csname);
+ 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(ulong *nr, ulong *nr2)
+{
+ if (is_null())
+ {
+ *nr^= (*nr << 1) | 1;
+ }
+ else
+ {
+ uint len= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
+ charset()->hash_sort(ptr + length_bytes, len, nr, nr2);
+ }
+}
+
+
+/**
+ Compress field
+
+ @param[out] to destination buffer for compressed data
+ @param[in] to_length size of to
+ @param[in] from data to compress
+ @param[in] length from length
+ @param[in] max_length truncate `from' to this length
+ @param[out] out_length compessed data length
+ @param[in] cs from character set
+ @param[in] nchars copy no more than "nchars" characters
+
+ In worst case (no compression performed) storage requirement is increased by
+ 1 byte to store header. If it exceeds field length, normal data truncation is
+ performed.
+
+ Generic compressed header format (1 byte):
+
+ Bits 1-4: method specific bits
+ Bits 5-8: compression method
+
+ If compression method is 0 then header is immediately followed by
+ uncompressed data.
+
+ If compression method is zlib:
+
+ Bits 1-3: number of bytes occupied by original data length
+ Bits 4: true if zlib wrapper not present
+ Bits 5-8: store 8 (zlib)
+
+ Header is immediately followed by original data length,
+ followed by compressed data.
+*/
+
+int Field_longstr::compress(char *to, uint to_length,
+ const char *from, uint length,
+ uint max_length,
+ uint *out_length,
+ CHARSET_INFO *cs, size_t nchars)
+{
+ THD *thd= get_thd();
+ char *buf;
+ uint buf_length;
+ int rc= 0;
+
+ if (String::needs_conversion_on_storage(length, cs, field_charset()) ||
+ max_length < length)
+ {
+ set_if_smaller(max_length, static_cast<ulonglong>(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, &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;
+}
+
+
+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,
+ 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_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 cmp_str_prefix(blob1, a_len, blob2, b_len, prefix_len,
+ field_charset());
+}
+
+
+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
+{
+ 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(),
+ &not_used, &not_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 ||
+ tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1))
+ {
+ tmp=0;
+ set_warning(WARN_DATA_TRUNCATED, 1);
+ err= 1;
+ }
+ }
+ else if (got_warning)
+ set_warning(WARN_DATA_TRUNCATED, 1);
+ store_type(tmp);
+ return err;
+}
+
+
+int Field_set::store(longlong nr, bool unsigned_val)
+{
+ 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;
+
+ /*
+ Some callers expect *val_buffer to contain the result,
+ so we assign to it, rather than doing 'return &empty_set_string.
+ */
+ *val_buffer= empty_set_string;
+ if (tmp == 0)
+ {
+ return val_buffer;
+ }
+
+ val_buffer->set_charset(field_charset());
+ val_buffer->length(0);
+
+ while (tmp && bitnr < (uint) typelib->count)
+ {
+ if (tmp & 1)
+ {
+ if (val_buffer->length())
+ val_buffer->append(&field_separator, 1, &my_charset_latin1);
+ String str(typelib->type_names[bitnr],
+ typelib->type_lengths[bitnr],
+ field_charset());
+ val_buffer->append(str);
+ }
+ tmp>>=1;
+ bitnr++;
+ }
+ return val_buffer;
+}
+
+
+void Field_set::sql_type(String &res) const
+{
+ char buffer[255];
+ String set_item(buffer, sizeof(buffer), res.charset());
+
+ res.length(0);
+ res.append(STRING_WITH_LEN("set("));
+
+ bool flag=0;
+ uint *len= typelib->type_lengths;
+ for (const char **pos= typelib->type_names; *pos; pos++, len++)
+ {
+ uint dummy_errors;
+ if (flag)
+ res.append(',');
+ /* convert to res.charset() == utf8, then quote */
+ set_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
+ append_unescaped(&res, set_item.ptr(), set_item.length());
+ flag= 1;
+ }
+ res.append(')');
+}
+
+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;
+}
+
+
+bool Field_enum::can_optimize_range(const Item_bool_func *cond,
+ const Item *item,
+ bool is_eq_func) const
+{
+ return item->cmp_type() != TIME_RESULT;
+}
+
+
+bool Field_enum::can_optimize_keypart_ref(const Item_bool_func *cond,
+ const Item *item) const
+{
+ switch (item->cmp_type())
+ {
+ case TIME_RESULT:
+ return false;
+ case INT_RESULT:
+ case DECIMAL_RESULT:
+ case REAL_RESULT:
+ return true;
+ case STRING_RESULT:
+ return charset() == cond->compare_collation();
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ break;
+ }
+ return false;
+}
+
+
+/*
+ Bit field.
+
+ We store the first 0 - 6 uneven bits among the null bits
+ at the start of the record. The rest bytes are stored in
+ the record itself.
+
+ For example:
+
+ CREATE TABLE t1 (a int, b bit(17), c bit(21) not null, d bit(8));
+ We would store data as follows in the record:
+
+ Byte Bit
+ 1 7 - reserve for delete
+ 6 - null bit for 'a'
+ 5 - null bit for 'b'
+ 4 - first (high) bit of 'b'
+ 3 - first (high) bit of 'c'
+ 2 - second bit of 'c'
+ 1 - third bit of 'c'
+ 0 - forth bit of 'c'
+ 2 7 - firth bit of 'c'
+ 6 - null bit for 'd'
+ 3 - 6 four bytes for 'a'
+ 7 - 8 two bytes for 'b'
+ 9 - 10 two bytes for 'c'
+ 11 one byte for 'd'
+*/
+
+Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
+ enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg)
+ : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg),
+ bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7),
+ bytes_in_rec(len_arg / 8)
+{
+ DBUG_ENTER("Field_bit::Field_bit");
+ DBUG_PRINT("enter", ("ptr_arg: %p, null_ptr_arg: %p, len_arg: %u, bit_len: %u, bytes_in_rec: %u",
+ ptr_arg, null_ptr_arg, len_arg, bit_len, bytes_in_rec));
+ flags|= UNSIGNED_FLAG;
+ /*
+ Ensure that Field::eq() can distinguish between two different bit fields.
+ (two bit fields that are not null, may have same ptr and null_ptr)
+ */
+ if (!null_ptr_arg)
+ null_bit= bit_ofs_arg;
+ DBUG_VOID_RETURN;
+}
+
+
+const DTCollation & Field_bit::dtcollation() const
+{
+ static DTCollation tmp(&my_charset_bin,
+ DERIVATION_IMPLICIT, MY_REPERTOIRE_UNICODE30);
+ return tmp;
+}
+
+
+void Field_bit::hash(ulong *nr, ulong *nr2)
+{
+ if (is_null())
+ {
+ *nr^= (*nr << 1) | 1;
+ }
+ else
+ {
+ CHARSET_INFO *cs= &my_charset_bin;
+ longlong value= Field_bit::val_int();
+ uchar tmp[8];
+ mi_int8store(tmp,value);
+ cs->hash_sort(tmp, 8, nr, nr2);
+ }
+}
+
+
+size_t
+Field_bit::do_last_null_byte() const
+{
+ /*
+ Code elsewhere is assuming that bytes are 8 bits, so I'm using
+ that value instead of the correct one: CHAR_BIT.
+
+ REFACTOR SUGGESTION (Matz): Change to use the correct number of
+ bits. On systems with CHAR_BIT > 8 (not very common), the storage
+ will lose the extra bits.
+ */
+ DBUG_PRINT("test", ("bit_ofs: %d, bit_len: %d bit_ptr: %p",
+ bit_ofs, bit_len, bit_ptr));
+ uchar *result;
+ if (bit_len == 0)
+ result= null_ptr;
+ else if (bit_ofs + bit_len > 8)
+ result= bit_ptr + 1;
+ else
+ result= bit_ptr;
+
+ if (result)
+ return (size_t) (result - table->record[0]) + 1;
+ return LAST_NULL_BYTE_UNDEF;
+}
+
+
+Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table,
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit)
+{
+ Field_bit *res;
+ if ((res= (Field_bit*) Field::new_key_field(root, new_table, new_ptr, length,
+ new_null_ptr, new_null_bit)))
+ {
+ /* Move bits normally stored in null_pointer to new_ptr */
+ res->bit_ptr= new_ptr;
+ res->bit_ofs= 0;
+ if (bit_len)
+ res->ptr++; // Store rest of data here
+ }
+ return res;
+}
+
+
+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_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 length) 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++;
+ length--;
+ }
+ 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,
+ CHARSET_INFO *cs,
+ column_definition_type_t type)
+{
+ DBUG_ASSERT(type_handler() == &type_handler_null);
+ DBUG_ASSERT(charset == &my_charset_bin || charset == NULL);
+ DBUG_ASSERT(length == 0);
+ DBUG_ASSERT(decimals == 0);
+
+ set_handler(def.type_handler());
+ return type_handler()->Column_definition_set_attributes(thd, this,
+ def, cs, type);
+}
+
+
+void
+Column_definition_attributes::set_length_and_dec(const Lex_length_and_dec_st
+ &type)
+{
+ if (type.length())
+ {
+ int err;
+ length= my_strtoll10(type.length(), NULL, &err);
+ if (err)
+ length= ~0ULL; // safety
+ }
+
+ if (type.dec())
+ decimals= (uint) atoi(type.dec());
+}
+
+
+void Column_definition::create_length_to_internal_length_bit()
+{
+ if (f_bit_as_char(pack_flag))
+ {
+ 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);
+ uint 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_GENERATED_VIRTUAL)
+ filter|= VCOL_NOT_VIRTUAL;
+
+ if (unlikely(ret || (res.errors & filter)))
+ {
+ my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
+ vcol_type_name(type), name->str);
+ return TRUE;
+ }
+ /*
+ Safe to call before fix_fields as long as vcol's don't include sub
+ queries (which is now checked in check_vcol_func_processor)
+ */
+ if (vcol->expr->check_cols(1))
+ return TRUE;
+ return FALSE;
+}
+
+
+bool Column_definition::check_length(uint mysql_errno, uint limit) const
+{
+ if (length <= limit)
+ return false;
+ my_error(mysql_errno, MYF(0), field_name.str, static_cast<ulong>(limit));
+ return true;
+}
+
+
+bool Column_definition::fix_attributes_int(uint default_length)
+{
+ if (length)
+ return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
+ length= default_length;
+ return false;
+}
+
+
+bool Column_definition::fix_attributes_real(uint default_length)
+{
+ /* change FLOAT(precision) to FLOAT or DOUBLE */
+ if (!length && !decimals)
+ {
+ length= default_length;
+ decimals= NOT_FIXED_DEC;
+ }
+ if (length < decimals && decimals != NOT_FIXED_DEC)
+ {
+ my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str);
+ return true;
+ }
+ if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS)
+ {
+ my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals),
+ field_name.str, static_cast<uint>(FLOATING_POINT_DECIMALS-1));
+ return true;
+ }
+ return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
+}
+
+
+bool Column_definition::fix_attributes_decimal()
+{
+ if (decimals >= NOT_FIXED_DEC)
+ {
+ my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals),
+ field_name.str, static_cast<uint>(NOT_FIXED_DEC - 1));
+ return true;
+ }
+ my_decimal_trim(&length, &decimals);
+ if (length > DECIMAL_MAX_PRECISION)
+ {
+ my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name.str,
+ DECIMAL_MAX_PRECISION);
+ return true;
+ }
+ if (length < decimals)
+ {
+ my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str);
+ return true;
+ }
+ length= my_decimal_precision_to_length((uint) length, decimals,
+ flags & UNSIGNED_FLAG);
+ pack_length= my_decimal_get_binary_size((uint) length, decimals);
+ return false;
+}
+
+
+bool Column_definition::fix_attributes_bit()
+{
+ if (!length)
+ length= 1;
+ pack_length= ((uint) length + 7) / 8;
+ return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_BIT_FIELD_LENGTH);
+}
+
+
+bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length)
+{
+ if (length > MAX_DATETIME_PRECISION)
+ {
+ my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name.str,
+ MAX_DATETIME_PRECISION);
+ return true;
+ }
+ 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 ((opt_explicit_defaults_for_timestamp ||
+ !is_timestamp_type()) && !vers_sys_field())
+ {
+ flags|= NO_DEFAULT_VALUE_FLAG;
+ }
+ }
+
+
+ if ((flags & AUTO_INCREMENT_FLAG) &&
+ !type_handler()->type_can_have_auto_increment_attribute())
+ {
+ my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE); /* success */
+}
+
+enum_field_types get_blob_type_from_length(ulong length)
+{
+ enum_field_types type;
+ if (length < 256)
+ type= MYSQL_TYPE_TINY_BLOB;
+ else if (length < 65536)
+ type= MYSQL_TYPE_BLOB;
+ else if (length < 256L*256L*256L)
+ type= MYSQL_TYPE_MEDIUM_BLOB;
+ else
+ type= MYSQL_TYPE_LONG_BLOB;
+ return type;
+}
+
+
+uint pack_length_to_packflag(uint type)
+{
+ switch (type) {
+ case 1: return f_settype((uint) MYSQL_TYPE_TINY);
+ case 2: return f_settype((uint) MYSQL_TYPE_SHORT);
+ case 3: return f_settype((uint) MYSQL_TYPE_INT24);
+ case 4: return f_settype((uint) MYSQL_TYPE_LONG);
+ case 8: return f_settype((uint) MYSQL_TYPE_LONGLONG);
+ }
+ return 0; // This shouldn't happen
+}
+
+
+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),
+ decimals(field->decimals()),
+ unireg_check(field->unireg_check),
+ interval(NULL),
+ charset(field->charset()), // May be NULL ptr
+ srid(0),
+ pack_flag(0)
+{}
+
+
+Column_definition_attributes::
+ Column_definition_attributes(const Type_all_attributes &attr)
+ :length(attr.max_length),
+ decimals(attr.decimals),
+ unireg_check(Field::NONE),
+ interval(attr.get_typelib()),
+ charset(attr.collation.collation),
+ srid(0),
+ pack_flag(attr.unsigned_flag ? 0 : FIELDFLAG_DECIMAL)
+{}
+
+
+/** Create a field suitable for create of table. */
+
+Column_definition::Column_definition(THD *thd, Field *old_field,
+ Field *orig_field)
+ :Column_definition_attributes(old_field)
+{
+ on_update= NULL;
+ field_name= old_field->field_name;
+ flags= old_field->flags;
+ pack_length=old_field->pack_length();
+ set_handler(old_field->type_handler());
+ comment= old_field->comment;
+ vcol_info= old_field->vcol_info;
+ option_list= old_field->option_list;
+ 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);
+
+ type_handler()->Column_definition_implicit_upgrade(this);
+
+ /*
+ 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 explicitely
+
+ This is:
+ - Not basic constants
+ - If field is a BLOB (Which doesn't support normal DEFAULT)
+*/
+
+bool Column_definition::has_default_expression()
+{
+ return (default_value &&
+ (!default_value->expr->basic_const_item() ||
+ (flags & BLOB_FLAG)));
+}
+
+
+bool Column_definition::set_compressed(const char *method)
+{
+ 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();
+ 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()));
+}
+
+
+/*
+ @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())
+ 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(STRING_WITH_LEN("NULL"));
+ 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;
+};