Server IP : 172.67.216.182 / Your IP : 172.68.242.43 Web Server : Apache System : Linux krdc-ubuntu-s-2vcpu-4gb-amd-blr1-01.localdomain 5.15.0-142-generic #152-Ubuntu SMP Mon May 19 10:54:31 UTC 2025 x86_64 User : www ( 1000) PHP Version : 7.4.33 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /www/server/mysql/src/sql/ |
Upload File : |
/* Copyright (c) 2000, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. 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, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file @brief This file implements classes defined in field.h */ #include "field.h" #include "filesort.h" // change_double_for_sort #include "item_timefunc.h" // Item_func_now_local #include "json_binary.h" // json_binary::serialize #include "json_dom.h" // Json_dom, Json_wrapper #include "item_json_func.h" // ensure_utf8mb4 #include "log_event.h" // class Table_map_log_event #include "rpl_rli.h" // Relay_log_info #include "rpl_slave.h" // rpl_master_has_bug #include "sql_class.h" // THD #include "sql_join_buffer.h" // CACHE_FIELD #include "sql_time.h" // str_to_datetime_with_warn #include "strfunc.h" // find_type2 #include "template_utils.h" // pointer_cast #include "tztime.h" // Time_zone #include "spatial.h" // Geometry #include "sql_base.h" // is_equal #include <algorithm> #include <memory> // auto_ptr using std::max; using std::min; #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") // Maximum allowed exponent value for converting string to decimal #define MAX_EXPONENT 1024 /** Static variables */ 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) - 1LL)) /* Rules for merging different types of fields in UNION NOTE: to avoid 256*256 table, gap in table types numeration is skiped following #defines describe that gap and how to canculate number of fields and index of field in thia array. */ #define FIELDTYPE_TEAR_FROM (MYSQL_TYPE_BIT + 1) #define FIELDTYPE_TEAR_TO (MYSQL_TYPE_JSON - 1) #define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO)) inline int field_type2index (enum_field_types field_type) { field_type= real_type_to_type(field_type); assert(field_type < FIELDTYPE_TEAR_FROM || field_type > FIELDTYPE_TEAR_TO); return (field_type < FIELDTYPE_TEAR_FROM ? field_type : ((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1); } static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= { /* MYSQL_TYPE_DECIMAL -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_DECIMAL, MYSQL_TYPE_DECIMAL, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_TINY -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_TINY, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_SHORT -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_SHORT, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_SHORT, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_SHORT, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_LONG -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_LONG, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_LONG, MYSQL_TYPE_LONG, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_LONG, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_LONGLONG, MYSQL_TYPE_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>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_FLOAT -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_DOUBLE, MYSQL_TYPE_FLOAT, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_FLOAT, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_FLOAT, MYSQL_TYPE_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>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_DOUBLE, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_DOUBLE -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_DOUBLE, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_DOUBLE, MYSQL_TYPE_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>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_DOUBLE, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_NULL -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_LONGLONG, MYSQL_TYPE_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>-<244> MYSQL_TYPE_BIT, //MYSQL_TYPE_JSON MYSQL_TYPE_JSON, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_ENUM, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY }, /* MYSQL_TYPE_TIMESTAMP -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_TIMESTAMP, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_LONGLONG -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_LONGLONG, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONGLONG, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_LONGLONG, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONG, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_LONGLONG, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_INT24 -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_INT24, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_INT24, MYSQL_TYPE_LONG, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_INT24, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_INT24, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_DATE -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_TIME -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIME, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_DATETIME -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_YEAR -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_YEAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_YEAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_NEWDATE -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_NEWDATE, MYSQL_TYPE_DATETIME, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_DATETIME, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_VARCHAR -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_BIT -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_BIT, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_BIT, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_JSON -> */ { //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_JSON, 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>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_JSON, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, 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_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_NEWDECIMAL -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_NEWDECIMAL, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_ENUM -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_ENUM, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_SET -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_SET, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_TINY_BLOB -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_JSON MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB }, /* MYSQL_TYPE_MEDIUM_BLOB -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_JSON MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB }, /* MYSQL_TYPE_LONG_BLOB -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_JSON MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB }, /* MYSQL_TYPE_BLOB -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_BLOB, //MYSQL_TYPE_JSON MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB }, /* MYSQL_TYPE_VAR_STRING -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_STRING -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_STRING, //MYSQL_TYPE_JSON MYSQL_TYPE_STRING, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_STRING, MYSQL_TYPE_STRING, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_STRING, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_STRING }, /* MYSQL_TYPE_GEOMETRY -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_GEOMETRY, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_BIT <16>-<244> MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_JSON MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY } }; /** Set field to temporary value NULL. */ void Field::set_tmp_null() { m_is_tmp_null= true; m_count_cuted_fields_saved= table ? table->in_use->count_cuted_fields : current_thd->count_cuted_fields; } /** Return type of which can carry value of both given types in UNION result. @param a type for merging @param b type for merging @return type of field */ enum_field_types Field::field_type_merge(enum_field_types a, enum_field_types b) { return field_types_merge_rules[field_type2index(a)] [field_type2index(b)]; } static Item_result field_types_result_type [FIELDTYPE_NUM]= { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY DECIMAL_RESULT, INT_RESULT, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG INT_RESULT, INT_RESULT, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE REAL_RESULT, REAL_RESULT, //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP STRING_RESULT, STRING_RESULT, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 INT_RESULT, INT_RESULT, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME STRING_RESULT, STRING_RESULT, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR STRING_RESULT, INT_RESULT, //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR STRING_RESULT, STRING_RESULT, //MYSQL_TYPE_BIT <16>-<244> STRING_RESULT, //MYSQL_TYPE_JSON STRING_RESULT, //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM DECIMAL_RESULT, STRING_RESULT, //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB STRING_RESULT, STRING_RESULT, //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB STRING_RESULT, STRING_RESULT, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING STRING_RESULT, STRING_RESULT, //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY STRING_RESULT, STRING_RESULT }; /** Convert Field::geometry_type to the corresponding Geometry::wkbType. @param t The geometry_type to convert @return The corresponding Geometry::wkbType, or Geometry::wkb_invalid_type if there's not suitable type. */ static Geometry::wkbType geometry_type_to_wkb_type(Field::geometry_type t) { switch(t) { case Field::GEOM_GEOMETRY: return Geometry::wkb_invalid_type; case Field::GEOM_POINT: return Geometry::wkb_point; case Field::GEOM_LINESTRING: return Geometry::wkb_linestring; case Field::GEOM_POLYGON: return Geometry::wkb_polygon; case Field::GEOM_MULTIPOINT: return Geometry::wkb_multipoint; case Field::GEOM_MULTILINESTRING: return Geometry::wkb_multilinestring; case Field::GEOM_MULTIPOLYGON: return Geometry::wkb_multipolygon; case Field::GEOM_GEOMETRYCOLLECTION: return Geometry::wkb_geometrycollection; default: assert(0); return Geometry::wkb_invalid_type; } } /* 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(const CHARSET_INFO *cs, const char *str, const char *strend) { if (cs != &my_charset_bin) str+= cs->cset->scan(cs, str, strend, MY_SEQ_SPACES); return (str < strend); } /** Function to compare two unsigned integers for their relative order. Used below. In an anonymous namespace to not clash with definitions in other files. */ namespace { int compare(unsigned int a, unsigned int b) { if (a < b) return -1; if (b < a) return 1; return 0; } } // namespace /** Detect Item_result by given field type of UNION merge result. @param field_type given field type @return Item_result (type of internal MySQL expression result) */ Item_result Field::result_merge_type(enum_field_types field_type) { return field_types_result_type[field_type2index(field_type)]; } /***************************************************************************** Static help functions *****************************************************************************/ /** Output a warning for erroneous conversion of strings to numerical values. For use with ER_TRUNCATED_WRONG_VALUE[_FOR_FIELD] @param thd THD object @param str pointer to string that failed to be converted @param length length of string @param cs charset for string @param typestr string describing type converted to @param error error value to output @param field_name (for *_FOR_FIELD) name of field @param row_num (for *_FOR_FIELD) row number */ static void push_numerical_conversion_warning(THD* thd, const char* str, uint length, const CHARSET_INFO* cs, const char* typestr, int error, const char* field_name="UNKNOWN", ulong row_num=0) { char buf[MY_MAX(MY_MAX(DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE, LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE), DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE)]; String tmp(buf, sizeof(buf), cs); tmp.copy(str, length, cs); push_warning_printf(thd, Sql_condition::SL_WARNING, error, ER(error), typestr, tmp.c_ptr(), field_name, row_num); } /** Emits a warning for the decimal conversion error. May modify dec_value if there was conversion overflow or bad number. @param dec_error decimal library return code (E_DEC_* see include/decimal.h) @param dec_value[in,out] Decimal value returned by convertion function. @param from Value converted from @param length Length of 'from' @param charset_arg Charset of 'from' */ static void set_decimal_warning(Field_new_decimal *field, int dec_error, my_decimal *dec_value, const char *from, size_t length, const CHARSET_INFO *charset_arg) { switch (dec_error) { case E_DEC_TRUNCATED: field->set_warning(Sql_condition::SL_NOTE, WARN_DATA_TRUNCATED, 1); break; case E_DEC_OVERFLOW: field->set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); field->set_value_on_overflow(dec_value, dec_value->sign()); break; case E_DEC_BAD_NUM: ErrConvString errmsg(from, length, charset_arg); const Diagnostics_area *da= field->table->in_use->get_stmt_da(); push_warning_printf(field->table->in_use, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "decimal", errmsg.ptr(), field->field_name, da->current_row_for_condition()); my_decimal_set_zero(dec_value); } } /** Copy string with optional character set conversion. This calls helper function well_formed_copy_nchars to copy string with optional character set convertion. Specially, it checks if the ASCII code point exceeds code range. If YES, it allows the input but raises a warning. @param to_cs Character set of "to" string @param to Store result here @param to_length Maxinum length of "to" string @param from_cs From character set @param from Copy from here @param from_length Length of from string @param nchars Copy not more that nchars characters @param well_formed_error_pos Return position when "from" is not well formed or NULL otherwise. @param cannot_convert_error_pos Return position where a not convertable character met, or NULL otherwise. @param from_end_pos Return position where scanning of "from" string stopped. @retval length of bytes copied to 'to' */ size_t field_well_formed_copy_nchars(const CHARSET_INFO *to_cs, char *to, size_t to_length, const CHARSET_INFO *from_cs, const char *from, size_t from_length, size_t nchars, const char **well_formed_error_pos, const char **cannot_convert_error_pos, const char **from_end_pos) { size_t res = well_formed_copy_nchars(to_cs, to, to_length, from_cs, from, from_length, nchars, well_formed_error_pos, cannot_convert_error_pos, from_end_pos); /* If the code point is out of ascii range, we only give user a warning in 5.7. Need to change to give a ERROR in future version. */ if ((to_cs->state & MY_CS_PUREASCII) && *well_formed_error_pos != NULL) { char tmp[32]; *well_formed_error_pos = NULL; convert_to_printable(tmp, sizeof(tmp), from, from_length, from_cs, 6); push_warning_printf(current_thd, Sql_condition::SL_WARNING, ER_INVALID_CHARACTER_STRING, ER_THD(current_thd, ER_INVALID_CHARACTER_STRING), "ascii", tmp); } return res; } /** Check whether a field type can be partially indexed by a key. This is a static method, rather than a virtual function, because we need to check the type of a non-Field in mysql_alter_table(). @param type field type @retval TRUE Type can have a prefixed key @retval FALSE Type can not have a prefixed key */ bool Field::type_can_have_key_part(enum enum_field_types type) { switch (type) { case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: case MYSQL_TYPE_GEOMETRY: return TRUE; default: return FALSE; } } /** Numeric fields base class constructor. */ Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, uint8 dec_arg, bool zero_arg, bool unsigned_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg) { if (zerofill) flags|=ZEROFILL_FLAG; if (unsigned_flag) flags|=UNSIGNED_FLAG; } void Field_num::prepend_zeros(String *value) { int diff; if ((diff= (int) (field_length - value->length())) > 0) { const bool error= value->mem_realloc(field_length); if (!error) { memmove(const_cast<char*>(value->ptr()) + field_length - value->length(), value->ptr(), value->length()); memset(const_cast<char*>(value->ptr()), '0', diff); value->length(field_length); } } } /** Test if given number is a int. @todo Make this multi-byte-character safe @param str String to test @param length Length of 'str' @param int_end Pointer to char after last used digit @param cs Character set @note This is called after one has called strntoull10rnd() function. @return TYPE_OK, TYPE_ERR_BAD_VALUE or TYPE_WARN_TRUNCATED */ type_conversion_status Field_num::check_int(const CHARSET_INFO *cs, const char *str, size_t length, const char *int_end, int error) { /* Test if we get an empty string or wrong integer */ if (str == int_end || error == MY_ERRNO_EDOM) { ErrConvString err(str, length, cs); push_warning_printf(table->in_use, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "integer", err.ptr(), field_name, table->in_use->get_stmt_da()-> current_row_for_condition()); return TYPE_ERR_BAD_VALUE; } /* Test if we have garbage at the end of the given string. */ if (test_if_important_data(cs, int_end, str + length)) { set_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); return TYPE_WARN_TRUNCATED; } return TYPE_OK; } /* Conver a string to an integer then check bounds. SYNOPSIS Field_num::get_int cs Character set from String to convert len Length of the string rnd OUT longlong value unsigned_max max unsigned value signed_min min signed value signed_max max signed value DESCRIPTION The function calls strntoull10rnd() to get an integer value then check bounds and errors returned. In case of any error a warning is raised. @return TYPE_OK, TYPE_WARN_OUT_OF_RANGE, TYPE_ERR_BAD_VALUE or TYPE_WARN_TRUNCATED */ type_conversion_status Field_num::get_int(const CHARSET_INFO *cs, const char *from, size_t len, longlong *rnd, ulonglong unsigned_max, longlong signed_min, longlong signed_max) { char *end; int error; *rnd= (longlong) cs->cset->strntoull10rnd(cs, from, len, unsigned_flag, &end, &error); if (unsigned_flag) { if ((((ulonglong) *rnd > unsigned_max) && (*rnd= (longlong) unsigned_max)) || error == MY_ERRNO_ERANGE) goto out_of_range; } else { if (*rnd < signed_min) { *rnd= signed_min; goto out_of_range; } else if (*rnd > signed_max) { *rnd= signed_max; goto out_of_range; } } if (table->in_use->count_cuted_fields != 0) return check_int(cs, from, len, end, error); return TYPE_OK; out_of_range: set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return TYPE_WARN_OUT_OF_RANGE; } /* This is a generic method which is executed only for Field_short, Field_medium, Field_long, Field_longlong and Field_tiny. The other field types that come from Field_num override this method: Field_real (common parent for Field_decimal, Field_float, Field_double), Field_new_decimal, Field_year. */ type_conversion_status Field_num::store_time(MYSQL_TIME *ltime, uint8 dec_arg MY_ATTRIBUTE((unused))) { longlong nr= TIME_to_ulonglong_round(ltime); return store(ltime->neg ? -nr : nr, 0); } /** 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 0 No error or some other errors except overflow @retval 1 There was overflow */ bool Field::warn_if_overflow(int op_result) { if (op_result == E_DEC_OVERFLOW) { set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return true; } if (op_result == E_DEC_TRUNCATED) { set_warning(Sql_condition::SL_NOTE, WARN_DATA_TRUNCATED, 1); /* We return 0 here as this is not a critical issue */ } return false; } /** Interpret field value as an integer but return the result as a string. This is used for printing bit_fields as numbers while debugging. */ String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_READ; const CHARSET_INFO *cs= &my_charset_bin; uint length; longlong value= val_int(); if (val_buffer->alloc(MY_INT64_NUM_DECIMAL_DIGITS)) return 0; length= (uint) (*cs->cset->longlong10_to_str)(cs, (char*) val_buffer->ptr(), MY_INT64_NUM_DECIMAL_DIGITS, unsigned_val ? 10 : -10, value); val_buffer->length(length); return val_buffer; } /// This is used as a table name when the table structure is not set up Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg) :ptr(ptr_arg), m_null_ptr(null_ptr_arg), m_is_tmp_nullable(false), m_is_tmp_null(false), table(0), orig_table(0), table_name(0), field_name(field_name_arg), unireg_check(unireg_check_arg), field_length(length_arg), null_bit(null_bit_arg), is_created_from_null_item(FALSE), m_indexed(false), m_warnings_pushed(0), gcol_info(0), stored_in_db(TRUE) { flags=real_maybe_null() ? 0: NOT_NULL_FLAG; comment.str= (char*) ""; comment.length=0; field_index= 0; } /** Check NOT NULL constraint on the field after temporary nullability is disabled. @param warning_no Warning to report. @return TYPE_OK if the value is Ok, or corresponding error code from the type_conversion_status enum. */ type_conversion_status Field::check_constraints(int mysql_errno) { /* Ensure that Field::check_constraints() is called only when temporary nullability is disabled. */ assert(!is_tmp_nullable()); if (real_maybe_null()) return TYPE_OK; // If the field is nullable, we're Ok. if (!m_is_tmp_null) return TYPE_OK; // If the field was not NULL, we're Ok. // The field has been set to NULL. /* If the field is of AUTO_INCREMENT, and the next number has been assigned to it, we're Ok. */ if (this == table->next_number_field) return TYPE_OK; /* If the field is of TIMESTAMP its default value is CURRENT_TIMESTAMP and was set before calling this method. Therefore m_is_tmp_null == false for such field and we leave check_constraints() before this assert is fired. */ assert (type() != MYSQL_TYPE_TIMESTAMP); switch (m_count_cuted_fields_saved) { case CHECK_FIELD_WARN: set_warning(Sql_condition::SL_WARNING, mysql_errno, 1); /* fall through */ case CHECK_FIELD_IGNORE: return TYPE_OK; case CHECK_FIELD_ERROR_FOR_NULL: if (!table->in_use->no_errors) my_error(ER_BAD_NULL_ERROR, MYF(0), field_name); return TYPE_ERR_NULL_CONSTRAINT_VIOLATION; } assert(0); // impossible return TYPE_ERR_NULL_CONSTRAINT_VIOLATION; } /** Set field to value NULL. @param row_offset This is the offset between the row being updated and table->record[0] */ void Field::set_null(my_ptrdiff_t row_offset) { if (real_maybe_null()) { m_null_ptr[row_offset]|= null_bit; } else if (is_tmp_nullable()) { set_tmp_null(); } } /** Set field to value NOT NULL. @param row_offset This is the offset between the row being updated and table->record[0] */ void Field::set_notnull(my_ptrdiff_t row_offset) { if (real_maybe_null()) { m_null_ptr[row_offset]&= (uchar) ~null_bit; } else if (is_tmp_nullable()) { reset_tmp_null(); } } void Field::hash(ulong *nr, ulong *nr2) { if (is_null()) { *nr^= (*nr << 1) | 1; } else { uint len= pack_length(); const CHARSET_INFO *cs= sort_charset(); cs->coll->hash_sort(cs, ptr, len, nr, nr2); } } size_t Field::do_last_null_byte() const { assert(!real_maybe_null() || m_null_ptr >= table->record[0]); return real_maybe_null() ? null_offset() + 1 : (size_t) LAST_NULL_BYTE_UNDEF; } void Field::copy_data(my_ptrdiff_t src_record_offset) { memcpy(ptr, ptr + src_record_offset, pack_length()); if (real_maybe_null()) { // Set to NULL if the source record is NULL, otherwise set to NOT-NULL. m_null_ptr[0]= (m_null_ptr[0] & ~null_bit) | (m_null_ptr[src_record_offset] & null_bit); } else if (is_tmp_nullable()) m_is_tmp_null= false; } bool Field::send_text(Protocol *protocol) { if (is_null()) return protocol->store_null(); char buff[MAX_FIELD_WIDTH]; String str(buff, sizeof(buff), &my_charset_bin); #ifndef NDEBUG my_bitmap_map *old_map= 0; if (table->file) old_map= dbug_tmp_use_all_columns(table, table->read_set); #endif String *res= val_str(&str); #ifndef NDEBUG if (old_map) dbug_tmp_restore_column_map(table->read_set, old_map); #endif return res ? protocol->store(res) : protocol->store_null(); } bool Field::send_binary(Protocol *protocol) { char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),charset()); if (is_null()) return protocol->store_null(); String *res= val_str(&tmp); return res ? protocol->store(res) : protocol->store_null(); } /** Check to see if field size is compatible with destination. This method is used in row-based replication to verify that the slave's field size is less than or equal to the master's field size. The encoded field metadata (from the master or source) is decoded and compared to the size of this field (the slave or destination). @note The comparison is made so that if the source data (from the master) is less than the target data (on the slave), -1 is returned in @c <code>*order_var</code>. This implies that a conversion is necessary, but that it is lossy and can result in truncation of the value. If the source data is strictly greater than the target data, 1 is returned in <code>*order_var</code>. This implies that the source type can is contained in the target type and that a conversion is necessary but is non-lossy. If no conversion is required to fit the source type in the target type, 0 is returned in <code>*order_var</code>. @param field_metadata Encoded size in field metadata @param mflags Flags from the table map event for the table. @param order_var Pointer to variable where the order between the source field and this field will be returned. @return @c true if this field's size is compatible with the master's field size, @c false otherwise. */ bool Field::compatible_field_size(uint field_metadata, Relay_log_info *rli_arg MY_ATTRIBUTE((unused)), uint16 mflags MY_ATTRIBUTE((unused)), int *order_var) { uint const source_size= pack_length_from_metadata(field_metadata); uint const destination_size= row_pack_length(); DBUG_PRINT("debug", ("real_type: %d, source_size: %u, destination_size: %u", real_type(), source_size, destination_size)); *order_var = compare(source_size, destination_size); return true; } type_conversion_status Field::store(const char *to, size_t length, const CHARSET_INFO *cs, enum_check_fields check_level) { enum_check_fields old_check_level= table->in_use->count_cuted_fields; table->in_use->count_cuted_fields= check_level; const type_conversion_status res= store(to, length, cs); table->in_use->count_cuted_fields= old_check_level; return res; } /** Pack the field into a format suitable for storage and transfer. To implement packing functionality, only the virtual function should be overridden. The other functions are just convenience functions and hence should not be overridden. The value of <code>low_byte_first</code> is dependent on how the packed data is going to be used: for local use, e.g., temporary store on disk or in memory, use the native format since that is faster. For data that is going to be transfered to other machines (e.g., when writing data to the binary log), data should always be stored in little-endian format. @note The default method for packing fields just copy the raw bytes of the record into the destination, but never more than <code>max_length</code> characters. @param to Pointer to memory area where representation of field should be put. @param from Pointer to memory area where record representation of field is stored. @param max_length Maximum length of the field, as given in the column definition. For example, for <code>CHAR(1000)</code>, the <code>max_length</code> is 1000. This information is sometimes needed to decide how to pack the data. @param low_byte_first @c TRUE if integers should be stored little-endian, @c FALSE if native format should be used. Note that for little-endian machines, the value of this flag is a moot point since the native format is little-endian. */ uchar * Field::pack(uchar *to, const uchar *from, uint max_length, bool low_byte_first MY_ATTRIBUTE((unused))) { uint32 length= pack_length(); set_if_smaller(length, max_length); memcpy(to, from, length); return to+length; } /** Unpack a field from row data. This method is used to unpack a field from a master whose size of the field is less than that of the slave. The <code>param_data</code> parameter is a two-byte integer (stored in the least significant 16 bits of the unsigned integer) usually consisting of two parts: the real type in the most significant byte and a original pack length in the least significant byte. The exact layout of the <code>param_data</code> field is given by the <code>Table_map_log_event::save_field_metadata()</code>. This is the default method for unpacking a field. It just copies the memory block in byte order (of original pack length bytes or length of field, whichever is smaller). @param to Destination of the data @param from Source of the data @param param_data Real type and original pack length of the field data @param low_byte_first If this flag is @c true, all composite entities (e.g., lengths) should be unpacked in little-endian format; otherwise, the entities are unpacked in native order. @return New pointer into memory based on from + length of the data */ const uchar * Field::unpack(uchar* to, const uchar *from, uint param_data, bool low_byte_first MY_ATTRIBUTE((unused))) { uint length=pack_length(); int from_type= 0; /* If from length is > 255, it has encoded data in the upper bits. Need to mask it out. */ if (param_data > 255) { from_type= (param_data & 0xff00) >> 8U; // real_type. param_data= param_data & 0x00ff; // length. } if ((param_data == 0) || (length == param_data) || (from_type != real_type())) { memcpy(to, from, length); return from+length; } uint len= (param_data && (param_data < length)) ? param_data : length; memcpy(to, from, param_data > length ? length : len); return from+len; } void Field_num::add_zerofill_and_unsigned(String &res) const { if (unsigned_flag) res.append(STRING_WITH_LEN(" unsigned")); if (zerofill) res.append(STRING_WITH_LEN(" zerofill")); } void Field::make_field(Send_field *field) { if (orig_table && orig_table->s->db.str && *orig_table->s->db.str) { field->db_name= orig_table->s->db.str; if (orig_table->pos_in_table_list && orig_table->pos_in_table_list->schema_table) field->org_table_name= (orig_table->pos_in_table_list-> schema_table->table_name); else field->org_table_name= orig_table->s->table_name.str; } else field->org_table_name= field->db_name= ""; if (orig_table && orig_table->alias) { field->table_name= orig_table->alias; field->org_col_name= field_name; } else { field->table_name= ""; field->org_col_name= ""; } field->col_name= field_name; field->charsetnr= charset()->number; field->length=field_length; field->type=type(); field->flags=table->is_nullable() ? (flags & ~NOT_NULL_FLAG) : flags; field->decimals= decimals(); field->field= false; } /** Conversion from decimal to longlong. Checks overflow and returns correct value (min/max) in case of overflow. @param val value to be converted @param unsigned_flag type of integer to which we convert val @param has_overflow true if there is overflow @return value converted from val */ longlong Field::convert_decimal2longlong(const my_decimal *val, bool unsigned_flag, bool *has_overflow) { if (unsigned_flag && val->sign()) { // Converting a signed decimal to unsigned int set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); *has_overflow= true; return 0; } longlong val_ll; int conversion_error= my_decimal2int(E_DEC_ERROR & ~E_DEC_OVERFLOW & ~E_DEC_TRUNCATED, val, unsigned_flag, &val_ll); if (warn_if_overflow(conversion_error)) { *has_overflow= true; if (unsigned_flag) return ULLONG_MAX; return (val->sign() ? LLONG_MIN : LLONG_MAX); } return val_ll; } /** Storing decimal in integer fields. @param val value for storing @note This method is used by all integer fields, real/decimal redefine it @retval TYPE_OK Storage of value went fine without warnings or errors @retval !TYPE_OK Warning/error as indicated by type_conversion_status enum value */ type_conversion_status Field_num::store_decimal(const my_decimal *val) { ASSERT_COLUMN_MARKED_FOR_WRITE; bool has_overflow= false; longlong i= convert_decimal2longlong(val, unsigned_flag, &has_overflow); const type_conversion_status res= store(i, unsigned_flag); return has_overflow ? TYPE_WARN_OUT_OF_RANGE : res; } /** Return decimal value of integer field. @param decimal_value buffer for storing decimal value @note This method is used by all integer fields, real/decimal redefine it. All longlong values fit in our decimal buffer which cal store 8*9=72 digits of integer number @return pointer to decimal buffer with value of field */ my_decimal* Field_num::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; assert(result_type() == INT_RESULT); longlong nr= val_int(); int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value); return decimal_value; } bool Field_num::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { assert(result_type() == INT_RESULT); return my_longlong_to_datetime_with_warn(val_int(), ltime, fuzzydate); } bool Field_num::get_time(MYSQL_TIME *ltime) { assert(result_type() == INT_RESULT); return my_longlong_to_time_with_warn(val_int(), ltime); } Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, const CHARSET_INFO *charset_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) { field_charset= charset_arg; if (charset_arg->state & MY_CS_BINSORT) flags|=BINARY_FLAG; field_derivation= DERIVATION_IMPLICIT; } void Field_str::make_field(Send_field *field) { Field::make_field(field); field->decimals= 0; } /** Decimal representation of Field_str. @param d value for storing @note Field_str is the base class for fields implemeting [VAR]CHAR, VAR[BINARY], BLOB/TEXT, GEOMETRY, JSON. 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 */ type_conversion_status Field_str::store_decimal(const my_decimal *d) { ASSERT_COLUMN_MARKED_FOR_WRITE; double val; /* TODO: use decimal2string? */ int err= my_decimal2double(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, d, &val); warn_if_overflow(err); const type_conversion_status res= store(val); return (err != E_DEC_OK) ? decimal_err_to_type_conv_status(err) : res; } uint Field::fill_cache_field(CACHE_FIELD *copy) { uint store_length; copy->str= ptr; copy->length= pack_length(); copy->field= this; if (flags & BLOB_FLAG) { copy->type= CACHE_BLOB; copy->length-= portable_sizeof_char_ptr; return copy->length; } else if (!zero_pack() && (type() == MYSQL_TYPE_STRING && copy->length >= 4 && copy->length < 256)) { copy->type= CACHE_STRIPPED; /* Remove end space */ store_length= 2; } else if (type() == MYSQL_TYPE_VARCHAR) { copy->type= pack_length()-row_pack_length() == 1 ? CACHE_VARSTR1: CACHE_VARSTR2; store_length= 0; } else { copy->type= 0; store_length= 0; } return copy->length + store_length; } bool Field::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { char buff[MAX_DATE_STRING_REP_LENGTH]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; return !(res= val_str(&tmp)) || str_to_datetime_with_warn(res, ltime, fuzzydate); } bool Field::get_time(MYSQL_TIME *ltime) { char buff[MAX_DATE_STRING_REP_LENGTH]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; return !(res= val_str(&tmp)) || str_to_time_with_warn(res, ltime); } bool Field::get_timestamp(struct timeval *tm, int *warnings) { MYSQL_TIME ltime; assert(!is_null()); return get_date(<ime, TIME_FUZZY_DATE) || datetime_to_timeval(current_thd, <ime, tm, warnings); } /** This is called when storing a date in a string. @note Needs to be changed if/when we want to support different time formats. */ type_conversion_status Field::store_time(MYSQL_TIME *ltime, uint8 dec_arg) { ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[MAX_DATE_STRING_REP_LENGTH]; uint length= (uint) my_TIME_to_str(ltime, buff, MY_MIN(dec_arg, DATETIME_MAX_DECIMALS)); /* 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) { return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE); } Field *Field::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type MY_ATTRIBUTE((unused))) { Field *tmp= clone(root); if (tmp == NULL) return 0; if (tmp->table->is_nullable()) tmp->flags&= ~NOT_NULL_FLAG; tmp->table= new_table; tmp->key_start.init(0); tmp->part_of_key.init(0); tmp->part_of_sortkey.init(0); tmp->m_indexed= false; /* todo: We should never alter unireg_check after an object is constructed, and the member should be made const. But a lot of code depends upon this hack, and the different utype values are completely unrelated so we can never be quite sure which parts of the server will break. */ tmp->unireg_check= Field::NONE; tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); tmp->reset_fields(); return tmp; } Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit) { Field *tmp; if ((tmp= new_field(root, new_table, table == new_table))) { tmp->ptr= new_ptr; tmp->m_null_ptr= new_null_ptr; tmp->null_bit= new_null_bit; } return tmp; } void Field::evaluate_insert_default_function() { if (has_insert_default_function()) Item_func_now_local::store_in(this); } void Field::evaluate_update_default_function() { if (has_update_default_function()) Item_func_now_local::store_in(this); } /**************************************************************************** Field_null, a field that always return NULL ****************************************************************************/ void Field_null::sql_type(String &res) const { res.set_ascii(STRING_WITH_LEN("null")); } /**************************************************************************** Functions for the Field_decimal class This is an number stored as a pre-space (or pre-zero) string ****************************************************************************/ type_conversion_status Field_decimal::reset(void) { Field_decimal::store(STRING_WITH_LEN("0"),&my_charset_bin); return TYPE_OK; } void Field_decimal::overflow(bool negative) { uint len=field_length; uchar *to=ptr, filler= '9'; set_warning(Sql_condition::SL_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 memset(to, ' ', whole_part); to+= whole_part; len-= whole_part; // The main code will also handle the 0 before the decimal point } } } memset(to, filler, len); if (dec) ptr[field_length-dec-1]='.'; return; } type_conversion_status Field_decimal::store(const char *from_arg, size_t len, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[STRING_BUFFER_USUAL_SIZE]; String tmp(buff,sizeof(buff), &my_charset_bin); const uchar *from= (uchar*) from_arg; /* Convert character set if the old one is multi uchar */ if (cs->mbmaxlen > 1) { uint dummy_errors; tmp.copy((char*) from, len, cs, &my_charset_bin, &dummy_errors); from= (uchar*) tmp.ptr(); len= tmp.length(); } const uchar *end= from+len; /* The pointer where the field value starts (i.e., "where to write") */ uchar *to= ptr; uint tmp_dec, tmp_uint; /* The sign of the number : will be 0 (means positive but sign not specified), '+' or '-' */ uchar sign_char=0; /* The pointers where prezeros start and stop */ const uchar *pre_zeros_from, *pre_zeros_end; /* The pointers where digits at the left of '.' start and stop */ const uchar *int_digits_from, *int_digits_end; /* The pointers where digits at the right of '.' start and stop */ const uchar *frac_digits_from, *frac_digits_end; /* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */ char expo_sign_char=0; uint exponent=0; // value of the exponent /* Pointers used when digits move from the left of the '.' to the right of the '.' (explained below) */ const uchar *int_digits_tail_from= NULL; /* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */ uint int_digits_added_zeros= 0; /* Pointer used when digits move from the right of the '.' to the left of the '.' */ const uchar *frac_digits_head_end= NULL; /* Number of 0 that need to be added at the right of the '.' (for 1E-3) */ uint frac_digits_added_zeros= 0; uchar *pos,*tmp_left_pos,*tmp_right_pos; /* Pointers that are used as limits (begin and end of the field buffer) */ uchar *left_wall,*right_wall; uchar tmp_char; /* To remember if table->in_use->cuted_fields has already been incremented, to do that only once */ bool is_cuted_fields_incr=0; /* There are three steps in this function : - parse the input string - modify the position of digits around the decimal dot '.' according to the exponent value (if specified) - write the formatted number */ if ((tmp_dec=dec)) tmp_dec++; /* skip pre-space */ while (from != end && my_isspace(&my_charset_bin,*from)) from++; if (from == end) { set_warning(Sql_condition::SL_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 TYPE_WARN_OUT_OF_RANGE; } } } pre_zeros_from= from; for (; from!=end && *from == '0'; from++) ; // Read prezeros pre_zeros_end=int_digits_from=from; /* Read non zero digits at the left of '.'*/ for (; from != end && my_isdigit(&my_charset_bin, *from) ; from++) ; int_digits_end=from; if (from!=end && *from == '.') // Some '.' ? from++; frac_digits_from= from; /* Read digits at the right of '.' */ for (;from!=end && my_isdigit(&my_charset_bin, *from); from++) ; frac_digits_end=from; // Some exponentiation symbol ? if (from != end && (*from == 'e' || *from == 'E')) { from++; if (from != end && (*from == '+' || *from == '-')) // Some exponent sign ? expo_sign_char= *from++; else expo_sign_char= '+'; /* Read digits of the exponent and compute its value. We must care about 'exponent' overflow, because as unsigned arithmetic is "modulo", big exponents will become small (e.g. 1e4294967296 will become 1e0, and the field will finally contain 1 instead of its max possible value). */ for (;from!=end && my_isdigit(&my_charset_bin, *from); from++) { exponent=10*exponent+(*from-'0'); if (exponent>MAX_EXPONENT) break; } } /* We only have to generate warnings if count_cuted_fields is set. This is to avoid extra checks of the number when they are not needed. Even if this flag is not set, it's OK to increment warnings, if it makes the code easer to read. */ if (table->in_use->count_cuted_fields) { // Skip end spaces for (;from != end && my_isspace(&my_charset_bin, *from); from++) ; if (from != end) // If still something left, warn { set_warning(Sql_condition::SL_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=min(exponent,(uint)(int_digits_end-int_digits_from)); frac_digits_added_zeros=exponent-tmp_uint; int_digits_end -= tmp_uint; frac_digits_head_end=int_digits_end+tmp_uint; tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from); } else // (expo_sign_char=='+') { tmp_uint=min(exponent,(uint)(frac_digits_end-frac_digits_from)); int_digits_added_zeros=exponent-tmp_uint; int_digits_tail_from=frac_digits_from; frac_digits_from=frac_digits_from+tmp_uint; /* We "eat" the heading zeros of the int_digits.int_digits_tail.int_digits_added_zeros concatenation (for example 0.003e3 must become 3 and not 0003) */ if (int_digits_from == int_digits_end) { /* There was nothing in the int_digits part, so continue eating int_digits_tail zeros */ for (; int_digits_tail_from != frac_digits_from && *int_digits_tail_from == '0'; int_digits_tail_from++) ; if (int_digits_tail_from == frac_digits_from) { // there were only zeros in int_digits_tail too int_digits_added_zeros=0; } } tmp_uint= (uint) (tmp_dec+(int_digits_end-int_digits_from)+ (uint)(frac_digits_from-int_digits_tail_from)+ int_digits_added_zeros); } /* Now write the formated number First the digits of the int_% parts. Do we have enough room to write these digits ? If the sign is defined and '-', we need one position for it */ if (field_length < tmp_uint + (int) (sign_char == '-')) { // too big number, change to max or min number Field_decimal::overflow(sign_char == '-'); return TYPE_WARN_OUT_OF_RANGE; } /* Tmp_left_pos is the position where the leftmost digit of the int_% parts will be written */ tmp_left_pos=pos=to+(uint)(field_length-tmp_uint); // Write all digits of the int_% parts while (int_digits_from != int_digits_end) *pos++ = *int_digits_from++ ; if (expo_sign_char == '+') { while (int_digits_tail_from != frac_digits_from) *pos++= *int_digits_tail_from++; while (int_digits_added_zeros-- >0) *pos++= '0'; } /* Note the position where the rightmost digit of the int_% parts has been written (this is to later check if the int_% parts contained nothing, meaning an extra 0 is needed). */ tmp_right_pos=pos; /* Step back to the position of the leftmost digit of the int_% parts, to write sign and fill with zeros or blanks or prezeros. */ pos=tmp_left_pos-1; if (zerofill) { left_wall=to-1; while (pos > left_wall) // Fill with zeros *pos--='0'; } else { left_wall=to+(sign_char != 0)-1; if (!expo_sign_char) // If exponent was specified, ignore prezeros { for (;pos > left_wall && pre_zeros_from !=pre_zeros_end; pre_zeros_from++) *pos--= '0'; } if (pos == tmp_right_pos-1) *pos--= '0'; // no 0 has ever been written, so write one left_wall= to-1; if (sign_char && pos != left_wall) { /* Write sign if possible (it is if sign is '-') */ *pos--= sign_char; } while (pos != left_wall) *pos--=' '; //fill with blanks } /* Write digits of the frac_% parts ; Depending on table->in_use->count_cutted_fields, we may also want to know if some non-zero tail of these parts will be truncated (for example, 0.002->0.00 will generate a warning, while 0.000->0.00 will not) (and 0E1000000000 will not, while 1E-1000000000 will) */ pos=to+(uint)(field_length-tmp_dec); // Calculate post to '.' right_wall=to+field_length; if (pos != right_wall) *pos++='.'; if (expo_sign_char == '-') { while (frac_digits_added_zeros-- > 0) { if (pos == right_wall) { if (table->in_use->count_cuted_fields && !is_cuted_fields_incr) break; // Go on below to see if we lose non zero digits return TYPE_OK; } *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(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); return TYPE_OK; } continue; } *pos++= tmp_char; } } for (;frac_digits_from!=frac_digits_end;) { tmp_char= *frac_digits_from++; if (pos == right_wall) { if (tmp_char != '0') // Losing a non zero digit ? { if (!is_cuted_fields_incr) { /* This is a note, not a warning, as we don't want to abort when we cut decimals in strict mode */ set_warning(Sql_condition::SL_NOTE, WARN_DATA_TRUNCATED, 1); } return TYPE_OK; } continue; } *pos++= tmp_char; } while (pos != right_wall) *pos++='0'; // Fill with zeros at right of '.' return TYPE_OK; } type_conversion_status Field_decimal::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; if (unsigned_flag && nr < 0) { overflow(1); return TYPE_WARN_OUT_OF_RANGE; } if (!my_isfinite(nr)) // Handle infinity as special case { overflow(nr < 0.0); return TYPE_WARN_OUT_OF_RANGE; } size_t i; size_t length; uchar fyllchar,*to; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; fyllchar = zerofill ? '0' : ' '; length= my_fcvt(nr, dec, buff, NULL); if (length > field_length) { overflow(nr < 0.0); return TYPE_WARN_OUT_OF_RANGE; } else { to=ptr; for (i=field_length-length ; i-- > 0 ;) *to++ = fyllchar; memcpy(to,buff,length); return TYPE_OK; } } type_conversion_status Field_decimal::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[22]; uint length, int_part; char fyllchar; uchar *to; if (nr < 0 && unsigned_flag && !unsigned_val) { overflow(1); return TYPE_WARN_OUT_OF_RANGE; } 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 TYPE_WARN_OUT_OF_RANGE; } fyllchar = zerofill ? '0' : ' '; to= ptr; for (uint i=int_part-length ; i-- > 0 ;) *to++ = fyllchar; memcpy(to,buff,length); if (dec) { to[length]='.'; memset(to + length + 1, '0', dec); } return TYPE_OK; } double Field_decimal::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *end_not_used; return my_strntod(&my_charset_bin, (char*) ptr, field_length, &end_not_used, ¬_used); } longlong Field_decimal::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; int not_used; if (unsigned_flag) return my_strntoull(&my_charset_bin, (char*) ptr, field_length, 10, NULL, ¬_used); return my_strntoll(&my_charset_bin, (char*) ptr, field_length, 10, NULL, ¬_used); } String *Field_decimal::val_str(String *val_buffer MY_ATTRIBUTE((unused)), String *val_ptr) { ASSERT_COLUMN_MARKED_FOR_READ; uchar *str; size_t tmp_length; for (str=ptr ; *str == ' ' ; str++) ; val_ptr->set_charset(&my_charset_numeric); tmp_length= (size_t) (str-ptr); if (field_length < tmp_length) // Error in data val_ptr->length(0); else val_ptr->set_ascii((const char*) str, field_length-tmp_length); return val_ptr; } /** Should be able to handle at least the following fixed decimal formats: 5.00 , -1.0, 05, -05, +5 with optional pre/end space */ int Field_decimal::cmp(const uchar *a_ptr,const uchar *b_ptr) { const uchar *end; int swap=0; /* First remove prefixes '0', ' ', and '-' */ for (end=a_ptr+field_length; a_ptr != end && (*a_ptr == *b_ptr || ((my_isspace(&my_charset_bin,*a_ptr) || *a_ptr == '+' || *a_ptr == '0') && (my_isspace(&my_charset_bin,*b_ptr) || *b_ptr == '+' || *b_ptr == '0'))); a_ptr++,b_ptr++) { if (*a_ptr == '-') // If both numbers are negative swap= -1 ^ 1; // Swap result } if (a_ptr == end) return 0; if (*a_ptr == '-') return -1; if (*b_ptr == '-') return 1; while (a_ptr != end) { if (*a_ptr++ != *b_ptr++) return swap ^ (a_ptr[-1] < b_ptr[-1] ? -1 : 1); // compare digits } return 0; } void Field_decimal::make_sort_key(uchar *to, size_t 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 { const CHARSET_INFO *cs=res.charset(); uint tmp=field_length; if (!unsigned_flag) tmp--; if (dec) tmp--; res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), "decimal(%d,%d)",tmp,dec)); add_zerofill_and_unsigned(res); } /**************************************************************************** ** Field_new_decimal ****************************************************************************/ Field_new_decimal::Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, uint8 dec_arg,bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) { precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); set_if_smaller(precision, DECIMAL_MAX_PRECISION); assert((precision <= DECIMAL_MAX_PRECISION) && (dec <= DECIMAL_MAX_SCALE)); bin_size= my_decimal_get_binary_size(precision, dec); } Field_new_decimal::Field_new_decimal(uint32 len_arg, bool maybe_null_arg, const char *name, uint8 dec_arg, bool unsigned_arg) :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0, NONE, name, dec_arg, 0, unsigned_arg) { precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); set_if_smaller(precision, DECIMAL_MAX_PRECISION); assert((precision <= DECIMAL_MAX_PRECISION) && (dec <= DECIMAL_MAX_SCALE)); bin_size= my_decimal_get_binary_size(precision, dec); } Field *Field_new_decimal::create_from_item (Item *item) { uint8 dec= item->decimals; uint8 intg= item->decimal_precision() - dec; uint32 len= item->max_char_length(); assert (item->result_type() == DECIMAL_RESULT); /* Trying to put too many digits overall in a DECIMAL(prec,dec) will always throw a warning. We must limit dec to DECIMAL_MAX_SCALE however to prevent an assert() later. */ if (dec > 0) { signed int overflow; dec= min<int>(dec, DECIMAL_MAX_SCALE); /* If the value still overflows the field with the corrected dec, we'll throw out decimals rather than integers. This is still bad and of course throws a truncation warning. +1: for decimal point */ const int required_length= my_decimal_precision_to_length(intg + dec, dec, item->unsigned_flag); overflow= required_length - len; if (overflow > 0) dec= max(0, dec - overflow); // too long, discard fract else /* Corrected value fits. */ len= required_length; } return new Field_new_decimal(len, item->maybe_null, item->item_name.ptr(), dec, item->unsigned_flag); } type_conversion_status Field_new_decimal::reset(void) { store_value(&decimal_zero); return TYPE_OK; } /** Generate max/min decimal value in case of overflow. @param decimal_value buffer for value @param sign sign of value which caused overflow */ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, bool sign) { DBUG_ENTER("Field_new_decimal::set_value_on_overflow"); max_my_decimal(decimal_value, precision, decimals()); if (sign) { if (unsigned_flag) my_decimal_set_zero(decimal_value); else decimal_value->sign(TRUE); } DBUG_VOID_RETURN; } /** Store decimal value in the binary buffer. Checks if decimal_value fits into field size. If it does, stores the decimal in the buffer using binary format. Otherwise sets maximal number that can be stored in the field. @param decimal_value my_decimal @retval 0 ok @retval 1 error */ type_conversion_status Field_new_decimal::store_value(const my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; DBUG_ENTER("Field_new_decimal::store_value"); #ifndef NDEBUG { 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(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; decimal_value= &decimal_zero; } #ifndef NDEBUG { 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 int err= my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, decimal_value, ptr, precision, dec); if (warn_if_overflow(err)) { my_decimal buff; DBUG_PRINT("info", ("overflow")); set_value_on_overflow(&buff, decimal_value->sign()); my_decimal2binary(E_DEC_FATAL_ERROR, &buff, ptr, precision, dec); } DBUG_EXECUTE("info", print_decimal_buff(decimal_value, ptr, bin_size);); DBUG_RETURN((err != E_DEC_OK) ? decimal_err_to_type_conv_status(err) : error); } type_conversion_status Field_new_decimal::store(const char *from, size_t length, const CHARSET_INFO *charset_arg) { ASSERT_COLUMN_MARKED_FOR_WRITE; my_decimal decimal_value; DBUG_ENTER("Field_new_decimal::store(char*)"); int err= str2my_decimal(E_DEC_FATAL_ERROR & ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM), from, length, charset_arg, &decimal_value); if (err != 0 && !table->in_use->lex->is_ignore() && table->in_use->is_strict_mode()) { ErrConvString errmsg(from, length, charset_arg); const Diagnostics_area *da= table->in_use->get_stmt_da(); push_warning_printf(table->in_use, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "decimal", errmsg.ptr(), field_name, da->current_row_for_condition()); DBUG_RETURN(decimal_err_to_type_conv_status(err)); } if (err != 0) set_decimal_warning(this, err, &decimal_value, from, length, charset_arg); #ifndef NDEBUG char dbug_buff[DECIMAL_MAX_STR_LENGTH+2]; DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, &decimal_value))); #endif type_conversion_status store_stat= store_value(&decimal_value); DBUG_RETURN(err != 0 ? decimal_err_to_type_conv_status(err) : store_stat); } type_conversion_status store_internal_with_error_check(Field_new_decimal *field, int err, my_decimal *value) { type_conversion_status stat= TYPE_OK; if (err != 0) { if (field->check_overflow(err)) { field->set_value_on_overflow(value, value->sign()); stat= TYPE_WARN_OUT_OF_RANGE; } else if (field->check_truncated(err)) stat= TYPE_NOTE_TRUNCATED; /* Only issue a warning if store_value doesn't issue an warning */ field->table->in_use->got_warning= 0; } type_conversion_status store_stat= field->store_value(value); if (store_stat != TYPE_OK) return store_stat; else if (err != 0 && !field->table->in_use->got_warning) field->warn_if_overflow(err); return stat; } /** @todo Fix following when double2my_decimal when double2decimal will return E_DEC_TRUNCATED always correctly */ type_conversion_status Field_new_decimal::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; DBUG_ENTER("Field_new_decimal::store(double)"); my_decimal decimal_value; int conv_err= double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr, &decimal_value); DBUG_RETURN(store_internal_with_error_check(this, conv_err, &decimal_value)); } type_conversion_status Field_new_decimal::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; DBUG_ENTER("Field_new_decimal::store(double, unsigned_val)"); my_decimal decimal_value; int conv_err= int2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr, unsigned_val, &decimal_value); DBUG_RETURN(store_internal_with_error_check(this, conv_err, &decimal_value)); } type_conversion_status Field_new_decimal::store_decimal(const my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_WRITE; return store_value(decimal_value); } type_conversion_status Field_new_decimal::store_time(MYSQL_TIME *ltime, uint8 dec_arg MY_ATTRIBUTE((unused))) { my_decimal decimal_value; return store_value(date2my_decimal(ltime, &decimal_value)); } double Field_new_decimal::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; double dbl; my_decimal decimal_value; my_decimal2double(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), &dbl); return dbl; } longlong Field_new_decimal::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; longlong i; my_decimal decimal_value; my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), unsigned_flag, &i); return i; } my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; DBUG_ENTER("Field_new_decimal::val_decimal"); binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value, precision, dec); DBUG_EXECUTE("info", print_decimal_buff(decimal_value, ptr, bin_size);); DBUG_RETURN(decimal_value); } String *Field_new_decimal::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; my_decimal decimal_value; uint fixed_precision= zerofill ? precision : 0; my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), fixed_precision, dec, '0', val_buffer); val_buffer->set_charset(&my_charset_numeric); return val_buffer; } bool Field_new_decimal::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { my_decimal buf, *decimal_value= val_decimal(&buf); if (!decimal_value) { set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME); return true; } return my_decimal_to_datetime_with_warn(decimal_value, ltime, fuzzydate); } bool Field_new_decimal::get_time(MYSQL_TIME *ltime) { my_decimal buf, *decimal_value= val_decimal(&buf); if (!decimal_value) { set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); return true; } return my_decimal_to_time_with_warn(decimal_value, ltime); } int Field_new_decimal::cmp(const uchar *a,const uchar*b) { return memcmp(a, b, bin_size); } void Field_new_decimal::make_sort_key(uchar *buff, size_t length) { memcpy(buff, ptr, min(length, static_cast<size_t>(bin_size))); } void Field_new_decimal::sql_type(String &str) const { const CHARSET_INFO *cs= str.charset(); str.length(cs->cset->snprintf(cs, (char*) str.ptr(), str.alloced_length(), "decimal(%d,%d)", precision, (int)dec)); add_zerofill_and_unsigned(str); } /** Save the field metadata for new decimal fields. Saves the precision in the first byte and decimals() in the second byte of the field metadata array at index of *metadata_ptr and *(metadata_ptr + 1). @param metadata_ptr First byte of field metadata @returns number of bytes written to metadata_ptr */ int Field_new_decimal::do_save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= precision; *(metadata_ptr + 1)= decimals(); return 2; } /** Returns the number of bytes field uses in row-based replication row packed size. This method is used in row-based replication to determine the number of bytes that the field consumes in the row record format. This is used to skip fields in the master that do not exist on the slave. @param field_metadata Encoded size in field metadata @returns The size of the field based on the field metadata. */ uint Field_new_decimal::pack_length_from_metadata(uint field_metadata) { uint const source_precision= (field_metadata >> 8U) & 0x00ff; uint const source_decimal= field_metadata & 0x00ff; uint const source_size= my_decimal_get_binary_size(source_precision, source_decimal); return (source_size); } /** Check to see if field size is compatible with destination. This method is used in row-based replication to verify that the slave's field size is less than or equal to the master's field size. The encoded field metadata (from the master or source) is decoded and compared to the size of this field (the slave or destination). @param field_metadata Encoded size in field metadata @param order_var Pointer to variable where the order between the source field and this field will be returned. @return @c true */ bool Field_new_decimal::compatible_field_size(uint field_metadata, Relay_log_info * MY_ATTRIBUTE((unused)), uint16 mflags MY_ATTRIBUTE((unused)), int *order_var) { uint const source_precision= (field_metadata >> 8U) & 0x00ff; uint const source_decimal= field_metadata & 0x00ff; int order= compare(source_precision, precision); *order_var= order != 0 ? order : compare(source_decimal, dec); return true; } uint Field_new_decimal::is_equal(Create_field *new_field) { return ((new_field->sql_type == real_type()) && ((new_field->flags & UNSIGNED_FLAG) == (uint) (flags & UNSIGNED_FLAG)) && ((new_field->flags & AUTO_INCREMENT_FLAG) == (uint) (flags & AUTO_INCREMENT_FLAG)) && (new_field->length == max_display_length()) && (new_field->decimals == dec)); } /** Unpack a decimal field from row data. This method is used to unpack a decimal or numeric field from a master whose size of the field is less than that of the slave. @param to Destination of the data @param from Source of the data @param param_data Precision (upper) and decimal (lower) values @return New pointer into memory based on from + length of the data */ const uchar * Field_new_decimal::unpack(uchar* to, const uchar *from, uint param_data, bool low_byte_first) { if (param_data == 0) return Field::unpack(to, from, param_data, low_byte_first); uint from_precision= (param_data & 0xff00) >> 8U; uint from_decimal= param_data & 0x00ff; uint length=pack_length(); uint from_pack_len= my_decimal_get_binary_size(from_precision, from_decimal); uint len= (param_data && (from_pack_len < length)) ? from_pack_len : length; if ((from_pack_len && (from_pack_len < length)) || (from_precision < precision) || (from_decimal < decimals())) { /* If the master's data is smaller than the slave, we need to convert the binary to decimal then resize the decimal converting it back to a decimal and write that to the raw data buffer. */ decimal_digit_t dec_buf[DECIMAL_MAX_PRECISION]; decimal_t dec_val; dec_val.len= from_precision; dec_val.buf= dec_buf; /* Note: bin2decimal does not change the length of the field. So it is just the first step the resizing operation. The second step does the resizing using the precision and decimals from the slave. */ bin2decimal((uchar *)from, &dec_val, from_precision, from_decimal); decimal2bin(&dec_val, to, precision, decimals()); } else memcpy(to, from, len); // Sizes are the same, just copy the data. return from+len; } bool Field_new_decimal::send_binary(Protocol *protocol) { my_decimal dec_value; if (is_null()) return protocol->store_null(); return protocol->store_decimal(val_decimal(&dec_value), zerofill ? precision : 0, dec); } /**************************************************************************** ** tiny int ****************************************************************************/ type_conversion_status Field_tiny::store(const char *from, size_t len, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; longlong rnd; const type_conversion_status error= get_int(cs, from, len, &rnd, 255, -128, 127); ptr[0]= unsigned_flag ? (char) (ulonglong) rnd : (char) rnd; return error; } type_conversion_status Field_tiny::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; nr=rint(nr); if (unsigned_flag) { if (nr < 0.0) { *ptr=0; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > 255.0) { *ptr=(char) 255; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else *ptr=(char) nr; } else { if (nr < -128.0) { *ptr= (char) -128; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > 127.0) { *ptr=127; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else *ptr=(char) (int) nr; } return error; } type_conversion_status Field_tiny::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; if (unsigned_flag) { if (nr < 0 && !unsigned_val) { *ptr= 0; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if ((ulonglong) nr > (ulonglong) 255) { *ptr= (char) 255; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else *ptr=(char) nr; } else { if (nr < 0 && unsigned_val) nr= 256; // Generate overflow if (nr < -128) { *ptr= (char) -128; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > 127) { *ptr=127; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else *ptr=(char) nr; } return error; } double Field_tiny::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int tmp= unsigned_flag ? (int) ptr[0] : (int) ((signed char*) ptr)[0]; return (double) tmp; } longlong Field_tiny::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; int tmp= unsigned_flag ? (int) ptr[0] : (int) ((signed char*) ptr)[0]; return (longlong) tmp; } String *Field_tiny::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; const CHARSET_INFO *cs= &my_charset_numeric; uint length; uint mlength=max(field_length+1,5*cs->mbmaxlen); val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); if (unsigned_flag) length= (uint) cs->cset->long10_to_str(cs,to,mlength, 10, (long) *ptr); else length= (uint) cs->cset->long10_to_str(cs,to,mlength,-10, (long) *((signed char*) ptr)); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); val_buffer->set_charset(cs); return val_buffer; } bool Field_tiny::send_binary(Protocol *protocol) { if (is_null()) return protocol->store_null(); return protocol->store_tiny((longlong) unsigned_flag? (uint8) ptr[0]: (int8) ptr[0]); } int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr) { signed char a,b; a=(signed char) a_ptr[0]; b= (signed char) b_ptr[0]; if (unsigned_flag) return ((uchar) a < (uchar) b) ? -1 : ((uchar) a > (uchar) b) ? 1 : 0; return (a < b) ? -1 : (a > b) ? 1 : 0; } void Field_tiny::make_sort_key(uchar *to, size_t length) { assert(length >= 1); if (unsigned_flag) *to= *ptr; else to[0] = (char) (ptr[0] ^ (uchar) 128); /* Revers signbit */ } void Field_tiny::sql_type(String &res) const { const CHARSET_INFO *cs=res.charset(); res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), "tinyint(%d)",(int) field_length)); add_zerofill_and_unsigned(res); } /**************************************************************************** Field type short int (2 byte) ****************************************************************************/ type_conversion_status Field_short::store(const char *from, size_t len, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; int store_tmp; longlong rnd; const type_conversion_status error= get_int(cs, from, len, &rnd, UINT_MAX16, INT_MIN16, INT_MAX16); store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int2store(ptr, store_tmp); } else #endif shortstore(ptr, (short) store_tmp); return error; } type_conversion_status Field_short::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; int16 res; nr=rint(nr); if (unsigned_flag) { if (nr < 0) { res=0; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > (double) UINT_MAX16) { res=(int16) UINT_MAX16; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else res=(int16) (uint16) nr; } else { if (nr < (double) INT_MIN16) { res=INT_MIN16; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > (double) INT_MAX16) { res=INT_MAX16; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else res=(int16) (int) nr; } #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int2store(ptr,res); } else #endif shortstore(ptr,res); return error; } type_conversion_status Field_short::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; int16 res; if (unsigned_flag) { if (nr < 0L && !unsigned_val) { res=0; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if ((ulonglong) nr > (ulonglong) UINT_MAX16) { res=(int16) UINT_MAX16; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } 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(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > (longlong) INT_MAX16) { res=INT_MAX16; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else res=(int16) nr; } #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int2store(ptr,res); } else #endif shortstore(ptr,res); return error; } double Field_short::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; short j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) j=sint2korr(ptr); else #endif shortget(&j, ptr); return unsigned_flag ? (double) (unsigned short) j : (double) j; } longlong Field_short::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; short j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) j=sint2korr(ptr); else #endif shortget(&j, ptr); return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j; } String *Field_short::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; const CHARSET_INFO *cs= &my_charset_numeric; uint length; uint mlength=max(field_length+1,7*cs->mbmaxlen); val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); short j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) j=sint2korr(ptr); else #endif shortget(&j, ptr); if (unsigned_flag) length=(uint) cs->cset->long10_to_str(cs, to, mlength, 10, (long) (uint16) j); else length=(uint) cs->cset->long10_to_str(cs, to, mlength,-10, (long) j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); val_buffer->set_charset(cs); return val_buffer; } bool Field_short::send_binary(Protocol *protocol) { if (is_null()) return protocol->store_null(); return protocol->store_short(Field_short::val_int()); } int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr) { short a,b; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { a=sint2korr(a_ptr); b=sint2korr(b_ptr); } else #endif { shortget(&a, a_ptr); shortget(&b, b_ptr); } if (unsigned_flag) return ((unsigned short) a < (unsigned short) b) ? -1 : ((unsigned short) a > (unsigned short) b) ? 1 : 0; return (a < b) ? -1 : (a > b) ? 1 : 0; } void Field_short::make_sort_key(uchar *to, size_t length) { assert(length >= 2); #ifdef WORDS_BIGENDIAN if (!table->s->db_low_byte_first) { if (unsigned_flag) to[0] = ptr[0]; else to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ to[1] = ptr[1]; } else #endif { if (unsigned_flag) to[0] = ptr[1]; else to[0] = (char) (ptr[1] ^ 128); /* Revers signbit */ to[1] = ptr[0]; } } void Field_short::sql_type(String &res) const { const CHARSET_INFO *cs=res.charset(); res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), "smallint(%d)",(int) field_length)); add_zerofill_and_unsigned(res); } /**************************************************************************** Field type medium int (3 byte) ****************************************************************************/ type_conversion_status Field_medium::store(const char *from, size_t len, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; int store_tmp; longlong rnd; const type_conversion_status 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; } type_conversion_status Field_medium::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; nr=rint(nr); if (unsigned_flag) { if (nr < 0) { int3store(ptr,0); set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (nr >= (double) (long) (1L << 24)) { uint32 tmp=(uint32) (1L << 24)-1L; int3store(ptr,tmp); set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else int3store(ptr,(uint32) nr); } else { if (nr < (double) INT_MIN24) { long tmp=(long) INT_MIN24; int3store(ptr,tmp); set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > (double) INT_MAX24) { long tmp=(long) INT_MAX24; int3store(ptr,tmp); set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else int3store(ptr,(long) nr); } return error; } type_conversion_status Field_medium::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; if (unsigned_flag) { if (nr < 0 && !unsigned_val) { int3store(ptr,0); set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24)) { long tmp= (long) (1L << 24)-1L; int3store(ptr,tmp); set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } 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(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > (longlong) INT_MAX24) { long tmp=(long) INT_MAX24; int3store(ptr,tmp); set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else int3store(ptr,(long) nr); } return error; } double Field_medium::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); return (double) j; } longlong Field_medium::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); return (longlong) j; } String *Field_medium::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; const CHARSET_INFO *cs= &my_charset_numeric; uint length; uint mlength=max(field_length+1,10*cs->mbmaxlen); val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); length=(uint) cs->cset->long10_to_str(cs,to,mlength,-10,j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); /* purecov: inspected */ val_buffer->set_charset(cs); return val_buffer; } bool Field_medium::send_binary(Protocol *protocol) { ASSERT_COLUMN_MARKED_FOR_READ; if (is_null()) return protocol->store_null(); return protocol->store_long(Field_medium::val_int()); } int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr) { long a,b; if (unsigned_flag) { a=uint3korr(a_ptr); b=uint3korr(b_ptr); } else { a=sint3korr(a_ptr); b=sint3korr(b_ptr); } return (a < b) ? -1 : (a > b) ? 1 : 0; } void Field_medium::make_sort_key(uchar *to, size_t length) { assert(length >= 3); if (unsigned_flag) to[0] = ptr[2]; else to[0] = (uchar) (ptr[2] ^ 128); /* Revers signbit */ to[1] = ptr[1]; to[2] = ptr[0]; } void Field_medium::sql_type(String &res) const { const CHARSET_INFO *cs=res.charset(); res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), "mediumint(%d)",(int) field_length)); add_zerofill_and_unsigned(res); } /**************************************************************************** ** long int ****************************************************************************/ type_conversion_status Field_long::store(const char *from, size_t len, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; long store_tmp; longlong rnd; const type_conversion_status error= get_int(cs, from, len, &rnd, UINT_MAX32, INT_MIN32, INT_MAX32); store_tmp= unsigned_flag ? (long) (ulonglong) rnd : (long) rnd; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int4store(ptr, store_tmp); } else #endif longstore(ptr, store_tmp); return error; } type_conversion_status Field_long::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; int32 res; nr=rint(nr); if (unsigned_flag) { if (nr < 0) { res=0; error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > (double) UINT_MAX32) { res= UINT_MAX32; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else res=(int32) (ulong) nr; } else { if (nr < (double) INT_MIN32) { res=(int32) INT_MIN32; error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > (double) INT_MAX32) { res=(int32) INT_MAX32; error= TYPE_WARN_OUT_OF_RANGE; } else res=(int32) (longlong) nr; } if (error) set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int4store(ptr,res); } else #endif longstore(ptr,res); return error; } /** Store a longlong in the field @param nr the value to store @param unsigned_val whether or not 'nr' should be interpreted as signed or unsigned. E.g., if 'nr' has all bits set it is interpreted as -1 if unsigned_val is false and ULLONG_MAX if unsigned_val is true. */ type_conversion_status Field_long::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; int32 res; if (unsigned_flag) { if (nr < 0 && !unsigned_val) { res=0; error= TYPE_WARN_OUT_OF_RANGE; } else if ((ulonglong) nr >= (1LL << 32)) { res=(int32) (uint32) ~0L; error= TYPE_WARN_OUT_OF_RANGE; } else res=(int32) (uint32) nr; } else { if (nr < 0 && unsigned_val) { nr= ((longlong) INT_MAX32) + 1; // Generate overflow error= TYPE_WARN_OUT_OF_RANGE; } if (nr < (longlong) INT_MIN32) { res=(int32) INT_MIN32; error= TYPE_WARN_OUT_OF_RANGE; } else if (nr > (longlong) INT_MAX32) { res=(int32) INT_MAX32; error= TYPE_WARN_OUT_OF_RANGE; } else res=(int32) nr; } if (error) set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int4store(ptr,res); } else #endif longstore(ptr,res); return error; } double Field_long::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int32 j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) j=sint4korr(ptr); else #endif longget(&j, ptr); return unsigned_flag ? (double) (uint32) j : (double) j; } longlong Field_long::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; int32 j; /* See the comment in Field_long::store(long long) */ assert(table->in_use == current_thd); #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) j=sint4korr(ptr); else #endif longget(&j, ptr); return unsigned_flag ? (longlong) (uint32) j : (longlong) j; } String *Field_long::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; const CHARSET_INFO *cs= &my_charset_numeric; size_t length; uint mlength=max(field_length+1,12*cs->mbmaxlen); val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); int32 j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) j=sint4korr(ptr); else #endif longget(&j, ptr); if (unsigned_flag) length=cs->cset->long10_to_str(cs,to,mlength, 10,(long) (uint32)j); else length=cs->cset->long10_to_str(cs,to,mlength,-10,(long) j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); val_buffer->set_charset(cs); return val_buffer; } bool Field_long::send_binary(Protocol *protocol) { ASSERT_COLUMN_MARKED_FOR_READ; if (is_null()) return protocol->store_null(); return protocol->store_long(Field_long::val_int()); } int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr) { int32 a,b; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { a=sint4korr(a_ptr); b=sint4korr(b_ptr); } else #endif { longget(&a, a_ptr); longget(&b, b_ptr); } if (unsigned_flag) return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; return (a < b) ? -1 : (a > b) ? 1 : 0; } void Field_long::make_sort_key(uchar *to, size_t length) { assert(length >= 4); #ifdef WORDS_BIGENDIAN if (!table->s->db_low_byte_first) { if (unsigned_flag) to[0] = ptr[0]; else to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ to[1] = ptr[1]; to[2] = ptr[2]; to[3] = ptr[3]; } else #endif { if (unsigned_flag) to[0] = ptr[3]; else to[0] = (char) (ptr[3] ^ 128); /* Revers signbit */ to[1] = ptr[2]; to[2] = ptr[1]; to[3] = ptr[0]; } } void Field_long::sql_type(String &res) const { const CHARSET_INFO *cs=res.charset(); res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), "int(%d)",(int) field_length)); add_zerofill_and_unsigned(res); } /**************************************************************************** Field type longlong int (8 bytes) ****************************************************************************/ type_conversion_status Field_longlong::store(const char *from, size_t len, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; int conv_err= 0; type_conversion_status error= TYPE_OK; char *end; ulonglong tmp; tmp= cs->cset->strntoull10rnd(cs,from,len,unsigned_flag,&end,&conv_err); if (conv_err == MY_ERRNO_ERANGE) { set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } else if (table->in_use->count_cuted_fields && check_int(cs, from, len, end, conv_err)) error= TYPE_WARN_OUT_OF_RANGE; else error= TYPE_OK; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int8store(ptr,tmp); } else #endif longlongstore(ptr,tmp); return error; } type_conversion_status Field_longlong::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; longlong res; nr= rint(nr); if (unsigned_flag) { if (nr < 0) { res=0; error= TYPE_WARN_OUT_OF_RANGE; } else if (nr >= (double) ULLONG_MAX) { res= ~(longlong) 0; error= TYPE_WARN_OUT_OF_RANGE; } else res=(longlong) double2ulonglong(nr); } else { if (nr <= (double) LLONG_MIN) { res= LLONG_MIN; if (nr < (double) LLONG_MIN) error= TYPE_WARN_OUT_OF_RANGE; } else if (nr >= (double) (ulonglong) LLONG_MAX) { res= LLONG_MAX; if (nr > (double) LLONG_MAX) error= TYPE_WARN_OUT_OF_RANGE; } else res=(longlong) nr; } if (error) set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int8store(ptr,res); } else #endif longlongstore(ptr,res); return error; } type_conversion_status Field_longlong::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; if (nr < 0) // Only possible error { /* if field is unsigned and value is signed (< 0) or if field is signed and value is unsigned we have an overflow */ if (unsigned_flag != unsigned_val) { nr= unsigned_flag ? (ulonglong) 0 : (ulonglong) LLONG_MAX; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); error= TYPE_WARN_OUT_OF_RANGE; } } #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int8store(ptr,nr); } else #endif longlongstore(ptr,nr); return error; } double Field_longlong::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; longlong j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { j=sint8korr(ptr); } else #endif longlongget(&j, ptr); /* The following is open coded to avoid a bug in gcc 3.3 */ if (unsigned_flag) { ulonglong tmp= (ulonglong) j; return ulonglong2double(tmp); } return (double) j; } longlong Field_longlong::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; longlong j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) j=sint8korr(ptr); else #endif longlongget(&j, ptr); return j; } String *Field_longlong::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { const CHARSET_INFO *cs= &my_charset_numeric; uint length; uint mlength=max(field_length+1,22*cs->mbmaxlen); val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); longlong j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) j=sint8korr(ptr); else #endif longlongget(&j, ptr); length=(uint) (cs->cset->longlong10_to_str)(cs,to,mlength, unsigned_flag ? 10 : -10, j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); val_buffer->set_charset(cs); return val_buffer; } bool Field_longlong::send_binary(Protocol *protocol) { ASSERT_COLUMN_MARKED_FOR_READ; if (is_null()) return protocol->store_null(); return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag); } int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr) { longlong a,b; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { a=sint8korr(a_ptr); b=sint8korr(b_ptr); } else #endif { longlongget(&a, a_ptr); longlongget(&b, b_ptr); } if (unsigned_flag) return ((ulonglong) a < (ulonglong) b) ? -1 : ((ulonglong) a > (ulonglong) b) ? 1 : 0; return (a < b) ? -1 : (a > b) ? 1 : 0; } void Field_longlong::make_sort_key(uchar *to, size_t length) { const size_t from_length= PACK_LENGTH; const size_t to_length= min(from_length, length); #ifdef WORDS_BIGENDIAN if (table == NULL || !table->s->db_low_byte_first) copy_integer<true>(to, to_length, ptr, from_length, unsigned_flag); else #endif copy_integer<false>(to, to_length, ptr, from_length, unsigned_flag); } void Field_longlong::sql_type(String &res) const { const CHARSET_INFO *cs=res.charset(); res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), "bigint(%d)",(int) field_length)); add_zerofill_and_unsigned(res); } /* Floating-point numbers */ uchar * Field_real::pack(uchar *to, const uchar *from, uint max_length, bool low_byte_first) { DBUG_ENTER("Field_real::pack"); assert(max_length >= pack_length()); #ifdef WORDS_BIGENDIAN if (low_byte_first != table->s->db_low_byte_first) { const uchar *dptr= from + pack_length(); while (dptr-- > from) *to++ = *dptr; DBUG_RETURN(to); } else #endif DBUG_RETURN(Field::pack(to, from, max_length, low_byte_first)); } const uchar * Field_real::unpack(uchar *to, const uchar *from, uint param_data, bool low_byte_first) { DBUG_ENTER("Field_real::unpack"); #ifdef WORDS_BIGENDIAN if (low_byte_first != table->s->db_low_byte_first) { const uchar *dptr= from + pack_length(); while (dptr-- > from) *to++ = *dptr; DBUG_RETURN(from + pack_length()); } else #endif DBUG_RETURN(Field::unpack(to, from, param_data, low_byte_first)); } type_conversion_status Field_real::store_time(MYSQL_TIME *ltime, uint8 dec_arg MY_ATTRIBUTE((unused))) { double nr= TIME_to_double(ltime); return store(ltime->neg ? -nr : nr); } /**************************************************************************** single precision float ****************************************************************************/ type_conversion_status Field_float::store(const char *from, size_t len, const CHARSET_INFO *cs) { int conv_error; type_conversion_status err= TYPE_OK; char *end; double nr= my_strntod(cs,(char*) from,len,&end,&conv_error); if (conv_error || (!len || ((uint) (end-from) != len && table->in_use->count_cuted_fields))) { set_warning(Sql_condition::SL_WARNING, (conv_error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1); err= conv_error ? TYPE_WARN_OUT_OF_RANGE : TYPE_WARN_TRUNCATED; } Field_float::store(nr); return err; } type_conversion_status Field_float::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; const type_conversion_status error= truncate(&nr, FLT_MAX) ? TYPE_WARN_OUT_OF_RANGE : TYPE_OK; float j= (float)nr; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float4store(ptr,j); } else #endif memcpy(ptr, &j, sizeof(j)); return error; } type_conversion_status Field_float::store(longlong nr, bool unsigned_val) { return Field_float::store(unsigned_val ? ulonglong2double((ulonglong) nr) : (double) nr); } double Field_float::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; float j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float4get(&j,ptr); } else #endif memcpy(&j, ptr, sizeof(j)); return ((double) j); } longlong Field_float::val_int(void) { float j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float4get(&j,ptr); } else #endif memcpy(&j, ptr, sizeof(j)); return (longlong) rint(j); } String *Field_float::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; assert(!zerofill || field_length <= MAX_FIELD_CHARLENGTH); float nr; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float4get(&nr,ptr); } else #endif memcpy(&nr, ptr, sizeof(nr)); uint to_length= 70; if (val_buffer->alloc(to_length)) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); return val_buffer; } char *to=(char*) val_buffer->ptr(); size_t len; if (dec >= NOT_FIXED_DEC) len= my_gcvt(nr, MY_GCVT_ARG_FLOAT, to_length - 1, to, NULL); else { /* We are safe here because the buffer length is 70, and fabs(float) < 10^39, dec < NOT_FIXED_DEC. So the resulting string will be not longer than 69 chars + terminating '\0'. */ len= my_fcvt(nr, dec, to, NULL); } val_buffer->length((uint) len); if (zerofill) prepend_zeros(val_buffer); val_buffer->set_charset(&my_charset_numeric); return val_buffer; } int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr) { float a,b; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float4get(&a,a_ptr); float4get(&b,b_ptr); } else #endif { memcpy(&a, a_ptr, sizeof(float)); memcpy(&b, b_ptr, sizeof(float)); } return (a < b) ? -1 : (a > b) ? 1 : 0; } #define FLT_EXP_DIG (sizeof(float)*8-FLT_MANT_DIG) void Field_float::make_sort_key(uchar *to, size_t length) { assert(length >= 4); float nr; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float4get(&nr,ptr); } else #endif memcpy(&nr, ptr, min(length, sizeof(float))); uchar *tmp= to; if (nr == (float) 0.0) { /* Change to zero string */ tmp[0]=(uchar) 128; memset(tmp + 1, 0, min(length, sizeof(nr) - 1)); } else { #ifdef WORDS_BIGENDIAN memcpy(tmp, &nr, sizeof(nr)); #else tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0]; #endif if (tmp[0] & 128) /* Negative */ { /* make complement */ uint i; for (i=0 ; i < sizeof(nr); i++) tmp[i]= (uchar) (tmp[i] ^ (uchar) 255); } else { ushort exp_part=(((ushort) tmp[0] << 8) | (ushort) tmp[1] | (ushort) 32768); exp_part+= (ushort) 1 << (16-1-FLT_EXP_DIG); tmp[0]= (uchar) (exp_part >> 8); tmp[1]= (uchar) exp_part; } } } bool Field_float::send_binary(Protocol *protocol) { ASSERT_COLUMN_MARKED_FOR_READ; if (is_null()) return protocol->store_null(); return protocol->store((float) Field_float::val_real(), dec, (String*) 0); } /** Save the field metadata for float fields. Saves the pack length in the first byte. @param metadata_ptr First byte of field metadata @returns number of bytes written to metadata_ptr */ int Field_float::do_save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= pack_length(); return 1; } void Field_float::sql_type(String &res) const { if (dec == NOT_FIXED_DEC) { res.set_ascii(STRING_WITH_LEN("float")); } else { const CHARSET_INFO *cs= res.charset(); res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), "float(%d,%d)",(int) field_length,dec)); } add_zerofill_and_unsigned(res); } /**************************************************************************** double precision floating point numbers ****************************************************************************/ type_conversion_status Field_double::store(const char *from, size_t len, const CHARSET_INFO *cs) { int conv_error; type_conversion_status error= TYPE_OK; char *end; double nr= my_strntod(cs,(char*) from, len, &end, &conv_error); if ((conv_error != 0) || (!len || ((uint) (end-from) != len && table->in_use->count_cuted_fields))) { set_warning(Sql_condition::SL_WARNING, (conv_error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1); error= conv_error ? TYPE_WARN_OUT_OF_RANGE : TYPE_WARN_TRUNCATED; } Field_double::store(nr); return error; } type_conversion_status Field_double::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; const type_conversion_status error= truncate(&nr, DBL_MAX) ? TYPE_WARN_OUT_OF_RANGE : TYPE_OK; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float8store(ptr,nr); } else #endif doublestore(ptr,nr); return error; } type_conversion_status 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. */ bool Field_real::truncate(double *nr, double max_value) { if (my_isnan(*nr)) { *nr= 0; set_null(); set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return true; } else if (unsigned_flag && *nr < 0) { *nr= 0; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return true; } if (!not_fixed) { uint order= field_length - dec; uint step= array_elements(log_10) - 1; max_value= 1.0; for (; order > step; order-= step) max_value*= log_10[step]; max_value*= log_10[order]; max_value-= 1.0 / log_10[dec]; /* Check for infinity so we don't get NaN in calculations */ if (!my_isinf(*nr)) { double tmp= rint((*nr - floor(*nr)) * log_10[dec]) / log_10[dec]; *nr= floor(*nr) + tmp; } } if (*nr < -max_value) { *nr= -max_value; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return true; } else if (*nr > max_value) { *nr= max_value; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return true; } return false; } type_conversion_status Field_real::store_decimal(const my_decimal *dm) { double dbl; my_decimal2double(E_DEC_FATAL_ERROR, dm, &dbl); return store(dbl); } double Field_double::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; double j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float8get(&j,ptr); } else #endif doubleget(&j,ptr); return j; } longlong Field_double::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; double j; longlong res; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float8get(&j,ptr); } else #endif doubleget(&j,ptr); /* Check whether we fit into longlong range */ if (j <= (double) LLONG_MIN) { res= (longlong) LLONG_MIN; goto warn; } if (j >= (double) (ulonglong) LLONG_MAX) { res= (longlong) LLONG_MAX; goto warn; } return (longlong) rint(j); warn: { char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; String tmp(buf, sizeof(buf), &my_charset_latin1), *str; str= val_str(&tmp, 0); ErrConvString err(str); push_warning_printf(current_thd, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", err.ptr()); } return res; } my_decimal *Field_real::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value); return decimal_value; } bool Field_real::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { return my_double_to_datetime_with_warn(val_real(), ltime, fuzzydate); } bool Field_real::get_time(MYSQL_TIME *ltime) { return my_double_to_time_with_warn(val_real(), ltime); } String *Field_double::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; assert(!zerofill || field_length <= MAX_FIELD_CHARLENGTH); double nr; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float8get(&nr,ptr); } else #endif doubleget(&nr,ptr); uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE; 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 >= NOT_FIXED_DEC) len= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, to_length - 1, to, NULL); else len= my_fcvt(nr, dec, to, NULL); val_buffer->length((uint) len); if (zerofill) prepend_zeros(val_buffer); val_buffer->set_charset(&my_charset_numeric); return val_buffer; } bool Field_double::send_binary(Protocol *protocol) { if (is_null()) return protocol->store_null(); String buf; return protocol->store(Field_double::val_real(), dec, &buf); } int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr) { double a,b; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float8get(&a,a_ptr); float8get(&b,b_ptr); } else #endif { doubleget(&a, a_ptr); doubleget(&b, b_ptr); } return (a < b) ? -1 : (a > b) ? 1 : 0; } #define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG) /* The following should work for IEEE */ void Field_double::make_sort_key(uchar *to, size_t length) { double nr; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { float8get(&nr, ptr); } else #endif doubleget(&nr, ptr); if (length < 8) { uchar buff[8]; change_double_for_sort(nr, buff); memcpy(to, buff, length); } else change_double_for_sort(nr, to); } /** Save the field metadata for double fields. Saves the pack length in the first byte of the field metadata array at index of *metadata_ptr. @param metadata_ptr First byte of field metadata @returns number of bytes written to metadata_ptr */ int Field_double::do_save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= pack_length(); return 1; } void Field_double::sql_type(String &res) const { const CHARSET_INFO *cs=res.charset(); if (dec == NOT_FIXED_DEC) { res.set_ascii(STRING_WITH_LEN("double")); } else { res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(), "double(%d,%d)",(int) field_length,dec)); } add_zerofill_and_unsigned(res); } /**************************************************************************** ** Common code for all temporal data types: DATE, DATETIME, TIMESTAMP, TIME *****************************************************************************/ uint Field_temporal::is_equal(Create_field *new_field) { return new_field->sql_type == real_type() && new_field->decimals == decimals(); } my_decimal *Field_temporal::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; assert(decimals() == 0); int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 0, decimal_value); return decimal_value; } /** Set warnings from a warning vector. Note, multiple warnings can be set at the same time. @param str Value. @param warnings Warning vector. */ void Field_temporal::set_warnings(ErrConvString str, int warnings) { int cut_incremented= 0; timestamp_type ts_type= field_type_to_timestamp_type(type()); if (warnings & MYSQL_TIME_WARN_TRUNCATED) { set_datetime_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, str, ts_type, !cut_incremented); cut_incremented= 1; } if (warnings & (MYSQL_TIME_WARN_OUT_OF_RANGE | MYSQL_TIME_WARN_ZERO_DATE | MYSQL_TIME_WARN_ZERO_IN_DATE)) { set_datetime_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, str, ts_type, !cut_incremented); cut_incremented= 1; } if (warnings & MYSQL_TIME_WARN_INVALID_TIMESTAMP) { set_datetime_warning(Sql_condition::SL_WARNING, ER_WARN_INVALID_TIMESTAMP, str, ts_type, !cut_incremented); cut_incremented= 1; } if ((warnings & MYSQL_TIME_NOTE_TRUNCATED) && !(warnings & MYSQL_TIME_WARN_TRUNCATED)) { set_datetime_warning(Sql_condition::SL_NOTE, WARN_DATA_TRUNCATED, str, ts_type, !cut_incremented); } } type_conversion_status Field_temporal::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; int warnings= 0; MYSQL_TIME ltime; type_conversion_status error= convert_number_to_TIME(nr, unsigned_val, 0, <ime, &warnings); if (error == TYPE_OK || error == TYPE_NOTE_TRUNCATED) error= store_internal(<ime, &warnings); else { assert(warnings != 0); // Must be set by convert_number_to_TIME if (warnings & (MYSQL_TIME_WARN_ZERO_DATE | MYSQL_TIME_WARN_ZERO_IN_DATE) && !current_thd->is_strict_mode()) error= TYPE_NOTE_TIME_TRUNCATED; } if (warnings) set_warnings(ErrConvString(nr, unsigned_val), warnings); return error; } type_conversion_status Field_temporal::store_lldiv_t(const lldiv_t *lld, int *warnings) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error; MYSQL_TIME ltime; error= convert_number_to_TIME(lld->quot, 0, static_cast<int>(lld->rem), <ime, warnings); if (error == TYPE_OK || error == TYPE_NOTE_TRUNCATED) error= store_internal_with_round(<ime, warnings); else if (!*warnings) { assert(warnings != 0); // Must be set by convert_number_to_TIME if (((*warnings & MYSQL_TIME_WARN_ZERO_DATE) != 0 || (*warnings & MYSQL_TIME_WARN_ZERO_IN_DATE) != 0) && !current_thd->is_strict_mode()) error= TYPE_NOTE_TIME_TRUNCATED; } return error; } type_conversion_status Field_temporal::store_decimal(const my_decimal *decimal) { ASSERT_COLUMN_MARKED_FOR_WRITE; lldiv_t lld; int warnings= 0; /* Pass 0 in the first argument, not to produce warnings automatically */ my_decimal2lldiv_t(0, decimal, &lld); const type_conversion_status error= store_lldiv_t(&lld, &warnings); if (warnings) set_warnings(ErrConvString(decimal), warnings); return error; } type_conversion_status Field_temporal::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; int warnings= 0; lldiv_t lld; double2lldiv_t(nr, &lld); const type_conversion_status error= store_lldiv_t(&lld, &warnings); if (warnings) set_warnings(ErrConvString(nr), warnings); return error; } /** Store string into a date/time/datetime field. @param from Date/time string @param len Length of the string @param cs Character set of the string @retval TYPE_OK Storage of value went fine without warnings or errors @retval !TYPE_OK Warning/error as indicated by type_conversion_status enum value */ type_conversion_status Field_temporal::store(const char *str, size_t len, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; MYSQL_TIME ltime; MYSQL_TIME_STATUS status; if (convert_str_to_TIME(str, len, cs, <ime, &status)) { /* When convert_str_to_TIME() returns error, ltime has been set to 0 so there's nothing to store in the field. */ reset(); if (status.warnings & (MYSQL_TIME_WARN_ZERO_DATE | MYSQL_TIME_WARN_ZERO_IN_DATE) && !current_thd->is_strict_mode()) error= TYPE_NOTE_TIME_TRUNCATED; else error= TYPE_ERR_BAD_VALUE; } else { error= time_warning_to_type_conversion_status(status.warnings); const type_conversion_status tmp_error= store_internal_with_round(<ime, &status.warnings); // Return the most serious error of the two, see type_conversion_status if (tmp_error > error) error= tmp_error; } if (status.warnings) set_warnings(ErrConvString(str, len, cs), status.warnings); return error; } /** @retval -1 Timestamp with wrong values @retval anything else DATETIME as integer in YYYYMMDDHHMMSS format */ longlong Field_temporal::convert_number_to_datetime(longlong nr, bool unsigned_val, MYSQL_TIME *ltime, int *warnings) { /* Note, number_to_datetime can return a result different from nr: e.g. 111111 -> 20111111000000 */ longlong tmp= number_to_datetime(nr, ltime, date_flags(), warnings); if (tmp == -1LL) reset(); return tmp; } /**************************************************************************** ** Common code for temporal data types with date: DATE, DATETIME, TIMESTAMP *****************************************************************************/ bool Field_temporal_with_date::get_internal_check_zero(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { if (get_date_internal(ltime)) /* '0000-00-00' */ { assert(type() == MYSQL_TYPE_TIMESTAMP); if (fuzzydate & TIME_NO_ZERO_DATE) return true; set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME); } return false; } longlong Field_temporal_with_date::val_date_temporal() { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; return get_date_internal(<ime) ? 0 : TIME_to_longlong_datetime_packed(<ime); } longlong Field_temporal_with_date::val_time_temporal() { /* There are currently no tests covering this method, as DATETIME seems to always superseed over TIME in comparison. */ ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; return get_date_internal(<ime) ? 0 : TIME_to_longlong_time_packed(<ime); } /** Convert a number in format YYMMDDhhmmss to string. Straight coded to avoid problem with slow longlong arithmetic and sprintf. @param[out] pos pointer to convert to. @param tmp number with datetime value. */ static inline int my_datetime_number_to_str(char *pos, longlong tmp) { long part1= (long) (tmp / 1000000LL); long part2= (long) (tmp - (ulonglong) part1 * 1000000LL); int part3; pos+= MAX_DATETIME_WIDTH; /* Start from the end */ *pos--= 0; *pos--= (char) ('0' + (char) (part2 % 10)); /* Seconds */ part2 /= 10; *pos--= (char) ('0' + (char) (part2 % 10)); part3= (int) (part2 / 10); *pos--= ':'; *pos--= (char) ('0' + (char) (part3 % 10)); /* Minutes */ part3 /= 10; *pos--= (char) ('0' + (char) (part3 % 10)); part3 /= 10; *pos--= ':'; *pos--= (char) ('0' + (char) (part3 % 10)); /* Hours */ part3 /= 10; *pos--= (char) ('0' + (char) part3); *pos--= ' '; *pos--= (char) ('0' + (char) (part1 % 10)); /* Day */ part1 /= 10; *pos--= (char) ('0' + (char) (part1 % 10)); part1 /= 10; *pos--= '-'; *pos--= (char) ('0' + (char) (part1 % 10)); /* Month */ part1 /= 10; *pos--= (char) ('0' + (char) (part1 % 10)); part3= (int) (part1 / 10); *pos--= '-'; *pos--= (char) ('0' + (char) (part3 % 10)); /* Year */ part3 /= 10; *pos--= (char) ('0' + (char) (part3 % 10)); part3 /= 10; *pos--= (char) ('0' + (char) (part3 % 10)); part3 /= 10; *pos= (char) ('0'+ (char) part3); return MAX_DATETIME_WIDTH; } String *Field_temporal_with_date::val_str(String *val_buffer, String *val_ptr) { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; val_buffer->alloc(field_length + 1); val_buffer->set_charset(&my_charset_numeric); if (get_date_internal(<ime)) { val_buffer->set_ascii(my_zero_datetime6, field_length); return val_buffer; } make_datetime((Date_time_format *) 0, <ime, val_buffer, dec); return val_buffer; } type_conversion_status Field_temporal_with_date::convert_number_to_TIME(longlong nr, bool unsigned_val, int nanoseconds, MYSQL_TIME *ltime, int *warnings) { if (nr < 0 || nanoseconds < 0) { reset(); *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; return TYPE_WARN_OUT_OF_RANGE; } if (convert_number_to_datetime(nr, unsigned_val, ltime, warnings) == -1LL) return TYPE_ERR_BAD_VALUE; if (ltime->time_type == MYSQL_TIMESTAMP_DATE && nanoseconds) { *warnings|= MYSQL_TIME_WARN_TRUNCATED; return TYPE_NOTE_TRUNCATED; } ltime->second_part= 0; if (datetime_add_nanoseconds_with_round(ltime, nanoseconds, warnings)) { reset(); return TYPE_WARN_OUT_OF_RANGE; } return TYPE_OK; } type_conversion_status Field_temporal_with_date::store_time(MYSQL_TIME *ltime, uint8 dec_arg MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error; int warnings= 0; switch (ltime->time_type) // TS-TODO: split into separate methods? { case MYSQL_TIMESTAMP_DATETIME: case MYSQL_TIMESTAMP_DATE: if (check_date(ltime, non_zero_date(ltime), date_flags(), &warnings)) { assert(warnings & (MYSQL_TIME_WARN_OUT_OF_RANGE | MYSQL_TIME_WARN_ZERO_DATE | MYSQL_TIME_WARN_ZERO_IN_DATE)); error= time_warning_to_type_conversion_status(warnings); reset(); } else error= store_internal_with_round(ltime, &warnings); break; case MYSQL_TIMESTAMP_TIME: { /* Convert TIME to DATETIME */ THD *thd= table ? table->in_use : current_thd; MYSQL_TIME ltime2; time_to_datetime(thd, ltime, <ime2); error= store_internal_with_round(<ime2, &warnings); break; } case MYSQL_TIMESTAMP_NONE: case MYSQL_TIMESTAMP_ERROR: default: warnings|= MYSQL_TIME_WARN_TRUNCATED; reset(); error= TYPE_WARN_TRUNCATED; } if (warnings) set_warnings(ErrConvString(ltime, decimals()), warnings); return error; } bool Field_temporal_with_date::convert_str_to_TIME(const char *str, size_t len, const CHARSET_INFO *cs, MYSQL_TIME *ltime, MYSQL_TIME_STATUS *status) { return str_to_datetime(cs, str, len, ltime, date_flags(), status); } bool Field_temporal_with_date::send_binary(Protocol *protocol) { MYSQL_TIME ltime; if (is_null()) return protocol->store_null(); if (get_date_internal(<ime)) { // Only MYSQL_TYPE_TIMESTAMP can return an error in get_date_internal() assert(type() == MYSQL_TYPE_TIMESTAMP); set_zero_time(<ime, MYSQL_TIMESTAMP_DATETIME); } return protocol->store(<ime, 0); } type_conversion_status Field_temporal_with_date::store_internal_with_round(MYSQL_TIME *ltime, int *warnings) { if (my_datetime_round(ltime, dec, warnings)) { reset(); return time_warning_to_type_conversion_status(*warnings); } else return store_internal(ltime, warnings); } /** Validate date value stored in the field. Now we check whether date value is zero or has zero in date or not and sets warning/error message appropriately(depending on the sql_mode). */ type_conversion_status Field_temporal_with_date::validate_stored_val(THD *thd) { MYSQL_TIME ltime; type_conversion_status error= TYPE_OK; int warnings= 0; if (is_real_null()) return error; memset(<ime, 0, sizeof(MYSQL_TIME)); get_date_internal(<ime); if (check_date(<ime, non_zero_date(<ime), date_flags(), &warnings)) error= time_warning_to_type_conversion_status(warnings); if (warnings) { ltime.time_type = field_type_to_timestamp_type(type()); set_warnings(ErrConvString(<ime, dec), warnings); } return error; } /**************************************************************************** ** Common code for data types with date and time: DATETIME, TIMESTAMP *****************************************************************************/ void Field_temporal_with_date_and_time::store_timestamp(const struct timeval *tm) { ASSERT_COLUMN_MARKED_FOR_WRITE; if (!my_time_fraction_remainder(tm->tv_usec, decimals())) { store_timestamp_internal(tm); return; } struct timeval tm2= *tm; my_timeval_round(&tm2, decimals()); store_timestamp_internal(&tm2); } bool Field_temporal_with_date_and_time::convert_TIME_to_timestamp(THD *thd, const MYSQL_TIME *ltime, struct timeval *tm, int *warnings) { /* No need to do check_date(TIME_NO_ZERO_IN_DATE), because it has been done earlier in store_time(), number_to_datetime() or str_to_datetime(). */ if (datetime_with_no_zero_in_date_to_timeval(thd, ltime, tm, warnings)) { tm->tv_sec= tm->tv_usec= 0; return true; } return false; } void Field_temporal_with_date_and_time::init_timestamp_flags() { if (unireg_check != NONE) { /* This TIMESTAMP column is hereby quietly assumed to have an insert or update default function. */ flags|= TIMESTAMP_FLAG; if (unireg_check != TIMESTAMP_DN_FIELD) flags|= ON_UPDATE_NOW_FLAG; } } /**************************************************************************** ** Common code for DATETIME(N) and TIMESTAMP(N) *****************************************************************************/ double Field_temporal_with_date_and_timef::val_real() { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; return get_date_internal(<ime) ? 0 : TIME_to_double_datetime(<ime); } longlong Field_temporal_with_date_and_timef::val_int() { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; return get_date_internal(<ime) ? 0 : TIME_to_ulonglong_datetime_round(<ime); } my_decimal *Field_temporal_with_date_and_timef::val_decimal(my_decimal *dec_arg) { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; if (get_date_internal(<ime)) { // Only MYSQL_TYPE_TIMESTAMP can return an error in get_date_internal() assert(type() == MYSQL_TYPE_TIMESTAMP); set_zero_time(<ime, MYSQL_TIMESTAMP_DATETIME); } return date2my_decimal(<ime, dec_arg); } /** TIMESTAMP type columns hold date and time values in the range 1970-01-01 00:00:01 UTC to 2038-01-01 00:00:00 UTC, stored as number of seconds since the start of the Unix Epoch (1970-01-01 00:00:01 UTC.) TIMESTAMP columns can be automatically set on row updates to and/or have CURRENT_TIMESTAMP as default value for inserts. The implementation of function defaults is heavily entangled with the binary .frm file format. The @c utype @c enum is part of the file format specification but is declared a member of the Field class. This constructor accepts a unireg_check value to initialize the column default expression. Five distinct unireg_check values are used for TIMESTAMP columns to distinguish various cases of DEFAULT or ON UPDATE values. These values are: - TIMESTAMP_OLD_FIELD - old timestamp, this has no significance when creating a the Field_timestamp. - TIMESTAMP_DN_FIELD - means TIMESTAMP DEFAULT CURRENT_TIMESTAMP. - TIMESTAMP_UN_FIELD - means TIMESTAMP DEFAULT <default value> ON UPDATE CURRENT_TIMESTAMP, where <default value> is an implicit or explicit expression other than CURRENT_TIMESTAMP or any synonym thereof (e.g. NOW().) - TIMESTAMP_DNUN_FIELD - means DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP. - NONE - means that the column has neither DEFAULT CURRENT_TIMESTAMP, nor ON UPDATE CURRENT_TIMESTAMP Note that columns with TIMESTAMP_OLD_FIELD are no longer created explicitly, the value is meant to preserve the ability to read tables from old databases. Such columns are replaced with their newer counterparts by CREATE TABLE and SHOW CREATE TABLE. This is because we want to prefer NONE unireg_check over TIMESTAMP_OLD_FIELD for "TIMESTAMP DEFAULT 'Const'" field. (Old TIMESTAMP columns allowed such definitions as well but ignored the default value for first the TIMESTAMP column. This is, of course, non-standard.) In most cases a user won't notice any change, only exception being different behavior of old/new TIMESTAMPS columns during ALTER TABLE. */ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg) :Field_temporal_with_date_and_time(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 0) { init_timestamp_flags(); /* For 4.0 MYD and 4.0 InnoDB compatibility */ flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; } Field_timestamp::Field_timestamp(bool maybe_null_arg, const char *field_name_arg) :Field_temporal_with_date_and_time((uchar *) 0, maybe_null_arg ? (uchar *) "" : 0, 0, NONE, field_name_arg, 0) { init_timestamp_flags(); /* For 4.0 MYD and 4.0 InnoDB compatibility */ flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; } my_time_flags_t Field_timestamp::date_flags(const THD *thd) { /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ my_time_flags_t date_flags= TIME_NO_ZERO_IN_DATE; if (thd->variables.sql_mode & MODE_NO_ZERO_DATE) date_flags|= TIME_NO_ZERO_DATE; return date_flags; } type_conversion_status Field_timestamp::store_internal(const MYSQL_TIME *ltime, int *warnings) { THD *thd= table ? table->in_use : current_thd; struct timeval tm; convert_TIME_to_timestamp(thd, ltime, &tm, warnings); const type_conversion_status error= time_warning_to_type_conversion_status(*warnings); store_timestamp_internal(&tm); return error; } /** Get a value from record, without checking fuzzy date flags. @retval true - if timestamp is 0, ltime is not touched in this case. @retval false - if timestamp is non-zero. */ bool Field_timestamp::get_date_internal(MYSQL_TIME *ltime) { ASSERT_COLUMN_MARKED_FOR_READ; uint32 temp; THD *thd= table ? table->in_use : current_thd; #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) temp= uint4korr(ptr); else #endif ulongget(&temp, ptr); if (!temp) return true; thd->time_zone()->gmt_sec_to_TIME(ltime, (my_time_t) temp); return false; } /** Get TIMESTAMP field value as seconds since begging of Unix Epoch */ bool Field_timestamp::get_timestamp(struct timeval *tm, int *warnings) { if (is_null()) return true; tm->tv_usec= 0; #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) { tm->tv_sec= sint4korr(ptr); return false; } #endif int32 tmp; longget(&tmp, ptr); tm->tv_sec= tmp; return false; } void Field_timestamp::store_timestamp_internal(const struct timeval *tm) { #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) { int4store(ptr, tm->tv_sec); } else #endif longstore(ptr, (uint32) tm->tv_sec); } type_conversion_status Field_timestamp::store_packed(longlong nr) { /* Make sure the stored value was previously properly rounded or truncated */ assert((MY_PACKED_TIME_GET_FRAC_PART(nr) % (int) log_10_int[DATETIME_MAX_DECIMALS - decimals()]) == 0); MYSQL_TIME ltime; TIME_from_longlong_datetime_packed(<ime, nr); return Field_timestamp::store_time(<ime, 0); } longlong Field_timestamp::val_int() { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; return get_date_internal(<ime) ? 0 : TIME_to_ulonglong_datetime(<ime); } bool Field_timestamp::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { /* Don't do check_fuzzy_date() as month and year are never 0 for timestamp */ return get_internal_check_zero(ltime, fuzzydate); } int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr) { int32 a,b; #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) { a=sint4korr(a_ptr); b=sint4korr(b_ptr); } else #endif { longget(&a, a_ptr); longget(&b, b_ptr); } return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; } void Field_timestamp::make_sort_key(uchar *to, size_t length MY_ATTRIBUTE((unused))) { #ifdef WORDS_BIGENDIAN if (!table || !table->s->db_low_byte_first) { to[0] = ptr[0]; to[1] = ptr[1]; to[2] = ptr[2]; to[3] = ptr[3]; } else #endif { to[0] = ptr[3]; to[1] = ptr[2]; to[2] = ptr[1]; to[3] = ptr[0]; } } void Field_timestamp::sql_type(String &res) const { res.set_ascii(STRING_WITH_LEN("timestamp")); } type_conversion_status Field_timestamp::validate_stored_val(THD *thd) { /* While deprecating "TIMESTAMP with implicit DEFAULT value", we can remove this function implementation and depend directly on "Field_temporal_with_date::validate_stored_val" */ if (!thd->variables.explicit_defaults_for_timestamp) return TYPE_OK; return (Field_temporal_with_date::validate_stored_val(thd)); } /**************************************************************************** ** timestamp(N) type ** In string context: YYYY-MM-DD HH:MM:SS.FFFFFF ** In number context: YYYYMMDDHHMMSS.FFFFFF ** Stored as a 7 byte value ****************************************************************************/ Field_timestampf::Field_timestampf(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, uint8 dec_arg) :Field_temporal_with_date_and_timef(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg) { init_timestamp_flags(); } Field_timestampf::Field_timestampf(bool maybe_null_arg, const char *field_name_arg, uint8 dec_arg) :Field_temporal_with_date_and_timef((uchar*) 0, maybe_null_arg ? (uchar*) "": 0, 0, NONE, field_name_arg, dec_arg) { if (unireg_check != TIMESTAMP_DN_FIELD) flags|= ON_UPDATE_NOW_FLAG; } my_time_flags_t Field_timestampf::date_flags(const THD *thd) { /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ my_time_flags_t date_flags= TIME_NO_ZERO_IN_DATE; if (thd->variables.sql_mode & MODE_NO_ZERO_DATE) date_flags|= TIME_NO_ZERO_DATE; return date_flags; } type_conversion_status Field_timestampf::reset() { memset(ptr, 0, pack_length()); return TYPE_OK; } void Field_timestampf::store_timestamp_internal(const struct timeval *tm) { my_timestamp_to_binary(tm, ptr, dec); } type_conversion_status Field_timestampf::store_internal(const MYSQL_TIME *ltime, int *warnings) { THD *thd= table ? table->in_use : current_thd; struct timeval tm; convert_TIME_to_timestamp(thd, ltime, &tm, warnings); const type_conversion_status error= time_warning_to_type_conversion_status(*warnings); store_timestamp_internal(&tm); return error; } type_conversion_status Field_timestampf::store_packed(longlong nr) { MYSQL_TIME ltime; TIME_from_longlong_datetime_packed(<ime, nr); return Field_timestampf::store_time(<ime, dec); } bool Field_timestampf::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { /* Don't do check_fuzzy_date() as month and year are never 0 for timestamp */ return get_internal_check_zero(ltime, fuzzydate); } void Field_timestampf::sql_type(String &res) const { if (dec == 0) { res.set_ascii(STRING_WITH_LEN("timestamp")); return; } const CHARSET_INFO *cs= res.charset(); res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(), "timestamp(%d)", dec)); } bool Field_timestampf::get_date_internal(MYSQL_TIME *ltime) { THD *thd= table ? table->in_use : current_thd; struct timeval tm; my_timestamp_from_binary(&tm, ptr, dec); if (tm.tv_sec == 0) return true; thd->time_zone()->gmt_sec_to_TIME(ltime, tm); return false; } bool Field_timestampf::get_timestamp(struct timeval *tm, int *warnings) { THD *thd= table ? table->in_use : current_thd; thd->time_zone_used= 1; assert(!is_null()); my_timestamp_from_binary(tm, ptr, dec); return false; } type_conversion_status Field_timestampf::validate_stored_val(THD *thd) { /* While deprecating "TIMESTAMP with implicit DEFAULT value", we can remove this function implementation and depend directly on "Field_temporal_with_date::validate_stored_val" */ if (!thd->variables.explicit_defaults_for_timestamp) return TYPE_OK; return (Field_temporal_with_date::validate_stored_val(thd)); } /**************************************************************************** ** TIME and TIME(N) common methods ****************************************************************************/ bool Field_time_common::convert_str_to_TIME(const char *str, size_t len, const CHARSET_INFO *cs, MYSQL_TIME *ltime, MYSQL_TIME_STATUS *status) { return str_to_time(cs, str, len, ltime, 0, status); } type_conversion_status Field_time_common::convert_number_to_TIME(longlong nr, bool unsigned_val, int nanoseconds, MYSQL_TIME *ltime, int *warnings) { if (unsigned_val && nr < 0) { *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; set_max_time(ltime, 0); store_internal(ltime, warnings); return TYPE_WARN_OUT_OF_RANGE; } if (number_to_time(nr, ltime, warnings)) { store_internal(ltime, warnings); return TYPE_WARN_OUT_OF_RANGE; } /* Both number_to_time() call and negative nanoseconds value affect ltime->neg, hence "|=" to combine them: */ if ((ltime->neg|= (nanoseconds < 0))) nanoseconds= -nanoseconds; ltime->second_part= 0; bool round_error= time_add_nanoseconds_with_round(ltime, nanoseconds, warnings); return round_error ? time_warning_to_type_conversion_status(*warnings) : TYPE_OK; } type_conversion_status Field_time_common::store_time(MYSQL_TIME *ltime, uint8 dec_arg MY_ATTRIBUTE((unused))) { /* Check if seconds or minutes are out of range */ if (ltime->second >= 60 || ltime->minute >= 60) { set_warnings(ErrConvString(ltime, decimals()), MYSQL_TIME_WARN_OUT_OF_RANGE); reset(); return TYPE_WARN_OUT_OF_RANGE; } int warnings= 0; return store_internal_with_round(ltime, &warnings); } type_conversion_status Field_time_common::store_internal_with_round(MYSQL_TIME *ltime, int *warnings) { if (my_time_round(ltime, dec)) return TYPE_WARN_OUT_OF_RANGE; return store_internal(ltime, warnings); } String *Field_time_common::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); val_buffer->set_charset(&my_charset_numeric); if (get_time(<ime)) { assert(0); set_zero_time(<ime, MYSQL_TIMESTAMP_TIME); } make_time((Date_time_format *) 0, <ime, val_buffer, dec); return val_buffer; } /** For a column for TIME type, get_date() takes the time value of the field, adds current date to it and returns the result as a DATETIME value. */ bool Field_time_common::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME tm; if (get_time(&tm)) { assert(0); set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); } time_to_datetime(table ? table->in_use : current_thd, &tm, ltime); return false; } longlong Field_time_common::val_date_temporal() { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME time, datetime; if (get_time(&time)) { assert(0); // Field_time*::get_time should not fail return 0; } time_to_datetime(table ? table->in_use : current_thd, &time, &datetime); return TIME_to_longlong_datetime_packed(&datetime); } bool Field_time_common::send_binary(Protocol *protocol) { MYSQL_TIME ltime; if (is_null()) return protocol->store_null(); if (get_time(<ime)) { assert(0); set_zero_time(<ime, MYSQL_TIMESTAMP_TIME); } ltime.day= ltime.hour / 24; // Move hours to days ltime.hour-= ltime.day * 24; return protocol->store_time(<ime, 0); } /**************************************************************************** ** time type ** In string context: HH:MM:SS ** In number context: HHMMSS ** Stored as a 3 byte unsigned int ****************************************************************************/ type_conversion_status Field_time::store_internal(const MYSQL_TIME *ltime, int *warnings) { long tmp= ((ltime->month ? 0 : ltime->day * 24L) + ltime->hour) * 10000L + (ltime->minute * 100 + ltime->second); if (ltime->neg) tmp= -tmp; int3store(ptr, tmp); return TYPE_OK; } type_conversion_status Field_time::store_packed(longlong nr) { MYSQL_TIME ltime; TIME_from_longlong_time_packed(<ime, nr); return Field_time::store_time(<ime, 0); } longlong Field_time::val_time_temporal() { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; return get_time(<ime) ? 0 : TIME_to_longlong_time_packed(<ime); } longlong Field_time::val_int() { ASSERT_COLUMN_MARKED_FOR_READ; return (longlong) sint3korr(ptr); } bool Field_time::get_time(MYSQL_TIME *ltime) { long tmp=(long) sint3korr(ptr); if ((ltime->neg= tmp < 0)) tmp= -tmp; ltime->year= ltime->month= ltime->day= 0; TIME_set_hhmmss(ltime, tmp); ltime->second_part=0; ltime->time_type= MYSQL_TIMESTAMP_TIME; return false; } int Field_time::cmp(const uchar *a_ptr, const uchar *b_ptr) { int32 a,b; a= sint3korr(a_ptr); b= sint3korr(b_ptr); return (a < b) ? -1 : (a > b) ? 1 : 0; } void Field_time::make_sort_key(uchar *to, size_t length) { assert(length >= 3); to[0] = (uchar) (ptr[2] ^ 128); to[1] = ptr[1]; to[2] = ptr[0]; } void Field_time::sql_type(String &res) const { res.set_ascii(STRING_WITH_LEN("time")); } /**************************************************************************** ** time type with fsp ** In string context: HH:MM:SS.FFFFFF ** In number context: HHMMSS.FFFFFF ****************************************************************************/ longlong Field_timef::val_int() { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; if (get_time(<ime)) { assert(0); set_zero_time(<ime, MYSQL_TIMESTAMP_TIME); } longlong tmp= (longlong) TIME_to_ulonglong_time_round(<ime); return ltime.neg ? -tmp : tmp; } my_decimal *Field_timef::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; if (get_time(<ime)) { assert(0); set_zero_time(<ime, MYSQL_TIMESTAMP_TIME); } return time2my_decimal(<ime, decimal_value); } double Field_timef::val_real() { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; if (get_time(<ime)) { assert(0); return 0; } double tmp= TIME_to_double_time(<ime); return ltime.neg ? -tmp : tmp; } void Field_timef::sql_type(String &res) const { if (dec == 0) { res.set_ascii(STRING_WITH_LEN("time")); return; } const CHARSET_INFO *cs= res.charset(); res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), "time(%d)", dec)); } type_conversion_status Field_timef::reset() { return store_packed(0); } type_conversion_status Field_timef::store_packed(longlong nr) { my_time_packed_to_binary(nr, ptr, dec); return TYPE_OK; } longlong Field_timef::val_time_temporal() { ASSERT_COLUMN_MARKED_FOR_READ; return my_time_packed_from_binary(ptr, dec); } type_conversion_status Field_timef::store_internal(const MYSQL_TIME *ltime, int *warnings) { type_conversion_status rc= store_packed(TIME_to_longlong_time_packed(ltime)); if (rc == TYPE_OK && non_zero_date(ltime)) { /* The DATE part got lost; we warn, like in Field_newdate::store_internal, and trigger some code in get_mm_leaf() (see err==TYPE_NOTE_TIME_TRUNCATED there). */ *warnings|= MYSQL_TIME_NOTE_TRUNCATED; rc= TYPE_NOTE_TIME_TRUNCATED; } return rc; } bool Field_timef::get_time(MYSQL_TIME *ltime) { longlong tmp= val_time_temporal(); TIME_from_longlong_time_packed(ltime, tmp); return false; } /**************************************************************************** ** year type ** Save in a byte the year 0, 1901->2155 ** Can handle 2 byte or 4 byte years! ****************************************************************************/ type_conversion_status Field_year::store(const char *from, size_t len,const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; char *end; int conv_error; type_conversion_status ret= TYPE_OK; longlong nr= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &conv_error); if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155 || conv_error == MY_ERRNO_ERANGE) { *ptr=0; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return TYPE_WARN_OUT_OF_RANGE; } if (conv_error) ret= TYPE_ERR_BAD_VALUE; if (table->in_use->count_cuted_fields) ret= check_int(cs, from, len, end, conv_error); if (ret != TYPE_OK) { if (ret == TYPE_ERR_BAD_VALUE) /* empty or incorrect string */ { *ptr= 0; // Invalid date return ret; } ret= TYPE_WARN_OUT_OF_RANGE; } 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 ret; } type_conversion_status Field_year::store(double nr) { if (nr < 0.0 || nr > 2155.0) { (void) Field_year::store((longlong) -1, FALSE); return TYPE_WARN_OUT_OF_RANGE; } return Field_year::store((longlong) nr, FALSE); } type_conversion_status Field_year::store_time(MYSQL_TIME *ltime, uint8 dec_arg MY_ATTRIBUTE((unused))) { if (ltime->time_type != MYSQL_TIMESTAMP_DATETIME && ltime->time_type != MYSQL_TIMESTAMP_DATE) { /* Convert time to datetime, then store year of the result */ THD *thd= table ? table->in_use : current_thd; MYSQL_TIME ltime2; time_to_datetime(thd, ltime, <ime2); return store(ltime2.year, 0); } return store(ltime->year, 0); } type_conversion_status Field_year::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155) { *ptr= 0; set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return TYPE_WARN_OUT_OF_RANGE; } 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 TYPE_OK; } bool Field_year::send_binary(Protocol *protocol) { ASSERT_COLUMN_MARKED_FOR_READ; if (is_null()) return protocol->store_null(); ulonglong tmp= Field_year::val_int(); return protocol->store_short(tmp); } double Field_year::val_real(void) { return (double) Field_year::val_int(); } longlong Field_year::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; 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 MY_ATTRIBUTE((unused))) { 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; } void Field_year::sql_type(String &res) const { const CHARSET_INFO *cs=res.charset(); res.length(cs->cset->snprintf(cs,(char*)res.ptr(),res.alloced_length(), "year(%d)",(int) field_length)); } /**************************************************************************** ** The new date type ** Stored as 3 bytes ** In number context: YYYYMMDD ****************************************************************************/ my_time_flags_t Field_newdate::date_flags(const THD *thd) { my_time_flags_t date_flags= TIME_FUZZY_DATE; if (thd->variables.sql_mode & MODE_NO_ZERO_DATE) date_flags|= TIME_NO_ZERO_DATE; if (thd->variables.sql_mode & MODE_NO_ZERO_IN_DATE) date_flags|= TIME_NO_ZERO_IN_DATE; if (thd->variables.sql_mode & MODE_INVALID_DATES) date_flags|= TIME_INVALID_DATES; return date_flags; } type_conversion_status Field_newdate::store_internal(const MYSQL_TIME *ltime, int *warnings) { long tmp= ltime->day + ltime->month * 32 + ltime->year * 16 * 32; int3store(ptr, tmp); if (non_zero_time(ltime)) { *warnings|= MYSQL_TIME_NOTE_TRUNCATED; return TYPE_NOTE_TIME_TRUNCATED; } return TYPE_OK; } bool Field_newdate::get_date_internal(MYSQL_TIME *ltime) { uint32 tmp= uint3korr(ptr); ltime->day= tmp & 31; ltime->month= (tmp >> 5) & 15; ltime->year= (tmp >> 9); ltime->time_type= MYSQL_TIMESTAMP_DATE; ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0; return false; } type_conversion_status Field_newdate::store_packed(longlong nr) { int warnings= 0; MYSQL_TIME ltime; TIME_from_longlong_date_packed(<ime, nr); return store_internal(<ime, &warnings); } bool Field_newdate::send_binary(Protocol *protocol) { MYSQL_TIME ltime; if (is_null()) return protocol->store_null(); get_date(<ime, 0); return protocol->store_date(<ime); } longlong Field_newdate::val_int() { ASSERT_COLUMN_MARKED_FOR_READ; ulong j= uint3korr(ptr); j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L; return (longlong) j; } longlong Field_newdate::val_date_temporal() { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; return get_date_internal(<ime) ? 0 : TIME_to_longlong_date_packed(<ime); } longlong Field_newdate::val_time_temporal() { ASSERT_COLUMN_MARKED_FOR_READ; return 0; } String *Field_newdate::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; val_buffer->alloc(field_length); val_buffer->length(field_length); uint32 tmp= 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_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { return get_internal_check_zero(ltime, fuzzydate) || check_fuzzy_date(ltime, fuzzydate); } int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr) { uint32 a,b; a= uint3korr(a_ptr); b= uint3korr(b_ptr); return (a < b) ? -1 : (a > b) ? 1 : 0; } void Field_newdate::make_sort_key(uchar *to, size_t length) { assert(length >= 3); to[0] = ptr[2]; to[1] = ptr[1]; to[2] = ptr[0]; } void Field_newdate::sql_type(String &res) const { res.set_ascii(STRING_WITH_LEN("date")); } /**************************************************************************** ** datetime type ** In string context: YYYY-MM-DD HH:MM:DD ** In number context: YYYYMMDDHHMMDD ** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int. ****************************************************************************/ my_time_flags_t Field_datetime::date_flags(const THD *thd) { my_time_flags_t date_flags= TIME_FUZZY_DATE; if (thd->variables.sql_mode & MODE_NO_ZERO_DATE) date_flags|= TIME_NO_ZERO_DATE; if (thd->variables.sql_mode & MODE_NO_ZERO_IN_DATE) date_flags|= TIME_NO_ZERO_IN_DATE; if (thd->variables.sql_mode & MODE_INVALID_DATES) date_flags|= TIME_INVALID_DATES; return date_flags; } void Field_datetime::store_timestamp_internal(const timeval *tm) { MYSQL_TIME mysql_time; THD *thd= current_thd; thd->variables.time_zone->gmt_sec_to_TIME(&mysql_time, *tm); thd->time_zone_used= true; int error= 0; store_internal(&mysql_time, &error); } /** Store a DATETIME in a 8-byte integer to record. @param table Table @param tmp The number, in YYYYMMDDhhmmss format @param ptr Where to store to */ static inline type_conversion_status datetime_store_internal(TABLE *table, ulonglong tmp, uchar *ptr) { #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) { int8store(ptr, tmp); } else #endif longlongstore(ptr, tmp); return TYPE_OK; } /** Read a DATETIME from record to a 8-byte integer @param table Table @param ptr Where to read from @retval An integer in format YYYYMMDDhhmmss */ static inline longlong datetime_get_internal(TABLE *table, uchar *ptr) { longlong tmp; #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) tmp= sint8korr(ptr); else #endif longlongget(&tmp, ptr); return tmp; } bool Field_datetime::get_date_internal(MYSQL_TIME *ltime) { longlong tmp= datetime_get_internal(table, ptr); ltime->time_type= MYSQL_TIMESTAMP_DATETIME; ltime->neg= 0; ltime->second_part= 0; TIME_set_yymmdd(ltime, (uint) (tmp / 1000000LL)); TIME_set_hhmmss(ltime, (uint) (tmp % 1000000LL)); return false; } type_conversion_status Field_datetime::store_internal(const MYSQL_TIME *ltime, int *warnings) { ulonglong tmp= TIME_to_ulonglong_datetime(ltime); return datetime_store_internal(table, tmp, ptr); } type_conversion_status Field_datetime::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; MYSQL_TIME ltime; int warnings; type_conversion_status error= TYPE_OK; longlong tmp= convert_number_to_datetime(nr, unsigned_val, <ime, &warnings); if (tmp == -1LL) error= TYPE_ERR_BAD_VALUE; else { error= time_warning_to_type_conversion_status(warnings); datetime_store_internal(table, tmp, ptr); } if (warnings) set_warnings(ErrConvString(nr, unsigned_val), warnings); return error; } type_conversion_status Field_datetime::store_packed(longlong nr) { MYSQL_TIME ltime; TIME_from_longlong_datetime_packed(<ime, nr); return Field_datetime::store_time(<ime, 0); } longlong Field_datetime::val_int() { ASSERT_COLUMN_MARKED_FOR_READ; return datetime_get_internal(table, ptr); } /* We don't reuse the parent method for performance purposes, to avoid convertion from number to MYSQL_TIME. Using my_datetime_number_to_str() instead of my_datetime_to_str(). */ String *Field_datetime::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; val_buffer->alloc(field_length + 1); val_buffer->set_charset(&my_charset_numeric); val_buffer->length(MAX_DATETIME_WIDTH); longlong tmp= datetime_get_internal(table, ptr); val_buffer->length(my_datetime_number_to_str((char *) val_buffer->ptr(), tmp)); return val_buffer; } bool Field_datetime::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { return get_internal_check_zero(ltime, fuzzydate) || check_fuzzy_date(ltime, fuzzydate); } int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr) { longlong a,b; #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) { a=sint8korr(a_ptr); b=sint8korr(b_ptr); } else #endif { longlongget(&a, a_ptr); longlongget(&b, b_ptr); } return ((ulonglong) a < (ulonglong) b) ? -1 : ((ulonglong) a > (ulonglong) b) ? 1 : 0; } void Field_datetime::make_sort_key(uchar *to, size_t length) { const size_t pack_length= PACK_LENGTH; const size_t to_length= min(pack_length, length); #ifdef WORDS_BIGENDIAN if (!table || !table->s->db_low_byte_first) copy_integer<true>(to, to_length, ptr, pack_length, true); else #endif copy_integer<false>(to, to_length, ptr, pack_length, true); } void Field_datetime::sql_type(String &res) const { res.set_ascii(STRING_WITH_LEN("datetime")); } /**************************************************************************** ** datetimef type ** In string context: YYYY-MM-DD HH:MM:DD.FFFFFF ** In number context: YYYYMMDDHHMMDD.FFFFFF ** Stored as a 8 byte value. ****************************************************************************/ my_time_flags_t Field_datetimef::date_flags(const THD *thd) { my_time_flags_t date_flags= TIME_FUZZY_DATE; if (thd->variables.sql_mode & MODE_NO_ZERO_DATE) date_flags|= TIME_NO_ZERO_DATE; if (thd->variables.sql_mode & MODE_NO_ZERO_IN_DATE) date_flags|= TIME_NO_ZERO_IN_DATE; if (thd->variables.sql_mode & MODE_INVALID_DATES) date_flags|= TIME_INVALID_DATES; return date_flags; } void Field_datetimef::store_timestamp_internal(const timeval *tm) { MYSQL_TIME mysql_time; THD *thd= current_thd; thd->variables.time_zone->gmt_sec_to_TIME(&mysql_time, *tm); thd->time_zone_used= true; int warnings= 0; store_internal(&mysql_time, &warnings); } bool Field_datetimef::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { return get_internal_check_zero(ltime, fuzzydate) || check_fuzzy_date(ltime, fuzzydate); } void Field_datetimef::sql_type(String &res) const { if (dec == 0) { res.set_ascii(STRING_WITH_LEN("datetime")); return; } const CHARSET_INFO *cs= res.charset(); res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(), "datetime(%d)", dec)); } bool Field_datetimef::get_date_internal(MYSQL_TIME *ltime) { TIME_from_longlong_datetime_packed(ltime, val_date_temporal()); return false; } type_conversion_status Field_datetimef::store_internal(const MYSQL_TIME *ltime, int *warnings) { store_packed(TIME_to_longlong_datetime_packed(ltime)); return TYPE_OK; } type_conversion_status Field_datetimef::reset() { store_packed(0); return TYPE_OK; } longlong Field_datetimef::val_date_temporal() { return my_datetime_packed_from_binary(ptr, dec); } type_conversion_status Field_datetimef::store_packed(longlong nr) { my_datetime_packed_to_binary(nr, ptr, dec); return TYPE_OK; } /**************************************************************************** ** 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. 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" @param original_string this is is the original string that was supposed to be copied. Helps in keeping track of whether a value has been completely truncated. @param well_formed_error_pos position of the first non-wellformed character in the source string @param cannot_convert_error_pos position of the first non-convertable character in the source string @param from_end_pos position where conversion stopped in the source string @param end end of the source string @param count_spaces treat trailing spaces as important data @param cs character set of the string @return TYPE_OK, TYPE_NOTE_TRUNCATED, TYPE_WARN_TRUNCATED, TYPE_WARN_INVALID_STRING */ type_conversion_status Field_longstr::check_string_copy_error(const char *original_string, const char *well_formed_error_pos, const char *cannot_convert_error_pos, const char *from_end_pos, const char *end, bool count_spaces, const CHARSET_INFO *cs) { const char *pos; char tmp[32]; THD *thd= table->in_use; if (!(pos= well_formed_error_pos) && !(pos= cannot_convert_error_pos)) return report_if_important_data(from_end_pos, end, count_spaces); convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6); push_warning_printf(thd, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "string", tmp, field_name, thd->get_stmt_da()->current_row_for_condition()); if (well_formed_error_pos != NULL) return TYPE_WARN_INVALID_STRING; return TYPE_WARN_TRUNCATED; } /* 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 TYPE_OK - None was truncated != TYPE_OK - Some bytes were 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. */ type_conversion_status Field_longstr::report_if_important_data(const char *pstr, const char *end, bool count_spaces) { if (pstr < end) // String is truncated { if (test_if_important_data(field_charset, pstr, end)) { // Warning should only be written when count_cuted_fields is set if (table->in_use->count_cuted_fields) { if (!table->in_use->lex->is_ignore() && table->in_use->is_strict_mode()) set_warning(Sql_condition::SL_WARNING, ER_DATA_TOO_LONG, 1); else set_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); } return TYPE_WARN_TRUNCATED; } else if (count_spaces) { /* If we lost only spaces then produce a NOTE, not a WARNING */ if (table->in_use->count_cuted_fields) { set_warning(Sql_condition::SL_NOTE, WARN_DATA_TRUNCATED, 1); } return TYPE_NOTE_TRUNCATED; } } return TYPE_OK; } /* Copy a string and fill with space */ type_conversion_status Field_string::store(const char *from, size_t length,const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; size_t copy_length; const char *well_formed_error_pos; const char *cannot_convert_error_pos; const char *from_end_pos; /* See the comment for Field_long::store(long long) */ assert(table->in_use == current_thd); copy_length= field_well_formed_copy_nchars(field_charset, (char*) ptr, field_length, cs, from, length, field_length / field_charset->mbmaxlen, &well_formed_error_pos, &cannot_convert_error_pos, &from_end_pos); /* Append spaces if the string was shorter than the field. */ if (copy_length < field_length) field_charset->cset->fill(field_charset,(char*) ptr+copy_length, field_length-copy_length, field_charset->pad_char); return check_string_copy_error(from, well_formed_error_pos, cannot_convert_error_pos, from_end_pos, from + length, false, cs); } /** Store double value in Field_string or Field_varstring. Pretty prints double number into field_length characters buffer. @param nr number */ type_conversion_status Field_str::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; uint local_char_length= field_length / charset()->mbmaxlen; size_t length= 0; my_bool error= (local_char_length == 0); // my_gcvt() requires width > 0, and we may have a CHAR(0) column. if (!error) length= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, local_char_length, buff, &error); if (error) { if (!table->in_use->lex->is_ignore() && table->in_use->is_strict_mode()) set_warning(Sql_condition::SL_WARNING, ER_DATA_TOO_LONG, 1); else set_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); } return store(buff, length, &my_charset_numeric); } /** Check whether generated columns' expressions are the same. @param field An existing field to compare against @return true means the same, otherwise not. */ bool Field::gcol_expr_is_equal(const Field *field) const { assert(is_gcol() && field->is_gcol()); return gcol_info->expr_item->eq(field->gcol_info->expr_item, true); } /** Check whether generated columns' expressions are the same. @param field A new field to compare against @return true means the same, otherwise not. */ bool Field::gcol_expr_is_equal(const Create_field *field) const { assert(is_gcol() && field->is_gcol()); return ::is_equal(&gcol_info->expr_str, &field->gcol_info->expr_str); } uint Field::is_equal(Create_field *new_field) { return (new_field->sql_type == real_type()); } uint Field_str::is_equal(Create_field *new_field) { return ((new_field->sql_type == real_type()) && new_field->charset == field_charset && new_field->length == max_display_length()); } type_conversion_status Field_string::store(longlong nr, bool unsigned_val) { char buff[64]; size_t l; const CHARSET_INFO *cs=charset(); l= (cs->cset->longlong10_to_str)(cs,buff,sizeof(buff), unsigned_val ? 10 : -10, nr); return Field_string::store(buff, l, cs); } type_conversion_status Field_longstr::store_decimal(const my_decimal *d) { char buff[DECIMAL_MAX_STR_LENGTH+1]; String str(buff, sizeof(buff), &my_charset_numeric); my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str); return store(str.ptr(), str.length(), str.charset()); } uint32 Field_longstr::max_data_length() const { return field_length + (field_length > 255 ? 2 : 1); } double Field_string::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int error; char *end; const CHARSET_INFO *cs= charset(); double result; result= my_strntod(cs,(char*) ptr,field_length,&end,&error); if (!table->in_use->no_errors && (error || (field_length != (uint32)(end - (char*) ptr) && !check_if_only_end_space(cs, end, (char*) ptr + field_length)))) { ErrConvString err((char*) ptr, field_length, cs); push_warning_printf(current_thd, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE", err.ptr()); } return result; } longlong Field_string::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; int error; char *end; const CHARSET_INFO *cs= charset(); longlong result; result= my_strntoll(cs, (char*) ptr,field_length,10,&end,&error); if (!table->in_use->no_errors && (error || (field_length != (uint32)(end - (char*) ptr) && !check_if_only_end_space(cs, end, (char*) ptr + field_length)))) { ErrConvString err((char*) ptr, field_length, cs); push_warning_printf(current_thd, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", err.ptr()); } return result; } String *Field_string::val_str(String *val_buffer MY_ATTRIBUTE((unused)), String *val_ptr) { ASSERT_COLUMN_MARKED_FOR_READ; /* See the comment for Field_long::store(long long) */ assert(table->in_use == current_thd); size_t length; if (table->in_use->variables.sql_mode & MODE_PAD_CHAR_TO_FULL_LENGTH) length= my_charpos(field_charset, ptr, ptr + field_length, field_length / field_charset->mbmaxlen); else length= field_charset->cset->lengthsp(field_charset, (const char*) ptr, field_length); val_ptr->set((const char*) ptr, length, field_charset); return val_ptr; } my_decimal *Field_string::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; int err= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr, field_length, charset(), decimal_value); if (!table->in_use->no_errors && err) { ErrConvString errmsg((char*) ptr, field_length, charset()); push_warning_printf(current_thd, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL", errmsg.ptr()); } return decimal_value; } struct Check_field_param { Field *field; }; #ifdef HAVE_REPLICATION static bool check_field_for_37426(const void *param_arg) { Check_field_param *param= (Check_field_param*) param_arg; assert(param->field->real_type() == MYSQL_TYPE_STRING); DBUG_PRINT("debug", ("Field %s - type: %d, size: %d", param->field->field_name, param->field->real_type(), param->field->row_pack_length())); return param->field->row_pack_length() > 255; } #endif bool Field_string::compatible_field_size(uint field_metadata, Relay_log_info *rli_arg, uint16 mflags MY_ATTRIBUTE((unused)), int *order_var) { #ifdef HAVE_REPLICATION const Check_field_param check_param = { this }; if (!is_mts_worker(rli_arg->info_thd) && rpl_master_has_bug(rli_arg, 37426, TRUE, check_field_for_37426, &check_param)) return FALSE; // Not compatible field sizes #endif return Field::compatible_field_size(field_metadata, rli_arg, mflags, order_var); } int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) { size_t a_len, b_len; if (field_charset->mbmaxlen != 1) { uint char_len= field_length/field_charset->mbmaxlen; a_len= my_charpos(field_charset, a_ptr, a_ptr + field_length, char_len); b_len= my_charpos(field_charset, b_ptr, b_ptr + field_length, char_len); } else a_len= b_len= field_length; /* We have to remove end space to be able to compare multi-byte-characters like in latin_de 'ae' and 0xe4 */ return field_charset->coll->strnncollsp(field_charset, a_ptr, a_len, b_ptr, b_len, 0); } void Field_string::make_sort_key(uchar *to, size_t length) { size_t tmp MY_ATTRIBUTE((unused))= field_charset->coll->strnxfrm(field_charset, to, length, char_length(), ptr, field_length, MY_STRXFRM_PAD_WITH_SPACE | MY_STRXFRM_PAD_TO_MAXLEN); assert(tmp == length); } void Field_string::sql_type(String &res) const { THD *thd= table->in_use; const CHARSET_INFO *cs=res.charset(); size_t length; length= cs->cset->snprintf(cs,(char*) res.ptr(), res.alloced_length(), "%s(%d)", ((type() == MYSQL_TYPE_VAR_STRING && !thd->variables.new_mode) ? (has_charset() ? "varchar" : "varbinary") : (has_charset() ? "char" : "binary")), (int) field_length / charset()->mbmaxlen); res.length(length); if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && has_charset() && (charset()->state & MY_CS_BINSORT)) res.append(STRING_WITH_LEN(" binary")); } uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length, bool low_byte_first MY_ATTRIBUTE((unused))) { uint length= min(field_length,max_length); uint local_char_length= max_length/field_charset->mbmaxlen; DBUG_PRINT("debug", ("Packing field '%s' - length: %u ", field_name, length)); if (length > local_char_length) local_char_length= my_charpos(field_charset, from, from+length, local_char_length); set_if_smaller(length, local_char_length); /* TODO: change charset interface to add a new function that does the following or add a flag to lengthsp to do it itself (this is for not packing padding adding bytes in BINARY fields). */ if (field_charset->mbmaxlen == 1) { while (length && from[length-1] == field_charset->pad_char) length --; } else length= field_charset->cset->lengthsp(field_charset, (const char*) from, length); // Length always stored little-endian *to++= (uchar) length; if (field_length > 255) *to++= (uchar) (length >> 8); // Store the actual bytes of the string memcpy(to, from, length); return to+length; } /** Unpack a string field from row data. This method is used to unpack a string field from a master whose size of the field is less than that of the slave. Note that there can be a variety of field types represented with this class. Certain types like ENUM or SET are processed differently. Hence, the upper byte of the @c param_data argument contains the result of field->real_type() from the master. @note For information about how the length is packed, see @c Field_string::do_save_field_metadata @param to Destination of the data @param from Source of the data @param param_data Real type (upper) and length (lower) values @return New pointer into memory based on from + length of the data */ const uchar * Field_string::unpack(uchar *to, const uchar *from, uint param_data, bool low_byte_first MY_ATTRIBUTE((unused))) { uint from_length, length; /* Compute the declared length of the field on the master. This is used to decide if one or two bytes should be read as length. */ if (param_data) from_length= (((param_data >> 4) & 0x300) ^ 0x300) + (param_data & 0x00ff); else from_length= field_length; DBUG_PRINT("debug", ("param_data: 0x%x, field_length: %u, from_length: %u", param_data, field_length, from_length)); /* Compute the actual length of the data by reading one or two bits (depending on the declared field length on the master). */ if (from_length > 255) { length= uint2korr(from); from+= 2; } else length= (uint) *from++; memcpy(to, from, length); // Pad the string with the pad character of the fields charset field_charset->cset->fill(field_charset, (char*) to + length, field_length - length, field_charset->pad_char); return from+length; } /** Save the field metadata for string fields. Saves the real type in the first byte and the field length in the second byte of the field metadata array at index of *metadata_ptr and *(metadata_ptr + 1). @note In order to be able to handle lengths exceeding 255 and be backwards-compatible with pre-5.1.26 servers, an extra two bits of the length has been added to the metadata in such a way that if they are set, a new unrecognized type is generated. This will cause pre-5.1-26 servers to stop due to a field type mismatch, while new servers will be able to extract the extra bits. If the length is <256, there will be no difference and both a new and an old server will be able to handle it. @note The extra two bits are added to bits 13 and 14 of the parameter data (with 1 being the least siginficant bit and 16 the most significant bit of the word) by xoring the extra length bits with the real type. Since all allowable types have 0xF as most significant bits of the metadata word, lengths <256 will not affect the real type at all, while all other values will result in a non-existant type in the range 17-244. @see Field_string::unpack @param metadata_ptr First byte of field metadata @returns number of bytes written to metadata_ptr */ int Field_string::do_save_field_metadata(uchar *metadata_ptr) { assert(field_length < 1024); assert((real_type() & 0xF0) == 0xF0); DBUG_PRINT("debug", ("field_length: %u, real_type: %u", field_length, real_type())); *metadata_ptr= (real_type() ^ ((field_length & 0x300) >> 4)); *(metadata_ptr + 1)= field_length & 0xFF; return 2; } uint Field_string::packed_col_length(const uchar *data_ptr, uint length) { if (length > 255) return uint2korr(data_ptr)+2; return (uint) *data_ptr + 1; } uint Field_string::max_packed_col_length() { const uint max_length= pack_length(); return (max_length > 255 ? 2 : 1) + max_length; } size_t Field_string::get_key_image(uchar *buff, size_t length, imagetype type_arg) { size_t bytes = my_charpos(field_charset, (char*) ptr, (char*) ptr + field_length, length / field_charset->mbmaxlen); memcpy(buff, ptr, bytes); if (bytes < length) field_charset->cset->fill(field_charset, (char*) buff + bytes, length - bytes, field_charset->pad_char); return bytes; } Field *Field_string::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type) { Field *field; if (type() != MYSQL_TYPE_VAR_STRING || keep_type) field= Field::new_field(root, new_table, keep_type); else if ((field= new Field_varstring(field_length, maybe_null(), field_name, new_table->s, charset()))) { /* Old VARCHAR field which should be modified to a VARCHAR on copy This is done to ensure that ALTER TABLE will convert old VARCHAR fields to now VARCHAR fields. */ field->init(new_table); /* Normally orig_table is different from table only if field was created via ::new_field. Here we alter the type of field, so ::new_field is not applicable. But we still need to preserve the original field metadata for the client-server protocol. */ field->orig_table= orig_table; } return field; } /**************************************************************************** VARCHAR type Data in field->ptr is stored as: 1 or 2 bytes length-prefix-header (from Field_varstring::length_bytes) data NOTE: When VARCHAR is stored in a key (for handler::index_read() etc) it's always stored with a 2 byte prefix. (Just like blob keys). Normally length_bytes is calculated as (field_length < 256 : 1 ? 2) The exception is if there is a prefix key field that is part of a long VARCHAR, in which case field_length for this may be 1 but the length_bytes is 2. ****************************************************************************/ const uint Field_varstring::MAX_SIZE= UINT_MAX16; /** Save the field metadata for varstring fields. Saves the field length in the first byte. Note: may consume 2 bytes. Caller must ensure second byte is contiguous with first byte (e.g. array index 0,1). @param metadata_ptr First byte of field metadata @returns number of bytes written to metadata_ptr */ int Field_varstring::do_save_field_metadata(uchar *metadata_ptr) { assert(field_length <= 65535); int2store((char*)metadata_ptr, field_length); return 2; } type_conversion_status Field_varstring::store(const char *from, size_t length, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; size_t copy_length; const char *well_formed_error_pos; const char *cannot_convert_error_pos; const char *from_end_pos; copy_length= field_well_formed_copy_nchars(field_charset, (char*) ptr + length_bytes, field_length, cs, from, length, field_length / field_charset->mbmaxlen, &well_formed_error_pos, &cannot_convert_error_pos, &from_end_pos); if (length_bytes == 1) *ptr= (uchar) copy_length; else int2store(ptr, static_cast<uint16>(copy_length)); return check_string_copy_error(from, well_formed_error_pos, cannot_convert_error_pos, from_end_pos, from + length, true, cs); } type_conversion_status Field_varstring::store(longlong nr, bool unsigned_val) { char buff[64]; uint length; length= (uint) (field_charset->cset->longlong10_to_str)(field_charset, buff, sizeof(buff), (unsigned_val ? 10: -10), nr); return Field_varstring::store(buff, length, field_charset); } double Field_varstring::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int error; char *end; double result; const CHARSET_INFO* cs= charset(); uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); result= my_strntod(cs, (char*)ptr+length_bytes, length, &end, &error); if (!table->in_use->no_errors && (error || (length != (uint)(end - (char*)ptr+length_bytes) && !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length)))) { push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes, length, cs,"DOUBLE", ER_TRUNCATED_WRONG_VALUE); } return result; } longlong Field_varstring::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; int error; char *end; const CHARSET_INFO *cs= charset(); uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); longlong result= my_strntoll(cs, (char*) ptr+length_bytes, length, 10, &end, &error); if (!table->in_use->no_errors && (error || (length != (uint)(end - (char*)ptr+length_bytes) && !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length)))) { push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes, length, cs, "INTEGER", ER_TRUNCATED_WRONG_VALUE); } return result; } String *Field_varstring::val_str(String *val_buffer MY_ATTRIBUTE((unused)), String *val_ptr) { ASSERT_COLUMN_MARKED_FOR_READ; uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); val_ptr->set((const char*) ptr+length_bytes, length, field_charset); return val_ptr; } my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; const CHARSET_INFO *cs= charset(); uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); int error= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr+length_bytes, length, cs, decimal_value); if (!table->in_use->no_errors && error) { push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes, length, cs, "DECIMAL", ER_TRUNCATED_WRONG_VALUE); } return decimal_value; } int Field_varstring::cmp_max(const uchar *a_ptr, const uchar *b_ptr, uint max_len) { uint a_length, b_length; int diff; if (length_bytes == 1) { a_length= (uint) *a_ptr; b_length= (uint) *b_ptr; } else { a_length= uint2korr(a_ptr); b_length= uint2korr(b_ptr); } set_if_smaller(a_length, max_len); set_if_smaller(b_length, max_len); diff= field_charset->coll->strnncollsp(field_charset, a_ptr+ length_bytes, a_length, b_ptr+ length_bytes, b_length,0); return diff; } /** @note varstring and blob keys are ALWAYS stored with a 2 byte length prefix */ int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length) { uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); uint local_char_length= max_key_length / field_charset->mbmaxlen; local_char_length= my_charpos(field_charset, ptr + length_bytes, ptr + length_bytes + length, local_char_length); set_if_smaller(length, local_char_length); return field_charset->coll->strnncollsp(field_charset, ptr + length_bytes, length, key_ptr+ HA_KEY_BLOB_LENGTH, uint2korr(key_ptr), 0); } /** Compare to key segments (always 2 byte length prefix). @note This is used only to compare key segments created for index_read(). (keys are created and compared in key.cc) */ int Field_varstring::key_cmp(const uchar *a,const uchar *b) { return field_charset->coll->strnncollsp(field_charset, a + HA_KEY_BLOB_LENGTH, uint2korr(a), b + HA_KEY_BLOB_LENGTH, uint2korr(b), 0); } void Field_varstring::make_sort_key(uchar *to, size_t length) { size_t tot_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); if (field_charset == &my_charset_bin) { /* Store length last in high-byte order to sort longer strings first */ if (length_bytes == 1) to[length-1]= tot_length; else mi_int2store(to+length-2, tot_length); length-= length_bytes; } tot_length= field_charset->coll->strnxfrm(field_charset, to, length, char_length(), ptr + length_bytes, tot_length, MY_STRXFRM_PAD_WITH_SPACE | MY_STRXFRM_PAD_TO_MAXLEN); assert(tot_length == length); } enum ha_base_keytype Field_varstring::key_type() const { enum ha_base_keytype res; if (binary()) res= length_bytes == 1 ? HA_KEYTYPE_VARBINARY1 : HA_KEYTYPE_VARBINARY2; else res= length_bytes == 1 ? HA_KEYTYPE_VARTEXT1 : HA_KEYTYPE_VARTEXT2; return res; } void Field_varstring::sql_type(String &res) const { THD *thd= table->in_use; const CHARSET_INFO *cs=res.charset(); size_t length; length= cs->cset->snprintf(cs,(char*) res.ptr(), res.alloced_length(), "%s(%d)", (has_charset() ? "varchar" : "varbinary"), (int) field_length / charset()->mbmaxlen); res.length(length); if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && has_charset() && (charset()->state & MY_CS_BINSORT)) res.append(STRING_WITH_LEN(" binary")); } uint32 Field_varstring::data_length(uint row_offset) { return length_bytes == 1 ? (uint32) *(ptr + row_offset) : uint2korr(ptr + row_offset); } /* Functions to create a packed row. Here the number of length bytes are depending on the given max_length */ uchar *Field_varstring::pack(uchar *to, const uchar *from, uint max_length, bool low_byte_first MY_ATTRIBUTE((unused))) { uint length= length_bytes == 1 ? (uint) *from : uint2korr(from); set_if_smaller(max_length, field_length); if (length > max_length) length=max_length; /* Length always stored little-endian */ *to++= length & 0xFF; if (max_length > 255) *to++= (length >> 8) & 0xFF; /* Store bytes of string */ if (length > 0) memcpy(to, from+length_bytes, length); return to+length; } /** Unpack a varstring field from row data. This method is used to unpack a varstring field from a master whose size of the field is less than that of the slave. @note The string length is always packed little-endian. @param to Destination of the data @param from Source of the data @param param_data Length bytes from the master's field data @return New pointer into memory based on from + length of the data */ const uchar * Field_varstring::unpack(uchar *to, const uchar *from, uint param_data, bool low_byte_first MY_ATTRIBUTE((unused))) { uint length; uint l_bytes= (param_data && (param_data < field_length)) ? (param_data <= 255) ? 1 : 2 : length_bytes; if (l_bytes == 1) { to[0]= *from++; length= to[0]; if (length_bytes == 2) to[1]= 0; } else /* l_bytes == 2 */ { length= uint2korr(from); to[0]= *from++; to[1]= *from++; } if (length) memcpy(to+ length_bytes, from, length); return from+length; } 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; } size_t Field_varstring::get_key_image(uchar *buff, size_t length, imagetype type) { /* If NULL, data bytes may have been left random by the storage engine, so don't try to read them. */ uint f_length= is_null() ? 0 : (length_bytes == 1 ? (uint) *ptr : uint2korr(ptr)); uint local_char_length= length / field_charset->mbmaxlen; uchar *pos= ptr+length_bytes; local_char_length= my_charpos(field_charset, pos, pos + f_length, local_char_length); set_if_smaller(f_length, local_char_length); /* Key is always stored with 2 bytes */ int2store(buff,f_length); memcpy(buff+HA_KEY_BLOB_LENGTH, pos, f_length); if (f_length < length) { /* Must clear this as we do a memcmp in opt_range.cc to detect identical keys */ memset(buff+HA_KEY_BLOB_LENGTH+f_length, 0, (length-f_length)); } return HA_KEY_BLOB_LENGTH+f_length; } void Field_varstring::set_key_image(const uchar *buff, size_t length) { length= uint2korr(buff); // Real length is here (void) Field_varstring::store((const char*) buff+HA_KEY_BLOB_LENGTH, length, field_charset); } int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, uint32 max_length) { uint32 a_length,b_length; if (length_bytes == 1) { a_length= (uint) *a_ptr; b_length= (uint) *b_ptr; } else { a_length= uint2korr(a_ptr); b_length= uint2korr(b_ptr); } set_if_smaller(a_length, max_length); set_if_smaller(b_length, max_length); if (a_length != b_length) return 1; return memcmp(a_ptr+length_bytes, b_ptr+length_bytes, a_length); } Field *Field_varstring::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type) { Field_varstring *res= (Field_varstring*) Field::new_field(root, new_table, keep_type); if (res) res->length_bytes= length_bytes; return res; } Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit) { Field_varstring *res; if ((res= (Field_varstring*) Field::new_key_field(root, new_table, new_ptr, new_null_ptr, new_null_bit))) { /* Keys length prefixes are always packed with 2 bytes */ res->length_bytes= 2; } return res; } uint Field_varstring::is_equal(Create_field *new_field) { if (new_field->sql_type == real_type() && new_field->charset == field_charset) { if (new_field->length == max_display_length()) return IS_EQUAL_YES; assert(0 == (new_field->length % field_charset->mbmaxlen)); assert(0 == (max_display_length() % field_charset->mbmaxlen)); if (new_field->length > max_display_length() && ((new_field->length <= 255 && max_display_length() <= 255) || (new_field->length > 255 && max_display_length() > 255))) return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length } return IS_EQUAL_NO; } void Field_varstring::hash(ulong *nr, ulong *nr2) { if (is_null()) { *nr^= (*nr << 1) | 1; } else { uint len= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); const CHARSET_INFO *cs= charset(); cs->coll->hash_sort(cs, ptr + length_bytes, len, nr, nr2); } } /**************************************************************************** ** blob type ** A blob is saved as a length and a pointer. The length is stored in the ** packlength slot and may be from 1-4. ****************************************************************************/ Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, TABLE_SHARE *share, uint blob_pack_length, const CHARSET_INFO *cs) :Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length), null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, cs), packlength(blob_pack_length), m_keep_old_value(false) { 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, bool low_byte_first) { switch (i_packlength) { case 1: i_ptr[0]= (uchar) i_number; break; case 2: #ifdef WORDS_BIGENDIAN if (low_byte_first) { int2store(i_ptr,(unsigned short) i_number); } else #endif shortstore(i_ptr,(unsigned short) i_number); break; case 3: int3store(i_ptr,i_number); break; case 4: #ifdef WORDS_BIGENDIAN if (low_byte_first) { int4store(i_ptr,i_number); } else #endif longstore(i_ptr,i_number); } } uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg, bool low_byte_first) { switch (packlength_arg) { case 1: return (uint32) pos[0]; case 2: { uint16 tmp; #ifdef WORDS_BIGENDIAN if (low_byte_first) tmp=sint2korr(pos); else #endif ushortget(&tmp, pos); return (uint32) tmp; } case 3: return uint3korr(pos); case 4: { uint32 tmp; #ifdef WORDS_BIGENDIAN if (low_byte_first) tmp=uint4korr(pos); else #endif ulongget(&tmp, pos); return tmp; } } /* When expanding this, see also MAX_FIELD_BLOBLENGTH. */ return 0; // Impossible } /** Put a blob length field into a record buffer. Depending on the maximum length of a blob, its length field is put into 1 to 4 bytes. This is a property of the blob object, described by 'packlength'. @param pos Pointer into the record buffer. @param length The length value to put. */ void Field_blob::put_length(uchar *pos, uint32 length) { switch (packlength) { case 1: *pos= (char) length; break; case 2: int2store(pos, length); break; case 3: int3store(pos, length); break; case 4: int4store(pos, length); break; } } /** Store a blob value to memory storage. @param from the string value to store. @param length length of the string value. @param cs character set of the string value. @param max_length Cut at this length safely (multibyte aware). @param[out] blob_storage Memory storage to put value to. */ type_conversion_status Field_blob::store_to_mem(const char *from, size_t length, const CHARSET_INFO *cs, size_t max_length, Blob_mem_storage *blob_storage) { /* We don't need to support escaping or character set conversions here, because store_to_mem() is currently called only when we process queries having GROUP_CONCAT with ORDER BY or DISTINCT, hence some assersions: */ assert(field_charset == cs); assert(length <= max_data_length()); if (length > max_length) { int well_formed_error; length= cs->cset->well_formed_len(cs, from, from + max_length, length, &well_formed_error); table->blob_storage->set_truncated_value(true); } char *tmp; if (!(tmp= table->blob_storage->store(from, length))) { memset(ptr, 0, Field_blob::pack_length()); return TYPE_ERR_OOM; } store_ptr_and_length(tmp, length); return TYPE_OK; } type_conversion_status Field_blob::store_internal(const char *from, size_t length, const CHARSET_INFO *cs) { size_t new_length; char buff[STRING_BUFFER_USUAL_SIZE], *tmp; String tmpstr(buff,sizeof(buff), &my_charset_bin); /* 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)) { store_ptr_and_length(from, length); return TYPE_OK; } if (tmpstr.copy(from, length, cs)) goto oom_error; from= tmpstr.ptr(); } new_length= min<size_t>(max_data_length(), field_charset->mbmaxlen * length); if (value.alloc(new_length)) goto oom_error; tmp= const_cast<char*>(value.ptr()); { const char *well_formed_error_pos; const char *cannot_convert_error_pos; const char *from_end_pos; /* "length" is OK as "nchars" argument to well_formed_copy_nchars as this is never used to limit the length of the data. The cut of long data is done with the new_length value. */ size_t copy_length= field_well_formed_copy_nchars(field_charset, tmp, new_length, cs, from, length, length, &well_formed_error_pos, &cannot_convert_error_pos, &from_end_pos); store_ptr_and_length(tmp, copy_length); return check_string_copy_error(from, well_formed_error_pos, cannot_convert_error_pos, from_end_pos, from + length, true, cs); } oom_error: /* Fatal OOM error */ memset(ptr, 0, Field_blob::pack_length()); return TYPE_ERR_OOM; } type_conversion_status Field_blob::store(const char *from, size_t length, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; if (table->blob_storage) // GROUP_CONCAT with ORDER BY | DISTINCT return store_to_mem(from, length, cs, table->in_use->variables.group_concat_max_len, table->blob_storage); return store_internal(from, length, cs); } type_conversion_status Field_blob::store(double nr) { const CHARSET_INFO *cs=charset(); value.set_real(nr, NOT_FIXED_DEC, cs); return Field_blob::store(value.ptr(), value.length(), cs); } type_conversion_status Field_blob::store(longlong nr, bool unsigned_val) { const CHARSET_INFO *cs=charset(); value.set_int(nr, unsigned_val, cs); return Field_blob::store(value.ptr(), value.length(), cs); } double Field_blob::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *end_not_used, *blob; uint32 length; const CHARSET_INFO *cs; memcpy(&blob, ptr+packlength, sizeof(char*)); if (!blob) return 0.0; length= get_length(ptr); cs= charset(); return my_strntod(cs, blob, length, &end_not_used, ¬_used); } longlong Field_blob::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *blob; memcpy(&blob, ptr+packlength, sizeof(char*)); if (!blob) return 0; uint32 length=get_length(ptr); return my_strntoll(charset(),blob,length,10,NULL,¬_used); } String *Field_blob::val_str(String *val_buffer MY_ATTRIBUTE((unused)), String *val_ptr) { ASSERT_COLUMN_MARKED_FOR_READ; char *blob; memcpy(&blob, ptr+packlength, sizeof(char*)); if (!blob) val_ptr->set("",0,charset()); // A bit safer than ->length(0) else val_ptr->set((const char*) blob,get_length(ptr),charset()); return val_ptr; } my_decimal *Field_blob::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; const char *blob; size_t length; memcpy(&blob, ptr+packlength, sizeof(const uchar*)); if (!blob) { blob= ""; length= 0; } else length= get_length(ptr); str2my_decimal(E_DEC_FATAL_ERROR, blob, length, charset(), decimal_value); return decimal_value; } int Field_blob::cmp(const uchar *a,uint32 a_length, const uchar *b, uint32 b_length) { return field_charset->coll->strnncollsp(field_charset, a, a_length, b, b_length, 0); } int Field_blob::cmp_max(const uchar *a_ptr, const uchar *b_ptr, uint max_length) { uchar *blob1,*blob2; memcpy(&blob1, a_ptr+packlength, sizeof(char*)); memcpy(&blob2, b_ptr+packlength, sizeof(char*)); uint a_len= get_length(a_ptr), b_len= get_length(b_ptr); set_if_smaller(a_len, max_length); set_if_smaller(b_len, max_length); return Field_blob::cmp(blob1,a_len,blob2,b_len); } int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, uint32 max_length) { char *a,*b; uint diff; uint32 a_length,b_length; memcpy(&a, a_ptr+packlength, sizeof(char*)); memcpy(&b, b_ptr+packlength, sizeof(char*)); a_length=get_length(a_ptr); if (a_length > max_length) a_length=max_length; b_length=get_length(b_ptr); if (b_length > max_length) b_length=max_length; diff=memcmp(a,b,min(a_length,b_length)); return diff ? diff : (int) (a_length - b_length); } /* The following is used only when comparing a key */ size_t Field_blob::get_key_image(uchar *buff, size_t length, imagetype type_arg) { uint32 blob_length= get_length(ptr); uchar *blob; if (type_arg == itMBR) { MBR mbr; Geometry_buffer buffer; Geometry *gobj; const uint image_length= SIZEOF_STORED_DOUBLE*4; if (blob_length < SRID_SIZE) { memset(buff, 0, image_length); return image_length; } get_ptr(&blob); gobj= Geometry::construct(&buffer, (char*) blob, blob_length); if (!gobj || gobj->get_mbr(&mbr)) memset(buff, 0, image_length); else { float8store(buff, mbr.xmin); float8store(buff+8, mbr.xmax); float8store(buff+16, mbr.ymin); float8store(buff+24, mbr.ymax); } return image_length; } get_ptr(&blob); uint local_char_length= length / field_charset->mbmaxlen; local_char_length= my_charpos(field_charset, blob, blob + blob_length, local_char_length); set_if_smaller(blob_length, local_char_length); if ((uint32) length > blob_length) { /* Must clear this as we do a memcmp in opt_range.cc to detect identical keys */ memset(buff+HA_KEY_BLOB_LENGTH+blob_length, 0, (length-blob_length)); length=(uint) blob_length; } int2store(buff, static_cast<uint16>(length)); memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length); return HA_KEY_BLOB_LENGTH+length; } void Field_blob::set_key_image(const uchar *buff, size_t length) { length= uint2korr(buff); (void) Field_blob::store((const char*) buff+HA_KEY_BLOB_LENGTH, length, field_charset); } int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length) { uchar *blob1; uint blob_length=get_length(ptr); memcpy(&blob1, ptr+packlength, sizeof(char*)); const CHARSET_INFO *cs= charset(); uint local_char_length= max_key_length / cs->mbmaxlen; local_char_length= my_charpos(cs, blob1, blob1+blob_length, local_char_length); set_if_smaller(blob_length, local_char_length); return Field_blob::cmp(blob1, blob_length, key_ptr+HA_KEY_BLOB_LENGTH, uint2korr(key_ptr)); } int Field_blob::key_cmp(const uchar *a,const uchar *b) { return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a), b+HA_KEY_BLOB_LENGTH, uint2korr(b)); } /** Save the field metadata for blob fields. Saves the pack length in the first byte of the field metadata array at index of *metadata_ptr. @param metadata_ptr First byte of field metadata @returns number of bytes written to metadata_ptr */ int Field_blob::do_save_field_metadata(uchar *metadata_ptr) { DBUG_ENTER("Field_blob::do_save_field_metadata"); *metadata_ptr= pack_length_no_ptr(); DBUG_PRINT("debug", ("metadata: %u (pack_length_no_ptr)", *metadata_ptr)); DBUG_RETURN(1); } uint32 Field_blob::sort_length() const { return (uint32) (current_thd->variables.max_sort_length + (field_charset == &my_charset_bin ? 0 : packlength)); } void Field_blob::make_sort_key(uchar *to, size_t length) { uchar *blob; size_t blob_length=get_length(); if (!blob_length) memset(to, 0, length); else { if (field_charset == &my_charset_bin) { uchar *pos; /* Store length of blob last in blob to shorter blobs before longer blobs */ length-= packlength; pos= to+length; switch (packlength) { case 1: *pos= (char) blob_length; break; case 2: mi_int2store(pos, blob_length); break; case 3: mi_int3store(pos, blob_length); break; case 4: mi_int4store(pos, blob_length); break; } } memcpy(&blob, ptr+packlength, sizeof(char*)); blob_length= field_charset->coll->strnxfrm(field_charset, to, length, length, blob, blob_length, MY_STRXFRM_PAD_WITH_SPACE | MY_STRXFRM_PAD_TO_MAXLEN); assert(blob_length == length); } } void Field_blob::sql_type(String &res) const { const char *str; uint length; switch (packlength) { default: str="tiny"; length=4; break; case 2: str=""; length=0; break; case 3: str="medium"; length= 6; break; case 4: str="long"; length=4; break; } res.set_ascii(str,length); if (charset() == &my_charset_bin) res.append(STRING_WITH_LEN("blob")); else { res.append(STRING_WITH_LEN("text")); } } uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length, bool low_byte_first) { uchar *save= ptr; ptr= (uchar*) from; uint32 length=get_length(); // Length of from string /* Store max length, which will occupy packlength bytes. If the max length given is smaller than the actual length of the blob, we just store the initial bytes of the blob. */ store_length(to, packlength, min(length, max_length), low_byte_first); /* Store the actual blob data, which will occupy 'length' bytes. */ if (length > 0) { get_ptr((uchar**) &from); memcpy(to+packlength, from,length); } ptr=save; // Restore org row pointer return to+packlength+length; } /** Unpack a blob field from row data. This method is used to unpack a blob field from a master whose size of the field is less than that of the slave. Note: This method is included to satisfy inheritance rules, but is not needed for blob fields. It simply is used as a pass-through to the original unpack() method for blob fields. @param to Destination of the data @param from Source of the data @param param_data @c TRUE if base types should be stored in little- endian format, @c FALSE if native format should be used. @return New pointer into memory based on from + length of the data */ const uchar *Field_blob::unpack(uchar *to, const uchar *from, uint param_data, bool low_byte_first) { DBUG_ENTER("Field_blob::unpack"); DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;" " param_data: %u; low_byte_first: %d", (ulong) to, (ulong) from, param_data, low_byte_first)); uint const master_packlength= param_data > 0 ? param_data & 0xFF : packlength; uint32 const length= get_length(from, master_packlength, low_byte_first); DBUG_DUMP("packed", from, length + master_packlength); bitmap_set_bit(table->write_set, field_index); Field_blob::store(pointer_cast<const char*>(from) + master_packlength, length, field_charset); #ifndef NDEBUG uchar *vptr; get_ptr(&vptr); DBUG_DUMP("field", ptr, pack_length() /* len bytes + ptr bytes */); DBUG_DUMP("value", vptr, length /* the blob value length */); #endif 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() { // We do not use addon fields for blobs. assert(false); const uint max_length= pack_length(); return (max_length > 255 ? 2 : 1) + max_length; } uint Field_blob::is_equal(Create_field *new_field) { return ((new_field->sql_type == get_blob_type_from_length(max_data_length())) && new_field->charset == field_charset && new_field->pack_length == pack_length()); } void Field_geom::sql_type(String &res) const { const CHARSET_INFO *cs= &my_charset_latin1; switch (geom_type) { case GEOM_POINT: res.set(STRING_WITH_LEN("point"), cs); break; case GEOM_LINESTRING: res.set(STRING_WITH_LEN("linestring"), cs); break; case GEOM_POLYGON: res.set(STRING_WITH_LEN("polygon"), cs); break; case GEOM_MULTIPOINT: res.set(STRING_WITH_LEN("multipoint"), cs); break; case GEOM_MULTILINESTRING: res.set(STRING_WITH_LEN("multilinestring"), cs); break; case GEOM_MULTIPOLYGON: res.set(STRING_WITH_LEN("multipolygon"), cs); break; case GEOM_GEOMETRYCOLLECTION: res.set(STRING_WITH_LEN("geometrycollection"), cs); break; default: res.set(STRING_WITH_LEN("geometry"), cs); } } type_conversion_status Field_geom::store(double nr) { my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); return TYPE_ERR_BAD_VALUE; } type_conversion_status Field_geom::store(longlong nr, bool unsigned_val) { my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); return TYPE_ERR_BAD_VALUE; } type_conversion_status Field_geom::store_decimal(const my_decimal *) { my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); return TYPE_ERR_BAD_VALUE; } type_conversion_status Field_geom::store(const char *from, size_t length, const CHARSET_INFO *cs) { if (length < SRID_SIZE + WKB_HEADER_SIZE + sizeof(uint32)) { memset(ptr, 0, Field_blob::pack_length()); my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, MYF(0)); return TYPE_ERR_BAD_VALUE; } return Field_blob::store(from, length, cs); } type_conversion_status Field_geom::store_internal(const char *from, size_t length, const CHARSET_INFO *cs) { // Check that the given WKB // 1. is at least 13 bytes long (length of GEOMETRYCOLLECTION EMPTY) // 2. isn't marked as bad geometry data // 3. isn't shorter than empty geometrycollection // 4. is a valid geometry type // 5. is well formed if (length < 13 || // 1 from == Geometry::bad_geometry_data.ptr() || // 2 length < SRID_SIZE + WKB_HEADER_SIZE + sizeof(uint32) || // 3 !Geometry::is_valid_geotype(uint4korr(from + SRID_SIZE + 1)) || // 4 !Geometry::is_well_formed(from, length, // 5 geometry_type_to_wkb_type(geom_type), Geometry::wkb_ndr)) { memset(ptr, 0, Field_blob::pack_length()); my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); return TYPE_ERR_BAD_VALUE; } if (table->copy_blobs || length <= MAX_FIELD_WIDTH) { // Must make a copy value.copy(from, length, cs); from= value.ptr(); } store_ptr_and_length(from, length); return TYPE_OK; } uint Field_geom::is_equal(Create_field *new_field) { return new_field->sql_type == real_type() && new_field->geom_type == get_geometry_type() && new_field->charset == field_charset && new_field->pack_length == pack_length(); } /** Get the type of this field (json). @param str the string that receives the type */ void Field_json::sql_type(String &str) const { str.set_ascii(STRING_WITH_LEN("json")); } /// Create a shallow clone of this field in the specified MEM_ROOT. Field_json *Field_json::clone(MEM_ROOT *mem_root) const { assert(type() == MYSQL_TYPE_JSON); return new (mem_root) Field_json(*this); } /// Create a shallow clone of this field. Field_json *Field_json::clone() const { assert(type() == MYSQL_TYPE_JSON); return new Field_json(*this); } /** Check if a new field is compatible with this one. @param new_field the new field @return true if new_field is compatible with this field, false otherwise */ uint Field_json::is_equal(Create_field *new_field) { // All JSON fields are compatible with each other. return (new_field->sql_type == real_type()); } /** Store data in this JSON field. JSON data is usually stored using store(Field_json*) or store_json(), so this function will only be called if non-JSON data is attempted stored in a JSON field. This results in an error in most cases. It will attempt to parse the string (unless it's binary) as JSON text, and store a binary representation of JSON document if the string could be parsed. Note that we override store() and not store_internal() because Field_blob::store() contains logic that bypasses store_internal() in some cases we care about. In particular: - When supplied an empty string, we want to raise a JSON syntax error instead of silently inserting an empty byte string. - When called from GROUP_CONCAT with ORDER BY or DISTINCT, we want to do the same data conversion as usual, whereas Field_blob::store() jumps directly to Field_blob::store_to_mem() with the unprocessed input data. @param from the start of the data to be stored @param length the length of the data @param cs the character set of the data @return zero on success, non-zero on failure */ type_conversion_status Field_json::store(const char *from, size_t length, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; /* First clear the field so that it doesn't contain garbage if we return with an error. Some callers continue for a while even after an error has been raised, and they could get into trouble if the field contains garbage. */ reset(); const char *s; size_t ss; String v(from, length, cs); if (ensure_utf8mb4(&v, &value, &s, &ss, true)) { return TYPE_ERR_BAD_VALUE; } const char *parse_err; size_t err_offset; std::auto_ptr<Json_dom> dom(Json_dom::parse(s, ss, &parse_err, &err_offset)); if (dom.get() == NULL) { if (parse_err != NULL) { // Syntax error. invalid_text(parse_err, err_offset); } return TYPE_ERR_BAD_VALUE; } if (json_binary::serialize(dom.get(), &value)) return TYPE_ERR_BAD_VALUE; return store_binary(value.ptr(), value.length()); } /** Helper function for raising an error when trying to store a value into a JSON column, and that value needs to be cast to JSON before it can be stored. */ type_conversion_status Field_json::unsupported_conversion() { ASSERT_COLUMN_MARKED_FOR_WRITE; invalid_text("not a JSON text, may need CAST", 0); return TYPE_ERR_BAD_VALUE; } /** Store the provided JSON binary data in this field. @param[in] ptr pointer to JSON binary data @param[in] length the length of the binary data @return zero on success, non-zero on failure */ type_conversion_status Field_json::store_binary(const char *ptr, size_t length) { /* We expect that a valid binary representation of a JSON document is passed to us. We make an exception for the case of an empty binary string. Even though an empty binary string is not a valid representation of a JSON document, we might be served one as a result of inserting NULL or DEFAULT into a not nullable JSON column using INSERT IGNORE, or inserting DEFAULT into a not nullable JSON column in non-strict SQL mode. We accept an empty binary string in those cases. Such values will be converted to the JSON null literal when they are read with Field_json::val_json(). */ assert(length == 0 || json_binary::parse_binary(ptr, length).is_valid()); if (value.length() > UINT_MAX32) { /* purecov: begin inspected */ my_error(ER_JSON_VALUE_TOO_BIG, MYF(0)); return TYPE_ERR_BAD_VALUE; /* purecov: end */ } return Field_blob::store(ptr, length, field_charset); } /// Store a double in a JSON field. Will raise an error for now. type_conversion_status Field_json::store(double nr) { return unsupported_conversion(); } /// Store an integer in a JSON field. Will raise an error for now. type_conversion_status Field_json::store(longlong nr, bool unsigned_val) { return unsupported_conversion(); } /// Store a decimal in a JSON field. Will raise an error for now. type_conversion_status Field_json::store_decimal(const my_decimal *) { return unsupported_conversion(); } /// Store a TIME value in a JSON field. Will raise an error for now. type_conversion_status Field_json::store_time(MYSQL_TIME *ltime, uint8 dec_arg) { return unsupported_conversion(); } /** Store a JSON value as binary. @param json the JSON value to store @return zero on success, non-zero otherwise */ type_conversion_status Field_json::store_json(Json_wrapper *json) { ASSERT_COLUMN_MARKED_FOR_WRITE; if (json->to_binary(&value)) return TYPE_ERR_BAD_VALUE; return store_binary(value.ptr(), value.length()); } /** Copy the contents of a non-null JSON field into this field. @param[in] field the field to copy data from @return zero on success, non-zero on failure */ type_conversion_status Field_json::store(Field_json *field) { /* The callers of this function have already checked for null, so we don't need to handle it here for now. Assert that field is not null. */ assert(!field->is_null()); String tmp; String *s= field->Field_blob::val_str(&tmp, &tmp); return store_binary(s->ptr(), s->length()); } bool Field_json::val_json(Json_wrapper *wr) { ASSERT_COLUMN_MARKED_FOR_READ; assert(!is_null()); String tmp; String *s= Field_blob::val_str(&tmp, &tmp); /* The empty string is not a valid JSON binary representation, so we should have returned an error. However, sometimes an empty Field_json object is created in order to retrieve meta-data. Return a dummy value instead of raising an error. Bug#21104470. The field could also contain an empty string after forcing NULL or DEFAULT into a not nullable JSON column using lax error checking (such as INSERT IGNORE or non-strict SQL mode). The JSON null literal is used to represent the empty value in this case. Bug#21437989. */ if (s->length() == 0) { Json_wrapper w(new (std::nothrow) Json_null()); wr->steal(&w); return false; } json_binary::Value v(json_binary::parse_binary(s->ptr(), s->length())); if (v.type() == json_binary::Value::ERROR) { /* purecov: begin inspected */ my_error(ER_INVALID_JSON_BINARY_DATA, MYF(0)); return true; /* purecov: end */ } Json_wrapper w(v); wr->steal(&w); return false; } longlong Field_json::val_int() { ASSERT_COLUMN_MARKED_FOR_READ; Json_wrapper wr; if (is_null() || val_json(&wr)) return 0; /* purecov: inspected */ return wr.coerce_int(field_name); } double Field_json::val_real() { ASSERT_COLUMN_MARKED_FOR_READ; Json_wrapper wr; if (is_null() || val_json(&wr)) return 0.0; /* purecov: inspected */ return wr.coerce_real(field_name); } String *Field_json::val_str(String *buf1, String *buf2 MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; /* Per contract of Field::val_str(String*,String*), buf1 should be used if the value needs to be converted to string, and buf2 should be used if the string value is already known. We need to convert, so use buf1. */ buf1->length(0); Json_wrapper wr; if (is_null() || val_json(&wr) || wr.to_string(buf1, true, field_name)) buf1->length(0); return buf1; } my_decimal *Field_json::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; Json_wrapper wr; if (is_null() || val_json(&wr)) { /* purecov: begin inspected */ my_decimal_set_zero(decimal_value); return decimal_value; /* purecov: end */ } return wr.coerce_decimal(decimal_value, field_name); } bool Field_json::get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { bool result= get_time(ltime); if (!result && ltime->time_type == MYSQL_TIMESTAMP_TIME) { MYSQL_TIME tmp= *ltime; time_to_datetime(current_thd, &tmp, ltime); } return result; } bool Field_json::get_time(MYSQL_TIME *ltime) { ASSERT_COLUMN_MARKED_FOR_READ; Json_wrapper wr; bool result= is_null() || val_json(&wr) || wr.coerce_time(ltime, field_name); if (result) set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME); /* purecov: inspected */ return result; } void Field_json::make_sort_key(uchar *to, size_t length) { Json_wrapper wr; if (val_json(&wr)) { /* purecov: begin inspected */ memset(to, 0, length); return; /* purecov: end */ } wr.make_sort_key(to, length); } ulonglong Field_json::make_hash_key(ulonglong *hash_val) { Json_wrapper wr; if (val_json(&wr)) return *hash_val; /* purecov: inspected */ return wr.make_hash_key(hash_val); } /**************************************************************************** ** enum type. ** This is a string which only can have a selection of different values. ** If one uses this string in a number context one gets the type number. ****************************************************************************/ enum ha_base_keytype Field_enum::key_type() const { switch (packlength) { default: return HA_KEYTYPE_BINARY; case 2: return HA_KEYTYPE_USHORT_INT; case 3: return HA_KEYTYPE_UINT24; case 4: return HA_KEYTYPE_ULONG_INT; case 8: return HA_KEYTYPE_ULONGLONG; } } void Field_enum::store_type(ulonglong value) { switch (packlength) { case 1: ptr[0]= (uchar) value; break; case 2: #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int2store(ptr,(unsigned short) value); } else { shortstore(ptr,(unsigned short) value); } #else shortstore(ptr,(unsigned short) value); #endif break; case 3: int3store(ptr,(long) value); break; case 4: #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int4store(ptr,value); } else { longstore(ptr,(long) value); } #else longstore(ptr,(long) value); #endif break; case 8: #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { int8store(ptr,value); } else { longlongstore(ptr,value); } #else longlongstore(ptr,value); #endif break; } } /** @note Storing a empty string in a enum field gives a warning (if there isn't a empty value in the enum) */ type_conversion_status Field_enum::store(const char *from, size_t length,const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; int err= 0; type_conversion_status ret= TYPE_OK; 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= field_charset->cset->lengthsp(field_charset, from, length); uint tmp=find_type2(typelib, from, length, field_charset); if (!tmp) { if (length < 6) // Can't be more than 99999 enums { /* This is for reading numbers with LOAD DATA INFILE */ char *end; tmp=(uint) my_strntoul(cs,from,length,10,&end,&err); if (err || end != from+length || tmp > typelib->count) { tmp=0; set_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); ret= TYPE_WARN_TRUNCATED; } if (!table->in_use->count_cuted_fields) ret= TYPE_OK; } else set_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); } store_type((ulonglong) tmp); return ret; } type_conversion_status Field_enum::store(double nr) { return Field_enum::store((longlong) nr, FALSE); } type_conversion_status Field_enum::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; if ((ulonglong) nr > typelib->count || nr == 0) { set_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); if (nr != 0 || table->in_use->count_cuted_fields) { nr= 0; error= TYPE_WARN_TRUNCATED; } } store_type((ulonglong) (uint) nr); return error; } double Field_enum::val_real(void) { return (double) Field_enum::val_int(); } my_decimal *Field_enum::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 0, decimal_value); return decimal_value; } longlong Field_enum::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; switch (packlength) { case 1: return (longlong) ptr[0]; case 2: { uint16 tmp; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) tmp=sint2korr(ptr); else #endif ushortget(&tmp, ptr); return (longlong) tmp; } case 3: return (longlong) uint3korr(ptr); case 4: { uint32 tmp; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) tmp=uint4korr(ptr); else #endif ulongget(&tmp, ptr); return (longlong) tmp; } case 8: { longlong tmp; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) tmp=sint8korr(ptr); else #endif longlongget(&tmp, ptr); return tmp; } } return 0; // impossible } /** Save the field metadata for enum fields. Saves the real type in the first byte and the pack length in the second byte of the field metadata array at index of *metadata_ptr and *(metadata_ptr + 1). @param metadata_ptr First byte of field metadata @returns number of bytes written to metadata_ptr */ int Field_enum::do_save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= real_type(); *(metadata_ptr + 1)= pack_length(); return 2; } String *Field_enum::val_str(String *val_buffer MY_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(typelib->type_names[tmp-1], typelib->type_lengths[tmp-1], field_charset); return val_ptr; } int Field_enum::cmp(const uchar *a_ptr, const uchar *b_ptr) { uchar *old= ptr; ptr= (uchar*) a_ptr; ulonglong a=Field_enum::val_int(); ptr= (uchar*) b_ptr; ulonglong b=Field_enum::val_int(); ptr= old; return (a < b) ? -1 : (a > b) ? 1 : 0; } void Field_enum::make_sort_key(uchar *to, size_t length) { #ifdef WORDS_BIGENDIAN if (!table->s->db_low_byte_first) copy_integer<true>(to, length, ptr, packlength, true); else #endif copy_integer<false>(to, length, ptr, packlength, true); } void Field_enum::sql_type(String &res) const { char buffer[255]; String enum_item(buffer, sizeof(buffer), res.charset()); res.length(0); res.append(STRING_WITH_LEN("enum(")); bool flag=0; uint *len= typelib->type_lengths; for (const char **pos= typelib->type_names; *pos; pos++, len++) { uint dummy_errors; if (flag) res.append(','); /* convert to res.charset() == utf8, then quote */ enum_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors); append_unescaped(&res, enum_item.ptr(), enum_item.length()); flag= 1; } res.append(')'); } Field *Field_enum::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type) { Field_enum *res= (Field_enum*) Field::new_field(root, new_table, keep_type); if (res) res->typelib= copy_typelib(root, typelib); return res; } /* set type. This is a string which can have a collection of different values. Each string value is separated with a ','. For example "One,two,five" If one uses this string in a number context one gets the bits as a longlong number. */ type_conversion_status Field_set::store(const char *from, size_t length,const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; bool got_warning= 0; int err= 0; type_conversion_status ret= TYPE_OK; char *not_used; uint not_used2; char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); /* Convert character set if necessary */ if (String::needs_conversion_on_storage(length, cs, field_charset)) { uint dummy_errors; tmpstr.copy(from, length, cs, field_charset, &dummy_errors); from= tmpstr.ptr(); length= tmpstr.length(); } ulonglong tmp= find_set(typelib, from, length, field_charset, ¬_used, ¬_used2, &got_warning); if (!tmp && length && length < 22) { /* This is for reading numbers with LOAD DATA INFILE */ char *end; tmp=my_strntoull(cs,from,length,10,&end,&err); if (err || end != from+length || (typelib->count < 64 && tmp >= (1ULL << typelib->count))) { tmp=0; set_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); ret= TYPE_WARN_TRUNCATED; } } else if (got_warning) set_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); store_type(tmp); return ret; } type_conversion_status Field_set::store(longlong nr, bool unsigned_val) { ASSERT_COLUMN_MARKED_FOR_WRITE; type_conversion_status error= TYPE_OK; ulonglong max_nr; if (sizeof(ulonglong)*8 <= typelib->count) max_nr= ULLONG_MAX; else max_nr= (1ULL << typelib->count) - 1; if ((ulonglong) nr > max_nr) { nr&= max_nr; set_warning(Sql_condition::SL_WARNING, WARN_DATA_TRUNCATED, 1); error= TYPE_WARN_TRUNCATED; } store_type((ulonglong) nr); return error; } String *Field_set::val_str(String *val_buffer, String *val_ptr MY_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 < typelib->count) { if (tmp & 1) { if (val_buffer->length()) val_buffer->append(&field_separator, 1, &my_charset_latin1); String str(typelib->type_names[bitnr], typelib->type_lengths[bitnr], field_charset); val_buffer->append(str); } tmp>>=1; bitnr++; } return val_buffer; } void Field_set::sql_type(String &res) const { char buffer[255]; String set_item(buffer, sizeof(buffer), res.charset()); res.length(0); res.append(STRING_WITH_LEN("set(")); bool flag=0; uint *len= typelib->type_lengths; for (const char **pos= typelib->type_names; *pos; pos++, len++) { uint dummy_errors; if (flag) res.append(','); /* convert to res.charset() == utf8, then quote */ set_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors); append_unescaped(&res, set_item.ptr(), set_item.length()); flag= 1; } res.append(')'); } /** @retval 1 if the fields are equally defined @retval 0 if the fields are unequally defined */ bool Field::eq_def(Field *field) { if (real_type() != field->real_type() || charset() != field->charset() || pack_length() != field->pack_length()) return 0; return 1; } /** Compare the first t1::count type names. @return TRUE if the type names of t1 match those of t2. FALSE otherwise. */ static bool compare_type_names(const CHARSET_INFO *charset, TYPELIB *t1, TYPELIB *t2) { for (uint i= 0; i < t1->count; i++) if (my_strnncoll(charset, (const uchar*) t1->type_names[i], t1->type_lengths[i], (const uchar*) t2->type_names[i], t2->type_lengths[i])) return FALSE; return TRUE; } /** @return returns 1 if the fields are equally defined */ bool Field_enum::eq_def(Field *field) { TYPELIB *values; if (!Field::eq_def(field)) return FALSE; values= ((Field_enum*) field)->typelib; /* Definition must be strictly equal. */ if (typelib->count != values->count) return FALSE; return compare_type_names(field_charset, typelib, values); } /** Check whether two fields can be considered 'equal' for table alteration purposes. Fields are equal if they retain the same pack length and if new members are added to the end of the list. @return IS_EQUAL_YES if fields are compatible. IS_EQUAL_NO otherwise. */ uint Field_enum::is_equal(Create_field *new_field) { TYPELIB *values= new_field->interval; /* The fields are compatible if they have the same flags, type, charset and have the same underlying length. */ if (new_field->sql_type != real_type() || new_field->charset != field_charset || new_field->pack_length != pack_length()) return IS_EQUAL_NO; /* Changing the definition of an ENUM or SET column by adding a new enumeration or set members to the end of the list of valid member values only alters table metadata and not table data. */ if (typelib->count > values->count) return IS_EQUAL_NO; /* Check whether there are modification before the end. */ if (! compare_type_names(field_charset, typelib, new_field->interval)) return IS_EQUAL_NO; return IS_EQUAL_YES; } uchar *Field_enum::pack(uchar *to, const uchar *from, uint max_length, bool low_byte_first) { DBUG_ENTER("Field_enum::pack"); DBUG_PRINT("debug", ("packlength: %d", packlength)); DBUG_DUMP("from", from, packlength); switch (packlength) { case 1: if (max_length > 0) *to = *from; DBUG_RETURN(to + 1); case 2: DBUG_RETURN(pack_int16(to, from, low_byte_first)); case 3: DBUG_RETURN(pack_int24(to, from, low_byte_first)); case 4: DBUG_RETURN(pack_int32(to, from, low_byte_first)); case 8: DBUG_RETURN(pack_int64(to, from, low_byte_first)); default: assert(0); } MY_ASSERT_UNREACHABLE(); DBUG_RETURN(NULL); } const uchar *Field_enum::unpack(uchar *to, const uchar *from, uint param_data, bool low_byte_first) { DBUG_ENTER("Field_enum::unpack"); DBUG_PRINT("debug", ("packlength: %d", packlength)); DBUG_DUMP("from", from, packlength); switch (packlength) { case 1: *to = *from; DBUG_RETURN(from + 1); case 2: DBUG_RETURN(unpack_int16(to, from, low_byte_first)); case 3: DBUG_RETURN(unpack_int24(to, from, low_byte_first)); case 4: DBUG_RETURN(unpack_int32(to, from, low_byte_first)); case 8: DBUG_RETURN(unpack_int64(to, from, low_byte_first)); default: assert(0); } MY_ASSERT_UNREACHABLE(); DBUG_RETURN(NULL); } /** @return returns 1 if the fields are equally defined */ bool Field_num::eq_def(Field *field) { if (!Field::eq_def(field)) return 0; Field_num *from_num= (Field_num*) field; if (unsigned_flag != from_num->unsigned_flag || (zerofill && !from_num->zerofill && !zero_pack()) || dec != from_num->dec) return 0; return 1; } /** Check whether two numeric fields can be considered 'equal' for table alteration purposes. Fields are equal if they are of the same type and retain the same pack length. */ uint Field_num::is_equal(Create_field *new_field) { return ((new_field->sql_type == real_type()) && ((new_field->flags & UNSIGNED_FLAG) == (uint) (flags & UNSIGNED_FLAG)) && ((new_field->flags & AUTO_INCREMENT_FLAG) == (uint) (flags & AUTO_INCREMENT_FLAG)) && (new_field->pack_length == pack_length())); } /* Bit field. We store the first 0 - 6 uneven bits among the null bits at the start of the record. The rest bytes are stored in the record itself. For example: CREATE TABLE t1 (a int, b bit(17), c bit(21) not null, d bit(8)); We would store data as follows in the record: Byte Bit 1 7 - reserve for delete 6 - null bit for 'a' 5 - null bit for 'b' 4 - first (high) bit of 'b' 3 - first (high) bit of 'c' 2 - second bit of 'c' 1 - third bit of 'c' 0 - forth bit of 'c' 2 7 - firth bit of 'c' 6 - null bit for 'd' 3 - 6 four bytes for 'a' 7 - 8 two bytes for 'b' 9 - 10 two bytes for 'c' 11 one byte for 'd' */ Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg, enum utype unireg_check_arg, const char *field_name_arg) : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7), bytes_in_rec(len_arg / 8) { 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 m_null_ptr) */ if (!null_ptr_arg) null_bit= bit_ofs_arg; DBUG_VOID_RETURN; } void Field_bit::hash(ulong *nr, ulong *nr2) { if (is_null()) { *nr^= (*nr << 1) | 1; } else { const CHARSET_INFO *cs= &my_charset_bin; longlong value= Field_bit::val_int(); uchar tmp[8]; mi_int8store(tmp,value); cs->coll->hash_sort(cs, tmp, 8, nr, nr2); } } size_t Field_bit::do_last_null_byte() const { /* Code elsewhere is assuming that bytes are 8 bits, so I'm using that value instead of the correct one: CHAR_BIT. REFACTOR SUGGESTION (Matz): Change to use the correct number of bits. On systems with CHAR_BIT > 8 (not very common), the storage will lose the extra bits. */ DBUG_PRINT("test", ("bit_ofs: %d, bit_len: %d bit_ptr: 0x%lx", bit_ofs, bit_len, (long) bit_ptr)); const uchar *result; if (bit_len == 0) result= get_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, uchar *new_null_ptr, uint new_null_bit) { Field_bit *res; if ((res= (Field_bit*) Field::new_key_field(root, new_table, new_ptr, new_null_ptr, new_null_bit))) { /* Move bits normally stored in null_pointer to new_ptr */ res->bit_ptr= new_ptr; res->bit_ofs= 0; if (bit_len) res->ptr++; // Store rest of data here } return res; } uint Field_bit::is_equal(Create_field *new_field) { return (new_field->sql_type == real_type() && new_field->length == max_display_length()); } type_conversion_status Field_bit::store(const char *from, size_t length, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; int delta; for (; length && !*from; from++, length--) ; // skip left 0's delta= bytes_in_rec - static_cast<int>(length); /* *from should probably be treated like uint here see BUG#13727586 */ if (delta < -1 || (delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) || (!bit_len && delta < 0)) { set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len); memset(ptr, 0xff, bytes_in_rec); if (table->in_use->is_strict_mode()) set_warning(Sql_condition::SL_WARNING, ER_DATA_TOO_LONG, 1); else set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return TYPE_WARN_OUT_OF_RANGE; } /* delta is >= -1 here */ if (delta > 0) { if (bit_len) clr_rec_bits(bit_ptr, bit_ofs, bit_len); memset(ptr, 0, 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 TYPE_OK; } type_conversion_status Field_bit::store(double nr) { return Field_bit::store((longlong) nr, FALSE); } type_conversion_status Field_bit::store(longlong nr, bool unsigned_val) { char buf[8]; mi_int8store(buf, nr); return store(buf, 8, NULL); } type_conversion_status Field_bit::store_decimal(const my_decimal *val) { bool has_overflow= false; longlong i= convert_decimal2longlong(val, 1, &has_overflow); type_conversion_status res= store(i, TRUE); return has_overflow ? TYPE_WARN_OUT_OF_RANGE : res; } double Field_bit::val_real(void) { return (double) Field_bit::val_int(); } longlong Field_bit::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; ulonglong bits= 0; if (bit_len) { bits= get_rec_bits(bit_ptr, bit_ofs, bit_len); bits<<= (bytes_in_rec * 8); } switch (bytes_in_rec) { case 0: return bits; case 1: return bits | (ulonglong) ptr[0]; case 2: return bits | mi_uint2korr(ptr); case 3: return bits | mi_uint3korr(ptr); case 4: return bits | mi_uint4korr(ptr); case 5: return bits | mi_uint5korr(ptr); case 6: return bits | mi_uint6korr(ptr); case 7: return bits | mi_uint7korr(ptr); default: return mi_uint8korr(ptr + bytes_in_rec - sizeof(longlong)); } } String *Field_bit::val_str(String *val_buffer, String *val_ptr MY_ATTRIBUTE((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; char buff[sizeof(longlong)]; uint length= min<uint>(pack_length(), sizeof(longlong)); ulonglong bits= val_int(); mi_int8store(buff,bits); val_buffer->alloc(length); memcpy((char *) val_buffer->ptr(), buff+8-length, length); val_buffer->length(length); val_buffer->set_charset(&my_charset_bin); return val_buffer; } my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value) { ASSERT_COLUMN_MARKED_FOR_READ; int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 1, deciaml_value); return deciaml_value; } /* Compare two bit fields using pointers within the record. SYNOPSIS cmp_max() a Pointer to field->ptr in first record b Pointer to field->ptr in second record max_len Maximum length used in index DESCRIPTION This method is used from key_rec_cmp used by merge sorts used by partitioned index read and later other similar places. The a and b pointer must be pointers to the field in a record (not the table->record[0] necessarily) */ int Field_bit::cmp_max(const uchar *a, const uchar *b, uint max_len) { my_ptrdiff_t a_diff= a - ptr; my_ptrdiff_t b_diff= b - ptr; if (bit_len) { int flag; uchar bits_a= get_rec_bits(bit_ptr+a_diff, bit_ofs, bit_len); uchar bits_b= get_rec_bits(bit_ptr+b_diff, bit_ofs, bit_len); if ((flag= (int) (bits_a - bits_b))) return flag; } return memcmp(a, b, pack_length()); } int Field_bit::key_cmp(const uchar *str, uint length) { if (bit_len) { int flag; uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len); if ((flag= (int) (bits - *str))) return flag; str++; length--; } return memcmp(ptr, str, length); } int Field_bit::cmp_offset(uint row_offset) { if (bit_len) { int flag; uchar bits_a= get_rec_bits(bit_ptr, bit_ofs, bit_len); uchar bits_b= get_rec_bits(bit_ptr + row_offset, bit_ofs, bit_len); if ((flag= (int) (bits_a - bits_b))) return flag; } return memcmp(ptr, ptr + row_offset, bytes_in_rec); } size_t Field_bit::get_key_image(uchar *buff, size_t length, imagetype type_arg) { if (bit_len) { uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len); *buff++= bits; length--; } size_t data_length = min(length, static_cast<size_t>(bytes_in_rec)); memcpy(buff, ptr, data_length); return data_length + 1; } /** Save the field metadata for bit fields. Saves the bit length in the first byte and bytes in record in the second byte of the field metadata array at index of *metadata_ptr and *(metadata_ptr + 1). @param metadata_ptr First byte of field metadata @returns number of bytes written to metadata_ptr */ int Field_bit::do_save_field_metadata(uchar *metadata_ptr) { DBUG_ENTER("Field_bit::do_save_field_metadata"); DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d", bit_len, bytes_in_rec)); /* Since this class and Field_bit_as_char have different ideas of what should be stored here, we compute the values of the metadata explicitly using the field_length. */ metadata_ptr[0]= field_length % 8; metadata_ptr[1]= field_length / 8; DBUG_RETURN(2); } /** Returns the number of bytes field uses in row-based replication row packed size. This method is used in row-based replication to determine the number of bytes that the field consumes in the row record format. This is used to skip fields in the master that do not exist on the slave. @param field_metadata Encoded size in field metadata @returns The size of the field based on the field metadata. */ uint Field_bit::pack_length_from_metadata(uint field_metadata) { uint const from_len= (field_metadata >> 8U) & 0x00ff; uint const from_bit_len= field_metadata & 0x00ff; uint const source_size= from_len + ((from_bit_len > 0) ? 1 : 0); return (source_size); } /** Check to see if field size is compatible with destination. This method is used in row-based replication to verify that the slave's field size is less than or equal to the master's field size. The encoded field metadata (from the master or source) is decoded and compared to the size of this field (the slave or destination). @param field_metadata Encoded size in field metadata @param order_var Pointer to variable where the order between the source field and this field will be returned. @return @c true */ bool Field_bit::compatible_field_size(uint field_metadata, Relay_log_info * MY_ATTRIBUTE((unused)), uint16 mflags, int *order_var) { DBUG_ENTER("Field_bit::compatible_field_size"); 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 { 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, bool low_byte_first MY_ATTRIBUTE((unused))) { assert(max_length > 0); uint length; if (bit_len > 0) { /* We have the following: ptr Points into a field in record R1 from Points to a field in a record R2 bit_ptr Points to the byte (in the null bytes) that holds the odd bits of R1 from_bitp Points to the byte that holds the odd bits of R2 We have the following: ptr - bit_ptr = from - from_bitp We want to isolate 'from_bitp', so this gives: ptr - bit_ptr - from = - from_bitp - ptr + bit_ptr + from = from_bitp bit_ptr + from - ptr = from_bitp */ uchar bits= get_rec_bits(bit_ptr + (from - ptr), bit_ofs, bit_len); *to++= bits; } length= min(bytes_in_rec, max_length - (bit_len > 0)); memcpy(to, from, length); return to + length; } /** Unpack a bit field from row data. This method is used to unpack a bit field from a master whose size of the field is less than that of the slave. @param to Destination of the data @param from Source of the data @param param_data Bit length (upper) and length (lower) values @return New pointer into memory based on from + length of the data */ const uchar * Field_bit::unpack(uchar *to, const uchar *from, uint param_data, bool low_byte_first MY_ATTRIBUTE((unused))) { 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 (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 new_len= (field_length + 7) / 8; char *value= (char *)my_alloca(new_len); memset(value, 0, new_len); uint len= from_len + ((from_bit_len > 0) ? 1 : 0); memcpy(value + (new_len - len), from, len); /* Mask out the unused bits in the partial byte. TODO: Add code to the master to always mask these bits and remove the following. */ if ((from_bit_len > 0) && (from_len > 0)) value[new_len - len]= value[new_len - len] & ((1U << from_bit_len) - 1); bitmap_set_bit(table->write_set,field_index); store(value, new_len, system_charset_info); DBUG_RETURN(from + len); } void Field_bit::set_default() { if (bit_len > 0) { my_ptrdiff_t offset= table->default_values_offset(); uchar bits= get_rec_bits(bit_ptr + offset, bit_ofs, bit_len); set_rec_bits(bits, bit_ptr, bit_ofs, bit_len); } Field::set_default(); } /* Bit field support for non-MyISAM tables. */ Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg) :Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0, unireg_check_arg, field_name_arg) { flags|= UNSIGNED_FLAG; bit_len= 0; bytes_in_rec= (len_arg + 7) / 8; } type_conversion_status Field_bit_as_char::store(const char *from, size_t length, const CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; int delta; uchar bits= (uchar) (field_length & 7); for (; length && !*from; from++, length--) ; // skip left 0's delta= bytes_in_rec - static_cast<int>(length); if (delta < 0 || (delta == 0 && bits && (uint) (uchar) *from >= (uint) (1 << bits))) { memset(ptr, 0xff, bytes_in_rec); if (bits) *ptr&= ((1 << bits) - 1); /* set first uchar */ if (table->in_use->is_strict_mode()) set_warning(Sql_condition::SL_WARNING, ER_DATA_TOO_LONG, 1); else set_warning(Sql_condition::SL_WARNING, ER_WARN_DATA_OUT_OF_RANGE, 1); return TYPE_WARN_OUT_OF_RANGE; } memset(ptr, 0, delta); memcpy(ptr + delta, from, length); return TYPE_OK; } void Field_bit_as_char::sql_type(String &res) const { 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 *****************************************************************************/ /** Convert create_field::length from number of characters to number of bytes. */ void Create_field::create_length_to_internal_length(void) { switch (sql_type) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_JSON: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VARCHAR: length*= charset->mbmaxlen; key_length= length; pack_length= calc_pack_length(sql_type, length); break; case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: /* Pack_length already calculated in sql_parse.cc */ length*= charset->mbmaxlen; key_length= pack_length; break; case MYSQL_TYPE_BIT: if (f_bit_as_char(pack_flag)) { key_length= pack_length= ((length + 7) & ~7) / 8; } else { pack_length= length / 8; /* We need one extra byte to store the bits we save among the null bits */ key_length= pack_length + MY_TEST(length & 7); } break; case MYSQL_TYPE_NEWDECIMAL: key_length= pack_length= my_decimal_get_binary_size(my_decimal_length_to_precision(length, decimals, flags & UNSIGNED_FLAG), decimals); break; default: key_length= pack_length= calc_pack_length(sql_type, length); break; } } /** Init for a tmp table field. To be extended if need be. */ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg, uint32 length_arg, uint32 decimals_arg, bool maybe_null, bool is_unsigned, uint pack_length_arg) { DBUG_ENTER("Create_field::init_for_tmp_table"); field_name= ""; sql_type= sql_type_arg; char_length= length= length_arg;; unireg_check= Field::NONE; interval= 0; charset= &my_charset_bin; geom_type= Field::GEOM_GEOMETRY; DBUG_PRINT("enter", ("sql_type: %d, length: %u, pack_length: %u", sql_type_arg, length_arg, pack_length_arg)); /* These pack flags are crafted to get it correctly through the branches of make_field(). */ switch (sql_type_arg) { case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: case MYSQL_TYPE_SET: pack_flag= 0; break; case MYSQL_TYPE_GEOMETRY: pack_flag= FIELDFLAG_GEOM; break; case MYSQL_TYPE_JSON: pack_flag= FIELDFLAG_JSON; break; case MYSQL_TYPE_ENUM: pack_flag= FIELDFLAG_INTERVAL; break; case MYSQL_TYPE_NEWDECIMAL: assert(decimals_arg <= DECIMAL_MAX_SCALE); case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: pack_flag= FIELDFLAG_NUMBER | (decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT; break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: pack_flag= FIELDFLAG_BLOB; break; case MYSQL_TYPE_BIT: pack_flag= FIELDFLAG_NUMBER | FIELDFLAG_TREAT_BIT_AS_CHAR; break; default: pack_flag= FIELDFLAG_NUMBER; break; } /* Set the pack flag correctly for the blob-like types. This sets the packtype to something that make_field can use. If the pack type is not set correctly, the packlength will be reeeeally wierd (like 129 or so). */ switch (sql_type_arg) { case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_JSON: /* If you are going to use the above types, you have to pass a pack_length as parameter. Assert that is really done. */ assert(pack_length_arg != ~0U); pack_flag|= pack_length_to_packflag(pack_length_arg); break; default: /* Nothing */ break; } pack_flag|= (maybe_null ? FIELDFLAG_MAYBE_NULL : 0) | (is_unsigned ? 0 : FIELDFLAG_DECIMAL); gcol_info= 0; stored_in_db= TRUE; DBUG_PRINT("debug", ("pack_flag: %s%s%s%s%s%s%s, pack_type: %d", FLAGSTR(pack_flag, FIELDFLAG_BINARY), FLAGSTR(pack_flag, FIELDFLAG_NUMBER), FLAGSTR(pack_flag, FIELDFLAG_INTERVAL), FLAGSTR(pack_flag, FIELDFLAG_GEOM), FLAGSTR(pack_flag, FIELDFLAG_BLOB), FLAGSTR(pack_flag, FIELDFLAG_DECIMAL), FLAGSTR(pack_flag, FIELDFLAG_JSON), f_packtype(pack_flag))); DBUG_VOID_RETURN; } /** Initialize a column definition object. Column definition objects can be used to construct Field objects. @param thd Session/Thread handle. @param fld_name Column name. @param fld_type Column type. @param fld_length Column length. @param fld_decimals Number of digits to the right of the decimal point (if any.) @param fld_type_modifier Additional type information. @param fld_default_value Column default expression (if any.) @param fld_on_update_value The expression in the ON UPDATE clause. @param fld_comment Column comment. @param fld_change Column change. @param fld_interval_list Interval list (if any.) @param fld_charset Column charset. @param fld_geom_type Column geometry type (if any.) @param fld_gcol_info Generated column data @retval FALSE on success. @retval TRUE on error. */ bool Create_field::init(THD *thd, const char *fld_name, enum_field_types fld_type, const char *fld_length, const char *fld_decimals, uint fld_type_modifier, Item *fld_default_value, Item *fld_on_update_value, LEX_STRING *fld_comment, const char *fld_change, List<String> *fld_interval_list, const CHARSET_INFO *fld_charset, uint fld_geom_type, Generated_column *fld_gcol_info) { uint sign_len, allowed_type_modifier= 0; ulong max_field_charlength= MAX_FIELD_CHARLENGTH; DBUG_ENTER("Create_field::init()"); field= 0; field_name= fld_name; flags= fld_type_modifier; charset= fld_charset; const bool on_update_is_function= (fld_on_update_value != NULL && fld_on_update_value->type() == Item::FUNC_ITEM); if (fld_default_value != NULL && fld_default_value->type() == Item::FUNC_ITEM) { // We have a function default for insertions. def= NULL; unireg_check= on_update_is_function ? Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates. Field::TIMESTAMP_DN_FIELD; // only for insertions. } else { // No function default for insertions. Either NULL or a constant. def= fld_default_value; if (on_update_is_function) // We have a function default for updates only. unireg_check= Field::TIMESTAMP_UN_FIELD; else // No function defaults. unireg_check= (fld_type_modifier & AUTO_INCREMENT_FLAG) != 0 ? Field::NEXT_NUMBER : // Automatic increment. Field::NONE; } decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0; if (is_temporal_real_type(fld_type)) { flags|= BINARY_FLAG; charset= &my_charset_numeric; if (decimals > DATETIME_MAX_DECIMALS) { my_error(ER_TOO_BIG_PRECISION, MYF(0), decimals, fld_name, DATETIME_MAX_DECIMALS); DBUG_RETURN(TRUE); } } else if (decimals >= NOT_FIXED_DEC) { my_error(ER_TOO_BIG_SCALE, MYF(0), decimals, fld_name, static_cast<ulong>(NOT_FIXED_DEC - 1)); DBUG_RETURN(TRUE); } sql_type= fld_type; length= 0; change= fld_change; interval= 0; pack_length= key_length= 0; geom_type= (Field::geometry_type) fld_geom_type; interval_list.empty(); comment= *fld_comment; gcol_info= fld_gcol_info; stored_in_db= TRUE; /* Initialize data for a virtual field */ if (gcol_info) { assert(gcol_info->expr_item); stored_in_db= gcol_info->get_field_stored(); /* Perform per item-type checks to determine if the expression is allowed for a generated column. Note that validation of the specific function is done later in procedures open_table_from_share and fix_fields_gcol_func */ switch (gcol_info->expr_item->type()) { case Item::FUNC_ITEM: if (((Item_func *)gcol_info->expr_item)->functype() == Item_func::FUNC_SP) { my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name); DBUG_RETURN(TRUE); } break; case Item::COPY_STR_ITEM: case Item::FIELD_AVG_ITEM: case Item::PROC_ITEM: case Item::REF_ITEM: case Item::FIELD_STD_ITEM: case Item::FIELD_VARIANCE_ITEM: case Item::INSERT_VALUE_ITEM: case Item::SUBSELECT_ITEM: case Item::CACHE_ITEM: case Item::TYPE_HOLDER: case Item::PARAM_ITEM: case Item::TRIGGER_FIELD_ITEM: case Item::XPATH_NODESET: case Item::XPATH_NODESET_CMP: case Item::VIEW_FIXER_ITEM: my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name); DBUG_RETURN(TRUE); break; default: // Continue with the field creation break; } /* Make a field created for the real type. Note that "real" and generated fields differ from each other only by Field::gcol_info, which is always 0 for normal columns. gcol_info is updated for fields later in procedure open_binary_frm. */ sql_type= fld_type= gcol_info->get_real_type(); } /* Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and it is NOT NULL and not an AUTO_INCREMENT field. */ if (!fld_default_value && (fld_type_modifier & NOT_NULL_FLAG) && !(fld_type_modifier & AUTO_INCREMENT_FLAG)) { /* TIMESTAMP columns get implicit DEFAULT value when explicit_defaults_for_timestamp is not set. */ if (thd->variables.explicit_defaults_for_timestamp || !is_timestamp_type(fld_type)) { flags|= NO_DEFAULT_VALUE_FLAG; } } if (fld_length != NULL) { errno= 0; length= strtoul(fld_length, NULL, 10); if ((errno != 0) || (length > MAX_FIELD_BLOBLENGTH)) { my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name, MAX_FIELD_BLOBLENGTH); DBUG_RETURN(TRUE); } if (length == 0) fld_length= NULL; /* purecov: inspected */ } sign_len= fld_type_modifier & UNSIGNED_FLAG ? 0 : 1; switch (fld_type) { case MYSQL_TYPE_TINY: if (!fld_length) length= MAX_TINYINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case MYSQL_TYPE_SHORT: if (!fld_length) length= MAX_SMALLINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case MYSQL_TYPE_INT24: if (!fld_length) length= MAX_MEDIUMINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case MYSQL_TYPE_LONG: if (!fld_length) length= MAX_INT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case MYSQL_TYPE_LONGLONG: if (!fld_length) length= MAX_BIGINT_WIDTH; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case MYSQL_TYPE_NULL: break; case MYSQL_TYPE_NEWDECIMAL: { ulong precision= static_cast<ulong>(length); my_decimal_trim(&precision, &decimals); length= precision; } if (length > DECIMAL_MAX_PRECISION) { my_error(ER_TOO_BIG_PRECISION, MYF(0), static_cast<int>(length), fld_name, static_cast<ulong>(DECIMAL_MAX_PRECISION)); DBUG_RETURN(TRUE); } if (length < decimals) { my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name); DBUG_RETURN(TRUE); } length= my_decimal_precision_to_length(length, decimals, fld_type_modifier & UNSIGNED_FLAG); pack_length= my_decimal_get_binary_size(length, decimals); break; case MYSQL_TYPE_VARCHAR: /* Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table if they don't have a default value */ max_field_charlength= MAX_FIELD_VARCHARLENGTH; break; case MYSQL_TYPE_STRING: break; case MYSQL_TYPE_BLOB: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_JSON: if (fld_default_value) { /* Allow empty as default value. */ String str,*res; res= fld_default_value->val_str(&str); /* A default other than '' is always an error, and any non-NULL specified default is an error in strict mode. */ if (res->length() || thd->is_strict_mode()) { my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), fld_name); /* purecov: inspected */ DBUG_RETURN(TRUE); } else { /* Otherwise a default of '' is just a warning. */ push_warning_printf(thd, Sql_condition::SL_WARNING, ER_BLOB_CANT_HAVE_DEFAULT, ER(ER_BLOB_CANT_HAVE_DEFAULT), fld_name); } def= 0; } flags|= BLOB_FLAG; break; case MYSQL_TYPE_YEAR: if (!fld_length || length != 4) length= 4; /* Default length */ flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; break; case MYSQL_TYPE_FLOAT: /* change FLOAT(precision) to FLOAT or DOUBLE */ allowed_type_modifier= AUTO_INCREMENT_FLAG; if (fld_length && !fld_decimals) { size_t tmp_length= length; if (tmp_length > PRECISION_FOR_DOUBLE) { my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name); DBUG_RETURN(TRUE); } else if (tmp_length > PRECISION_FOR_FLOAT) { sql_type= MYSQL_TYPE_DOUBLE; length= MAX_DOUBLE_STR_LENGTH; } else length= MAX_FLOAT_STR_LENGTH; decimals= NOT_FIXED_DEC; break; } if (!fld_length && !fld_decimals) { length= MAX_FLOAT_STR_LENGTH; decimals= NOT_FIXED_DEC; } if (length < decimals && decimals != NOT_FIXED_DEC) { my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name); DBUG_RETURN(TRUE); } break; case MYSQL_TYPE_DOUBLE: allowed_type_modifier= AUTO_INCREMENT_FLAG; if (!fld_length && !fld_decimals) { length= DBL_DIG+7; decimals= NOT_FIXED_DEC; } if (length < decimals && decimals != NOT_FIXED_DEC) { my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name); DBUG_RETURN(TRUE); } break; case MYSQL_TYPE_TIMESTAMP: /* Add flags for TIMESTAMP for 4.0 MYD and 4.0 InnoDB compatibility */ flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; /* Fall through */ case MYSQL_TYPE_TIMESTAMP2: if (fld_length == NULL) { length= MAX_DATETIME_WIDTH + (decimals ? (1 + decimals) : 0); } else if (length != MAX_DATETIME_WIDTH) { /* We support only even TIMESTAMP lengths less or equal than 14 and 19 as length of 4.1 compatible representation. Silently shrink it to MAX_DATETIME_COMPRESSED_WIDTH. */ assert(MAX_DATETIME_COMPRESSED_WIDTH < UINT_MAX); if (length != UINT_MAX) /* avoid overflow; is safe because of min() */ length= ((length+1)/2)*2; length= min<size_t>(length, MAX_DATETIME_COMPRESSED_WIDTH); } /* Since we silently rewrite down to MAX_DATETIME_COMPRESSED_WIDTH bytes, the parser should not raise errors unless bizzarely large. */ max_field_charlength= UINT_MAX; break; case MYSQL_TYPE_DATE: /* Old date type. */ sql_type= MYSQL_TYPE_NEWDATE; /* fall through */ case MYSQL_TYPE_NEWDATE: length= MAX_DATE_WIDTH; break; case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIME2: length= MAX_TIME_WIDTH + (decimals ? (1 + decimals) : 0); break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_DATETIME2: length= MAX_DATETIME_WIDTH + (decimals ? (1 + decimals) : 0); break; case MYSQL_TYPE_SET: { pack_length= get_set_pack_length(fld_interval_list->elements); List_iterator<String> it(*fld_interval_list); String *tmp; while ((tmp= it++)) interval_list.push_back(tmp); /* Set fake length to 1 to pass the below conditions. Real length will be set in mysql_prepare_table() when we know the character set of the column */ length= 1; break; } case MYSQL_TYPE_ENUM: { /* Should be safe. */ pack_length= get_enum_pack_length(fld_interval_list->elements); List_iterator<String> it(*fld_interval_list); String *tmp; while ((tmp= it++)) interval_list.push_back(tmp); length= 1; /* See comment for MYSQL_TYPE_SET above. */ break; } case MYSQL_TYPE_VAR_STRING: assert(0); /* Impossible. */ break; case MYSQL_TYPE_BIT: { if (!fld_length) { my_error(ER_INVALID_FIELD_SIZE, MYF(0), fld_name); DBUG_RETURN(TRUE); } if (length > MAX_BIT_FIELD_LENGTH) { my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name, static_cast<ulong>(MAX_BIT_FIELD_LENGTH)); DBUG_RETURN(TRUE); } pack_length= (length + 7) / 8; break; } case MYSQL_TYPE_DECIMAL: assert(0); /* Was obsolete */ } /* Remember the value of length */ char_length= length; if (!(flags & BLOB_FLAG) && ((length > max_field_charlength && fld_type != MYSQL_TYPE_SET && fld_type != MYSQL_TYPE_ENUM && (fld_type != MYSQL_TYPE_VARCHAR || fld_default_value)) || ((length == 0) && fld_type != MYSQL_TYPE_STRING && fld_type != MYSQL_TYPE_VARCHAR && fld_type != MYSQL_TYPE_GEOMETRY))) { my_error((fld_type == MYSQL_TYPE_VAR_STRING || fld_type == MYSQL_TYPE_VARCHAR || fld_type == MYSQL_TYPE_STRING) ? ER_TOO_BIG_FIELDLENGTH : ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name, max_field_charlength); /* purecov: inspected */ DBUG_RETURN(TRUE); } fld_type_modifier&= AUTO_INCREMENT_FLAG; if ((~allowed_type_modifier) & fld_type_modifier) { my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name); DBUG_RETURN(TRUE); } DBUG_RETURN(FALSE); /* success */ } enum_field_types get_blob_type_from_length(ulong length) { enum_field_types type; if (length < 256) type= MYSQL_TYPE_TINY_BLOB; else if (length < 65536) type= MYSQL_TYPE_BLOB; else if (length < 256L*256L*256L) type= MYSQL_TYPE_MEDIUM_BLOB; else type= MYSQL_TYPE_LONG_BLOB; return type; } /* Make a field from the .frm file info */ size_t calc_pack_length(enum_field_types type, size_t length) { switch (type) { case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: case MYSQL_TYPE_DECIMAL: return (length); case MYSQL_TYPE_VARCHAR: return (length + (length < 256 ? 1: 2)); case MYSQL_TYPE_YEAR: case MYSQL_TYPE_TINY : return 1; case MYSQL_TYPE_SHORT : return 2; case MYSQL_TYPE_INT24: case MYSQL_TYPE_NEWDATE: return 3; case MYSQL_TYPE_TIME: return 3; case MYSQL_TYPE_TIME2: return length > MAX_TIME_WIDTH ? my_time_binary_length(length - MAX_TIME_WIDTH - 1) : 3; case MYSQL_TYPE_TIMESTAMP: return 4; case MYSQL_TYPE_TIMESTAMP2: return length > MAX_DATETIME_WIDTH ? my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4; case MYSQL_TYPE_DATE: case MYSQL_TYPE_LONG : return 4; case MYSQL_TYPE_FLOAT : return sizeof(float); case MYSQL_TYPE_DOUBLE: return sizeof(double); case MYSQL_TYPE_DATETIME: return 8; case MYSQL_TYPE_DATETIME2: return length > MAX_DATETIME_WIDTH ? my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5; case MYSQL_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */ case MYSQL_TYPE_NULL : return 0; case MYSQL_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr; case MYSQL_TYPE_BLOB: return 2+portable_sizeof_char_ptr; case MYSQL_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr; case MYSQL_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr; case MYSQL_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr; case MYSQL_TYPE_JSON: return 4+portable_sizeof_char_ptr; case MYSQL_TYPE_SET: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_NEWDECIMAL: abort(); return 0; // This shouldn't happen case MYSQL_TYPE_BIT: return length / 8; default: return 0; } } uint pack_length_to_packflag(uint type) { switch (type) { case 1: return f_settype((uint) MYSQL_TYPE_TINY); case 2: return f_settype((uint) MYSQL_TYPE_SHORT); case 3: return f_settype((uint) MYSQL_TYPE_INT24); case 4: return f_settype((uint) MYSQL_TYPE_LONG); case 8: return f_settype((uint) MYSQL_TYPE_LONGLONG); } return 0; // This shouldn't happen } Field *make_field(TABLE_SHARE *share, uchar *ptr, size_t field_length, uchar *null_pos, uchar null_bit, uint pack_flag, enum_field_types field_type, const CHARSET_INFO *field_charset, Field::geometry_type geom_type, Field::utype unireg_check, TYPELIB *interval, const char *field_name) { uchar *bit_ptr= NULL; uchar bit_offset= 0; if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { bit_ptr= null_pos; bit_offset= null_bit; if (f_maybe_null(pack_flag)) // if null field { bit_ptr+= (null_bit == 7); // shift bit_ptr and bit_offset bit_offset= (bit_offset + 1) & 7; } } if (!f_maybe_null(pack_flag)) { null_pos=0; null_bit=0; } else { null_bit= ((uchar) 1) << null_bit; } if (is_temporal_real_type(field_type)) field_charset= &my_charset_numeric; DBUG_PRINT("debug", ("field_type: %d, field_length: %zu, " "interval: %p, pack_flag: %s%s%s%s%s", field_type, field_length, interval, FLAGSTR(pack_flag, FIELDFLAG_BINARY), FLAGSTR(pack_flag, FIELDFLAG_INTERVAL), FLAGSTR(pack_flag, FIELDFLAG_NUMBER), FLAGSTR(pack_flag, FIELDFLAG_PACK), FLAGSTR(pack_flag, FIELDFLAG_BLOB))); if (f_is_alpha(pack_flag)) { if (!f_is_packed(pack_flag)) { if (field_type == MYSQL_TYPE_STRING || field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string field_type == MYSQL_TYPE_VAR_STRING) return new Field_string(ptr,field_length,null_pos,null_bit, unireg_check, field_name, field_charset); if (field_type == MYSQL_TYPE_VARCHAR) return new Field_varstring(ptr,field_length, HA_VARCHAR_PACKLENGTH(field_length), null_pos,null_bit, unireg_check, field_name, share, field_charset); return 0; // Error } uint pack_length=calc_pack_length((enum_field_types) f_packtype(pack_flag), field_length); if (f_is_geom(pack_flag)) return new Field_geom(ptr,null_pos,null_bit, unireg_check, field_name, share, pack_length, geom_type); if (f_is_json(pack_flag)) return new Field_json(ptr, null_pos, null_bit, unireg_check, field_name, share, pack_length); if (f_is_blob(pack_flag)) return new Field_blob(ptr,null_pos,null_bit, unireg_check, field_name, share, pack_length, field_charset); if (interval) { if (f_is_enum(pack_flag)) return new Field_enum(ptr,field_length,null_pos,null_bit, unireg_check, field_name, pack_length, interval, field_charset); else return new Field_set(ptr,field_length,null_pos,null_bit, unireg_check, field_name, pack_length, interval, field_charset); } } switch (field_type) { case MYSQL_TYPE_DECIMAL: return new Field_decimal(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_decimals(pack_flag), f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_NEWDECIMAL: return new Field_new_decimal(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_decimals(pack_flag), f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_FLOAT: return new Field_float(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_decimals(pack_flag), f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag)== 0); case MYSQL_TYPE_DOUBLE: return new Field_double(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_decimals(pack_flag), f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag)== 0); case MYSQL_TYPE_TINY: return new Field_tiny(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_SHORT: return new Field_short(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_INT24: return new Field_medium(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_LONG: return new Field_long(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_LONGLONG: return new Field_longlong(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_TIMESTAMP: return new Field_timestamp(ptr, field_length, null_pos, null_bit, unireg_check, field_name); case MYSQL_TYPE_TIMESTAMP2: return new Field_timestampf(ptr, null_pos, null_bit, unireg_check, field_name, field_length > MAX_DATETIME_WIDTH ? field_length - 1 - MAX_DATETIME_WIDTH : 0); case MYSQL_TYPE_YEAR: return new Field_year(ptr,field_length,null_pos,null_bit, unireg_check, field_name); case MYSQL_TYPE_NEWDATE: return new Field_newdate(ptr, null_pos, null_bit, unireg_check, field_name); case MYSQL_TYPE_TIME: return new Field_time(ptr, null_pos, null_bit, unireg_check, field_name); case MYSQL_TYPE_TIME2: return new Field_timef(ptr, null_pos, null_bit, unireg_check, field_name, (field_length > MAX_TIME_WIDTH) ? field_length - 1 - MAX_TIME_WIDTH : 0); case MYSQL_TYPE_DATETIME: return new Field_datetime(ptr, null_pos, null_bit, unireg_check, field_name); case MYSQL_TYPE_DATETIME2: return new Field_datetimef(ptr, null_pos, null_bit, unireg_check, field_name, (field_length > MAX_DATETIME_WIDTH) ? field_length - 1 - MAX_DATETIME_WIDTH : 0); case MYSQL_TYPE_NULL: return new Field_null(ptr, field_length, unireg_check, field_name, field_charset); case MYSQL_TYPE_BIT: return f_bit_as_char(pack_flag) ? new Field_bit_as_char(ptr, field_length, null_pos, null_bit, unireg_check, field_name) : new Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr, bit_offset, unireg_check, field_name); default: // Impossible (Wrong version) break; } return 0; } /** Constructs a column definition from an actual column object. This is a reverse-engineering procedure that creates a column definition object as produced by the parser (Create_field) from a resolved column object (Field). @param old_field The column object from which the column definition is constructed. @param orig_field Used for copying default values. This parameter may be NULL, but if present it is used for copying default values. Default values are copied into an Item_string unless: @li The default value is a function. @li There is no default value. @li old_field is a BLOB column. @li old_field has its data pointer improperly initialized. */ Create_field::Create_field(Field *old_field,Field *orig_field) : field_name(old_field->field_name), change(NULL), comment(old_field->comment), sql_type(old_field->real_type()), length(old_field->field_length), decimals(old_field->decimals()), flags(old_field->flags), pack_length(old_field->pack_length()), key_length(old_field->key_length()), unireg_check(old_field->unireg_check), charset(old_field->charset()), // May be NULL ptr field(old_field), gcol_info(old_field->gcol_info), stored_in_db(old_field->stored_in_db) { switch (sql_type) { case MYSQL_TYPE_BLOB: switch (pack_length - portable_sizeof_char_ptr) { case 1: sql_type= MYSQL_TYPE_TINY_BLOB; break; case 2: sql_type= MYSQL_TYPE_BLOB; break; case 3: sql_type= MYSQL_TYPE_MEDIUM_BLOB; break; default: sql_type= MYSQL_TYPE_LONG_BLOB; break; } length/= charset->mbmaxlen; key_length/= charset->mbmaxlen; break; case MYSQL_TYPE_STRING: /* Change CHAR -> VARCHAR if dynamic record length */ if (old_field->type() == MYSQL_TYPE_VAR_STRING) sql_type= MYSQL_TYPE_VARCHAR; /* fall through */ case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: /* This is corrected in create_length_to_internal_length */ length= (length+charset->mbmaxlen-1) / charset->mbmaxlen; break; case MYSQL_TYPE_GEOMETRY: geom_type= ((Field_geom*)old_field)->geom_type; break; case MYSQL_TYPE_YEAR: if (length != 4) length= 4; //set default value break; default: break; } if (flags & (ENUM_FLAG | SET_FLAG)) interval= ((Field_enum*) old_field)->typelib; else interval=0; def=0; char_length= length; /* Copy the default (constant/function) from the column object orig_field, if supplied. We do this if all these conditions are met: - The column allows a default. - The column type is not a BLOB type. - The original column (old_field) was properly initialized with a record buffer pointer. */ if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && old_field->ptr != NULL && orig_field != NULL) { bool default_now= false; if (real_type_with_now_as_default(sql_type)) { // The SQL type of the new field allows a function default: default_now= orig_field->has_insert_default_function(); bool update_now= orig_field->has_update_default_function(); if (default_now && update_now) unireg_check= Field::TIMESTAMP_DNUN_FIELD; else if (default_now) unireg_check= Field::TIMESTAMP_DN_FIELD; else if (update_now) unireg_check= Field::TIMESTAMP_UN_FIELD; } if (!default_now) // Give a constant default { char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff), charset); /* Get the value from default_values */ my_ptrdiff_t diff= orig_field->table->default_values_offset(); orig_field->move_field_offset(diff); // Points now at default_values if (!orig_field->is_real_null()) { char buff[MAX_FIELD_WIDTH], *pos; String tmp(buff, sizeof(buff), charset), *res; res= orig_field->val_str(&tmp); pos= sql_strmake(res->ptr(), res->length()); def= new Item_string(pos, res->length(), charset); } orig_field->move_field_offset(-diff); // Back to record[0] } } } /** maximum possible 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() { switch (packlength) { case 1: return 255; case 2: return 65535; case 3: return 16777215; case 4: return (uint32) 4294967295U; default: assert(0); // we should never go here return 0; } } /** This function creates a separate copy of blob value. @param [in] mem_root mem_root that is used to allocate memory for 'copy_of_value'. @return - Can fail if we are out of memory. @retval false Success @retval true Failure */ bool Field_blob::copy_blob_value(MEM_ROOT *mem_root) { DBUG_ENTER("copy_blob_value"); // Testing memory allocation failure DBUG_EXECUTE_IF("simulate_blob_memory_allocation_fail", DBUG_SET("+d,simulate_out_of_memory");); // Allocate new memory location size_t ulen= get_length(ptr); char *blob_value= (char *) alloc_root(mem_root, ulen); if (!blob_value) DBUG_RETURN(true); // Copy Data uchar *temp_ptr; get_ptr(&temp_ptr); memcpy(blob_value, temp_ptr, ulen); // Set ptr of Field for duplicated data store_ptr_and_length(blob_value, ulen); // Set 'value' with the duplicated data value.set(blob_value, ulen, value.charset()); DBUG_RETURN(false); } /** maximum possible display length for blob. @return length */ uint32 Field_blob::max_display_length() { switch (packlength) { case 1: return 255 * field_charset->mbmaxlen; case 2: return 65535 * field_charset->mbmaxlen; case 3: return 16777215 * field_charset->mbmaxlen; case 4: return (uint32) 4294967295U; default: 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 @param view_db_name - if set this is the database name for view that causes the warning @param view_name - if set this is the name of view that causes the warning @note This function won't produce warning and increase cut fields counter if count_cuted_fields == CHECK_FIELD_IGNORE for current thread. if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes. This allows us to avoid notes in optimisation, like convert_constant_item(). In case of execution statements INSERT/INSERT SELECT/REPLACE/REPLACE SELECT the method emits only one warning message for the following types of warning: ER_BAD_NULL_ERROR, ER_WARN_NULL_TO_NOTNULL, ER_NO_DEFAULT_FOR_FIELD. @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_severity_level level, uint code, int cut_increment, const char *view_db_name, const char *view_name) { /* If this field was created only for type conversion purposes it will have table == NULL. */ THD *thd= table ? table->in_use : current_thd; if (!thd->count_cuted_fields) return level >= Sql_condition::SL_WARNING; thd->cuted_fields+= cut_increment; if (thd->lex->sql_command != SQLCOM_INSERT && thd->lex->sql_command != SQLCOM_INSERT_SELECT && thd->lex->sql_command != SQLCOM_REPLACE && thd->lex->sql_command != SQLCOM_REPLACE_SELECT) { // We aggregate warnings from only INSERT and REPLACE statements. push_warning_printf(thd, level, code, ER(code), field_name, thd->get_stmt_da()->current_row_for_condition()); return 0; } unsigned int current_warning_mask= 0; if (code == ER_BAD_NULL_ERROR) current_warning_mask= BAD_NULL_ERROR_PUSHED; else if (code == ER_NO_DEFAULT_FOR_FIELD) current_warning_mask= NO_DEFAULT_FOR_FIELD_PUSHED; if (current_warning_mask) { if (!(m_warnings_pushed & current_warning_mask)) { push_warning_printf(thd, level, code, ER(code), field_name, thd->get_stmt_da()->current_row_for_condition()); m_warnings_pushed|= current_warning_mask; } } else if (code == ER_NO_DEFAULT_FOR_VIEW_FIELD) { if (!(m_warnings_pushed & NO_DEFAULT_FOR_VIEW_FIELD_PUSHED)) { push_warning_printf(thd, Sql_condition::SL_WARNING, ER_NO_DEFAULT_FOR_VIEW_FIELD, ER(ER_NO_DEFAULT_FOR_VIEW_FIELD), view_db_name, view_name); m_warnings_pushed|= NO_DEFAULT_FOR_VIEW_FIELD_PUSHED; } } else { push_warning_printf(thd, level, code, ER(code), field_name, thd->get_stmt_da()->current_row_for_condition()); } return 0; } /** Produce warning or note about double datetime data saved into field. @param level level of message (Note/Warning/Error) @param code error code of message to be produced @param val error parameter (the value) @param ts_type type of datetime value (datetime/date/time) @param cut_increment whenever we should increase cut fields count @note This function will always produce some warning but won't increase cut fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current thread. */ void Field_temporal::set_datetime_warning(Sql_condition::enum_severity_level level, uint code, ErrConvString val, timestamp_type ts_type, int cut_increment) { THD *thd= table ? table->in_use : current_thd; if ((!thd->lex->is_ignore() && ((thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) || (thd->variables.sql_mode & MODE_STRICT_TRANS_TABLES && !thd->get_transaction()->cannot_safely_rollback(Transaction_ctx::STMT)))) || set_warning(level, code, cut_increment)) make_truncated_value_warning(thd, level, val, ts_type, field_name); } bool Field::is_part_of_actual_key(THD *thd, uint cur_index, KEY *cur_index_info) { return thd->optimizer_switch_flag(OPTIMIZER_SWITCH_USE_INDEX_EXTENSIONS) && !(cur_index_info->flags & HA_NOSAME) ? part_of_key.is_set(cur_index) : part_of_key_not_extended.is_set(cur_index); }