Commit 6075463f authored by unknown's avatar unknown
Browse files

InnoDB: Make CREATE TABLE return error when the minimum row length

exceeds the maximum record size.  (Bug #5682)


innobase/data/data0type.c:
  Remove function dtype_str_needs_mysql_cmp().
  Document dtype_get_at_most_n_mbchars().
  dtype_get_at_most_n_mbchars(): Use mbminlen and mbmaxlen.
innobase/dict/dict0crea.c:
  dict_build_table_def_step(): Reject if minimum row size is too big.
innobase/include/data0type.h:
  Remove dtype_str_needs_mysql_cmp().
  Document dtype_get_at_most_n_mbchars().
  Add dtype_get_min_size().
innobase/include/data0type.ic:
  Add dtype_get_min_size().
innobase/include/row0mysql.h:
  row_mysql_store_col_in_innobase_format(): Add parameter comp,
  as we will only truncate UTF-8 CHAR(n) columns in row_format=compact.
innobase/include/row0mysql.ic:
  row_mysql_store_col_in_innobase_format(): Add parameter comp,
  as we will only truncate UTF-8 CHAR(n) columns in row_format=compact.
innobase/row/row0mysql.c:
  Pass parameter comp to row_mysql_store_col_in_innobase_format().
innobase/row/row0sel.c:
  Pass parameter comp to row_mysql_store_col_in_innobase_format().
  row_sel_field_store_in_mysql_format(): Undo the stripping of
  UTF-8 CHAR(n) columns by padding with spaces.
parent ca021698
Loading
Loading
Loading
Loading
+14 −35
Original line number Diff line number Diff line
@@ -45,54 +45,33 @@ dtype_t dtype_binary_val = {DATA_BINARY, 0, 0, 0, 0, 0};
dtype_t* 	dtype_binary 	= &dtype_binary_val;

/*************************************************************************
Checks if a string type has to be compared by the MySQL comparison functions.
InnoDB internally only handles binary byte string comparisons, as well as
latin1_swedish_ci strings. For example, UTF-8 strings have to be compared
by MySQL. */

ibool
dtype_str_needs_mysql_cmp(
/*======================*/
				/* out: TRUE if a string type that requires
				comparison with MySQL functions */
	dtype_t*	dtype)	/* in: type struct */
{
	if (dtype->mtype == DATA_MYSQL
	    || dtype->mtype == DATA_VARMYSQL
	    || (dtype->mtype == DATA_BLOB
	        && 0 == (dtype->prtype & DATA_BINARY_TYPE)
		&& dtype_get_charset_coll(dtype->prtype) !=
				data_mysql_latin1_swedish_charset_coll)) {
		return(TRUE);
	}

	return(FALSE);
}

/*************************************************************************
For the documentation of this function, see innobase_get_at_most_n_mbchars()
in ha_innodb.cc. */
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. */

ulint
dtype_get_at_most_n_mbchars(
/*========================*/
	dtype_t*	dtype,
	ulint		prefix_len,
	ulint		data_len,
	const char*	str)
					/* out: length of the prefix,
					in bytes */
	const dtype_t*	dtype,		/* in: data type */
	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 */
{
#ifndef UNIV_HOTBACKUP
	ut_a(data_len != UNIV_SQL_NULL);
	ut_a(!(prefix_len % dtype->mbmaxlen));

	if (dtype_str_needs_mysql_cmp(dtype)) {
	if (dtype->mbminlen != dtype->mbmaxlen) {
		return(innobase_get_at_most_n_mbchars(
				dtype_get_charset_coll(dtype->prtype),
				prefix_len, data_len, str));
	}

	/* We assume here that the string types that InnoDB itself can compare
	are single-byte charsets! */

	if (prefix_len < data_len) {

		return(prefix_len);
+11 −0
Original line number Diff line number Diff line
@@ -220,6 +220,8 @@ dict_build_table_def_step(
	const char*	path_or_name;
	ibool		is_path;
	mtr_t		mtr;
	ulint		i;
	ulint		row_len;

#ifdef UNIV_SYNC_DEBUG
	ut_ad(mutex_own(&(dict_sys->mutex)));
@@ -231,6 +233,15 @@ dict_build_table_def_step(

	thr_get_trx(thr)->table_id = table->id;

	row_len = 0;
	for (i = 0; i < table->n_def; i++) {
		row_len += dtype_get_min_size(dict_col_get_type(
						&table->cols[i]));
	}
	if (row_len > BTR_PAGE_MAX_REC_SIZE) {
		return(DB_TOO_BIG_RECORD);
	}

	if (table->type == DICT_TABLE_CLUSTER_MEMBER) {

		cluster_table = dict_table_get_low(table->cluster_name);
+20 −18
Original line number Diff line number Diff line
@@ -145,28 +145,22 @@ store the charset-collation number; one byte is left unused, though */
#define DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE	6

/*************************************************************************
Checks if a string type has to be compared by the MySQL comparison functions.
InnoDB internally only handles binary byte string comparisons, as well as
latin1_swedish_ci strings. For example, UTF-8 strings have to be compared
by MySQL. */

ibool
dtype_str_needs_mysql_cmp(
/*======================*/
				/* out: TRUE if a string type that requires
				comparison with MySQL functions */
	dtype_t*	dtype);	/* in: type struct */
/*************************************************************************
For the documentation of this function, see innobase_get_at_most_n_mbchars()
in ha_innodb.cc. */
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. */

ulint
dtype_get_at_most_n_mbchars(
/*========================*/
	dtype_t*	dtype,
	ulint		prefix_len,
	ulint		data_len,
	const char*	str);
					/* out: length of the prefix,
					in bytes */
	const dtype_t*	dtype,		/* in: data type */
	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 */
/*************************************************************************
Checks if a data main type is a string type. Also a BLOB is considered a
string type. */
@@ -306,6 +300,14 @@ dtype_get_fixed_size(
				/* out: fixed size, or 0 */
	dtype_t*	type);	/* in: type */
/***************************************************************************
Returns the minimum size of a data type. */
UNIV_INLINE
ulint
dtype_get_min_size(
/*===============*/
				/* out: minimum size */
	const dtype_t*	type);	/* in: type */
/***************************************************************************
Returns a stored SQL NULL size for a type. For fixed length types it is
the fixed length of the type, otherwise 0. */
UNIV_INLINE
+58 −7
Original line number Diff line number Diff line
@@ -314,6 +314,7 @@ dtype_new_read_for_order_and_null_size(
	dtype_set_mblen(type);
}

#ifndef UNIV_HOTBACKUP
/***************************************************************************
Returns the size of a fixed size data type, 0 if not a fixed size type. */
UNIV_INLINE
@@ -323,7 +324,6 @@ dtype_get_fixed_size(
				/* out: fixed size, or 0 */
	dtype_t*	type)	/* in: type */
{
#ifndef UNIV_HOTBACKUP
	ulint	mtype;

	mtype = dtype_get_mtype(type);
@@ -401,14 +401,65 @@ dtype_get_fixed_size(
	}

	return(0);
#else /* UNIV_HOTBACKUP */
	/* This function depends on MySQL code that is not included in
	InnoDB Hot Backup builds.  Besides, this function should never
	be called in InnoDB Hot Backup. */
	ut_error;
#endif /* UNIV_HOTBACKUP */
}

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

	return(0);
}
#endif /* !UNIV_HOTBACKUP */

/***************************************************************************
Returns a stored SQL NULL size for a type. For fixed length types it is
the fixed length of the type, otherwise 0. */
+1 −0
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ row_mysql_store_col_in_innobase_format(
					as dfield is used! */
	ulint		col_len,	/* in: MySQL column length */
	ulint		type,		/* in: data type */
	bool		comp,		/* in: TRUE=compact format */
	ulint		is_unsigned);	/* in: != 0 if unsigned integer type */
/********************************************************************
Handles user errors and lock waits detected by the database engine. */
Loading