mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	 10c063f9f0
			
		
	
	
	10c063f9f0
	
	
	
		
			
			Fixing the code adding MySQL _0900_ collations as _uca1400_ aliases
not to perform deep initialization of the corresponding _uca1400_
collations.
Only basic initialization is now performed which allows to watch
these collations (both _0900_ and _uca1400_) in queries to
INFORMATION_SCHEMA tables COLLATIONS and
COLLATION_CHARACTER_SET_APPLICABILITY,
as well as in SHOW COLLATION statements.
Deep initialization is now performed only when a collation
(either the _0900_ alias or the corresponding  _uca1400_ collation)
is used for the very first time after the server startup.
Refactoring was done to maintain the code easier:
- most of the _uca1400_ code was moved from ctype-uca.c
  to a new file ctype-uca1400.c
- most of the _0900_ code was moved from type-uca.c
  to a new file ctype-uca0900.c
Change details:
- The original function add_alias_for_collation() added by the patch for
   "MDEV-20912 Add support for utf8mb4_0900_* collations in MariaDB Server"
  was removed from mysys/charset.c, as it had two two problems:
  a. it forced deep initialization of the _uca1400_ collations
     when adding _0900_ aliases for them at the server startup
     (the main reported problem)
  b. the collation initialization code in add_alias_for_collation()
     was related more to collations rather than to memory management,
     so /strings should be a better place for it than /mysys.
  The code from add_alias_for_collation() was split into separate functions.
  Cyclic dependency was removed. `#include <my_sys.h>` was removed
  from /strings/ctype-uca.c. Collations are now added using a callback
  function MY_CHARSET_LOADED::add_collation, like it is done for
  user collations defined in Index.xml. The code in /mysys sets
  MY_CHARSET_LOADED::add_collation to add_compiled_collation().
- The function compare_collations() was removed.
  A new virtual function was added into my_collation_handler_st instead:
    my_bool (*eq_collation)(CHARSET_INFO *self, CHARSET_INFO *other);
  because it is the collation handler who knows how to detect equal
  collations by comparing only some of CHARSET_INFO members without
  their deep initialization.
  Three implementations were added:
  - my_ci_eq_collation_uca() for UCA collations, it compares
    _0900_ collations as equal to their corresponding _uca1400_ collations.
  - my_ci_eq_collation_utf8mb4_bin(), it compares
    utf8mb4_nopad_bin and utf8mb4_0900_bin as equal.
  - my_ci_eq_collation_generic() - the default implementation,
    which compares all collations as not equal.
  A C++ wrapper CHARSET_INFO::eq_collations() was added.
  The code in /sql was changes to use the wrapper instead of
  the former calls for the removed function compare_collations().
- A part of add_alias_for_collation() was moved into a new function
  my_ci_alloc(). It allocates a memory for a new charset_info_st
  instance together with the collation name and the comment using a single
  MY_CHARSET_LOADER::once_alloc call, which points to my_once_alloc()
  in the server.
- A part of add_alias_for_collation() was moved into a new function
  my_ci_make_comment_for_alias(). It makes an "Alias for xxx" string,
  e.g. "Alias for utf8mb4_uca1400_swedish_ai_ci" in case of
  utf8mb4_sv_0900_ai_ci.
- A part of the code in create_tailoring() was moved to
  a new function my_uca1400_collation_get_initialized_shared_uca(),
  to reuse the code between _uca1400_ and _0900_ collations.
- A new function my_collation_id_is_mysql_uca0900() was added
  in addition to my_collation_id_is_mysql_uca1400().
- Functions to build collation names were added:
   my_uca0900_collation_build_name()
   my_uca1400_collation_build_name()
- A shared function function was added:
  my_bool
  my_uca1400_collation_alloc_and_init(MY_CHARSET_LOADER *loader,
                                      LEX_CSTRING name,
                                      LEX_CSTRING comment,
                                      const uca_collation_def_param_t *param,
                                      uint id)
  It's reused to add _uca1400_ and _0900_ collations, with basic
  initialization (without deep initialization).
- The function add_compiled_collation() changed its return type from
  void to int, to make it compatible with MY_CHARSET_LOADER::add_collation.
- Functions mysql_uca0900_collation_definition_add(),
  mysql_uca0900_utf8mb4_collation_definitions_add(),
  mysql_utf8mb4_0900_bin_add() were added into ctype-uca0900.c.
  They get MY_CHARSET_LOADER as a parameter.
- Functions my_uca1400_collation_definition_add(),
  my_uca1400_collation_definitions_add() were moved from
  charset-def.c to strings/ctype-uca1400.c.
  The latter now accepts MY_CHARSET_LOADER as the first parameter
  instead of initializing a MY_CHARSET_LOADER inside.
- init_compiled_charsets() now initializes a MY_CHARSET_LOADER
  variable and passes it to all functions adding collations:
  - mysql_utf8mb4_0900_collation_definitions_add()
  - mysql_uca0900_utf8mb4_collation_definitions_add()
  - mysql_utf8mb4_0900_bin_add()
- A new structure was added into ctype-uca.h:
  typedef struct uca_collation_def_param
  {
    my_cs_encoding_t cs_id;
    uint tailoring_id;
    uint nopad_flags;
    uint level_flags;
  } uca_collation_def_param_t;
  It simplifies reusing the code for _uca1400_ and _0900_ collations.
- The definition of MY_UCA1400_COLLATION_DEFINITION was
  moved from ctype-uca.c to ctype-uca1400.h, to reuse
  the code for _uca1400_ and _0900_ collations.
- The definitions of "MY_UCA_INFO my_uca_v1400" and
  "MY_UCA_INFO my_uca1400_info_tailored[][]" were moved from
  ctype-uca.c to ctype-uca1400.c.
- The definitions/declarations of:
  - mysql_0900_collation_start,
  - struct mysql_0900_to_mariadb_1400_mapping
  - mysql_0900_to_mariadb_1400_mapping
  - mysql_utf8mb4_0900_collation_definitions_add()
  were moved from ctype-uca.c to ctype-uca0900.c
- Functions
  my_uca1400_make_builtin_collation_id()
  my_uca1400_collation_definition_init()
  my_uca1400_collation_id_uca400_compat()
  my_ci_get_collation_name_uca1400_context()
  were moved from ctype-uca.c to ctype-uca1400.c and ctype-uca1400.h
- A part of my_uca1400_collation_definition_init()
  was moved into my_uca0520_builtin_collation_by_id(),
  to make functions smaller.
		
	
			
		
			
				
	
	
		
			11766 lines
		
	
	
	
		
			342 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			11766 lines
		
	
	
	
		
			342 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|    Copyright (c) 2000, 2017, Oracle and/or its affiliates.
 | |
|    Copyright (c) 2008, 2022, MariaDB
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; version 2 of the License.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA
 | |
| */
 | |
| 
 | |
| /**
 | |
|   @file
 | |
| 
 | |
|   @brief
 | |
|   This file implements classes defined in field.h
 | |
| */
 | |
| 
 | |
| #ifdef USE_PRAGMA_IMPLEMENTATION
 | |
| #pragma implementation				// gcc: Class implementation
 | |
| #endif
 | |
| 
 | |
| #include "mariadb.h"
 | |
| #include "sql_priv.h"
 | |
| #include "sql_select.h"
 | |
| #include "rpl_rli.h"                            // Pull in Relay_log_info
 | |
| #include "slave.h"                              // Pull in rpl_master_has_bug()
 | |
| #include "strfunc.h"                            // find_type2, find_set
 | |
| #include "tztime.h"                      // struct Time_zone
 | |
| #include "filesort.h"                    // change_double_for_sort
 | |
| #include "log_event.h"                   // class Table_map_log_event
 | |
| #include <m_ctype.h>
 | |
| 
 | |
| // Maximum allowed exponent value for converting string to decimal
 | |
| #define MAX_EXPONENT 1024
 | |
| 
 | |
| /*****************************************************************************
 | |
|   Instantiate templates and static variables
 | |
| *****************************************************************************/
 | |
| 
 | |
| static const char *zero_timestamp="0000-00-00 00:00:00.000000";
 | |
| LEX_CSTRING temp_lex_str= {STRING_WITH_LEN("temp")};
 | |
| 
 | |
| uchar Field_null::null[1]={1};
 | |
| const char field_separator=',';
 | |
| 
 | |
| #define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE FLOATING_POINT_BUFFER
 | |
| #define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128
 | |
| #define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128
 | |
| #define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
 | |
|                         ((ulong) ((1LL << MY_MIN(arg, 4) * 8) - 1))
 | |
| 
 | |
| // Column marked for read or the field set to read out of record[0]
 | |
| bool Field::marked_for_read() const
 | |
| {
 | |
|   return !table ||
 | |
|          (!table->read_set ||
 | |
|           bitmap_is_set(table->read_set, field_index) ||
 | |
|           (!(ptr >= table->record[0] &&
 | |
|              ptr < table->record[0] + table->s->reclength)));
 | |
| }
 | |
| 
 | |
| /*
 | |
|   The name of this function is a bit misleading as in 10.4 we don't
 | |
|   have to test anymore if the field is computed. Instead we mark
 | |
|   changed fields with DBUG_FIX_WRITE_SET() in table.cc
 | |
| */
 | |
| 
 | |
| bool Field::marked_for_write_or_computed() const
 | |
| {
 | |
|   return (!table ||
 | |
|           (!table->write_set ||
 | |
|            bitmap_is_set(table->write_set, field_index) ||
 | |
|            (!(ptr >= table->record[0] &&
 | |
|               ptr < table->record[0] + table->s->reclength))));
 | |
| }
 | |
| 
 | |
| 
 | |
| #define FLAGSTR(S,F) ((S) & (F) ? #F " " : "")
 | |
| 
 | |
| /*
 | |
|   Rules for merging different types of fields in UNION
 | |
| 
 | |
|   NOTE: to avoid 256*256 table, gap in table types numeration is skipped
 | |
|   following #defines describe that gap and how to canculate number of fields
 | |
|   and index of field in this array.
 | |
| */
 | |
| const int FIELDTYPE_TEAR_FROM= (MYSQL_TYPE_BIT + 1);
 | |
| const int FIELDTYPE_TEAR_TO=   (MYSQL_TYPE_NEWDECIMAL - 1);
 | |
| const int FIELDTYPE_LAST=      254;
 | |
| const int FIELDTYPE_NUM=       FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST -
 | |
|                                                       FIELDTYPE_TEAR_TO);
 | |
| 
 | |
| static inline int merge_type2index(enum_field_types merge_type)
 | |
| {
 | |
|   DBUG_ASSERT(merge_type < FIELDTYPE_TEAR_FROM ||
 | |
|               merge_type > FIELDTYPE_TEAR_TO);
 | |
|   DBUG_ASSERT(merge_type <= FIELDTYPE_LAST);
 | |
|   if (merge_type < FIELDTYPE_TEAR_FROM)
 | |
|     return merge_type;
 | |
|   return FIELDTYPE_TEAR_FROM + (merge_type - FIELDTYPE_TEAR_TO) - 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Implements data type merge rules for the built-in traditional data types.
 | |
|   Used for operations such as:
 | |
|   - UNION
 | |
|   - CASE and its abbreviations COALESCE, IF, IFNULL
 | |
|   - LEAST/GREATEST
 | |
| 
 | |
|   Given Fields A and B of real_types a and b, we find the result type of
 | |
|   COALESCE(A, B) by querying:
 | |
|     field_types_merge_rules[field_type_to_index(a)][field_type_to_index(b)].
 | |
| */
 | |
| static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
 | |
| {
 | |
|   /* MYSQL_TYPE_DECIMAL -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_TINY -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_TINY,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_TINY,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_SHORT -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_SHORT,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_SHORT,       MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_SHORT,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_LONG -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_LONG,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_LONG,        MYSQL_TYPE_LONG,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_LONG,         MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONG,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_LONG,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_FLOAT -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_FLOAT,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_FLOAT,       MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_FLOAT,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_FLOAT,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_DOUBLE -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_NULL -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_TINY,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_NULL,        MYSQL_TYPE_TIMESTAMP,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONGLONG,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_TIME,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_YEAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_BIT,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_ENUM,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_SET,         MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_TIMESTAMP -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_TIMESTAMP,   MYSQL_TYPE_TIMESTAMP,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_DATETIME,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_LONGLONG -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_LONGLONG,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONGLONG,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONGLONG,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_LONGLONG,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_INT24 -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_INT24,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_INT24,       MYSQL_TYPE_LONG,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_INT24,       MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_INT24,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL    MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_DATE -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,  MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_TIME -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_TIME,        MYSQL_TYPE_DATETIME,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_TIME,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_DATETIME -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_DATETIME,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_DATETIME,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_YEAR -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_TINY,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_YEAR,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_YEAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_NEWDATE -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_VARCHAR -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_VARCHAR
 | |
|   },
 | |
|   /* MYSQL_TYPE_BIT -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_BIT,         MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_BIT,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_NEWDECIMAL -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_NEWDECIMAL,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_ENUM -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_ENUM,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_SET -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_SET,         MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   },
 | |
|   /* MYSQL_TYPE_TINY_BLOB -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_TINY_BLOB
 | |
|   },
 | |
|   /* MYSQL_TYPE_MEDIUM_BLOB -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_MEDIUM_BLOB
 | |
|   },
 | |
|   /* MYSQL_TYPE_LONG_BLOB -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_LONG_BLOB
 | |
|   },
 | |
|   /* MYSQL_TYPE_BLOB -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_BLOB
 | |
|   },
 | |
|   /* MYSQL_TYPE_VAR_STRING -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_VARCHAR
 | |
|   },
 | |
|   /* MYSQL_TYPE_STRING -> */
 | |
|   {
 | |
|   //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
 | |
|   //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
 | |
|   //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
 | |
|   //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
 | |
|   //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
 | |
|   //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
 | |
|   //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
 | |
|   //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_BIT          <16>-<245>
 | |
|     MYSQL_TYPE_STRING,
 | |
|   //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
 | |
|   //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
 | |
|     MYSQL_TYPE_STRING,      MYSQL_TYPE_TINY_BLOB,
 | |
|   //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
 | |
|     MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
 | |
|   //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
 | |
|     MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
 | |
|   //MYSQL_TYPE_STRING
 | |
|     MYSQL_TYPE_STRING
 | |
|   }
 | |
| };
 | |
| 
 | |
| 
 | |
| const Type_handler *
 | |
| Type_handler::aggregate_for_result_traditional(const Type_handler *a,
 | |
|                                                const Type_handler *b)
 | |
| {
 | |
|   if (a == b)
 | |
|   {
 | |
|     /*
 | |
|       If two traditional handlers are equal, quickly return "a".
 | |
|       Some handlers (e.g. Type_handler_bool) pretend to be traditional,
 | |
|       but in fact they are not traditional in full extent, they are
 | |
|       only sub-types for now (and don't have a corresponding Field_xxx yet).
 | |
|       Here we preserve such handlers during aggregation.
 | |
|       As a result, COALESCE(true,true) preserves the "boolean" data type.
 | |
| 
 | |
|       Need to do this conversion for deprecated data types,
 | |
|       similar to what field_type_merge_rules[][] does.
 | |
|     */
 | |
|     switch (a->field_type()) {
 | |
|     case MYSQL_TYPE_DECIMAL:     return &type_handler_newdecimal;
 | |
|     case MYSQL_TYPE_DATE:        return &type_handler_newdate;
 | |
|     case MYSQL_TYPE_VAR_STRING:  return &type_handler_varchar;
 | |
|     default: break;
 | |
|     }
 | |
|     return a;
 | |
|   }
 | |
|   enum_field_types ta= a->traditional_merge_field_type();
 | |
|   enum_field_types tb= b->traditional_merge_field_type();
 | |
|   enum_field_types res= field_types_merge_rules[merge_type2index(ta)]
 | |
|                                                [merge_type2index(tb)];
 | |
|   return Type_handler::get_handler_by_real_type(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::check_assignability_from(const Type_handler *from,
 | |
|                                      bool ignore) const
 | |
| {
 | |
|   /*
 | |
|     Using type_handler_for_item_field() here to get the data type handler
 | |
|     on both sides. This is needed to make sure aggregation for Field
 | |
|     works the same way with how Item_field aggregates for UNION or CASE,
 | |
|     so these statements:
 | |
|       SELECT a FROM t1 UNION SELECT b FROM t1; // Item_field vs Item_field
 | |
|       UPDATE t1 SET a=b;                       // Field      vs Item_field
 | |
|     either both return "Illegal parameter data types" or both pass
 | |
|     the data type compatibility test.
 | |
|     For MariaDB standard data types, using type_handler_for_item_field()
 | |
|     turns ENUM/SET into just CHAR.
 | |
|   */
 | |
|   Type_handler_hybrid_field_type th(type_handler()->
 | |
|                                       type_handler_for_item_field());
 | |
|   if (th.aggregate_for_result(from->type_handler_for_item_field()))
 | |
|   {
 | |
|     bool error= (!ignore && get_thd()->is_strict_mode()) ||
 | |
|                 (type_handler()->is_scalar_type() != from->is_scalar_type());
 | |
|     /*
 | |
|       Display fully qualified column name for table columns.
 | |
|       Display non-qualified names for other things,
 | |
|       e.g. SP variables, SP return values, SP and CURSOR parameters.
 | |
|     */
 | |
|     if (table->s->db.str && table->s->table_name.str)
 | |
|       my_printf_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION,
 | |
|                       "Cannot cast '%s' as '%s' in assignment of %`s.%`s.%`s",
 | |
|                       MYF(error ? 0 : ME_WARNING),
 | |
|                       from->name().ptr(), type_handler()->name().ptr(),
 | |
|                       table->s->db.str, table->s->table_name.str,
 | |
|                       field_name.str);
 | |
|     else
 | |
|       my_printf_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION,
 | |
|                       "Cannot cast '%s' as '%s' in assignment of %`s",
 | |
|                       MYF(error ? 0 : ME_WARNING),
 | |
|                       from->name().ptr(), type_handler()->name().ptr(),
 | |
|                       field_name.str);
 | |
|     return error;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Test if the given string contains important data:
 | |
|   not spaces for character string,
 | |
|   or any data for binary string.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     test_if_important_data()
 | |
|     cs          Character set
 | |
|     str         String to test
 | |
|     strend      String end
 | |
| 
 | |
|   RETURN
 | |
|     FALSE - If string does not have important data
 | |
|     TRUE  - If string has some important data
 | |
| */
 | |
| 
 | |
| static bool
 | |
| test_if_important_data(CHARSET_INFO *cs, const char *str, const char *strend)
 | |
| {
 | |
|   if (cs != &my_charset_bin)
 | |
|     str+= cs->scan(str, strend, MY_SEQ_SPACES);
 | |
|   return (str < strend);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Function to compare two unsigned integers for their relative order.
 | |
|    Used below. In an anonymous namespace to not clash with definitions
 | |
|    in other files.
 | |
|  */
 | |
| 
 | |
| CPP_UNNAMED_NS_START
 | |
| 
 | |
| int compare(unsigned int a, unsigned int b)
 | |
| {
 | |
|   if (a < b)
 | |
|     return -1;
 | |
|   if (b < a)
 | |
|     return 1;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| CPP_UNNAMED_NS_END
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|   Static help functions
 | |
| *****************************************************************************/
 | |
| 
 | |
| /*
 | |
|   @brief
 | |
|     Create a fixed size sort key part
 | |
| 
 | |
|   @param  buff           buffer where values are written
 | |
|   @param  length         fixed size of the sort column
 | |
| */
 | |
| 
 | |
| void Field::make_sort_key_part(uchar *buff,uint length)
 | |
| {
 | |
|   if (maybe_null())
 | |
|   {
 | |
|     if (is_null())
 | |
|     {
 | |
|       bzero(buff, length + 1);
 | |
|       return;
 | |
|     }
 | |
|     *buff++= 1;
 | |
|   }
 | |
|   sort_string(buff, length);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   @brief
 | |
|     Create a packed sort key part
 | |
| 
 | |
|   @param  buff           buffer where values are written
 | |
|   @param  sort_field     sort column structure
 | |
| 
 | |
|   @retval
 | |
|     length of the bytes written, does not include the NULL bytes
 | |
| */
 | |
| uint
 | |
| Field::make_packed_sort_key_part(uchar *buff,
 | |
|                                  const SORT_FIELD_ATTR *sort_field)
 | |
| {
 | |
|   if (maybe_null())
 | |
|   {
 | |
|     if (is_null())
 | |
|     {
 | |
|       *buff++= 0;
 | |
|       return 0;  // For NULL values don't write any data
 | |
|     }
 | |
|     *buff++=1;
 | |
|   }
 | |
|   sort_string(buff, sort_field->original_length);
 | |
|   return sort_field->original_length;
 | |
| }
 | |
| 
 | |
| 
 | |
| uint
 | |
| Field_longstr::make_packed_sort_key_part(uchar *buff,
 | |
|                                          const SORT_FIELD_ATTR *sort_field)
 | |
| {
 | |
|   if (maybe_null())
 | |
|   {
 | |
|     if (is_null())
 | |
|     {
 | |
|       *buff++= 0;
 | |
|       return 0;   // For NULL values don't write any data
 | |
|     }
 | |
|     *buff++=1;
 | |
|   }
 | |
|   uchar *end= pack_sort_string(buff, sort_field);
 | |
|   return (uint) (end-buff);
 | |
| }
 | |
| 
 | |
| 
 | |
| uchar*
 | |
| Field_longstr::pack_sort_string(uchar *to, const SORT_FIELD_ATTR *sort_field)
 | |
| {
 | |
|   StringBuffer<LONGLONG_BUFFER_SIZE+1> buf;
 | |
|   val_str(&buf, &buf);
 | |
|   return to + sort_field->pack_sort_string(to, &buf, field_charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @brief
 | |
|   Determine the relative position of the field value in a numeric interval
 | |
| 
 | |
|   @details
 | |
|   The function returns a double number between 0.0 and 1.0 as the relative
 | |
|   position of the value of the this field in the numeric interval of [min,max].
 | |
|   If the value is not in the interval the the function returns 0.0 when
 | |
|   the value is less than min, and, 1.0 when the value is greater than max.
 | |
| 
 | |
|   @param  min  value of the left end of the interval
 | |
|   @param  max  value of the right end of the interval
 | |
| 
 | |
|   @return
 | |
|   relative position of the field value in the numeric interval [min,max] 
 | |
| */
 | |
| 
 | |
| double pos_in_interval_for_double(double midp_val, double min_val,
 | |
|                                   double max_val)
 | |
| {
 | |
|   double n, d;
 | |
|   n= midp_val - min_val;
 | |
|   if (n < 0)
 | |
|     return 0.0;
 | |
|   d= max_val - min_val;
 | |
|   if (d <= 0)
 | |
|     return 1.0;
 | |
|   return MY_MIN(n/d, 1.0);
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field::pos_in_interval_val_real(Field *min, Field *max)
 | |
| {
 | |
|   return pos_in_interval_for_double(val_real(), min->val_real(),
 | |
|                                     max->val_real());
 | |
| }
 | |
| 
 | |
| 
 | |
| static
 | |
| inline ulonglong char_prefix_to_ulonglong(uchar *src)
 | |
| {
 | |
|   uint sz= sizeof(ulonglong);
 | |
|   for (uint i= 0; i < sz/2; i++)
 | |
|   {
 | |
|     uchar tmp= src[i];
 | |
|     src[i]= src[sz-1-i];
 | |
|     src[sz-1-i]= tmp;
 | |
|   }
 | |
|   return uint8korr(src); 
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Compute res = a - b, without losing precision and taking care that these are
 | |
|   unsigned numbers.
 | |
| */
 | |
| static inline double safe_substract(ulonglong a, ulonglong b)
 | |
| {
 | |
|   return (a > b)? double(a - b) : -double(b - a);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @brief
 | |
|   Determine the relative position of the field value in a string interval
 | |
| 
 | |
|   @details
 | |
|   The function returns a double number between 0.0 and 1.0 as the relative
 | |
|   position of the value of the this field in the string interval of [min,max].
 | |
|   If the value is not in the interval the the function returns 0.0 when
 | |
|   the value is less than min, and, 1.0 when the value is greater than max.
 | |
| 
 | |
|   @note
 | |
|   To calculate the relative position of the string value v in the interval
 | |
|   [min, max] the function first converts the beginning of these three
 | |
|   strings v, min, max into the strings that are used for byte comparison.
 | |
|   For each string not more sizeof(ulonglong) first bytes are taken
 | |
|   from the result of conversion. Then these bytes are interpreted as the
 | |
|   big-endian representation of an ulonglong integer. The values of these
 | |
|   integer numbers obtained for the strings v, min, max are used to calculate
 | |
|   the position of v in [min,max] in the same way is it's done for numeric
 | |
|   fields (see Field::pos_in_interval_val_real).
 | |
| 
 | |
|   @todo
 | |
|   Improve the procedure for the case when min and max have the same
 | |
|   beginning
 | |
|      
 | |
|   @param  min  value of the left end of the interval
 | |
|   @param  max  value of the right end of the interval
 | |
| 
 | |
|   @return
 | |
|   relative position of the field value in the string interval [min,max] 
 | |
| */
 | |
| 
 | |
| double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
 | |
| {
 | |
|   return pos_in_interval_for_string(charset(),
 | |
|             ptr      + data_offset, data_length(),
 | |
|             min->ptr + data_offset, min->data_length(),
 | |
|             max->ptr + data_offset, max->data_length()
 | |
|          );
 | |
| }
 | |
| 
 | |
| 
 | |
| double pos_in_interval_for_string(CHARSET_INFO *cset,
 | |
|                                   const uchar *midp_val, uint32 midp_len,
 | |
|                                   const uchar *min_val,  uint32 min_len,
 | |
|                                   const uchar *max_val,  uint32 max_len)
 | |
| {
 | |
|   uchar mp_prefix[sizeof(ulonglong)];
 | |
|   uchar minp_prefix[sizeof(ulonglong)];
 | |
|   uchar maxp_prefix[sizeof(ulonglong)];
 | |
|   ulonglong mp, minp, maxp;
 | |
| 
 | |
|   cset->strnxfrm(mp_prefix, sizeof(mp), midp_val, midp_len);
 | |
|   cset->strnxfrm(minp_prefix, sizeof(minp), min_val, min_len);
 | |
|   cset->strnxfrm(maxp_prefix, sizeof(maxp), max_val, max_len);
 | |
| 
 | |
|   mp=   char_prefix_to_ulonglong(mp_prefix);
 | |
|   minp= char_prefix_to_ulonglong(minp_prefix);
 | |
|   maxp= char_prefix_to_ulonglong(maxp_prefix);
 | |
| 
 | |
|   double n, d;
 | |
|   n= safe_substract(mp, minp);
 | |
|   if (n < 0)
 | |
|     return 0.0;
 | |
|   d= safe_substract(maxp, minp);
 | |
|   if (d <= 0)
 | |
|     return 1.0;
 | |
|   return MY_MIN(n/d, 1.0);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const
 | |
| {
 | |
|   DBUG_ASSERT(cmp_type() != STRING_RESULT); // For STRING_RESULT see Field_str
 | |
|   /*
 | |
|     We use result_type() rather than cmp_type() in the below condition,
 | |
|     because it covers a special case that string literals guarantee uniqueness
 | |
|     for temporal columns, so the query:
 | |
|       WHERE temporal_column='string'
 | |
|     cannot return multiple distinct temporal values.
 | |
| 
 | |
|     TODO: perhaps we could allow INT/DECIMAL/DOUBLE types for temporal items.
 | |
|   */
 | |
|   return result_type() == item->result_type();
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check whether a field item can be substituted for an equal item
 | |
| 
 | |
|   @details
 | |
|   The function checks whether a substitution of a field item for
 | |
|   an equal item is valid.
 | |
| 
 | |
|   @param arg   *arg != NULL <-> the field is in the context
 | |
|                where substitution for an equal item is valid
 | |
| 
 | |
|   @note
 | |
|     The following statement is not always true:
 | |
|   @n
 | |
|     x=y => F(x)=F(x/y).
 | |
|   @n
 | |
|     This means substitution of an item for an equal item not always
 | |
|     yields an equavalent condition. Here's an example:
 | |
|     @code
 | |
|     'a'='a '
 | |
|     (LENGTH('a')=1) != (LENGTH('a ')=2)
 | |
|   @endcode
 | |
|     Such a substitution is surely valid if either the substituted
 | |
|     field is not of a STRING type or if it is an argument of
 | |
|     a comparison predicate.
 | |
| 
 | |
|   @retval
 | |
|     TRUE   substitution is valid
 | |
|   @retval
 | |
|     FALSE  otherwise
 | |
| */
 | |
| 
 | |
| bool Field::can_be_substituted_to_equal_item(const Context &ctx,
 | |
|                                              const Item_equal *item_equal)
 | |
| {
 | |
|   DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT);
 | |
|   DBUG_ASSERT(cmp_type() != STRING_RESULT);
 | |
|   switch (ctx.subst_constraint()) {
 | |
|   case ANY_SUBST:
 | |
|     /*
 | |
|       Disable const propagation for items used in different comparison contexts.
 | |
|       This must be done because, for example, Item_hex_string->val_int() is not
 | |
|       the same as (Item_hex_string->val_str() in BINARY column)->val_int().
 | |
|       We cannot simply disable the replacement in a particular context (
 | |
|       e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since
 | |
|       Items don't know the context they are in and there are functions like
 | |
|       IF (<hex_string>, 'yes', 'no').
 | |
|     */
 | |
|     return ctx.compare_type_handler() == item_equal->compare_type_handler();
 | |
|   case IDENTITY_SUBST:
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::cmp_is_done_using_type_handler_of_this(const Item_bool_func *cond,
 | |
|                                                    const Item *item) const
 | |
| {
 | |
|   /*
 | |
|     We could eventually take comparison_type_handler() from cond,
 | |
|     instead of calculating it again. But only some descendants of
 | |
|     Item_bool_func has this method. So this needs some hierarchy changes.
 | |
|     Another option is to pass "class Context" to this method.
 | |
|   */
 | |
|   Type_handler_hybrid_field_type cmp(type_handler_for_comparison());
 | |
|   return !cmp.aggregate_for_comparison(item->type_handler_for_comparison()) &&
 | |
|          cmp.type_handler() == type_handler_for_comparison();
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   This handles all numeric and BIT data types.
 | |
| */ 
 | |
| Data_type_compatibility
 | |
| Field::can_optimize_keypart_ref(const Item_bool_func *cond,
 | |
|                                 const Item *item) const
 | |
| {
 | |
|   DBUG_ASSERT(cmp_type() != STRING_RESULT);
 | |
|   DBUG_ASSERT(cmp_type() != TIME_RESULT);
 | |
|   return item->cmp_type() != TIME_RESULT ?
 | |
|          Data_type_compatibility::OK :
 | |
|          Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   This handles all numeric and BIT data types.
 | |
| */ 
 | |
| Data_type_compatibility
 | |
| Field::can_optimize_group_min_max(const Item_bool_func *cond,
 | |
|                                   const Item *const_item) const
 | |
| {
 | |
|   DBUG_ASSERT(cmp_type() != STRING_RESULT);
 | |
|   DBUG_ASSERT(cmp_type() != TIME_RESULT);
 | |
|   return const_item->cmp_type() != TIME_RESULT ?
 | |
|          Data_type_compatibility::OK :
 | |
|          Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   This covers all numeric types, BIT
 | |
| */
 | |
| Data_type_compatibility
 | |
| Field::can_optimize_range(const Item_bool_func *cond,
 | |
|                           const Item *item,
 | |
|                           bool is_eq_func) const
 | |
| {
 | |
|   DBUG_ASSERT(cmp_type() != TIME_RESULT);   // Handled in Field_temporal
 | |
|   DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants
 | |
|   return item->cmp_type() != TIME_RESULT ?
 | |
|          Data_type_compatibility::OK :
 | |
|          Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field::store_hex_hybrid(const char *str, size_t length)
 | |
| {
 | |
|   DBUG_ASSERT(result_type() != STRING_RESULT);
 | |
|   ulonglong nr;
 | |
| 
 | |
|   if (length > 8)
 | |
|   {
 | |
|     nr= flags & UNSIGNED_FLAG ? ULONGLONG_MAX : LONGLONG_MAX;
 | |
|     goto warn;
 | |
|   }
 | |
|   nr= (ulonglong) longlong_from_hex_hybrid(str, length);
 | |
|   if ((length == 8) && cmp_type()== INT_RESULT &&
 | |
|       !(flags & UNSIGNED_FLAG) && (nr > LONGLONG_MAX))
 | |
|   {
 | |
|     nr= LONGLONG_MAX;
 | |
|     goto warn;
 | |
|   }
 | |
|   return store((longlong) nr, true);  // Assume hex numbers are unsigned
 | |
| 
 | |
| warn:
 | |
|   if (!store((longlong) nr, true))
 | |
|     set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   If a field does not have a corresponding data, it's behavior can vary:
 | |
|   - In case of the fixed file format
 | |
|     it's set to the default value for the data type,
 | |
|     such as 0 for numbers or '' for strings.
 | |
|   - In case of a non-fixed format
 | |
|     it's set to NULL for nullable fields, and
 | |
|     it's set to the default value for the data type for NOT NULL fields.
 | |
|   This seems to be by design.
 | |
| */
 | |
| bool Field::load_data_set_no_data(THD *thd, bool fixed_format)
 | |
| {
 | |
|   reset();                  // Do not use the DEFAULT value
 | |
|   if (fixed_format)
 | |
|   {
 | |
|     set_notnull();
 | |
|     /*
 | |
|       We're loading a fixed format file, e.g.:
 | |
|         LOAD DATA INFILE 't1.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
 | |
|       Suppose the file ended unexpectedly and no data was provided for an
 | |
|       auto-increment column in the current row.
 | |
|       Historically, if sql_mode=NO_AUTO_VALUE_ON_ZERO, then the column value
 | |
|       is set to 0 in such case (the next auto_increment value is not used).
 | |
|       This behaviour was introduced by the fix for "bug#12053" in mysql-4.1.
 | |
|       Note, loading a delimited file works differently:
 | |
|       "no data" is not converted to 0 on NO_AUTO_VALUE_ON_ZERO:
 | |
|       it's considered as equal to setting the column to NULL,
 | |
|       which is then replaced to the next auto_increment value.
 | |
|       This difference seems to be intentional.
 | |
|     */
 | |
|     if (this == table->next_number_field)
 | |
|       table->auto_increment_field_not_null= true;
 | |
|   }
 | |
|   set_has_explicit_value(); // Do not auto-update this field
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::load_data_set_null(THD *thd)
 | |
| {
 | |
|   reset();
 | |
|   set_null();
 | |
|   if (!maybe_null())
 | |
|   {
 | |
|     if (this != table->next_number_field)
 | |
|       set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_NULL_TO_NOTNULL, 1);
 | |
|   }
 | |
|   set_has_explicit_value(); // Do not auto-update this field
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field::load_data_set_value(const char *pos, uint length,
 | |
|                                 CHARSET_INFO *cs)
 | |
| {
 | |
|   /*
 | |
|     Mark field as not null, we should do this for each row because of
 | |
|     restore_record...
 | |
|   */
 | |
|   set_notnull();
 | |
|   if (this == table->next_number_field)
 | |
|     table->auto_increment_field_not_null= true;
 | |
|   store(pos, length, cs);
 | |
|   set_has_explicit_value(); // Do not auto-update this field
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::sp_prepare_and_store_item(THD *thd, Item **value)
 | |
| {
 | |
|   DBUG_ENTER("Field::sp_prepare_and_store_item");
 | |
|   DBUG_ASSERT(value);
 | |
| 
 | |
|   Item *expr_item;
 | |
| 
 | |
|   if (!(expr_item= thd->sp_fix_func_item_for_assignment(this, value)))
 | |
|     goto error;
 | |
| 
 | |
|   if (expr_item->check_is_evaluable_expression_or_error())
 | |
|     goto error;
 | |
| 
 | |
|   /* Save the value in the field. Convert the value if needed. */
 | |
| 
 | |
|   expr_item->save_in_field(this, 0);
 | |
| 
 | |
|   if (likely(!thd->is_error()))
 | |
|     DBUG_RETURN(false);
 | |
| 
 | |
| error:
 | |
|   /*
 | |
|     In case of error during evaluation, leave the result field set to NULL.
 | |
|     Sic: we can't do it in the beginning of the function because the 
 | |
|     result field might be needed for its own re-evaluation, e.g. case of 
 | |
|     set x = x + 1;
 | |
|   */
 | |
|   set_null();
 | |
|   DBUG_ASSERT(thd->is_error());
 | |
|   DBUG_RETURN(true);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field::error_generated_column_function_is_not_allowed(THD *thd,
 | |
|                                                            bool error) const
 | |
| {
 | |
|   StringBuffer<64> tmp;
 | |
|   vcol_info->expr->print(&tmp, (enum_query_type)
 | |
|                                (QT_TO_SYSTEM_CHARSET |
 | |
|                                 QT_ITEM_IDENT_SKIP_DB_NAMES |
 | |
|                                 QT_ITEM_IDENT_SKIP_TABLE_NAMES));
 | |
|   my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED,
 | |
|            MYF(error ? 0 : ME_WARNING),
 | |
|            tmp.c_ptr_safe(), vcol_info->get_vcol_type_name(),
 | |
|            const_cast<const char*>(field_name.str));
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Check if an indexed or a persistent virtual column depends on sql_mode flags
 | |
|   that it cannot handle.
 | |
|   See sql_mode.h for details.
 | |
| */
 | |
| bool Field::check_vcol_sql_mode_dependency(THD *thd, vcol_init_mode mode) const
 | |
| {
 | |
|   DBUG_ASSERT(vcol_info);
 | |
|   if ((flags & PART_KEY_FLAG) != 0 || stored_in_db())
 | |
|   {
 | |
|     Sql_mode_dependency valdep= vcol_info->expr->value_depends_on_sql_mode();
 | |
|     sql_mode_t cnvdep= conversion_depends_on_sql_mode(thd, vcol_info->expr);
 | |
|     Sql_mode_dependency dep=
 | |
|         (valdep | Sql_mode_dependency(0, cnvdep)) &
 | |
|         Sql_mode_dependency(~0, ~can_handle_sql_mode_dependency_on_store());
 | |
|     if (dep)
 | |
|     {
 | |
|       bool error= (mode & VCOL_INIT_DEPENDENCY_FAILURE_IS_ERROR) != 0;
 | |
|       error_generated_column_function_is_not_allowed(thd, error);
 | |
|       dep.push_dependency_warnings(thd);
 | |
|       return error;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::make_empty_rec_store_default_value(THD *thd, Item *item)
 | |
| {
 | |
|   DBUG_ASSERT(!(flags & BLOB_FLAG));
 | |
|   int res= item->save_in_field(this, true);
 | |
|   return res != 0 && res != 3;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Numeric fields base class constructor.
 | |
| */
 | |
| Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
 | |
|                      uchar null_bit_arg, utype unireg_check_arg,
 | |
|                      const LEX_CSTRING *field_name_arg,
 | |
|                      decimal_digits_t dec_arg, bool zero_arg, bool unsigned_arg)
 | |
|   :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
 | |
|          unireg_check_arg, field_name_arg),
 | |
|   dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg)
 | |
| {
 | |
|   if (zerofill)
 | |
|     flags|=ZEROFILL_FLAG;
 | |
|   if (unsigned_flag)
 | |
|     flags|=UNSIGNED_FLAG;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_num::prepend_zeros(String *value) const
 | |
| {
 | |
|   int diff;
 | |
|   if ((diff= (int) (field_length - value->length())) > 0)
 | |
|   {
 | |
|     const bool error= value->realloc(field_length);
 | |
|     if (likely(!error))
 | |
|     {
 | |
|       bmove_upp((uchar*) value->ptr()+field_length,
 | |
|                 (uchar*) value->ptr()+value->length(),
 | |
| 	        value->length());
 | |
|       bfill((uchar*) value->ptr(),diff,'0');
 | |
|       value->length(field_length);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| sql_mode_t Field_num::can_handle_sql_mode_dependency_on_store() const
 | |
| {
 | |
|   return MODE_PAD_CHAR_TO_FULL_LENGTH;
 | |
| }
 | |
| 
 | |
| 
 | |
| Item *Field_num::get_equal_zerofill_const_item(THD *thd, const Context &ctx,
 | |
|                                                Item *const_item)
 | |
| {
 | |
|   switch (ctx.subst_constraint()) {
 | |
|   case IDENTITY_SUBST:
 | |
|     return NULL; // Not safe to propagate if not in comparison. See MDEV-8369.
 | |
|   case ANY_SUBST:
 | |
|     break;
 | |
|   }
 | |
|   DBUG_ASSERT(const_item->const_item());
 | |
|   DBUG_ASSERT(ctx.compare_type_handler()->cmp_type() != STRING_RESULT);
 | |
|   return const_item;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
| Construct warning parameters using thd->no_errors
 | |
|   to determine whether to generate or suppress warnings.
 | |
|   We can get here in a query like this:
 | |
|     SELECT COUNT(@@basedir);
 | |
|   from Item_func_get_system_var::update_null_value().
 | |
| */
 | |
| Value_source::Warn_filter::Warn_filter(const THD *thd)
 | |
|  :m_want_warning_edom(!thd->no_errors),
 | |
|   m_want_note_truncated_spaces(!thd->no_errors)
 | |
| { }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check string-to-number conversion and produce a warning if
 | |
|   - could not convert any digits (EDOM-alike error)
 | |
|   - found garbage at the end of the string
 | |
|   - found trailing spaces (a note)
 | |
|   See also Field_num::check_edom_and_truncation() for a similar function.
 | |
| 
 | |
|   @param thd        - the thread
 | |
|   @param filter     - which warnings/notes are allowed
 | |
|   @param type       - name of the data type (e.g. "INTEGER", "DECIMAL", "DOUBLE")
 | |
|   @param cs         - character set of the original string
 | |
|   @param str        - the original string
 | |
|   @param end        - the end of the string
 | |
| 
 | |
|   Unlike Field_num::check_edom_and_truncation(), this function does not
 | |
|   distinguish between EDOM and truncation and reports the same warning for
 | |
|   both cases. Perhaps we should eventually print different warnings, to make
 | |
|   the explicit CAST work closer to the implicit cast in Field_xxx::store().
 | |
| */
 | |
| void
 | |
| Value_source::Converter_string_to_number::check_edom_and_truncation(THD *thd,
 | |
|                                                        Warn_filter filter,
 | |
|                                                        const char *type,
 | |
|                                                        CHARSET_INFO *cs,
 | |
|                                                        const char *str,
 | |
|                                                        size_t length) const
 | |
| {
 | |
|   DBUG_ASSERT(str <= m_end_of_num);
 | |
|   DBUG_ASSERT(m_end_of_num <= str + length);
 | |
|   if (m_edom || (m_end_of_num < str + length &&
 | |
|                  !check_if_only_end_space(cs, m_end_of_num, str + length)))
 | |
|   {
 | |
|     // EDOM or important trailing data truncation
 | |
|     if (filter.want_warning_edom())
 | |
|     {
 | |
|       /*
 | |
|         We can use err.ptr() here as ErrConvString is guaranteed to put an
 | |
|         end \0 here.
 | |
|       */
 | |
|       THD *wthd= thd ? thd : current_thd;
 | |
|       push_warning_printf(wthd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                           ER_TRUNCATED_WRONG_VALUE,
 | |
|                           ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type,
 | |
|                           ErrConvString(str, length, cs).ptr());
 | |
|     }
 | |
|   }
 | |
|   else if (m_end_of_num < str + length)
 | |
|   {
 | |
|     // Unimportant trailing data (spaces) truncation
 | |
|     if (filter.want_note_truncated_spaces())
 | |
|     {
 | |
|       THD *wthd= thd ? thd : current_thd;
 | |
|       push_warning_printf(wthd, Sql_condition::WARN_LEVEL_NOTE,
 | |
|                           ER_TRUNCATED_WRONG_VALUE,
 | |
|                           ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type,
 | |
|                           ErrConvString(str, length, cs).ptr());
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check a string-to-number conversion routine result and generate warnings
 | |
|   in case when it:
 | |
|   - could not convert any digits
 | |
|   - found garbage at the end of the string.
 | |
| 
 | |
|   @param type          Data type name (e.g. "decimal", "integer", "double")
 | |
|   @param edom          Indicates that the string-to-number routine returned
 | |
|                        an error code equivalent to EDOM (value out of domain),
 | |
|                        i.e. the string fully consisted of garbage and the
 | |
|                        conversion routine could not get any digits from it.
 | |
|   @param str           The original string
 | |
|   @param length        Length of 'str'
 | |
|   @param cs            Character set
 | |
|   @param end           Pointer to char after last used digit
 | |
| 
 | |
|   @note
 | |
|     This is called after one has called one of the following functions:
 | |
|     - strntoull10rnd()
 | |
|     - strntod()
 | |
|     - str2my_decimal()
 | |
| 
 | |
|   @retval
 | |
|     0        OK
 | |
|   @retval
 | |
|     1        error: could not scan any digits (EDOM),
 | |
|              e.g. empty string, or garbage.
 | |
|   @retval
 | |
|     2        error: scanned some digits,
 | |
|              but then found garbage at the end of the string.
 | |
| */
 | |
| 
 | |
| 
 | |
| int Field_num::check_edom_and_important_data_truncation(const char *type,
 | |
|                                                         bool edom,
 | |
|                                                         CHARSET_INFO *cs,
 | |
|                                                         const char *str, size_t length,
 | |
|                                                         const char *end)
 | |
| {
 | |
|   /* Test if we get an empty string or garbage */
 | |
|   if (edom)
 | |
|   {
 | |
|     ErrConvString err(str, length, cs);
 | |
|     set_warning_truncated_wrong_value(type, err.ptr());
 | |
|     return 1;
 | |
|   }
 | |
|   /* Test if we have garbage at the end of the given string. */
 | |
|   if (test_if_important_data(cs, end, str + length))
 | |
|   {
 | |
|     set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|     return 2;
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_num::check_edom_and_truncation(const char *type, bool edom,
 | |
|                                          CHARSET_INFO *cs,
 | |
|                                          const char *str, size_t length,
 | |
|                                          const char *end)
 | |
| {
 | |
|   int rc= check_edom_and_important_data_truncation(type, edom,
 | |
|                                                    cs, str, length, end);
 | |
|   if (!rc && end < str + length)
 | |
|     set_note(WARN_DATA_TRUNCATED, 1);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert a string to an integer then check bounds.
 | |
|   
 | |
|   SYNOPSIS
 | |
|     Field_num::get_int
 | |
|     cs            Character set
 | |
|     from          String to convert
 | |
|     len           Length of the string
 | |
|     rnd           OUT longlong value
 | |
|     unsigned_max  max unsigned value
 | |
|     signed_min    min signed value
 | |
|     signed_max    max signed value
 | |
| 
 | |
|   DESCRIPTION
 | |
|     The function calls strntoull10rnd() to get an integer value then
 | |
|     check bounds and errors returned. In case of any error a warning
 | |
|     is raised.
 | |
| 
 | |
|   RETURN
 | |
|     0   ok
 | |
|     1   error
 | |
| */
 | |
| 
 | |
| bool Field_num::get_int(CHARSET_INFO *cs, const char *from, size_t len,
 | |
|                         longlong *rnd, ulonglong unsigned_max, 
 | |
|                         longlong signed_min, longlong signed_max)
 | |
| {
 | |
|   char *end;
 | |
|   int error;
 | |
|   
 | |
|   *rnd= (longlong) cs->strntoull10rnd(from, len,
 | |
|                                       unsigned_flag, &end,
 | |
|                                       &error);
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
| 
 | |
|     if ((((ulonglong) *rnd > unsigned_max) &&
 | |
|          (*rnd= (longlong) unsigned_max)) ||
 | |
|         error == MY_ERRNO_ERANGE)
 | |
|     {
 | |
|       goto out_of_range;
 | |
|     }
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (*rnd < signed_min)
 | |
|     {
 | |
|       *rnd= signed_min;
 | |
|       goto out_of_range;
 | |
|     }
 | |
|     else if (*rnd > signed_max)
 | |
|     {
 | |
|       *rnd= signed_max;
 | |
|       goto out_of_range;
 | |
|     }
 | |
|   }
 | |
|   if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
 | |
|       check_int(cs, from, len, end, error))
 | |
|     return 1;
 | |
| 
 | |
|   return error && get_thd()->count_cuted_fields == CHECK_FIELD_EXPRESSION;
 | |
| 
 | |
| out_of_range:
 | |
|   set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_real::get_double(const char *str, size_t length, CHARSET_INFO *cs,
 | |
|                               int *error)
 | |
| {
 | |
|   char *end;
 | |
|   double nr= cs->strntod((char*) str, length, &end, error);
 | |
|   if (unlikely(*error))
 | |
|   {
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     *error= 1;
 | |
|   }
 | |
|   else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
 | |
|            check_edom_and_truncation("double", str == end,
 | |
|                                      cs, str, length, end))
 | |
|     *error= 1;
 | |
|   return nr;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Process decimal library return codes and issue warnings for overflow and
 | |
|   truncation.
 | |
| 
 | |
|   @param op_result  decimal library return code (E_DEC_* see include/decimal.h)
 | |
| 
 | |
|   @retval
 | |
|     1  there was overflow
 | |
|   @retval
 | |
|     0  no error or some other errors except overflow
 | |
| */
 | |
| 
 | |
| int Field::warn_if_overflow(int op_result)
 | |
| {
 | |
|   if (op_result == E_DEC_OVERFLOW)
 | |
|   {
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     return 1;
 | |
|   }
 | |
|   if (op_result == E_DEC_TRUNCATED)
 | |
|   {
 | |
|     set_note(WARN_DATA_TRUNCATED, 1);
 | |
|     /* We return 0 here as this is not a critical issue */
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Interpret field value as an integer but return the result as a string.
 | |
| 
 | |
|   This is used for printing bit_fields as numbers while debugging.
 | |
| */
 | |
| 
 | |
| String *Field::val_int_as_str(String *val_buffer, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   CHARSET_INFO *cs= &my_charset_bin;
 | |
|   uint length;
 | |
|   longlong value= val_int();
 | |
| 
 | |
|   if (val_buffer->alloc(MY_INT64_NUM_DECIMAL_DIGITS))
 | |
|     return 0;
 | |
|   length= (uint) (cs->longlong10_to_str)((char*) val_buffer->ptr(),
 | |
|                                          MY_INT64_NUM_DECIMAL_DIGITS,
 | |
|                                          unsigned_val ? 10 : -10,
 | |
|                                          value);
 | |
|   val_buffer->length(length);
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| /// This is used as a table name when the table structure is not set up
 | |
| Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
 | |
| 	     uchar null_bit_arg,
 | |
| 	     utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
 | |
|   :ptr(ptr_arg),
 | |
|   null_ptr(null_ptr_arg), table(0), orig_table(0),
 | |
|   table_name(0), field_name(*field_name_arg), option_list(0),
 | |
|   option_struct(0), key_start(0), part_of_key(0),
 | |
|   part_of_key_not_clustered(0), part_of_sortkey(0),
 | |
|   unireg_check(unireg_check_arg), invisible(VISIBLE), field_length(length_arg),
 | |
|   null_bit(null_bit_arg), is_created_from_null_item(FALSE),
 | |
|   read_stats(NULL), collected_stats(0), vcol_info(0), check_constraint(0),
 | |
|   default_value(0)
 | |
| {
 | |
|   flags=null_ptr ? 0: NOT_NULL_FLAG;
 | |
|   comment.str= (char*) "";
 | |
|   comment.length=0;
 | |
|   field_index= 0;
 | |
|   cond_selectivity= 1.0;
 | |
|   next_equal_field= NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field::hash_not_null(Hasher *hasher)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   DBUG_ASSERT(!is_null());
 | |
|   hasher->add(sort_charset(), ptr, pack_length());
 | |
| }
 | |
| 
 | |
| size_t
 | |
| Field::do_last_null_byte() const
 | |
| {
 | |
|   DBUG_ASSERT(null_ptr == NULL || null_ptr >= table->record[0]);
 | |
|   if (null_ptr)
 | |
|     return (size_t) (null_ptr - table->record[0]) + 1;
 | |
|   return LAST_NULL_BYTE_UNDEF;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field::copy_from_tmp(int row_offset)
 | |
| {
 | |
|   memcpy(ptr,ptr+row_offset,pack_length());
 | |
|   if (null_ptr)
 | |
|   {
 | |
|     *null_ptr= (uchar) ((null_ptr[0] & (uchar) ~(uint) null_bit) |
 | |
| 			(null_ptr[row_offset] & (uchar) null_bit));
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::send(Protocol *protocol)
 | |
| {
 | |
|   StringBuffer<MAX_FIELD_WIDTH> tmp(charset());
 | |
|   val_str(&tmp);
 | |
|   return protocol->store(tmp.ptr(), tmp.length(), tmp.charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_num::send_numeric_zerofill_str(Protocol_text *protocol,
 | |
|                                           protocol_send_type_t send_type)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   StringBuffer<MAX_FIELD_WIDTH> tmp(&my_charset_latin1);
 | |
|   val_str(&tmp);
 | |
|   return protocol->store_numeric_zerofill_str(tmp.ptr(),
 | |
|                                               tmp.length(),
 | |
|                                               send_type);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Check to see if field size is compatible with destination.
 | |
| 
 | |
|    This method is used in row-based replication to verify that the
 | |
|    slave's field size is less than or equal to the master's field
 | |
|    size. The encoded field metadata (from the master or source) is
 | |
|    decoded and compared to the size of this field (the slave or
 | |
|    destination).
 | |
| 
 | |
|    @note
 | |
| 
 | |
|    The comparison is made so that if the source data (from the master)
 | |
|    is less than the target data (on the slave), -1 is returned in @c
 | |
|    <code>*order_var</code>. This implies that a conversion is
 | |
|    necessary, but that it is lossy and can result in truncation of the
 | |
|    value.
 | |
| 
 | |
|    If the source data is strictly greater than the target data, 1 is
 | |
|    returned in <code>*order_var</code>. This implies that the source
 | |
|    type can is contained in the target type and that a conversion is
 | |
|    necessary but is non-lossy.
 | |
| 
 | |
|    If no conversion is required to fit the source type in the target
 | |
|    type, 0 is returned in <code>*order_var</code>.
 | |
| 
 | |
|    @param   field_metadata   Encoded size in field metadata
 | |
|    @param   mflags           Flags from the table map event for the table.
 | |
|    @param   order_var        Pointer to variable where the order
 | |
|                              between the source field and this field
 | |
|                              will be returned.
 | |
| 
 | |
|    @return @c true if this field's size is compatible with the
 | |
|    master's field size, @c false otherwise.
 | |
| */
 | |
| bool Field::compatible_field_size(uint field_metadata,
 | |
|                                   const Relay_log_info *rli_arg __attribute__((unused)),
 | |
|                                   uint16 mflags __attribute__((unused)),
 | |
|                                   int *order_var) const
 | |
| {
 | |
|   uint const source_size= pack_length_from_metadata(field_metadata);
 | |
|   uint const destination_size= row_pack_length();
 | |
|   DBUG_PRINT("debug", ("real_type: %d, source_size: %u, destination_size: %u",
 | |
|                        real_type(), source_size, destination_size));
 | |
|   *order_var = compare(source_size, destination_size);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field::store(const char *to, size_t length, CHARSET_INFO *cs,
 | |
|                  enum_check_fields check_level)
 | |
| {
 | |
|   Check_level_instant_set tmp_level(get_thd(), check_level);
 | |
|   return store(to, length, cs);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field::store_text(const char *to, size_t length, CHARSET_INFO *cs,
 | |
|                       enum_check_fields check_level)
 | |
| {
 | |
|   Check_level_instant_set tmp_level(get_thd(), check_level);
 | |
|   return store_text(to, length, cs);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field::store_timestamp_dec(const timeval &ts, uint dec)
 | |
| {
 | |
|   return store_time_dec(Datetime(get_thd(), ts).get_mysql_time(), dec);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field::store_to_statistical_minmax_field(Field *field, String *val)
 | |
| {
 | |
|   val_str(val);
 | |
|   size_t length= Well_formed_prefix(val->charset(), val->ptr(),
 | |
|                  MY_MIN(val->length(), field->field_length)).length();
 | |
|   return field->store(val->ptr(), length, &my_charset_bin);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field::store_from_statistical_minmax_field(Field *stat_field, String *str,
 | |
|                                                MEM_ROOT *mem)
 | |
| {
 | |
|   stat_field->val_str(str);
 | |
|   return store_text(str->ptr(), str->length(), &my_charset_bin);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Same as above, but store the string in the statistics mem_root to make it
 | |
|   easy to free everything by just freeing the mem_root.
 | |
| */
 | |
| 
 | |
| int Field_blob::store_from_statistical_minmax_field(Field *stat_field,
 | |
|                                                     String *str,
 | |
|                                                     MEM_ROOT *mem)
 | |
| {
 | |
|   String *tmp= stat_field->val_str(str);
 | |
|   uchar *ptr;
 | |
|   if (!(ptr= (uchar*) memdup_root(mem, tmp->ptr(), tmp->length())))
 | |
|   {
 | |
|     set_ptr((uint32) 0, NULL);
 | |
|     return 1;
 | |
|   }
 | |
|   set_ptr(tmp->length(), ptr);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Pack the field into a format suitable for storage and transfer.
 | |
| 
 | |
|    To implement packing functionality, only the virtual function
 | |
|    should be overridden. The other functions are just convenience
 | |
|    functions and hence should not be overridden.
 | |
| 
 | |
|    @note The default method for packing fields just copy the raw bytes
 | |
|    of the record into the destination, but never more than
 | |
|    <code>max_length</code> characters.
 | |
| 
 | |
|    @param to
 | |
|    Pointer to memory area where representation of field should be put.
 | |
| 
 | |
|    @param from
 | |
|    Pointer to memory area where record representation of field is
 | |
|    stored.
 | |
| 
 | |
|    @param max_length
 | |
|    Maximum length of the field, as given in the column definition. For
 | |
|    example, for <code>CHAR(1000)</code>, the <code>max_length</code>
 | |
|    is 1000. This information is sometimes needed to decide how to pack
 | |
|    the data.
 | |
| 
 | |
| */
 | |
| uchar *
 | |
| Field::pack(uchar *to, const uchar *from, uint max_length)
 | |
| {
 | |
|   uint32 length= pack_length();
 | |
|   set_if_smaller(length, max_length);
 | |
|   memcpy(to, from, length);
 | |
|   return to+length;
 | |
| }
 | |
| 
 | |
| /**
 | |
|    Unpack a field from row data.
 | |
| 
 | |
|    This method is used to unpack a field from a master whose size of
 | |
|    the field is less than that of the slave.
 | |
| 
 | |
|    The <code>param_data</code> parameter is a two-byte integer (stored
 | |
|    in the least significant 16 bits of the unsigned integer) usually
 | |
|    consisting of two parts: the real type in the most significant byte
 | |
|    and a original pack length in the least significant byte.
 | |
| 
 | |
|    The exact layout of the <code>param_data</code> field is given by
 | |
|    the <code>Table_map_log_event::save_field_metadata()</code>.
 | |
| 
 | |
|    This is the default method for unpacking a field. It just copies
 | |
|    the memory block in byte order (of original pack length bytes or
 | |
|    length of field, whichever is smaller).
 | |
| 
 | |
|    @param   to         Destination of the data
 | |
|    @param   from       Source of the data
 | |
|    @param   param_data Real type and original pack length of the field
 | |
|                        data
 | |
| 
 | |
|    @return  New pointer into memory based on from + length of the data
 | |
|    @return  0 if wrong data
 | |
| */
 | |
| const uchar *
 | |
| Field::unpack(uchar* to, const uchar *from, const uchar *from_end,
 | |
|               uint param_data)
 | |
| {
 | |
|   uint length=pack_length(), len;
 | |
|   int from_type= 0;
 | |
|   /*
 | |
|     If from length is > 255, it has encoded data in the upper bits. Need
 | |
|     to mask it out.
 | |
|   */
 | |
|   if (param_data > 255)
 | |
|   {
 | |
|     from_type= (param_data & 0xff00) >> 8U;  // real_type.
 | |
|     param_data= param_data & 0x00ff;        // length.
 | |
|   }
 | |
| 
 | |
|   if ((param_data == 0) ||
 | |
|       (length == param_data) ||
 | |
|       (from_type != real_type()))
 | |
|   {
 | |
|     if (from + length > from_end)
 | |
|       return 0;                                 // Error in data
 | |
| 
 | |
|     memcpy(to, from, length);
 | |
|     return from+length;
 | |
|   }
 | |
| 
 | |
|   len= (param_data && (param_data < length)) ? param_data : length;
 | |
| 
 | |
|   if (from + len > from_end)
 | |
|     return 0;                                   // Error in data
 | |
| 
 | |
|   memcpy(to, from, len);
 | |
|   return from+len;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_num::add_zerofill_and_unsigned(String &res) const
 | |
| {
 | |
|   if (unsigned_flag)
 | |
|     res.append(STRING_WITH_LEN(" unsigned"));
 | |
|   if (zerofill)
 | |
|     res.append(STRING_WITH_LEN(" zerofill"));
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field::make_send_field(Send_field *field)
 | |
| {
 | |
|   if (orig_table && orig_table->s->db.str && *orig_table->s->db.str)
 | |
|   {
 | |
|     field->db_name= orig_table->s->db;
 | |
|     if (orig_table->pos_in_table_list &&
 | |
|         orig_table->pos_in_table_list->schema_table)
 | |
|       field->org_table_name= Lex_cstring_strlen(orig_table->pos_in_table_list->
 | |
|                                                 schema_table->table_name);
 | |
|     else
 | |
|       field->org_table_name= orig_table->s->table_name;
 | |
|   }
 | |
|   else
 | |
|     field->org_table_name= field->db_name= empty_clex_str;
 | |
|   if (orig_table && orig_table->alias.ptr())
 | |
|   {
 | |
|     orig_table->alias.get_value(&field->table_name);
 | |
|     field->org_col_name= field_name;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     field->table_name= empty_clex_str;
 | |
|     field->org_col_name= empty_clex_str;
 | |
|   }
 | |
|   field->col_name= field_name;
 | |
|   field->length=field_length;
 | |
|   field->set_handler(type_handler());
 | |
|   field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
 | |
|   field->decimals= 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Conversion from decimal to longlong with checking overflow and
 | |
|   setting correct value (min/max) in case of overflow.
 | |
| 
 | |
|   @param val             value which have to be converted
 | |
|   @param unsigned_flag   type of integer in which we convert val
 | |
|   @param err             variable to pass error code
 | |
| 
 | |
|   @return
 | |
|     value converted from val
 | |
| */
 | |
| longlong Field::convert_decimal2longlong(const my_decimal *val,
 | |
|                                          bool unsigned_flag, int *err)
 | |
| {
 | |
|   longlong i;
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (val->sign())
 | |
|     {
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       i= 0;
 | |
|       *err= 1;
 | |
|     }
 | |
|     else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR &
 | |
|                                               ~E_DEC_OVERFLOW &
 | |
|                                               ~E_DEC_TRUNCATED),
 | |
|                                              val, TRUE, &i)))
 | |
|     {
 | |
|       i= ~(longlong) 0;
 | |
|       *err= 1;
 | |
|     }
 | |
|   }
 | |
|   else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR &
 | |
|                                             ~E_DEC_OVERFLOW &
 | |
|                                             ~E_DEC_TRUNCATED),
 | |
|                                            val, FALSE, &i)))
 | |
|   {
 | |
|     i= (val->sign() ? LONGLONG_MIN : LONGLONG_MAX);
 | |
|     *err= 1;
 | |
|   }
 | |
|   return i;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Storing decimal in integer fields.
 | |
| 
 | |
|   @param val       value for storing
 | |
| 
 | |
|   @note
 | |
|     This method is used by all integer fields, real/decimal redefine it
 | |
| 
 | |
|   @retval
 | |
|     0     OK
 | |
|   @retval
 | |
|     !=0  error
 | |
| */
 | |
| 
 | |
| int Field_int::store_decimal(const my_decimal *val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int err= 0;
 | |
|   longlong i= convert_decimal2longlong(val, unsigned_flag, &err);
 | |
|   return MY_TEST(err | store(i, unsigned_flag));
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Return decimal value of integer field.
 | |
| 
 | |
|   @param decimal_value     buffer for storing decimal value
 | |
| 
 | |
|   @note
 | |
|     This method is used by all integer fields, real/decimal redefine it.
 | |
|     All longlong values fit in our decimal buffer which cal store 8*9=72
 | |
|     digits of integer number
 | |
| 
 | |
|   @return
 | |
|     pointer to decimal buffer with value of field
 | |
| */
 | |
| 
 | |
| my_decimal* Field_int::val_decimal(my_decimal *decimal_value)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   longlong nr= val_int();
 | |
|   int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value);
 | |
|   return decimal_value;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_int::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   Longlong_hybrid nr(val_int(), (flags & UNSIGNED_FLAG));
 | |
|   return int_to_datetime_with_warn(get_thd(), nr, ltime,
 | |
|                                    fuzzydate, table->s, field_name.str);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   DBUG_ASSERT(ltime);
 | |
|   if (!table || !table->s)
 | |
|     return true;
 | |
|   DBUG_ASSERT(table->versioned(VERS_TRX_ID) ||
 | |
|     (table->versioned() && table->s->table_category == TABLE_CATEGORY_TEMPORARY));
 | |
|   if (!trx_id)
 | |
|     return true;
 | |
| 
 | |
|   THD *thd= get_thd();
 | |
|   DBUG_ASSERT(thd);
 | |
|   if (trx_id == ULONGLONG_MAX)
 | |
|   {
 | |
|     thd->variables.time_zone->gmt_sec_to_TIME(ltime, TIMESTAMP_MAX_VALUE);
 | |
|     ltime->second_part= TIME_MAX_SECOND_PART;
 | |
|     return false;
 | |
|   }
 | |
|   if (cached == trx_id)
 | |
|   {
 | |
|     *ltime= cache;
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   TR_table trt(thd);
 | |
|   bool found= trt.query(trx_id);
 | |
|   if (found)
 | |
|   {
 | |
|     trt[TR_table::FLD_COMMIT_TS]->get_date(&cache, fuzzydate);
 | |
|     *ltime= cache;
 | |
|     cached= trx_id;
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                       ER_VERS_NO_TRX_ID, ER_THD(thd, ER_VERS_NO_TRX_ID),
 | |
|                       (longlong) trx_id);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
 | |
|                      uchar null_bit_arg, utype unireg_check_arg,
 | |
|                      const LEX_CSTRING *field_name_arg,
 | |
|                      const DTCollation &collation)
 | |
|   :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
 | |
|          unireg_check_arg, field_name_arg)
 | |
| {
 | |
|   m_collation= collation;
 | |
|   if (collation.collation->state & MY_CS_BINSORT)
 | |
|     flags|=BINARY_FLAG;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const
 | |
| {
 | |
|   /*
 | |
|     Can't guarantee uniqueness when comparing a CHAR/VARCHAR/TEXT,
 | |
|     BINARY/VARBINARY/BLOB, ENUM,SET columns to an item with cmp_type()
 | |
|     of INT_RESULT, DOUBLE_RESULT, DECIMAL_RESULT or TIME_RESULT.
 | |
|     Example:
 | |
|       SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01'
 | |
|     return non-unuque values, e.g. '2001-01-01' and '2001-01-01x'.
 | |
|   */
 | |
|   if (!field_charset()->propagate(0, 0) ||
 | |
|       item->cmp_type() != STRING_RESULT)
 | |
|     return false;
 | |
|   /*
 | |
|     Can't guarantee uniqueness when comparing to
 | |
|     an item of a different collation.
 | |
|     Example:
 | |
|       SELECT * FROM t1
 | |
|       WHERE latin1_bin_column = _latin1'A' COLLATE latin1_swedish_ci
 | |
|     return non-unique values 'a' and 'A'.
 | |
|   */
 | |
|   DTCollation tmp(dtcollation());
 | |
|   return !tmp.aggregate(item->collation) && tmp.collation == field_charset();
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_str::can_be_substituted_to_equal_item(const Context &ctx,
 | |
|                                                   const Item_equal *item_equal)
 | |
| {
 | |
|   DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() == STRING_RESULT);
 | |
|   switch (ctx.subst_constraint()) {
 | |
|   case ANY_SUBST:
 | |
|     return ctx.compare_type_handler() == item_equal->compare_type_handler() &&
 | |
|           (ctx.compare_type_handler()->cmp_type() != STRING_RESULT ||
 | |
|            ctx.compare_collation() == item_equal->compare_collation());
 | |
|   case IDENTITY_SUBST:
 | |
|     return ((charset()->state & MY_CS_BINSORT) &&
 | |
|             (charset()->state & MY_CS_NOPAD));
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_str::change_charset(const DTCollation &new_cs)
 | |
| {
 | |
|   if (!has_charset())
 | |
|     return;
 | |
| 
 | |
|   field_length= (field_length * new_cs.collation->mbmaxlen) /
 | |
|     m_collation.collation->mbmaxlen;
 | |
|   m_collation= new_cs;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_num::make_send_field(Send_field *field)
 | |
| {
 | |
|   Field::make_send_field(field);
 | |
|   field->decimals= dec;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Decimal representation of Field_str.
 | |
| 
 | |
|   @param d         value for storing
 | |
| 
 | |
|   @note
 | |
|     Field_str is the base class for fields like Field_enum,
 | |
|     Field_date and some similar. Some dates use fraction and also
 | |
|     string value should be converted to floating point value according
 | |
|     our rules, so we use double to store value of decimal in string.
 | |
| 
 | |
|   @todo
 | |
|     use decimal2string?
 | |
| 
 | |
|   @retval
 | |
|     0     OK
 | |
|   @retval
 | |
|     !=0  error
 | |
| */
 | |
| 
 | |
| int Field_str::store_decimal(const my_decimal *d)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   double val;
 | |
|   /* TODO: use decimal2string? */
 | |
|   int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR &
 | |
|                                             ~E_DEC_OVERFLOW, d, &val));
 | |
|   return err | store(val);
 | |
| }
 | |
| 
 | |
| 
 | |
| my_decimal *Field_str::val_decimal(my_decimal *decimal_value)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   longlong nr= val_int();
 | |
|   int2my_decimal(E_DEC_FATAL_ERROR, nr, 0, decimal_value);
 | |
|   return decimal_value;
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Field::fill_cache_field(CACHE_FIELD *copy)
 | |
| {
 | |
|   uint store_length;
 | |
|   copy->str= ptr;
 | |
|   copy->length= pack_length_in_rec();
 | |
|   copy->field= this;
 | |
|   if (flags & BLOB_FLAG)
 | |
|   {
 | |
|     copy->type= CACHE_BLOB;
 | |
|     copy->length-= portable_sizeof_char_ptr;
 | |
|     return copy->length;
 | |
|   }
 | |
|   else if (!zero_pack() &&
 | |
|            (type() == MYSQL_TYPE_STRING && copy->length >= 4 &&
 | |
|             copy->length < 256))
 | |
|   {
 | |
|     copy->type= CACHE_STRIPPED;			    /* Remove end space */
 | |
|     store_length= 2;
 | |
|   }
 | |
|   else if (type() ==  MYSQL_TYPE_VARCHAR)
 | |
|   {
 | |
|     copy->type= pack_length()-row_pack_length() == 1 ? CACHE_VARSTR1:
 | |
|                                                       CACHE_VARSTR2;
 | |
|     store_length= 0;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     copy->type= 0;
 | |
|     store_length= 0;
 | |
|   }
 | |
|   return copy->length + store_length;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::get_date(MYSQL_TIME *to, date_mode_t mode)
 | |
| {
 | |
|   StringBuffer<40> tmp;
 | |
|   Temporal::Warn_push warn(get_thd(), nullptr, nullptr, nullptr, to, mode);
 | |
|   Temporal_hybrid *t= new(to) Temporal_hybrid(get_thd(), &warn,
 | |
|                                               val_str(&tmp), mode);
 | |
|   return !t->is_valid_temporal();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field::val_datetime_packed(THD *thd)
 | |
| {
 | |
|   MYSQL_TIME ltime, tmp;
 | |
|   if (get_date(<ime, Datetime::Options_cmp(thd)))
 | |
|     return 0;
 | |
|   if (ltime.time_type != MYSQL_TIMESTAMP_TIME)
 | |
|     return pack_time(<ime);
 | |
|   if (time_to_datetime_with_warn(thd, <ime, &tmp, TIME_CONV_NONE))
 | |
|     return 0;
 | |
|   return pack_time(&tmp);
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field::val_time_packed(THD *thd)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   Time::Options_cmp opt(thd);
 | |
|   if (get_date(<ime, opt))
 | |
|     return 0;
 | |
|   if (ltime.time_type == MYSQL_TIMESTAMP_TIME)
 | |
|     return pack_time(<ime);
 | |
|   // Conversion from DATETIME or DATE to TIME is needed
 | |
|   return Time(thd, <ime, opt).to_packed();
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This is called when storing a date in a string.
 | |
| 
 | |
|   @note
 | |
|     Needs to be changed if/when we want to support different time formats.
 | |
| */
 | |
| 
 | |
| int Field::store_time_dec(const MYSQL_TIME *ltime, uint dec)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   char buff[MAX_DATE_STRING_REP_LENGTH];
 | |
|   uint length= (uint) my_TIME_to_str(ltime, buff, dec);
 | |
|   /* Avoid conversion when field character set is ASCII compatible */
 | |
|   return store(buff, length, (charset()->state & MY_CS_NONASCII) ?
 | |
|                               &my_charset_latin1 : charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::optimize_range(uint idx, uint part) const
 | |
| {
 | |
|   return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE);
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                              bool keep_type __attribute__((unused)))
 | |
| {
 | |
|   Field *tmp;
 | |
|   if (!(tmp= (Field*) memdup_root(root,(char*) this,size_of())))
 | |
|     return 0;
 | |
| 
 | |
|   if (tmp->table->maybe_null)
 | |
|     tmp->flags&= ~NOT_NULL_FLAG;
 | |
|   tmp->table= new_table;
 | |
|   tmp->key_start.init(0);
 | |
|   tmp->part_of_key.init(0);
 | |
|   tmp->part_of_sortkey.init(0);
 | |
|   tmp->read_stats= NULL;
 | |
|   /*
 | |
|     TODO: it is not clear why this method needs to reset unireg_check.
 | |
|     Try not to reset it, or explain why it needs to be reset.
 | |
|   */
 | |
|   tmp->unireg_check= Field::NONE;
 | |
|   tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
 | |
|                 ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
 | |
|   tmp->reset_fields();
 | |
|   tmp->invisible= VISIBLE;
 | |
|   return tmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                             uchar *new_ptr, uint32 length,
 | |
|                             uchar *new_null_ptr, uint new_null_bit)
 | |
| {
 | |
|   Field *tmp;
 | |
|   if ((tmp= make_new_field(root, new_table, table == new_table)))
 | |
|   {
 | |
|     tmp->ptr=      new_ptr;
 | |
|     tmp->null_ptr= new_null_ptr;
 | |
|     tmp->null_bit= new_null_bit;
 | |
|   }
 | |
|   return tmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Create field for temporary table from given field.
 | |
| 
 | |
|   @param thd	        Thread handler
 | |
|   @param table	        Temporary table
 | |
|   @param maybe_null_arg If the result field should be NULL-able,
 | |
|                         even if the original field is NOT NULL, e.g. for:
 | |
|                         - OUTER JOIN fields
 | |
|                         - WITH ROLLUP fields
 | |
|                         - arguments of aggregate functions, e.g. SUM(column1)
 | |
|   @retval               NULL, on error
 | |
|   @retval               pointer to the new field created, on success.
 | |
| */
 | |
| 
 | |
| Field *Field::create_tmp_field(MEM_ROOT *mem_root, TABLE *new_table,
 | |
|                                bool maybe_null_arg)
 | |
| {
 | |
|   Field *new_field;
 | |
| 
 | |
|   if ((new_field= make_new_field(mem_root, new_table, new_table == table)))
 | |
|   {
 | |
|     new_field->init_for_tmp_table(this, new_table);
 | |
|     new_field->flags|= flags & NO_DEFAULT_VALUE_FLAG;
 | |
|     if (maybe_null_arg)
 | |
|       new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join
 | |
|   }
 | |
|   return new_field;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* This is used to generate a field in TABLE from TABLE_SHARE */
 | |
| 
 | |
| Field *Field::clone(MEM_ROOT *root, TABLE *new_table)
 | |
| {
 | |
|   Field *tmp;
 | |
|   if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
 | |
|   {
 | |
|     tmp->init(new_table);
 | |
|     tmp->move_field_offset((my_ptrdiff_t) (new_table->record[0] -
 | |
|                                            new_table->s->default_values));
 | |
|   }
 | |
|   return tmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field::clone(MEM_ROOT *root, TABLE *new_table, my_ptrdiff_t diff)
 | |
| {
 | |
|   Field *tmp;
 | |
|   if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
 | |
|   {
 | |
|     if (new_table)
 | |
|       tmp->init(new_table);
 | |
|     tmp->move_field_offset(diff);
 | |
|   }
 | |
|   return tmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field::set_default()
 | |
| {
 | |
|   if (default_value)
 | |
|   {
 | |
|     Query_arena backup_arena;
 | |
|     /*
 | |
|       TODO: this may impose memory leak until table flush.
 | |
|           See comment in
 | |
|           TABLE::update_virtual_fields(handler *, enum_vcol_update_mode).
 | |
|     */
 | |
|     table->in_use->set_n_backup_active_arena(table->expr_arena, &backup_arena);
 | |
|     int rc= default_value->expr->save_in_field(this, 0);
 | |
|     table->in_use->restore_active_arena(table->expr_arena, &backup_arena);
 | |
|     return rc;
 | |
|   }
 | |
|   /* Copy constant value stored in s->default_values */
 | |
|   my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values -
 | |
|                                         table->record[0]);
 | |
|   memcpy(ptr, ptr + l_offset, pack_length_in_rec());
 | |
|   if (maybe_null_in_table())
 | |
|     *null_ptr= ((*null_ptr & (uchar) ~null_bit) |
 | |
|                 (null_ptr[l_offset] & null_bit));
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
|   Field_null, a field that always return NULL
 | |
| ****************************************************************************/
 | |
| 
 | |
| void Field_null::sql_type(String &res) const
 | |
| {
 | |
|   res.set_ascii(STRING_WITH_LEN("null"));
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_null::is_equal(const Column_definition &new_field) const
 | |
| {
 | |
|   DBUG_ASSERT(!compression_method());
 | |
|   return (new_field.type_handler() == type_handler() &&
 | |
|           new_field.charset->eq_collation(field_charset()) &&
 | |
|           new_field.length == max_display_length());
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
|   Field_row, e.g. for ROW-type SP variables
 | |
| ****************************************************************************/
 | |
| 
 | |
| Field_row::~Field_row()
 | |
| {
 | |
|   delete m_table;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_row::sp_prepare_and_store_item(THD *thd, Item **value)
 | |
| {
 | |
|   DBUG_ENTER("Field_row::sp_prepare_and_store_item");
 | |
| 
 | |
|   if (value[0]->type() == Item::NULL_ITEM)
 | |
|   {
 | |
|     /*
 | |
|       We're in a auto-generated sp_inst_set, to assign
 | |
|       the explicit default NULL value to a ROW variable.
 | |
|     */
 | |
|     m_table->set_all_fields_to_null();
 | |
|     DBUG_RETURN(false);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|     - In case if we're assigning a ROW variable from another ROW variable,
 | |
|       value[0] points to Item_splocal. sp_fix_func_item() will return the
 | |
|       fixed underlying Item_field pointing to Field_row.
 | |
|     - In case if we're assigning from a ROW() value, src and value[0] will
 | |
|       point to the same Item_row.
 | |
|     - In case if we're assigning from a subselect, src and value[0] also
 | |
|       point to the same Item_singlerow_subselect.
 | |
|   */
 | |
|   Item *src;
 | |
|   if (!(src= thd->sp_fix_func_item(value)) ||
 | |
|       src->cmp_type() != ROW_RESULT ||
 | |
|       src->cols() != m_table->s->fields)
 | |
|   {
 | |
|     my_error(ER_OPERAND_COLUMNS, MYF(0), m_table->s->fields);
 | |
|     m_table->set_all_fields_to_null();
 | |
|     DBUG_RETURN(true);
 | |
|   }
 | |
| 
 | |
|   src->bring_value();
 | |
|   DBUG_RETURN(m_table->sp_set_all_fields_from_item(thd, src));
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
|   Functions for the Field_decimal class
 | |
|   This is an number stored as a pre-space (or pre-zero) string
 | |
| ****************************************************************************/
 | |
| 
 | |
| int
 | |
| Field_decimal::reset(void)
 | |
| {
 | |
|   Field_decimal::store(STRING_WITH_LEN("0"),&my_charset_bin);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void Field_decimal::overflow(bool negative)
 | |
| {
 | |
|   uint len=field_length;
 | |
|   uchar *to=ptr, filler= '9';
 | |
| 
 | |
|   set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|   if (negative)
 | |
|   {
 | |
|     if (!unsigned_flag)
 | |
|     {
 | |
|       /* Put - sign as a first digit so we'll have -999..999 or 999..999 */
 | |
|       *to++ = '-';
 | |
|       len--;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       filler= '0';				// Fill up with 0
 | |
|       if (!zerofill)
 | |
|       {
 | |
| 	/*
 | |
| 	  Handle unsigned integer without zerofill, in which case
 | |
| 	  the number should be of format '   0' or '   0.000'
 | |
| 	*/
 | |
| 	uint whole_part=field_length- (dec ? dec+2 : 1);
 | |
| 	// Fill with spaces up to the first digit
 | |
| 	bfill(to, whole_part, ' ');
 | |
| 	to+=  whole_part;
 | |
| 	len-= whole_part;
 | |
| 	// The main code will also handle the 0 before the decimal point
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   bfill(to, len, filler);
 | |
|   if (dec)
 | |
|     ptr[field_length-dec-1]='.';
 | |
|   return;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   char buff[STRING_BUFFER_USUAL_SIZE];
 | |
|   String tmp(buff,sizeof(buff), &my_charset_bin);
 | |
|   const uchar *from= (uchar*) from_arg;
 | |
| 
 | |
|   /* Convert character set if the old one is multi uchar */
 | |
|   if (cs->mbmaxlen > 1)
 | |
|   { 
 | |
|     uint dummy_errors;
 | |
|     tmp.copy((char*) from, len, cs, &my_charset_bin, &dummy_errors);
 | |
|     from= (uchar*) tmp.ptr();
 | |
|     len=  tmp.length();
 | |
|   }
 | |
| 
 | |
|   const uchar *end= from+len;
 | |
|   /* The pointer where the field value starts (i.e., "where to write") */
 | |
|   uchar *to= ptr;
 | |
|   uint tmp_dec, tmp_uint;
 | |
|   /*
 | |
|     The sign of the number : will be 0 (means positive but sign not
 | |
|     specified), '+' or '-'
 | |
|   */
 | |
|   uchar sign_char=0;
 | |
|   /* The pointers where prezeros start and stop */
 | |
|   const uchar *pre_zeros_from, *pre_zeros_end;
 | |
|   /* The pointers where digits at the left of '.' start and stop */
 | |
|   const uchar *int_digits_from, *int_digits_end;
 | |
|   /* The pointers where digits at the right of '.' start and stop */
 | |
|   const uchar *frac_digits_from, *frac_digits_end;
 | |
|   /* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */
 | |
|   char expo_sign_char=0;
 | |
|   uint exponent=0;                                // value of the exponent
 | |
|   /*
 | |
|     Pointers used when digits move from the left of the '.' to the
 | |
|     right of the '.' (explained below)
 | |
|   */
 | |
|   const uchar *UNINIT_VAR(int_digits_tail_from);
 | |
|   /* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */
 | |
|   uint UNINIT_VAR(int_digits_added_zeros);
 | |
|   /*
 | |
|     Pointer used when digits move from the right of the '.' to the left
 | |
|     of the '.'
 | |
|   */
 | |
|   const uchar *UNINIT_VAR(frac_digits_head_end);
 | |
|   /* Number of 0 that need to be added at the right of the '.' (for 1E-3) */
 | |
|   uint UNINIT_VAR(frac_digits_added_zeros);
 | |
|   uchar *pos,*tmp_left_pos,*tmp_right_pos;
 | |
|   /* Pointers that are used as limits (begin and end of the field buffer) */
 | |
|   uchar *left_wall,*right_wall;
 | |
|   uchar tmp_char;
 | |
|   /*
 | |
|     To remember if get_thd()->cuted_fields has already been incremented,
 | |
|     to do that only once
 | |
|   */
 | |
|   bool is_cuted_fields_incr=0;
 | |
| 
 | |
|   /*
 | |
|     There are three steps in this function :
 | |
|     - parse the input string
 | |
|     - modify the position of digits around the decimal dot '.' 
 | |
|       according to the exponent value (if specified)
 | |
|     - write the formatted number
 | |
|   */
 | |
| 
 | |
|   if ((tmp_dec=dec))
 | |
|     tmp_dec++;
 | |
| 
 | |
|   /* skip pre-space */
 | |
|   while (from != end && my_isspace(&my_charset_bin,*from))
 | |
|     from++;
 | |
|   if (from == end)
 | |
|   {
 | |
|     set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|     is_cuted_fields_incr=1;
 | |
|   }
 | |
|   else if (*from == '+' || *from == '-')	// Found some sign ?
 | |
|   {
 | |
|     sign_char= *from++;
 | |
|     /*
 | |
|       We allow "+" for unsigned decimal unless defined different
 | |
|       Both options allowed as one may wish not to have "+" for unsigned numbers
 | |
|       because of data processing issues
 | |
|     */ 
 | |
|     if (unsigned_flag)  
 | |
|     { 
 | |
|       if (sign_char=='-')
 | |
|       {
 | |
|         Field_decimal::overflow(1);
 | |
|         return 1;
 | |
|       }
 | |
|       /* 
 | |
| 	 Defining this will not store "+" for unsigned decimal type even if
 | |
| 	 it is passed in numeric string. This will make some tests to fail
 | |
|       */	 
 | |
| #ifdef DONT_ALLOW_UNSIGNED_PLUS      
 | |
|       else 
 | |
|         sign_char=0;
 | |
| #endif 	
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   pre_zeros_from= from;
 | |
|   for (; from!=end && *from == '0'; from++) ;	// Read prezeros
 | |
|   pre_zeros_end=int_digits_from=from;      
 | |
|   /* Read non zero digits at the left of '.'*/
 | |
|   for (; from != end && my_isdigit(&my_charset_bin, *from) ; from++) ;
 | |
|   int_digits_end=from;
 | |
|   if (from!=end && *from == '.')		// Some '.' ?
 | |
|     from++;
 | |
|   frac_digits_from= from;
 | |
|   /* Read digits at the right of '.' */
 | |
|   for (;from!=end && my_isdigit(&my_charset_bin, *from); from++) ;
 | |
|   frac_digits_end=from;
 | |
|   // Some exponentiation symbol ?
 | |
|   if (from != end && (*from == 'e' || *from == 'E'))
 | |
|   {   
 | |
|     from++;
 | |
|     if (from != end && (*from == '+' || *from == '-'))  // Some exponent sign ?
 | |
|       expo_sign_char= *from++;
 | |
|     else
 | |
|       expo_sign_char= '+';
 | |
|     /*
 | |
|       Read digits of the exponent and compute its value.  We must care about
 | |
|       'exponent' overflow, because as unsigned arithmetic is "modulo", big 
 | |
|       exponents will become small (e.g. 1e4294967296 will become 1e0, and the 
 | |
|       field will finally contain 1 instead of its max possible value).
 | |
|     */
 | |
|     for (;from!=end && my_isdigit(&my_charset_bin, *from); from++)
 | |
|     {
 | |
|       exponent=10*exponent+(*from-'0');
 | |
|       if (exponent>MAX_EXPONENT)
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   /*
 | |
|     We only have to generate warnings if count_cuted_fields is set.
 | |
|     This is to avoid extra checks of the number when they are not needed.
 | |
|     Even if this flag is not set, it's OK to increment warnings, if
 | |
|     it makes the code easier to read.
 | |
|   */
 | |
| 
 | |
|   if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
 | |
|   {
 | |
|     // Skip end spaces
 | |
|     for (;from != end && my_isspace(&my_charset_bin, *from); from++) ;
 | |
|     if (from != end)                     // If still something left, warn
 | |
|     {
 | |
|       set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|       is_cuted_fields_incr=1;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   /*
 | |
|     Now "move" digits around the decimal dot according to the exponent value,
 | |
|     and add necessary zeros.
 | |
|     Examples :
 | |
|     - 1E+3 : needs 3 more zeros at the left of '.' (int_digits_added_zeros=3)
 | |
|     - 1E-3 : '1' moves at the right of '.', and 2 more zeros are needed
 | |
|     between '.' and '1'
 | |
|     - 1234.5E-3 : '234' moves at the right of '.'
 | |
|     These moves are implemented with pointers which point at the begin
 | |
|     and end of each moved segment. Examples :
 | |
|     - 1234.5E-3 : before the code below is executed, the int_digits part is
 | |
|     from '1' to '4' and the frac_digits part from '5' to '5'. After the code
 | |
|     below, the int_digits part is from '1' to '1', the frac_digits_head
 | |
|     part is from '2' to '4', and the frac_digits part from '5' to '5'.
 | |
|     - 1234.5E3 : before the code below is executed, the int_digits part is
 | |
|     from '1' to '4' and the frac_digits part from '5' to '5'. After the code
 | |
|     below, the int_digits part is from '1' to '4', the int_digits_tail
 | |
|     part is from '5' to '5', the frac_digits part is empty, and
 | |
|     int_digits_added_zeros=2 (to make 1234500).
 | |
|   */
 | |
|   
 | |
|   /* 
 | |
|      Below tmp_uint cannot overflow with small enough MAX_EXPONENT setting,
 | |
|      as int_digits_added_zeros<=exponent<4G and 
 | |
|      (int_digits_end-int_digits_from)<=max_allowed_packet<=2G and
 | |
|      (frac_digits_from-int_digits_tail_from)<=max_allowed_packet<=2G
 | |
|   */
 | |
| 
 | |
|   if (!expo_sign_char)
 | |
|     tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from);
 | |
|   else if (expo_sign_char == '-') 
 | |
|   {
 | |
|     tmp_uint=MY_MIN(exponent,(uint)(int_digits_end-int_digits_from));
 | |
|     frac_digits_added_zeros=exponent-tmp_uint;
 | |
|     int_digits_end -= tmp_uint;
 | |
|     frac_digits_head_end=int_digits_end+tmp_uint;
 | |
|     tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from);     
 | |
|   }
 | |
|   else // (expo_sign_char=='+') 
 | |
|   {
 | |
|     tmp_uint=MY_MIN(exponent,(uint)(frac_digits_end-frac_digits_from));
 | |
|     int_digits_added_zeros=exponent-tmp_uint;
 | |
|     int_digits_tail_from=frac_digits_from;
 | |
|     frac_digits_from=frac_digits_from+tmp_uint;
 | |
|     /*
 | |
|       We "eat" the heading zeros of the 
 | |
|       int_digits.int_digits_tail.int_digits_added_zeros concatenation
 | |
|       (for example 0.003e3 must become 3 and not 0003)
 | |
|     */
 | |
|     if (int_digits_from == int_digits_end) 
 | |
|     {
 | |
|       /*
 | |
| 	There was nothing in the int_digits part, so continue
 | |
| 	eating int_digits_tail zeros
 | |
|       */
 | |
|       for (; int_digits_tail_from != frac_digits_from &&
 | |
| 	     *int_digits_tail_from == '0'; int_digits_tail_from++) ;
 | |
|       if (int_digits_tail_from == frac_digits_from) 
 | |
|       {
 | |
| 	// there were only zeros in int_digits_tail too
 | |
| 	int_digits_added_zeros=0;
 | |
|       }
 | |
|     }
 | |
|     tmp_uint= (uint) (tmp_dec+(int_digits_end-int_digits_from)+
 | |
|                (uint)(frac_digits_from-int_digits_tail_from)+
 | |
|                int_digits_added_zeros);
 | |
|   }
 | |
|   
 | |
|   /*
 | |
|     Now write the formatted number
 | |
|     
 | |
|     First the digits of the int_% parts.
 | |
|     Do we have enough room to write these digits ?
 | |
|     If the sign is defined and '-', we need one position for it
 | |
|   */
 | |
| 
 | |
|   if (field_length < tmp_uint + (int) (sign_char == '-')) 
 | |
|   {
 | |
|     // too big number, change to max or min number
 | |
|     Field_decimal::overflow(sign_char == '-');
 | |
|     return 1;
 | |
|   }
 | |
|  
 | |
|   /*
 | |
|     Tmp_left_pos is the position where the leftmost digit of
 | |
|     the int_% parts will be written
 | |
|   */
 | |
|   tmp_left_pos=pos=to+(uint)(field_length-tmp_uint);
 | |
|   
 | |
|   // Write all digits of the int_% parts
 | |
|   while (int_digits_from != int_digits_end)
 | |
|     *pos++ = *int_digits_from++ ;
 | |
| 
 | |
|   if (expo_sign_char == '+')
 | |
|   {    
 | |
|     while (int_digits_tail_from != frac_digits_from)
 | |
|       *pos++= *int_digits_tail_from++;
 | |
|     while (int_digits_added_zeros-- >0)
 | |
|       *pos++= '0';  
 | |
|   }
 | |
|   /*
 | |
|     Note the position where the rightmost digit of the int_% parts has been
 | |
|     written (this is to later check if the int_% parts contained nothing,
 | |
|     meaning an extra 0 is needed).
 | |
|   */
 | |
|   tmp_right_pos=pos;
 | |
| 
 | |
|   /*
 | |
|     Step back to the position of the leftmost digit of the int_% parts,
 | |
|     to write sign and fill with zeros or blanks or prezeros.
 | |
|   */
 | |
|   pos=tmp_left_pos-1;
 | |
|   if (zerofill)
 | |
|   {
 | |
|     left_wall=to-1;
 | |
|     while (pos > left_wall)			// Fill with zeros
 | |
|       *pos--='0';
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     left_wall=to+(sign_char != 0)-1;
 | |
|     if (!expo_sign_char)	// If exponent was specified, ignore prezeros
 | |
|     {
 | |
|       for (;pos > left_wall && pre_zeros_from !=pre_zeros_end;
 | |
| 	   pre_zeros_from++)
 | |
| 	*pos--= '0';
 | |
|     }
 | |
|     if (pos == tmp_right_pos-1)
 | |
|       *pos--= '0';		// no 0 has ever been written, so write one
 | |
|     left_wall= to-1;
 | |
|     if (sign_char && pos != left_wall)
 | |
|     {
 | |
|       /* Write sign if possible (it is if sign is '-') */
 | |
|       *pos--= sign_char;
 | |
|     }
 | |
|     while (pos != left_wall)
 | |
|       *pos--=' ';  //fill with blanks
 | |
|   }
 | |
|   
 | |
|   /*
 | |
|     Write digits of the frac_% parts ;
 | |
|     Depending on get_thd()->count_cuted_fields, we may also want
 | |
|     to know if some non-zero tail of these parts will
 | |
|     be truncated (for example, 0.002->0.00 will generate a warning,
 | |
|     while 0.000->0.00 will not)
 | |
|     (and 0E1000000000 will not, while 1E-1000000000 will)
 | |
|   */
 | |
|       
 | |
|   pos=to+(uint)(field_length-tmp_dec);	// Calculate post to '.'
 | |
|   right_wall=to+field_length;
 | |
|   if (pos != right_wall) 
 | |
|     *pos++='.';
 | |
| 
 | |
|   if (expo_sign_char == '-')
 | |
|   {
 | |
|     while (frac_digits_added_zeros-- > 0)
 | |
|     {
 | |
|       if (pos == right_wall) 
 | |
|       {
 | |
|         if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
 | |
|             !is_cuted_fields_incr)
 | |
|           break; // Go on below to see if we lose non zero digits
 | |
|         return 0;
 | |
|       }
 | |
|       *pos++='0';
 | |
|     }
 | |
|     while (int_digits_end != frac_digits_head_end)
 | |
|     {
 | |
|       tmp_char= *int_digits_end++;
 | |
|       if (pos == right_wall)
 | |
|       {
 | |
|         if (tmp_char != '0')			// Losing a non zero digit ?
 | |
|         {
 | |
|           if (!is_cuted_fields_incr)
 | |
|             set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|           return 0;
 | |
|         }
 | |
|         continue;
 | |
|       }
 | |
|       *pos++= tmp_char;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (;frac_digits_from!=frac_digits_end;) 
 | |
|   {
 | |
|     tmp_char= *frac_digits_from++;
 | |
|     if (pos == right_wall)
 | |
|     {
 | |
|       if (tmp_char != '0')			// Losing a non zero digit ?
 | |
|       {
 | |
|         if (!is_cuted_fields_incr)
 | |
|         {
 | |
|           /*
 | |
|             This is a note, not a warning, as we don't want to abort
 | |
|             when we cut decimals in strict mode
 | |
|           */
 | |
| 	  set_note(WARN_DATA_TRUNCATED, 1);
 | |
|         }
 | |
|         return 0;
 | |
|       }
 | |
|       continue;
 | |
|     }
 | |
|     *pos++= tmp_char;
 | |
|   }
 | |
|       
 | |
|   while (pos != right_wall)
 | |
|    *pos++='0';			// Fill with zeros at right of '.'
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_decimal::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   if (unsigned_flag && nr < 0)
 | |
|   {
 | |
|     overflow(1);
 | |
|     return 1;
 | |
|   }
 | |
|   
 | |
|   if (!std::isfinite(nr)) // Handle infinity as special case
 | |
|   {
 | |
|     overflow(nr < 0.0);
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   size_t length;
 | |
|   uchar fyllchar,*to;
 | |
|   char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
 | |
| 
 | |
|   fyllchar = zerofill ? (char) '0' : (char) ' ';
 | |
|   length= my_fcvt(nr, dec, buff, NULL);
 | |
| 
 | |
|   if (length > field_length)
 | |
|   {
 | |
|     overflow(nr < 0.0);
 | |
|     return 1;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     to=ptr;
 | |
|     for (size_t i=field_length-length ; i-- > 0 ;)
 | |
|       *to++ = fyllchar;
 | |
|     memcpy(to,buff,length);
 | |
|     return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_decimal::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   char buff[22];
 | |
|   uint length, int_part;
 | |
|   char fyllchar;
 | |
|   uchar *to;
 | |
| 
 | |
|   if (nr < 0 && unsigned_flag && !unsigned_val)
 | |
|   {
 | |
|     overflow(1);
 | |
|     return 1;
 | |
|   }
 | |
|   length= (uint) (longlong10_to_str(nr,buff,unsigned_val ? 10 : -10) - buff);
 | |
|   int_part= field_length- (dec  ? dec+1 : 0);
 | |
| 
 | |
|   if (length > int_part)
 | |
|   {
 | |
|     overflow(!unsigned_val && nr < 0L);		/* purecov: inspected */
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   fyllchar = zerofill ? (char) '0' : (char) ' ';
 | |
|   to= ptr;
 | |
|   for (uint i=int_part-length ; i-- > 0 ;)
 | |
|     *to++ = fyllchar;
 | |
|   memcpy(to,buff,length);
 | |
|   if (dec)
 | |
|   {
 | |
|     to[length]='.';
 | |
|     bfill(to+length+1,dec,'0');
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_decimal::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int not_used;
 | |
|   char *end_not_used;
 | |
|   return my_charset_bin.strntod((char*) ptr, field_length, &end_not_used, ¬_used);
 | |
| }
 | |
| 
 | |
| longlong Field_decimal::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int not_used;
 | |
|   if (unsigned_flag)
 | |
|     return my_charset_bin.strntoull((char*) ptr, field_length, 10, NULL, ¬_used);
 | |
|   return my_charset_bin.strntoll((char*) ptr, field_length, 10, NULL, ¬_used);
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
 | |
| 			       String *val_ptr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   uchar *str;
 | |
|   size_t tmp_length;
 | |
| 
 | |
|   for (str=ptr ; *str == ' ' ; str++) ;
 | |
|   val_ptr->set_charset(&my_charset_numeric);
 | |
|   tmp_length= (size_t) (str-ptr);
 | |
|   if (field_length < tmp_length)		// Error in data
 | |
|     val_ptr->length(0);
 | |
|   else
 | |
|     val_ptr->set_ascii((const char*) str, field_length-tmp_length);
 | |
|   return val_ptr;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Should be able to handle at least the following fixed decimal formats:
 | |
|   5.00 , -1.0,  05,  -05, +5 with optional pre/end space
 | |
| */
 | |
| 
 | |
| int Field_decimal::cmp(const uchar *a_ptr,const uchar *b_ptr) const
 | |
| {
 | |
|   const uchar *end;
 | |
|   int swap=0;
 | |
|   /* First remove prefixes '0', ' ', and '-' */
 | |
|   for (end=a_ptr+field_length;
 | |
|        a_ptr != end &&
 | |
| 	 (*a_ptr == *b_ptr ||
 | |
| 	  ((my_isspace(&my_charset_bin,*a_ptr)  || *a_ptr == '+' || 
 | |
|             *a_ptr == '0') &&
 | |
| 	   (my_isspace(&my_charset_bin,*b_ptr) || *b_ptr == '+' || 
 | |
|             *b_ptr == '0')));
 | |
|        a_ptr++,b_ptr++)
 | |
|   {
 | |
|     if (*a_ptr == '-')				// If both numbers are negative
 | |
|       swap= -1 ^ 1;				// Swap result      
 | |
|   }
 | |
|   if (a_ptr == end)
 | |
|     return 0;
 | |
|   if (*a_ptr == '-')
 | |
|     return -1;
 | |
|   if (*b_ptr == '-')
 | |
|     return 1;
 | |
| 
 | |
|   while (a_ptr != end)
 | |
|   {
 | |
|     if (*a_ptr++ != *b_ptr++)
 | |
|       return swap ^ (a_ptr[-1] < b_ptr[-1] ? -1 : 1); // compare digits
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_decimal::sort_string(uchar *to,uint length)
 | |
| {
 | |
|   uchar *str,*end;
 | |
|   for (str=ptr,end=ptr+length;
 | |
|        str != end &&
 | |
| 	 ((my_isspace(&my_charset_bin,*str) || *str == '+' ||
 | |
| 	   *str == '0')) ;
 | |
|        str++)
 | |
|     *to++=' ';
 | |
|   if (str == end)
 | |
|     return;					/* purecov: inspected */
 | |
| 
 | |
|   if (*str == '-')
 | |
|   {
 | |
|     *to++=1;					// Smaller than any number
 | |
|     str++;
 | |
|     while (str != end)
 | |
|       if (my_isdigit(&my_charset_bin,*str))
 | |
| 	*to++= (char) ('9' - *str++);
 | |
|       else
 | |
| 	*to++= *str++;
 | |
|   }
 | |
|   else memcpy(to,str,(uint) (end-str));
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_decimal::sql_type(String &res) const
 | |
| {
 | |
|   CHARSET_INFO *cs=res.charset();
 | |
|   uint tmp=field_length;
 | |
|   if (!unsigned_flag)
 | |
|     tmp--;
 | |
|   if (dec)
 | |
|     tmp--;
 | |
|   res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
 | |
| 			  "decimal(%d,%d)/*old*/",tmp,dec));
 | |
|   add_zerofill_and_unsigned(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field_decimal::make_new_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                                      bool keep_type)
 | |
| {
 | |
|   if (keep_type)
 | |
|     return Field_real::make_new_field(root, new_table, keep_type);
 | |
| 
 | |
|   Field *field= new (root) Field_new_decimal(NULL, field_length,
 | |
|                                              maybe_null() ? (uchar*) "" : 0, 0,
 | |
|                                              NONE, &field_name,
 | |
|                                              dec, flags & ZEROFILL_FLAG,
 | |
|                                              unsigned_flag);
 | |
|   if (field)
 | |
|     field->init_for_make_new_field(new_table, orig_table);
 | |
|   return field;
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** Field_new_decimal
 | |
| ****************************************************************************/
 | |
| 
 | |
| static decimal_digits_t get_decimal_precision(uint len, decimal_digits_t dec,
 | |
|                                               bool unsigned_val)
 | |
| {
 | |
|   uint precision= my_decimal_length_to_precision(len, dec, unsigned_val);
 | |
|   return (decimal_digits_t) MY_MIN(precision, DECIMAL_MAX_PRECISION);
 | |
| }
 | |
| 
 | |
| Field_new_decimal::Field_new_decimal(uchar *ptr_arg,
 | |
|                                      uint32 len_arg, uchar *null_ptr_arg,
 | |
|                                      uchar null_bit_arg,
 | |
|                                      enum utype unireg_check_arg,
 | |
|                                      const LEX_CSTRING *field_name_arg,
 | |
|                                      decimal_digits_t dec_arg,bool zero_arg,
 | |
|                                      bool unsigned_arg)
 | |
|   :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
 | |
|              unireg_check_arg, field_name_arg,
 | |
|              MY_MIN(dec_arg, DECIMAL_MAX_SCALE), zero_arg, unsigned_arg)
 | |
| {
 | |
|   precision= get_decimal_precision(len_arg, dec_arg, unsigned_arg);
 | |
|   DBUG_ASSERT(precision <= DECIMAL_MAX_PRECISION);
 | |
|   DBUG_ASSERT(dec <= DECIMAL_MAX_SCALE);
 | |
|   bin_size= my_decimal_get_binary_size(precision, dec);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_new_decimal::reset(void)
 | |
| {
 | |
|   store_value(&decimal_zero);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Generate max/min decimal value in case of overflow.
 | |
| 
 | |
|   @param decimal_value     buffer for value
 | |
|   @param sign              sign of value which caused overflow
 | |
| */
 | |
| 
 | |
| void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
 | |
|                                               bool sign)
 | |
| {
 | |
|   DBUG_ENTER("Field_new_decimal::set_value_on_overflow");
 | |
|   max_my_decimal(decimal_value, precision, decimals());
 | |
|   if (sign)
 | |
|   {
 | |
|     if (unsigned_flag)
 | |
|       my_decimal_set_zero(decimal_value);
 | |
|     else
 | |
|       decimal_value->sign(TRUE);
 | |
|   }
 | |
|   DBUG_VOID_RETURN;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Store decimal value in the binary buffer.
 | |
| 
 | |
|   Checks if decimal_value fits into field size.
 | |
|   If it does, stores the decimal in the buffer using binary format.
 | |
|   Otherwise sets maximal number that can be stored in the field.
 | |
| 
 | |
|   @param       decimal_value   my_decimal
 | |
|   @param [OUT] native_error    the error returned by my_decimal::to_binary().
 | |
| 
 | |
|   @retval
 | |
|     0 ok
 | |
|   @retval
 | |
|     1 error
 | |
| */
 | |
| 
 | |
| bool Field_new_decimal::store_value(const my_decimal *decimal_value,
 | |
|                                     int *native_error)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   DBUG_ENTER("Field_new_decimal::store_value");
 | |
| #ifdef DBUG_TRACE
 | |
|   {
 | |
|     char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
 | |
|     DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, decimal_value)));
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   /* check that we do not try to write negative value in unsigned field */
 | |
|   if (unsigned_flag && decimal_value->sign())
 | |
|   {
 | |
|     DBUG_PRINT("info", ("unsigned overflow"));
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     error= 1;
 | |
|     decimal_value= &decimal_zero;
 | |
|   }
 | |
| #ifdef DBUG_TRACE
 | |
|   {
 | |
|     char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
 | |
|     DBUG_PRINT("info", ("saving with precision %d  scale: %d  value %s",
 | |
|                         (int)precision, (int)dec,
 | |
|                         dbug_decimal_as_string(dbug_buff, decimal_value)));
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   *native_error= decimal_value->to_binary(ptr, precision, dec,
 | |
|                                           E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW);
 | |
| 
 | |
|   if (unlikely(*native_error == E_DEC_OVERFLOW))
 | |
|   {
 | |
|     my_decimal buff;
 | |
|     DBUG_PRINT("info", ("overflow"));
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     set_value_on_overflow(&buff, decimal_value->sign());
 | |
|     buff.to_binary(ptr, precision, dec);
 | |
|     error= 1;
 | |
|   }
 | |
|   DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
 | |
|                                           bin_size););
 | |
|   DBUG_RETURN(error);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_new_decimal::store_value(const my_decimal *decimal_value)
 | |
| {
 | |
|   int native_error;
 | |
|   bool rc= store_value(decimal_value, &native_error);
 | |
|   if (unlikely(!rc && native_error == E_DEC_TRUNCATED))
 | |
|     set_note(WARN_DATA_TRUNCATED, 1);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_new_decimal::store(const char *from, size_t length,
 | |
|                              CHARSET_INFO *charset_arg)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   my_decimal decimal_value;
 | |
|   THD *thd= get_thd();
 | |
|   DBUG_ENTER("Field_new_decimal::store(char*)");
 | |
| 
 | |
|   const char *end;
 | |
|   int err= str2my_decimal(E_DEC_FATAL_ERROR &
 | |
|                           ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM),
 | |
|                            from, length, charset_arg,
 | |
|                            &decimal_value, &end);
 | |
| 
 | |
|   if (err == E_DEC_OVERFLOW) // Too many digits (>81) in the integer part
 | |
|   {
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     if (!thd->abort_on_warning)
 | |
|     {
 | |
|       set_value_on_overflow(&decimal_value, decimal_value.sign());
 | |
|       store_decimal(&decimal_value);
 | |
|     }
 | |
|     DBUG_RETURN(1);
 | |
|   }
 | |
| 
 | |
|   if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION)
 | |
|   {
 | |
|     if (check_edom_and_important_data_truncation("decimal",
 | |
|                                                  err && err != E_DEC_TRUNCATED,
 | |
|                                                  charset_arg,
 | |
|                                                  from, length, end))
 | |
|     {
 | |
|       if (!thd->abort_on_warning)
 | |
|       {
 | |
|         if (err && err != E_DEC_TRUNCATED)
 | |
|         {
 | |
|           /*
 | |
|             If check_decimal() failed because of EDOM-alike error,
 | |
|             (e.g. E_DEC_BAD_NUM), we have to initialize decimal_value to zero.
 | |
|             Note: if check_decimal() failed because of truncation,
 | |
|             decimal_value is already properly initialized.
 | |
|           */
 | |
|           my_decimal_set_zero(&decimal_value);
 | |
|           /*
 | |
|             TODO: check str2my_decimal() with HF. It seems to do
 | |
|             decimal_make_zero() on fatal errors, so my_decimal_set_zero()
 | |
|             is probably not needed here.
 | |
|           */
 | |
|         }
 | |
|         store_decimal(&decimal_value);
 | |
|       }
 | |
|       DBUG_RETURN(1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #ifdef DBUG_TRACE
 | |
|   char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
 | |
|   DBUG_PRINT("enter", ("value: %s",
 | |
|                        dbug_decimal_as_string(dbug_buff, &decimal_value)));
 | |
| #endif
 | |
|   int err2;
 | |
|   if (store_value(&decimal_value, &err2))
 | |
|     DBUG_RETURN(1);
 | |
| 
 | |
|   /*
 | |
|     E_DEC_TRUNCATED means minor truncation, a note should be enough:
 | |
|     - in err: str2my_decimal() truncated '1e-1000000000000' to 0.0
 | |
|     - in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2)
 | |
|     Also, we send a note if a string had some trailing spaces: '1.12 '
 | |
|   */
 | |
|   if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
 | |
|       (err == E_DEC_TRUNCATED ||
 | |
|        err2 == E_DEC_TRUNCATED ||
 | |
|        end < from + length))
 | |
|     set_note(WARN_DATA_TRUNCATED, 1);
 | |
|   DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @todo
 | |
|   Fix following when double2my_decimal when double2decimal
 | |
|   will return E_DEC_TRUNCATED always correctly
 | |
| */
 | |
| 
 | |
| int Field_new_decimal::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   my_decimal decimal_value;
 | |
|   int err;
 | |
|   THD *thd= get_thd();
 | |
|   DBUG_ENTER("Field_new_decimal::store(double)");
 | |
| 
 | |
|   err= double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr,
 | |
|                          &decimal_value);
 | |
|   if (err)
 | |
|   {
 | |
|     if (check_overflow(err))
 | |
|       set_value_on_overflow(&decimal_value, decimal_value.sign());
 | |
|     /* Only issue a warning if store_value doesn't issue an warning */
 | |
|     thd->got_warning= 0;
 | |
|   }
 | |
|   if (store_value(&decimal_value))
 | |
|     err= 1;
 | |
|   else if (err && !thd->got_warning)
 | |
|     err= warn_if_overflow(err);
 | |
|   DBUG_RETURN(err);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_new_decimal::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   my_decimal decimal_value;
 | |
|   int err;
 | |
| 
 | |
|   if ((err= int2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
 | |
|                            nr, unsigned_val, &decimal_value)))
 | |
|   {
 | |
|     if (check_overflow(err))
 | |
|       set_value_on_overflow(&decimal_value, decimal_value.sign());
 | |
|     /* Only issue a warning if store_value doesn't issue an warning */
 | |
|     get_thd()->got_warning= 0;
 | |
|   }
 | |
|   if (store_value(&decimal_value))
 | |
|     err= 1;
 | |
|   else if (err && !get_thd()->got_warning)
 | |
|     err= warn_if_overflow(err);
 | |
|   return err;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_new_decimal::store_decimal(const my_decimal *decimal_value)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   return store_value(decimal_value);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_new_decimal::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
 | |
| {
 | |
|   my_decimal decimal_value;
 | |
|   return store_value(date2my_decimal(ltime, &decimal_value));
 | |
| }
 | |
| 
 | |
| 
 | |
| my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   DBUG_ENTER("Field_new_decimal::val_decimal");
 | |
|   binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value,
 | |
|                     precision, dec);
 | |
|   DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
 | |
|                                           bin_size););
 | |
|   DBUG_RETURN(decimal_value);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_new_decimal::cmp(const uchar *a,const uchar*b) const
 | |
| {
 | |
|   return memcmp(a, b, bin_size);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_new_decimal::sort_string(uchar *buff, uint length)
 | |
| {
 | |
|   memcpy(buff, ptr, length);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_new_decimal::sql_type(String &str) const
 | |
| {
 | |
|   CHARSET_INFO *cs= str.charset();
 | |
|   str.length(cs->cset->snprintf(cs, (char*) str.ptr(), str.alloced_length(),
 | |
|                                 "decimal(%d,%d)", precision, (int)dec));
 | |
|   add_zerofill_and_unsigned(str);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Save the field metadata for new decimal fields.
 | |
| 
 | |
|    Saves the precision in the first byte and decimals() in the second
 | |
|    byte of the field metadata array at index of *metadata_ptr and 
 | |
|    *(metadata_ptr + 1).
 | |
| 
 | |
|    @param   metadata_ptr   First byte of field metadata
 | |
| 
 | |
|    @returns number of bytes written to metadata_ptr
 | |
| */
 | |
| 
 | |
| Binlog_type_info Field_new_decimal::binlog_type_info() const
 | |
| {
 | |
|   DBUG_ASSERT(Field_new_decimal::type() == binlog_type());
 | |
|   return Binlog_type_info(Field_new_decimal::type(), precision +
 | |
|                           (decimals() << 8), 2, binlog_signedness());
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Returns the number of bytes field uses in row-based replication 
 | |
|    row packed size.
 | |
| 
 | |
|    This method is used in row-based replication to determine the number
 | |
|    of bytes that the field consumes in the row record format. This is
 | |
|    used to skip fields in the master that do not exist on the slave.
 | |
| 
 | |
|    @param   field_metadata   Encoded size in field metadata
 | |
| 
 | |
|    @returns The size of the field based on the field metadata.
 | |
| */
 | |
| uint Field_new_decimal::pack_length_from_metadata(uint field_metadata) const
 | |
| {
 | |
|   uint const source_precision= (field_metadata >> 8U) & 0x00ff;
 | |
|   uint const source_decimal= field_metadata & 0x00ff; 
 | |
|   uint const source_size= my_decimal_get_binary_size(source_precision, 
 | |
|                                                      source_decimal);
 | |
|   return (source_size);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_new_decimal::compatible_field_size(uint field_metadata,
 | |
|                                               const Relay_log_info * __attribute__((unused)),
 | |
|                                               uint16 mflags __attribute__((unused)),
 | |
|                                               int *order_var) const
 | |
| {
 | |
|   uint const source_precision= (field_metadata >> 8U) & 0x00ff;
 | |
|   uint const source_decimal= field_metadata & 0x00ff; 
 | |
|   int order= compare(source_precision, precision);
 | |
|   *order_var= order != 0 ? order : compare(source_decimal, dec);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_new_decimal::is_equal(const Column_definition &new_field) const
 | |
| {
 | |
|   return ((new_field.type_handler() == type_handler()) &&
 | |
|           ((new_field.flags & UNSIGNED_FLAG) ==
 | |
|            (uint) (flags & UNSIGNED_FLAG)) &&
 | |
|           ((new_field.flags & AUTO_INCREMENT_FLAG) <=
 | |
|            (uint) (flags & AUTO_INCREMENT_FLAG)) &&
 | |
|           (new_field.length == max_display_length()) &&
 | |
|           (new_field.decimals == dec));
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Unpack a decimal field from row data.
 | |
| 
 | |
|    This method is used to unpack a decimal or numeric field from a master
 | |
|    whose size of the field is less than that of the slave.
 | |
|   
 | |
|    @param   to         Destination of the data
 | |
|    @param   from       Source of the data
 | |
|    @param   param_data Precision (upper) and decimal (lower) values
 | |
| 
 | |
|    @return  New pointer into memory based on from + length of the data
 | |
| */
 | |
| const uchar *
 | |
| Field_new_decimal::unpack(uchar* to, const uchar *from, const uchar *from_end,
 | |
|                           uint param_data)
 | |
| {
 | |
|   if (param_data == 0)
 | |
|     return Field::unpack(to, from, from_end, param_data);
 | |
| 
 | |
|   uint from_precision= (param_data & 0xff00) >> 8U;
 | |
|   uint from_decimal= param_data & 0x00ff;
 | |
|   uint length=pack_length();
 | |
|   uint from_pack_len= my_decimal_get_binary_size(from_precision, from_decimal);
 | |
|   uint len= (param_data && (from_pack_len < length)) ?
 | |
|             from_pack_len : length;
 | |
|   if ((from_pack_len && (from_pack_len < length)) ||
 | |
|       (from_precision < precision) ||
 | |
|       (from_decimal < decimals()))
 | |
|   {
 | |
|     /*
 | |
|       If the master's data is smaller than the slave, we need to convert
 | |
|       the binary to decimal then resize the decimal converting it back to
 | |
|       a decimal and write that to the raw data buffer.
 | |
|     */
 | |
|     decimal_digit_t dec_buf[DECIMAL_MAX_PRECISION];
 | |
|     decimal_t dec_val;
 | |
|     dec_val.len= from_precision;
 | |
|     dec_val.buf= dec_buf;
 | |
|     /*
 | |
|       Note: bin2decimal does not change the length of the field. So it is
 | |
|       just the first step the resizing operation. The second step does the
 | |
|       resizing using the precision and decimals from the slave.
 | |
|     */
 | |
|     bin2decimal((uchar *)from, &dec_val, from_precision, from_decimal);
 | |
|     decimal2bin(&dec_val, to, precision, decimals());
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (from + len > from_end)
 | |
|       return 0;                                 // Wrong data
 | |
|     memcpy(to, from, len); // Sizes are the same, just copy the data.
 | |
|   }
 | |
|   return from+len;
 | |
| }
 | |
| 
 | |
| 
 | |
| Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx,
 | |
|                                               Item *const_item)
 | |
| {
 | |
|   if (flags & ZEROFILL_FLAG)
 | |
|     return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item);
 | |
|   switch (ctx.subst_constraint()) {
 | |
|   case IDENTITY_SUBST:
 | |
|     if (const_item->field_type() != MYSQL_TYPE_NEWDECIMAL ||
 | |
|         const_item->decimal_scale() != decimals())
 | |
|     {
 | |
|       VDec val(const_item);
 | |
|       if (val.is_null())
 | |
|       {
 | |
|         DBUG_ASSERT(0);
 | |
|         return const_item;
 | |
|       }
 | |
|       /*
 | |
|         Truncate or extend the decimal value to the scale of the field.
 | |
|         See comments about truncation in the same place in
 | |
|         Field_time::get_equal_const_item().
 | |
|       */
 | |
|       my_decimal tmp;
 | |
|       val.round_to(&tmp, decimals(), TRUNCATE);
 | |
|       return new (thd->mem_root) Item_decimal(thd, field_name.str, &tmp,
 | |
|                                               decimals(), field_length);
 | |
|     }
 | |
|     break;
 | |
|   case ANY_SUBST:
 | |
|     break;
 | |
|   }
 | |
|   return const_item;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_int::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
 | |
| {
 | |
|   longlong v= TIME_to_ulonglong(ltime);
 | |
|   if (ltime->neg == 0)
 | |
|     return store(v, true);
 | |
|   return store(-v, false);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_int::sql_type(String &res) const
 | |
| {
 | |
|   CHARSET_INFO *cs=res.charset();
 | |
|   Name name= type_handler()->type_handler_signed()->name();
 | |
|   res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
 | |
| 			  "%.*s(%d)", (int) name.length(), name.ptr(),
 | |
| 			  (int) field_length));
 | |
|   add_zerofill_and_unsigned(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** tiny int
 | |
| ****************************************************************************/
 | |
| 
 | |
| int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error;
 | |
|   longlong rnd;
 | |
|   
 | |
|   error= get_int(cs, from, len, &rnd, 255, -128, 127);
 | |
|   ptr[0]= unsigned_flag ? (char) (ulonglong) rnd : (char) rnd;
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_tiny::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   nr=rint(nr);
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (nr < 0.0)
 | |
|     {
 | |
|       *ptr=0;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > 255.0)
 | |
|     {
 | |
|       *ptr= (uchar) 255;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       *ptr= (uchar) nr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (nr < -128.0)
 | |
|     {
 | |
|       *ptr= (uchar) -128;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > 127.0)
 | |
|     {
 | |
|       *ptr=127;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       *ptr=(uchar) (int) nr;
 | |
|   }
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_tiny::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
| 
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (nr < 0 && !unsigned_val)
 | |
|     {
 | |
|       *ptr= 0;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if ((ulonglong) nr > (ulonglong) 255)
 | |
|     {
 | |
|       *ptr= (char) 255;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       *ptr=(char) nr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (nr < 0 && unsigned_val)
 | |
|       nr= 256;                                    // Generate overflow
 | |
|     if (nr < -128)
 | |
|     {
 | |
|       *ptr= (char) -128;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > 127)
 | |
|     {
 | |
|       *ptr=127;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       *ptr=(char) nr;
 | |
|   }
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_tiny::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int tmp= unsigned_flag ? (int) ptr[0] :
 | |
|     (int) ((signed char*) ptr)[0];
 | |
|   return (double) tmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_tiny::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int tmp= unsigned_flag ? (int) ptr[0] :
 | |
|     (int) ((signed char*) ptr)[0];
 | |
|   return (longlong) tmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_tiny::val_str(String *val_buffer,
 | |
| 			    String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   long nr= unsigned_flag ? (long) ptr[0] : (long) ((signed char*) ptr)[0];
 | |
|   return val_str_from_long(val_buffer, 5, -10, nr);
 | |
| }
 | |
| 
 | |
| bool Field_tiny::send(Protocol *protocol)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   Protocol_text *txt;
 | |
|   if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
 | |
|     return send_numeric_zerofill_str(txt, PROTOCOL_SEND_TINY);
 | |
|   return protocol->store_tiny(Field_tiny::val_int());
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   signed char a,b;
 | |
|   a=(signed char) a_ptr[0]; b= (signed char) b_ptr[0];
 | |
|   if (unsigned_flag)
 | |
|     return ((uchar) a < (uchar) b) ? -1 : ((uchar) a > (uchar) b) ? 1 : 0;
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_tiny::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   if (unsigned_flag)
 | |
|     *to= *ptr;
 | |
|   else
 | |
|     to[0] = (char) (ptr[0] ^ (uchar) 128);	/* Revers signbit */
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  Field type short int (2 byte)
 | |
| ****************************************************************************/
 | |
| 
 | |
| int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int store_tmp;
 | |
|   int error;
 | |
|   longlong rnd;
 | |
|   
 | |
|   error= get_int(cs, from, len, &rnd, UINT_MAX16, INT_MIN16, INT_MAX16);
 | |
|   store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd;
 | |
|   int2store(ptr, store_tmp);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_short::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   int16 res;
 | |
|   nr=rint(nr);
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (nr < 0)
 | |
|     {
 | |
|       res=0;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > (double) UINT_MAX16)
 | |
|     {
 | |
|       res=(int16) UINT_MAX16;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       res=(int16) (uint16) nr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (nr < (double) INT_MIN16)
 | |
|     {
 | |
|       res=INT_MIN16;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > (double) INT_MAX16)
 | |
|     {
 | |
|       res=INT_MAX16;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       res=(int16) (int) nr;
 | |
|   }
 | |
|   int2store(ptr,res);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_short::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   int16 res;
 | |
| 
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (nr < 0L && !unsigned_val)
 | |
|     {
 | |
|       res=0;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if ((ulonglong) nr > (ulonglong) UINT_MAX16)
 | |
|     {
 | |
|       res=(int16) UINT_MAX16;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       res=(int16) (uint16) nr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (nr < 0 && unsigned_val)
 | |
|       nr= UINT_MAX16+1;                         // Generate overflow
 | |
| 
 | |
|     if (nr < INT_MIN16)
 | |
|     {
 | |
|       res=INT_MIN16;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > (longlong) INT_MAX16)
 | |
|     {
 | |
|       res=INT_MAX16;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       res=(int16) nr;
 | |
|   }
 | |
|   int2store(ptr,res);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_short::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   short j;
 | |
|   j=sint2korr(ptr);
 | |
|   return unsigned_flag ? (double) (unsigned short) j : (double) j;
 | |
| }
 | |
| 
 | |
| longlong Field_short::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   short j;
 | |
|   j=sint2korr(ptr);
 | |
|   return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_short::val_str(String *val_buffer,
 | |
| 			     String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   short j= sint2korr(ptr);
 | |
|   long nr= unsigned_flag ? (long) (unsigned short) j : (long) j;
 | |
|   return val_str_from_long(val_buffer, 7, -10, nr);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_short::send(Protocol *protocol)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   Protocol_text *txt;
 | |
|   if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
 | |
|     return send_numeric_zerofill_str(txt, PROTOCOL_SEND_SHORT);
 | |
|   return protocol->store_short(Field_short::val_int());
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   short a,b;
 | |
|   a=sint2korr(a_ptr);
 | |
|   b=sint2korr(b_ptr);
 | |
| 
 | |
|   if (unsigned_flag)
 | |
|     return ((unsigned short) a < (unsigned short) b) ? -1 :
 | |
|     ((unsigned short) a > (unsigned short) b) ? 1 : 0;
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_short::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   if (unsigned_flag)
 | |
|     to[0] = ptr[1];
 | |
|   else
 | |
|     to[0] = (char) (ptr[1] ^ 128);              /* Revers signbit */
 | |
|   to[1]   = ptr[0];
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|   Field type medium int (3 byte)
 | |
| ****************************************************************************/
 | |
| 
 | |
| int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int store_tmp;
 | |
|   int error;
 | |
|   longlong rnd;
 | |
|   
 | |
|   error= get_int(cs, from, len, &rnd, UINT_MAX24, INT_MIN24, INT_MAX24);
 | |
|   store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd;
 | |
|   int3store(ptr, store_tmp);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_medium::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   nr=rint(nr);
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (nr < 0)
 | |
|     {
 | |
|       int3store(ptr,0);
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr >= (double) (long) (1L << 24))
 | |
|     {
 | |
|       uint32 tmp=(uint32) (1L << 24)-1L;
 | |
|       int3store(ptr,tmp);
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       int3store(ptr,(uint32) nr);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (nr < (double) INT_MIN24)
 | |
|     {
 | |
|       long tmp=(long) INT_MIN24;
 | |
|       int3store(ptr,tmp);
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > (double) INT_MAX24)
 | |
|     {
 | |
|       long tmp=(long) INT_MAX24;
 | |
|       int3store(ptr,tmp);
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       int3store(ptr,(long) nr);
 | |
|   }
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_medium::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
| 
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (nr < 0 && !unsigned_val)
 | |
|     {
 | |
|       int3store(ptr,0);
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24))
 | |
|     {
 | |
|       long tmp= (long) (1L << 24)-1L;
 | |
|       int3store(ptr,tmp);
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       int3store(ptr,(uint32) nr);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (nr < 0 && unsigned_val)
 | |
|       nr= (ulonglong) (long) (1L << 24);        // Generate overflow
 | |
| 
 | |
|     if (nr < (longlong) INT_MIN24)
 | |
|     {
 | |
|       long tmp= (long) INT_MIN24;
 | |
|       int3store(ptr,tmp);
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > (longlong) INT_MAX24)
 | |
|     {
 | |
|       long tmp=(long) INT_MAX24;
 | |
|       int3store(ptr,tmp);
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       int3store(ptr,(long) nr);
 | |
|   }
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_medium::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
 | |
|   return (double) j;
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_medium::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
 | |
|   return (longlong) j;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_medium::val_str(String *val_buffer,
 | |
| 			      String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   long nr= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
 | |
|   return val_str_from_long(val_buffer, 10, -10, nr);
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_int::val_str_from_long(String *val_buffer,
 | |
|                                      uint max_char_length,
 | |
|                                      int radix, long nr)
 | |
| {
 | |
|   CHARSET_INFO *cs= &my_charset_numeric;
 | |
|   uint length;
 | |
|   uint mlength= MY_MAX(field_length + 1, max_char_length * cs->mbmaxlen);
 | |
|   val_buffer->alloc(mlength);
 | |
|   char *to=(char*) val_buffer->ptr();
 | |
|   length= (uint) cs->long10_to_str(to, mlength, radix, nr);
 | |
|   val_buffer->length(length);
 | |
|   if (zerofill)
 | |
|     prepend_zeros(val_buffer); /* purecov: inspected */
 | |
|   val_buffer->set_charset(cs);
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_medium::send(Protocol *protocol)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   Protocol_text *txt;
 | |
|   if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
 | |
|     return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG);
 | |
|   return protocol->store_long(Field_medium::val_int());
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   long a,b;
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     a=uint3korr(a_ptr);
 | |
|     b=uint3korr(b_ptr);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     a=sint3korr(a_ptr);
 | |
|     b=sint3korr(b_ptr);
 | |
|   }
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_medium::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   if (unsigned_flag)
 | |
|     to[0] = ptr[2];
 | |
|   else
 | |
|     to[0] = (uchar) (ptr[2] ^ 128);		/* Revers signbit */
 | |
|   to[1] = ptr[1];
 | |
|   to[2] = ptr[0];
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** long int
 | |
| ****************************************************************************/
 | |
| 
 | |
| int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   long store_tmp;
 | |
|   int error;
 | |
|   longlong rnd;
 | |
|   
 | |
|   error= get_int(cs, from, len, &rnd, UINT_MAX32, INT_MIN32, INT_MAX32);
 | |
|   store_tmp= unsigned_flag ? (long) (ulonglong) rnd : (long) rnd;
 | |
|   int4store(ptr, store_tmp);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_long::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   int32 res;
 | |
|   nr=rint(nr);
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (nr < 0)
 | |
|     {
 | |
|       res=0;
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > (double) UINT_MAX32)
 | |
|     {
 | |
|       res= UINT_MAX32;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       res=(int32) (ulong) nr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (nr < (double) INT_MIN32)
 | |
|     {
 | |
|       res=(int32) INT_MIN32;
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > (double) INT_MAX32)
 | |
|     {
 | |
|       res=(int32) INT_MAX32;
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       res=(int32) (longlong) nr;
 | |
|   }
 | |
|   if (unlikely(error))
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
| 
 | |
|   int4store(ptr,res);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_long::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   int32 res;
 | |
| 
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (nr < 0 && !unsigned_val)
 | |
|     {
 | |
|       res=0;
 | |
|       error= 1;
 | |
|     }
 | |
|     else if ((ulonglong) nr >= (1LL << 32))
 | |
|     {
 | |
|       res=(int32) (uint32) ~0L;
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       res=(int32) (uint32) nr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (nr < 0 && unsigned_val)
 | |
|       nr= ((longlong) INT_MAX32) + 1;           // Generate overflow
 | |
|     if (nr < (longlong) INT_MIN32) 
 | |
|     {
 | |
|       res=(int32) INT_MIN32;
 | |
|       error= 1;
 | |
|     }
 | |
|     else if (nr > (longlong) INT_MAX32)
 | |
|     {
 | |
|       res=(int32) INT_MAX32;
 | |
|       error= 1;
 | |
|     }
 | |
|     else
 | |
|       res=(int32) nr;
 | |
|   }
 | |
|   if (unlikely(error))
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
| 
 | |
|   int4store(ptr,res);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_long::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int32 j;
 | |
|   j=sint4korr(ptr);
 | |
|   return unsigned_flag ? (double) (uint32) j : (double) j;
 | |
| }
 | |
| 
 | |
| longlong Field_long::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int32 j;
 | |
|   /* See the comment in Field_long::store(long long) */
 | |
|   DBUG_ASSERT(!table || table->in_use == current_thd);
 | |
|   j=sint4korr(ptr);
 | |
|   return unsigned_flag ? (longlong) (uint32) j : (longlong) j;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_long::val_str(String *val_buffer,
 | |
| 			    String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   long nr= unsigned_flag ? (long) uint4korr(ptr) : sint4korr(ptr);
 | |
|   return val_str_from_long(val_buffer, 12, unsigned_flag ? 10 : -10, nr);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_long::send(Protocol *protocol)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   Protocol_text *txt;
 | |
|   if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
 | |
|     return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG);
 | |
|   return protocol->store_long(Field_long::val_int());
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   int32 a,b;
 | |
|   a=sint4korr(a_ptr);
 | |
|   b=sint4korr(b_ptr);
 | |
|   if (unsigned_flag)
 | |
|     return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_long::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   if (unsigned_flag)
 | |
|     to[0] = ptr[3];
 | |
|   else
 | |
|     to[0] = (char) (ptr[3] ^ 128);              /* Revers signbit */
 | |
|   to[1]   = ptr[2];
 | |
|   to[2]   = ptr[1];
 | |
|   to[3]   = ptr[0];
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
|  Field type longlong int (8 bytes)
 | |
| ****************************************************************************/
 | |
| 
 | |
| int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   char *end;
 | |
|   ulonglong tmp;
 | |
| 
 | |
|   tmp= cs->strntoull10rnd(from, len, unsigned_flag, &end, &error);
 | |
|   if (unlikely(error == MY_ERRNO_ERANGE))
 | |
|   {
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     error= 1;
 | |
|   }
 | |
|   else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
 | |
|            check_int(cs, from, len, end, error))
 | |
|     error= 1;
 | |
|   else
 | |
|     error= 0;
 | |
|   int8store(ptr,tmp);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_longlong::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   Converter_double_to_longlong conv(nr, unsigned_flag);
 | |
| 
 | |
|   if (unlikely(conv.error()))
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
| 
 | |
|   int8store(ptr, conv.result());
 | |
|   return conv.error();
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_longlong::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
| 
 | |
|   if (unlikely(nr < 0))                         // Only possible error
 | |
|   {
 | |
|     /*
 | |
|       if field is unsigned and value is signed (< 0) or
 | |
|       if field is signed and value is unsigned we have an overflow
 | |
|     */
 | |
|     if (unsigned_flag != unsigned_val)
 | |
|     {
 | |
|       nr= unsigned_flag ? (ulonglong) 0 : (ulonglong) LONGLONG_MAX;
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|       error= 1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   int8store(ptr,nr);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_longlong::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   longlong j;
 | |
|   j=sint8korr(ptr);
 | |
|   /* The following is open coded to avoid a bug in gcc 3.3 */
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     ulonglong tmp= (ulonglong) j;
 | |
|     return ulonglong2double(tmp);
 | |
|   }
 | |
|   return (double) j;
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_longlong::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   longlong j;
 | |
|   j=sint8korr(ptr);
 | |
|   return j;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_longlong::val_str(String *val_buffer,
 | |
| 				String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   CHARSET_INFO *cs= &my_charset_numeric;
 | |
|   uint length;
 | |
|   uint mlength=MY_MAX(field_length+1,22*cs->mbmaxlen);
 | |
|   val_buffer->alloc(mlength);
 | |
|   char *to=(char*) val_buffer->ptr();
 | |
|   longlong j;
 | |
|   j=sint8korr(ptr);
 | |
| 
 | |
|   length=(uint) (cs->longlong10_to_str)(to, mlength,
 | |
| 		                        unsigned_flag ? 10 : -10, j);
 | |
|   val_buffer->length(length);
 | |
|   if (zerofill)
 | |
|     prepend_zeros(val_buffer);
 | |
|   val_buffer->set_charset(cs);
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_longlong::send(Protocol *protocol)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   Protocol_text *txt;
 | |
|   if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
 | |
|     return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONGLONG);
 | |
|   return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   longlong a,b;
 | |
|   a=sint8korr(a_ptr);
 | |
|   b=sint8korr(b_ptr);
 | |
|   if (unsigned_flag)
 | |
|     return ((ulonglong) a < (ulonglong) b) ? -1 :
 | |
|     ((ulonglong) a > (ulonglong) b) ? 1 : 0;
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_longlong::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   if (unsigned_flag)
 | |
|     to[0] = ptr[7];
 | |
|   else
 | |
|     to[0] = (char) (ptr[7] ^ 128);		/* Revers signbit */
 | |
|   to[1]   = ptr[6];
 | |
|   to[2]   = ptr[5];
 | |
|   to[3]   = ptr[4];
 | |
|   to[4]   = ptr[3];
 | |
|   to[5]   = ptr[2];
 | |
|   to[6]   = ptr[1];
 | |
|   to[7]   = ptr[0];
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_longlong::set_max()
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   set_notnull();
 | |
|   int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX);
 | |
| }
 | |
| 
 | |
| bool Field_longlong::is_max()
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     ulonglong j;
 | |
|     j= uint8korr(ptr);
 | |
|     return j == ULONGLONG_MAX;
 | |
|   }
 | |
|   longlong j;
 | |
|   j= sint8korr(ptr);
 | |
|   return j == LONGLONG_MAX;
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Floating-point numbers
 | |
|  */
 | |
| 
 | |
| /****************************************************************************
 | |
|   single precision float
 | |
| ****************************************************************************/
 | |
| 
 | |
| int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   int error;
 | |
|   Field_float::store(get_double(from, len, cs, &error));
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_float::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= truncate_double(&nr, field_length,
 | |
|                              not_fixed ? NOT_FIXED_DEC : dec,
 | |
|                              unsigned_flag, FLT_MAX);
 | |
|   if (unlikely(error))
 | |
|   {
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     if (error < 0)                                // Wrong double value
 | |
|     {
 | |
|       error= 1;
 | |
|       set_null();
 | |
|     }
 | |
|   }
 | |
|   float j= (float)nr;
 | |
| 
 | |
|   float4store(ptr,j);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_float::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   return Field_float::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
 | |
|                             (double) nr);
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_float::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   float j;
 | |
|   float4get(j,ptr);
 | |
|   return ((double) j);
 | |
| }
 | |
| 
 | |
| longlong Field_float::val_int(void)
 | |
| {
 | |
|   float j;
 | |
|   float4get(j,ptr);
 | |
|   return Converter_double_to_longlong(j, false).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_float::val_str(String *val_buffer,
 | |
| 			     String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
 | |
| 
 | |
|   if (Float(ptr).to_string(val_buffer, dec))
 | |
|   {
 | |
|     my_error(ER_OUT_OF_RESOURCES, MYF(0));
 | |
|     return val_buffer;
 | |
|   }
 | |
| 
 | |
|   if (zerofill)
 | |
|     prepend_zeros(val_buffer);
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   float a,b;
 | |
|   float4get(a,a_ptr);
 | |
|   float4get(b,b_ptr);
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| #define FLT_EXP_DIG (sizeof(float)*8-FLT_MANT_DIG)
 | |
| 
 | |
| void Field_float::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   float nr;
 | |
|   float4get(nr,ptr);
 | |
| 
 | |
|   uchar *tmp= to;
 | |
|   if (nr == (float) 0.0)
 | |
|   {						/* Change to zero string */
 | |
|     tmp[0]=(uchar) 128;
 | |
|     bzero((char*) tmp+1,sizeof(nr)-1);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
| #ifdef WORDS_BIGENDIAN
 | |
|     memcpy(tmp, &nr, sizeof(nr));
 | |
| #else
 | |
|     tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0];
 | |
| #endif
 | |
|     if (tmp[0] & 128)				/* Negative */
 | |
|     {						/* make complement */
 | |
|       uint i;
 | |
|       for (i=0 ; i < sizeof(nr); i++)
 | |
| 	tmp[i]= (uchar) (tmp[i] ^ (uchar) 255);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       ushort exp_part=(((ushort) tmp[0] << 8) | (ushort) tmp[1] |
 | |
| 		       (ushort) 32768);
 | |
|       exp_part+= (ushort) 1 << (16-1-FLT_EXP_DIG);
 | |
|       tmp[0]= (uchar) (exp_part >> 8);
 | |
|       tmp[1]= (uchar) exp_part;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_float::send(Protocol *protocol)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   Protocol_text *txt;
 | |
|   if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
 | |
|     return send_numeric_zerofill_str(txt, PROTOCOL_SEND_FLOAT);
 | |
|   return protocol->store_float((float) Field_float::val_real(), dec);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Save the field metadata for float fields.
 | |
| 
 | |
|    Saves the pack length in the first byte.
 | |
| 
 | |
|    @param   metadata_ptr   First byte of field metadata
 | |
| 
 | |
|    @returns number of bytes written to metadata_ptr
 | |
| */
 | |
| Binlog_type_info Field_float::binlog_type_info() const
 | |
| {
 | |
|   DBUG_ASSERT(Field_float::type() == binlog_type());
 | |
|   return Binlog_type_info(Field_float::type(), pack_length(), 1,
 | |
|                           binlog_signedness());
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
|   double precision floating point numbers
 | |
| ****************************************************************************/
 | |
| 
 | |
| int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   int error;
 | |
|   Field_double::store(get_double(from, len, cs, &error));
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_double::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= truncate_double(&nr, field_length,
 | |
|                              not_fixed ? NOT_FIXED_DEC : dec,
 | |
|                              unsigned_flag, DBL_MAX);
 | |
|   if (unlikely(error))
 | |
|   {
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     if (error < 0)                                // Wrong double value
 | |
|     {
 | |
|       error= 1;
 | |
|       set_null();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   float8store(ptr,nr);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_double::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   return Field_double::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
 | |
|                              (double) nr);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   If a field has fixed length, truncate the double argument pointed to by 'nr'
 | |
|   appropriately.
 | |
|   Also ensure that the argument is within [-max_value; max_value] range.
 | |
| 
 | |
|   return
 | |
|     0   ok
 | |
|     -1  Illegal double value
 | |
|     1   Value was truncated
 | |
| */
 | |
| 
 | |
| int truncate_double(double *nr, uint field_length, decimal_digits_t dec,
 | |
|                     bool unsigned_flag, double max_value)
 | |
| {
 | |
|   int error= 0;
 | |
|   double res= *nr;
 | |
|   
 | |
|   if (std::isnan(res))
 | |
|   {
 | |
|     *nr= 0;
 | |
|     return -1;
 | |
|   }
 | |
|   else if (unsigned_flag && res < 0)
 | |
|   {
 | |
|     *nr= 0;
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   if (dec < FLOATING_POINT_DECIMALS)
 | |
|   {
 | |
|     uint order= field_length - dec;
 | |
|     uint step= array_elements(log_10) - 1;
 | |
|     double max_value_by_dec= 1.0;
 | |
|     for (; order > step; order-= step)
 | |
|       max_value_by_dec*= log_10[step];
 | |
|     max_value_by_dec*= log_10[order];
 | |
|     max_value_by_dec-= 1.0 / log_10[dec];
 | |
|     set_if_smaller(max_value, max_value_by_dec);
 | |
| 
 | |
|     /* Check for infinity so we don't get NaN in calculations */
 | |
|     if (!std::isinf(res))
 | |
|     {
 | |
|       double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec];
 | |
|       res= floor(res) + tmp;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   if (res < -max_value)
 | |
|   {
 | |
|     res= -max_value;
 | |
|     error= 1;
 | |
|   }
 | |
|   else if (res > max_value)
 | |
|   {
 | |
|     res= max_value;
 | |
|     error= 1;
 | |
|   }
 | |
| 
 | |
|   *nr= res;
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Convert double to longlong / ulonglong.
 | |
|   If double is outside of the supported range,
 | |
|   adjust m_result and set m_error.
 | |
| 
 | |
|   @param nr             Number to convert
 | |
|   @param unsigned_flag  true if result is unsigned
 | |
| */
 | |
| 
 | |
| Value_source::
 | |
| Converter_double_to_longlong::Converter_double_to_longlong(double nr,
 | |
|                                                            bool unsigned_flag)
 | |
|   :m_error(false)
 | |
| {
 | |
|   nr= rint(nr);
 | |
|   if (unsigned_flag)
 | |
|   {
 | |
|     if (nr < 0)
 | |
|     {
 | |
|       m_result= 0;
 | |
|       m_error= true;
 | |
|     }
 | |
|     else if (nr >= (double) ULONGLONG_MAX)
 | |
|     {
 | |
|       m_result= ~(longlong) 0;
 | |
|       m_error= true;
 | |
|     }
 | |
|     else
 | |
|       m_result= (longlong) double2ulonglong(nr);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (nr <= (double) LONGLONG_MIN)
 | |
|     {
 | |
|       m_result= LONGLONG_MIN;
 | |
|       m_error= (nr < (double) LONGLONG_MIN);
 | |
|     }
 | |
|     else if (nr >= (double) (ulonglong) LONGLONG_MAX)
 | |
|     {
 | |
|       m_result= LONGLONG_MAX;
 | |
|       m_error= (nr > (double) LONGLONG_MAX);
 | |
|     }
 | |
|     else
 | |
|       m_result= (longlong) nr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void Value_source::
 | |
| Converter_double_to_longlong::push_warning(THD *thd,
 | |
|                                            double nr,
 | |
|                                            bool unsigned_flag)
 | |
| {
 | |
|   push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                       ER_DATA_OVERFLOW, ER_THD(thd, ER_DATA_OVERFLOW),
 | |
|                       ErrConvDouble(nr).ptr(),
 | |
|                       unsigned_flag ? "UNSIGNED INT" : "INT");
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint)
 | |
| {
 | |
|   return store(TIME_to_double(ltime));
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_double::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   double j;
 | |
|   float8get(j,ptr);
 | |
|   return j;
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_double::val_int_from_real(bool want_unsigned_result)
 | |
| {
 | |
|   Converter_double_to_longlong conv(val_real(), want_unsigned_result);
 | |
|   if (unlikely(!want_unsigned_result && conv.error()))
 | |
|     conv.push_warning(get_thd(), Field_double::val_real(), false);
 | |
|   return conv.result();
 | |
| }
 | |
| 
 | |
| 
 | |
| my_decimal *Field_real::val_decimal(my_decimal *decimal_value)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value);
 | |
|   return decimal_value;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_real::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   double nr= val_real();
 | |
|   return double_to_datetime_with_warn(get_thd(), nr, ltime, fuzzydate,
 | |
|                                       table->s, field_name.str);
 | |
| }
 | |
| 
 | |
| 
 | |
| Item *Field_real::get_equal_const_item(THD *thd, const Context &ctx,
 | |
|                                        Item *const_item)
 | |
| {
 | |
|   if (flags & ZEROFILL_FLAG)
 | |
|     return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item);
 | |
|   switch (ctx.subst_constraint()) {
 | |
|   case IDENTITY_SUBST:
 | |
|     if (const_item->decimal_scale() != Field_real::decimals())
 | |
|     {
 | |
|       double val= const_item->val_real();
 | |
|       return new (thd->mem_root) Item_float(thd, val, Field_real::decimals());
 | |
|     }
 | |
|     break;
 | |
|   case ANY_SUBST:
 | |
|     break;
 | |
|   }
 | |
|   return const_item;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_real::sql_type(String &res) const
 | |
| {
 | |
|   const Name name= type_handler()->name();
 | |
|   if (dec >= FLOATING_POINT_DECIMALS)
 | |
|   {
 | |
|     res.set_ascii(name.ptr(), name.length());
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     CHARSET_INFO *cs= res.charset();
 | |
|     res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
 | |
| 			    "%.*s(%d,%d)", (int) name.length(), name.ptr(),
 | |
| 			    (int) field_length,dec));
 | |
|   }
 | |
|   add_zerofill_and_unsigned(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_double::val_str(String *val_buffer,
 | |
| 			      String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
 | |
|   double nr;
 | |
|   float8get(nr,ptr);
 | |
| 
 | |
|   uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE;
 | |
|   if (val_buffer->alloc(to_length))
 | |
|   {
 | |
|     my_error(ER_OUT_OF_RESOURCES, MYF(0));
 | |
|     return val_buffer;
 | |
|   }
 | |
| 
 | |
|   char *to=(char*) val_buffer->ptr();
 | |
|   size_t len;
 | |
| 
 | |
|   if (dec >= FLOATING_POINT_DECIMALS)
 | |
|     len= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, to_length - 1, to, NULL);
 | |
|   else
 | |
|     len= my_fcvt(nr, dec, to, NULL);
 | |
| 
 | |
|   val_buffer->length((uint) len);
 | |
|   if (zerofill)
 | |
|     prepend_zeros(val_buffer);
 | |
|   val_buffer->set_charset(&my_charset_numeric);
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| bool Field_double::send(Protocol *protocol)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   Protocol_text *txt;
 | |
|   if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
 | |
|     return send_numeric_zerofill_str(txt, PROTOCOL_SEND_DOUBLE);
 | |
|   return protocol->store_double(Field_double::val_real(), dec);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   double a,b;
 | |
|   float8get(a,a_ptr);
 | |
|   float8get(b,b_ptr);
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| #define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)
 | |
| 
 | |
| /* The following should work for IEEE */
 | |
| 
 | |
| void Field_double::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   double nr;
 | |
|   float8get(nr,ptr);
 | |
|   change_double_for_sort(nr, to);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Save the field metadata for double fields.
 | |
| 
 | |
|    Saves the pack length in the first byte of the field metadata array
 | |
|    at index of *metadata_ptr.
 | |
| 
 | |
|    @param   metadata_ptr   First byte of field metadata
 | |
| 
 | |
|    @returns number of bytes written to metadata_ptr
 | |
| */
 | |
| Binlog_type_info Field_double::binlog_type_info() const
 | |
| {
 | |
|   DBUG_ASSERT(Field_double::type() == binlog_type());
 | |
|   return Binlog_type_info(Field_double::type(), pack_length(), 1,
 | |
|                           binlog_signedness());
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   TIMESTAMP type holds datetime values in range from 1970-01-01 00:00:01 UTC to 
 | |
|   2038-01-01 00:00:00 UTC stored as number of seconds since Unix 
 | |
|   Epoch in UTC.
 | |
|   
 | |
|   Actually SQL-99 says that we should allow niladic functions (like NOW())
 | |
|   as defaults for any field. The current limitation (only NOW() and only 
 | |
|   for TIMESTAMP and DATETIME fields) are because of restricted binary .frm
 | |
|   format and should go away in the future.
 | |
|   
 | |
|   Also because of this limitation of binary .frm format we use 5 different
 | |
|   unireg_check values with TIMESTAMP field to distinguish various cases of
 | |
|   DEFAULT or ON UPDATE values. These values are:
 | |
|   
 | |
|   TIMESTAMP_OLD_FIELD - old timestamp, if there was not any fields with
 | |
|     auto-set-on-update (or now() as default) in this table before, then this 
 | |
|     field has NOW() as default and is updated when row changes, else it is 
 | |
|     field which has 0 as default value and is not automatically updated.
 | |
|   TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update
 | |
|     automatically (TIMESTAMP DEFAULT NOW()), not used in Field since 10.2.2
 | |
|   TIMESTAMP_UN_FIELD - field which is set on update automatically but has not 
 | |
|     NOW() as default (but it may has 0 or some other const timestamp as 
 | |
|     default) (TIMESTAMP ON UPDATE NOW()).
 | |
|   TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on 
 | |
|     update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW()), not used in Field since 10.2.2
 | |
|   NONE - field which is not auto-set on update with some other than NOW() 
 | |
|     default value (TIMESTAMP DEFAULT 0).
 | |
| 
 | |
|   Note that TIMESTAMP_OLD_FIELDs are never created explicitly now, they are 
 | |
|   left only for preserving ability to read old tables. Such fields replaced 
 | |
|   with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE. This is 
 | |
|   because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for 
 | |
|   "TIMESTAMP DEFAULT 'Const'" field. (Old timestamps allowed such 
 | |
|   specification too but ignored default value for first timestamp, which of 
 | |
|   course is non-standard.) In most cases user won't notice any change, only
 | |
|   exception is different behavior of old/new timestamps during ALTER TABLE.
 | |
|  */
 | |
| 
 | |
| Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
 | |
|                                  uchar *null_ptr_arg, uchar null_bit_arg,
 | |
| 				 enum utype unireg_check_arg,
 | |
| 				 const LEX_CSTRING *field_name_arg,
 | |
| 				 TABLE_SHARE *share)
 | |
|   :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
 | |
|                   unireg_check_arg, field_name_arg)
 | |
| {
 | |
|   /* For 4.0 MYD and 4.0 InnoDB compatibility */
 | |
|   flags|= UNSIGNED_FLAG;
 | |
|   if (unireg_check != NONE)
 | |
|   {
 | |
|     /*
 | |
|       We mark the flag with TIMESTAMP_FLAG to indicate to the client that
 | |
|       this field will be automatically updated on insert.
 | |
|     */
 | |
|     flags|= TIMESTAMP_FLAG;
 | |
|     if (unireg_check != TIMESTAMP_DN_FIELD)
 | |
|       flags|= ON_UPDATE_NOW_FLAG;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| sql_mode_t
 | |
| Field_timestamp::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
 | |
| {
 | |
|   return expr->datetime_precision(thd) > decimals() ?
 | |
|          MODE_TIME_ROUND_FRACTIONAL : 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp::save_in_field(Field *to)
 | |
| {
 | |
|   ulong sec_part;
 | |
|   my_time_t ts= get_timestamp(&sec_part);
 | |
|   if (!ts && !sec_part)
 | |
|     return to->store_time_dec(Datetime::zero().get_mysql_time(), decimals());
 | |
|   return to->store_timestamp_dec(Timeval(ts, sec_part), decimals());
 | |
| }
 | |
| 
 | |
| my_time_t Field_timestamp0::get_timestamp(const uchar *pos,
 | |
|                                           ulong *sec_part) const
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   *sec_part= 0;
 | |
|   return sint4korr(pos);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_timestamp0::val_native(Native *to)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   my_time_t sec= (my_time_t) sint4korr(ptr);
 | |
|   return Timestamp_or_zero_datetime(Timestamp(sec, 0), sec == 0).
 | |
|            to_native(to, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt,
 | |
|                                              const ErrConv *str, int was_cut)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   static const Timestamp zero(0, 0);
 | |
| 
 | |
|   // Handle totally bad values
 | |
|   if (!dt->is_valid_datetime())
 | |
|   {
 | |
|     set_datetime_warning(WARN_DATA_TRUNCATED, str, "datetime", 1);
 | |
|     store_TIMESTAMP(zero);
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   // Handle values that do not need DATETIME to TIMESTAMP conversion
 | |
|   if (!dt->get_mysql_time()->month)
 | |
|   {
 | |
|     /*
 | |
|       Zero date is allowed by the current sql_mode. Store zero timestamp.
 | |
|       Return success or a warning about non-fatal truncation, e.g.:
 | |
|         INSERT INTO t1 (ts) VALUES ('0000-00-00 00:00:00 some tail');
 | |
|     */
 | |
|     store_TIMESTAMP(zero);
 | |
|     return store_TIME_return_code_with_warnings(was_cut, str, "datetime");
 | |
|   }
 | |
| 
 | |
|   // Convert DATETIME to TIMESTAMP
 | |
|   uint conversion_error;
 | |
|   const MYSQL_TIME *l_time= dt->get_mysql_time();
 | |
|   my_time_t timestamp= TIME_to_timestamp(thd, l_time, &conversion_error);
 | |
|   if (timestamp == 0 && l_time->second_part == 0)
 | |
|   {
 | |
|     set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, "datetime", 1);
 | |
|     store_TIMESTAMP(zero);
 | |
|     return 1; // date was fine but pointed to a DST gap
 | |
|   }
 | |
| 
 | |
|   // Store the value
 | |
|   DBUG_ASSERT(!dt->fraction_remainder(decimals()));
 | |
|   store_TIMESTAMP(Timestamp(timestamp, l_time->second_part));
 | |
| 
 | |
|   // Calculate return value and send warnings if needed
 | |
|   if (unlikely(conversion_error)) // e.g. DATETIME in the DST gap
 | |
|   {
 | |
|     set_datetime_warning(conversion_error, str, "datetime", 1);
 | |
|     return 1;
 | |
|   }
 | |
|   return store_TIME_return_code_with_warnings(was_cut, str, "datetime");
 | |
| }
 | |
| 
 | |
| 
 | |
| date_conv_mode_t Timestamp::sql_mode_for_timestamp(THD *thd)
 | |
| {
 | |
|   // We don't want to store invalid or fuzzy datetime values in TIMESTAMP
 | |
|   return date_conv_mode_t((thd->variables.sql_mode & MODE_NO_ZERO_DATE) |
 | |
|                           MODE_NO_ZERO_IN_DATE);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec)
 | |
| {
 | |
|   int warn;
 | |
|   ErrConvTime str(ltime);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(thd, &warn, ltime, Timestamp::DatetimeOptions(thd), decimals());
 | |
|   return store_TIME_with_warning(thd, &dt, &str, warn);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   ErrConvString str(from, len, cs);
 | |
|   THD *thd= get_thd();
 | |
|   MYSQL_TIME_STATUS st;
 | |
|   Datetime dt(thd, &st, from, len, cs, Timestamp::DatetimeOptions(thd), decimals());
 | |
|   return store_TIME_with_warning(thd, &dt, &str, st.warnings);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp::store(double nr)
 | |
| {
 | |
|   int error;
 | |
|   ErrConvDouble str(nr);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(thd, &error, nr, Timestamp::DatetimeOptions(thd), decimals());
 | |
|   return store_TIME_with_warning(thd, &dt, &str, error);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   int error;
 | |
|   Longlong_hybrid tmp(nr, unsigned_val);
 | |
|   ErrConvInteger str(tmp);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(&error, tmp, Timestamp::DatetimeOptions(thd));
 | |
|   return store_TIME_with_warning(thd, &dt, &str, error);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp::store_timestamp_dec(const timeval &tv, uint dec)
 | |
| {
 | |
|   int warn= 0;
 | |
|   time_round_mode_t mode= Datetime::default_round_mode(get_thd());
 | |
|   const Timestamp ts= Timestamp(tv).round(decimals(), mode, &warn);
 | |
|   store_TIMESTAMP(ts);
 | |
|   if (ts.tv().tv_sec == 0 && ts.tv().tv_usec == 0)
 | |
|   {
 | |
|     /*
 | |
|       The value {tv_sec==0, tv_usec==0} here means '1970-01-01 00:00:00 +00'.
 | |
|       It does not mean zero datetime! because store_timestamp_dec() knows
 | |
|       nothing about zero dates. It inserts only real timeval values.
 | |
|       Zero ts={0,0} here is possible in two scenarios:
 | |
|       - the passed tv was already {0,0} meaning '1970-01-01 00:00:00 +00'
 | |
|       - the passed tv had some microseconds but they were rounded/truncated
 | |
|         to zero: '1970-01-01 00:00:00.1 +00' -> '1970-01-01 00:00:00 +00'.
 | |
|       It does not matter whether rounding/truncation really happened.
 | |
|       In both cases the call for store_TIMESTAMP(ts) above re-interpreted
 | |
|       '1970-01-01 00:00:00 +00:00' to zero date. Return 1 no matter what
 | |
|       sql_mode is. Even if sql_mode allows zero dates, there is still a problem
 | |
|       here: '1970-01-01 00:00:00 +00' could not be stored as-is!
 | |
|     */
 | |
|     ErrConvString str(STRING_WITH_LEN("1970-01-01 00:00:00 +00:00"),
 | |
|                       system_charset_info);
 | |
|     set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, &str, "datetime", 1);
 | |
|     return 1; // '1970-01-01 00:00:00 +00' was converted to a zero date
 | |
|   }
 | |
|   if (warn)
 | |
|   {
 | |
|     /*
 | |
|       We're here if rounding would overflow outside of the supported TIMESTAMP
 | |
|       range, so truncation happened instead:
 | |
|         CREATE TABLE t1 (a TIMESTAMP(6));
 | |
|         INSERT INTO t1 VALUES ('maximum-possible-timestamp.999999');
 | |
|         ALTER TABLE t1 MODIFY a TIMESTAMP(5);
 | |
|         SELECT * FROM t1; --> 'maximum-possible-timestamp.99999' (5 digits)
 | |
|       Raise a warning, like DATETIME does for '9999-12-31 23:59:59.999999'.
 | |
|     */
 | |
|     set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp::zero_time_stored_return_code_with_warning()
 | |
| {
 | |
|   if (get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE)
 | |
|   {
 | |
|     ErrConvString s(
 | |
|       STRING_WITH_LEN("0000-00-00 00:00:00.000000") - (decimals() ? 6 - decimals() : 7),
 | |
|       system_charset_info);
 | |
|     set_datetime_warning(WARN_DATA_TRUNCATED, &s, "datetime", 1);
 | |
|     return 1;
 | |
|   }
 | |
|   return 0;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp::store_native(const Native &value)
 | |
| {
 | |
|   if (!value.length()) // Zero datetime
 | |
|   {
 | |
|     reset();
 | |
|     return zero_time_stored_return_code_with_warning();
 | |
|   }
 | |
|   /*
 | |
|     The exact second precision is not important here.
 | |
|     Field_timestamp*::store_timestamp_dec() do not use the "dec" parameter.
 | |
|     Passing TIME_SECOND_PART_DIGITS is OK.
 | |
|   */
 | |
|   return store_timestamp_dec(Timestamp(value).tv(), TIME_SECOND_PART_DIGITS);
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_timestamp::val_int(void)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
 | |
|     return 0;
 | |
| 
 | |
|   return ltime.year * 10000000000LL + ltime.month * 100000000LL +
 | |
|          ltime.day * 1000000L + ltime.hour * 10000L +
 | |
|          ltime.minute * 100 + ltime.second;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   uint32 temp, temp2;
 | |
|   uint dec;
 | |
|   char *to;
 | |
| 
 | |
|   val_buffer->alloc(field_length+1);
 | |
|   to= (char*) val_buffer->ptr();
 | |
|   val_buffer->length(field_length);
 | |
| 
 | |
|   if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
 | |
|   {				      /* Zero time is "000000" */
 | |
|     val_ptr->set(zero_timestamp, field_length, &my_charset_numeric);
 | |
|     return val_ptr;
 | |
|   }
 | |
|   val_buffer->set_charset(&my_charset_numeric);	// Safety
 | |
|    
 | |
|   temp= ltime.year % 100;
 | |
|   if (temp < YY_PART_YEAR - 1)
 | |
|   {
 | |
|     *to++= '2';
 | |
|     *to++= '0';
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     *to++= '1';
 | |
|     *to++= '9';
 | |
|   }
 | |
|   temp2=temp/10; temp=temp-temp2*10;
 | |
|   *to++= (char) ('0'+(char) (temp2));
 | |
|   *to++= (char) ('0'+(char) (temp));
 | |
|   *to++= '-';
 | |
|   temp=ltime.month;
 | |
|   temp2=temp/10; temp=temp-temp2*10;
 | |
|   *to++= (char) ('0'+(char) (temp2));
 | |
|   *to++= (char) ('0'+(char) (temp));
 | |
|   *to++= '-';
 | |
|   temp=ltime.day;
 | |
|   temp2=temp/10; temp=temp-temp2*10;
 | |
|   *to++= (char) ('0'+(char) (temp2));
 | |
|   *to++= (char) ('0'+(char) (temp));
 | |
|   *to++= ' ';
 | |
|   temp=ltime.hour;
 | |
|   temp2=temp/10; temp=temp-temp2*10;
 | |
|   *to++= (char) ('0'+(char) (temp2));
 | |
|   *to++= (char) ('0'+(char) (temp));
 | |
|   *to++= ':';
 | |
|   temp=ltime.minute;
 | |
|   temp2=temp/10; temp=temp-temp2*10;
 | |
|   *to++= (char) ('0'+(char) (temp2));
 | |
|   *to++= (char) ('0'+(char) (temp));
 | |
|   *to++= ':';
 | |
|   temp=ltime.second;
 | |
|   temp2=temp/10; temp=temp-temp2*10;
 | |
|   *to++= (char) ('0'+(char) (temp2));
 | |
|   *to++= (char) ('0'+(char) (temp));
 | |
|   *to= 0;
 | |
|   val_buffer->set_charset(&my_charset_numeric);
 | |
| 
 | |
|   if ((dec= decimals()))
 | |
|   {
 | |
|     ulong sec_part= (ulong) sec_part_shift(ltime.second_part, dec);
 | |
|     char *buf= const_cast<char*>(val_buffer->ptr() + MAX_DATETIME_WIDTH);
 | |
|     for (int i= dec; i > 0; i--, sec_part/= 10)
 | |
|     buf[i]= (char)(sec_part % 10) + '0';
 | |
|     buf[0]= '.';
 | |
|     buf[dec + 1]= 0;
 | |
|   }
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| Field_timestamp::validate_value_in_record(THD *thd, const uchar *record) const
 | |
| {
 | |
|   DBUG_ASSERT(!is_null_in_record(record));
 | |
|   ulong sec_part;
 | |
|   return !get_timestamp(ptr_in_record(record), &sec_part) && !sec_part &&
 | |
|          bool(sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_timestamp::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
 | |
| {
 | |
|   ulong sec_part;
 | |
|   my_time_t ts= get_timestamp(&sec_part);
 | |
|   return get_thd()->timestamp_to_TIME(ltime, ts, sec_part, fuzzydate);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_timestamp0::send(Protocol *protocol)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   Field_timestamp0::get_date(<ime, date_mode_t(0));
 | |
|   return protocol->store_datetime(<ime, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   int32 a,b;
 | |
|   a=sint4korr(a_ptr);
 | |
|   b=sint4korr(b_ptr);
 | |
|   return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_timestamp0::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   to[0] = ptr[3];
 | |
|   to[1] = ptr[2];
 | |
|   to[2] = ptr[1];
 | |
|   to[3] = ptr[0];
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp0::set_time()
 | |
| {
 | |
|   set_notnull();
 | |
|   store_TIMESTAMP(Timestamp(get_thd()->query_start(), 0));
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_timestamp::load_data_set_no_data(THD *thd, bool fixed_format)
 | |
| {
 | |
|   if (!maybe_null())
 | |
|   {
 | |
|     /*
 | |
|       Timestamp fields that are NOT NULL are autoupdated if there is no
 | |
|       corresponding value in the data file.
 | |
|     */
 | |
|     set_time();
 | |
|     set_has_explicit_value();
 | |
|     return false;
 | |
|   }
 | |
|   return Field::load_data_set_no_data(thd, fixed_format);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_timestamp::load_data_set_null(THD *thd)
 | |
| {
 | |
|   if (!maybe_null())
 | |
|   {
 | |
|     /*
 | |
|       Timestamp fields that are NOT NULL are autoupdated if there is no
 | |
|       corresponding value in the data file.
 | |
|     */
 | |
|     set_time();
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     reset();
 | |
|     set_null();
 | |
|   }
 | |
|   set_has_explicit_value(); // Do not auto-update this field
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifdef NOT_USED
 | |
| static void store_native(ulonglong num, uchar *to, uint bytes)
 | |
| {
 | |
|   switch(bytes) {
 | |
|   case 1: *to= (uchar)num;              break;
 | |
|   case 2: shortstore(to, (ushort)num);  break;
 | |
|   case 3: int3store(to, num); /* Sic!*/ break;
 | |
|   case 4: longstore(to, (ulong)num);    break;
 | |
|   case 8: longlongstore(to, num);       break;
 | |
|   default: DBUG_ASSERT(0);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static longlong read_native(const uchar *from, uint bytes)
 | |
| {
 | |
|   switch(bytes) {
 | |
|   case 1: return from[0];
 | |
|   case 2: { uint16 tmp; shortget(tmp, from); return tmp; }
 | |
|   case 3: return uint3korr(from);
 | |
|   case 4: { uint32 tmp; longget(tmp, from); return tmp; }
 | |
|   case 8: { longlong tmp; longlongget(tmp, from); return tmp; }
 | |
|   default: DBUG_ASSERT(0); return 0;
 | |
|   }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| void Field_timestamp_hires::store_TIMEVAL(const timeval &tv)
 | |
| {
 | |
|   mi_int4store(ptr, tv.tv_sec);
 | |
|   store_bigendian(sec_part_shift(tv.tv_usec, dec), ptr+4, sec_part_bytes(dec));
 | |
| }
 | |
| 
 | |
| my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos,
 | |
|                                                ulong *sec_part) const
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   *sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes(dec)), dec);
 | |
|   return mi_uint4korr(pos);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_timestamp_hires::val_native(Native *to)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   struct timeval tm;
 | |
|   tm.tv_sec= mi_uint4korr(ptr);
 | |
|   tm.tv_usec= (ulong) sec_part_unshift(read_bigendian(ptr+4, sec_part_bytes(dec)), dec);
 | |
|   return Timestamp_or_zero_datetime(Timestamp(tm), tm.tv_sec == 0).
 | |
|            to_native(to, dec);
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_timestamp_with_dec::val_real(void)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
 | |
|     return 0;
 | |
|   
 | |
|   return ltime.year * 1e10 + ltime.month * 1e8 +
 | |
|          ltime.day * 1e6 + ltime.hour * 1e4 +
 | |
|          ltime.minute * 1e2 + ltime.second + ltime.second_part*1e-6;
 | |
| }
 | |
| 
 | |
| my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, date_mode_t(0));
 | |
|   return TIME_to_my_decimal(<ime, d);
 | |
| }
 | |
|  
 | |
| int Field_timestamp::store_decimal(const my_decimal *d)
 | |
| {
 | |
|   int error;
 | |
|   THD *thd= get_thd();
 | |
|   ErrConvDecimal str(d);
 | |
|   Datetime dt(thd, &error, d, Timestamp::DatetimeOptions(thd), decimals());
 | |
|   return store_TIME_with_warning(thd, &dt, &str, error);
 | |
| }
 | |
| 
 | |
| int Field_timestamp_with_dec::set_time()
 | |
| {
 | |
|   THD *thd= get_thd();
 | |
|   set_notnull();
 | |
|   // Avoid writing microseconds into binlog for FSP=0
 | |
|   ulong msec= decimals() ? thd->query_start_sec_part() : 0;
 | |
|   store_TIMESTAMP(Timestamp(thd->query_start(), msec).trunc(decimals()));
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| bool Field_timestamp_with_dec::send(Protocol *protocol)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   Field_timestamp::get_date(<ime, date_mode_t(0));
 | |
|   return protocol->store_datetime(<ime, dec);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   int32 a,b;
 | |
|   ulong a_sec_part, b_sec_part;
 | |
|   a= mi_uint4korr(a_ptr);
 | |
|   a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes(dec));
 | |
|   b= mi_uint4korr(b_ptr);
 | |
|   b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes(dec));
 | |
|   return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 :
 | |
|           a_sec_part < b_sec_part  ? -1 :  a_sec_part > b_sec_part  ? 1 : 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_timestamp_with_dec::make_send_field(Send_field *field)
 | |
| {
 | |
|   Field::make_send_field(field);
 | |
|   field->decimals= dec;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*************************************************************
 | |
| ** MySQL-5.6 compatible TIMESTAMP(N)
 | |
| **************************************************************/
 | |
| 
 | |
| void Field_timestampf::store_TIMEVAL(const timeval &tm)
 | |
| {
 | |
|   my_timestamp_to_binary(&tm, ptr, dec);
 | |
| }
 | |
| 
 | |
| void Field_timestampf::set_max()
 | |
| {
 | |
|   DBUG_ENTER("Field_timestampf::set_max");
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS);
 | |
| 
 | |
|   set_notnull();
 | |
|   mi_int4store(ptr, TIMESTAMP_MAX_VALUE);
 | |
|   mi_int3store(ptr + 4, TIME_MAX_SECOND_PART);
 | |
| 
 | |
|   DBUG_VOID_RETURN;
 | |
| }
 | |
| 
 | |
| bool Field_timestampf::is_max()
 | |
| {
 | |
|   DBUG_ENTER("Field_timestampf::is_max");
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
| 
 | |
|   DBUG_RETURN(mi_sint4korr(ptr) == TIMESTAMP_MAX_VALUE &&
 | |
|               mi_sint3korr(ptr + 4) == TIME_MAX_SECOND_PART);
 | |
| }
 | |
| 
 | |
| my_time_t Field_timestampf::get_timestamp(const uchar *pos,
 | |
|                                           ulong *sec_part) const
 | |
| {
 | |
|   struct timeval tm;
 | |
|   my_timestamp_from_binary(&tm, pos, dec);
 | |
|   *sec_part= tm.tv_usec;
 | |
|   return tm.tv_sec;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_timestampf::val_native(Native *to)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   char zero[8]= "\0\0\0\0\0\0\0";
 | |
|   DBUG_ASSERT(pack_length () <= sizeof(zero));
 | |
|   // Check if it's '0000-00-00 00:00:00' rather than a real timestamp
 | |
|   if (!memcmp(ptr, zero, pack_length()))
 | |
|   {
 | |
|     to->length(0);
 | |
|     return false;
 | |
|   }
 | |
|   return Field::val_native(to);
 | |
| }
 | |
| 
 | |
| Binlog_type_info Field_timestampf::binlog_type_info() const
 | |
| {
 | |
|   return Binlog_type_info(Field_timestampf::binlog_type(), decimals(), 1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*************************************************************/
 | |
| sql_mode_t Field_temporal::can_handle_sql_mode_dependency_on_store() const
 | |
| {
 | |
|   return MODE_PAD_CHAR_TO_FULL_LENGTH;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_temporal::is_equal(const Column_definition &new_field) const
 | |
| {
 | |
|   return new_field.type_handler() == type_handler() &&
 | |
|          new_field.length == max_display_length();
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
 | |
|                                   const ErrConv *str, int was_cut,
 | |
|                                   const char *typestr)
 | |
| {
 | |
|   /*
 | |
|     error code logic:
 | |
|     MYSQL_TIME_WARN_TRUNCATED means that the value was not a date/time at all.
 | |
|       it will be stored as zero date/time.
 | |
|     MYSQL_TIME_WARN_OUT_OF_RANGE means that the value was a date/time,
 | |
|       that is, it was parsed as such, but the value was invalid.
 | |
| 
 | |
|     Also, MYSQL_TIME_WARN_TRUNCATED is used when storing a DATETIME in
 | |
|     a DATE field and non-zero time part is thrown away.
 | |
|   */
 | |
|   if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
 | |
|     set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, str, typestr, 1);
 | |
|   if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
 | |
|     set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, typestr, 1);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_temporal::sql_type_dec_comment(String &res,
 | |
|                                           const Name &name,
 | |
|                                           uint dec,
 | |
|                                           const Name &comment) const
 | |
| {
 | |
|   CHARSET_INFO *cs=res.charset();
 | |
|   res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
 | |
|                                 "%.*s(%u)%s%.*s%s",
 | |
|                                 (uint) name.length(), name.ptr(),
 | |
|                                 dec,
 | |
|                                 comment.length() ? " /* " : "",
 | |
|                                 (uint) comment.length(), comment.ptr(),
 | |
|                                 comment.length() ? " */" : ""));
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_temporal::sql_type_comment(String &res,
 | |
|                                       const Name &name,
 | |
|                                       const Name &comment) const
 | |
| {
 | |
|   CHARSET_INFO *cs=res.charset();
 | |
|   res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
 | |
|                                 "%.*s%s%.*s%s",
 | |
|                                 (uint) name.length(), name.ptr(),
 | |
|                                 comment.length() ? " /* " : "",
 | |
|                                 (uint) comment.length(), comment.ptr(),
 | |
|                                 comment.length() ? " */" : ""));
 | |
| }
 | |
| 
 | |
| 
 | |
| const Name & Field_temporal::type_version_mysql56()
 | |
| {
 | |
|   DBUG_EXECUTE_IF("sql_type", return Type_handler::version_mysql56(); );
 | |
|   static Name none(NULL, 0);
 | |
|   return none;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Store string into a date/time field
 | |
| 
 | |
|   RETURN
 | |
|     0  ok
 | |
|     1  Value was cut during conversion
 | |
|     2  value was out of range
 | |
|     3  Datetime value that was cut (warning level NOTE)
 | |
|        This is used by opt_range.cc:get_mm_leaf().
 | |
| */
 | |
| int Field_datetime::store_TIME_with_warning(const Datetime *dt,
 | |
|                                             const ErrConv *str,
 | |
|                                             int was_cut)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   // Handle totally bad values
 | |
|   if (!dt->is_valid_datetime())
 | |
|     return store_invalid_with_warning(str, was_cut, "datetime");
 | |
|   // Store the value
 | |
|   DBUG_ASSERT(!dt->fraction_remainder(decimals()));
 | |
|   store_datetime(*dt);
 | |
|   // Caclulate return value and send warnings if needed
 | |
|   return store_TIME_return_code_with_warnings(was_cut, str, "datetime");
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_datetime::store(const char *from, size_t len, CHARSET_INFO *cs)
 | |
| {
 | |
|   MYSQL_TIME_STATUS st;
 | |
|   ErrConvString str(from, len, cs);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(thd, &st, from, len, cs, Datetime::Options(thd), decimals());
 | |
|   return store_TIME_with_warning(&dt, &str, st.warnings);
 | |
| }
 | |
| 
 | |
| int Field_datetime::store(double nr)
 | |
| {
 | |
|   int error;
 | |
|   ErrConvDouble str(nr);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(thd, &error, nr, Datetime::Options(thd), decimals());
 | |
|   return store_TIME_with_warning(&dt, &str, error);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_datetime::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   int error;
 | |
|   Longlong_hybrid tmp(nr, unsigned_val);
 | |
|   ErrConvInteger str(tmp);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(&error, tmp, Datetime::Options(thd));
 | |
|   return store_TIME_with_warning(&dt, &str, error);
 | |
| }
 | |
| 
 | |
| int Field_datetime::store_time_dec(const MYSQL_TIME *ltime, uint dec)
 | |
| {
 | |
|   int error;
 | |
|   ErrConvTime str(ltime);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(thd, &error, ltime, Datetime::Options(thd), decimals());
 | |
|   return store_TIME_with_warning(&dt, &str, error);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_datetime::store_decimal(const my_decimal *d)
 | |
| {
 | |
|   int error;
 | |
|   ErrConvDecimal str(d);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime tm(thd, &error, d, Datetime::Options(thd), decimals());
 | |
|   return store_TIME_with_warning(&tm, &str, error);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| Field_temporal_with_date::validate_value_in_record(THD *thd,
 | |
|                                                    const uchar *record) const
 | |
| {
 | |
|   DBUG_ASSERT(!is_null_in_record(record));
 | |
|   MYSQL_TIME ltime;
 | |
|   return get_TIME(<ime, ptr_in_record(record), Datetime::Options(thd));
 | |
| }
 | |
| 
 | |
| 
 | |
| my_decimal *Field_temporal::val_decimal(my_decimal *d)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   if (get_date(<ime, date_mode_t(0)))
 | |
|   {
 | |
|     bzero(<ime, sizeof(ltime));
 | |
|     ltime.time_type= type_handler()->mysql_timestamp_type();
 | |
|   }
 | |
|   return TIME_to_my_decimal(<ime, d);
 | |
| }
 | |
| 
 | |
| 
 | |
| Data_type_compatibility
 | |
| Field_temporal::can_optimize_keypart_ref(const Item_bool_func *cond,
 | |
|                                          const Item *value) const
 | |
| {
 | |
|   // Field is of TIME_RESULT, which supersedes everything else.
 | |
|   return Data_type_compatibility::OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| Data_type_compatibility
 | |
| Field_temporal::can_optimize_group_min_max(const Item_bool_func *cond,
 | |
|                                            const Item *const_item) const
 | |
| {
 | |
|   // Field is of TIME_RESULT, which supersedes everything else.
 | |
|   return Data_type_compatibility::OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| Item *Field_temporal::get_equal_const_item_datetime(THD *thd,
 | |
|                                                     const Context &ctx,
 | |
|                                                     Item *const_item)
 | |
| {
 | |
|   switch (ctx.subst_constraint()) {
 | |
|   case IDENTITY_SUBST:
 | |
|     if ((const_item->field_type() != MYSQL_TYPE_DATETIME &&
 | |
|          const_item->field_type() != MYSQL_TYPE_TIMESTAMP) ||
 | |
|         const_item->decimals != decimals())
 | |
|     {
 | |
|       Datetime::Options opt(TIME_CONV_NONE, thd);
 | |
|       Datetime dt(thd, const_item, opt, decimals());
 | |
|       if (!dt.is_valid_datetime())
 | |
|         return NULL;
 | |
|       /*
 | |
|         See comments about truncation in the same place in
 | |
|         Field_time::get_equal_const_item().
 | |
|       */
 | |
|       return new (thd->mem_root) Item_datetime_literal(thd, &dt, decimals());
 | |
|     }
 | |
|     break;
 | |
|   case ANY_SUBST:
 | |
|     if (!is_temporal_type_with_date(const_item->field_type()))
 | |
|     {
 | |
|       Datetime dt= type_handler()->field_type() == MYSQL_TYPE_TIMESTAMP ?
 | |
|         Datetime(thd, const_item, Timestamp::DatetimeOptions(thd)) :
 | |
|         Datetime(thd, const_item, Datetime::Options_cmp(thd));
 | |
|       if (!dt.is_valid_datetime())
 | |
|         return NULL;
 | |
|       return new (thd->mem_root)
 | |
|         Item_datetime_literal_for_invalid_dates(thd, &dt,
 | |
|                                                 dt.get_mysql_time()->
 | |
|                                                 second_part ?
 | |
|                                                 TIME_SECOND_PART_DIGITS : 0);
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   return const_item;
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** time type
 | |
| ** In string context: HH:MM:SS
 | |
| ** In number context: HHMMSS
 | |
| ** Stored as a 3 byte unsigned int
 | |
| ****************************************************************************/
 | |
| sql_mode_t
 | |
| Field_time::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
 | |
| {
 | |
|   return expr->time_precision(thd) > decimals() ?
 | |
|          MODE_TIME_ROUND_FRACTIONAL : 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_time::store_TIME_with_warning(const Time *t,
 | |
|                                         const ErrConv *str, int warn)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   // Handle totally bad values
 | |
|   if (!t->is_valid_time())
 | |
|     return store_invalid_with_warning(str, warn, "time");
 | |
|   // Store the value
 | |
|   DBUG_ASSERT(!t->fraction_remainder(decimals()));
 | |
|   store_TIME(*t);
 | |
|   // Calculate return value and send warnings if needed
 | |
|   return store_TIME_return_code_with_warnings(warn, str, "time");
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_time0::store_TIME(const MYSQL_TIME *ltime)
 | |
| {
 | |
|   DBUG_ASSERT(ltime->year == 0);
 | |
|   DBUG_ASSERT(ltime->month == 0);
 | |
|   long tmp= (ltime->day*24L+ltime->hour)*10000L +
 | |
|             (ltime->minute*100+ltime->second);
 | |
|   if (ltime->neg)
 | |
|     tmp= -tmp;
 | |
|   int3store(ptr,tmp);
 | |
| }
 | |
| 
 | |
| int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   ErrConvString str(from, len, cs);
 | |
|   MYSQL_TIME_STATUS st;
 | |
|   THD *thd= get_thd();
 | |
|   /*
 | |
|     Unlike number-to-time conversion, we need to additionally pass
 | |
|     MODE_NO_ZERO_DATE here (if it presents in the current sql_mode):
 | |
|       SET sql_mode='STRICT_ALL_TABLES,NO_ZERO_DATE';
 | |
|       INSERT INTO t1 VALUES ('0000-00-00 00:00:00');  -- error
 | |
|       INSERT INTO t1 VALUES (0);                      -- ok
 | |
|     In the first INSERT we have a zero date.
 | |
|     In the second INSERT we don't have a zero date (it is just a zero time).
 | |
|   */
 | |
|   Time::Options opt(sql_mode_for_dates(thd), thd);
 | |
|   Time tm(thd, &st, from, len, cs, opt, decimals());
 | |
|   return store_TIME_with_warning(&tm, &str, st.warnings);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec)
 | |
| {
 | |
|   ErrConvTime str(ltime);
 | |
|   int warn;
 | |
|   Time tm(&warn, ltime, curdays, Time::Options(get_thd()), decimals());
 | |
|   return store_TIME_with_warning(&tm, &str, warn);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_time::store(double nr)
 | |
| {
 | |
|   ErrConvDouble str(nr);
 | |
|   int was_cut;
 | |
|   Time tm(get_thd(), &was_cut, nr, Time::Options(get_thd()), decimals());
 | |
|   return store_TIME_with_warning(&tm, &str, was_cut);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_time::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   Longlong_hybrid tmp(nr, unsigned_val);
 | |
|   ErrConvInteger str(tmp);
 | |
|   int was_cut;
 | |
|   THD *thd= get_thd();
 | |
|   /*
 | |
|     Need fractional digit truncation if nr overflows to '838:59:59.999999'.
 | |
|     The constructor used below will always truncate (never round).
 | |
|     We don't need to care to overwrite the default session rounding mode
 | |
|     from HALF_UP to TRUNCATE.
 | |
|   */
 | |
|   Time tm(thd, &was_cut, tmp, Time::Options(thd), decimals());
 | |
|   return store_TIME_with_warning(&tm, &str, was_cut);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_time::set_curdays(THD *thd)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   set_current_date(thd, <ime);
 | |
|   curdays= calc_daynr(ltime.year, ltime.month, ltime.day);
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field_time::new_key_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                                  uchar *new_ptr, uint32 length,
 | |
|                                  uchar *new_null_ptr, uint new_null_bit)
 | |
| {
 | |
|   THD *thd= get_thd();
 | |
|   Field_time *res=
 | |
|     (Field_time*) Field::new_key_field(root, new_table, new_ptr, length,
 | |
|                                        new_null_ptr, new_null_bit);
 | |
|   if (!(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) && res)
 | |
|     res->set_curdays(thd);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_time0::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   uint32 j= (uint32) uint3korr(ptr);
 | |
|   return (double) j;
 | |
| }
 | |
| 
 | |
| longlong Field_time0::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   return (longlong) sint3korr(ptr);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @note
 | |
|   This function is multi-byte safe as the result string is always of type
 | |
|   my_charset_bin
 | |
| */
 | |
| 
 | |
| String *Field_time::val_str(String *str,
 | |
| 			    String *unused __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, Datetime::Options(TIME_TIME_ONLY, get_thd()));
 | |
|   str->alloc(field_length + 1);
 | |
|   str->length(my_time_to_str(<ime, const_cast<char*>(str->ptr()), decimals()));
 | |
|   str->set_charset(&my_charset_numeric);
 | |
|   return str;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_time::check_zero_in_date_with_warn(date_mode_t fuzzydate)
 | |
| {
 | |
|   date_conv_mode_t tmp= date_conv_mode_t(fuzzydate);
 | |
|   if (!(tmp & TIME_TIME_ONLY) && (tmp & TIME_NO_ZERO_IN_DATE))
 | |
|   {
 | |
|     THD *thd= get_thd();
 | |
|     push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                         ER_WARN_DATA_OUT_OF_RANGE,
 | |
|                         ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name.str,
 | |
|                         thd->get_stmt_da()->current_row_for_warning());
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @note
 | |
|   Normally we would not consider 'time' as a valid date, but we allow
 | |
|   get_date() here to be able to do things like
 | |
|   DATE_FORMAT(time, "%l.%i %p")
 | |
| */
 | |
|  
 | |
| bool Field_time0::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
 | |
| {
 | |
|   if (check_zero_in_date_with_warn(fuzzydate))
 | |
|     return true;
 | |
|   long tmp=(long) sint3korr(ptr);
 | |
|   ltime->neg=0;
 | |
|   if (tmp < 0)
 | |
|   {
 | |
|     ltime->neg= 1;
 | |
|     tmp=-tmp;
 | |
|   }
 | |
|   ltime->year= ltime->month= ltime->day= 0;
 | |
|   ltime->hour=   (int) (tmp/10000);
 | |
|   tmp-=ltime->hour*10000;
 | |
|   ltime->minute= (int) tmp/100;
 | |
|   ltime->second= (int) tmp % 100;
 | |
|   ltime->second_part=0;
 | |
|   ltime->time_type= MYSQL_TIMESTAMP_TIME;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_time::store_native(const Native &value)
 | |
| {
 | |
|   Time t(value);
 | |
|   DBUG_ASSERT(t.is_valid_time());
 | |
|   store_TIME(t);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_time::val_native(Native *to)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, date_mode_t(0));
 | |
|   int warn;
 | |
|   return Time(&warn, <ime, 0).to_native(to, decimals());
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_time::send(Protocol *protocol)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd()));
 | |
|   return protocol->store_time(<ime, decimals());
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_time0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   int32 a,b;
 | |
|   a=(int32) sint3korr(a_ptr);
 | |
|   b=(int32) sint3korr(b_ptr);
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_time0::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   to[0] = (uchar) (ptr[2] ^ 128);
 | |
|   to[1] = ptr[1];
 | |
|   to[2] = ptr[0];
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_time_hires::reset()
 | |
| {
 | |
|   store_bigendian(zero_point, ptr, Field_time_hires::pack_length());
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_time_hires::store_TIME(const MYSQL_TIME *ltime)
 | |
| {
 | |
|   DBUG_ASSERT(ltime->year == 0);
 | |
|   DBUG_ASSERT(ltime->month == 0);
 | |
|   ulonglong packed= sec_part_shift(pack_time(ltime), dec) + zero_point;
 | |
|   store_bigendian(packed, ptr, Field_time_hires::pack_length());
 | |
| }
 | |
| 
 | |
| int Field_time::store_decimal(const my_decimal *d)
 | |
| {
 | |
|   ErrConvDecimal str(d);
 | |
|   int was_cut;
 | |
|   Time tm(get_thd(), &was_cut, d, Time::Options(get_thd()), decimals());
 | |
|   return store_TIME_with_warning(&tm, &str, was_cut);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_time::can_be_substituted_to_equal_item(const Context &ctx,
 | |
|                                                   const Item_equal *item_equal)
 | |
| {
 | |
|   DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT);
 | |
|   switch (ctx.subst_constraint()) {
 | |
|   case ANY_SUBST:
 | |
|     /*
 | |
|       A TIME field in a DATETIME comparison can be substituted to
 | |
|       Item_equal with TIME comparison.
 | |
| 
 | |
|         SET timestamp=UNIX_TIMESTAMP('2015-08-30 10:20:30');
 | |
|         CREATE OR REPLACE TABLE t1 (a TIME);
 | |
|         INSERT INTO t1 VALUES ('00:00:00'),('00:00:01');
 | |
|         SELECT * FROM t1 WHERE a>=TIMESTAMP'2015-08-30 00:00:00'
 | |
|                          AND a='00:00:00';
 | |
| 
 | |
|       The above query can be simplified to:
 | |
|         SELECT * FROM t1 WHERE TIME'00:00:00'>=TIMESTAMP'2015-08-30 00:00:00'
 | |
|                            AND a='00:00:00';
 | |
|       And further to:
 | |
|         SELECT * FROM t1 WHERE a=TIME'00:00:00';
 | |
|     */
 | |
|     if (ctx.compare_type_handler() == &type_handler_datetime &&
 | |
|         item_equal->compare_type_handler() == &type_handler_time)
 | |
|       return true;
 | |
|     return ctx.compare_type_handler() == item_equal->compare_type_handler();
 | |
|   case IDENTITY_SUBST:
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
 | |
|                                        Item *const_item)
 | |
| {
 | |
|   /*
 | |
|     Old mode conversion from DATETIME with non-zero YYYYMMDD part
 | |
|     to TIME works very inconsistently. Possible variants:
 | |
|     - truncate the YYYYMMDD part
 | |
|     - add (MM*33+DD)*24 to hours
 | |
|     - add (MM*31+DD)*24 to hours
 | |
|     Let's disallow propagation of DATETIME with non-zero YYYYMMDD
 | |
|     as an equal constant for a TIME field.
 | |
|   */
 | |
|   Time::datetime_to_time_mode_t mode=
 | |
|     (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) ?
 | |
|     Time::DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY :
 | |
|     Time::DATETIME_TO_TIME_MINUS_CURRENT_DATE;
 | |
| 
 | |
|   switch (ctx.subst_constraint()) {
 | |
|   case ANY_SUBST:
 | |
|     if (const_item->field_type() != MYSQL_TYPE_TIME)
 | |
|     {
 | |
|       // Get the value of const_item with conversion from DATETIME to TIME
 | |
|       Time tm(get_thd(), const_item, Time::Options_cmp(thd, mode));
 | |
|       if (!tm.is_valid_time())
 | |
|         return NULL;
 | |
|       /*
 | |
|         Replace a DATE/DATETIME constant to a TIME constant:
 | |
|           WHERE LENGTH(time_column)=8
 | |
|             AND time_column=TIMESTAMP'2015-08-30 10:20:30';
 | |
|         to:
 | |
|           WHERE LENGTH(time_column)=10
 | |
|             AND time_column=TIME'10:20:30'
 | |
| 
 | |
|         (assuming CURRENT_DATE is '2015-08-30'
 | |
|       */
 | |
|       return new (thd->mem_root) Item_time_literal(thd, &tm,
 | |
|                                                    tm.get_mysql_time()->
 | |
|                                                    second_part ?
 | |
|                                                    TIME_SECOND_PART_DIGITS :
 | |
|                                                    0);
 | |
|     }
 | |
|     break;
 | |
|    case IDENTITY_SUBST:
 | |
|     if (const_item->field_type() != MYSQL_TYPE_TIME ||
 | |
|         const_item->decimals != decimals())
 | |
|     {
 | |
|       /*
 | |
|         Note, the value returned in "ltime" can have more fractional
 | |
|         digits that decimals(). The Item_time_literal constructor will
 | |
|         truncate these digits. We could disallow propagation is such
 | |
|         cases, but it's still useful (and safe) to optimize:
 | |
|           WHERE time0_column='00:00:00.123' AND LENGTH(a)=12
 | |
|         to
 | |
|           WHERE time0_column='00:00:00.123' AND LENGTH(TIME'00:00:00')=12
 | |
|         and then to
 | |
|           WHERE FALSE
 | |
|         The original WHERE would do the full table scan (in case of no keys).
 | |
|         The optimized WHERE will return with "Impossible WHERE", without
 | |
|         having to do the full table scan.
 | |
|       */
 | |
|       Time tm(thd, const_item, Time::Options(TIME_TIME_ONLY, thd, mode),
 | |
|               decimals());
 | |
|       if (!tm.is_valid_time())
 | |
|         return NULL;
 | |
|       return new (thd->mem_root) Item_time_literal(thd, &tm, decimals());
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   return const_item;
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_time_with_dec::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd()));
 | |
|   longlong val= TIME_to_ulonglong_time(<ime);
 | |
|   return ltime.neg ? -val : val;
 | |
| }
 | |
| 
 | |
| double Field_time_with_dec::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd()));
 | |
|   return TIME_to_double(<ime);
 | |
| }
 | |
| 
 | |
| bool Field_time_hires::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
 | |
| {
 | |
|   if (check_zero_in_date_with_warn(fuzzydate))
 | |
|     return true;
 | |
|   uint32 len= pack_length();
 | |
|   longlong packed= read_bigendian(ptr, len);
 | |
| 
 | |
|   packed= sec_part_unshift(packed - zero_point, dec);
 | |
| 
 | |
|   unpack_time(packed, ltime, MYSQL_TIMESTAMP_TIME);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_time_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   ulonglong a=read_bigendian(a_ptr, Field_time_hires::pack_length());
 | |
|   ulonglong b=read_bigendian(b_ptr, Field_time_hires::pack_length());
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_time_hires::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(length == Field_time_hires::pack_length());
 | |
|   memcpy(to, ptr, length);
 | |
|   to[0]^= 128;
 | |
| }
 | |
| 
 | |
| void Field_time_with_dec::make_send_field(Send_field *field)
 | |
| {
 | |
|   Field::make_send_field(field);
 | |
|   field->decimals= dec;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
| ** time type with fsp (MySQL-5.6 version)
 | |
| ** In string context: HH:MM:SS.FFFFFF
 | |
| ** In number context: HHMMSS.FFFFFF
 | |
| ****************************************************************************/
 | |
| 
 | |
| int Field_timef::reset()
 | |
| {
 | |
|   my_time_packed_to_binary(0, ptr, dec);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void Field_timef::store_TIME(const MYSQL_TIME *ltime)
 | |
| {
 | |
|   longlong tmp= TIME_to_longlong_time_packed(ltime);
 | |
|   my_time_packed_to_binary(tmp, ptr, dec);
 | |
| }
 | |
| 
 | |
| bool Field_timef::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
 | |
| {
 | |
|   if (check_zero_in_date_with_warn(fuzzydate))
 | |
|     return true;
 | |
|   longlong tmp= my_time_packed_from_binary(ptr, dec);
 | |
|   TIME_from_longlong_time_packed(ltime, tmp);
 | |
|   return false;
 | |
| }
 | |
| Binlog_type_info Field_timef::binlog_type_info() const
 | |
| {
 | |
|   return Binlog_type_info(Field_timef::binlog_type(), decimals(), 1);
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_timef::val_time_packed(THD *thd)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   longlong tmp= my_time_packed_from_binary(ptr, dec);
 | |
|   MYSQL_TIME ltime;
 | |
|   TIME_from_longlong_time_packed(<ime, tmp);
 | |
|   return pack_time(<ime);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_timef::store_native(const Native &value)
 | |
| {
 | |
|   DBUG_ASSERT(value.length() == my_time_binary_length(dec));
 | |
|   DBUG_ASSERT(Time(value).is_valid_time());
 | |
|   memcpy(ptr, value.ptr(), value.length());
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_timef::val_native(Native *to)
 | |
| {
 | |
|   uint32 binlen= my_time_binary_length(dec);
 | |
|   return to->copy((const char*) ptr, binlen);
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** year type
 | |
| ** Save in a byte the year 0, 1901->2155
 | |
| ** Can handle 2 byte or 4 byte years!
 | |
| ****************************************************************************/
 | |
| 
 | |
| int Field_year::store(const char *from, size_t len,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   THD *thd= get_thd();
 | |
|   char *end;
 | |
|   int error;
 | |
|   longlong nr= cs->strntoull10rnd(from, len, 0, &end, &error);
 | |
| 
 | |
|   if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155 || 
 | |
|       error == MY_ERRNO_ERANGE)
 | |
|   {
 | |
|     *ptr=0;
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   if (thd->count_cuted_fields <= CHECK_FIELD_EXPRESSION && error == MY_ERRNO_EDOM)
 | |
|   {
 | |
|     *ptr= 0;
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION && 
 | |
|       (error= check_int(cs, from, len, end, error)))
 | |
|   {
 | |
|     if (unlikely(error == 1)  /* empty or incorrect string */)
 | |
|     {
 | |
|       *ptr= 0;
 | |
|       return 1;
 | |
|     }
 | |
|     error= 1;
 | |
|   }
 | |
| 
 | |
|   if (nr != 0 || len != 4)
 | |
|   {
 | |
|     if (nr < YY_PART_YEAR)
 | |
|       nr+=100;					// 2000 - 2069
 | |
|     else if (nr > 1900)
 | |
|       nr-= 1900;
 | |
|   }
 | |
|   *ptr= (char) (uchar) nr;
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_year::store(double nr)
 | |
| {
 | |
|   if (nr < 0.0 || nr > 2155.0)
 | |
|   {
 | |
|     (void) Field_year::store((longlong) -1, FALSE);
 | |
|     return 1;
 | |
|   }
 | |
|   return Field_year::store((longlong) nr, FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_year::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155)
 | |
|   {
 | |
|     *ptr= 0;
 | |
|     set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     return 1;
 | |
|   }
 | |
|   if (nr != 0 || field_length != 4)		// 0000 -> 0; 00 -> 2000
 | |
|   {
 | |
|     if (nr < YY_PART_YEAR)
 | |
|       nr+=100;					// 2000 - 2069
 | |
|     else if (nr > 1900)
 | |
|       nr-= 1900;
 | |
|   }
 | |
|   *ptr= (char) (uchar) nr;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_year::store_time_dec(const MYSQL_TIME *ltime, uint)
 | |
| {
 | |
|   ErrConvTime str(ltime);
 | |
|   if (Field_year::store(ltime->year, 0))
 | |
|     return 1;
 | |
| 
 | |
|   const char *typestr= Temporal::type_name_by_timestamp_type(ltime->time_type);
 | |
|   set_datetime_warning(WARN_DATA_TRUNCATED, &str, typestr, 1);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| bool Field_year::send(Protocol *protocol)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   Protocol_text *txt;
 | |
|   if ((txt= dynamic_cast<Protocol_text*>(protocol)))
 | |
|     return send_numeric_zerofill_str(txt, PROTOCOL_SEND_SHORT);
 | |
|   ulonglong tmp= Field_year::val_int();
 | |
|   return protocol->store_short(tmp);
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_year::val_real(void)
 | |
| {
 | |
|   return (double) Field_year::val_int();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_year::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   DBUG_ASSERT(field_length == 2 || field_length == 4);
 | |
|   int tmp= (int) ptr[0];
 | |
|   if (field_length != 4)
 | |
|     tmp%=100;					// Return last 2 char
 | |
|   else if (tmp)
 | |
|     tmp+=1900;
 | |
|   return (longlong) tmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_year::val_str(String *val_buffer,
 | |
| 			    String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(field_length < 5);
 | |
|   val_buffer->alloc(5);
 | |
|   val_buffer->length(field_length);
 | |
|   char *to=(char*) val_buffer->ptr();
 | |
|   sprintf(to,field_length == 2 ? "%02d" : "%04d",(int) Field_year::val_int());
 | |
|   val_buffer->set_charset(&my_charset_numeric);
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_year::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
 | |
| {
 | |
|   int tmp= (int) ptr[0];
 | |
|   if (tmp || field_length != 4)
 | |
|     tmp+= 1900;
 | |
|   return int_to_datetime_with_warn(get_thd(),
 | |
|                                    Longlong_hybrid(tmp * 10000, true),
 | |
|                                    ltime, fuzzydate, table->s, field_name.str);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_year::sql_type(String &res) const
 | |
| {
 | |
|   CHARSET_INFO *cs=res.charset();
 | |
|   res.length(cs->cset->snprintf(cs,(char*)res.ptr(),res.alloced_length(),
 | |
| 			  "year(%d)",(int) field_length));
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************/
 | |
| 
 | |
| int Field_date_common::store_TIME_with_warning(const Datetime *dt,
 | |
|                                                const ErrConv *str,
 | |
|                                                int was_cut)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   // Handle totally bad values
 | |
|   if (!dt->is_valid_datetime())
 | |
|     return store_invalid_with_warning(str, was_cut, "date");
 | |
|   // Store the value
 | |
|   if (!dt->hhmmssff_is_zero())
 | |
|     was_cut|= MYSQL_TIME_NOTE_TRUNCATED;
 | |
|   store_datetime(*dt);
 | |
|   // Caclulate return value and send warnings if needed
 | |
|   return store_TIME_return_code_with_warnings(was_cut, str, "date");
 | |
| }
 | |
| 
 | |
| int Field_date_common::store(const char *from, size_t len, CHARSET_INFO *cs)
 | |
| {
 | |
|   MYSQL_TIME_STATUS st;
 | |
|   ErrConvString str(from, len, cs);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(thd, &st, from, len, cs, Date::Options(thd), 0);
 | |
|   return store_TIME_with_warning(&dt, &str, st.warnings);
 | |
| }
 | |
| 
 | |
| int Field_date_common::store(double nr)
 | |
| {
 | |
|   int error;
 | |
|   ErrConvDouble str(nr);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(thd, &error, nr, Date::Options(thd), 0);
 | |
|   return store_TIME_with_warning(&dt, &str, error);
 | |
| }
 | |
| 
 | |
| int Field_date_common::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   int error;
 | |
|   Longlong_hybrid tmp(nr, unsigned_val);
 | |
|   ErrConvInteger str(tmp);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(&error, tmp, Date::Options(thd));
 | |
|   return store_TIME_with_warning(&dt, &str, error);
 | |
| }
 | |
| 
 | |
| int Field_date_common::store_time_dec(const MYSQL_TIME *ltime, uint dec)
 | |
| {
 | |
|   int error;
 | |
|   ErrConvTime str(ltime);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime dt(thd, &error, ltime, Date::Options(thd), 0);
 | |
|   return store_TIME_with_warning(&dt, &str, error);
 | |
| }
 | |
| 
 | |
| int Field_date_common::store_decimal(const my_decimal *d)
 | |
| {
 | |
|   int error;
 | |
|   ErrConvDecimal str(d);
 | |
|   THD *thd= get_thd();
 | |
|   Datetime tm(thd, &error, d, Date::Options(thd), 0);
 | |
|   return store_TIME_with_warning(&tm, &str, error);
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** date type
 | |
| ** In string context: YYYY-MM-DD
 | |
| ** In number context: YYYYMMDD
 | |
| ** Stored as a 4 byte unsigned int
 | |
| ****************************************************************************/
 | |
| 
 | |
| void Field_date::store_TIME(const MYSQL_TIME *ltime)
 | |
| {
 | |
|   uint tmp= ltime->year*10000L + ltime->month*100+ltime->day;
 | |
|   int4store(ptr,tmp);
 | |
| }
 | |
| 
 | |
| bool Field_date::send(Protocol *protocol)
 | |
| {
 | |
|   longlong tmp= Field_date::val_int();
 | |
|   MYSQL_TIME tm;
 | |
|   tm.year= (uint32) tmp/10000L % 10000;
 | |
|   tm.month= (uint32) tmp/100 % 100;
 | |
|   tm.day= (uint32) tmp % 100;
 | |
|   return protocol->store_date(&tm);
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_date::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int32 j;
 | |
|   j=sint4korr(ptr);
 | |
|   return (double) (uint32) j;
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_date::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int32 j;
 | |
|   j=sint4korr(ptr);
 | |
|   return (longlong) (uint32) j;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_date::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
 | |
|                           date_mode_t fuzzydate) const
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int32 tmp= sint4korr(pos);
 | |
|   ltime->year= (int) ((uint32) tmp/10000L % 10000);
 | |
|   ltime->month= (int) ((uint32) tmp/100 % 100);
 | |
|   ltime->day= (int) ((uint32) tmp % 100);
 | |
|   ltime->time_type= MYSQL_TIMESTAMP_DATE;
 | |
|   ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0;
 | |
|   return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_date::val_str(String *val_buffer,
 | |
| 			    String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   get_TIME(<ime, ptr, date_mode_t(0));
 | |
|   val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
 | |
|   uint length= (uint) my_date_to_str(<ime,
 | |
|                                      const_cast<char*>(val_buffer->ptr()));
 | |
|   val_buffer->length(length);
 | |
|   val_buffer->set_charset(&my_charset_numeric);
 | |
| 
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_date::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   int32 a,b;
 | |
|   a=sint4korr(a_ptr);
 | |
|   b=sint4korr(b_ptr);
 | |
|   return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_date::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   to[0] = ptr[3];
 | |
|   to[1] = ptr[2];
 | |
|   to[2] = ptr[1];
 | |
|   to[3] = ptr[0];
 | |
| }
 | |
| 
 | |
| void Field_date::sql_type(String &res) const
 | |
| {
 | |
|   res.set_ascii(STRING_WITH_LEN("date"));
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** The new date type
 | |
| ** This is identical to the old date type, but stored on 3 bytes instead of 4
 | |
| ** In number context: YYYYMMDD
 | |
| ****************************************************************************/
 | |
| 
 | |
| void Field_newdate::store_TIME(const MYSQL_TIME *ltime)
 | |
| {
 | |
|   uint tmp= ltime->year*16*32 + ltime->month*32+ltime->day;
 | |
|   int3store(ptr,tmp);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_newdate::send(Protocol *protocol)
 | |
| {
 | |
|   MYSQL_TIME tm;
 | |
|   Field_newdate::get_date(&tm, date_mode_t(0));
 | |
|   return protocol->store_date(&tm);
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_newdate::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   return (double) Field_newdate::val_int();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_newdate::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   ulong j= uint3korr(ptr);
 | |
|   j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L;
 | |
|   return (longlong) j;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_newdate::val_str(String *val_buffer,
 | |
| 			       String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   val_buffer->alloc(field_length+1);
 | |
|   val_buffer->length(field_length);
 | |
|   uint32 tmp=(uint32) uint3korr(ptr);
 | |
|   int part;
 | |
|   char *pos=(char*) val_buffer->ptr()+10;
 | |
| 
 | |
|   /* Open coded to get more speed */
 | |
|   *pos--=0;					// End NULL
 | |
|   part=(int) (tmp & 31);
 | |
|   *pos--= (char) ('0'+part%10);
 | |
|   *pos--= (char) ('0'+part/10);
 | |
|   *pos--= '-';
 | |
|   part=(int) (tmp >> 5 & 15);
 | |
|   *pos--= (char) ('0'+part%10);
 | |
|   *pos--= (char) ('0'+part/10);
 | |
|   *pos--= '-';
 | |
|   part=(int) (tmp >> 9);
 | |
|   *pos--= (char) ('0'+part%10); part/=10;
 | |
|   *pos--= (char) ('0'+part%10); part/=10;
 | |
|   *pos--= (char) ('0'+part%10); part/=10;
 | |
|   *pos=   (char) ('0'+part);
 | |
|   val_buffer->set_charset(&my_charset_numeric);
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_newdate::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
 | |
|                              date_mode_t fuzzydate) const
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   uint32 tmp=(uint32) uint3korr(pos);
 | |
|   ltime->day=   tmp & 31;
 | |
|   ltime->month= (tmp >> 5) & 15;
 | |
|   ltime->year=  (tmp >> 9);
 | |
|   ltime->time_type= MYSQL_TIMESTAMP_DATE;
 | |
|   ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0;
 | |
|   return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_newdate::val_datetime_packed(THD *thd)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   Field_newdate::get_date(<ime, date_mode_t(0));
 | |
|   return pack_time(<ime);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   uint32 a,b;
 | |
|   a=(uint32) uint3korr(a_ptr);
 | |
|   b=(uint32) uint3korr(b_ptr);
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_newdate::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   to[0] = ptr[2];
 | |
|   to[1] = ptr[1];
 | |
|   to[2] = ptr[0];
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_newdate::sql_type(String &res) const
 | |
| {
 | |
|   res.set_ascii(STRING_WITH_LEN("date"));
 | |
| }
 | |
| 
 | |
| 
 | |
| Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx,
 | |
|                                           Item *const_item)
 | |
| {
 | |
|   switch (ctx.subst_constraint()) {
 | |
|   case ANY_SUBST:
 | |
|     if (!is_temporal_type_with_date(const_item->field_type()))
 | |
|     {
 | |
|       /*
 | |
|         DATE is compared to DATETIME-alike non-temporal values
 | |
|         (such as VARCHAR, DECIMAL) as DATETIME, e.g.:
 | |
|           WHERE date_column=20010101235959.0000009
 | |
|         So here we convert the constant to DATETIME normally.
 | |
|         In case if TIME_ROUND_FRACTIONAL is enabled, nanoseconds will round.
 | |
|       */
 | |
|       Datetime dt(thd, const_item, Datetime::Options_cmp(thd));
 | |
|       if (!dt.is_valid_datetime())
 | |
|         return NULL;
 | |
|       /*
 | |
|         Replace the constant to a DATE or DATETIME constant.
 | |
|         Example:
 | |
|           WHERE LENGTH(date_column)=10
 | |
|             AND date_column=TIME'10:20:30';
 | |
|         to:
 | |
|           WHERE LENGTH(date_column)=10
 | |
|             AND date_column=TIMESTAMP'2015-08-30 10:20:30'
 | |
| 
 | |
|         (assuming CURRENT_DATE is '2015-08-30'
 | |
|       */
 | |
|       if (!dt.hhmmssff_is_zero())
 | |
|         return new (thd->mem_root)
 | |
|           Item_datetime_literal_for_invalid_dates(thd, &dt,
 | |
|                                                   dt.get_mysql_time()->
 | |
|                                                     second_part ?
 | |
|                                                   TIME_SECOND_PART_DIGITS : 0);
 | |
|       Date d(&dt);
 | |
|       return new (thd->mem_root) Item_date_literal_for_invalid_dates(thd, &d);
 | |
|     }
 | |
|     break;
 | |
|   case IDENTITY_SUBST:
 | |
|     if (const_item->field_type() != MYSQL_TYPE_DATE)
 | |
|     {
 | |
|       /*
 | |
|         DATE is compared to non-temporal as DATETIME.
 | |
|         We need to convert to DATETIME first, taking into account the
 | |
|         current session rounding mode (even though this is IDENTITY_SUBSTS!),
 | |
|         then convert the result to DATE.
 | |
|       */
 | |
|       Datetime dt(thd, const_item, Datetime::Options(TIME_CONV_NONE, thd));
 | |
|       if (!dt.is_valid_datetime())
 | |
|         return NULL;
 | |
|       Date d(&dt);
 | |
|       return new (thd->mem_root) Item_date_literal(thd, &d);
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   return const_item;
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** datetime type
 | |
| ** In string context: YYYY-MM-DD HH:MM:DD
 | |
| ** In number context: YYYYMMDDHHMMDD
 | |
| ** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int.
 | |
| ****************************************************************************/
 | |
| 
 | |
| void Field_datetime0::store_TIME(const MYSQL_TIME *ltime)
 | |
| {
 | |
|   ulonglong tmp= TIME_to_ulonglong_datetime(ltime);
 | |
|   int8store(ptr,tmp);
 | |
| }
 | |
| 
 | |
| 
 | |
| sql_mode_t
 | |
| Field_datetime::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
 | |
| {
 | |
|   return expr->datetime_precision(thd) > decimals() ?
 | |
|          MODE_TIME_ROUND_FRACTIONAL : 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_datetime0::send(Protocol *protocol)
 | |
| {
 | |
|   MYSQL_TIME tm;
 | |
|   Field_datetime0::get_date(&tm, date_mode_t(0));
 | |
|   return protocol->store_datetime(&tm, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_datetime0::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   longlong j;
 | |
|   j=sint8korr(ptr);
 | |
|   return j;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_datetime0::val_str(String *val_buffer,
 | |
|                                  String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   val_buffer->alloc(field_length+1);
 | |
|   val_buffer->length(field_length);
 | |
| 
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   ulonglong tmp;
 | |
|   long part1,part2;
 | |
|   char *pos;
 | |
|   int part3;
 | |
| 
 | |
|   tmp= Field_datetime0::val_int();
 | |
| 
 | |
|   /*
 | |
|     Avoid problem with slow longlong arithmetic and sprintf
 | |
|   */
 | |
| 
 | |
|   part1=(long) (tmp/1000000LL);
 | |
|   part2=(long) (tmp - (ulonglong) part1*1000000LL);
 | |
| 
 | |
|   pos=(char*) val_buffer->ptr() + MAX_DATETIME_WIDTH;
 | |
|   *pos--=0;
 | |
|   *pos--= (char) ('0'+(char) (part2%10)); part2/=10;
 | |
|   *pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10);
 | |
|   *pos--= ':';
 | |
|   *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
 | |
|   *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
 | |
|   *pos--= ':';
 | |
|   *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
 | |
|   *pos--= (char) ('0'+(char) part3);
 | |
|   *pos--= ' ';
 | |
|   *pos--= (char) ('0'+(char) (part1%10)); part1/=10;
 | |
|   *pos--= (char) ('0'+(char) (part1%10)); part1/=10;
 | |
|   *pos--= '-';
 | |
|   *pos--= (char) ('0'+(char) (part1%10)); part1/=10;
 | |
|   *pos--= (char) ('0'+(char) (part1%10)); part3= (int) (part1/10);
 | |
|   *pos--= '-';
 | |
|   *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
 | |
|   *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
 | |
|   *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
 | |
|   *pos=(char) ('0'+(char) part3);
 | |
|   val_buffer->set_charset(&my_charset_numeric);
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| bool Field_datetime0::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
 | |
|                                date_mode_t fuzzydate) const
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   longlong tmp= sint8korr(pos);
 | |
|   uint32 part1,part2;
 | |
|   part1=(uint32) (tmp/1000000LL);
 | |
|   part2=(uint32) (tmp - (ulonglong) part1*1000000LL);
 | |
| 
 | |
|   ltime->time_type=	MYSQL_TIMESTAMP_DATETIME;
 | |
|   ltime->neg=		0;
 | |
|   ltime->second_part=	0;
 | |
|   ltime->second=	(int) (part2%100);
 | |
|   ltime->minute=	(int) (part2/100%100);
 | |
|   ltime->hour=		(int) (part2/10000);
 | |
|   ltime->day=		(int) (part1%100);
 | |
|   ltime->month= 	(int) (part1/100%100);
 | |
|   ltime->year= 		(int) (part1/10000);
 | |
|   return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_datetime0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   longlong a,b;
 | |
|   a=sint8korr(a_ptr);
 | |
|   b=sint8korr(b_ptr);
 | |
|   return ((ulonglong) a < (ulonglong) b) ? -1 :
 | |
|     ((ulonglong) a > (ulonglong) b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_datetime0::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   to[0] = ptr[7];
 | |
|   to[1] = ptr[6];
 | |
|   to[2] = ptr[5];
 | |
|   to[3] = ptr[4];
 | |
|   to[4] = ptr[3];
 | |
|   to[5] = ptr[2];
 | |
|   to[6] = ptr[1];
 | |
|   to[7] = ptr[0];
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_datetime::set_time()
 | |
| {
 | |
|   THD *thd= table->in_use;
 | |
|   set_notnull();
 | |
|   // Here we always truncate (not round), no matter what sql_mode is
 | |
|   if (decimals())
 | |
|     store_datetime(Datetime(thd, Timeval(thd->query_start(),
 | |
|                                          thd->query_start_sec_part())
 | |
|                             ).trunc(decimals()));
 | |
|   else
 | |
|     store_datetime(Datetime(thd, Timeval(thd->query_start(), 0)));
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_datetime_hires::store_TIME(const MYSQL_TIME *ltime)
 | |
| {
 | |
|   ulonglong packed= sec_part_shift(pack_time(ltime), dec);
 | |
|   store_bigendian(packed, ptr, Field_datetime_hires::pack_length());
 | |
| }
 | |
| 
 | |
| bool Field_datetime_with_dec::send(Protocol *protocol)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, date_mode_t(0));
 | |
|   return protocol->store_datetime(<ime, dec);
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_datetime_with_dec::val_real(void)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, date_mode_t(0));
 | |
|   return TIME_to_double(<ime);
 | |
| }
 | |
| 
 | |
| longlong Field_datetime_with_dec::val_int(void)
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, date_mode_t(0));
 | |
|   return TIME_to_ulonglong_datetime(<ime);
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_datetime_with_dec::val_str(String *str,
 | |
|                                          String *unused __attribute__((unused)))
 | |
| {
 | |
|   MYSQL_TIME ltime;
 | |
|   get_date(<ime, date_mode_t(0));
 | |
|   str->alloc(field_length+1);
 | |
|   str->length(field_length);
 | |
|   my_datetime_to_str(<ime, (char*) str->ptr(), dec);
 | |
|   str->set_charset(&my_charset_numeric);
 | |
|   return str;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
 | |
|                                     date_mode_t fuzzydate) const
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   ulonglong packed= read_bigendian(pos, Field_datetime_hires::pack_length());
 | |
|   unpack_time(sec_part_unshift(packed, dec), ltime, MYSQL_TIMESTAMP_DATETIME);
 | |
|   return validate_MMDD(packed, ltime->month, ltime->day, fuzzydate);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length());
 | |
|   ulonglong b=read_bigendian(b_ptr, Field_datetime_hires::pack_length());
 | |
|   return a < b ? -1 : a > b ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_datetime_with_dec::make_send_field(Send_field *field)
 | |
| {
 | |
|   Field::make_send_field(field);
 | |
|   field->decimals= dec;
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** MySQL-5.6 compatible DATETIME(N)
 | |
| **
 | |
| ****************************************************************************/
 | |
| int Field_datetimef::reset()
 | |
| {
 | |
|   my_datetime_packed_to_binary(0, ptr, dec);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void Field_datetimef::store_TIME(const MYSQL_TIME *ltime)
 | |
| {
 | |
|   longlong tmp= TIME_to_longlong_datetime_packed(ltime);
 | |
|   my_datetime_packed_to_binary(tmp, ptr, dec);
 | |
| }
 | |
| 
 | |
| bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
 | |
|                                date_mode_t fuzzydate) const
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   longlong tmp= my_datetime_packed_from_binary(pos, dec);
 | |
|   TIME_from_longlong_datetime_packed(ltime, tmp);
 | |
|   return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
 | |
| }
 | |
| Binlog_type_info Field_datetimef::binlog_type_info() const
 | |
| {
 | |
|   return Binlog_type_info(Field_datetimef::binlog_type(), decimals(), 1);
 | |
| }
 | |
| 
 | |
| longlong Field_datetimef::val_datetime_packed(THD *thd)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   longlong tmp= my_datetime_packed_from_binary(ptr, dec);
 | |
|   MYSQL_TIME ltime;
 | |
|   TIME_from_longlong_datetime_packed(<ime, tmp);
 | |
|   return pack_time(<ime);
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** string type
 | |
| ** A string may be varchar or binary
 | |
| ****************************************************************************/
 | |
| 
 | |
| /*
 | |
|   Report "not well formed" or "cannot convert" error
 | |
|   after storing a character string info a field.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     check_string_copy_error()
 | |
|     copier                   - the conversion status
 | |
|     end                      - the very end of the source string
 | |
|                                that was just copied
 | |
|     cs                       - character set of the string
 | |
| 
 | |
|   NOTES
 | |
|     As of version 5.0 both cases return the same error:
 | |
|   
 | |
|       "Invalid string value: 'xxx' for column 't' at row 1"
 | |
|   
 | |
|   Future versions will possibly introduce a new error message:
 | |
| 
 | |
|       "Cannot convert character string: 'xxx' for column 't' at row 1"
 | |
| 
 | |
|   RETURN
 | |
|     FALSE - If errors didn't happen
 | |
|     TRUE  - If an error happened
 | |
| */
 | |
| 
 | |
| bool
 | |
| Field_longstr::check_string_copy_error(const String_copier *copier,
 | |
|                                        const char *end,
 | |
|                                        CHARSET_INFO *cs)
 | |
| {
 | |
|   const char *pos;
 | |
|   char tmp[32];
 | |
| 
 | |
|   if (likely(!(pos= copier->most_important_error_pos())))
 | |
|     return FALSE;
 | |
| 
 | |
|   /* Ignore errors from internal expressions */
 | |
|   if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
 | |
|   {
 | |
|     DBUG_ASSERT(sizeof(tmp) >= convert_to_printable_required_length(6));
 | |
|     convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
 | |
|     set_warning_truncated_wrong_value("string", tmp);
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Check if we lost any important data and send a truncation error/warning
 | |
| 
 | |
|   SYNOPSIS
 | |
|     Field_longstr::report_if_important_data()
 | |
|     pstr                     - Truncated rest of string
 | |
|     end                      - End of truncated string
 | |
|     count_spaces             - Treat traling spaces as important data
 | |
| 
 | |
|   RETURN VALUES
 | |
|     0   - None was truncated (or we don't count cut fields)
 | |
|     2   - Some bytes was truncated
 | |
| 
 | |
|   NOTE
 | |
|     Check if we lost any important data (anything in a binary string,
 | |
|     or any non-space in others). If only trailing spaces was lost,
 | |
|     send a truncation note, otherwise send a truncation error.
 | |
|     Silently ignore traling spaces if the count_space parameter is FALSE.
 | |
| */
 | |
| 
 | |
| int
 | |
| Field_longstr::report_if_important_data(const char *pstr, const char *end,
 | |
|                                         bool count_spaces)
 | |
| {
 | |
|   THD *thd;
 | |
|   if ((pstr < end) &&
 | |
|       (thd= get_thd())->count_cuted_fields > CHECK_FIELD_EXPRESSION)
 | |
|   {
 | |
|     if (test_if_important_data(field_charset(), pstr, end))
 | |
|     {
 | |
|       if (thd->abort_on_warning)
 | |
|         set_warning(ER_DATA_TOO_LONG, 1);
 | |
|       else
 | |
|         set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|       return 2;
 | |
|     }
 | |
|     else if (count_spaces)
 | |
|     {
 | |
|       /* If we lost only spaces then produce a NOTE, not a WARNING */
 | |
|       set_note(WARN_DATA_TRUNCATED, 1);
 | |
|       return 2;
 | |
|     }
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   This is JSON specific.
 | |
|   We should eventually add Field_json_varchar and Field_json_blob
 | |
|   and move make_send_field() to the new classes.
 | |
| */
 | |
| void Field_longstr::make_send_field(Send_field *field)
 | |
| {
 | |
|   Field_str::make_send_field(field);
 | |
|   if (check_constraint)
 | |
|   {
 | |
|     /*
 | |
|       Append the format that is implicitly implied by the CHECK CONSTRAINT.
 | |
|       For example:
 | |
|         CREATE TABLE t1 (js longtext DEFAULT NULL CHECK (json_valid(a)));
 | |
|         SELECT j FROM t1;
 | |
|       will add "format=json" to the extended type info metadata for t1.js.
 | |
|     */
 | |
|     check_constraint->expr->set_format_by_check_constraint(field);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   An optimized version that uses less stack than Field::send().
 | |
| */
 | |
| bool Field_longstr::send(Protocol *protocol)
 | |
| {
 | |
|   String tmp;
 | |
|   val_str(&tmp, &tmp);
 | |
|   /*
 | |
|     Ensure this function is only used with classes that do not allocate
 | |
|     memory in val_str()
 | |
|   */
 | |
|   DBUG_ASSERT(tmp.alloced_length() == 0);
 | |
|   return protocol->store(tmp.ptr(), tmp.length(), tmp.charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| const Type_handler *Field_string::type_handler() const
 | |
| {
 | |
|   if (is_var_string())
 | |
|     return &type_handler_var_string;
 | |
|   /*
 | |
|     This is a temporary solution and will be fixed soon (in 10.9?).
 | |
|     Type_handler_string_json will provide its own Field_string_json.
 | |
|   */
 | |
|   if (Type_handler_json_common::has_json_valid_constraint(this))
 | |
|    return &type_handler_string_json;
 | |
|   return &type_handler_string;
 | |
| }
 | |
| 
 | |
| 	/* Copy a string and fill with space */
 | |
| 
 | |
| int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   uint copy_length;
 | |
|   int rc;
 | |
| 
 | |
|   /* See the comment for Field_long::store(long long) */
 | |
|   DBUG_ASSERT(!table || table->in_use == current_thd);
 | |
| 
 | |
|   rc= well_formed_copy_with_check((char*) ptr, field_length,
 | |
|                                   cs, from, length,
 | |
|                                   Field_string::char_length(),
 | |
|                                   false, ©_length);
 | |
| 
 | |
|   /* Append spaces if the string was shorter than the field. */
 | |
|   if (copy_length < field_length)
 | |
|     field_charset()->fill((char*) ptr + copy_length,
 | |
|                           field_length - copy_length,
 | |
|                           field_charset()->pad_char);
 | |
| 
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_str::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   char buff[64];
 | |
|   uint  length;
 | |
|   length= (uint) (field_charset()->longlong10_to_str)(buff, sizeof(buff),
 | |
|                                                       (unsigned_val ? 10: -10),
 | |
|                                                       nr);
 | |
|   return store(buff, length, field_charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Store double value in Field_string or Field_varstring.
 | |
| 
 | |
|   Pretty prints double number into field_length characters buffer.
 | |
| 
 | |
|   @param nr            number
 | |
| */
 | |
| 
 | |
| int Field_str::store(double nr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
 | |
|   uint local_char_length= MY_MIN(sizeof(buff), Field_str::char_length());
 | |
|   size_t length= 0;
 | |
|   my_bool error= (local_char_length == 0);
 | |
| 
 | |
|   // my_gcvt() requires width > 0, and we may have a CHAR(0) column.
 | |
|   if (likely(!error))
 | |
|     length= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, local_char_length, buff, &error);
 | |
| 
 | |
|   if (unlikely(error))
 | |
|   {
 | |
|     if (get_thd()->abort_on_warning)
 | |
|       set_warning(ER_DATA_TOO_LONG, 1);
 | |
|     else
 | |
|       set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|   }
 | |
|   return store(buff, (uint)length, &my_charset_numeric);
 | |
| }
 | |
| 
 | |
| bool Field_string::is_equal(const Column_definition &new_field) const
 | |
| {
 | |
|   DBUG_ASSERT(!compression_method());
 | |
|   return (new_field.type_handler() == type_handler() &&
 | |
|           new_field.char_length == char_length() &&
 | |
|           new_field.charset->eq_collation(field_charset()) &&
 | |
|           new_field.length == max_display_length());
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_longstr::store_decimal(const my_decimal *d)
 | |
| {
 | |
|   StringBuffer<DECIMAL_MAX_STR_LENGTH+1> str;
 | |
|   d->to_string(&str);
 | |
|   return store(str.ptr(), str.length(), str.charset());
 | |
| }
 | |
| 
 | |
| uint32 Field_longstr::max_data_length() const
 | |
| {
 | |
|   return field_length + (field_length > 255 ? 2 : 1);
 | |
| }
 | |
| 
 | |
| 
 | |
| Data_type_compatibility
 | |
| Field_longstr::cmp_to_string_with_same_collation(const Item_bool_func *cond,
 | |
|                                                  const Item *item) const
 | |
| {
 | |
|   return (!cmp_is_done_using_type_handler_of_this(cond, item) ?
 | |
|           Data_type_compatibility::INCOMPATIBLE_DATA_TYPE :
 | |
|           !charset()->eq_collation(cond->compare_collation()) ?
 | |
|           Data_type_compatibility::INCOMPATIBLE_COLLATION :
 | |
|           Data_type_compatibility::OK);
 | |
| }
 | |
| 
 | |
| 
 | |
| Data_type_compatibility
 | |
| Field_longstr::cmp_to_string_with_stricter_collation(const Item_bool_func *cond,
 | |
|                                                      const Item *item) const
 | |
| {
 | |
|   return (!cmp_is_done_using_type_handler_of_this(cond, item) ?
 | |
|           Data_type_compatibility::INCOMPATIBLE_DATA_TYPE :
 | |
|           (!charset()->eq_collation(cond->compare_collation()) &&
 | |
|            !(cond->compare_collation()->state & MY_CS_BINSORT) &&
 | |
|            !Utf8_narrow::should_do_narrowing(this, cond->compare_collation())) ?
 | |
|           Data_type_compatibility::INCOMPATIBLE_COLLATION :
 | |
|           Data_type_compatibility::OK);
 | |
| }
 | |
| 
 | |
| 
 | |
| Data_type_compatibility
 | |
| Field_longstr::can_optimize_keypart_ref(const Item_bool_func *cond,
 | |
|                                         const Item *item) const
 | |
| {
 | |
|   DBUG_ASSERT(cmp_type() == STRING_RESULT);
 | |
|   /*
 | |
|     So, we have an equality:  tbl.string_key = 'abc'
 | |
| 
 | |
|     The comparison is the string comparison. Can we use index lookups to
 | |
|     find matching rows?  We can do that when:
 | |
|      - The comparison uses the same collation as tbl.string_key
 | |
|      - the comparison uses binary collation, while tbl.string_key
 | |
|        uses some other collation.
 | |
|        In this case, we will find matches in some collation. For example, for
 | |
|        'abc' we may find 'abc', 'ABC', and 'äbc'.
 | |
|        But we're certain that will find the row with the identical binary, 'abc'.
 | |
|   */
 | |
|   return cmp_to_string_with_stricter_collation(cond, item);
 | |
| }
 | |
| 
 | |
| 
 | |
| Data_type_compatibility
 | |
| Field_longstr::can_optimize_hash_join(const Item_bool_func *cond,
 | |
|                                       const Item *item) const
 | |
| {
 | |
|   DBUG_ASSERT(cmp_type() == STRING_RESULT);
 | |
|   return cmp_to_string_with_same_collation(cond, item);
 | |
| }
 | |
| 
 | |
| 
 | |
| Data_type_compatibility
 | |
| Field_longstr::can_optimize_group_min_max(const Item_bool_func *cond,
 | |
|                                           const Item *const_item) const
 | |
| {
 | |
|   /*
 | |
|     Can't use indexes when comparing a string to a number or a date
 | |
|     Don't use an index when comparing strings of different collations.
 | |
|   */
 | |
|   DBUG_ASSERT(cmp_type() == STRING_RESULT);
 | |
|   return cmp_to_string_with_same_collation(cond, const_item);
 | |
| }
 | |
| 
 | |
| 
 | |
| Data_type_compatibility
 | |
| Field_longstr::can_optimize_range(const Item_bool_func *cond,
 | |
|                                   const Item *item,
 | |
|                                   bool is_eq_func) const
 | |
| {
 | |
|   return is_eq_func ?
 | |
|          cmp_to_string_with_stricter_collation(cond, item) :
 | |
|          cmp_to_string_with_same_collation(cond, item);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This overrides the default behavior of the parent constructor
 | |
|   Warn_filter(thd) to suppress notes about trailing spaces in case of CHAR(N),
 | |
|   as they are truncated during val_str().
 | |
|   We still do want truncation notes in case of BINARY(N),
 | |
|   as trailing spaces are not truncated in val_str().
 | |
| */
 | |
| Field_string::Warn_filter_string::Warn_filter_string(const THD *thd,
 | |
|                                                      const Field_string *field)
 | |
|   :Warn_filter(!thd->no_errors,
 | |
|                !thd->no_errors &&
 | |
|                field->field_charset() == &my_charset_bin)
 | |
| { }
 | |
| 
 | |
| 
 | |
| double Field_string::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   const LEX_CSTRING str= to_lex_cstring();
 | |
|   return Converter_strntod_with_warn(thd,
 | |
|                                      Warn_filter_string(thd, this),
 | |
|                                      Field_string::charset(),
 | |
|                                      str.str, str.length).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_string::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   const LEX_CSTRING str= to_lex_cstring();
 | |
|   return Converter_strntoll_with_warn(thd, Warn_filter_string(thd, this),
 | |
|                                       Field_string::charset(),
 | |
|                                       str.str, str.length).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| sql_mode_t Field_string::value_depends_on_sql_mode() const
 | |
| {
 | |
|   return has_charset() ? MODE_PAD_CHAR_TO_FULL_LENGTH : sql_mode_t(0);
 | |
| };
 | |
| 
 | |
| 
 | |
| sql_mode_t Field_string::can_handle_sql_mode_dependency_on_store() const
 | |
| {
 | |
|   return has_charset() ? MODE_PAD_CHAR_TO_FULL_LENGTH : sql_mode_t(0);
 | |
| }
 | |
| 
 | |
| 
 | |
| LEX_CSTRING Field_string::to_lex_cstring() const
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   /* See the comment for Field_long::store(long long) */
 | |
|   DBUG_ASSERT(!table || table->in_use == current_thd);
 | |
|   if (get_thd()->variables.sql_mode & MODE_PAD_CHAR_TO_FULL_LENGTH)
 | |
|     return Lex_cstring((const char*) ptr,
 | |
|                        field_charset()->charpos(ptr, ptr + field_length,
 | |
|                                                 Field_string::char_length()));
 | |
|   return Lex_cstring((const char *) ptr,
 | |
|                      field_charset()->lengthsp((const char*) ptr, field_length));
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_string::val_str(String *val_buffer __attribute__((unused)),
 | |
| 			      String *val_ptr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   const LEX_CSTRING str= to_lex_cstring();
 | |
|   val_ptr->set(str.str, str.length, field_charset());
 | |
|   return val_ptr;
 | |
| }
 | |
| 
 | |
| 
 | |
| my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   const LEX_CSTRING str= to_lex_cstring();
 | |
|   Converter_str2my_decimal_with_warn(thd,
 | |
|                                      Warn_filter_string(thd, this),
 | |
|                                      E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM,
 | |
|                                      Field_string::charset(),
 | |
|                                      str.str, str.length, decimal_value);
 | |
|   return decimal_value;
 | |
| }
 | |
| 
 | |
| 
 | |
| struct Check_field_param {
 | |
|   const Field *field;
 | |
| };
 | |
| 
 | |
| #ifdef HAVE_REPLICATION
 | |
| static bool
 | |
| check_field_for_37426(const void *param_arg)
 | |
| {
 | |
|   Check_field_param *param= (Check_field_param*) param_arg;
 | |
|   DBUG_ASSERT(param->field->real_type() == MYSQL_TYPE_STRING);
 | |
|   DBUG_PRINT("debug", ("Field %s - type: %d, size: %d",
 | |
|                        param->field->field_name.str,
 | |
|                        param->field->real_type(),
 | |
|                        param->field->row_pack_length()));
 | |
|   return param->field->row_pack_length() > 255;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| bool
 | |
| Field_string::compatible_field_size(uint field_metadata,
 | |
|                                     const Relay_log_info *rli_arg,
 | |
|                                     uint16 mflags __attribute__((unused)),
 | |
|                                     int *order_var) const
 | |
| {
 | |
| #ifdef HAVE_REPLICATION
 | |
|   const Check_field_param check_param = { this };
 | |
|   if (rpl_master_has_bug(rli_arg, 37426, TRUE,
 | |
|                          check_field_for_37426, &check_param))
 | |
|     return FALSE;                        // Not compatible field sizes
 | |
| #endif
 | |
|   return Field::compatible_field_size(field_metadata, rli_arg, mflags, order_var);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   return field_charset()->coll->strnncollsp_nchars(field_charset(),
 | |
|                                                    a_ptr, field_length,
 | |
|                                                    b_ptr, field_length,
 | |
|                                                    Field_string::char_length(),
 | |
|                                                    0);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_string::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
 | |
|                              size_t prefix_char_len) const
 | |
| {
 | |
|   size_t field_len= table->field[field_index]->field_length;
 | |
| 
 | |
|   return field_charset()->coll->strnncollsp_nchars(field_charset(),
 | |
|                                                    a_ptr, field_len,
 | |
|                                                    b_ptr, field_len,
 | |
|                                                    prefix_char_len,
 | |
|                                                    0);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_string::sort_string(uchar *to,uint length)
 | |
| {
 | |
| #ifdef DBUG_ASSERT_EXISTS
 | |
|   size_t tmp=
 | |
| #endif
 | |
|     field_charset()->strnxfrm(to, length,
 | |
|                               char_length() * field_charset()->strxfrm_multiply,
 | |
|                               ptr, field_length,
 | |
|                               MY_STRXFRM_PAD_WITH_SPACE |
 | |
|                               MY_STRXFRM_PAD_TO_MAXLEN);
 | |
|   DBUG_ASSERT(tmp == length);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_string::sql_type(String &res) const
 | |
| {
 | |
|   THD *thd= table->in_use;
 | |
|   CHARSET_INFO *cs=res.charset();
 | |
|   size_t length;
 | |
| 
 | |
|   length= cs->cset->snprintf(cs,(char*) res.ptr(),
 | |
|                              res.alloced_length(), "%s(%d)%s",
 | |
|                              (type() == MYSQL_TYPE_VAR_STRING ?
 | |
|                               (has_charset() ? "varchar" : "varbinary") :
 | |
| 			      (has_charset() ? "char" : "binary")),
 | |
|                              (int) field_length / charset()->mbmaxlen,
 | |
|                              type() == MYSQL_TYPE_VAR_STRING ? "/*old*/" : "");
 | |
|   res.length(length);
 | |
|   if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
 | |
|       has_charset() && (charset()->state & MY_CS_BINSORT))
 | |
|     res.append(STRING_WITH_LEN(" binary"));
 | |
| }
 | |
| 
 | |
| /**
 | |
|    For fields which are associated with character sets their length is provided
 | |
|    in octets and their character set information is also provided as part of
 | |
|    type information.
 | |
| 
 | |
|    @param   res       String which contains filed type and length.
 | |
| */
 | |
| void Field_string::sql_rpl_type(String *res) const
 | |
| {
 | |
|   if (Field_string::has_charset())
 | |
|   {
 | |
|     CHARSET_INFO *cs= res->charset();
 | |
|     DBUG_ASSERT(cs->mbminlen == 1);
 | |
|     size_t length= cs->cset->snprintf(cs, (char*) res->ptr(),
 | |
|                                       res->alloced_length(),
 | |
|                                       "char(%u octets) character set %s",
 | |
|                                       field_length,
 | |
|                                       charset()->cs_name.str);
 | |
|     res->length(length);
 | |
|   }
 | |
|   else
 | |
|     Field_string::sql_type(*res);
 | |
|  }
 | |
| 
 | |
| uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length)
 | |
| {
 | |
|   DBUG_PRINT("debug", ("Packing field '%s'", field_name.str));
 | |
|   return StringPack(field_charset(), field_length).pack(to, from, max_length);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Unpack a string field from row data.
 | |
| 
 | |
|    This method is used to unpack a string field from a master whose size 
 | |
|    of the field is less than that of the slave. Note that there can be a
 | |
|    variety of field types represented with this class. Certain types like
 | |
|    ENUM or SET are processed differently. Hence, the upper byte of the 
 | |
|    @c param_data argument contains the result of field->real_type() from
 | |
|    the master.
 | |
| 
 | |
|    @note For information about how the length is packed, see @c
 | |
|    Field_string::save_field_metadata
 | |
| 
 | |
|    @param   to         Destination of the data
 | |
|    @param   from       Source of the data
 | |
|    @param   param_data Real type (upper) and length (lower) values
 | |
| 
 | |
|    @return  New pointer into memory based on from + length of the data
 | |
| */
 | |
| const uchar *
 | |
| Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end,
 | |
|                      uint param_data)
 | |
| {
 | |
|   return StringPack(field_charset(), field_length).unpack(to, from, from_end,
 | |
|                                                           param_data);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Save the field metadata for string fields.
 | |
| 
 | |
|    Saves the real type in the first byte and the field length in the 
 | |
|    second byte of the field metadata array at index of *metadata_ptr and
 | |
|    *(metadata_ptr + 1).
 | |
| 
 | |
|    @note In order to be able to handle lengths exceeding 255 and be
 | |
|    backwards-compatible with pre-5.1.26 servers, an extra two bits of
 | |
|    the length has been added to the metadata in such a way that if
 | |
|    they are set, a new unrecognized type is generated.  This will
 | |
|    cause pre-5.1-26 servers to stop due to a field type mismatch,
 | |
|    while new servers will be able to extract the extra bits. If the
 | |
|    length is <256, there will be no difference and both a new and an
 | |
|    old server will be able to handle it.
 | |
| 
 | |
|    @note The extra two bits are added to bits 13 and 14 of the
 | |
|    parameter data (with 1 being the least siginficant bit and 16 the
 | |
|    most significant bit of the word) by xoring the extra length bits
 | |
|    with the real type.  Since all allowable types have 0xF as most
 | |
|    significant bits of the metadata word, lengths <256 will not affect
 | |
|    the real type at all, while all other values will result in a
 | |
|    non-existent type in the range 17-244.
 | |
| 
 | |
|    @see Field_string::unpack
 | |
| 
 | |
|    @param   metadata_ptr   First byte of field metadata
 | |
| 
 | |
|    @returns number of bytes written to metadata_ptr
 | |
| */
 | |
| 
 | |
| Binlog_type_info_fixed_string::Binlog_type_info_fixed_string(uchar type_code,
 | |
|                                                              uint32 octets,
 | |
|                                                              CHARSET_INFO *cs)
 | |
|  :Binlog_type_info(type_code, 0, 2, cs)
 | |
| {
 | |
|   DBUG_ASSERT(octets < 1024);
 | |
|   DBUG_ASSERT((type_code & 0xF0) == 0xF0);
 | |
|   DBUG_PRINT("debug", ("octets: %u, type_code: %u", octets, type_code));
 | |
|   m_metadata= (type_code ^ ((octets & 0x300) >> 4)) +
 | |
|               (((uint)(octets & 0xFF)) << 8);
 | |
| }
 | |
| 
 | |
| 
 | |
| Binlog_type_info Field_string::binlog_type_info() const
 | |
| {
 | |
|   DBUG_ASSERT(Field_string::type() == binlog_type());
 | |
|   return Binlog_type_info_fixed_string(Field_string::binlog_type(),
 | |
|                                        field_length, charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Field_string::packed_col_length(const uchar *data_ptr, uint length)
 | |
| {
 | |
|   return StringPack::packed_col_length(data_ptr, length);
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Field_string::max_packed_col_length(uint max_length)
 | |
| {
 | |
|   return StringPack::max_packed_col_length(max_length);
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Field_string::get_key_image(uchar *buff, uint length, const uchar *ptr_arg,
 | |
|                                  imagetype type_arg) const
 | |
| {
 | |
|   size_t bytes= field_charset()->charpos((char*) ptr_arg,
 | |
|                                          (char*) ptr_arg + field_length,
 | |
|                                          length / mbmaxlen());
 | |
|   memcpy(buff, ptr_arg, bytes);
 | |
|   if (bytes < length)
 | |
|     field_charset()->fill((char*) buff + bytes,
 | |
|                           length - bytes,
 | |
|                           field_charset()->pad_char);
 | |
|   return (uint)bytes;
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field_string::make_new_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                                     bool keep_type)
 | |
| {
 | |
|   Field *field;
 | |
|   if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
 | |
|     field= Field::make_new_field(root, new_table, keep_type);
 | |
|   else if ((field= new (root) Field_varstring(field_length, maybe_null(),
 | |
|                                               &field_name,
 | |
|                                               new_table->s, charset())))
 | |
|   {
 | |
|     /*
 | |
|       Old VARCHAR field which should be modified to a VARCHAR on copy
 | |
|       This is done to ensure that ALTER TABLE will convert old VARCHAR fields
 | |
|       to now VARCHAR fields.
 | |
|     */
 | |
|     field->init_for_make_new_field(new_table, orig_table);
 | |
|   }
 | |
|   return field;
 | |
| }
 | |
| 
 | |
| 
 | |
| en_fieldtype Field_string::tmp_engine_column_type(bool use_packed_rows) const
 | |
| {
 | |
|   return field_length >= MIN_STRING_LENGTH_TO_PACK_ROWS ? FIELD_SKIP_ENDSPACE :
 | |
|                                                           FIELD_NORMAL;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|   VARCHAR type
 | |
|   Data in field->ptr is stored as:
 | |
|     1 or 2 bytes length-prefix-header  (from Field_varstring::length_bytes)
 | |
|     data
 | |
| 
 | |
|   NOTE:
 | |
|   When VARCHAR is stored in a key (for handler::index_read() etc) it's always
 | |
|   stored with a 2 byte prefix. (Just like blob keys).
 | |
| 
 | |
|   Normally length_bytes is calculated as (field_length < 256 : 1 ? 2)
 | |
|   The exception is if there is a prefix key field that is part of a long
 | |
|   VARCHAR, in which case field_length for this may be 1 but the length_bytes
 | |
|   is 2.
 | |
| ****************************************************************************/
 | |
| 
 | |
| const uint Field_varstring::MAX_SIZE= UINT_MAX16;
 | |
| 
 | |
| 
 | |
| const Type_handler *Field_varstring::type_handler() const
 | |
| {
 | |
|   /*
 | |
|     This is a temporary solution and will be fixed soon (in 10.9?).
 | |
|     Type_handler_varchar_json will provide its own Field_varstring_json
 | |
|     and Field_varstring_compressed_json
 | |
|   */
 | |
|   if (Type_handler_json_common::has_json_valid_constraint(this))
 | |
|     return &type_handler_varchar_json;
 | |
|   return &type_handler_varchar;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Save the field metadata for varstring fields.
 | |
| 
 | |
|    Saves the field length in the first byte. Note: may consume
 | |
|    2 bytes. Caller must ensure second byte is contiguous with
 | |
|    first byte (e.g. array index 0,1).
 | |
| 
 | |
|    @param   metadata_ptr   First byte of field metadata
 | |
| 
 | |
|    @returns number of bytes written to metadata_ptr
 | |
| */
 | |
| Binlog_type_info Field_varstring::binlog_type_info() const
 | |
| {
 | |
|   DBUG_ASSERT(Field_varstring::type() == binlog_type());
 | |
|   return Binlog_type_info(Field_varstring::type(), field_length, 2, charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_varstring::memcpy_field_possible(const Field *from) const
 | |
| {
 | |
|   return (Field_str::memcpy_field_possible(from) &&
 | |
|           !compression_method() == !from->compression_method() &&
 | |
|           length_bytes == ((Field_varstring*) from)->length_bytes &&
 | |
|           (table->file && !(table->file->ha_table_flags() &
 | |
|                             HA_RECORD_MUST_BE_CLEAN_ON_WRITE)));
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   uint copy_length;
 | |
|   int rc;
 | |
| 
 | |
|   rc= well_formed_copy_with_check((char*) get_data(), field_length,
 | |
|                                   cs, from, length,
 | |
|                                   Field_varstring::char_length(),
 | |
|                                   true, ©_length);
 | |
| 
 | |
|   store_length(copy_length);
 | |
| 
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_varstring::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   return Converter_strntod_with_warn(thd, Warn_filter(thd),
 | |
|                                      Field_varstring::charset(),
 | |
|                                      (const char *) get_data(),
 | |
|                                      get_length()).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_varstring::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   return Converter_strntoll_with_warn(thd, Warn_filter(thd),
 | |
|                                       Field_varstring::charset(),
 | |
|                                       (const char *) get_data(),
 | |
|                                       get_length()).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
 | |
| 				 String *val_ptr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   val_ptr->set((const char*) get_data(), get_length(), field_charset());
 | |
|   return val_ptr;
 | |
| }
 | |
| 
 | |
| 
 | |
| my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   Converter_str2my_decimal_with_warn(thd, Warn_filter(thd),
 | |
|                                      E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM,
 | |
|                                      Field_varstring::charset(),
 | |
|                                      (const char *) get_data(),
 | |
|                                      get_length(), decimal_value);
 | |
|   return decimal_value;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   An optimized version that uses less stack and less temporary
 | |
|   variable initialization than Field_longstr::send()
 | |
| */
 | |
| bool Field_varstring::send(Protocol *protocol)
 | |
| {
 | |
|   return protocol->store((const char *) get_data(), get_length(),
 | |
|                          field_charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifdef HAVE_MEM_CHECK
 | |
| 
 | |
| /*
 | |
|   Mark the unused part of the varstring as defined.
 | |
| 
 | |
|   This function is only used be Unique when calculating statistics.
 | |
| 
 | |
|   The marking is needed as we write the whole tree to disk in case of
 | |
|   overflows.  For using or comparing values the undefined value part
 | |
|   is never used. We could also use bzero() here, but it would be
 | |
|   slower in production environments.
 | |
|   This function is tested by main.stat_tables-enospc
 | |
| */
 | |
| 
 | |
| void Field_varstring::mark_unused_memory_as_defined()
 | |
| {
 | |
|   uint used_length __attribute__((unused)) = get_length();
 | |
|   MEM_MAKE_DEFINED(get_data() + used_length, field_length - used_length);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| int Field_varstring::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   uint a_length, b_length;
 | |
|   int diff;
 | |
| 
 | |
|   if (length_bytes == 1)
 | |
|   {
 | |
|     a_length= (uint) *a_ptr;
 | |
|     b_length= (uint) *b_ptr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     a_length= uint2korr(a_ptr);
 | |
|     b_length= uint2korr(b_ptr);
 | |
|   }
 | |
|   set_if_smaller(a_length, field_length);
 | |
|   set_if_smaller(b_length, field_length);
 | |
|   diff= field_charset()->strnncollsp(a_ptr + length_bytes, a_length,
 | |
|                                      b_ptr + length_bytes, b_length);
 | |
|   return diff;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_varstring::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
 | |
|                                 size_t prefix_char_len) const
 | |
| {
 | |
|   /* avoid more expensive strnncollsp_nchars() if possible */
 | |
|   if (prefix_char_len * field_charset()->mbmaxlen ==
 | |
|       table->field[field_index]->field_length)
 | |
|     return Field_varstring::cmp(a_ptr, b_ptr);
 | |
| 
 | |
|   size_t a_length, b_length;
 | |
| 
 | |
|   if (length_bytes == 1)
 | |
|   {
 | |
|     a_length= *a_ptr;
 | |
|     b_length= *b_ptr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     a_length= uint2korr(a_ptr);
 | |
|     b_length= uint2korr(b_ptr);
 | |
|   }
 | |
|   return field_charset()->coll->strnncollsp_nchars(field_charset(),
 | |
|                                                    a_ptr + length_bytes,
 | |
|                                                    a_length,
 | |
|                                                    b_ptr + length_bytes,
 | |
|                                                    b_length,
 | |
|                                                    prefix_char_len,
 | |
|                                                    0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @note
 | |
|     varstring and blob keys are ALWAYS stored with a 2 byte length prefix
 | |
| */
 | |
| 
 | |
| int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length) const
 | |
| {
 | |
|   size_t length=  length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
 | |
|   size_t local_char_length= max_key_length / mbmaxlen();
 | |
| 
 | |
|   local_char_length= field_charset()->charpos(ptr + length_bytes,
 | |
|                                               ptr + length_bytes + length,
 | |
|                                               local_char_length);
 | |
|   set_if_smaller(length, local_char_length);
 | |
|   return field_charset()->strnncollsp(ptr + length_bytes,
 | |
|                                       length,
 | |
|                                       key_ptr + HA_KEY_BLOB_LENGTH,
 | |
|                                       uint2korr(key_ptr));
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Compare to key segments (always 2 byte length prefix).
 | |
| 
 | |
|   @note
 | |
|     This is used only to compare key segments created for index_read().
 | |
|     (keys are created and compared in key.cc)
 | |
| */
 | |
| 
 | |
| int Field_varstring::key_cmp(const uchar *a,const uchar *b) const
 | |
| {
 | |
|   return field_charset()->strnncollsp(a + HA_KEY_BLOB_LENGTH, uint2korr(a),
 | |
|                                       b + HA_KEY_BLOB_LENGTH, uint2korr(b));
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_varstring::sort_string(uchar *to,uint length)
 | |
| {
 | |
|   String buf;
 | |
| 
 | |
|   val_str(&buf, &buf);
 | |
| 
 | |
|   if (field_charset() == &my_charset_bin)
 | |
|   {
 | |
|     /* Store length last in high-byte order to sort longer strings first */
 | |
|     if (length_bytes == 1)
 | |
|       to[length - 1]= buf.length();
 | |
|     else
 | |
|       mi_int2store(to + length - 2, buf.length());
 | |
|     length-= length_bytes;
 | |
|   }
 | |
| 
 | |
| #ifdef DBUG_ASSERT_EXISTS
 | |
|     size_t rc=
 | |
| #endif
 | |
|   field_charset()->strnxfrm(to, length,
 | |
|                             char_length() * field_charset()->strxfrm_multiply,
 | |
|                             (const uchar *) buf.ptr(), buf.length(),
 | |
|                             MY_STRXFRM_PAD_WITH_SPACE |
 | |
|                             MY_STRXFRM_PAD_TO_MAXLEN);
 | |
|   DBUG_ASSERT(rc == length);
 | |
| }
 | |
| 
 | |
| 
 | |
| enum ha_base_keytype Field_varstring::key_type() const
 | |
| {
 | |
|   enum ha_base_keytype res;
 | |
| 
 | |
|   if (binary())
 | |
|     res= length_bytes == 1 ? HA_KEYTYPE_VARBINARY1 : HA_KEYTYPE_VARBINARY2;
 | |
|   else
 | |
|     res= length_bytes == 1 ? HA_KEYTYPE_VARTEXT1 : HA_KEYTYPE_VARTEXT2;
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Compressed columns need one extra byte to store the compression method.
 | |
|   This byte is invisible to the end user, but not for the storage engine.
 | |
| */
 | |
| 
 | |
| void Field_varstring::sql_type(String &res) const
 | |
| {
 | |
|   THD *thd= table->in_use;
 | |
|   CHARSET_INFO *cs=res.charset();
 | |
|   size_t length;
 | |
| 
 | |
|   length= cs->cset->snprintf(cs,(char*) res.ptr(),
 | |
|                              res.alloced_length(), "%s(%u)",
 | |
|                               (has_charset() ? "varchar" : "varbinary"),
 | |
|                               (uint) char_length());
 | |
|   res.length(length);
 | |
|   if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
 | |
|       has_charset() && (charset()->state & MY_CS_BINSORT))
 | |
|     res.append(STRING_WITH_LEN(" binary"));
 | |
| }
 | |
| 
 | |
| /**
 | |
|    For fields which are associated with character sets their length is provided
 | |
|    in octets and their character set information is also provided as part of
 | |
|    type information.
 | |
| 
 | |
|    @param   res       String which contains filed type and length.
 | |
| */
 | |
| void Field_varstring::sql_rpl_type(String *res) const
 | |
| {
 | |
|   if (Field_varstring::has_charset())
 | |
|   {
 | |
|     CHARSET_INFO *cs= res->charset();
 | |
|     DBUG_ASSERT(cs->mbminlen == 1);
 | |
|     size_t length= cs->cset->snprintf(cs, (char*) res->ptr(),
 | |
|                                       res->alloced_length(),
 | |
|                                       "varchar(%u octets) character set %s",
 | |
|                                       field_length,
 | |
|                                       charset()->cs_name.str);
 | |
|     res->length(length);
 | |
|   }
 | |
|   else
 | |
|     Field_varstring::sql_type(*res);
 | |
| }
 | |
| 
 | |
| 
 | |
| uint32 Field_varstring::data_length()
 | |
| {
 | |
|   return length_bytes == 1 ? (uint32) *ptr : uint2korr(ptr);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Functions to create a packed row.
 | |
|   Here the number of length bytes are depending on the given max_length
 | |
| */
 | |
| 
 | |
| uchar *Field_varstring::pack(uchar *to, const uchar *from, uint max_length)
 | |
| {
 | |
|   uint length= length_bytes == 1 ? (uint) *from : uint2korr(from);
 | |
|   set_if_smaller(max_length, field_length);
 | |
|   if (length > max_length)
 | |
|     length=max_length;
 | |
| 
 | |
|   /* Length always stored little-endian */
 | |
|   *to++= length & 0xFF;
 | |
|   if (max_length > 255)
 | |
|     *to++= (length >> 8) & 0xFF;
 | |
| 
 | |
|   /* Store bytes of string */
 | |
|   if (length > 0)
 | |
|     memcpy(to, from+length_bytes, length);
 | |
|   return to+length;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Unpack a varstring field from row data.
 | |
| 
 | |
|    This method is used to unpack a varstring field from a master
 | |
|    whose size of the field is less than that of the slave.
 | |
| 
 | |
|    @note
 | |
|    The string length is always packed little-endian.
 | |
|   
 | |
|    @param   to         Destination of the data
 | |
|    @param   from       Source of the data
 | |
|    @param   param_data Length bytes from the master's field data
 | |
| 
 | |
|    @return  New pointer into memory based on from + length of the data
 | |
| */
 | |
| const uchar *
 | |
| Field_varstring::unpack(uchar *to, const uchar *from, const uchar *from_end,
 | |
|                         uint param_data)
 | |
| {
 | |
|   uint length;
 | |
|   uint l_bytes= (param_data && (param_data < field_length)) ? 
 | |
|                 (param_data <= 255) ? 1 : 2 : length_bytes;
 | |
| 
 | |
|   if (from + l_bytes > from_end)
 | |
|     return 0;                                 // Error in data
 | |
| 
 | |
|   if (l_bytes == 1)
 | |
|   {
 | |
|     to[0]= *from++;
 | |
|     length= to[0];
 | |
|     if (length_bytes == 2)
 | |
|       to[1]= 0;
 | |
|   }
 | |
|   else /* l_bytes == 2 */
 | |
|   {
 | |
|     length= uint2korr(from);
 | |
|     to[0]= *from++;
 | |
|     to[1]= *from++;
 | |
|   }
 | |
|   if (length)
 | |
|   {
 | |
|     if (from + length > from_end || length > field_length)
 | |
|       return 0;                                 // Error in data
 | |
|     memcpy(to+ length_bytes, from, length);
 | |
|   }
 | |
|   return from+length;
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Field_varstring::packed_col_length(const uchar *data_ptr, uint length)
 | |
| {
 | |
|   if (length > 255)
 | |
|     return uint2korr(data_ptr)+2;
 | |
|   return (uint) *data_ptr + 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Field_varstring::max_packed_col_length(uint max_length)
 | |
| {
 | |
|   return (max_length > 255 ? 2 : 1)+max_length;
 | |
| }
 | |
| 
 | |
| void Field_varstring::val_str_from_ptr(String *val, const uchar *ptr) const
 | |
| {
 | |
|   val->set((const char*) get_data(ptr), get_length(ptr), field_charset());
 | |
| }
 | |
| 
 | |
| uint Field_varstring::get_key_image(uchar *buff, uint length,
 | |
|                                     const uchar *ptr_arg,
 | |
|                                     imagetype type_arg) const
 | |
| {
 | |
|   String val;
 | |
|   val_str_from_ptr(&val, ptr_arg);
 | |
| 
 | |
|   uint local_char_length= val.charpos(length / mbmaxlen());
 | |
|   if (local_char_length < val.length())
 | |
|     val.length(local_char_length);
 | |
|   /* Key is always stored with 2 bytes */
 | |
|   int2store(buff, val.length());
 | |
|   memcpy(buff + HA_KEY_BLOB_LENGTH, val.ptr(), val.length());
 | |
|   if (val.length() < length)
 | |
|   {
 | |
|     /*
 | |
|       Must clear this as we do a memcmp in opt_range.cc to detect
 | |
|       identical keys
 | |
|     */
 | |
|     memset(buff + HA_KEY_BLOB_LENGTH + val.length(), 0, length - val.length());
 | |
|   }
 | |
|   return HA_KEY_BLOB_LENGTH + val.length();
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_varstring::set_key_image(const uchar *buff,uint length)
 | |
| {
 | |
|   length= uint2korr(buff);			// Real length is here
 | |
|   (void) store((const char*) buff + HA_KEY_BLOB_LENGTH, length, field_charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
 | |
|                                 uint32 max_length) const
 | |
| {
 | |
|   uint32 a_length,b_length;
 | |
| 
 | |
|   if (length_bytes == 1)
 | |
|   {
 | |
|     a_length= (uint) *a_ptr;
 | |
|     b_length= (uint) *b_ptr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     a_length= uint2korr(a_ptr);
 | |
|     b_length= uint2korr(b_ptr);
 | |
|   }
 | |
|   set_if_smaller(a_length, max_length);
 | |
|   set_if_smaller(b_length, max_length);
 | |
|   if (a_length != b_length)
 | |
|     return 1;
 | |
|   return memcmp(a_ptr+length_bytes, b_ptr+length_bytes, a_length);
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field_varstring::make_new_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                                        bool keep_type)
 | |
| {
 | |
|   Field_varstring *res= (Field_varstring*) Field::make_new_field(root,
 | |
|                                                                  new_table,
 | |
|                                                                  keep_type);
 | |
|   if (res)
 | |
|     res->length_bytes= length_bytes;
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                                       uchar *new_ptr, uint32 length,
 | |
|                                       uchar *new_null_ptr, uint new_null_bit)
 | |
| {
 | |
|   Field_varstring *res;
 | |
|   if ((res= (Field_varstring*) Field::new_key_field(root, new_table,
 | |
|                                                     new_ptr, length,
 | |
|                                                     new_null_ptr, new_null_bit)))
 | |
|   {
 | |
|     /* Keys length prefixes are always packed with 2 bytes */
 | |
|     res->length_bytes= 2;
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| bool Field_varstring::is_equal(const Column_definition &new_field) const
 | |
| {
 | |
|   return (new_field.type_handler() == type_handler() &&
 | |
|           new_field.length == field_length &&
 | |
|           new_field.char_length == char_length() &&
 | |
|           !new_field.compression_method() == !compression_method() &&
 | |
|           new_field.charset->eq_collation(field_charset()));
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_varstring::hash_not_null(Hasher *hasher)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   DBUG_ASSERT(!is_null());
 | |
|   uint len=  length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
 | |
|   hasher->add(charset(), ptr + length_bytes, len);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Compress field
 | |
| 
 | |
|   @param[out]    to         destination buffer for compressed data
 | |
|   @param[in]     to_length  size of to
 | |
|   @param[in]     from       data to compress
 | |
|   @param[in]     length     from length
 | |
|   @param[in]     max_length truncate `from' to this length
 | |
|   @param[out]    out_length compessed data length
 | |
|   @param[in]     cs         from character set
 | |
|   @param[in]     nchars     copy no more than "nchars" characters
 | |
| 
 | |
|   In worst case (no compression performed) storage requirement is increased by
 | |
|   1 byte to store header. If it exceeds field length, normal data truncation is
 | |
|   performed.
 | |
| 
 | |
|   Generic compressed header format (1 byte):
 | |
| 
 | |
|   Bits 1-4: method specific bits
 | |
|   Bits 5-8: compression method
 | |
| 
 | |
|   If compression method is 0 then header is immediately followed by
 | |
|   uncompressed data.
 | |
| 
 | |
|   If compression method is zlib:
 | |
| 
 | |
|   Bits 1-3: number of bytes occupied by original data length
 | |
|   Bits   4: true if zlib wrapper not present
 | |
|   Bits 5-8: store 8 (zlib)
 | |
| 
 | |
|   Header is immediately followed by original data length,
 | |
|   followed by compressed data.
 | |
| */
 | |
| 
 | |
| int Field_longstr::compress(char *to, uint to_length,
 | |
|                             const char *from, uint length,
 | |
|                             uint max_length,
 | |
|                             uint *out_length,
 | |
|                             CHARSET_INFO *cs, size_t nchars)
 | |
| {
 | |
|   THD *thd= get_thd();
 | |
|   char *buf;
 | |
|   uint buf_length;
 | |
|   int rc= 0;
 | |
| 
 | |
|   if (String::needs_conversion_on_storage(length, cs, field_charset()) ||
 | |
|       max_length < length)
 | |
|   {
 | |
|     set_if_smaller(max_length, static_cast<ulonglong>(mbmaxlen()) * length + 1);
 | |
|     if (!(buf= (char*) my_malloc(PSI_INSTRUMENT_ME, max_length, MYF(MY_WME))))
 | |
|     {
 | |
|       *out_length= 0;
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|     rc= well_formed_copy_with_check(buf, max_length, cs, from, length,
 | |
|                                     nchars, true, &buf_length);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     buf= const_cast<char*>(from);
 | |
|     buf_length= length;
 | |
|   }
 | |
| 
 | |
|   if (buf_length == 0)
 | |
|     *out_length= 0;
 | |
|   else if (buf_length >= thd->variables.column_compression_threshold &&
 | |
|       (*out_length= compression_method()->compress(thd, to, buf, buf_length)))
 | |
|     status_var_increment(thd->status_var.column_compressions);
 | |
|   else
 | |
|   {
 | |
|     /* Store uncompressed */
 | |
|     to[0]= 0;
 | |
|     if (buf_length < to_length)
 | |
|       memcpy(to + 1, buf, buf_length);
 | |
|     else
 | |
|     {
 | |
|       /* Storing string at blob capacity, e.g. 255 bytes string to TINYBLOB. */
 | |
|       rc= well_formed_copy_with_check(to + 1, to_length - 1, cs, from, length,
 | |
|                                       nchars, true, &buf_length);
 | |
|     }
 | |
|     *out_length= buf_length + 1;
 | |
|   }
 | |
| 
 | |
|   if (buf != from)
 | |
|     my_free(buf);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Memory is allocated only when original data was actually compressed.
 | |
|   Otherwise val_ptr points at data located immediately after header.
 | |
| 
 | |
|   Data can be stored uncompressed if data was shorter than threshold
 | |
|   or compressed data was longer than original data.
 | |
| */
 | |
| 
 | |
| String *Field_longstr::uncompress(String *val_buffer, String *val_ptr,
 | |
|                                   const uchar *from, uint from_length) const
 | |
| {
 | |
|   if (from_length)
 | |
|   {
 | |
|     uchar method= (*from & 0xF0) >> 4;
 | |
| 
 | |
|     /* Uncompressed data */
 | |
|     if (!method)
 | |
|     {
 | |
|       val_ptr->set((const char*) from + 1, from_length - 1, field_charset());
 | |
|       return val_ptr;
 | |
|     }
 | |
| 
 | |
|     if (compression_methods[method].uncompress)
 | |
|     {
 | |
|       if (!compression_methods[method].uncompress(val_buffer, from, from_length,
 | |
|                                                   field_length))
 | |
|       {
 | |
|         val_buffer->set_charset(field_charset());
 | |
|         status_var_increment(get_thd()->status_var.column_decompressions);
 | |
|         return val_buffer;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     It would be better to return 0 in case of errors, but to take the
 | |
|     safer route, let's return a zero string and let the general
 | |
|     handler catch the error.
 | |
|   */
 | |
|   val_ptr->set("", 0, field_charset());
 | |
|   return val_ptr;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_varstring_compressed::store(const char *from, size_t length,
 | |
|                                       CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   uint compressed_length;
 | |
|   int rc= compress((char*) get_data(), field_length, from, (uint) length,
 | |
|                    Field_varstring_compressed::max_display_length(),
 | |
|                    &compressed_length, cs,
 | |
|                    Field_varstring_compressed::char_length());
 | |
|   store_length(compressed_length);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| void Field_varstring_compressed::val_str_from_ptr(String *val, const uchar *ptr) const
 | |
| {
 | |
|   uncompress(val, val, get_data(ptr), get_length(ptr));
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   return uncompress(val_buffer, val_ptr, get_data(), get_length());
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_varstring_compressed::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   String buf;
 | |
|   val_str(&buf, &buf);
 | |
|   return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset(),
 | |
|                                      buf.ptr(), buf.length()).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_varstring_compressed::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   String buf;
 | |
|   val_str(&buf, &buf);
 | |
|   return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(),
 | |
|                                       buf.ptr(), buf.length()).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_varstring_compressed::cmp(const uchar *a_ptr,
 | |
|                                     const uchar *b_ptr) const
 | |
| {
 | |
|   String a, b;
 | |
|   uint a_length, b_length;
 | |
| 
 | |
|   if (length_bytes == 1)
 | |
|   {
 | |
|     a_length= (uint) *a_ptr;
 | |
|     b_length= (uint) *b_ptr;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     a_length= uint2korr(a_ptr);
 | |
|     b_length= uint2korr(b_ptr);
 | |
|   }
 | |
| 
 | |
|   uncompress(&a, &a, a_ptr + length_bytes, a_length);
 | |
|   uncompress(&b, &b, b_ptr + length_bytes, b_length);
 | |
| 
 | |
|   return sortcmp(&a, &b, field_charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| Binlog_type_info Field_varstring_compressed::binlog_type_info() const
 | |
| {
 | |
|   return Binlog_type_info(Field_varstring_compressed::binlog_type(),
 | |
|                           field_length, 2, charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************
 | |
| ** blob type
 | |
| ** A blob is saved as a length and a pointer. The length is stored in the
 | |
| ** packlength slot and may be from 1-4.
 | |
| ****************************************************************************/
 | |
| 
 | |
| Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
 | |
| 		       enum utype unireg_check_arg,
 | |
|                        const LEX_CSTRING *field_name_arg,
 | |
|                        TABLE_SHARE *share, uint blob_pack_length,
 | |
| 		       const DTCollation &collation)
 | |
|   :Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
 | |
|                  null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
 | |
|                  collation),
 | |
|    packlength(blob_pack_length)
 | |
| {
 | |
|   DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently
 | |
|   flags|= BLOB_FLAG;
 | |
|   share->blob_fields++;
 | |
|   /* TODO: why do not fill table->s->blob_field array here? */
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number)
 | |
| {
 | |
|   store_lowendian(i_number, i_ptr, i_packlength);
 | |
| }
 | |
| 
 | |
| 
 | |
| uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) const
 | |
| {
 | |
|   return (uint32)read_lowendian(pos, packlength_arg);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Copy a value from another BLOB field of the same character set.
 | |
|   This method is used by Copy_field, e.g. during ALTER TABLE.
 | |
| */
 | |
| int Field_blob::copy_value(Field_blob *from)
 | |
| {
 | |
|   DBUG_ASSERT(field_charset()->eq_collation(from->charset()));
 | |
|   DBUG_ASSERT(!compression_method() == !from->compression_method());
 | |
|   int rc= 0;
 | |
|   uint32 length= from->get_length();
 | |
|   uchar *data= from->get_ptr();
 | |
|   if (packlength < from->packlength)
 | |
|   {
 | |
|     set_if_smaller(length, Field_blob::max_data_length());
 | |
|     length= (uint32) Well_formed_prefix(field_charset(),
 | |
|                                         (const char *) data, length).length();
 | |
|     rc= report_if_important_data((const char *) data + length,
 | |
|                                  (const char *) data + from->get_length(),
 | |
|                                  true);
 | |
|   }
 | |
|   store_length(length);
 | |
|   bmove(ptr + packlength, (uchar*) &data, sizeof(char*));
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   size_t copy_length, new_length;
 | |
|   uint copy_len;
 | |
|   char *tmp;
 | |
|   char buff[STRING_BUFFER_USUAL_SIZE];
 | |
|   String tmpstr(buff,sizeof(buff), &my_charset_bin);
 | |
|   int rc;
 | |
| 
 | |
|   if (!length)
 | |
|   {
 | |
|     bzero(ptr,Field_blob::pack_length());
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     For min/max fields of statistical data 'table' is set to NULL.
 | |
|     It could not be otherwise as this data is shared by many instances
 | |
|     of the same base table.
 | |
|   */
 | |
| 
 | |
|   if (table && table->blob_storage)    // GROUP_CONCAT with ORDER BY | DISTINCT
 | |
|   {
 | |
|     DBUG_ASSERT(!f_is_hex_escape(flags));
 | |
|     DBUG_ASSERT(field_charset() == cs);
 | |
|     DBUG_ASSERT(length <= max_data_length());
 | |
|     
 | |
|     new_length= length;
 | |
|     copy_length= table->in_use->variables.group_concat_max_len;
 | |
|     if (new_length > copy_length)
 | |
|     {
 | |
|       new_length= Well_formed_prefix(cs,
 | |
|                                      from, copy_length, new_length).length();
 | |
|       table->blob_storage->set_truncated_value(true);
 | |
|     }
 | |
|     if (!(tmp= table->blob_storage->store(from, new_length)))
 | |
|       goto oom_error;
 | |
| 
 | |
|     Field_blob::store_length(new_length);
 | |
|     bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     If the 'from' address is in the range of the temporary 'value'-
 | |
|     object we need to copy the content to a different location or it will be
 | |
|     invalidated when the 'value'-object is reallocated to make room for
 | |
|     the new character set.
 | |
|   */
 | |
|   if (from >= value.ptr() && from <= value.ptr()+value.length())
 | |
|   {
 | |
|     /*
 | |
|       If content of the 'from'-address is cached in the 'value'-object
 | |
|       it is possible that the content needs a character conversion.
 | |
|     */
 | |
|     if (!String::needs_conversion_on_storage(length, cs, field_charset()))
 | |
|     {
 | |
|       Field_blob::store_length(length);
 | |
|       bmove(ptr + packlength, &from, sizeof(char*));
 | |
|       return 0;
 | |
|     }
 | |
|     if (tmpstr.copy(from, length, cs))
 | |
|       goto oom_error;
 | |
|     from= tmpstr.ptr();
 | |
|   }
 | |
| 
 | |
|   new_length= MY_MIN(max_data_length(), mbmaxlen() * length);
 | |
|   if (value.alloc(new_length))
 | |
|     goto oom_error;
 | |
|   tmp= const_cast<char*>(value.ptr());
 | |
| 
 | |
|   if (f_is_hex_escape(flags))
 | |
|   {
 | |
|     copy_length= my_copy_with_hex_escaping(field_charset(),
 | |
|                                            tmp, new_length,
 | |
|                                            from, length);
 | |
|     Field_blob::store_length(copy_length);
 | |
|     bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
 | |
|     return 0;
 | |
|   }
 | |
|   rc= well_formed_copy_with_check((char*) value.ptr(), (uint) new_length,
 | |
|                                   cs, from, length,
 | |
|                                   length, true, ©_len);
 | |
|   value.length(copy_len);
 | |
|   Field_blob::store_length(copy_len);
 | |
|   bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
 | |
| 
 | |
|   return rc;
 | |
| 
 | |
| oom_error:
 | |
|   /* Fatal OOM error */
 | |
|   bzero(ptr,Field_blob::pack_length());
 | |
|   return -1; 
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_blob::hash_not_null(Hasher *hasher)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   DBUG_ASSERT(!is_null());
 | |
|   char *blob;
 | |
|   memcpy(&blob, ptr + packlength, sizeof(char*));
 | |
|   if (blob)
 | |
|     hasher->add(Field_blob::charset(), blob, get_length(ptr));
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_blob::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   char *blob;
 | |
|   memcpy(&blob, ptr+packlength, sizeof(char*));
 | |
|   if (!blob)
 | |
|     return 0.0;
 | |
|   THD *thd= get_thd();
 | |
|   return  Converter_strntod_with_warn(thd, Warn_filter(thd),
 | |
|                                       Field_blob::charset(),
 | |
|                                       blob, get_length(ptr)).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_blob::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   char *blob;
 | |
|   memcpy(&blob, ptr+packlength, sizeof(char*));
 | |
|   if (!blob)
 | |
|     return 0;
 | |
|   THD *thd= get_thd();
 | |
|   return Converter_strntoll_with_warn(thd, Warn_filter(thd),
 | |
|                                       Field_blob::charset(),
 | |
|                                       blob, get_length(ptr)).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
 | |
| 			    String *val_ptr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   char *blob;
 | |
|   memcpy(&blob, ptr+packlength, sizeof(char*));
 | |
|   if (!blob)
 | |
|     val_ptr->set("",0,charset());	// A bit safer than ->length(0)
 | |
|   else
 | |
|     val_ptr->set((const char*) blob,get_length(ptr),charset());
 | |
|   return val_ptr;
 | |
| }
 | |
| 
 | |
| 
 | |
| my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   const char *blob;
 | |
|   size_t length;
 | |
|   memcpy(&blob, ptr+packlength, sizeof(const uchar*));
 | |
|   if (!blob)
 | |
|   {
 | |
|     blob= "";
 | |
|     length= 0;
 | |
|   }
 | |
|   else
 | |
|     length= get_length(ptr);
 | |
| 
 | |
|   THD *thd= get_thd();
 | |
|   Converter_str2my_decimal_with_warn(thd, Warn_filter(thd),
 | |
|                                      E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM,
 | |
|                                      Field_blob::charset(),
 | |
|                                      blob, length, decimal_value);
 | |
|   return decimal_value;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_blob::cmp(const uchar *a,uint32 a_length, const uchar *b,
 | |
| 		    uint32 b_length) const
 | |
| {
 | |
|   return field_charset()->strnncollsp(a, a_length, b, b_length);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_blob::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   uchar *blob1,*blob2;
 | |
|   memcpy(&blob1, a_ptr+packlength, sizeof(char*));
 | |
|   memcpy(&blob2, b_ptr+packlength, sizeof(char*));
 | |
|   size_t a_len= get_length(a_ptr), b_len= get_length(b_ptr);
 | |
|   return cmp(blob1, (uint32)a_len, blob2, (uint32)b_len);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_blob::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
 | |
|                            size_t prefix_char_len) const
 | |
| {
 | |
|   uchar *blob1,*blob2;
 | |
|   memcpy(&blob1, a_ptr+packlength, sizeof(char*));
 | |
|   memcpy(&blob2, b_ptr+packlength, sizeof(char*));
 | |
|   size_t a_len= get_length(a_ptr), b_len= get_length(b_ptr);
 | |
|   return field_charset()->coll->strnncollsp_nchars(field_charset(),
 | |
|                                                    blob1, a_len,
 | |
|                                                    blob2, b_len,
 | |
|                                                    prefix_char_len,
 | |
|                                                    0);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
 | |
| 			   uint32 max_length) const
 | |
| {
 | |
|   char *a,*b;
 | |
|   uint diff;
 | |
|   uint32 a_length,b_length;
 | |
|   memcpy(&a, a_ptr+packlength, sizeof(char*));
 | |
|   memcpy(&b, b_ptr+packlength, sizeof(char*));
 | |
|   a_length=get_length(a_ptr);
 | |
|   if (a_length > max_length)
 | |
|     a_length=max_length;
 | |
|   b_length=get_length(b_ptr);
 | |
|   if (b_length > max_length)
 | |
|     b_length=max_length;
 | |
|   if (uint32 len= MY_MIN(a_length,b_length))
 | |
|     diff= memcmp(a,b,len);
 | |
|   else
 | |
|     diff= 0;
 | |
|   return diff ? diff : (int) (a_length - b_length);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* The following is used only when comparing a key */
 | |
| 
 | |
| uint Field_blob::get_key_image_itRAW(const uchar *ptr_arg, uchar *buff,
 | |
|                                      uint length) const
 | |
| {
 | |
|   size_t blob_length= get_length(ptr_arg);
 | |
|   const uchar *blob= get_ptr(ptr_arg);
 | |
|   size_t local_char_length= length / mbmaxlen();
 | |
|   local_char_length= field_charset()->charpos(blob, blob + blob_length,
 | |
|                                               local_char_length);
 | |
|   set_if_smaller(blob_length, local_char_length);
 | |
| 
 | |
|   if (length > blob_length)
 | |
|   {
 | |
|     /*
 | |
|       Must clear this as we do a memcmp in opt_range.cc to detect
 | |
|       identical keys
 | |
|     */
 | |
|     bzero(buff+HA_KEY_BLOB_LENGTH+blob_length, (length-blob_length));
 | |
|     length=(uint) blob_length;
 | |
|   }
 | |
|   int2store(buff,length);
 | |
|   if (length)
 | |
|     memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length);
 | |
|   return HA_KEY_BLOB_LENGTH+length;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_blob::set_key_image(const uchar *buff,uint length)
 | |
| {
 | |
|   length= uint2korr(buff);
 | |
|   (void) Field_blob::store((const char*) buff+HA_KEY_BLOB_LENGTH, length,
 | |
|                            field_charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length) const
 | |
| {
 | |
|   uchar *blob1;
 | |
|   size_t blob_length=get_length(ptr);
 | |
|   memcpy(&blob1, ptr+packlength, sizeof(char*));
 | |
|   CHARSET_INFO *cs= charset();
 | |
|   size_t local_char_length= max_key_length / cs->mbmaxlen;
 | |
|   local_char_length= cs->charpos(blob1, blob1+blob_length,
 | |
|                                  local_char_length);
 | |
|   set_if_smaller(blob_length, local_char_length);
 | |
|   return Field_blob::cmp(blob1, (uint32)blob_length,
 | |
| 			 key_ptr+HA_KEY_BLOB_LENGTH,
 | |
| 			 uint2korr(key_ptr));
 | |
| }
 | |
| 
 | |
| int Field_blob::key_cmp(const uchar *a,const uchar *b) const
 | |
| {
 | |
|   return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a),
 | |
| 			 b+HA_KEY_BLOB_LENGTH, uint2korr(b));
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifndef DBUG_OFF
 | |
| /* helper to assert that new_table->blob_storage is NULL */
 | |
| static struct blob_storage_check
 | |
| {
 | |
|   union { bool b; intptr p; } val;
 | |
|   blob_storage_check() { val.p= -1; val.b= false; }
 | |
| } blob_storage_check;
 | |
| #endif
 | |
| Field *Field_blob::make_new_field(MEM_ROOT *root, TABLE *newt, bool keep_type)
 | |
| {
 | |
|   DBUG_ASSERT((intptr(newt->blob_storage) & blob_storage_check.val.p) == 0);
 | |
|   if (newt->group_concat)
 | |
|     return new (root) Field_blob(field_length, maybe_null(), &field_name,
 | |
|                                  charset());
 | |
|   return Field::make_new_field(root, newt, keep_type);
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                                  uchar *new_ptr, uint32 length,
 | |
|                                  uchar *new_null_ptr, uint new_null_bit)
 | |
| {
 | |
|   Field_varstring *res= new (root) Field_varstring(new_ptr, length, 2,
 | |
|                                                    new_null_ptr,
 | |
|                                                    new_null_bit, Field::NONE,
 | |
|                                                    &field_name,
 | |
|                                                    table->s, charset());
 | |
|   res->init(new_table);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Save the field metadata for blob fields.
 | |
| 
 | |
|    Saves the pack length in the first byte of the field metadata array
 | |
|    at index of *metadata_ptr.
 | |
| 
 | |
|    @param   metadata_ptr   First byte of field metadata
 | |
| 
 | |
|    @returns number of bytes written to metadata_ptr
 | |
| */
 | |
| Binlog_type_info Field_blob::binlog_type_info() const
 | |
| {
 | |
|   DBUG_ASSERT(Field_blob::type() == binlog_type());
 | |
|   return Binlog_type_info(Field_blob::type(), pack_length_no_ptr(), 1,
 | |
|                           charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| uint32 Field_blob::sort_length() const
 | |
| {
 | |
|   return packlength == 4 ?
 | |
|     UINT_MAX32 :
 | |
|     (uint32) field_length + sort_suffix_length();
 | |
| }
 | |
| 
 | |
| 
 | |
| uint32 Field_blob::sort_suffix_length() const
 | |
| {
 | |
|   return field_charset() == &my_charset_bin ?  packlength : 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_blob::sort_string(uchar *to,uint length)
 | |
| {
 | |
|   String buf;
 | |
| 
 | |
|   val_str(&buf, &buf);
 | |
|   if (!buf.length() && field_charset()->pad_char == 0)
 | |
|     bzero(to,length);
 | |
|   else
 | |
|   {
 | |
|     if (field_charset() == &my_charset_bin)
 | |
|     {
 | |
|       /*
 | |
|         Store length of blob last in blob to shorter blobs before longer blobs
 | |
|       */
 | |
|       length-= packlength;
 | |
|       store_bigendian(buf.length(), to + length, packlength);
 | |
|     }
 | |
| 
 | |
| #ifdef DBUG_ASSERT_EXISTS
 | |
|     size_t rc=
 | |
| #endif
 | |
|     field_charset()->strnxfrm(to, length, length,
 | |
|                               (const uchar *) buf.ptr(), buf.length(),
 | |
|                               MY_STRXFRM_PAD_WITH_SPACE |
 | |
|                               MY_STRXFRM_PAD_TO_MAXLEN);
 | |
|     DBUG_ASSERT(rc == length);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Return the data type handler, according to packlength.
 | |
|   Implemented in field.cc rather than in field.h
 | |
|   to avoid exporting type_handler_xxx with MYSQL_PLUGIN_IMPORT.
 | |
| */
 | |
| const Type_handler *Field_blob::type_handler() const
 | |
| {
 | |
|   /*
 | |
|     This is a temporary solution and will be fixed soon (in 10.9?).
 | |
|     Type_handler_*blob_json will provide its own Field_blob_json
 | |
|     and Field_blob_compressed_json.
 | |
|   */
 | |
|   if (Type_handler_json_common::has_json_valid_constraint(this))
 | |
|     return Type_handler_json_common::
 | |
|              json_blob_type_handler_by_length_bytes(packlength);
 | |
| 
 | |
|   switch (packlength) {
 | |
|   case 1: return &type_handler_tiny_blob;
 | |
|   case 2: return &type_handler_blob;
 | |
|   case 3: return &type_handler_medium_blob;
 | |
|   }
 | |
|   return &type_handler_long_blob;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_blob::sql_type(String &res) const
 | |
| {
 | |
|   const char *str;
 | |
|   uint length;
 | |
|   switch (packlength) {
 | |
|   default: str="tiny"; length=4; break;
 | |
|   case 2:  str="";     length=0; break;
 | |
|   case 3:  str="medium"; length= 6; break;
 | |
|   case 4:  str="long";  length=4; break;
 | |
|   }
 | |
|   res.set_ascii(str,length);
 | |
|   if (charset() == &my_charset_bin)
 | |
|   {
 | |
|     res.append(STRING_WITH_LEN("blob"));
 | |
|     if (packlength == 2 && (get_thd()->variables.sql_mode & MODE_ORACLE))
 | |
|       res.append(STRING_WITH_LEN("(65535)"));
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     res.append(STRING_WITH_LEN("text"));
 | |
|   }
 | |
| }
 | |
| 
 | |
| uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
 | |
| {
 | |
|   uint32 length=get_length(from, packlength);			// Length of from string
 | |
| 
 | |
|   /*
 | |
|     Store max length, which will occupy packlength bytes. If the max
 | |
|     length given is smaller than the actual length of the blob, we
 | |
|     just store the initial bytes of the blob.
 | |
|   */
 | |
|   store_length(to, packlength, MY_MIN(length, max_length));
 | |
| 
 | |
|   /*
 | |
|     Store the actual blob data, which will occupy 'length' bytes.
 | |
|    */
 | |
|   if (length > 0)
 | |
|   {
 | |
|     from= get_ptr(from);
 | |
|     memcpy(to+packlength, from,length);
 | |
|   }
 | |
|   return to+packlength+length;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Unpack a blob field from row data.
 | |
| 
 | |
|    This method is used to unpack a blob field from a master whose size of 
 | |
|    the field is less than that of the slave. Note: This method is included
 | |
|    to satisfy inheritance rules, but is not needed for blob fields. It
 | |
|    simply is used as a pass-through to the original unpack() method for
 | |
|    blob fields.
 | |
| 
 | |
|    @param   to         Destination of the data
 | |
|    @param   from       Source of the data
 | |
|    @param   param_data @c TRUE if base types should be stored in little-
 | |
|                        endian format, @c FALSE if native format should
 | |
|                        be used.
 | |
| 
 | |
|    @return  New pointer into memory based on from + length of the data
 | |
| */
 | |
| 
 | |
| const uchar *Field_blob::unpack(uchar *to, const uchar *from,
 | |
|                                 const uchar *from_end, uint param_data)
 | |
| {
 | |
|   DBUG_ENTER("Field_blob::unpack");
 | |
|   DBUG_PRINT("enter", ("to: %p; from: %p; param_data: %u",
 | |
|                        to, from, param_data));
 | |
|   uint const master_packlength=
 | |
|     param_data > 0 ? param_data & 0xFF : packlength;
 | |
|   if (from + master_packlength > from_end)
 | |
|     DBUG_RETURN(0);                             // Error in data
 | |
|   uint32 const length= get_length(from, master_packlength);
 | |
|   DBUG_DUMP("packed", from, length + master_packlength);
 | |
|   if (from + master_packlength + length > from_end)
 | |
|     DBUG_RETURN(0);
 | |
|   set_ptr(length, const_cast<uchar*> (from) + master_packlength);
 | |
|   DBUG_RETURN(from + master_packlength + length);
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Field_blob::packed_col_length(const uchar *data_ptr, uint length)
 | |
| {
 | |
|   if (length > 255)
 | |
|     return uint2korr(data_ptr)+2;
 | |
|   return (uint) *data_ptr + 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Field_blob::max_packed_col_length(uint max_length)
 | |
| {
 | |
|   return (max_length > 255 ? 2 : 1)+max_length;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Blob fields are regarded equal if they have same character set,
 | |
|   same blob store length and if either both are compressed or both are
 | |
|   uncompressed.
 | |
|   The logic for compression is that we don't have to uncompress and compress
 | |
|   again an already compressed field just because compression method changes.
 | |
| */
 | |
| 
 | |
| bool Field_blob::is_equal(const Column_definition &new_field) const
 | |
| {
 | |
|   return (new_field.type_handler() == type_handler() &&
 | |
|           !new_field.compression_method() == !compression_method() &&
 | |
|           new_field.pack_length == pack_length() &&
 | |
|           new_field.charset->eq_collation(field_charset()));
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_blob::make_send_field(Send_field *field)
 | |
| {
 | |
|   /*
 | |
|     Historically all BLOB variant Fields are displayed as MYSQL_TYPE_BLOB
 | |
|     in the result set metadata. Note, Item can work differently and
 | |
|     display the exact BLOB type, such as
 | |
|     MYSQL_TYPE_{TINY_BLOB|BLOB|MEDIUM_BLOB|LONG_BLOB}.
 | |
|     QQ: this should be made consistent eventually.
 | |
|   */
 | |
|   Field_longstr::make_send_field(field);
 | |
|   field->set_handler(&type_handler_blob);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_blob::make_empty_rec_store_default_value(THD *thd, Item *item)
 | |
| {
 | |
|   DBUG_ASSERT(flags & BLOB_FLAG);
 | |
|   int res= item->save_in_field(this, true);
 | |
|   DBUG_ASSERT(res != 3); // Field_blob never returns 3
 | |
|   if (res)
 | |
|     return true; // E.g. truncation happened
 | |
|   reset(); // Clear the pointer to a String, it should not be written to frm
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_blob_compressed::store(const char *from, size_t length,
 | |
|                                  CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   uint compressed_length;
 | |
|   uint max_length= max_data_length();
 | |
|   uint to_length= (uint) MY_MIN(max_length, mbmaxlen() * length + 1);
 | |
|   String tmp(from, length, cs);
 | |
|   int rc;
 | |
| 
 | |
|   if (from >= value.ptr() && from <= value.end() && tmp.copy(from, length, cs))
 | |
|     goto oom;
 | |
| 
 | |
|   if (value.alloc(to_length))
 | |
|     goto oom;
 | |
| 
 | |
|   rc= compress((char*) value.ptr(), to_length, tmp.ptr(), (uint) length,
 | |
|                max_length, &compressed_length, cs, (uint) length);
 | |
|   set_ptr(compressed_length, (uchar*) value.ptr());
 | |
|   return rc;
 | |
| 
 | |
| oom:
 | |
|   set_ptr((uint32) 0, NULL);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_blob_compressed::val_str(String *val_buffer, String *val_ptr)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   return uncompress(val_buffer, val_ptr, get_ptr(), get_length());
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_blob_compressed::val_real(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   String buf;
 | |
|   val_str(&buf, &buf);
 | |
|   return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset(),
 | |
|                                      buf.ptr(), buf.length()).result();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_blob_compressed::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   THD *thd= get_thd();
 | |
|   String buf;
 | |
|   val_str(&buf, &buf);
 | |
|   return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(),
 | |
|                                       buf.ptr(), buf.length()).result();
 | |
| }
 | |
| 
 | |
| Binlog_type_info Field_blob_compressed::binlog_type_info() const
 | |
| {
 | |
|   return Binlog_type_info(Field_blob_compressed::binlog_type(),
 | |
|                           pack_length_no_ptr(), 1, charset());
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
| ** enum type.
 | |
| ** This is a string which only can have a selection of different values.
 | |
| ** If one uses this string in a number context one gets the type number.
 | |
| ****************************************************************************/
 | |
| 
 | |
| sql_mode_t Field_enum::can_handle_sql_mode_dependency_on_store() const
 | |
| {
 | |
|   return MODE_PAD_CHAR_TO_FULL_LENGTH;
 | |
| }
 | |
| 
 | |
| 
 | |
| enum ha_base_keytype Field_enum::key_type() const
 | |
| {
 | |
|   switch (packlength) {
 | |
|   default: return HA_KEYTYPE_BINARY;
 | |
|   case 2: return HA_KEYTYPE_USHORT_INT;
 | |
|   case 3: return HA_KEYTYPE_UINT24;
 | |
|   case 4: return HA_KEYTYPE_ULONG_INT;
 | |
|   case 8: return HA_KEYTYPE_ULONGLONG;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void Field_enum::store_type(ulonglong value)
 | |
| {
 | |
|   store_lowendian(value, ptr, packlength);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @note
 | |
|     Storing a empty string in a enum field gives a warning
 | |
|     (if there isn't a empty value in the enum)
 | |
| */
 | |
| 
 | |
| int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int err= 0;
 | |
|   char buff[STRING_BUFFER_USUAL_SIZE];
 | |
|   String tmpstr(buff,sizeof(buff), &my_charset_bin);
 | |
| 
 | |
|   /* Convert character set if necessary */
 | |
|   if (String::needs_conversion_on_storage(length, cs, field_charset()))
 | |
|   { 
 | |
|     uint dummy_errors;
 | |
|     tmpstr.copy(from, length, cs, field_charset(), &dummy_errors);
 | |
|     from= tmpstr.ptr();
 | |
|     length=  tmpstr.length();
 | |
|   }
 | |
| 
 | |
|   /* Remove end space */
 | |
|   length= (uint) field_charset()->lengthsp(from, length);
 | |
|   uint tmp=find_type2(typelib, from, length, field_charset());
 | |
|   if (!tmp)
 | |
|   {
 | |
|     if (length < 6) // Can't be more than 99999 enums
 | |
|     {
 | |
|       /* This is for reading numbers with LOAD DATA INFILE */
 | |
|       char *end;
 | |
|       tmp=(uint) cs->strntoul(from,length,10,&end,&err);
 | |
|       if (err || end != from+length || tmp > typelib->count)
 | |
|       {
 | |
| 	tmp=0;
 | |
| 	set_warning(WARN_DATA_TRUNCATED, 1);
 | |
| 	err= 1;
 | |
|       }
 | |
|       if ((get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) && !length)
 | |
|         err= 0;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|       err= 1;
 | |
|     }
 | |
|   }
 | |
|   store_type((ulonglong) tmp);
 | |
|   return err;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_enum::store(double nr)
 | |
| {
 | |
|   return Field_enum::store((longlong) nr, FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_enum::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   if ((ulonglong) nr > typelib->count || nr == 0)
 | |
|   {
 | |
|     set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|     if (nr != 0 || get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
 | |
|     {
 | |
|       nr= 0;
 | |
|       error= 1;
 | |
|     }
 | |
|   }
 | |
|   store_type((ulonglong) (uint) nr);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_enum::val_real(void)
 | |
| {
 | |
|   return (double) Field_enum::val_int();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_enum::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   return val_int(ptr);
 | |
| }
 | |
| 
 | |
| longlong Field_enum::val_int(const uchar *real_ptr) const
 | |
| {
 | |
|   return read_lowendian(real_ptr, packlength);
 | |
| }
 | |
| 
 | |
| /**
 | |
|    Save the field metadata for enum fields.
 | |
| 
 | |
|    Saves the real type in the first byte and the pack length in the 
 | |
|    second byte of the field metadata array at index of *metadata_ptr and
 | |
|    *(metadata_ptr + 1).
 | |
| 
 | |
|    @param   metadata_ptr   First byte of field metadata
 | |
| 
 | |
|    @returns number of bytes written to metadata_ptr
 | |
| */
 | |
| Binlog_type_info Field_enum::binlog_type_info() const
 | |
| {
 | |
|   DBUG_ASSERT(Field_enum::type() == binlog_type());
 | |
|   return Binlog_type_info(Field_enum::type(), real_type() + (pack_length() << 8),
 | |
|                           2, charset(), (TYPELIB *)get_typelib(), NULL);
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_enum::val_str(String *val_buffer __attribute__((unused)),
 | |
| 			    String *val_ptr)
 | |
| {
 | |
|   uint tmp=(uint) Field_enum::val_int();
 | |
|   if (!tmp || tmp > typelib->count)
 | |
|     val_ptr->set("", 0, field_charset());
 | |
|   else
 | |
|     val_ptr->set((const char*) typelib->type_names[tmp-1],
 | |
| 		 typelib->type_lengths[tmp-1],
 | |
| 		 field_charset());
 | |
|   return val_ptr;
 | |
| }
 | |
| 
 | |
| int Field_enum::cmp(const uchar *a_ptr, const uchar *b_ptr) const
 | |
| {
 | |
|   ulonglong a=Field_enum::val_int(a_ptr);
 | |
|   ulonglong b=Field_enum::val_int(b_ptr);
 | |
|   return (a < b) ? -1 : (a > b) ? 1 : 0;
 | |
| }
 | |
| 
 | |
| void Field_enum::sort_string(uchar *to,uint length __attribute__((unused)))
 | |
| {
 | |
|   ulonglong value=Field_enum::val_int();
 | |
|   to+=packlength-1;
 | |
|   for (uint i=0 ; i < packlength ; i++)
 | |
|   {
 | |
|     *to-- = (uchar) (value & 255);
 | |
|     value>>=8;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_enum::sql_type(String &res) const
 | |
| {
 | |
|   char buffer[255];
 | |
|   String enum_item(buffer, sizeof(buffer), res.charset());
 | |
| 
 | |
|   res.length(0);
 | |
|   res.append(STRING_WITH_LEN("enum("));
 | |
| 
 | |
|   bool flag=0;
 | |
|   uint *len= typelib->type_lengths;
 | |
|   for (const char **pos= typelib->type_names; *pos; pos++, len++)
 | |
|   {
 | |
|     uint dummy_errors;
 | |
|     if (flag)
 | |
|       res.append(',');
 | |
|     /* convert to res.charset() == utf8, then quote */
 | |
|     enum_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
 | |
|     append_unescaped(&res, enum_item.ptr(), enum_item.length());
 | |
|     flag= 1;
 | |
|   }
 | |
|   res.append(')');
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                                   bool keep_type)
 | |
| {
 | |
|   Field_enum *res= (Field_enum*) Field::make_new_field(root, new_table,
 | |
|                                                        keep_type);
 | |
|   if (res)
 | |
|     res->typelib= copy_typelib(root, typelib);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|    set type.
 | |
|    This is a string which can have a collection of different values.
 | |
|    Each string value is separated with a ','.
 | |
|    For example "One,two,five"
 | |
|    If one uses this string in a number context one gets the bits as a longlong
 | |
|    number.
 | |
| */
 | |
| 
 | |
| 
 | |
| int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   bool got_warning= 0;
 | |
|   int err= 0;
 | |
|   char *not_used;
 | |
|   uint not_used2;
 | |
|   char buff[STRING_BUFFER_USUAL_SIZE];
 | |
|   String tmpstr(buff,sizeof(buff), &my_charset_bin);
 | |
| 
 | |
|   /* Convert character set if necessary */
 | |
|   if (String::needs_conversion_on_storage(length, cs, field_charset()))
 | |
|   { 
 | |
|     uint dummy_errors;
 | |
|     tmpstr.copy(from, length, cs, field_charset(), &dummy_errors);
 | |
|     from= tmpstr.ptr();
 | |
|     length=  tmpstr.length();
 | |
|   }
 | |
|   ulonglong tmp= find_set(typelib, from, length, field_charset(),
 | |
|                           ¬_used, ¬_used2, &got_warning);
 | |
|   if (!tmp && length && length < 22)
 | |
|   {
 | |
|     /* This is for reading numbers with LOAD DATA INFILE */
 | |
|     char *end;
 | |
|     tmp= cs->strntoull(from, length, 10, &end, &err);
 | |
|     if (err || end != from + length)
 | |
|     {
 | |
|       set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|       store_type(0);
 | |
|       return 1;
 | |
|     }
 | |
|     return Field_set::store((longlong) tmp, true/*unsigned*/);
 | |
|   }
 | |
|   else if (got_warning)
 | |
|     set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|   store_type(tmp);
 | |
|   return err;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_set::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int error= 0;
 | |
|   ulonglong max_nr;
 | |
| 
 | |
|   if (sizeof(ulonglong)*8 <= typelib->count)
 | |
|     max_nr= ULONGLONG_MAX;
 | |
|   else
 | |
|     max_nr= (1ULL << typelib->count) - 1;
 | |
| 
 | |
|   if ((ulonglong) nr > max_nr)
 | |
|   {
 | |
|     nr&= max_nr;
 | |
|     set_warning(WARN_DATA_TRUNCATED, 1);
 | |
|     error=1;
 | |
|   }
 | |
|   store_type((ulonglong) nr);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *Field_set::val_str(String *val_buffer,
 | |
| 			   String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   ulonglong tmp=(ulonglong) Field_enum::val_int();
 | |
|   uint bitnr=0;
 | |
| 
 | |
|   val_buffer->copy("", 0, field_charset());
 | |
| 
 | |
|   while (tmp && bitnr < (uint) typelib->count)
 | |
|   {
 | |
|     if (tmp & 1)
 | |
|     {
 | |
|       if (val_buffer->length())
 | |
| 	val_buffer->append(&field_separator, 1, &my_charset_latin1);
 | |
|       String str(typelib->type_names[bitnr], typelib->type_lengths[bitnr],
 | |
| 		 field_charset());
 | |
|       val_buffer->append(str);
 | |
|     }
 | |
|     tmp>>=1;
 | |
|     bitnr++;
 | |
|   }
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_set::sql_type(String &res) const
 | |
| {
 | |
|   char buffer[255];
 | |
|   String set_item(buffer, sizeof(buffer), res.charset());
 | |
| 
 | |
|   res.length(0);
 | |
|   res.append(STRING_WITH_LEN("set("));
 | |
| 
 | |
|   bool flag=0;
 | |
|   uint *len= typelib->type_lengths;
 | |
|   for (const char **pos= typelib->type_names; *pos; pos++, len++)
 | |
|   {
 | |
|     uint dummy_errors;
 | |
|     if (flag)
 | |
|       res.append(',');
 | |
|     /* convert to res.charset() == utf8, then quote */
 | |
|     set_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
 | |
|     append_unescaped(&res, set_item.ptr(), set_item.length());
 | |
|     flag= 1;
 | |
|   }
 | |
|   res.append(')');
 | |
| }
 | |
| 
 | |
| Binlog_type_info Field_set::binlog_type_info() const
 | |
| {
 | |
|   DBUG_ASSERT(Field_set::type() == binlog_type());
 | |
|   return Binlog_type_info(Field_set::type(), real_type()
 | |
|            + (pack_length() << 8), 2, charset(), NULL, (TYPELIB *)get_typelib());
 | |
| }
 | |
| 
 | |
| /**
 | |
|   @retval
 | |
|     1  if the fields are equally defined
 | |
|   @retval
 | |
|     0  if the fields are unequally defined
 | |
| */
 | |
| 
 | |
| bool Field::eq_def(const Field *field) const
 | |
| {
 | |
|   if (real_type() != field->real_type() || charset() != field->charset() ||
 | |
|       pack_length() != field->pack_length())
 | |
|     return 0;
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Compare the first t1::count type names.
 | |
| 
 | |
|   @return TRUE if the type names of t1 match those of t2. FALSE otherwise.
 | |
| */
 | |
| 
 | |
| static bool compare_type_names(CHARSET_INFO *charset, const TYPELIB *t1,
 | |
|                                                       const TYPELIB *t2)
 | |
| {
 | |
|   for (uint i= 0; i < t1->count; i++)
 | |
|     if (charset->strnncoll(t1->type_names[i], t1->type_lengths[i],
 | |
|                            t2->type_names[i], t2->type_lengths[i]))
 | |
|       return FALSE;
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   @return
 | |
|   returns 1 if the fields are equally defined
 | |
| */
 | |
| 
 | |
| bool Field_enum::eq_def(const Field *field) const
 | |
| {
 | |
|   const TYPELIB *values;
 | |
| 
 | |
|   if (!Field::eq_def(field))
 | |
|     return FALSE;
 | |
| 
 | |
|   values= ((Field_enum*) field)->typelib;
 | |
| 
 | |
|   /* Definition must be strictly equal. */
 | |
|   if (typelib->count != values->count)
 | |
|     return FALSE;
 | |
| 
 | |
|   return compare_type_names(field_charset(), typelib, values);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check whether two fields can be considered 'equal' for table
 | |
|   alteration purposes. Fields are equal if they retain the same
 | |
|   pack length and if new members are added to the end of the list.
 | |
| 
 | |
|   @return true if fields are compatible.
 | |
|           false otherwise.
 | |
| */
 | |
| 
 | |
| bool Field_enum::is_equal(const Column_definition &new_field) const
 | |
| {
 | |
|   const TYPELIB *values= new_field.interval;
 | |
| 
 | |
|   /*
 | |
|     The fields are compatible if they have the same flags,
 | |
|     type, charset and have the same underlying length.
 | |
|   */
 | |
|   if (new_field.type_handler() != type_handler() ||
 | |
|       !new_field.charset->eq_collation(field_charset()) ||
 | |
|       new_field.pack_length != pack_length())
 | |
|     return false;
 | |
| 
 | |
|   /*
 | |
|     Changing the definition of an ENUM or SET column by adding a new
 | |
|     enumeration or set members to the end of the list of valid member
 | |
|     values only alters table metadata and not table data.
 | |
|   */
 | |
|   if (typelib->count > values->count)
 | |
|     return false;
 | |
| 
 | |
|   /* Check whether there are modification before the end. */
 | |
|   if (! compare_type_names(field_charset(), typelib, new_field.interval))
 | |
|     return false;
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| uchar *Field_enum::pack(uchar *to, const uchar *from, uint max_length)
 | |
| {
 | |
|   DBUG_ENTER("Field_enum::pack");
 | |
|   DBUG_PRINT("debug", ("packlength: %d", packlength));
 | |
|   DBUG_DUMP("from", from, packlength);
 | |
|   DBUG_RETURN(pack_int(to, from, packlength));
 | |
| }
 | |
| 
 | |
| const uchar *Field_enum::unpack(uchar *to, const uchar *from, 
 | |
|                                 const uchar *from_end, uint param_data)
 | |
| {
 | |
|   DBUG_ENTER("Field_enum::unpack");
 | |
|   DBUG_PRINT("debug", ("packlength: %d", packlength));
 | |
|   DBUG_DUMP("from", from, packlength);
 | |
|   DBUG_RETURN(unpack_int(to, from, from_end, packlength));
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @return
 | |
|   returns 1 if the fields are equally defined
 | |
| */
 | |
| bool Field_num::eq_def(const Field *field) const
 | |
| {
 | |
|   if (!Field::eq_def(field))
 | |
|     return 0;
 | |
|   Field_num *from_num= (Field_num*) field;
 | |
| 
 | |
|   if (unsigned_flag != from_num->unsigned_flag ||
 | |
|       (zerofill && !from_num->zerofill && !zero_pack()) ||
 | |
|       dec != from_num->dec)
 | |
|     return 0;
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check whether two numeric fields can be considered 'equal' for table
 | |
|   alteration purposes. Fields are equal if they are of the same type
 | |
|   and retain the same pack length.
 | |
| */
 | |
| 
 | |
| bool Field_num::is_equal(const Column_definition &new_field) const
 | |
| {
 | |
|   if (((new_field.flags & UNSIGNED_FLAG) != (flags & UNSIGNED_FLAG)) ||
 | |
|       ((new_field.flags & AUTO_INCREMENT_FLAG) > (flags & AUTO_INCREMENT_FLAG)))
 | |
|     return false;
 | |
| 
 | |
|   const Type_handler *th= type_handler(), *new_th = new_field.type_handler();
 | |
| 
 | |
|   if (th == new_th && new_field.pack_length == pack_length())
 | |
|     return true;
 | |
|   /* FIXME: Test and consider returning true for the following:
 | |
|   TINYINT UNSIGNED to BIT(8)
 | |
|   SMALLINT UNSIGNED to BIT(16)
 | |
|   MEDIUMINT UNSIGNED to BIT(24)
 | |
|   INT UNSIGNED to BIT(32)
 | |
|   BIGINT UNSIGNED to BIT(64)
 | |
| 
 | |
|   BIT(1..7) to TINYINT, or BIT(1..8) to TINYINT UNSIGNED
 | |
|   BIT(9..15) to SMALLINT, or BIT(9..16) to SMALLINT UNSIGNED
 | |
|   BIT(17..23) to MEDIUMINT, or BIT(17..24) to MEDIUMINT UNSIGNED
 | |
|   BIT(25..31) to INT, or BIT(25..32) to INT UNSIGNED
 | |
|   BIT(57..63) to BIGINT, or BIT(57..64) to BIGINT UNSIGNED
 | |
| 
 | |
|   Note: InnoDB stores integers in big-endian format, and BIT appears
 | |
|   to use big-endian format. For storage engines that use little-endian
 | |
|   format for integers, we can only return true for the TINYINT
 | |
|   conversion. */
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| Data_type_compatibility
 | |
| Field_enum::can_optimize_range_or_keypart_ref(const Item_bool_func *cond,
 | |
|                                               const Item *item) const
 | |
| {
 | |
|   switch (item->cmp_type())
 | |
|   {
 | |
|   case TIME_RESULT:
 | |
|     return Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
 | |
|   case INT_RESULT:
 | |
|   case DECIMAL_RESULT:
 | |
|   case REAL_RESULT:
 | |
|     return Data_type_compatibility::OK;
 | |
|   case STRING_RESULT:
 | |
|     return (charset()->eq_collation(cond->compare_collation()) ?
 | |
|             Data_type_compatibility::OK :
 | |
|             Data_type_compatibility::INCOMPATIBLE_COLLATION);
 | |
|   case ROW_RESULT:
 | |
|     DBUG_ASSERT(0);
 | |
|     break;
 | |
|   }
 | |
|   return Data_type_compatibility::INCOMPATIBLE_DATA_TYPE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Bit field.
 | |
| 
 | |
|   We store the first 0 - 6 uneven bits among the null bits 
 | |
|   at the start of the record. The rest bytes are stored in 
 | |
|   the record itself.
 | |
| 
 | |
|   For example:
 | |
| 
 | |
|   CREATE TABLE t1 (a int, b bit(17), c bit(21) not null, d bit(8));
 | |
|   We would store data  as follows in the record:
 | |
| 
 | |
|   Byte        Bit
 | |
|   1           7 - reserve for delete
 | |
|               6 - null bit for 'a'
 | |
|               5 - null bit for 'b'
 | |
|               4 - first (high) bit of 'b'
 | |
|               3 - first (high) bit of 'c'
 | |
|               2 - second bit of 'c'
 | |
|               1 - third bit of 'c'
 | |
|               0 - forth bit of 'c'
 | |
|   2           7 - firth bit of 'c'
 | |
|               6 - null bit for 'd'
 | |
|   3 - 6       four bytes for 'a'
 | |
|   7 - 8       two bytes for 'b'
 | |
|   9 - 10      two bytes for 'c'
 | |
|   11          one byte for 'd'
 | |
| */
 | |
| 
 | |
| Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 | |
|                      uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
 | |
|                      enum utype unireg_check_arg,
 | |
|                      const LEX_CSTRING *field_name_arg)
 | |
|   : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
 | |
|           unireg_check_arg, field_name_arg),
 | |
|     bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7),
 | |
|     bytes_in_rec(len_arg / 8)
 | |
| {
 | |
|   DBUG_ENTER("Field_bit::Field_bit");
 | |
|   DBUG_PRINT("enter", ("ptr_arg: %p, null_ptr_arg: %p, len_arg: %u, bit_len: %u, bytes_in_rec: %u",
 | |
|                        ptr_arg, null_ptr_arg, len_arg, bit_len, bytes_in_rec));
 | |
|   flags|= UNSIGNED_FLAG;
 | |
|   /*
 | |
|     Ensure that Field::eq() can distinguish between two different bit fields.
 | |
|     (two bit fields that are not null, may have same ptr and null_ptr)
 | |
|   */
 | |
|   if (!null_ptr_arg)
 | |
|     null_bit= bit_ofs_arg;
 | |
|   DBUG_VOID_RETURN;
 | |
| }
 | |
| 
 | |
| 
 | |
| const DTCollation & Field_bit::dtcollation() const
 | |
| {
 | |
|   static DTCollation tmp(&my_charset_bin,
 | |
|                          DERIVATION_IMPLICIT, MY_REPERTOIRE_UNICODE30);
 | |
|   return tmp;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   This method always calculates hash over 8 bytes.
 | |
|   This is different from how the HEAP engine calculate hash:
 | |
|   HEAP takes into account the actual octet size, so say for BIT(18)
 | |
|   it calculates hash over three bytes only:
 | |
|   - the incomplete byte with bits 16..17
 | |
|   - the two full bytes with bits 0..15
 | |
|   See hp_rec_hashnr(), hp_hashnr() for details.
 | |
| 
 | |
|   The HEAP way is more efficient, especially for short lengths.
 | |
|   Let's consider fixing Field_bit eventually to do it in the HEAP way,
 | |
|   with proper measures to upgrade partitioned tables easy.
 | |
| */
 | |
| void Field_bit::hash_not_null(Hasher *hasher)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   DBUG_ASSERT(!is_null());
 | |
|   longlong value= Field_bit::val_int();
 | |
|   uchar tmp[8];
 | |
|   mi_int8store(tmp,value);
 | |
|   hasher->add(&my_charset_bin, tmp, 8);
 | |
| }
 | |
| 
 | |
| 
 | |
| size_t
 | |
| Field_bit::do_last_null_byte() const
 | |
| {
 | |
|   /*
 | |
|     Code elsewhere is assuming that bytes are 8 bits, so I'm using
 | |
|     that value instead of the correct one: CHAR_BIT.
 | |
| 
 | |
|     REFACTOR SUGGESTION (Matz): Change to use the correct number of
 | |
|     bits. On systems with CHAR_BIT > 8 (not very common), the storage
 | |
|     will lose the extra bits.
 | |
|   */
 | |
|   DBUG_PRINT("test", ("bit_ofs: %d, bit_len: %d  bit_ptr: %p",
 | |
|                       bit_ofs, bit_len, bit_ptr));
 | |
|   uchar *result;
 | |
|   if (bit_len == 0)
 | |
|     result= null_ptr;
 | |
|   else if (bit_ofs + bit_len > 8)
 | |
|     result= bit_ptr + 1;
 | |
|   else
 | |
|     result= bit_ptr;
 | |
| 
 | |
|   if (result)
 | |
|     return (size_t) (result - table->record[0]) + 1;
 | |
|   return LAST_NULL_BYTE_UNDEF;
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table,
 | |
|                                 uchar *new_ptr, uint32 length, 
 | |
|                                 uchar *new_null_ptr, uint new_null_bit)
 | |
| {
 | |
|   Field_bit *res;
 | |
|   if ((res= (Field_bit*) Field::new_key_field(root, new_table, new_ptr, length,
 | |
|                                               new_null_ptr, new_null_bit)))
 | |
|   {
 | |
|     /* Move bits normally stored in null_pointer to new_ptr */
 | |
|     res->bit_ptr= new_ptr;
 | |
|     res->bit_ofs= 0;
 | |
|     if (bit_len)
 | |
|       res->ptr++;                               // Store rest of data here
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_bit::is_equal(const Column_definition &new_field) const
 | |
| {
 | |
|   return new_field.type_handler() == type_handler() &&
 | |
|          new_field.length == max_display_length();
 | |
| }
 | |
| 
 | |
|                        
 | |
| int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int delta;
 | |
| 
 | |
|   for (; length && !*from; from++, length--)          // skip left 0's
 | |
|     ;
 | |
|   delta= (int)(bytes_in_rec - length);
 | |
| 
 | |
|   if (delta < -1 ||
 | |
|       (delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) ||
 | |
|       (!bit_len && delta < 0))
 | |
|   {
 | |
|     set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len);
 | |
|     memset(ptr, 0xff, bytes_in_rec);
 | |
|     if (get_thd()->really_abort_on_warning())
 | |
|       set_warning(ER_DATA_TOO_LONG, 1);
 | |
|     else
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     return 1;
 | |
|   }
 | |
|   /* delta is >= -1 here */
 | |
|   if (delta > 0)
 | |
|   {
 | |
|     if (bit_len)
 | |
|       clr_rec_bits(bit_ptr, bit_ofs, bit_len);
 | |
|     bzero(ptr, delta);
 | |
|     memcpy(ptr + delta, from, length);
 | |
|   }
 | |
|   else if (delta == 0)
 | |
|   {
 | |
|     if (bit_len)
 | |
|       clr_rec_bits(bit_ptr, bit_ofs, bit_len);
 | |
|     memcpy(ptr, from, length);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (bit_len)
 | |
|     {
 | |
|       set_rec_bits((uchar) *from, bit_ptr, bit_ofs, bit_len);
 | |
|       from++;
 | |
|     }
 | |
|     memcpy(ptr, from, bytes_in_rec);
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_bit::store(double nr)
 | |
| {
 | |
|   return Field_bit::store((longlong) nr, FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_bit::store(longlong nr, bool unsigned_val)
 | |
| {
 | |
|   char buf[8];
 | |
| 
 | |
|   mi_int8store(buf, nr);
 | |
|   return store(buf, 8, NULL);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_bit::store_decimal(const my_decimal *val)
 | |
| {
 | |
|   int err= 0;
 | |
|   longlong i= convert_decimal2longlong(val, 1, &err);
 | |
|   return MY_TEST(err | store(i, TRUE));
 | |
| }
 | |
| 
 | |
| 
 | |
| double Field_bit::val_real(void)
 | |
| {
 | |
|   return (double) Field_bit::val_int();
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong Field_bit::val_int(void)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   ulonglong bits= 0;
 | |
|   if (bit_len)
 | |
|   {
 | |
|     bits= get_rec_bits(bit_ptr, bit_ofs, bit_len);
 | |
|     bits<<= (bytes_in_rec * 8);
 | |
|   }
 | |
| 
 | |
|   switch (bytes_in_rec) {
 | |
|   case 0: return bits;
 | |
|   case 1: return bits | (ulonglong) ptr[0];
 | |
|   case 2: return bits | mi_uint2korr(ptr);
 | |
|   case 3: return bits | mi_uint3korr(ptr);
 | |
|   case 4: return bits | mi_uint4korr(ptr);
 | |
|   case 5: return bits | mi_uint5korr(ptr);
 | |
|   case 6: return bits | mi_uint6korr(ptr);
 | |
|   case 7: return bits | mi_uint7korr(ptr);
 | |
|   default: return mi_uint8korr(ptr + bytes_in_rec - sizeof(longlong));
 | |
|   }
 | |
| }  
 | |
| 
 | |
| 
 | |
| String *Field_bit::val_str(String *val_buffer,
 | |
|                            String *val_ptr __attribute__((unused)))
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   char buff[sizeof(longlong)];
 | |
|   uint length= MY_MIN(pack_length(), sizeof(longlong));
 | |
|   ulonglong bits= val_int();
 | |
|   mi_int8store(buff,bits);
 | |
| 
 | |
|   val_buffer->alloc(length);
 | |
|   memcpy((char *) val_buffer->ptr(), buff+8-length, length);
 | |
|   val_buffer->length(length);
 | |
|   val_buffer->set_charset(&my_charset_bin);
 | |
|   return val_buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_read());
 | |
|   int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 1, deciaml_value);
 | |
|   return deciaml_value;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Compare two bit fields using pointers within the record.
 | |
|   SYNOPSIS
 | |
|     cmp_max()
 | |
|     a                 Pointer to field->ptr in first record
 | |
|     b                 Pointer to field->ptr in second record
 | |
|     max_len           Maximum length used in index
 | |
|   DESCRIPTION
 | |
|     This method is used from key_rec_cmp used by merge sorts used
 | |
|     by partitioned index read and later other similar places.
 | |
|     The a and b pointer must be pointers to the field in a record
 | |
|     (not the table->record[0] necessarily)
 | |
| */
 | |
| int Field_bit::cmp_prefix(const uchar *a, const uchar *b,
 | |
|                           size_t prefix_char_len) const
 | |
| {
 | |
|   my_ptrdiff_t a_diff= a - ptr;
 | |
|   my_ptrdiff_t b_diff= b - ptr;
 | |
|   if (bit_len)
 | |
|   {
 | |
|     int flag;
 | |
|     uchar bits_a= get_rec_bits(bit_ptr+a_diff, bit_ofs, bit_len);
 | |
|     uchar bits_b= get_rec_bits(bit_ptr+b_diff, bit_ofs, bit_len);
 | |
|     if ((flag= (int) (bits_a - bits_b)))
 | |
|       return flag;
 | |
|   }
 | |
|   if (!bytes_in_rec)
 | |
|     return 0;
 | |
|   return memcmp(a, b, bytes_in_rec);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_bit::key_cmp(const uchar *str, uint) const
 | |
| {
 | |
|   if (bit_len)
 | |
|   {
 | |
|     int flag;
 | |
|     uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len);
 | |
|     if ((flag= (int) (bits - *str)))
 | |
|       return flag;
 | |
|     str++;
 | |
|   }
 | |
|   return memcmp(ptr, str, bytes_in_rec);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_bit::cmp_offset(my_ptrdiff_t row_offset)
 | |
| {
 | |
|   if (bit_len)
 | |
|   {
 | |
|     int flag;
 | |
|     uchar bits_a= get_rec_bits(bit_ptr, bit_ofs, bit_len);
 | |
|     uchar bits_b= get_rec_bits(bit_ptr + row_offset, bit_ofs, bit_len);
 | |
|     if ((flag= (int) (bits_a - bits_b)))
 | |
|       return flag;
 | |
|   }
 | |
|   return memcmp(ptr, ptr + row_offset, bytes_in_rec);
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Field_bit::get_key_image(uchar *buff, uint length, const uchar *ptr_arg, imagetype type_arg) const
 | |
| {
 | |
|   if (bit_len)
 | |
|   {
 | |
|     const uchar *bit_ptr_for_arg= ptr_arg + (bit_ptr - ptr);
 | |
|     uchar bits= get_rec_bits(bit_ptr_for_arg, bit_ofs, bit_len);
 | |
|     *buff++= bits;
 | |
|     length--;
 | |
|   }
 | |
|   uint tmp_data_length = MY_MIN(length, bytes_in_rec);
 | |
|   memcpy(buff, ptr, tmp_data_length);
 | |
|   return tmp_data_length + 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Returns the number of bytes field uses in row-based replication 
 | |
|    row packed size.
 | |
| 
 | |
|    This method is used in row-based replication to determine the number
 | |
|    of bytes that the field consumes in the row record format. This is
 | |
|    used to skip fields in the master that do not exist on the slave.
 | |
| 
 | |
|    @param   field_metadata   Encoded size in field metadata
 | |
| 
 | |
|    @returns The size of the field based on the field metadata.
 | |
| */
 | |
| uint Field_bit::pack_length_from_metadata(uint field_metadata) const
 | |
| {
 | |
|   uint const from_len= (field_metadata >> 8U) & 0x00ff;
 | |
|   uint const from_bit_len= field_metadata & 0x00ff;
 | |
|   uint const source_size= from_len + ((from_bit_len > 0) ? 1 : 0);
 | |
|   return (source_size);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| Field_bit::compatible_field_size(uint field_metadata,
 | |
|                                  const Relay_log_info * __attribute__((unused)),
 | |
|                                  uint16 mflags,
 | |
|                                  int *order_var) const
 | |
| {
 | |
|   DBUG_ENTER("Field_bit::compatible_field_size");
 | |
|   DBUG_ASSERT((field_metadata >> 16) == 0);
 | |
|   uint from_bit_len=
 | |
|     8 * (field_metadata >> 8) + (field_metadata & 0xff);
 | |
|   uint to_bit_len= max_display_length();
 | |
|   DBUG_PRINT("debug", ("from_bit_len: %u, to_bit_len: %u",
 | |
|                        from_bit_len, to_bit_len));
 | |
|   /*
 | |
|     If the bit length exact flag is clear, we are dealing with an old
 | |
|     master, so we allow some less strict behaviour if replicating by
 | |
|     moving both bit lengths to an even multiple of 8.
 | |
| 
 | |
|     We do this by computing the number of bytes to store the field
 | |
|     instead, and then compare the result.
 | |
|    */
 | |
|   if (!(mflags & Table_map_log_event::TM_BIT_LEN_EXACT_F)) {
 | |
|     from_bit_len= (from_bit_len + 7) / 8;
 | |
|     to_bit_len= (to_bit_len + 7) / 8;
 | |
|   }
 | |
| 
 | |
|   *order_var= compare(from_bit_len, to_bit_len);
 | |
|   DBUG_RETURN(TRUE);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void Field_bit::sql_type(String &res) const
 | |
| {
 | |
|   CHARSET_INFO *cs= res.charset();
 | |
|   size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
 | |
|                                    "bit(%d)", (int) field_length);
 | |
|   res.length(length);
 | |
| }
 | |
| 
 | |
| 
 | |
| uchar *
 | |
| Field_bit::pack(uchar *to, const uchar *from, uint max_length)
 | |
| {
 | |
|   DBUG_ASSERT(max_length > 0);
 | |
|   uint length;
 | |
|   if (bit_len > 0)
 | |
|   {
 | |
|     /*
 | |
|       We have the following:
 | |
| 
 | |
|       ptr        Points into a field in record R1
 | |
|       from       Points to a field in a record R2
 | |
|       bit_ptr    Points to the byte (in the null bytes) that holds the
 | |
|                  odd bits of R1
 | |
|       from_bitp  Points to the byte that holds the odd bits of R2
 | |
| 
 | |
|       We have the following:
 | |
| 
 | |
|           ptr - bit_ptr = from - from_bitp
 | |
| 
 | |
|       We want to isolate 'from_bitp', so this gives:
 | |
| 
 | |
|           ptr - bit_ptr - from = - from_bitp
 | |
|           - ptr + bit_ptr + from = from_bitp
 | |
|           bit_ptr + from - ptr = from_bitp
 | |
|      */
 | |
|     uchar bits= get_rec_bits(bit_ptr + (from - ptr), bit_ofs, bit_len);
 | |
|     *to++= bits;
 | |
|   }
 | |
|   length= MY_MIN(bytes_in_rec, max_length - (bit_len > 0));
 | |
|   memcpy(to, from, length);
 | |
|   return to + length;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Unpack a bit field from row data.
 | |
| 
 | |
|    This method is used to unpack a bit field from a master whose size
 | |
|    of the field is less than that of the slave.
 | |
| 
 | |
|    @param   to         Destination of the data
 | |
|    @param   from       Source of the data
 | |
|    @param   param_data Bit length (upper) and length (lower) values
 | |
| 
 | |
|    @return  New pointer into memory based on from + length of the data
 | |
| */
 | |
| const uchar *
 | |
| Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end,
 | |
|                   uint param_data)
 | |
| {
 | |
|   DBUG_ENTER("Field_bit::unpack");
 | |
|   DBUG_PRINT("enter", ("to: %p, from: %p, param_data: 0x%x",
 | |
|                        to, from, param_data));
 | |
|   DBUG_PRINT("debug", ("bit_ptr: %p, bit_len: %u, bit_ofs: %u",
 | |
|                        bit_ptr, bit_len, bit_ofs));
 | |
|   uint const from_len= (param_data >> 8U) & 0x00ff;
 | |
|   uint const from_bit_len= param_data & 0x00ff;
 | |
|   DBUG_PRINT("debug", ("from_len: %u, from_bit_len: %u",
 | |
|                        from_len, from_bit_len));
 | |
|   /*
 | |
|     If the parameter data is zero (i.e., undefined), or if the master
 | |
|     and slave have the same sizes, then use the old unpack() method.
 | |
|   */
 | |
|   if (param_data == 0 ||
 | |
|       ((from_bit_len == bit_len) && (from_len == bytes_in_rec)))
 | |
|   {
 | |
|     if (from + bytes_in_rec + MY_TEST(bit_len) > from_end)
 | |
|       return 0;                                 // Error in data
 | |
| 
 | |
|     if (bit_len > 0)
 | |
|     {
 | |
|       /*
 | |
|         set_rec_bits is a macro, don't put the post-increment in the
 | |
|         argument since that might cause strange side-effects.
 | |
| 
 | |
|         For the choice of the second argument, see the explanation for
 | |
|         Field_bit::pack().
 | |
|       */
 | |
|       set_rec_bits(*from, bit_ptr + (to - ptr), bit_ofs, bit_len);
 | |
|       from++;
 | |
|     }
 | |
|     memcpy(to, from, bytes_in_rec);
 | |
|     DBUG_RETURN(from + bytes_in_rec);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     We are converting a smaller bit field to a larger one here.
 | |
|     To do that, we first need to construct a raw value for the original
 | |
|     bit value stored in the from buffer. Then that needs to be converted
 | |
|     to the larger field then sent to store() for writing to the field.
 | |
|     Lastly the odd bits need to be masked out if the bytes_in_rec > 0.
 | |
|     Otherwise stray bits can cause spurious values.
 | |
|   */
 | |
| 
 | |
|   uint len= from_len + ((from_bit_len > 0) ? 1 : 0);
 | |
|   uint new_len= (field_length + 7) / 8;
 | |
| 
 | |
|   if (from + len > from_end || new_len < len)
 | |
|     return 0;                                 // Error in data
 | |
| 
 | |
|   char *value= (char *)my_alloca(new_len);
 | |
|   bzero(value, new_len);
 | |
| 
 | |
|   memcpy(value + (new_len - len), from, len);
 | |
|   /*
 | |
|     Mask out the unused bits in the partial byte. 
 | |
|     TODO: Add code to the master to always mask these bits and remove
 | |
|           the following.
 | |
|   */
 | |
|   if ((from_bit_len > 0) && (from_len > 0))
 | |
|     value[new_len - len]= value[new_len - len] & ((1U << from_bit_len) - 1);
 | |
|   bitmap_set_bit(table->write_set,field_index);
 | |
|   store(value, new_len, system_charset_info);
 | |
|   my_afree(value);
 | |
|   DBUG_RETURN(from + len);
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_bit::set_default()
 | |
| {
 | |
|   if (bit_len > 0)
 | |
|   {
 | |
|     my_ptrdiff_t const col_offset= table->s->default_values - table->record[0];
 | |
|     uchar bits= get_rec_bits(bit_ptr + col_offset, bit_ofs, bit_len);
 | |
|     set_rec_bits(bits, bit_ptr, bit_ofs, bit_len);
 | |
|   }
 | |
|   return Field::set_default();
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Bit field support for non-MyISAM tables.
 | |
| */
 | |
| 
 | |
| Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg,
 | |
|                                      uchar *null_ptr_arg, uchar null_bit_arg,
 | |
|                                      enum utype unireg_check_arg,
 | |
|                                      const LEX_CSTRING *field_name_arg)
 | |
|   :Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0,
 | |
|              unireg_check_arg, field_name_arg)
 | |
| {
 | |
|   flags|= UNSIGNED_FLAG;
 | |
|   bit_len= 0;
 | |
|   bytes_in_rec= (len_arg + 7) / 8;
 | |
| }
 | |
| 
 | |
| 
 | |
| int Field_bit_as_char::store(const char *from, size_t length, CHARSET_INFO *cs)
 | |
| {
 | |
|   DBUG_ASSERT(marked_for_write_or_computed());
 | |
|   int delta;
 | |
|   uchar bits= (uchar) (field_length & 7);
 | |
| 
 | |
|   for (; length && !*from; from++, length--)          // skip left 0's
 | |
|     ;
 | |
|   delta= (int)(bytes_in_rec - length);
 | |
| 
 | |
|   if (delta < 0 ||
 | |
|       (delta == 0 && bits && (uint) (uchar) *from >= (uint) (1 << bits)))
 | |
|   {
 | |
|     memset(ptr, 0xff, bytes_in_rec);
 | |
|     if (bits)
 | |
|       *ptr&= ((1 << bits) - 1); /* set first uchar */
 | |
|     if (get_thd()->really_abort_on_warning())
 | |
|       set_warning(ER_DATA_TOO_LONG, 1);
 | |
|     else
 | |
|       set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
 | |
|     return 1;
 | |
|   }
 | |
|   bzero(ptr, delta);
 | |
|   memcpy(ptr + delta, from, length);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_bit_as_char::sql_type(String &res) const
 | |
| {
 | |
|   CHARSET_INFO *cs= res.charset();
 | |
|   size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
 | |
|                                    "bit(%d)", (int) field_length);
 | |
|   res.length(length);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|   Handling of field and Create_field
 | |
| *****************************************************************************/
 | |
| 
 | |
| bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
 | |
|                                                bool reuse_interval_list_values)
 | |
| {
 | |
|   DBUG_ENTER("Column_definition::create_interval_from_interval_list");
 | |
|   DBUG_ASSERT(!interval);
 | |
|   TYPELIB *tmpint;
 | |
|   if (!(interval= tmpint= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB))))
 | |
|     DBUG_RETURN(true); // EOM
 | |
| 
 | |
|   List_iterator<String> it(interval_list);
 | |
|   StringBuffer<64> conv;
 | |
|   char comma_buf[5]; /* 5 bytes for 'filename' charset */
 | |
|   DBUG_ASSERT(sizeof(comma_buf) >= charset->mbmaxlen);
 | |
|   int comma_length= charset->wc_mb(',',
 | |
|                                    (uchar*) comma_buf,
 | |
|                                    (uchar*) comma_buf + sizeof(comma_buf));
 | |
|   DBUG_ASSERT(comma_length >= 0 && comma_length <= (int) sizeof(comma_buf));
 | |
| 
 | |
|   if (!multi_alloc_root(mem_root,
 | |
|                         &tmpint->type_names,
 | |
|                         sizeof(char*) * (interval_list.elements + 1),
 | |
|                         &tmpint->type_lengths,
 | |
|                         sizeof(uint) * (interval_list.elements + 1),
 | |
|                         NullS))
 | |
|     goto err; // EOM
 | |
| 
 | |
|   tmpint->name= "";
 | |
|   tmpint->count= interval_list.elements;
 | |
| 
 | |
|   for (uint i= 0; i < interval_list.elements; i++)
 | |
|   {
 | |
|     uint32 dummy;
 | |
|     String *tmp= it++;
 | |
|     LEX_CSTRING value;
 | |
|     if (String::needs_conversion(tmp->length(), tmp->charset(),
 | |
|                                  charset, &dummy))
 | |
|     {
 | |
|       uint cnv_errs;
 | |
|       conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), charset, &cnv_errs);
 | |
|       value.str= strmake_root(mem_root, conv.ptr(), conv.length());
 | |
|       value.length= conv.length();
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       value.str= reuse_interval_list_values ? tmp->ptr() :
 | |
|                                               strmake_root(mem_root,
 | |
|                                                            tmp->ptr(),
 | |
|                                                            tmp->length());
 | |
|       value.length= tmp->length();
 | |
|     }
 | |
|     if (!value.str)
 | |
|       goto err; // EOM
 | |
| 
 | |
|     // Strip trailing spaces.
 | |
|     value.length= charset->lengthsp(value.str, value.length);
 | |
|     ((char*) value.str)[value.length]= '\0';
 | |
| 
 | |
|     if (real_field_type() == MYSQL_TYPE_SET)
 | |
|     {
 | |
|       if (charset->instr(value.str, value.length,
 | |
|                          comma_buf, comma_length, NULL, 0))
 | |
|       {
 | |
|         ErrConvString err(tmp);
 | |
|         my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr());
 | |
|         goto err;
 | |
|       }
 | |
|     }
 | |
|     tmpint->type_names[i]= value.str;
 | |
|     tmpint->type_lengths[i]= (uint)value.length;
 | |
|   }
 | |
|   tmpint->type_names[interval_list.elements]= 0;    // End marker
 | |
|   tmpint->type_lengths[interval_list.elements]= 0;
 | |
|   interval_list.empty();  // Don't need interval_list anymore
 | |
|   DBUG_RETURN(false);
 | |
| err:
 | |
|   interval= NULL;  // Avoid having both non-empty interval_list and interval
 | |
|   DBUG_RETURN(true);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root,
 | |
|                                                bool reuse_interval_list_values)
 | |
| {
 | |
|   DBUG_ENTER("Column_definition::prepare_interval_field");
 | |
|   DBUG_ASSERT(real_field_type() == MYSQL_TYPE_ENUM ||
 | |
|               real_field_type() == MYSQL_TYPE_SET);
 | |
|   /*
 | |
|     Interval values are either in "interval" or in "interval_list",
 | |
|     but not in both at the same time, and are not empty at the same time.
 | |
|     - Values are in "interval_list" when we're coming from the parser
 | |
|       in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}.
 | |
|     - Values are in "interval" when we're in ALTER TABLE.
 | |
| 
 | |
|     In a corner case with an empty set like SET(''):
 | |
|     - after the parser we have interval_list.elements==1
 | |
|     - in ALTER TABLE we have a non-NULL interval with interval->count==1,
 | |
|       with interval->type_names[0]=="" and interval->type_lengths[0]==0.
 | |
|     So the assert is still valid for this corner case.
 | |
| 
 | |
|     ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible,
 | |
|     as the parser requires at least one element, so for a ENUM or SET field it
 | |
|     should never happen that both internal_list.elements and interval are 0.
 | |
|   */
 | |
|   DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0));
 | |
| 
 | |
|   /*
 | |
|     Create typelib from interval_list, and if necessary
 | |
|     convert strings from client character set to the
 | |
|     column character set.
 | |
|   */
 | |
|   if (interval_list.elements &&
 | |
|       create_interval_from_interval_list(mem_root,
 | |
|                                          reuse_interval_list_values))
 | |
|     DBUG_RETURN(true);
 | |
| 
 | |
|   if (!reuse_interval_list_values)
 | |
|   {
 | |
|     /*
 | |
|       We're initializing from an existing table or view Field_enum
 | |
|       (e.g. for a %TYPE variable) rather than from the parser.
 | |
|       The constructor Column_definition(THD*,Field*,Field*) has already
 | |
|       copied the TYPELIB pointer from the original Field_enum.
 | |
|       Now we need to make a permanent copy of that TYPELIB,
 | |
|       as the original field can be freed before the end of the life
 | |
|       cycle of "this".
 | |
|     */
 | |
|     DBUG_ASSERT(interval);
 | |
|     if (!(interval= copy_typelib(mem_root, interval)))
 | |
|       DBUG_RETURN(true);
 | |
|   }
 | |
|   prepare_interval_field_calc_length();
 | |
|   DBUG_RETURN(false);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::set_attributes(THD *thd,
 | |
|                                        const Lex_field_type_st &def,
 | |
|                                        column_definition_type_t type)
 | |
| {
 | |
|   DBUG_ASSERT(type_handler() == &type_handler_null);
 | |
|   DBUG_ASSERT(length == 0);
 | |
|   DBUG_ASSERT(decimals == 0);
 | |
| 
 | |
|   set_handler(def.type_handler());
 | |
|   return type_handler()->Column_definition_set_attributes(thd, this,
 | |
|                                                           def, type);
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| Column_definition_attributes::set_length_and_dec(const Lex_length_and_dec_st
 | |
|                                                  &type)
 | |
| {
 | |
|   if (type.has_explicit_length())
 | |
|     length= type.length_overflowed() ? (ulonglong) UINT_MAX32 + 1 :
 | |
|                                        (ulonglong) type.length();
 | |
| 
 | |
|   if (type.has_explicit_dec())
 | |
|     decimals= type.dec();
 | |
| }
 | |
| 
 | |
| 
 | |
| void Column_definition::create_length_to_internal_length_bit()
 | |
| {
 | |
|   if (f_bit_as_char(pack_flag))
 | |
|   {
 | |
|     pack_length= ((length + 7) & ~7) / 8;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     pack_length= (uint) length / 8;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void Column_definition::create_length_to_internal_length_newdecimal()
 | |
| {
 | |
|   DBUG_ASSERT(length < UINT_MAX32);
 | |
|   decimal_digit_t prec= get_decimal_precision((uint)length, decimals,
 | |
|                                               flags & UNSIGNED_FLAG);
 | |
|   pack_length= my_decimal_get_binary_size(prec, decimals);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name,
 | |
|                       enum_vcol_info_type type, Alter_info *alter_info)
 | |
| {
 | |
|   bool ret;
 | |
|   Item::vcol_func_processor_result res;
 | |
|   res.alter_info= alter_info;
 | |
| 
 | |
|   if (!vcol->name.length)
 | |
|     vcol->name= *name;
 | |
| 
 | |
|   /*
 | |
|     Walk through the Item tree checking if all items are valid
 | |
|     to be part of the virtual column
 | |
|   */
 | |
|   ret= vcol->expr->walk(&Item::check_vcol_func_processor, 0, &res);
 | |
|   vcol->flags= res.errors;
 | |
| 
 | |
|   uint filter= VCOL_IMPOSSIBLE;
 | |
|   if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT)
 | |
|     filter|= VCOL_NOT_STRICTLY_DETERMINISTIC;
 | |
|   if (type != VCOL_DEFAULT)
 | |
|     filter|= VCOL_NEXTVAL;
 | |
| 
 | |
|   if (unlikely(ret || (res.errors & filter)))
 | |
|   {
 | |
|     my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
 | |
|              vcol_type_name(type), name->str);
 | |
|     return TRUE;
 | |
|   }
 | |
|   /*
 | |
|     Safe to call before fix_fields as long as vcol's don't include sub
 | |
|     queries (which is now checked in check_vcol_func_processor)
 | |
|   */
 | |
|   if (vcol->expr->check_cols(1))
 | |
|     return TRUE;
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::check_length(uint mysql_errno, uint limit) const
 | |
| {
 | |
|   if (length <= limit)
 | |
|     return false;
 | |
|   my_error(mysql_errno, MYF(0), field_name.str, static_cast<ulong>(limit));
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::fix_attributes_int(uint default_length)
 | |
| {
 | |
|   if (length)
 | |
|     return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
 | |
|   length= default_length;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::fix_attributes_real(uint default_length)
 | |
| {
 | |
|   /* change FLOAT(precision) to FLOAT or DOUBLE */
 | |
|   if (!length && !decimals)
 | |
|   {
 | |
|     length= default_length;
 | |
|     decimals= NOT_FIXED_DEC;
 | |
|   }
 | |
|   if (length < decimals && decimals != NOT_FIXED_DEC)
 | |
|   {
 | |
|     my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str);
 | |
|     return true;
 | |
|   }
 | |
|   if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS)
 | |
|   {
 | |
|     my_error(ER_TOO_BIG_SCALE, MYF(0),
 | |
|              field_name.str, static_cast<uint>(FLOATING_POINT_DECIMALS-1));
 | |
|     return true;
 | |
|   }
 | |
|   return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::fix_attributes_decimal()
 | |
| {
 | |
|   if (decimals >= NOT_FIXED_DEC)
 | |
|   {
 | |
|     my_error(ER_TOO_BIG_SCALE, MYF(0),
 | |
|              field_name.str, static_cast<uint>(NOT_FIXED_DEC - 1));
 | |
|     return true;
 | |
|   }
 | |
|   my_decimal_trim(&length, &decimals);
 | |
|   if (length > DECIMAL_MAX_PRECISION)
 | |
|   {
 | |
|     my_error(ER_TOO_BIG_PRECISION, MYF(0), field_name.str,
 | |
|              DECIMAL_MAX_PRECISION);
 | |
|     return true;
 | |
|   }
 | |
|   if (length < decimals)
 | |
|   {
 | |
|     my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str);
 | |
|     return true;
 | |
|   }
 | |
|   length= my_decimal_precision_to_length((uint) length, decimals,
 | |
|                                          flags & UNSIGNED_FLAG);
 | |
|   pack_length= my_decimal_get_binary_size((uint) length, decimals);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::fix_attributes_bit()
 | |
| {
 | |
|   if (!length)
 | |
|     length= 1;
 | |
|   pack_length= ((uint) length + 7) / 8;
 | |
|   return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_BIT_FIELD_LENGTH);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length)
 | |
| {
 | |
|   if (length > MAX_DATETIME_PRECISION)
 | |
|   {
 | |
|     my_error(ER_TOO_BIG_PRECISION, MYF(0), field_name.str,
 | |
|              MAX_DATETIME_PRECISION);
 | |
|     return true;
 | |
|   }
 | |
|   decimals= (uint) length;
 | |
|   length+= int_part_length + (length ? 1 : 0);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::validate_check_constraint(THD *thd)
 | |
| {
 | |
|   return check_constraint &&
 | |
|          check_expression(check_constraint, &field_name, VCOL_CHECK_FIELD);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::check(THD *thd)
 | |
| {
 | |
|   DBUG_ENTER("Column_definition::check");
 | |
| 
 | |
|   /* Initialize data for a computed field */
 | |
|   if (vcol_info)
 | |
|   {
 | |
|     DBUG_ASSERT(vcol_info->expr);
 | |
|     vcol_info->set_handler(type_handler());
 | |
|     if (check_expression(vcol_info, &field_name, vcol_info->is_stored()
 | |
|                          ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL))
 | |
|       DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (type_handler()->Column_definition_validate_check_constraint(thd, this))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   if (default_value)
 | |
|   {
 | |
|     Item *def_expr= default_value->expr;
 | |
|     if (check_expression(default_value, &field_name, VCOL_DEFAULT))
 | |
|       DBUG_RETURN(TRUE);
 | |
| 
 | |
|     /* Constant's are stored in the 'empty_record', except for blobs */
 | |
|     if (def_expr->basic_const_item())
 | |
|     {
 | |
|       if (def_expr->type() == Item::NULL_ITEM)
 | |
|       {
 | |
|         default_value= 0;
 | |
|         if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG)
 | |
|         {
 | |
|           my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
 | |
|           DBUG_RETURN(1);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (default_value && (flags & AUTO_INCREMENT_FLAG))
 | |
|   {
 | |
|     my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
 | |
|     DBUG_RETURN(1);
 | |
|   }
 | |
| 
 | |
|   if (default_value && !default_value->expr->basic_const_item() &&
 | |
|       mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME &&
 | |
|       default_value->expr->type() == Item::FUNC_ITEM)
 | |
|   {
 | |
|     /*
 | |
|       Special case: NOW() for TIMESTAMP and DATETIME fields are handled
 | |
|       as in MariaDB 10.1 by marking them in unireg_check.
 | |
|     */
 | |
|     Item_func *fn= static_cast<Item_func*>(default_value->expr);
 | |
|     if (fn->functype() == Item_func::NOW_FUNC &&
 | |
|         (fn->decimals == 0 || fn->decimals >= length))
 | |
|     {
 | |
|       default_value= 0;
 | |
|       unireg_check= Field::TIMESTAMP_DN_FIELD;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (on_update)
 | |
|   {
 | |
|     if (mysql_timestamp_type() != MYSQL_TIMESTAMP_DATETIME ||
 | |
|         on_update->decimals < length)
 | |
|     {
 | |
|       my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name.str);
 | |
|       DBUG_RETURN(TRUE);
 | |
|     }
 | |
|     unireg_check= unireg_check == Field::NONE ? Field::TIMESTAMP_UN_FIELD
 | |
|                                               : Field::TIMESTAMP_DNUN_FIELD;
 | |
|   }
 | |
|   else if (flags & AUTO_INCREMENT_FLAG)
 | |
|     unireg_check= Field::NEXT_NUMBER;
 | |
| 
 | |
|   if (type_handler()->Column_definition_fix_attributes(this))
 | |
|     DBUG_RETURN(true);
 | |
| 
 | |
|   /* Remember the value of length */
 | |
|   char_length= (uint)length;
 | |
| 
 | |
|   /*
 | |
|     Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and
 | |
|     it is NOT NULL, not an AUTO_INCREMENT field.
 | |
|     We need to do this check here and in mysql_create_prepare_table() as
 | |
|     sp_head::fill_field_definition() calls this function.
 | |
|   */
 | |
|   if (!default_value && unireg_check == Field::NONE && (flags & NOT_NULL_FLAG))
 | |
|   {
 | |
|     /*
 | |
|       TIMESTAMP columns get implicit DEFAULT value when
 | |
|       explicit_defaults_for_timestamp is not set.
 | |
|     */
 | |
|     if (((thd->variables.option_bits & OPTION_EXPLICIT_DEF_TIMESTAMP) ||
 | |
|         !is_timestamp_type()) && !vers_sys_field())
 | |
|     {
 | |
|       flags|= NO_DEFAULT_VALUE_FLAG;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   if ((flags & AUTO_INCREMENT_FLAG) &&
 | |
|       !type_handler()->type_can_have_auto_increment_attribute())
 | |
|   {
 | |
|     my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str);
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   DBUG_RETURN(FALSE); /* success */
 | |
| }
 | |
| 
 | |
| enum_field_types get_blob_type_from_length(ulong length)
 | |
| {
 | |
|   enum_field_types type;
 | |
|   if (length < 256)
 | |
|     type= MYSQL_TYPE_TINY_BLOB;
 | |
|   else if (length < 65536)
 | |
|     type= MYSQL_TYPE_BLOB;
 | |
|   else if (length < 256L*256L*256L)
 | |
|     type= MYSQL_TYPE_MEDIUM_BLOB;
 | |
|   else
 | |
|     type= MYSQL_TYPE_LONG_BLOB;
 | |
|   return type;
 | |
| }
 | |
| 
 | |
| 
 | |
| uint pack_length_to_packflag(uint type)
 | |
| {
 | |
|   switch (type) {
 | |
|     case 1: return f_settype((uint) MYSQL_TYPE_TINY);
 | |
|     case 2: return f_settype((uint) MYSQL_TYPE_SHORT);
 | |
|     case 3: return f_settype((uint) MYSQL_TYPE_INT24);
 | |
|     case 4: return f_settype((uint) MYSQL_TYPE_LONG);
 | |
|     case 8: return f_settype((uint) MYSQL_TYPE_LONGLONG);
 | |
|   }
 | |
|   return 0;					// This shouldn't happen
 | |
| }
 | |
| 
 | |
| 
 | |
| uint Column_definition_attributes::pack_flag_to_pack_length() const
 | |
| {
 | |
|   uint type= f_packtype(pack_flag); // 0..15
 | |
|   DBUG_ASSERT(type < 16);
 | |
|   switch (type) {
 | |
|   case MYSQL_TYPE_TINY:     return 1;
 | |
|   case MYSQL_TYPE_SHORT:    return 2;
 | |
|   case MYSQL_TYPE_LONG:     return 4;
 | |
|   case MYSQL_TYPE_LONGLONG: return 8;
 | |
|   case MYSQL_TYPE_INT24:    return 3;
 | |
|   }
 | |
|   return 0; // This should not happen
 | |
| }
 | |
| 
 | |
| 
 | |
| Field *Column_definition_attributes::make_field(TABLE_SHARE *share,
 | |
|                                                 MEM_ROOT *mem_root,
 | |
|                                                 const Record_addr *rec,
 | |
|                                                 const Type_handler *handler,
 | |
|                                                 const LEX_CSTRING *field_name,
 | |
|                                                 uint32 flags)
 | |
|                                                 const
 | |
| {
 | |
|   DBUG_ASSERT(length <= UINT_MAX32);
 | |
|   DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
 | |
|                        handler->name().ptr(), (uint) length, interval,
 | |
|                        FLAGSTR(pack_flag, FIELDFLAG_BINARY),
 | |
|                        FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
 | |
|                        FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
 | |
|                        FLAGSTR(pack_flag, FIELDFLAG_PACK),
 | |
|                        FLAGSTR(pack_flag, FIELDFLAG_BLOB)));
 | |
| 
 | |
|   Record_addr addr(rec->ptr(), f_maybe_null(pack_flag) ? rec->null() :
 | |
|                                                          Bit_addr());
 | |
|   /*
 | |
|     Special code for the BIT-alike data types
 | |
|     who store data bits together with NULL-bits.
 | |
|   */
 | |
|   Bit_addr bit(rec->null());
 | |
|   if (f_maybe_null(pack_flag))
 | |
|     bit.inc();
 | |
|   return handler->make_table_field_from_def(share, mem_root, field_name,
 | |
|                                             addr, bit, this, flags);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const
 | |
| {
 | |
|   return item->is_of_type(Item::CONST_ITEM, TIME_RESULT);
 | |
| }
 | |
| 
 | |
| 
 | |
| Column_definition_attributes::Column_definition_attributes(const Field *field)
 | |
|  :length(field->character_octet_length() / field->charset()->mbmaxlen),
 | |
|   interval(NULL),
 | |
|   charset(field->charset()), // May be NULL ptr
 | |
|   srid(0),
 | |
|   pack_flag(0),
 | |
|   decimals(field->decimals()),
 | |
|   unireg_check(field->unireg_check)
 | |
| {}
 | |
| 
 | |
| 
 | |
| Column_definition_attributes::
 | |
|   Column_definition_attributes(const Type_all_attributes &attr)
 | |
|  :length(attr.max_length),
 | |
|   interval(attr.get_typelib()),
 | |
|   charset(attr.collation.collation),
 | |
|   srid(0),
 | |
|   pack_flag(attr.unsigned_flag ? 0 : FIELDFLAG_DECIMAL),
 | |
|   decimals(attr.decimals),
 | |
|   unireg_check(Field::NONE)
 | |
| {}
 | |
| 
 | |
| 
 | |
| /** Create a field suitable for create of table. */
 | |
| 
 | |
| Column_definition::Column_definition(THD *thd, Field *old_field,
 | |
|                                                Field *orig_field)
 | |
|  :Column_definition_attributes(old_field)
 | |
| {
 | |
|   srid= 0;
 | |
|   on_update=  NULL;
 | |
|   field_name= old_field->field_name;
 | |
|   flags=      old_field->flags;
 | |
|   pack_length=old_field->pack_length();
 | |
|   set_handler(old_field->type_handler());
 | |
|   comment=    old_field->comment;
 | |
|   vcol_info=  old_field->vcol_info;
 | |
|   option_list= old_field->option_list;
 | |
|   explicitly_nullable= !(old_field->flags & NOT_NULL_FLAG);
 | |
|   compression_method_ptr= 0;
 | |
|   versioning= VERSIONING_NOT_SET;
 | |
|   invisible= old_field->invisible;
 | |
|   interval_list.empty(); // prepare_interval_field() needs this
 | |
|   char_length= (uint) length;
 | |
| 
 | |
|   if (orig_field)
 | |
|   {
 | |
|     default_value= orig_field->default_value;
 | |
|     check_constraint= orig_field->check_constraint;
 | |
|     if (orig_field->unireg_check == Field::TMYSQL_COMPRESSED)
 | |
|     {
 | |
|       unireg_check= Field::TMYSQL_COMPRESSED;
 | |
|       compression_method_ptr= zlib_compression_method;
 | |
|     }
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     default_value= 0;
 | |
|     check_constraint= 0;
 | |
|   }
 | |
| 
 | |
|   type_handler()->Column_definition_reuse_fix_attributes(thd, this, old_field);
 | |
| 
 | |
|   /*
 | |
|     Copy the default (constant/function) from the column object orig_field, if
 | |
|     supplied. We do this if all these conditions are met:
 | |
| 
 | |
|     - The column allows a default.
 | |
| 
 | |
|     - The column type is not a BLOB type (as BLOB's doesn't have constant
 | |
|       defaults)
 | |
| 
 | |
|     - The original column (old_field) was properly initialized with a record
 | |
|       buffer pointer.
 | |
| 
 | |
|     - The column didn't have a default expression
 | |
|   */
 | |
|   if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
 | |
|       old_field->ptr != NULL && orig_field != NULL)
 | |
|   {
 | |
|     if (orig_field->unireg_check != Field::NEXT_NUMBER)
 | |
|       unireg_check= orig_field->unireg_check;
 | |
| 
 | |
|     /* Get the value from default_values */
 | |
|     const uchar *dv= orig_field->table->s->default_values;
 | |
|     if (!default_value && !orig_field->is_null_in_record(dv))
 | |
|     {
 | |
|       StringBuffer<MAX_FIELD_WIDTH> tmp(charset);
 | |
|       String *res= orig_field->val_str(&tmp, orig_field->ptr_in_record(dv));
 | |
|       char *pos= (char*) thd->strmake(res->ptr(), res->length());
 | |
|       default_value= new (thd->mem_root) Virtual_column_info();
 | |
|       default_value->expr=
 | |
|         new (thd->mem_root) Item_string(thd, pos, res->length(), charset);
 | |
|       default_value->utf8= 0;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The common part for data type redefinition:
 | |
|     CREATE TABLE t1 (a INT) AS SELECT a FROM t2;
 | |
|   See Type_handler::Column_definition_redefine_stage1()
 | |
|   for data type specific code.
 | |
| 
 | |
|   @param this         - The field definition corresponding to the expression
 | |
|                         in the "AS SELECT.." part.
 | |
| 
 | |
|   @param dup_field    - The field definition from the "CREATE TABLE (...)" part.
 | |
|                         It has already underwent prepare_stage1(), so
 | |
|                         must be fully initialized:
 | |
|                         -- dup_field->charset is set and BINARY
 | |
|                            sorting style is applied, see find_bin_collation().
 | |
| 
 | |
|   @param file         - The table handler
 | |
| */
 | |
| void
 | |
| Column_definition::redefine_stage1_common(const Column_definition *dup_field,
 | |
|                                           const handler *file)
 | |
| {
 | |
|   set_handler(dup_field->type_handler());
 | |
|   default_value= dup_field->default_value;
 | |
|   DBUG_ASSERT(dup_field->charset); // Set by prepare_stage1()
 | |
|   charset=      dup_field->charset;
 | |
|   length=       dup_field->char_length;
 | |
|   pack_length=  dup_field->pack_length;
 | |
|   decimals=     dup_field->decimals;
 | |
|   unireg_check= dup_field->unireg_check;
 | |
|   flags=        dup_field->flags;
 | |
|   interval=     dup_field->interval;
 | |
|   vcol_info=    dup_field->vcol_info;
 | |
|   invisible=    dup_field->invisible;
 | |
|   check_constraint= dup_field->check_constraint;
 | |
|   comment=      dup_field->comment;
 | |
|   option_list=  dup_field->option_list;
 | |
|   versioning=   dup_field->versioning;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**
 | |
|   maximum possible character length for blob.
 | |
|   
 | |
|   This method is used in Item_field::set_field to calculate
 | |
|   max_length for Item.
 | |
|   
 | |
|   For example:
 | |
|     CREATE TABLE t2 SELECT CONCAT(tinyblob_utf8_column) FROM t1;
 | |
|   must create a "VARCHAR(255) CHARACTER SET utf8" column.
 | |
|   
 | |
|   @return
 | |
|     length
 | |
| */
 | |
| 
 | |
| uint32 Field_blob::char_length() const
 | |
| {
 | |
|   return Field_blob::character_octet_length();
 | |
| }
 | |
| 
 | |
| 
 | |
| uint32 Field_blob::character_octet_length() const
 | |
| {
 | |
|   switch (packlength)
 | |
|   {
 | |
|   case 1:
 | |
|     return 255;
 | |
|   case 2:
 | |
|     return 65535;
 | |
|   case 3:
 | |
|     return 16777215;
 | |
|   case 4:
 | |
|     return (uint32) UINT_MAX32;
 | |
|   default:
 | |
|     DBUG_ASSERT(0); // we should never go here
 | |
|     return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Makes a clone of this object for ALTER/CREATE TABLE
 | |
| 
 | |
|   @param mem_root        MEM_ROOT where to clone the field
 | |
| */
 | |
| 
 | |
| Create_field *Create_field::clone(MEM_ROOT *mem_root) const
 | |
| {
 | |
|   Create_field *res= new (mem_root) Create_field(*this);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| /**
 | |
|    Return true if default is an expression that must be saved explicitly
 | |
| 
 | |
|    This is:
 | |
|      - Not basic constants
 | |
|      - If field is a BLOB (Which doesn't support normal DEFAULT)
 | |
| */
 | |
| 
 | |
| bool Column_definition::has_default_expression()
 | |
| {
 | |
|   return (default_value &&
 | |
|           (!default_value->expr->basic_const_item() ||
 | |
|            (flags & BLOB_FLAG)));
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::set_compressed(const char *method)
 | |
| {
 | |
|   if (!method || !strcmp(method, zlib_compression_method->name))
 | |
|   {
 | |
|     unireg_check= Field::TMYSQL_COMPRESSED;
 | |
|     compression_method_ptr= zlib_compression_method;
 | |
|     return false;
 | |
|   }
 | |
|   my_error(ER_UNKNOWN_COMPRESSION_METHOD, MYF(0), method);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::set_compressed_deprecated(THD *thd, const char *method)
 | |
| {
 | |
|   warn_deprecated<1004>(thd,
 | |
|                       "<data type> <character set clause> ... COMPRESSED...",
 | |
|                       "'<data type> COMPRESSED... <character set clause> ...'");
 | |
|   return set_compressed(method);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| Column_definition::set_compressed_deprecated_column_attribute(THD *thd,
 | |
|                                                               const char *pos,
 | |
|                                                               const char *method)
 | |
| {
 | |
|   if (compression_method_ptr)
 | |
|   {
 | |
|     /*
 | |
|       Compression method has already been set, e.g.:
 | |
|         a VARCHAR(10) COMPRESSED DEFAULT 10 COMPRESSED
 | |
|     */
 | |
|     thd->parse_error(ER_SYNTAX_ERROR, pos);
 | |
|     return true;
 | |
|   }
 | |
|   enum enum_field_types sql_type= real_field_type();
 | |
|   /* We can't use f_is_blob here as pack_flag is not yet set */
 | |
|   if (sql_type == MYSQL_TYPE_VARCHAR || sql_type == MYSQL_TYPE_TINY_BLOB ||
 | |
|       sql_type == MYSQL_TYPE_BLOB || sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
 | |
|       sql_type == MYSQL_TYPE_LONG_BLOB)
 | |
|     return set_compressed_deprecated(thd, method);
 | |
|   else
 | |
|     my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Column_definition::check_vcol_for_key(THD *thd) const
 | |
| {
 | |
|   if (vcol_info && (vcol_info->flags & VCOL_NOT_STRICTLY_DETERMINISTIC))
 | |
|   {
 | |
|     /* use check_expression() to report an error */
 | |
|     check_expression(vcol_info, &field_name, VCOL_GENERATED_STORED);
 | |
|     DBUG_ASSERT(thd->is_error());
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| Send_field::Send_field(THD *thd, Item *item)
 | |
| {
 | |
|   item->make_send_field(thd, this);
 | |
|   normalize();
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   maximum possible display length for blob.
 | |
| 
 | |
|   @return
 | |
|     length
 | |
| */
 | |
| 
 | |
| uint32 Field_blob::max_display_length() const
 | |
| {
 | |
|   switch (packlength)
 | |
|   {
 | |
|   case 1:
 | |
|     return 255 * mbmaxlen();
 | |
|   case 2:
 | |
|     return 65535 * mbmaxlen();
 | |
|   case 3:
 | |
|     return 16777215 * mbmaxlen();
 | |
|   case 4:
 | |
|     return (uint32) UINT_MAX32;
 | |
|   default:
 | |
|     DBUG_ASSERT(0); // we should never go here
 | |
|     return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  Warning handling
 | |
| *****************************************************************************/
 | |
| 
 | |
| /**
 | |
| *  Produce warning or note about data saved into field.
 | |
| 
 | |
|   @param level            - level of message (Note/Warning/Error)
 | |
|   @param code             - error code of message to be produced
 | |
|   @param cut_increment    - whenever we should increase cut fields count
 | |
|   @current_row            - current row number
 | |
| 
 | |
|   @note
 | |
|     This function won't produce warning or notes or increase cut fields counter
 | |
|     if count_cuted_fields == CHECK_FIELD_IGNORE or CHECK_FIELD_EXPRESSION
 | |
|     for the current thread.
 | |
| 
 | |
|     This allows us to avoid notes in optimisation, like
 | |
|     convert_constant_item().
 | |
| 
 | |
|   @retval
 | |
|     1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE
 | |
|   @retval
 | |
|     0 otherwise
 | |
| */
 | |
| 
 | |
| bool 
 | |
| Field::set_warning(Sql_condition::enum_warning_level level, uint code,
 | |
|                    int cut_increment, ulong current_row) const
 | |
| {
 | |
|   /*
 | |
|     If this field was created only for type conversion purposes it
 | |
|     will have table == NULL.
 | |
|   */
 | |
|   THD *thd= get_thd();
 | |
| 
 | |
|   /*
 | |
|     In INPLACE ALTER, server can't know which row has generated
 | |
|     the warning, so the value of current row is supplied by the engine.
 | |
|   */
 | |
|   if (current_row)
 | |
|     thd->get_stmt_da()->reset_current_row_for_warning(current_row);
 | |
| 
 | |
|   if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION)
 | |
|   {
 | |
|     thd->cuted_fields+= cut_increment;
 | |
|     push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str,
 | |
|                         current_row ? current_row
 | |
|                         : thd->get_stmt_da()->current_row_for_warning());
 | |
|     return 0;
 | |
|   }
 | |
|   return level >= Sql_condition::WARN_LEVEL_WARN;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Produce warning or note about datetime string data saved into field.
 | |
| 
 | |
|   @param level            level of message (Note/Warning/Error)
 | |
|   @param code             error code of message to be produced
 | |
|   @param str              string value which we tried to save
 | |
|   @param ts_type          type of datetime value (datetime/date/time)
 | |
|   @param cuted_increment  whenever we should increase cut fields count or not
 | |
| 
 | |
|   @note
 | |
|     This function will always produce some warning but won't increase cut
 | |
|     fields counter if count_cuted_fields ==FIELD_CHECK_IGNORE for current
 | |
|     thread.
 | |
| 
 | |
|     See also bug#2336
 | |
| 
 | |
| */
 | |
| 
 | |
| void Field::set_datetime_warning(Sql_condition::enum_warning_level level,
 | |
|                                  uint code, const ErrConv *str,
 | |
|                                  const char *typestr, int cuted_increment)
 | |
|                                  const
 | |
| {
 | |
|   THD *thd= get_thd();
 | |
|   if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN)
 | |
|   {
 | |
|     /*
 | |
|       field_name.str can be NULL when field is not in the select list:
 | |
|         SET SESSION SQL_MODE= 'STRICT_ALL_TABLES,NO_ZERO_DATE';
 | |
|         CREATE OR REPLACE TABLE t2 SELECT 1 AS f FROM t1 GROUP BY FROM_DAYS(d);
 | |
|       Can't call push_warning_truncated_value_for_field() directly here,
 | |
|       as it expect a non-NULL name.
 | |
|     */
 | |
|     thd->push_warning_wrong_or_truncated_value(level, false, typestr,
 | |
|                                                str->ptr(),
 | |
|                                                table->s->db.str,
 | |
|                                                table->s->table_name.str,
 | |
|                                                field_name.str);
 | |
|   }
 | |
|   else
 | |
|     set_warning(level, code, cuted_increment);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field::set_warning_truncated_wrong_value(const char *type_arg,
 | |
|                                               const char *value)
 | |
| {
 | |
|   THD *thd= get_thd();
 | |
|   const char *db_name;
 | |
|   const char *table_name;
 | |
|   /*
 | |
|     table has in the past been 0 in case of wrong calls when processing
 | |
|     statistics tables. Let's protect against that.
 | |
|   */
 | |
|   DBUG_ASSERT(table);
 | |
| 
 | |
|   db_name= (table && table->s->db.str) ? table->s->db.str : "";
 | |
|   table_name= (table && table->s->table_name.str) ?
 | |
|               table->s->table_name.str : "";
 | |
| 
 | |
|   push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                       ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
 | |
|                       ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
 | |
|                       type_arg, value, db_name, table_name, field_name.str,
 | |
|                       static_cast<ulong>(thd->get_stmt_da()->
 | |
|                       current_row_for_warning()));
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Give warning for unusable key
 | |
| 
 | |
|   Note that the caller is responsible to call it only under ther
 | |
|   right note_verbosity level
 | |
| */
 | |
| 
 | |
| void Field::raise_note_cannot_use_key_part(THD *thd,
 | |
|                                            uint keynr, uint part,
 | |
|                                            const LEX_CSTRING &op,
 | |
|                                            CHARSET_INFO *op_collation,
 | |
|                                            Item *value,
 | |
|                                            Data_type_compatibility reason)
 | |
|                                            const
 | |
| {
 | |
|   StringBuffer<128> value_buffer;
 | |
|   const LEX_CSTRING keyname= table->s->key_info[keynr].name;
 | |
|   size_t value_length;
 | |
| 
 | |
|   value->print(&value_buffer, QT_EXPLAIN);
 | |
|   value_length= Well_formed_prefix(value_buffer.charset(),
 | |
|                                    value_buffer.ptr(),
 | |
|                                    MY_MIN(value_buffer.length(), 64)).length();
 | |
|   /*
 | |
|     We must use c_ptr() here for the 'T' argument as it only works with
 | |
|     zero terminated strings.
 | |
|   */
 | |
|   switch (reason){
 | |
|   case Data_type_compatibility::INCOMPATIBLE_COLLATION:
 | |
|     {
 | |
|       const LEX_CSTRING colf(charset()->coll_name);
 | |
|       const LEX_CSTRING colv(op_collation->coll_name);
 | |
|       push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
 | |
|                           ER_UNKNOWN_ERROR,
 | |
|                           "Cannot use key %`.*s part[%u] for lookup: "
 | |
|                           "%`.*s.%`.*s.%`.*s of collation %`.*s "
 | |
|                           "%.*s \"%.*T\" of collation %`.*s",
 | |
|                           (int) keyname.length, keyname.str,
 | |
|                           part,
 | |
|                           (int) table->s->db.length, table->s->db.str,
 | |
|                           (int) table->s->table_name.length,
 | |
|                           table->s->table_name.str,
 | |
|                           (int) field_name.length, field_name.str,
 | |
|                           (int) colf.length, colf.str,
 | |
|                           (int) op.length, op.str,
 | |
|                           (int) value_length, value_buffer.c_ptr_safe(),
 | |
|                           (int) colv.length, colv.str);
 | |
|     }
 | |
|     break;
 | |
|   case Data_type_compatibility::OK:
 | |
|     DBUG_ASSERT(0);
 | |
|     /* fall through */
 | |
|   case Data_type_compatibility::INCOMPATIBLE_DATA_TYPE:
 | |
|     {
 | |
|       const LEX_CSTRING dtypef(type_handler()->name().lex_cstring());
 | |
|       const LEX_CSTRING dtypev(value->type_handler()->name().lex_cstring());
 | |
|       push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
 | |
|                           ER_UNKNOWN_ERROR,
 | |
|                           "Cannot use key %`.*s part[%u] for lookup: "
 | |
|                           "%`.*s.%`.*s.%`.*s of type %`.*s "
 | |
|                           "%.*s \"%.*T\" of type %`.*s",
 | |
|                           (int) keyname.length, keyname.str,
 | |
|                           part,
 | |
|                           (int) table->s->db.length, table->s->db.str,
 | |
|                           (int) table->s->table_name.length,
 | |
|                           table->s->table_name.str,
 | |
|                           (int) field_name.length, field_name.str,
 | |
|                           (int) dtypef.length, dtypef.str,
 | |
|                           (int) op.length, op.str,
 | |
|                           (int) value_length, value_buffer.c_ptr_safe(),
 | |
|                           (int) dtypev.length, dtypev.str);
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Give warning for unusable key
 | |
| 
 | |
|   Note that the caller is responsible to call it only under ther
 | |
|   right note_verbosity level
 | |
| */
 | |
| 
 | |
| void Field::raise_note_key_become_unused(THD *thd, const String &expr) const
 | |
| {
 | |
|   push_warning_printf(thd,
 | |
|     Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
 | |
|     "Cannot use key parts with %`.*s.%`.*s.%`.*s "
 | |
|     "in the rewritten condition: %`.*s",
 | |
|     (int) table->s->db.length, table->s->db.str,
 | |
|     (int) table->s->table_name.length, table->s->table_name.str,
 | |
|     (int) field_name.length, field_name.str,
 | |
|     (int) expr.length(), expr.ptr());
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   @brief
 | |
|   Return possible keys for a field
 | |
| 
 | |
|   @details
 | |
|   Return bit map of keys over this field which can be used by the range
 | |
|   optimizer. For a field of a generic table such keys are all keys that starts
 | |
|   from this field. For a field of a materialized derived table/view such keys
 | |
|   are all keys in which this field takes a part. This is less restrictive as
 | |
|   keys for a materialized derived table/view are generated on the fly from
 | |
|   present fields, thus the case when a field for the beginning of a key is
 | |
|   absent is impossible.
 | |
| 
 | |
|   @return map of possible keys
 | |
| */
 | |
| 
 | |
| key_map Field::get_possible_keys()
 | |
| {
 | |
|   DBUG_ASSERT(table->pos_in_table_list);
 | |
|   return (table->pos_in_table_list->is_materialized_derived() ?
 | |
|           part_of_key : key_start);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record)
 | |
| {
 | |
|   MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->read_set);
 | |
|   bool rc;
 | |
|   if ((rc= validate_value_in_record(thd, record)))
 | |
|   {
 | |
|     // Get and report val_str() for the DEFAULT value
 | |
|     StringBuffer<MAX_FIELD_WIDTH> tmp;
 | |
|     val_str(&tmp, ptr_in_record(record));
 | |
|     push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                         ER_INVALID_DEFAULT_VALUE_FOR_FIELD,
 | |
|                         ER_THD(thd, ER_INVALID_DEFAULT_VALUE_FOR_FIELD),
 | |
|                         ErrConvString(&tmp).ptr(), field_name.str);
 | |
|   }
 | |
|   dbug_tmp_restore_column_map(&table->read_set, old_map);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Find which reaction should be for IGNORE value.
 | |
| */
 | |
| 
 | |
| ignore_value_reaction find_ignore_reaction(THD *thd)
 | |
| {
 | |
|   enum_sql_command com= thd->lex->sql_command;
 | |
| 
 | |
|   // All insert-like commands
 | |
|   if (com == SQLCOM_INSERT || com == SQLCOM_REPLACE ||
 | |
|       com == SQLCOM_INSERT_SELECT || com == SQLCOM_REPLACE_SELECT ||
 | |
|       com == SQLCOM_LOAD)
 | |
|   {
 | |
|     return IGNORE_MEANS_DEFAULT;
 | |
|   }
 | |
|   // Update commands
 | |
|   if (com == SQLCOM_UPDATE || com == SQLCOM_UPDATE_MULTI)
 | |
|   {
 | |
|     return IGNORE_MEANS_FIELD_VALUE;
 | |
|   }
 | |
|   return IGNORE_MEANS_ERROR;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::save_in_field_default_value(bool view_error_processing)
 | |
| {
 | |
|   THD *thd= table->in_use;
 | |
| 
 | |
|   /*
 | |
|      TODO: MDEV-19597 Refactor TABLE::vers_update_fields() via stored virtual columns
 | |
|      This condition will go away as well as other conditions with vers_sys_field().
 | |
|   */
 | |
|   if (vers_sys_field())
 | |
|   {
 | |
|     if (flags & VERS_ROW_START)
 | |
|       set_time();
 | |
|     else
 | |
|       set_max();
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (unlikely(flags & NO_DEFAULT_VALUE_FLAG &&
 | |
|                real_type() != MYSQL_TYPE_ENUM))
 | |
|   {
 | |
|     if (reset())
 | |
|     {
 | |
|       my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
 | |
|                  ER_THD(thd, ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     if (view_error_processing)
 | |
|     {
 | |
|       TABLE_LIST *view= table->pos_in_table_list->top_table();
 | |
|       push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                           ER_NO_DEFAULT_FOR_VIEW_FIELD,
 | |
|                           ER_THD(thd, ER_NO_DEFAULT_FOR_VIEW_FIELD),
 | |
|                           view->view_db.str,
 | |
|                           view->view_name.str);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                           ER_NO_DEFAULT_FOR_FIELD,
 | |
|                           ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD),
 | |
|                           field_name.str);
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   set_default();
 | |
|   return
 | |
|     !is_null() &&
 | |
|     validate_value_in_record_with_warn(thd, table->record[0]) &&
 | |
|     thd->is_error();
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::save_in_field_ignore_value(bool view_error_processing)
 | |
| {
 | |
|   enum_sql_command com= table->in_use->lex->sql_command;
 | |
|   // All insert-like commands
 | |
|   if (com == SQLCOM_INSERT || com == SQLCOM_REPLACE ||
 | |
|       com == SQLCOM_INSERT_SELECT || com == SQLCOM_REPLACE_SELECT ||
 | |
|       com == SQLCOM_LOAD)
 | |
|     return save_in_field_default_value(view_error_processing);
 | |
|   return 0; // ignore
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field::register_field_in_read_map()
 | |
| {
 | |
|   if (vcol_info)
 | |
|   {
 | |
|     Item *vcol_item= vcol_info->expr;
 | |
|     vcol_item->walk(&Item::register_field_in_read_map, 1, 0);
 | |
|   }
 | |
|   bitmap_set_bit(table->read_set, field_index);
 | |
| }
 | |
| 
 | |
| 
 | |
| LEX_STRING Field::val_lex_string_strmake(MEM_ROOT *mem)
 | |
| {
 | |
|   StringBuffer<MAX_FIELD_WIDTH> str;
 | |
|   val_str(&str);
 | |
|   char *to= strmake_root(mem, str.ptr(), str.length());
 | |
|   return to ? LEX_STRING{to, str.length()} : LEX_STRING{NULL, 0};
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to)
 | |
| {
 | |
|   StringBuffer<MAX_FIELD_WIDTH> str;
 | |
|   bool rc= false;
 | |
|   THD *thd= get_thd();
 | |
|   Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
 | |
| 
 | |
|   val_str(&str);
 | |
|   if (!(to->length= str.length()))
 | |
|     *to= empty_clex_str;
 | |
|   else if ((rc= !(to->str= strmake_root(mem_root, str.ptr(), str.length()))))
 | |
|     to->length= 0;
 | |
| 
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field::print_key_value(String *out, uint32 length)
 | |
| {
 | |
|   if (charset() == &my_charset_bin)
 | |
|     print_key_value_binary(out, ptr, length);
 | |
|   else
 | |
|     val_str(out);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_string::print_key_value(String *out, uint32 length)
 | |
| {
 | |
|   if (charset() == &my_charset_bin)
 | |
|   {
 | |
|     size_t len= field_charset()->lengthsp((const char*) ptr, length);
 | |
|     print_key_value_binary(out, ptr, static_cast<uint32>(len));
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     THD *thd= get_thd();
 | |
|     sql_mode_t sql_mode_backup= thd->variables.sql_mode;
 | |
|     thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
 | |
|     val_str(out,out);
 | |
|     thd->variables.sql_mode= sql_mode_backup;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_varstring::print_key_value(String *out, uint32 length)
 | |
| {
 | |
|   if (charset() == &my_charset_bin)
 | |
|     print_key_value_binary(out, get_data(), get_length());
 | |
|   else
 | |
|     val_str(out,out);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field_blob::print_key_value(String *out, uint32 length)
 | |
| {
 | |
|   if (charset() == &my_charset_bin)
 | |
|   {
 | |
|     uchar *blob;
 | |
|     memcpy(&blob, ptr+packlength, sizeof(uchar*));
 | |
|     print_key_value_binary(out, blob, get_length());
 | |
|   }
 | |
|   else
 | |
|     val_str(out, out);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   @brief Print value of the key part
 | |
| 
 | |
|   @param
 | |
|     out                Output string
 | |
|     key                value of the key
 | |
|     length             Length of field in bytes,
 | |
|                        excluding NULL flag and length bytes
 | |
| */
 | |
| 
 | |
| 
 | |
| void
 | |
| Field::print_key_part_value(String *out, const uchar* key, uint32 length)
 | |
| {
 | |
|   StringBuffer<128> tmp(system_charset_info);
 | |
|   uint null_byte= 0;
 | |
|   if (real_maybe_null())
 | |
|   {
 | |
|     /*
 | |
|       Byte 0 of key is the null-byte. If set, key is NULL.
 | |
|       Otherwise, print the key value starting immediately after the
 | |
|       null-byte
 | |
|     */
 | |
|     if (*key)
 | |
|     {
 | |
|       out->append(NULL_clex_str);
 | |
|       return;
 | |
|     }
 | |
|     null_byte++;  // Skip null byte
 | |
|   }
 | |
| 
 | |
|   set_key_image(key + null_byte, length);
 | |
|   print_key_value(&tmp, length);
 | |
|   if (charset() == &my_charset_bin)
 | |
|     out->append(tmp.ptr(), tmp.length(), tmp.charset());
 | |
|   else
 | |
|     tmp.print(out, system_charset_info);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Field::print_key_value_binary(String *out, const uchar* key, uint32 length)
 | |
| {
 | |
|   out->append_semi_hex((const char*)key, length, charset());
 | |
| }
 | |
| 
 | |
| 
 | |
| Virtual_column_info* Virtual_column_info::clone(THD *thd)
 | |
| {
 | |
|   Virtual_column_info* dst= new (thd->mem_root) Virtual_column_info(*this);
 | |
|   if (!dst)
 | |
|     return NULL;
 | |
|   if (expr)
 | |
|   {
 | |
|     dst->expr= expr->build_clone(thd);
 | |
|     if (!dst->expr)
 | |
|       return NULL;
 | |
|   }
 | |
|   if (!thd->make_lex_string(&dst->name, name.str, name.length))
 | |
|     return NULL;
 | |
|   return dst;
 | |
| };
 |