Many files:

  InnoDB true VARCHAR
parent cb4553b7
...@@ -24,7 +24,11 @@ extern dtype_t* dtype_binary; ...@@ -24,7 +24,11 @@ extern dtype_t* dtype_binary;
/*-------------------------------------------*/ /*-------------------------------------------*/
/* The 'MAIN TYPE' of a column */ /* The 'MAIN TYPE' of a column */
#define DATA_VARCHAR 1 /* character varying of the #define DATA_VARCHAR 1 /* character varying of the
latin1_swedish_ci charset-collation */ 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 */
#define DATA_CHAR 2 /* fixed length character of the #define DATA_CHAR 2 /* fixed length character of the
latin1_swedish_ci charset-collation */ latin1_swedish_ci charset-collation */
#define DATA_FIXBINARY 3 /* binary string of fixed length */ #define DATA_FIXBINARY 3 /* binary string of fixed length */
...@@ -102,6 +106,8 @@ columns, and for them the precise type is usually not used at all. ...@@ -102,6 +106,8 @@ columns, and for them the precise type is usually not used at all.
#define DATA_MYSQL_TYPE_MASK 255 /* AND with this mask to extract the MySQL #define DATA_MYSQL_TYPE_MASK 255 /* AND with this mask to extract the MySQL
type from the precise type */ type from the precise type */
#define DATA_MYSQL_TRUE_VARCHAR 15 /* MySQL type code for the >= 5.0.3
format true VARCHAR */
/* Precise data types for system columns and the length of those columns; /* 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 NOTE: the values must run from 0 up in the order given! All codes must
...@@ -134,6 +140,10 @@ be less than 256 */ ...@@ -134,6 +140,10 @@ be less than 256 */
In earlier versions this was set for some In earlier versions this was set for some
BLOB columns. BLOB columns.
*/ */
#define DATA_LONG_TRUE_VARCHAR 4096 /* 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 */
/*-------------------------------------------*/ /*-------------------------------------------*/
/* This many bytes we need to store the type information affecting the /* This many bytes we need to store the type information affecting the
...@@ -144,6 +154,15 @@ SQL null*/ ...@@ -144,6 +154,15 @@ SQL null*/
store the charset-collation number; one byte is left unused, though */ store the charset-collation number; one byte is left unused, though */
#define DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE 6 #define DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE 6
/*************************************************************************
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 */
/************************************************************************* /*************************************************************************
Determine how many bytes the first n characters of the given string occupy. 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 If the string is shorter than n characters, returns the number of bytes
......
...@@ -32,6 +32,19 @@ dtype_get_charset_coll( ...@@ -32,6 +32,19 @@ dtype_get_charset_coll(
return((prtype >> 16) & 0xFFUL); 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);
}
/************************************************************************* /*************************************************************************
Sets the mbminlen and mbmaxlen members of a data type structure. */ Sets the mbminlen and mbmaxlen members of a data type structure. */
UNIV_INLINE UNIV_INLINE
......
...@@ -359,7 +359,8 @@ struct que_thr_struct{ ...@@ -359,7 +359,8 @@ struct que_thr_struct{
the control came */ the control came */
ulint resource; /* resource usage of the query thread ulint resource; /* resource usage of the query thread
thus far */ thus far */
ulint lock_state; /* lock state of thread (table or row) */ ulint lock_state; /* lock state of thread (table or
row) */
}; };
#define QUE_THR_MAGIC_N 8476583 #define QUE_THR_MAGIC_N 8476583
......
...@@ -21,36 +21,6 @@ Created 9/17/2000 Heikki Tuuri ...@@ -21,36 +21,6 @@ Created 9/17/2000 Heikki Tuuri
typedef struct row_prebuilt_struct row_prebuilt_t; typedef struct row_prebuilt_struct row_prebuilt_t;
/***********************************************************************
Stores a variable-length field (like VARCHAR) length to dest, in the
MySQL format. */
UNIV_INLINE
byte*
row_mysql_store_var_len(
/*====================*/
/* out: dest + 2 */
byte* dest, /* in: where to store */
ulint len); /* in: length, must fit in two bytes */
/***********************************************************************
Reads a MySQL format variable-length field (like VARCHAR) length and
returns pointer to the field data. */
UNIV_INLINE
byte*
row_mysql_read_var_ref(
/*===================*/
/* out: field + 2 */
ulint* len, /* out: variable-length field length */
byte* field); /* in: field */
/***********************************************************************
Reads a MySQL format variable-length field (like VARCHAR) length and
returns pointer to the field data. */
byte*
row_mysql_read_var_ref_noninline(
/*=============================*/
/* out: field + 2 */
ulint* len, /* out: variable-length field length */
byte* field); /* in: field */
/*********************************************************************** /***********************************************************************
Frees the blob heap in prebuilt when no longer needed. */ Frees the blob heap in prebuilt when no longer needed. */
...@@ -60,6 +30,30 @@ row_mysql_prebuilt_free_blob_heap( ...@@ -60,6 +30,30 @@ row_mysql_prebuilt_free_blob_heap(
row_prebuilt_t* prebuilt); /* in: prebuilt struct of a row_prebuilt_t* prebuilt); /* in: prebuilt struct of a
ha_innobase:: table handle */ ha_innobase:: table handle */
/*********************************************************************** /***********************************************************************
Stores a >= 5.0.3 format true VARCHAR length to dest, in the MySQL row
format. */
byte*
row_mysql_store_true_var_len(
/*=========================*/
/* out: pointer to the data, we skip the 1 or 2 bytes
at the start that are used to store the len */
byte* dest, /* in: where to store */
ulint len, /* in: length, must fit in two bytes */
ulint lenlen);/* in: storage length of len: either 1 or 2 bytes */
/***********************************************************************
Reads a >= 5.0.3 format true VARCHAR length, in the MySQL row format, and
returns a pointer to the data. */
byte*
row_mysql_read_true_varchar(
/*========================*/
/* out: pointer to the data, we skip the 1 or 2 bytes
at the start that are used to store the len */
ulint* len, /* out: variable-length field length */
byte* field, /* in: field in the MySQL format */
ulint lenlen);/* in: storage length of len: either 1 or 2 bytes */
/***********************************************************************
Stores a reference to a BLOB in the MySQL format. */ Stores a reference to a BLOB in the MySQL format. */
void void
...@@ -83,24 +77,40 @@ row_mysql_read_blob_ref( ...@@ -83,24 +77,40 @@ row_mysql_read_blob_ref(
ulint col_len); /* in: BLOB reference length (not BLOB ulint col_len); /* in: BLOB reference length (not BLOB
length) */ length) */
/****************************************************************** /******************************************************************
Stores a non-SQL-NULL field given in the MySQL format in the Innobase Stores a non-SQL-NULL field given in the MySQL format in the InnoDB format.
format. */ The counterpart of this function is row_sel_field_store_in_mysql_format() in
UNIV_INLINE row0sel.c. */
void
byte*
row_mysql_store_col_in_innobase_format( row_mysql_store_col_in_innobase_format(
/*===================================*/ /*===================================*/
dfield_t* dfield, /* in/out: dfield */ /* out: up to which byte we used
byte* buf, /* in/out: buffer for the converted buf in the conversion */
value */ dfield_t* dfield, /* in/out: dfield where dtype
information must be already set when
this function is called! */
byte* buf, /* in/out: buffer for a converted
integer value; this must be at least
col_len long then! */
ibool row_format_col, /* TRUE if the mysql_data is from
a MySQL row, FALSE if from a MySQL
key value;
in MySQL, a true VARCHAR storage
format differs in a row and in a
key value: in a key value the length
is always stored in 2 bytes! */
byte* mysql_data, /* in: MySQL column value, not byte* mysql_data, /* in: MySQL column value, not
SQL NULL; NOTE that dfield may also SQL NULL; NOTE that dfield may also
get a pointer to mysql_data, get a pointer to mysql_data,
therefore do not discard this as long therefore do not discard this as long
as dfield is used! */ as dfield is used! */
ulint col_len, /* in: MySQL column length */ ulint col_len, /* in: MySQL column length; NOTE that
ulint type, /* in: data type */ this is the storage length of the
bool comp, /* in: TRUE=compact format */ column in the MySQL format row, not
ulint is_unsigned); /* in: != 0 if unsigned integer type */ necessarily the length of the actual
payload data; if the column is a true
VARCHAR then this is irrelevant */
ibool comp); /* in: TRUE = compact format */
/******************************************************************** /********************************************************************
Handles user errors and lock waits detected by the database engine. */ Handles user errors and lock waits detected by the database engine. */
...@@ -457,6 +467,16 @@ struct mysql_row_templ_struct { ...@@ -457,6 +467,16 @@ struct mysql_row_templ_struct {
zero if column cannot be NULL */ zero if column cannot be NULL */
ulint type; /* column type in Innobase mtype ulint type; /* column type in Innobase mtype
numbers DATA_CHAR... */ numbers DATA_CHAR... */
ulint mysql_type; /* MySQL type code; this is always
< 256 */
ulint mysql_length_bytes; /* if mysql_type
== DATA_MYSQL_TRUE_VARCHAR, this tells
whether we should use 1 or 2 bytes to
store the MySQL true VARCHAR data
length at the start of row in the MySQL
format (NOTE that the MySQL key value
format always uses 2 bytes for the data
len) */
ulint charset; /* MySQL charset-collation code ulint charset; /* MySQL charset-collation code
of the column, or zero */ of the column, or zero */
ulint mbminlen; /* minimum length of a char, in bytes, ulint mbminlen; /* minimum length of a char, in bytes,
......
...@@ -5,149 +5,3 @@ MySQL interface for Innobase ...@@ -5,149 +5,3 @@ MySQL interface for Innobase
Created 1/23/2001 Heikki Tuuri Created 1/23/2001 Heikki Tuuri
*******************************************************/ *******************************************************/
/***********************************************************************
Stores a variable-length field (like VARCHAR) length to dest, in the
MySQL format. No real var implemented in MySQL yet! */
UNIV_INLINE
byte*
row_mysql_store_var_len(
/*====================*/
/* out: dest + 2 */
byte* dest, /* in: where to store */
ulint len __attribute__((unused))) /* in: length, must fit in two
bytes */
{
ut_ad(len < 256 * 256);
/*
mach_write_to_2_little_endian(dest, len);
return(dest + 2);
*/
return(dest); /* No real var implemented in MySQL yet! */
}
/***********************************************************************
Reads a MySQL format variable-length field (like VARCHAR) length and
returns pointer to the field data. No real var implemented in MySQL yet! */
UNIV_INLINE
byte*
row_mysql_read_var_ref(
/*===================*/
/* out: field + 2 */
ulint* len, /* out: variable-length field length; does not work
yet! */
byte* field) /* in: field */
{
/*
*len = mach_read_from_2_little_endian(field);
return(field + 2);
*/
UT_NOT_USED(len);
return(field); /* No real var implemented in MySQL yet! */
}
/******************************************************************
Stores a non-SQL-NULL field given in the MySQL format in the Innobase
format. */
UNIV_INLINE
void
row_mysql_store_col_in_innobase_format(
/*===================================*/
dfield_t* dfield, /* in/out: dfield */
byte* buf, /* in/out: buffer for the converted
value; this must be at least col_len
long! */
byte* mysql_data, /* in: MySQL column value, not
SQL NULL; NOTE that dfield may also
get a pointer to mysql_data,
therefore do not discard this as long
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 */
{
byte* ptr = mysql_data;
if (type == DATA_INT) {
/* Store integer data in Innobase in a big-endian format,
sign bit negated */
ptr = buf + col_len;
for (;;) {
ptr--;
*ptr = *mysql_data;
if (ptr == buf) {
break;
}
mysql_data++;
}
if (!is_unsigned) {
*ptr = (byte) (*ptr ^ 128);
}
} else if (type == DATA_VARCHAR || type == DATA_VARMYSQL
|| type == DATA_BINARY) {
/* Remove trailing spaces. */
/* Handle UCS2 strings differently. */
ulint mbminlen = dtype_get_mbminlen(
dfield_get_type(dfield));
ptr = row_mysql_read_var_ref(&col_len, mysql_data);
if (mbminlen == 2) {
/* space=0x0020 */
/* Trim "half-chars", just in case. */
col_len &= ~1;
while (col_len >= 2 && ptr[col_len - 2] == 0x00
&& ptr[col_len - 1] == 0x20) {
col_len -= 2;
}
} else {
ut_a(mbminlen == 1);
/* space=0x20 */
while (col_len > 0 && ptr[col_len - 1] == 0x20) {
col_len--;
}
}
} else if (comp && type == DATA_MYSQL
&& dtype_get_mbminlen(dfield_get_type(dfield)) == 1
&& dtype_get_mbmaxlen(dfield_get_type(dfield)) > 1) {
/* We assume that this CHAR field is encoded in a
variable-length character set where spaces have
1:1 correspondence to 0x20 bytes, such as UTF-8.
Consider a CHAR(n) field, a field of n characters.
It will contain between n*mbminlen and n*mbmaxlen bytes.
We will try to truncate it to n bytes by stripping
space padding. If the field contains single-byte
characters only, it will be truncated to n characters.
Consider a CHAR(5) field containing the string ".a "
where "." denotes a 3-byte character represented by
the bytes "$%&". After our stripping, the string will
be stored as "$%&a " (5 bytes). The string ".abc "
will be stored as "$%&abc" (6 bytes).
The space padding will be restored in row0sel.c, function
row_sel_field_store_in_mysql_format(). */
ulint n_chars;
dtype_t* dtype = dfield_get_type(dfield);
ut_a(!(dtype_get_len(dtype) % dtype_get_mbmaxlen(dtype)));
n_chars = dtype_get_len(dtype) / dtype_get_mbmaxlen(dtype);
/* Strip space padding. */
while (col_len > n_chars && ptr[col_len - 1] == 0x20) {
col_len--;
}
} else if (type == DATA_BLOB) {
ptr = row_mysql_read_blob_ref(&col_len, mysql_data, col_len);
}
dfield_set_data(dfield, ptr, col_len);
}
...@@ -521,6 +521,10 @@ row_ins_cascade_calc_update_vec( ...@@ -521,6 +521,10 @@ row_ins_cascade_calc_update_vec(
fixed_size = dtype_get_fixed_size(type); fixed_size = dtype_get_fixed_size(type);
/* TODO: pad in UCS-2 with 0x0020.
TODO: How does the special truncation of
UTF-8 CHAR cols affect this? */
if (fixed_size if (fixed_size
&& ufield->new_val.len != UNIV_SQL_NULL && ufield->new_val.len != UNIV_SQL_NULL
&& ufield->new_val.len < fixed_size) { && ufield->new_val.len < fixed_size) {
......
...@@ -105,20 +105,6 @@ row_mysql_delay_if_needed(void) ...@@ -105,20 +105,6 @@ row_mysql_delay_if_needed(void)
} }
} }
/***********************************************************************
Reads a MySQL format variable-length field (like VARCHAR) length and
returns pointer to the field data. */
byte*
row_mysql_read_var_ref_noninline(
/*=============================*/
/* out: field + 2 */
ulint* len, /* out: variable-length field length */
byte* field) /* in: field */
{
return(row_mysql_read_var_ref(len, field));
}
/*********************************************************************** /***********************************************************************
Frees the blob heap in prebuilt when no longer needed. */ Frees the blob heap in prebuilt when no longer needed. */
...@@ -132,6 +118,61 @@ row_mysql_prebuilt_free_blob_heap( ...@@ -132,6 +118,61 @@ row_mysql_prebuilt_free_blob_heap(
prebuilt->blob_heap = NULL; prebuilt->blob_heap = NULL;
} }
/***********************************************************************
Stores a >= 5.0.3 format true VARCHAR length to dest, in the MySQL row
format. */
byte*
row_mysql_store_true_var_len(
/*=========================*/
/* out: pointer to the data, we skip the 1 or 2 bytes
at the start that are used to store the len */
byte* dest, /* in: where to store */
ulint len, /* in: length, must fit in two bytes */
ulint lenlen) /* in: storage length of len: either 1 or 2 bytes */
{
if (lenlen == 2) {
ut_a(len < 256 * 256);
mach_write_to_2_little_endian(dest, len);
return(dest + 2);
}
ut_a(lenlen == 1);
ut_a(len < 256);
mach_write_to_1(dest, len);
return(dest + 1);
}
/***********************************************************************
Reads a >= 5.0.3 format true VARCHAR length, in the MySQL row format, and
returns a pointer to the data. */
byte*
row_mysql_read_true_varchar(
/*========================*/
/* out: pointer to the data, we skip the 1 or 2 bytes
at the start that are used to store the len */
ulint* len, /* out: variable-length field length */
byte* field, /* in: field in the MySQL format */
ulint lenlen) /* in: storage length of len: either 1 or 2 bytes */
{
if (lenlen == 2) {
*len = mach_read_from_2_little_endian(field);
return(field + 2);
}
ut_a(lenlen == 1);
*len = mach_read_from_1(field);
return(field + 1);
}
/*********************************************************************** /***********************************************************************
Stores a reference to a BLOB in the MySQL format. */ Stores a reference to a BLOB in the MySQL format. */
...@@ -191,15 +232,177 @@ row_mysql_read_blob_ref( ...@@ -191,15 +232,177 @@ row_mysql_read_blob_ref(
} }
/****************************************************************** /******************************************************************
Convert a row in the MySQL format to a row in the Innobase format. */ Stores a non-SQL-NULL field given in the MySQL format in the InnoDB format.
The counterpart of this function is row_sel_field_store_in_mysql_format() in
row0sel.c. */
byte*
row_mysql_store_col_in_innobase_format(
/*===================================*/
/* out: up to which byte we used
buf in the conversion */
dfield_t* dfield, /* in/out: dfield where dtype
information must be already set when
this function is called! */
byte* buf, /* in/out: buffer for a converted
integer value; this must be at least
col_len long then! */
ibool row_format_col, /* TRUE if the mysql_data is from
a MySQL row, FALSE if from a MySQL
key value;
in MySQL, a true VARCHAR storage
format differs in a row and in a
key value: in a key value the length
is always stored in 2 bytes! */
byte* mysql_data, /* in: MySQL column value, not
SQL NULL; NOTE that dfield may also
get a pointer to mysql_data,
therefore do not discard this as long
as dfield is used! */
ulint col_len, /* in: MySQL column length; NOTE that
this is the storage length of the
column in the MySQL format row, not
necessarily the length of the actual
payload data; if the column is a true
VARCHAR then this is irrelevant */
ibool comp) /* in: TRUE = compact format */
{
byte* ptr = mysql_data;
dtype_t* dtype;
ulint type;
ulint lenlen;
dtype = dfield_get_type(dfield);
type = dtype->mtype;
if (type == DATA_INT) {
/* Store integer data in Innobase in a big-endian format,
sign bit negated if the data is a signed integer. In MySQL,
integers are stored in a little-endian format. */
ptr = buf + col_len;
for (;;) {
ptr--;
*ptr = *mysql_data;
if (ptr == buf) {
break;
}
mysql_data++;
}
if (!(dtype->prtype & DATA_UNSIGNED)) {
*ptr = (byte) (*ptr ^ 128);
}
buf += col_len;
} else if ((type == DATA_VARCHAR
|| type == DATA_VARMYSQL
|| type == DATA_BINARY)) {
if (dtype_get_mysql_type(dtype) == DATA_MYSQL_TRUE_VARCHAR) {
/* The length of the actual data is stored to 1 or 2
bytes at the start of the field */
if (row_format_col) {
if (dtype->prtype & DATA_LONG_TRUE_VARCHAR) {
lenlen = 2;
} else {
lenlen = 1;
}
} else {
/* In a MySQL key value, lenlen is always 2 */
lenlen = 2;
}
ptr = row_mysql_read_true_varchar(&col_len, mysql_data,
lenlen);
} else {
/* Remove trailing spaces from old style VARCHAR
columns. */
/* Handle UCS2 strings differently. */
ulint mbminlen = dtype_get_mbminlen(dtype);
ptr = mysql_data;
if (mbminlen == 2) {
/* space=0x0020 */
/* Trim "half-chars", just in case. */
col_len &= ~1;
while (col_len >= 2 && ptr[col_len - 2] == 0x00
&& ptr[col_len - 1] == 0x20) {
col_len -= 2;
}
} else {
ut_a(mbminlen == 1);
/* space=0x20 */
while (col_len > 0
&& ptr[col_len - 1] == 0x20) {
col_len--;
}
}
}
} else if (comp && type == DATA_MYSQL
&& dtype_get_mbminlen(dtype) == 1
&& dtype_get_mbmaxlen(dtype) > 1) {
/* In some cases we strip trailing spaces from UTF-8 and other
multibyte charsets, from FIXED-length CHAR columns, to save
space. UTF-8 would otherwise normally use 3 * the string length
bytes to store a latin1 string! */
/* We assume that this CHAR field is encoded in a
variable-length character set where spaces have
1:1 correspondence to 0x20 bytes, such as UTF-8.
Consider a CHAR(n) field, a field of n characters.
It will contain between n * mbminlen and n * mbmaxlen bytes.
We will try to truncate it to n bytes by stripping
space padding. If the field contains single-byte
characters only, it will be truncated to n characters.
Consider a CHAR(5) field containing the string ".a "
where "." denotes a 3-byte character represented by
the bytes "$%&". After our stripping, the string will
be stored as "$%&a " (5 bytes). The string ".abc "
will be stored as "$%&abc" (6 bytes).
The space padding will be restored in row0sel.c, function
row_sel_field_store_in_mysql_format(). */
ulint n_chars;
ut_a(!(dtype_get_len(dtype) % dtype_get_mbmaxlen(dtype)));
n_chars = dtype_get_len(dtype) / dtype_get_mbmaxlen(dtype);
/* Strip space padding. */
while (col_len > n_chars && ptr[col_len - 1] == 0x20) {
col_len--;
}
} else if (type == DATA_BLOB && row_format_col) {
ptr = row_mysql_read_blob_ref(&col_len, mysql_data, col_len);
}
dfield_set_data(dfield, ptr, col_len);
return(buf);
}
/******************************************************************
Convert a row in the MySQL format to a row in the Innobase format. Note that
the function to convert a MySQL format key value to an InnoDB dtuple is
row_sel_convert_mysql_key_to_innobase() in row0sel.c. */
static static
void void
row_mysql_convert_row_to_innobase( row_mysql_convert_row_to_innobase(
/*==============================*/ /*==============================*/
dtuple_t* row, /* in/out: Innobase row where the dtuple_t* row, /* in/out: Innobase row where the
field type information is already field type information is already
copied there, or will be copied copied there! */
later */
row_prebuilt_t* prebuilt, /* in: prebuilt struct where template row_prebuilt_t* prebuilt, /* in: prebuilt struct where template
must be of type ROW_MYSQL_WHOLE_ROW */ must be of type ROW_MYSQL_WHOLE_ROW */
byte* mysql_rec) /* in: row in the MySQL format; byte* mysql_rec) /* in: row in the MySQL format;
...@@ -236,10 +439,10 @@ row_mysql_convert_row_to_innobase( ...@@ -236,10 +439,10 @@ row_mysql_convert_row_to_innobase(
row_mysql_store_col_in_innobase_format(dfield, row_mysql_store_col_in_innobase_format(dfield,
prebuilt->ins_upd_rec_buff prebuilt->ins_upd_rec_buff
+ templ->mysql_col_offset, + templ->mysql_col_offset,
TRUE, /* MySQL row format data */
mysql_rec + templ->mysql_col_offset, mysql_rec + templ->mysql_col_offset,
templ->mysql_col_len, templ->mysql_col_len,
templ->type, prebuilt->table->comp, prebuilt->table->comp);
templ->is_unsigned);
next_column: next_column:
; ;
} }
...@@ -594,7 +797,8 @@ static ...@@ -594,7 +797,8 @@ static
dtuple_t* dtuple_t*
row_get_prebuilt_insert_row( row_get_prebuilt_insert_row(
/*========================*/ /*========================*/
/* out: prebuilt dtuple */ /* out: prebuilt dtuple; the column
type information is also set in it */
row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
handle */ handle */
{ {
...@@ -784,6 +988,7 @@ row_unlock_tables_for_mysql( ...@@ -784,6 +988,7 @@ row_unlock_tables_for_mysql(
lock_release_tables_off_kernel(trx); lock_release_tables_off_kernel(trx);
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
} }
/************************************************************************* /*************************************************************************
Sets a table lock on the table mentioned in prebuilt. */ Sets a table lock on the table mentioned in prebuilt. */
...@@ -962,10 +1167,13 @@ row_insert_for_mysql( ...@@ -962,10 +1167,13 @@ row_insert_for_mysql(
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
que_thr_stop_for_mysql(thr); que_thr_stop_for_mysql(thr);
thr->lock_state= QUE_THR_LOCK_ROW;
/* TODO: what is this? */ thr->lock_state= QUE_THR_LOCK_ROW;
was_lock_wait = row_mysql_handle_errors(&err, trx, thr, was_lock_wait = row_mysql_handle_errors(&err, trx, thr,
&savept); &savept);
thr->lock_state= QUE_THR_LOCK_NOLOCK; thr->lock_state= QUE_THR_LOCK_NOLOCK;
if (was_lock_wait) { if (was_lock_wait) {
goto run_again; goto run_again;
} }
......
...@@ -2119,10 +2119,10 @@ row_sel_convert_mysql_key_to_innobase( ...@@ -2119,10 +2119,10 @@ row_sel_convert_mysql_key_to_innobase(
+ 256 * key_ptr[data_offset + 1]; + 256 * key_ptr[data_offset + 1];
data_field_len = data_offset + 2 + field->prefix_len; data_field_len = data_offset + 2 + field->prefix_len;
data_offset += 2; data_offset += 2;
type = DATA_CHAR; /* now that we know the length, we /* now that we know the length, we store the column
store the column value like it would value like it would be a fixed char field */
be a fixed char field */
} else if (field->prefix_len > 0) { } else if (field->prefix_len > 0) {
/* Looks like MySQL pads unused end bytes in the /* Looks like MySQL pads unused end bytes in the
prefix with space. Therefore, also in UTF-8, it is ok prefix with space. Therefore, also in UTF-8, it is ok
...@@ -2146,11 +2146,12 @@ row_sel_convert_mysql_key_to_innobase( ...@@ -2146,11 +2146,12 @@ row_sel_convert_mysql_key_to_innobase(
if (!is_null) { if (!is_null) {
row_mysql_store_col_in_innobase_format( row_mysql_store_col_in_innobase_format(
dfield, buf, key_ptr + data_offset, dfield,
data_len, type, buf,
index->table->comp, FALSE, /* MySQL key value format col */
dfield_get_type(dfield)->prtype key_ptr + data_offset,
& DATA_UNSIGNED); data_len,
index->table->comp);
buf += data_len; buf += data_len;
} }
...@@ -2225,7 +2226,7 @@ row_sel_store_row_id_to_prebuilt( ...@@ -2225,7 +2226,7 @@ row_sel_store_row_id_to_prebuilt(
dict_index_name_print(stderr, prebuilt->trx, index); dict_index_name_print(stderr, prebuilt->trx, index);
fprintf(stderr, "\n" fprintf(stderr, "\n"
"InnoDB: Field number %lu, record:\n", "InnoDB: Field number %lu, record:\n",
(ulong) dict_index_get_sys_col_pos(index, DATA_ROW_ID)); (ulong) dict_index_get_sys_col_pos(index, DATA_ROW_ID));
rec_print_new(stderr, index_rec, offsets); rec_print_new(stderr, index_rec, offsets);
putc('\n', stderr); putc('\n', stderr);
ut_error; ut_error;
...@@ -2235,8 +2236,9 @@ row_sel_store_row_id_to_prebuilt( ...@@ -2235,8 +2236,9 @@ row_sel_store_row_id_to_prebuilt(
} }
/****************************************************************** /******************************************************************
Stores a non-SQL-NULL field in the MySQL format. */ Stores a non-SQL-NULL field in the MySQL format. The counterpart of this
UNIV_INLINE function is row_mysql_store_col_in_innobase_format() in row0mysql.c. */
static
void void
row_sel_field_store_in_mysql_format( row_sel_field_store_in_mysql_format(
/*================================*/ /*================================*/
...@@ -2251,6 +2253,8 @@ row_sel_field_store_in_mysql_format( ...@@ -2251,6 +2253,8 @@ row_sel_field_store_in_mysql_format(
ulint len) /* in: length of the data */ ulint len) /* in: length of the data */
{ {
byte* ptr; byte* ptr;
byte* field_end;
byte* pad_ptr;
ut_ad(len != UNIV_SQL_NULL); ut_ad(len != UNIV_SQL_NULL);
...@@ -2274,25 +2278,66 @@ row_sel_field_store_in_mysql_format( ...@@ -2274,25 +2278,66 @@ row_sel_field_store_in_mysql_format(
} }
ut_ad(templ->mysql_col_len == len); ut_ad(templ->mysql_col_len == len);
} else if (templ->type == DATA_VARCHAR || templ->type == DATA_VARMYSQL } else if (templ->type == DATA_VARCHAR
|| templ->type == DATA_BINARY) { || templ->type == DATA_VARMYSQL
/* Store the length of the data to the first two bytes of || templ->type == DATA_BINARY) {
dest; does not do anything yet because MySQL has
no real vars! */ field_end = dest + templ->mysql_col_len;
if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
/* This is a >= 5.0.3 type true VARCHAR. Store the
length of the data to the first byte or the first
two bytes of dest. */
dest = row_mysql_store_var_len(dest, len); dest = row_mysql_store_true_var_len(dest, len,
templ->mysql_length_bytes);
}
/* Copy the actual data */
ut_memcpy(dest, data, len); ut_memcpy(dest, data, len);
#if 0
/* No real var implemented in MySQL yet! */
ut_ad(templ->mysql_col_len >= len + 2);
#endif
/* Pad with trailing spaces. We pad with spaces also the
unused end of a >= 5.0.3 true VARCHAR column, just in case
MySQL expects its contents to be deterministic. */
pad_ptr = dest + len;
ut_ad(templ->mbminlen <= templ->mbmaxlen);
/* We handle UCS2 charset strings differently. */
if (templ->mbminlen == 2) {
/* A space char is two bytes, 0x0020 in UCS2 */
if (len & 1) {
/* A 0x20 has been stripped from the column.
Pad it back. */
if (pad_ptr < field_end) {
*pad_ptr = 0x20;
pad_ptr++;
}
}
/* Pad the rest of the string with 0x0020 */
while (pad_ptr < field_end) {
*pad_ptr = 0x00;
pad_ptr++;
*pad_ptr = 0x20;
pad_ptr++;
}
} else {
ut_ad(templ->mbminlen == 1);
/* space=0x20 */
memset(pad_ptr, 0x20, field_end - pad_ptr);
}
} else if (templ->type == DATA_BLOB) { } else if (templ->type == DATA_BLOB) {
/* Store a pointer to the BLOB buffer to dest: the BLOB was /* Store a pointer to the BLOB buffer to dest: the BLOB was
already copied to the buffer in row_sel_store_mysql_rec */ already copied to the buffer in row_sel_store_mysql_rec */
row_mysql_store_blob_ref(dest, templ->mysql_col_len, row_mysql_store_blob_ref(dest, templ->mysql_col_len, data,
data, len); len);
} else if (templ->type == DATA_MYSQL) { } else if (templ->type == DATA_MYSQL) {
memcpy(dest, data, len); memcpy(dest, data, len);
...@@ -2306,9 +2351,10 @@ row_sel_field_store_in_mysql_format( ...@@ -2306,9 +2351,10 @@ row_sel_field_store_in_mysql_format(
ut_a(len * templ->mbmaxlen >= templ->mysql_col_len); ut_a(len * templ->mbmaxlen >= templ->mysql_col_len);
if (templ->mbminlen != templ->mbmaxlen) { if (templ->mbminlen != templ->mbmaxlen) {
/* Pad with spaces. This undoes the stripping /* Pad with spaces. This undoes the stripping
done in row0mysql.ic, function done in row0mysql.ic, function
row_mysql_store_col_in_innobase_format(). */ row_mysql_store_col_in_innobase_format(). */
memset(dest + len, 0x20, templ->mysql_col_len - len); memset(dest + len, 0x20, templ->mysql_col_len - len);
} }
} else { } else {
...@@ -2320,6 +2366,7 @@ row_sel_field_store_in_mysql_format( ...@@ -2320,6 +2366,7 @@ row_sel_field_store_in_mysql_format(
|| templ->type == DATA_DOUBLE || templ->type == DATA_DOUBLE
|| templ->type == DATA_DECIMAL); || templ->type == DATA_DECIMAL);
ut_ad(templ->mysql_col_len == len); ut_ad(templ->mysql_col_len == len);
memcpy(dest, data, len); memcpy(dest, data, len);
} }
} }
...@@ -2436,40 +2483,6 @@ row_sel_store_mysql_rec( ...@@ -2436,40 +2483,6 @@ row_sel_store_mysql_rec(
mysql_rec + templ->mysql_col_offset, mysql_rec + templ->mysql_col_offset,
templ, data, len); templ, data, len);
if (templ->type == DATA_VARCHAR
|| templ->type == DATA_VARMYSQL
|| templ->type == DATA_BINARY) {
/* Pad with trailing spaces */
data = mysql_rec + templ->mysql_col_offset;
ut_ad(templ->mbminlen <= templ->mbmaxlen);
/* Handle UCS2 strings differently. */
if (templ->mbminlen == 2) {
/* space=0x0020 */
ulint col_len = templ->mysql_col_len;
ut_a(!(col_len & 1));
if (len & 1) {
/* A 0x20 has been stripped
from the column.
Pad it back. */
goto pad_0x20;
}
/* Pad the rest of the string
with 0x0020 */
while (len < col_len) {
data[len++] = 0x00;
pad_0x20:
data[len++] = 0x20;
}
} else {
ut_ad(templ->mbminlen == 1);
/* space=0x20 */
memset(data + len, 0x20,
templ->mysql_col_len - len);
}
}
/* Cleanup */ /* Cleanup */
if (extern_field_heap) { if (extern_field_heap) {
mem_heap_free(extern_field_heap); mem_heap_free(extern_field_heap);
......
...@@ -1958,7 +1958,7 @@ trx_recover_for_mysql( ...@@ -1958,7 +1958,7 @@ trx_recover_for_mysql(
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
" InnoDB: %d transactions in prepare state after recovery\n", " InnoDB: %d transactions in prepared state after recovery\n",
count); count);
return (count); return (count);
......
...@@ -1074,6 +1074,8 @@ innobase_init(void) ...@@ -1074,6 +1074,8 @@ innobase_init(void)
DBUG_ENTER("innobase_init"); DBUG_ENTER("innobase_init");
ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR);
os_innodb_umask = (ulint)my_umask; os_innodb_umask = (ulint)my_umask;
/* First calculate the default path for innodb_data_home_dir etc., /* First calculate the default path for innodb_data_home_dir etc.,
...@@ -2244,7 +2246,9 @@ innobase_mysql_cmp( ...@@ -2244,7 +2246,9 @@ innobase_mysql_cmp(
} }
/****************************************************************** /******************************************************************
Converts a MySQL type to an InnoDB type. */ Converts a MySQL type to an InnoDB type. Note that this function returns
the 'mtype' of InnoDB. InnoDB differentiates between MySQL's old <= 4.1
VARCHAR and the new true VARCHAR in >= 5.0.3 by the 'prtype'. */
inline inline
ulint ulint
get_innobase_type_from_mysql_type( get_innobase_type_from_mysql_type(
...@@ -2259,8 +2263,9 @@ get_innobase_type_from_mysql_type( ...@@ -2259,8 +2263,9 @@ get_innobase_type_from_mysql_type(
switch (field->type()) { switch (field->type()) {
/* NOTE that we only allow string types in DATA_MYSQL /* NOTE that we only allow string types in DATA_MYSQL
and DATA_VARMYSQL */ and DATA_VARMYSQL */
case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VAR_STRING: /* old <= 4.1 VARCHAR */
case MYSQL_TYPE_VARCHAR: if (field->binary()) { case MYSQL_TYPE_VARCHAR: /* new >= 5.0.3 true VARCHAR */
if (field->binary()) {
return(DATA_BINARY); return(DATA_BINARY);
} else if (strcmp( } else if (strcmp(
field->charset()->name, field->charset()->name,
...@@ -2313,6 +2318,35 @@ get_innobase_type_from_mysql_type( ...@@ -2313,6 +2318,35 @@ get_innobase_type_from_mysql_type(
return(0); return(0);
} }
/***********************************************************************
Writes an unsigned integer value < 64k to 2 bytes, in the little-endian
storage format. */
inline
void
innobase_write_to_2_little_endian(
/*==============================*/
byte* buf, /* in: where to store */
ulint val) /* in: value to write, must be < 64k */
{
ut_a(val < 256 * 256);
buf[0] = (byte)(val & 0xFF);
buf[1] = (byte)(val / 256);
}
/***********************************************************************
Reads an unsigned integer value < 64k from 2 bytes, in the little-endian
storage format. */
inline
uint
innobase_read_from_2_little_endian(
/*===============================*/
/* out: value */
const mysql_byte* buf) /* in: from where to read */
{
return((ulint)(buf[0]) + 256 * ((ulint)(buf[1])));
}
/*********************************************************************** /***********************************************************************
Stores a key value for a row to a buffer. */ Stores a key value for a row to a buffer. */
...@@ -2352,9 +2386,14 @@ ha_innobase::store_key_val_for_row( ...@@ -2352,9 +2386,14 @@ ha_innobase::store_key_val_for_row(
3. In a column prefix field, prefix_len next bytes are reserved for 3. In a column prefix field, prefix_len next bytes are reserved for
data. In a normal field the max field length next bytes are reserved data. In a normal field the max field length next bytes are reserved
for data. For a VARCHAR(n) the max field length is n. If the stored for data. For a VARCHAR(n) the max field length is n. If the stored
value is the SQL NULL then these data bytes are set to 0. */ value is the SQL NULL then these data bytes are set to 0.
/* We have to zero-fill the buffer so that MySQL is able to use a 4. We always use a 2 byte length for a true >= 5.0.3 VARCHAR. Note that
in the MySQL row format, the length is stored in 1 or 2 bytes,
depending on the maximum allowed length. But in the MySQL key value
format, the length always takes 2 bytes.
We have to zero-fill the buffer so that MySQL is able to use a
simple memcmp to compare two key values to determine if they are simple memcmp to compare two key values to determine if they are
equal. MySQL does this to compare contents of two 'ref' values. */ equal. MySQL does this to compare contents of two 'ref' values. */
...@@ -2377,7 +2416,43 @@ ha_innobase::store_key_val_for_row( ...@@ -2377,7 +2416,43 @@ ha_innobase::store_key_val_for_row(
field = key_part->field; field = key_part->field;
mysql_type = field->type(); mysql_type = field->type();
if (mysql_type == FIELD_TYPE_TINY_BLOB if (mysql_type == MYSQL_TYPE_VARCHAR) {
/* >= 5.0.3 true VARCHAR */
ulint lenlen;
ulint len;
byte* data;
if (is_null) {
buff += key_part->length + 2;
continue;
}
lenlen = (ulint)
(((Field_varstring*)field)->length_bytes);
data = row_mysql_read_true_varchar(&len,
(byte*) (record
+ (ulint)get_field_offset(table, field)),
lenlen);
/* The length in a key value is always stored in 2
bytes */
row_mysql_store_true_var_len((byte*)buff, len, 2);
buff += 2;
memcpy(buff, data, len);
/* Note that we always reserve the maximum possible
length of the true VARCHAR in the key value, though
only len first bytes after the 2 length bytes contain
actual data. The rest of the space was reset to zero
in the bzero() call above. */
buff += key_part->length;
} else if (mysql_type == FIELD_TYPE_TINY_BLOB
|| mysql_type == FIELD_TYPE_MEDIUM_BLOB || mysql_type == FIELD_TYPE_MEDIUM_BLOB
|| mysql_type == FIELD_TYPE_BLOB || mysql_type == FIELD_TYPE_BLOB
|| mysql_type == FIELD_TYPE_LONG_BLOB) { || mysql_type == FIELD_TYPE_LONG_BLOB) {
...@@ -2385,9 +2460,9 @@ ha_innobase::store_key_val_for_row( ...@@ -2385,9 +2460,9 @@ ha_innobase::store_key_val_for_row(
ut_a(key_part->key_part_flag & HA_PART_KEY_SEG); ut_a(key_part->key_part_flag & HA_PART_KEY_SEG);
if (is_null) { if (is_null) {
buff += key_part->length + 2; buff += key_part->length + 2;
continue; continue;
} }
blob_data = row_mysql_read_blob_ref(&blob_len, blob_data = row_mysql_read_blob_ref(&blob_len,
...@@ -2404,12 +2479,15 @@ ha_innobase::store_key_val_for_row( ...@@ -2404,12 +2479,15 @@ ha_innobase::store_key_val_for_row(
/* MySQL reserves 2 bytes for the length and the /* MySQL reserves 2 bytes for the length and the
storage of the number is little-endian */ storage of the number is little-endian */
ut_a(blob_len < 256); innobase_write_to_2_little_endian(
*((byte*)buff) = (byte)blob_len; (byte*)buff, (ulint)blob_len);
buff += 2; buff += 2;
memcpy(buff, blob_data, blob_len); memcpy(buff, blob_data, blob_len);
/* Note that we always reserve the maximum possible
length of the BLOB prefix in the key value. */
buff += key_part->length; buff += key_part->length;
} else { } else {
if (is_null) { if (is_null) {
...@@ -2573,6 +2651,13 @@ build_template( ...@@ -2573,6 +2651,13 @@ build_template(
templ->mysql_col_len = (ulint) field->pack_length(); templ->mysql_col_len = (ulint) field->pack_length();
templ->type = get_innobase_type_from_mysql_type(field); templ->type = get_innobase_type_from_mysql_type(field);
templ->mysql_type = (ulint)field->type();
if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
templ->mysql_length_bytes = (ulint)
(((Field_varstring*)field)->length_bytes);
}
templ->charset = dtype_get_charset_coll_noninline( templ->charset = dtype_get_charset_coll_noninline(
index->table->cols[i].type.prtype); index->table->cols[i].type.prtype);
templ->mbminlen = index->table->cols[i].type.mbminlen; templ->mbminlen = index->table->cols[i].type.mbminlen;
...@@ -2810,54 +2895,6 @@ ha_innobase::write_row( ...@@ -2810,54 +2895,6 @@ ha_innobase::write_row(
DBUG_RETURN(error); DBUG_RETURN(error);
} }
/******************************************************************
Converts field data for storage in an InnoDB update vector. */
inline
mysql_byte*
innobase_convert_and_store_changed_col(
/*===================================*/
/* out: pointer to the end of the converted
data in the buffer */
upd_field_t* ufield, /* in/out: field in the update vector */
mysql_byte* buf, /* in: buffer we can use in conversion */
mysql_byte* data, /* in: column data to store */
ulint len, /* in: data len */
ulint col_type,/* in: data type in InnoDB type numbers */
ulint is_unsigned)/* in: != 0 if an unsigned integer type */
{
uint i;
if (len == UNIV_SQL_NULL) {
data = NULL;
} else if (col_type == DATA_VARCHAR || col_type == DATA_BINARY
|| col_type == DATA_VARMYSQL) {
/* Remove trailing spaces */
while (len > 0 && data[len - 1] == ' ') {
len--;
}
} else if (col_type == DATA_INT) {
/* Store integer data in InnoDB in a big-endian
format, sign bit negated, if signed */
for (i = 0; i < len; i++) {
buf[len - 1 - i] = data[i];
}
if (!is_unsigned) {
buf[0] = buf[0] ^ 128;
}
data = buf;
buf += len;
}
ufield->new_val.data = data;
ufield->new_val.len = len;
return(buf);
}
/************************************************************************** /**************************************************************************
Checks which fields have changed in a row and stores information Checks which fields have changed in a row and stores information
of them to an update vector. */ of them to an update vector. */
...@@ -2878,9 +2915,11 @@ calc_row_difference( ...@@ -2878,9 +2915,11 @@ calc_row_difference(
{ {
mysql_byte* original_upd_buff = upd_buff; mysql_byte* original_upd_buff = upd_buff;
Field* field; Field* field;
enum_field_types field_mysql_type;
uint n_fields; uint n_fields;
ulint o_len; ulint o_len;
ulint n_len; ulint n_len;
ulint col_pack_len;
byte* o_ptr; byte* o_ptr;
byte* n_ptr; byte* n_ptr;
byte* buf; byte* buf;
...@@ -2888,6 +2927,7 @@ calc_row_difference( ...@@ -2888,6 +2927,7 @@ calc_row_difference(
ulint col_type; ulint col_type;
ulint is_unsigned; ulint is_unsigned;
ulint n_changed = 0; ulint n_changed = 0;
dfield_t dfield;
uint i; uint i;
n_fields = table->s->fields; n_fields = table->s->fields;
...@@ -2907,9 +2947,13 @@ calc_row_difference( ...@@ -2907,9 +2947,13 @@ calc_row_difference(
o_ptr = (byte*) old_row + get_field_offset(table, field); o_ptr = (byte*) old_row + get_field_offset(table, field);
n_ptr = (byte*) new_row + get_field_offset(table, field); n_ptr = (byte*) new_row + get_field_offset(table, field);
o_len = field->pack_length();
n_len = field->pack_length(); col_pack_len = field->pack_length();
o_len = col_pack_len;
n_len = col_pack_len;
field_mysql_type = field->type();
col_type = get_innobase_type_from_mysql_type(field); col_type = get_innobase_type_from_mysql_type(field);
is_unsigned = (ulint) (field->flags & UNSIGNED_FLAG); is_unsigned = (ulint) (field->flags & UNSIGNED_FLAG);
...@@ -2918,14 +2962,29 @@ calc_row_difference( ...@@ -2918,14 +2962,29 @@ calc_row_difference(
case DATA_BLOB: case DATA_BLOB:
o_ptr = row_mysql_read_blob_ref(&o_len, o_ptr, o_len); o_ptr = row_mysql_read_blob_ref(&o_len, o_ptr, o_len);
n_ptr = row_mysql_read_blob_ref(&n_len, n_ptr, n_len); n_ptr = row_mysql_read_blob_ref(&n_len, n_ptr, n_len);
break; break;
case DATA_VARCHAR: case DATA_VARCHAR:
case DATA_BINARY: case DATA_BINARY:
case DATA_VARMYSQL: case DATA_VARMYSQL:
o_ptr = row_mysql_read_var_ref_noninline(&o_len, if (field_mysql_type == MYSQL_TYPE_VARCHAR) {
o_ptr); /* This is a >= 5.0.3 type true VARCHAR where
n_ptr = row_mysql_read_var_ref_noninline(&n_len, the real payload data length is stored in
n_ptr); 1 or 2 bytes */
o_ptr = row_mysql_read_true_varchar(
&o_len, o_ptr,
(ulint)
(((Field_varstring*)field)->length_bytes));
n_ptr = row_mysql_read_true_varchar(
&n_len, n_ptr,
(ulint)
(((Field_varstring*)field)->length_bytes));
}
break;
default: default:
; ;
} }
...@@ -2947,12 +3006,29 @@ calc_row_difference( ...@@ -2947,12 +3006,29 @@ calc_row_difference(
/* The field has changed */ /* The field has changed */
ufield = uvect->fields + n_changed; ufield = uvect->fields + n_changed;
/* Let us use a dummy dfield to make the conversion
from the MySQL column format to the InnoDB format */
dfield.type = (prebuilt->table->cols + i)->type;
if (n_len != UNIV_SQL_NULL) {
buf = row_mysql_store_col_in_innobase_format(
&dfield,
(byte*)buf,
TRUE,
n_ptr,
col_pack_len,
prebuilt->table->comp);
ufield->new_val.data =
dfield_get_data(&dfield);
ufield->new_val.len =
dfield_get_len(&dfield);
} else {
ufield->new_val.data = NULL;
ufield->new_val.len = UNIV_SQL_NULL;
}
buf = (byte*)
innobase_convert_and_store_changed_col(ufield,
(mysql_byte*)buf,
(mysql_byte*)n_ptr, n_len, col_type,
is_unsigned);
ufield->exp = NULL; ufield->exp = NULL;
ufield->field_no = ufield->field_no =
(prebuilt->table->cols + i)->clust_pos; (prebuilt->table->cols + i)->clust_pos;
...@@ -3701,7 +3777,7 @@ ha_innobase::rnd_pos( ...@@ -3701,7 +3777,7 @@ ha_innobase::rnd_pos(
} }
if (error) { if (error) {
DBUG_PRINT("error",("Got error: %ld",error)); DBUG_PRINT("error", ("Got error: %ld", error));
DBUG_RETURN(error); DBUG_RETURN(error);
} }
...@@ -3709,10 +3785,11 @@ ha_innobase::rnd_pos( ...@@ -3709,10 +3785,11 @@ ha_innobase::rnd_pos(
for the table, and it is == ref_length */ for the table, and it is == ref_length */
error = index_read(buf, pos, ref_length, HA_READ_KEY_EXACT); error = index_read(buf, pos, ref_length, HA_READ_KEY_EXACT);
if (error)
{ if (error) {
DBUG_PRINT("error",("Got error: %ld",error)); DBUG_PRINT("error", ("Got error: %ld", error));
} }
change_active_index(keynr); change_active_index(keynr);
DBUG_RETURN(error); DBUG_RETURN(error);
...@@ -3752,12 +3829,11 @@ ha_innobase::position( ...@@ -3752,12 +3829,11 @@ ha_innobase::position(
ref_length, record); ref_length, record);
} }
/* Since we do not store len to the buffer 'ref', we must assume /* We assume that the 'ref' value len is always fixed for the same
that len is always fixed for this table. The following assertion table. */
checks this. */
if (len != ref_length) { if (len != ref_length) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: stored ref len is %lu, but table ref len is %lu\n", "InnoDB: Error: stored ref len is %lu, but table ref len is %lu\n",
(ulong)len, (ulong)ref_length); (ulong)len, (ulong)ref_length);
} }
...@@ -3788,9 +3864,11 @@ create_table_def( ...@@ -3788,9 +3864,11 @@ create_table_def(
ulint n_cols; ulint n_cols;
int error; int error;
ulint col_type; ulint col_type;
ulint col_len;
ulint nulls_allowed; ulint nulls_allowed;
ulint unsigned_type; ulint unsigned_type;
ulint binary_type; ulint binary_type;
ulint long_true_varchar;
ulint charset_no; ulint charset_no;
ulint i; ulint i;
...@@ -3837,17 +3915,40 @@ create_table_def( ...@@ -3837,17 +3915,40 @@ create_table_def(
charset_no = (ulint)field->charset()->number; charset_no = (ulint)field->charset()->number;
ut_a(charset_no < 256); /* in ut0type.h we assume that ut_a(charset_no < 256); /* in data0type.h we assume
the number fits in one byte */ that the number fits in one
byte */
}
ut_a(field->type() < 256); /* we assume in dtype_form_prtype()
that this fits in one byte */
col_len = field->pack_length();
/* The MySQL pack length contains 1 or 2 bytes length field
for a true VARCHAR. Let us subtract that, so that the InnoDB
column length in the InnoDB data dictionary is the real
maximum byte length of the actual data. */
long_true_varchar = 0;
if (field->type() == MYSQL_TYPE_VARCHAR) {
col_len -= ((Field_varstring*)field)->length_bytes;
if (((Field_varstring*)field)->length_bytes == 2) {
long_true_varchar = DATA_LONG_TRUE_VARCHAR;
}
} }
dict_mem_table_add_col(table, (char*) field->field_name, dict_mem_table_add_col(table,
col_type, dtype_form_prtype( (char*) field->field_name,
(ulint)field->type() col_type,
| nulls_allowed | unsigned_type dtype_form_prtype(
| binary_type, (ulint)field->type()
+ charset_no), | nulls_allowed | unsigned_type
field->pack_length(), 0); | binary_type | long_true_varchar,
charset_no),
col_len,
0);
} }
error = row_create_table_for_mysql(table, trx); error = row_create_table_for_mysql(table, trx);
...@@ -6125,54 +6226,79 @@ ha_innobase::get_auto_increment() ...@@ -6125,54 +6226,79 @@ ha_innobase::get_auto_increment()
return((ulonglong) nr); return((ulonglong) nr);
} }
/***********************************************************************
Compares two 'refs'. A 'ref' is the (internal) primary key value of the row.
If there is no explicitly declared non-null unique key or a primary key, then
InnoDB internally uses the row id as the primary key. */
int int
ha_innobase::cmp_ref( ha_innobase::cmp_ref(
const mysql_byte *ref1, /*=================*/
const mysql_byte *ref2) /* out: < 0 if ref1 < ref2, 0 if equal, else
> 0 */
const mysql_byte* ref1, /* in: an (internal) primary key value in the
MySQL key value format */
const mysql_byte* ref2) /* in: an (internal) primary key value in the
MySQL key value format */
{ {
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
enum_field_types mysql_type; enum_field_types mysql_type;
Field* field; Field* field;
int result; KEY_PART_INFO* key_part;
KEY_PART_INFO* key_part_end;
if (prebuilt->clust_index_was_generated) uint len1;
return memcmp(ref1, ref2, DATA_ROW_ID_LEN); uint len2;
int result;
/* Do type-aware comparison of Primary Key members. PK members
are always NOT NULL, so no checks for NULL are performed */ if (prebuilt->clust_index_was_generated) {
KEY_PART_INFO *key_part= /* The 'ref' is an InnoDB row id */
table->key_info[table->s->primary_key].key_part;
KEY_PART_INFO *key_part_end= return(memcmp(ref1, ref2, DATA_ROW_ID_LEN));
key_part + table->key_info[table->s->primary_key].key_parts; }
/* Do a type-aware comparison of primary key fields. PK fields
are always NOT NULL, so no checks for NULL are performed. */
key_part = table->key_info[table->s->primary_key].key_part;
key_part_end = key_part
+ table->key_info[table->s->primary_key].key_parts;
for (; key_part != key_part_end; ++key_part) { for (; key_part != key_part_end; ++key_part) {
field = key_part->field; field = key_part->field;
mysql_type = field->type(); mysql_type = field->type();
if (mysql_type == FIELD_TYPE_TINY_BLOB if (mysql_type == FIELD_TYPE_TINY_BLOB
|| mysql_type == FIELD_TYPE_MEDIUM_BLOB || mysql_type == FIELD_TYPE_MEDIUM_BLOB
|| mysql_type == FIELD_TYPE_BLOB || mysql_type == FIELD_TYPE_BLOB
|| mysql_type == FIELD_TYPE_LONG_BLOB) { || mysql_type == FIELD_TYPE_LONG_BLOB) {
ut_a(!ref1[1]); /* In the MySQL key value format, a column prefix of
ut_a(!ref2[1]); a BLOB is preceded by a 2-byte length field */
byte len1= *ref1;
byte len2= *ref2; len1 = innobase_read_from_2_little_endian(ref1);
len2 = innobase_read_from_2_little_endian(ref2);
ref1 += 2; ref1 += 2;
ref2 += 2; ref2 += 2;
result = result = ((Field_blob*)field)->cmp(
((Field_blob*)field)->cmp((const char*)ref1, len1, (const char*)ref1, len1,
(const char*)ref2, len2); (const char*)ref2, len2);
} else { } else {
result = result = field->cmp((const char*)ref1,
field->cmp((const char*)ref1, (const char*)ref2); (const char*)ref2);
}
if (result) {
return(result);
} }
if (result)
return result;
ref1 += key_part->length; ref1 += key_part->length;
ref2 += key_part->length; ref2 += key_part->length;
} }
return 0;
return(0);
} }
char* char*
......
/* Copyright (C) 2000 MySQL AB && Innobase Oy /* Copyright (C) 2000-2005 MySQL AB && Innobase Oy
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -40,9 +40,10 @@ my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, ...@@ -40,9 +40,10 @@ my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name,
/* The class defining a handle to an Innodb table */ /* The class defining a handle to an Innodb table */
class ha_innobase: public handler class ha_innobase: public handler
{ {
void* innobase_prebuilt; /* (row_prebuilt_t*) prebuilt void* innobase_prebuilt;/* (row_prebuilt_t*) prebuilt
struct in Innodb, used to save struct in InnoDB, used to save
CPU */ CPU time with prebuilt data
structures*/
THD* user_thd; /* the thread handle of the user THD* user_thd; /* the thread handle of the user
currently using the handle; this is currently using the handle; this is
set in external_lock function */ set in external_lock function */
...@@ -83,12 +84,12 @@ class ha_innobase: public handler ...@@ -83,12 +84,12 @@ class ha_innobase: public handler
public: public:
ha_innobase(TABLE *table): handler(table), ha_innobase(TABLE *table): handler(table),
int_table_flags(HA_REC_NOT_IN_SEQ | int_table_flags(HA_REC_NOT_IN_SEQ |
HA_NULL_IN_KEY | HA_FAST_KEY_READ | HA_NULL_IN_KEY |
HA_FAST_KEY_READ |
HA_CAN_INDEX_BLOBS | HA_CAN_INDEX_BLOBS |
HA_CAN_SQL_HANDLER | HA_CAN_SQL_HANDLER |
HA_NOT_EXACT_COUNT | HA_NOT_EXACT_COUNT |
HA_PRIMARY_KEY_IN_READ_INDEX | HA_PRIMARY_KEY_IN_READ_INDEX |
HA_NO_VARCHAR |
HA_TABLE_SCAN_ON_INDEX), HA_TABLE_SCAN_ON_INDEX),
last_dup_key((uint) -1), last_dup_key((uint) -1),
start_of_scan(0), start_of_scan(0),
...@@ -108,7 +109,10 @@ class ha_innobase: public handler ...@@ -108,7 +109,10 @@ class ha_innobase: public handler
ulong table_flags() const { return int_table_flags; } ulong table_flags() const { return int_table_flags; }
ulong index_flags(uint idx, uint part, bool all_parts) const ulong index_flags(uint idx, uint part, bool all_parts) const
{ {
return (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_READ_RANGE | return (HA_READ_NEXT |
HA_READ_PREV |
HA_READ_ORDER |
HA_READ_RANGE |
HA_KEYREAD_ONLY); HA_KEYREAD_ONLY);
} }
uint max_supported_keys() const { return MAX_KEY; } uint max_supported_keys() const { return MAX_KEY; }
...@@ -163,7 +167,8 @@ class ha_innobase: public handler ...@@ -163,7 +167,8 @@ class ha_innobase: public handler
int start_stmt(THD *thd); int start_stmt(THD *thd);
void position(byte *record); void position(byte *record);
ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); ha_rows records_in_range(uint inx, key_range *min_key, key_range
*max_key);
ha_rows estimate_rows_upper_bound(); ha_rows estimate_rows_upper_bound();
int create(const char *name, register TABLE *form, int create(const char *name, register TABLE *form,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment