📄 row0mysql.c
字号:
/******************************************************Interface between Innobase row operations and MySQL.Contains also create table and other data dictionary operations.(c) 2000 Innobase OyCreated 9/17/2000 Heikki Tuuri*******************************************************/#include "row0mysql.h"#ifdef UNIV_NONINL#include "row0mysql.ic"#endif#include "row0ins.h"#include "row0sel.h"#include "row0upd.h"#include "row0row.h"#include "que0que.h"#include "pars0pars.h"#include "dict0dict.h"#include "dict0crea.h"#include "dict0load.h"#include "dict0boot.h"#include "trx0roll.h"#include "trx0purge.h"#include "lock0lock.h"#include "rem0cmp.h"#include "log0log.h"#include "btr0sea.h"#include "fil0fil.h"#include "ibuf0ibuf.h"/* A dummy variable used to fool the compiler */ibool row_mysql_identically_false = FALSE;/* List of tables we should drop in background. ALTER TABLE in MySQL requiresthat the table handler can drop the table in background when there are noqueries to it any more. Protected by the kernel mutex. */typedef struct row_mysql_drop_struct row_mysql_drop_t;struct row_mysql_drop_struct{ char* table_name; UT_LIST_NODE_T(row_mysql_drop_t) row_mysql_drop_list;};UT_LIST_BASE_NODE_T(row_mysql_drop_t) row_mysql_drop_list;ibool row_mysql_drop_list_inited = FALSE;/* Magic table names for invoking various monitor threads */static const char S_innodb_monitor[] = "innodb_monitor";static const char S_innodb_lock_monitor[] = "innodb_lock_monitor";static const char S_innodb_tablespace_monitor[] = "innodb_tablespace_monitor";static const char S_innodb_table_monitor[] = "innodb_table_monitor";static const char S_innodb_mem_validate[] = "innodb_mem_validate";/* Name suffix for recovered orphaned temporary tables */static const char S_recover_innodb_tmp_table[] = "_recover_innodb_tmp_table";/***********************************************************************Determine if the given name ends in the suffix reserved for recoveredorphaned temporary tables. */staticiboolrow_mysql_is_recovered_tmp_table(/*=============================*/ /* out: TRUE if table name ends in the reserved suffix */ const char* name){ ulint namelen = strlen(name) + 1; return(namelen >= sizeof S_recover_innodb_tmp_table && !memcmp(name + namelen - sizeof S_recover_innodb_tmp_table, S_recover_innodb_tmp_table, sizeof S_recover_innodb_tmp_table));}/***********************************************************************Determine if the given name is a name reserved for MySQL system tables. */staticiboolrow_mysql_is_system_table(/*======================*/ /* out: TRUE if name is a MySQL system table name */ const char* name){ if (memcmp(name, "mysql/", 6)) { return(FALSE); } return(0 == strcmp(name + 6, "host") || 0 == strcmp(name + 6, "user") || 0 == strcmp(name + 6, "db"));}/***********************************************************************Delays an INSERT, DELETE or UPDATE operation if the purge is lagging. */staticvoidrow_mysql_delay_if_needed(void)/*===========================*/{ if (srv_dml_needed_delay) { os_thread_sleep(srv_dml_needed_delay); }}/***********************************************************************Frees the blob heap in prebuilt when no longer needed. */voidrow_mysql_prebuilt_free_blob_heap(/*==============================*/ row_prebuilt_t* prebuilt) /* in: prebuilt struct of a ha_innobase:: table handle */{ mem_heap_free(prebuilt->blob_heap); prebuilt->blob_heap = NULL;}/***********************************************************************Stores a >= 5.0.3 format true VARCHAR length to dest, in the MySQL rowformat. */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, andreturns 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. */voidrow_mysql_store_blob_ref(/*=====================*/ byte* dest, /* in: where to store */ ulint col_len, /* in: dest buffer size: determines into how many bytes the BLOB length is stored, the space for the length may vary from 1 to 4 bytes */ byte* data, /* in: BLOB data; if the value to store is SQL NULL this should be NULL pointer */ ulint len) /* in: BLOB length; if the value to store is SQL NULL this should be 0; remember also to set the NULL bit in the MySQL record header! */{ /* MySQL might assume the field is set to zero except the length and the pointer fields */ memset(dest, '\0', col_len); /* In dest there are 1 - 4 bytes reserved for the BLOB length, and after that 8 bytes reserved for the pointer to the data. In 32-bit architectures we only use the first 4 bytes of the pointer slot. */ ut_a(col_len - 8 > 1 || len < 256); ut_a(col_len - 8 > 2 || len < 256 * 256); ut_a(col_len - 8 > 3 || len < 256 * 256 * 256); mach_write_to_n_little_endian(dest, col_len - 8, len); ut_memcpy(dest + col_len - 8, (byte*)&data, sizeof(byte*)); }/***********************************************************************Reads a reference to a BLOB in the MySQL format. */byte*row_mysql_read_blob_ref(/*====================*/ /* out: pointer to BLOB data */ ulint* len, /* out: BLOB length */ byte* ref, /* in: BLOB reference in the MySQL format */ ulint col_len) /* in: BLOB reference length (not BLOB length) */{ byte* data; *len = mach_read_from_n_little_endian(ref, col_len - 8); ut_memcpy((byte*)&data, ref + col_len - 8, sizeof(byte*)); return(data);}/******************************************************************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() inrow0sel.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 */ ulint comp) /* in: nonzero=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 thatthe function to convert a MySQL format key value to an InnoDB dtuple isrow_sel_convert_mysql_key_to_innobase() in row0sel.c. */staticvoidrow_mysql_convert_row_to_innobase(/*==============================*/ dtuple_t* row, /* in/out: Innobase row where the field type information is already copied there! */ row_prebuilt_t* prebuilt, /* in: prebuilt struct where template must be of type ROW_MYSQL_WHOLE_ROW */ byte* mysql_rec) /* in: row in the MySQL format; NOTE: do not discard as long as row is used, as row may contain pointers to this record! */{ mysql_row_templ_t* templ; dfield_t* dfield; ulint i; ut_ad(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); ut_ad(prebuilt->mysql_template); for (i = 0; i < prebuilt->n_template; i++) { templ = prebuilt->mysql_template + i; dfield = dtuple_get_nth_field(row, i); if (templ->mysql_null_bit_mask != 0) { /* Column may be SQL NULL */ if (mysql_rec[templ->mysql_null_byte_offset] & (byte) (templ->mysql_null_bit_mask)) { /* It is SQL NULL */ dfield_set_data(dfield, NULL, UNIV_SQL_NULL); goto next_column; } } row_mysql_store_col_in_innobase_format(dfield, prebuilt->ins_upd_rec_buff + templ->mysql_col_offset, TRUE, /* MySQL row format data */ mysql_rec + templ->mysql_col_offset, templ->mysql_col_len, prebuilt->table->comp);next_column: ; } }/********************************************************************Handles user errors and lock waits detected by the database engine. */iboolrow_mysql_handle_errors(/*====================*/ /* out: TRUE if it was a lock wait and we should continue running the query thread */ ulint* new_err,/* out: possible new error encountered in lock wait, or if no new error, the value of trx->error_state at the entry of this function */ trx_t* trx, /* in: transaction */ que_thr_t* thr, /* in: query thread */ trx_savept_t* savept) /* in: savepoint or NULL */{#ifndef UNIV_HOTBACKUP ulint err;handle_new_error: err = trx->error_state; ut_a(err != DB_SUCCESS); trx->error_state = DB_SUCCESS; if (err == DB_DUPLICATE_KEY) { if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ trx_general_rollback_for_mysql(trx, TRUE, savept); } } else if (err == DB_TOO_BIG_RECORD) { if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ trx_general_rollback_for_mysql(trx, TRUE, savept); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -