/******************************************************
Data types

(c) 1996 Innobase Oy

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

#include "mach0data.h"

#ifndef UNIV_HOTBACKUP
/**********************************************************************
Get the variable length bounds of the given character set.

NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
this function, you MUST change also the prototype here! */
extern
void
innobase_get_cset_width(
/*====================*/
	ulint	cset,		/* in: MySQL charset-collation code */
	ulint*	mbminlen,	/* out: minimum length of a char (in bytes) */
	ulint*	mbmaxlen);	/* out: maximum length of a char (in bytes) */
#endif /* !UNIV_HOTBACKUP */

/*************************************************************************
Gets the MySQL charset-collation code for MySQL string types. */
UNIV_INLINE
ulint
dtype_get_charset_coll(
/*===================*/
	ulint	prtype)	/* in: precise data type */
{
	return((prtype >> 16) & 0xFFUL);
}

/*************************************************************************
Gets the MySQL type code from a dtype. */
UNIV_INLINE
ulint
dtype_get_mysql_type(
/*=================*/
				/* out: MySQL type code; this is NOT an InnoDB
				type code! */
	dtype_t*	type)	/* in: type struct */
{
	return(type->prtype & 0xFFUL);
}

/*************************************************************************
Compute the mbminlen and mbmaxlen members of a data type structure. */
UNIV_INLINE
void
dtype_get_mblen(
/*============*/
	ulint	mtype,		/* in: main type */
	ulint	prtype,		/* in: precise type (and collation) */
	ulint*	mbminlen,	/* out: minimum length of a
				multi-byte character */
	ulint*	mbmaxlen)	/* out: maximum length of a
				multi-byte character */
{
	if (dtype_is_string_type(mtype)) {
#ifndef UNIV_HOTBACKUP
		innobase_get_cset_width(dtype_get_charset_coll(prtype),
					mbminlen, mbmaxlen);
		ut_ad(*mbminlen <= *mbmaxlen);
		ut_ad(*mbminlen <= 2); /* mbminlen in dtype_t is 0..3 */
		ut_ad(*mbmaxlen < 1 << 3); /* mbmaxlen in dtype_t is 0..7 */
#else /* !UNIV_HOTBACKUP */
		ut_a(mtype <= DATA_BINARY);
		*mbminlen = *mbmaxlen = 1;
#endif /* !UNIV_HOTBACKUP */
	} else {
		*mbminlen = *mbmaxlen = 0;
	}
}

/*************************************************************************
Compute the mbminlen and mbmaxlen members of a data type structure. */
UNIV_INLINE
void
dtype_set_mblen(
/*============*/
	dtype_t*	type)	/* in/out: type */
{
	ulint	mbminlen;
	ulint	mbmaxlen;

	dtype_get_mblen(type->mtype, type->prtype, &mbminlen, &mbmaxlen);
	type->mbminlen = mbminlen;
	type->mbmaxlen = mbmaxlen;

	ut_ad(dtype_validate(type));
}

/*************************************************************************
Sets a data type structure. */
UNIV_INLINE
void
dtype_set(
/*======*/
	dtype_t*	type,	/* in: type struct to init */
	ulint		mtype,	/* in: main data type */
	ulint		prtype,	/* in: precise type */
	ulint		len)	/* in: precision of type */
{
	ut_ad(type);
	ut_ad(mtype <= DATA_MTYPE_MAX);

	type->mtype = mtype;
	type->prtype = prtype;
	type->len = len;

	dtype_set_mblen(type);
}

/*************************************************************************
Copies a data type structure. */
UNIV_INLINE
void
dtype_copy(
/*=======*/
	dtype_t*	type1,	/* in: type struct to copy to */
	const dtype_t*	type2)	/* in: type struct to copy from */
{
	*type1 = *type2;

	ut_ad(dtype_validate(type1));
}

/*************************************************************************
Gets the SQL main data type. */
UNIV_INLINE
ulint
dtype_get_mtype(
/*============*/
	dtype_t*	type)
{
	ut_ad(type);

	return(type->mtype);
}

/*************************************************************************
Gets the precise data type. */
UNIV_INLINE
ulint
dtype_get_prtype(
/*=============*/
	dtype_t*	type)
{
	ut_ad(type);

	return(type->prtype);
}

/*************************************************************************
Gets the type length. */
UNIV_INLINE
ulint
dtype_get_len(
/*==========*/
	dtype_t*	type)
{
	ut_ad(type);

	return(type->len);
}

/*************************************************************************
Gets the minimum length of a character, in bytes. */
UNIV_INLINE
ulint
dtype_get_mbminlen(
/*===============*/
				/* out: minimum length of a char, in bytes,
				or 0 if this is not a character type */
	const dtype_t*	type)	/* in: type */
{
	ut_ad(type);
	return(type->mbminlen);
}
/*************************************************************************
Gets the maximum length of a character, in bytes. */
UNIV_INLINE
ulint
dtype_get_mbmaxlen(
/*===============*/
				/* out: maximum length of a char, in bytes,
				or 0 if this is not a character type */
	const dtype_t*	type)	/* in: type */
{
	ut_ad(type);
	return(type->mbmaxlen);
}

/*************************************************************************
Gets the padding character code for a type. */
UNIV_INLINE
ulint
dtype_get_pad_char(
/*===============*/
				/* out: padding character code, or
				ULINT_UNDEFINED if no padding specified */
	ulint	mtype,		/* in: main type */
	ulint	prtype)		/* in: precise type */
{
	switch (mtype) {
	case DATA_FIXBINARY:
	case DATA_BINARY:
		if (UNIV_UNLIKELY(dtype_get_charset_coll(prtype)
				  == DATA_MYSQL_BINARY_CHARSET_COLL)) {
			/* Starting from 5.0.18, do not pad
			VARBINARY or BINARY columns. */
			return(ULINT_UNDEFINED);
		}
		/* Fall through */
	case DATA_CHAR:
	case DATA_VARCHAR:
	case DATA_MYSQL:
	case DATA_VARMYSQL:
		/* Space is the padding character for all char and binary
		strings, and starting from 5.0.3, also for TEXT strings. */

		return(0x20);
	case DATA_BLOB:
		if (!(prtype & DATA_BINARY_TYPE)) {
			return(0x20);
		}
		/* Fall through */
	default:
		/* No padding specified */
		return(ULINT_UNDEFINED);
	}
}

/**************************************************************************
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. */
UNIV_INLINE
void
dtype_new_store_for_order_and_null_size(
/*====================================*/
	byte*		buf,	/* in: buffer for
				DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE
				bytes where we store the info */
	dtype_t*	type,	/* in: type struct */
	ulint		prefix_len)/* in: prefix length to
				replace type->len, or 0 */
{
#if 6 != DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE
#error "6 != DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE"
#endif
	ulint	len;

	buf[0] = (byte)(type->mtype & 0xFFUL);

	if (type->prtype & DATA_BINARY_TYPE) {
		buf[0] = buf[0] | 128;
	}

	/* In versions < 4.1.2 we had:	if (type->prtype & DATA_NONLATIN1) {
	buf[0] = buf[0] | 64;
	}
	*/

	buf[1] = (byte)(type->prtype & 0xFFUL);

	len = prefix_len ? prefix_len : type->len;

	mach_write_to_2(buf + 2, len & 0xFFFFUL);

	ut_ad(dtype_get_charset_coll(type->prtype) < 256);
	mach_write_to_2(buf + 4, dtype_get_charset_coll(type->prtype));

	if (type->prtype & DATA_NOT_NULL) {
		buf[4] |= 128;
	}
}

/**************************************************************************
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. */
UNIV_INLINE
void
dtype_read_for_order_and_null_size(
/*===============================*/
	dtype_t*	type,	/* in: type struct */
	byte*		buf)	/* in: buffer for stored type order info */
{
#if 4 != DATA_ORDER_NULL_TYPE_BUF_SIZE
# error "4 != DATA_ORDER_NULL_TYPE_BUF_SIZE"
#endif

	type->mtype = buf[0] & 63;
	type->prtype = buf[1];

	if (buf[0] & 128) {
		type->prtype = type->prtype | DATA_BINARY_TYPE;
	}

	type->len = mach_read_from_2(buf + 2);

	type->prtype = dtype_form_prtype(type->prtype,
					 data_mysql_default_charset_coll);
	dtype_set_mblen(type);
}

/**************************************************************************
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. */
UNIV_INLINE
void
dtype_new_read_for_order_and_null_size(
/*===================================*/
	dtype_t*	type,	/* in: type struct */
	byte*		buf)	/* in: buffer for stored type order info */
{
	ulint	charset_coll;

#if 6 != DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE
#error "6 != DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE"
#endif

	type->mtype = buf[0] & 63;
	type->prtype = buf[1];

	if (buf[0] & 128) {
		type->prtype |= DATA_BINARY_TYPE;
	}

	if (buf[4] & 128) {
		type->prtype |= DATA_NOT_NULL;
	}

	type->len = mach_read_from_2(buf + 2);

	mach_read_from_2(buf + 4);

	charset_coll = mach_read_from_2(buf + 4) & 0x7fff;

	if (dtype_is_string_type(type->mtype)) {
		ut_a(charset_coll < 256);

		if (charset_coll == 0) {
			/* This insert buffer record was inserted with MySQL
			version < 4.1.2, and the charset-collation code was not
			explicitly stored to dtype->prtype at that time. It
			must be the default charset-collation of this MySQL
			installation. */

			charset_coll = data_mysql_default_charset_coll;
		}

		type->prtype = dtype_form_prtype(type->prtype, charset_coll);
	}
	dtype_set_mblen(type);
}

/***************************************************************************
Returns the size of a fixed size data type, 0 if not a fixed size type. */
UNIV_INLINE
ulint
dtype_get_fixed_size_low(
/*=====================*/
				/* out: fixed size, or 0 */
	ulint	mtype,		/* in: main type */
	ulint	prtype,		/* in: precise type */
	ulint	len,		/* in: length */
	ulint	mbminlen,	/* in: minimum length of a multibyte char */
	ulint	mbmaxlen)	/* in: maximum length of a multibyte char */
{
	switch (mtype) {
	case DATA_SYS:
#ifdef UNIV_DEBUG
		switch (prtype & DATA_MYSQL_TYPE_MASK) {
		case DATA_ROW_ID:
			ut_ad(len == DATA_ROW_ID_LEN);
			break;
		case DATA_TRX_ID:
			ut_ad(len == DATA_TRX_ID_LEN);
			break;
		case DATA_ROLL_PTR:
			ut_ad(len == DATA_ROLL_PTR_LEN);
			break;
		default:
			ut_ad(0);
			return(0);
		}
#endif /* UNIV_DEBUG */
	case DATA_CHAR:
	case DATA_FIXBINARY:
	case DATA_INT:
	case DATA_FLOAT:
	case DATA_DOUBLE:
		return(len);
	case DATA_MYSQL:
		if (prtype & DATA_BINARY_TYPE) {
			return(len);
		} else {
#ifdef UNIV_HOTBACKUP
			if (mbminlen == mbmaxlen) {
				return(len);
			}
#else /* UNIV_HOTBACKUP */
			/* We play it safe here and ask MySQL for
			mbminlen and mbmaxlen.	Although
			mbminlen and mbmaxlen are
			initialized if and only if prtype
			is (in one of the 3 functions in this file),
			it could be that none of these functions
			has been called. */

			ulint	i_mbminlen, i_mbmaxlen;

			innobase_get_cset_width(
				dtype_get_charset_coll(prtype),
				&i_mbminlen, &i_mbmaxlen);

			if (UNIV_UNLIKELY(mbminlen != i_mbminlen)
			    || UNIV_UNLIKELY(mbmaxlen != i_mbmaxlen)) {

				ut_print_timestamp(stderr);
				fprintf(stderr, "  InnoDB: "
					"mbminlen=%lu, "
					"mbmaxlen=%lu, "
					"type->mbminlen=%lu, "
					"type->mbmaxlen=%lu\n",
					(ulong) i_mbminlen,
					(ulong) i_mbmaxlen,
					(ulong) mbminlen,
					(ulong) mbmaxlen);
			}
			if (mbminlen == mbmaxlen) {
				return(len);
			}
#endif /* !UNIV_HOTBACKUP */
		}
		/* fall through for variable-length charsets */
	case DATA_VARCHAR:
	case DATA_BINARY:
	case DATA_DECIMAL:
	case DATA_VARMYSQL:
	case DATA_BLOB:
		return(0);
	default:
		ut_error;
	}

	return(0);
}

/***************************************************************************
Returns the minimum size of a data type. */
UNIV_INLINE
ulint
dtype_get_min_size_low(
/*===================*/
				/* out: minimum size */
	ulint	mtype,		/* in: main type */
	ulint	prtype,		/* in: precise type */
	ulint	len,		/* in: length */
	ulint	mbminlen,	/* in: minimum length of a multibyte char */
	ulint	mbmaxlen)	/* in: maximum length of a multibyte char */
{
	switch (mtype) {
	case DATA_SYS:
#ifdef UNIV_DEBUG
		switch (prtype & DATA_MYSQL_TYPE_MASK) {
		case DATA_ROW_ID:
			ut_ad(len == DATA_ROW_ID_LEN);
			break;
		case DATA_TRX_ID:
			ut_ad(len == DATA_TRX_ID_LEN);
			break;
		case DATA_ROLL_PTR:
			ut_ad(len == DATA_ROLL_PTR_LEN);
			break;
		default:
			ut_ad(0);
			return(0);
		}
#endif /* UNIV_DEBUG */
	case DATA_CHAR:
	case DATA_FIXBINARY:
	case DATA_INT:
	case DATA_FLOAT:
	case DATA_DOUBLE:
		return(len);
	case DATA_MYSQL:
		if ((prtype & DATA_BINARY_TYPE) || mbminlen == mbmaxlen) {
			return(len);
		}
		/* this is a variable-length character set */
		ut_a(mbminlen > 0);
		ut_a(mbmaxlen > mbminlen);
		ut_a(len % mbmaxlen == 0);
		return(len * mbminlen / mbmaxlen);
	case DATA_VARCHAR:
	case DATA_BINARY:
	case DATA_DECIMAL:
	case DATA_VARMYSQL:
	case DATA_BLOB:
		return(0);
	default:
		ut_error;
	}

	return(0);
}

/***************************************************************************
Returns the maximum size of a data type. Note: types in system tables may be
incomplete and return incorrect information. */
UNIV_INLINE
ulint
dtype_get_max_size_low(
/*===================*/
				/* out: maximum size */
	ulint	mtype,		/* in: main type */
	ulint	len)		/* in: length */
{
	switch (mtype) {
	case DATA_SYS:
	case DATA_CHAR:
	case DATA_FIXBINARY:
	case DATA_INT:
	case DATA_FLOAT:
	case DATA_DOUBLE:
	case DATA_MYSQL:
	case DATA_VARCHAR:
	case DATA_BINARY:
	case DATA_DECIMAL:
	case DATA_VARMYSQL:
		return(len);
	case DATA_BLOB:
		break;
	default:
		ut_error;
	}

	return(ULINT_MAX);
}

/***************************************************************************
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. */
UNIV_INLINE
ulint
dtype_get_sql_null_size(
/*====================*/
				/* out: SQL null storage size
				in ROW_FORMAT=REDUNDANT */
	const dtype_t*	type)	/* in: type */
{
	return(dtype_get_fixed_size_low(type->mtype, type->prtype, type->len,
					type->mbminlen, type->mbmaxlen) > 0);
}
