/*****************************************************************************

Copyright (c) 1996, 2024, Oracle and/or its affiliates.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.

This program is designed to work with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation.  The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have either included with
the program or referenced in the documentation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA

*****************************************************************************/

/** @file include/data0type.h
 Data types

 Created 1/16/1996 Heikki Tuuri
 *******************************************************/

#ifndef data0type_h
#define data0type_h

#include "univ.i"

#ifdef XTRABACKUP
#include "sql/dd/types/column.h"  // dd::enum_column_types

#endif /* XTRABACKUP */

extern ulint data_mysql_default_charset_coll;
constexpr uint32_t DATA_MYSQL_BINARY_CHARSET_COLL = 63;

/* SQL data type struct */
struct dtype_t;

/** SQL Like operator comparison types */
enum ib_like_t {
  IB_LIKE_EXACT, /**< e.g.  STRING */
  IB_LIKE_PREFIX /**< e.g., STRING% */
};

/*-------------------------------------------*/
/* The 'MAIN TYPE' of a column */
/** missing column */
constexpr uint32_t DATA_MISSING = 0;
/** character varying of the latin1_swedish_ci charset-collation; note that the
 MySQL format for this, DATA_BINARY, DATA_VARMYSQL, is also affected by
 whether the 'precise type' contains DATA_MYSQL_TRUE_VARCHAR */
constexpr uint32_t DATA_VARCHAR = 1;
/** fixed length character of the latin1_swedish_ci charset-collation */
constexpr uint32_t DATA_CHAR = 2;
/** binary string of fixed length */
constexpr uint32_t DATA_FIXBINARY = 3;
/** binary string */
constexpr uint32_t DATA_BINARY = 4;
/** binary large object, or a TEXT type; if prtype & DATA_BINARY_TYPE == 0, then
 this is actually a TEXT column (or a BLOB created with < 4.0.14; since column
 prefix indexes came only in 4.0.14, the missing flag in BLOBs  created before
 that does not cause any harm) */
constexpr uint32_t DATA_BLOB = 5;
/** integer: can be any size 1 - 8 bytes */
constexpr uint32_t DATA_INT = 6;
/** address of the child page in node pointer */
constexpr uint32_t DATA_SYS_CHILD = 7;
/** system column */
constexpr uint32_t DATA_SYS = 8;

/* Data types >= DATA_FLOAT must be compared using the whole field, not as
binary strings */

constexpr uint32_t DATA_FLOAT = 9;
constexpr uint32_t DATA_DOUBLE = 10;
/** decimal number stored as an ASCII string */
constexpr uint32_t DATA_DECIMAL = 11;
/** any charset varying length char */
constexpr uint32_t DATA_VARMYSQL = 12;
/** any charset fixed length char
NOTE that 4.1.1 used DATA_MYSQL and DATA_VARMYSQL for all character sets, and
the charset-collation for tables created with it can also be latin1_swedish_ci
*/
constexpr uint32_t DATA_MYSQL = 13;

/* DATA_POINT&DATA_VAR_POINT are for standard geometry datatype 'point' and
DATA_GEOMETRY include all other standard geometry datatypes as described in
OGC standard(line_string, polygon, multi_point, multi_polygon,
multi_line_string, geometry_collection, geometry).
Currently, geometry data is stored in the standard Well-Known Binary(WKB)
format (http://www.opengeospatial.org/standards/sfa).
We use BLOB as underlying datatype for DATA_GEOMETRY and DATA_VAR_POINT
while CHAR for DATA_POINT */
/* geometry datatype of variable length */
constexpr uint32_t DATA_GEOMETRY = 14;
/* The following two are disabled temporarily, we won't create them in
get_innobase_type_from_mysql_type().
TODO: We will enable DATA_POINT/them when we come to the fixed-length POINT
again. */
/** geometry datatype of fixed length POINT */
constexpr uint32_t DATA_POINT = 15;
/** geometry datatype of variable length POINT, used when we want to store POINT
 as BLOB internally */
constexpr uint32_t DATA_VAR_POINT = 16;
/** dtype_store_for_order_and_null_size()  requires the values are <= 63 */
constexpr uint32_t DATA_MTYPE_MAX = 63;

/** minimum value of mtype */
constexpr uint32_t DATA_MTYPE_CURRENT_MIN = DATA_VARCHAR;
/** maximum value of mtype */
constexpr uint32_t DATA_MTYPE_CURRENT_MAX = DATA_VAR_POINT;
/*-------------------------------------------*/
/* The 'PRECISE TYPE' of a column */
/*
Tables created by a MySQL user have the following convention:

- In the least significant byte in the precise type we store the MySQL type
code (not applicable for system columns).

- In the second least significant byte we OR flags DATA_NOT_NULL,
DATA_UNSIGNED, DATA_BINARY_TYPE.

- In the third least significant byte of the precise type of string types we
store the MySQL charset-collation code. In DATA_BLOB columns created with
< 4.0.14 we do not actually know if it is a BLOB or a TEXT column. Since there
are no indexes on prefixes of BLOB or TEXT columns in < 4.0.14, this is no
problem, though.

Note that versions < 4.1.2 or < 5.0.1 did not store the charset code to the
precise type, since the charset was always the default charset of the MySQL
installation. If the stored charset code is 0 in the system table SYS_COLUMNS
of InnoDB, that means that the default charset of this MySQL installation
should be used.

When loading a table definition from the system tables to the InnoDB data
dictionary cache in main memory, InnoDB versions >= 4.1.2 and >= 5.0.1 check
if the stored charset-collation is 0, and if that is the case and the type is
a non-binary string, replace that 0 by the default charset-collation code of
this MySQL installation. In short, in old tables, the charset-collation code
in the system tables on disk can be 0, but in in-memory data structures
(dtype_t), the charset-collation code is always != 0 for non-binary string
types.

In new tables, in binary string types, the charset-collation code is the
MySQL code for the 'binary charset', that is, != 0.

For binary string types and for DATA_CHAR, DATA_VARCHAR, and for those
DATA_BLOB which are binary or have the charset-collation latin1_swedish_ci,
InnoDB performs all comparisons internally, without resorting to the MySQL
comparison functions. This is to save CPU time.

InnoDB's own internal system tables have different precise types for their
columns, and for them the precise type is usually not used at all.
*/

/** English language character string: this is a relic from pre-MySQL time and
 only used for InnoDB's own system tables */
constexpr uint32_t DATA_ENGLISH = 4;
/** another relic from pre-MySQL time */
constexpr uint32_t DATA_ERROR = 111;

/** AND with this mask to extract the MySQL type from the precise type */
constexpr uint32_t DATA_MYSQL_TYPE_MASK = 255;
/** MySQL type code for the >= 5.0.3 format true VARCHAR */
constexpr uint32_t DATA_MYSQL_TRUE_VARCHAR = 15;

/* Precise data types for system columns and the length of those columns;
NOTE: the values must run from 0 up in the order given! All codes must
be less than 256 */
/** row id: a 48-bit integer */
constexpr uint32_t DATA_ROW_ID = 0;
/** stored length for row id */
constexpr uint32_t DATA_ROW_ID_LEN = 6;

/** Transaction id: 6 bytes */
constexpr size_t DATA_TRX_ID = 1;

/** Transaction ID type size in bytes. */
constexpr size_t DATA_TRX_ID_LEN = 6;

/** Rollback data pointer: 7 bytes */
constexpr size_t DATA_ROLL_PTR = 2;

/** Rollback data pointer type size in bytes. */
constexpr size_t DATA_ROLL_PTR_LEN = 7;

/** number of system columns defined above */
constexpr uint32_t DATA_N_SYS_COLS = 3;

/** number of system columns for intrinsic temporary table */
constexpr uint32_t DATA_ITT_N_SYS_COLS = 2;

/** Used as FTS DOC ID column */
constexpr uint32_t DATA_FTS_DOC_ID = 3;

/** mask to extract the above from prtype */
constexpr uint32_t DATA_SYS_PRTYPE_MASK = 0xF;

/* Flags ORed to the precise data type */
/** this is ORed to the precise type when the column is declared as NOT NULL */
constexpr uint32_t DATA_NOT_NULL = 256;
/* this id ORed to the precise type when we have an unsigned integer type */
constexpr uint32_t DATA_UNSIGNED = 512;
/** if the data type is a binary character string, this is ORed to the precise
 type: this only holds for tables created with >= MySQL-4.0.14 */
constexpr uint32_t DATA_BINARY_TYPE = 1024;
/** Used as GIS MBR column */
constexpr uint32_t DATA_GIS_MBR = 2048;
/** GIS MBR length*/
constexpr uint32_t DATA_MBR_LEN = SPDIMS * 2 * sizeof(double);

/** this is ORed to the precise data type when the column is true VARCHAR where
 MySQL uses 2 bytes to store the data len; for shorter VARCHARs MySQL uses
 only 1 byte */
constexpr uint32_t DATA_LONG_TRUE_VARCHAR = 4096;
/** Virtual column */
constexpr uint32_t DATA_VIRTUAL = 8192;
/** Multi-value Virtual column */
constexpr uint32_t DATA_MULTI_VALUE = 16384;

/*-------------------------------------------*/

/* This many bytes we need to store the type information affecting the
alphabetical order for a single field and decide the storage size of an
SQL null*/
constexpr uint32_t DATA_ORDER_NULL_TYPE_BUF_SIZE = 4;
/* In the >= 4.1.x storage format we add 2 bytes more so that we can also
store the charset-collation number; one byte is left unused, though */
constexpr uint32_t DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE = 6;

/* Maximum multi-byte character length in bytes, plus 1 */
constexpr uint32_t DATA_MBMAX = 5;

/* For DATA_POINT of dimension 2, the length of value in btree is always 25,
which is the summary of:
SRID_SIZE(4) + WKB_HEADER_SIZE(1+4) + POINT_DATA_SIZE(8*2).
So the length of physical record or POINT KEYs on btree are 25.
GIS_TODO: When we support multi-dimensions DATA_POINT, we should get the
length from corresponding column or index definition, instead of this MACRO
*/
constexpr uint32_t DATA_POINT_LEN = 25;

/* Pack mbminlen, mbmaxlen to mbminmaxlen. */
inline ulint DATA_MBMINMAXLEN(ulint mbminlen, ulint mbmaxlen) {
  return mbmaxlen * DATA_MBMAX + mbminlen;
}
/* Get mbminlen from mbminmaxlen. Cast the result of UNIV_EXPECT to ulint
because in GCC it returns a long. */
inline ulint DATA_MBMINLEN(ulint mbminmaxlen) {
  return UNIV_EXPECT(mbminmaxlen % DATA_MBMAX, 1);
}
/* Get mbmaxlen from mbminmaxlen. */
inline ulint DATA_MBMAXLEN(ulint mbminmaxlen) {
  return mbminmaxlen / DATA_MBMAX;
}

/* For checking if a geom_type is POINT */
inline bool DATA_POINT_MTYPE(ulint mtype) {
  return mtype == DATA_POINT || mtype == DATA_VAR_POINT;
}

/* For checking if mtype is GEOMETRY datatype */
inline bool DATA_GEOMETRY_MTYPE(ulint mtype) {
  return DATA_POINT_MTYPE(mtype) || mtype == DATA_GEOMETRY;
}

/* For checking if mtype is BLOB or GEOMETRY, since we use BLOB as
the underling datatype of GEOMETRY(not DATA_POINT) data. */
inline bool DATA_LARGE_MTYPE(ulint mtype) {
  return mtype == DATA_BLOB || mtype == DATA_VAR_POINT ||
         mtype == DATA_GEOMETRY;
}

/* For checking if data type is big length data type. */
inline bool DATA_BIG_LEN_MTYPE(ulint len, ulint mtype) {
  return len > 255 || DATA_LARGE_MTYPE(mtype);
}

/* For checking if the column is a big length column. */
#define DATA_BIG_COL(col) DATA_BIG_LEN_MTYPE((col)->len, (col)->mtype)

/* We now support 15 bits (up to 32767) collation number */
constexpr uint32_t MAX_CHAR_COLL_NUM = 32767;

/* Mask to get the Charset Collation number (0x7fff) */
constexpr uint32_t CHAR_COLL_MASK = MAX_CHAR_COLL_NUM;

#ifndef UNIV_HOTBACKUP
/** Gets the MySQL type code from a dtype.
 @return MySQL type code; this is NOT an InnoDB type code! */
static inline ulint dtype_get_mysql_type(
    const dtype_t *type); /*!< in: type struct */
/** Determine how many bytes the first n characters of the given string occupy.
 If the string is shorter than n characters, returns the number of bytes the
 characters in the string occupy.
 @return length of the prefix, in bytes */
ulint dtype_get_at_most_n_mbchars(
    ulint prtype,      /*!< in: precise type */
    ulint mbminmaxlen, /*!< in: minimum and maximum length of
                       a multi-byte character */
    ulint prefix_len,  /*!< in: length of the requested
                       prefix, in characters, multiplied by
                       dtype_get_mbmaxlen(dtype) */
    ulint data_len,    /*!< in: length of str (in bytes) */
    const char *str);  /*!< in: the string whose prefix
                       length is being determined */
#endif                 /* !UNIV_HOTBACKUP */
/** Checks if a data main type is a string type. Also a BLOB is considered a
 string type.
 @return true if string type */
bool dtype_is_string_type(
    ulint mtype); /*!< in: InnoDB main data type code: DATA_CHAR, ... */

#ifdef XTRABACKUP
/** Checks if the main data type is a real temporal type with fractional
    seconds

 "real type" in MySQL has different meanings in different contexts. It can mean

 - a data type allowing fractional numbers for precision
 - an underlying datatype used internally.

   For example binlog stores CHAR, VARCHAR, SET and ENUM as MYSQL_TYPE_STRING.
   For such types, field->type() will be MYSQL_TYPE_STRING and
   field->real_type() will return the actual underlying data type.

   However in the context of collation of temporal types, it is safe to assume
   that real temporal type are the types which allow with fractional seconds
   since ther are stored and displayed as DATA_FIXBINARY.

@param[in]      type   InnoDB main column type TINY, SHORT,
@return true if real temporal type */
bool dtype_is_real_temporal_type(dd::enum_column_types type);
#endif /* XTRABACKUP */

/** Checks if a type is a binary string type. Note that for tables created
 with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT
 column. For those DATA_BLOB columns this function currently returns false.
 @return true if binary string type */
bool dtype_is_binary_string_type(ulint mtype,   /*!< in: main data type */
                                 ulint prtype); /*!< in: precise type */
/** Checks if a type is a non-binary string type. That is,
 dtype_is_string_type is true and dtype_is_binary_string_type is false. Note
 that for tables created with < 4.0.14, we do not know if a DATA_BLOB column
 is a BLOB or a TEXT column. For those DATA_BLOB columns this function
 currently returns true.
 @return true if non-binary string type */
bool dtype_is_non_binary_string_type(ulint mtype,   /*!< in: main data type */
                                     ulint prtype); /*!< in: precise type */

/** Sets a data type structure.
@param[in]      type    type struct to init
@param[in]      mtype   main data type
@param[in]      prtype  precise type
@param[in]      len     precision of type */
static inline void dtype_set(dtype_t *type, ulint mtype, ulint prtype,
                             ulint len);

/** Copies a data type structure.
@param[in]      type1   type struct to copy to
@param[in]      type2   type struct to copy from */
static inline void dtype_copy(dtype_t *type1, const dtype_t *type2);

/** Gets the SQL main data type.
 @return SQL main data type */
static inline ulint dtype_get_mtype(const dtype_t *type); /*!< in: data type */
/** Gets the precise data type.
 @return precise data type */
static inline ulint dtype_get_prtype(const dtype_t *type); /*!< in: data type */

#ifndef UNIV_HOTBACKUP
/** Compute the mbminlen and mbmaxlen members of a data type structure.
@param[in]      mtype           main type
@param[in]      prtype          precise type (and collation)
@param[out]     mbminlen        minimum length of a multi-byte character
@param[out]     mbmaxlen        maximum length of a multi-byte character */
static inline void dtype_get_mblen(ulint mtype, ulint prtype, ulint *mbminlen,
                                   ulint *mbmaxlen);
#endif

/** Gets the MySQL charset-collation code for MySQL string types.
 @return MySQL charset-collation code */
static inline ulint dtype_get_charset_coll(
    ulint prtype); /*!< in: precise data type */
/** Forms a precise type from the < 4.1.2 format precise type plus the
 charset-collation code.
 @return precise type, including the charset-collation code */
ulint dtype_form_prtype(
    ulint old_prtype,    /*!< in: the MySQL type code and the flags
                         DATA_BINARY_TYPE etc. */
    ulint charset_coll); /*!< in: MySQL charset-collation code */

#ifndef UNIV_HOTBACKUP
/** Determines if a MySQL string type is a subset of UTF-8.  This function
 may return false negatives, in case further character-set collation
 codes are introduced in MySQL later.
 @return true if a subset of UTF-8 */
static inline bool dtype_is_utf8(ulint prtype); /*!< in: precise data type */
#endif

/** Gets the type length.
 @return fixed length of the type, in bytes, or 0 if variable-length */
static inline ulint dtype_get_len(const dtype_t *type); /*!< in: data type */
#ifndef UNIV_HOTBACKUP
/** Gets the minimum length of a character, in bytes.
 @return minimum length of a char, in bytes, or 0 if this is not a
 character type */
static inline ulint dtype_get_mbminlen(const dtype_t *type); /*!< in: type */
/** Gets the maximum length of a character, in bytes.
 @return maximum length of a char, in bytes, or 0 if this is not a
 character type */
static inline ulint dtype_get_mbmaxlen(const dtype_t *type); /*!< in: type */

/** Sets the minimum and maximum length of a character, in bytes.
@param[in,out]  type            type
@param[in]      mbminlen        minimum length of a char, in bytes, or 0 if
                                this is not a character type
@param[in]      mbmaxlen        maximum length of a char, in bytes, or 0 if
                                this is not a character type */
static inline void dtype_set_mbminmaxlen(dtype_t *type, ulint mbminlen,
                                         ulint mbmaxlen);
#endif /* !UNIV_HOTBACKUP */

/** Returns the size of a fixed size data type, 0 if not a fixed size type.
@param[in]      mtype           main type
@param[in]      prtype          precise type
@param[in]      len             length
@param[in]      mbminmaxlen     minimum and maximum length of a multibyte
                                character, in bytes
@param[in]      comp            nonzero=ROW_FORMAT=COMPACT
@return fixed size, or 0 */
static inline ulint dtype_get_fixed_size_low(ulint mtype, ulint prtype,
                                             ulint len, ulint mbminmaxlen,
                                             bool comp);

/** Returns the minimum size of a data type.
@param[in]      mtype           main type
@param[in]      prtype          precise type
@param[in]      len             length
@param[in]      mbminmaxlen     minimum and maximum length of a multibyte
                                character, in bytes
@return minimum size */
static inline ulint dtype_get_min_size_low(ulint mtype, ulint prtype, ulint len,
                                           ulint mbminmaxlen);

/** Returns the maximum size of a data type. Note: types in system tables may
be incomplete and return incorrect information.
@param[in]      mtype   main type
@param[in]      len     length
@return maximum size */
static inline ulint dtype_get_max_size_low(ulint mtype, ulint len);

/** Returns the ROW_FORMAT=REDUNDANT stored SQL NULL size of a type.
For fixed length types it is the fixed length of the type, otherwise 0.
@param[in]      type    type struct
@param[in]      comp    nonzero=ROW_FORMAT=COMPACT
@return SQL null storage size in ROW_FORMAT=REDUNDANT */
static inline ulint dtype_get_sql_null_size(const dtype_t *type, bool comp);

#ifndef UNIV_HOTBACKUP
/** Reads to a type the stored information which determines its alphabetical
ordering and the storage size of an SQL NULL value.
@param[in]      type    type struct
@param[in]      buf     buffer for the stored order info */
static inline void dtype_read_for_order_and_null_size(dtype_t *type,
                                                      const byte *buf);

/** Stores for a type the information which determines its alphabetical
ordering and the storage size of an SQL NULL value. This is the >= 4.1.x
storage format.
@param[in]      buf             buffer for DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE
                                bytes where we store the info
@param[in]      type            type struct
@param[in]      prefix_len      prefix length to replace type->len, or 0 */
static inline void dtype_new_store_for_order_and_null_size(byte *buf,
                                                           const dtype_t *type,
                                                           ulint prefix_len);

/** Reads to a type the stored information which determines its alphabetical
ordering and the storage size of an SQL NULL value. This is the 4.1.x storage
format.
@param[in]      type    type struct
@param[in]      buf     buffer for stored type order info */
static inline void dtype_new_read_for_order_and_null_size(dtype_t *type,
                                                          const byte *buf);

/** Returns the type's SQL name (e.g. BIGINT UNSIGNED) from mtype,prtype,len
@param[in]      mtype   main type
@param[in]      prtype  precise type
@param[in]      len     length
@param[out]     name    SQL name
@param[in]      name_sz size of the name buffer
@return the SQL type name */
static inline char *dtype_sql_name(unsigned mtype, unsigned prtype,
                                   unsigned len, char *name, unsigned name_sz);
#endif /* !UNIV_HOTBACKUP */

/** Validates a data type structure.
 @return true if ok */
bool dtype_validate(const dtype_t *type); /*!< in: type struct to validate */
#ifdef UNIV_DEBUG
/** Print a data type structure.
@param[in]      type    data type */
void dtype_print(const dtype_t *type);
#endif /* UNIV_DEBUG */

/* Structure for an SQL data type.
If you add fields to this structure, be sure to initialize them everywhere.
This structure is initialized in the following functions:
dtype_set()
dtype_read_for_order_and_null_size()
dtype_new_read_for_order_and_null_size()
sym_tab_add_null_lit() */

struct dtype_t {
  unsigned prtype : 32; /*!< precise type; MySQL data
                        type, charset code, flags to
                        indicate nullability,
                        signedness, whether this is a
                        binary string, whether this is
                        a true VARCHAR where MySQL
                        uses 2 bytes to store the length */
  unsigned mtype : 8;   /*!< main data type */

  /* the remaining fields do not affect alphabetical ordering: */

  unsigned len : 16;        /*!< length; for MySQL data this
                            is field->pack_length(),
                            except that for a >= 5.0.3
                            type true VARCHAR this is the
                            maximum byte length of the
                            string data (in addition to
                            the string, MySQL uses 1 or 2
                            bytes to store the string length) */
  unsigned mbminmaxlen : 5; /*!< minimum and maximum length of a
                            character, in bytes;
                            DATA_MBMINMAXLEN(mbminlen,mbmaxlen);
                            mbminlen=DATA_MBMINLEN(mbminmaxlen);
                            mbmaxlen=DATA_MBMINLEN(mbminmaxlen) */

  bool is_virtual() const { return ((prtype & DATA_VIRTUAL) == DATA_VIRTUAL); }

  /** Check if the value in mtype is valid.
  @return true if mtype is valid, false otherwise. */
  bool is_mtype_valid() const;

  std::ostream &print(std::ostream &out) const;
};

inline std::ostream &operator<<(std::ostream &out, const dtype_t &obj) {
  return (obj.print(out));
}

static_assert(true == 1, "true != 1");

static_assert(DATA_TRX_ID_LEN == 6, "DATA_TRX_ID_LEN != 6!");

static_assert(DATA_ROLL_PTR_LEN == 7, "DATA_PTR_LEN != 7!");

static_assert(DATA_TRX_ID + 1 == DATA_ROLL_PTR, "DATA_TRX_ID value invalid!");

inline bool dtype_t::is_mtype_valid() const {
  const bool is_valid =
      (mtype >= DATA_MTYPE_CURRENT_MIN) && (mtype <= DATA_MTYPE_CURRENT_MAX);
  ut_ad(is_valid);
  return is_valid;
}

#include "data0type.ic"

#endif
