diff options
Diffstat (limited to 'sql/ha_innodb.cc')
-rw-r--r-- | sql/ha_innodb.cc | 462 |
1 files changed, 363 insertions, 99 deletions
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 222e2b4ce8a..efd74a543c2 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -20,10 +20,9 @@ NOTE: You can only use noninlined InnoDB functions in this file, because we have disables the InnoDB inlining in this file. */ /* TODO list for the InnoDB handler in 4.1: - - Check if the query_id is now right also in prepared and executed stats - in build_template() - - Add multi-language char set support to CREATE TABLE and the comparison - of strings + - Remove the flag innodb_active_trans from thd and replace it with a + function call innodb_active_trans(thd), which looks at the InnoDB + trx struct state field - Find out what kind of problems the OS X case-insensitivity causes to table and database names; should we 'normalize' the names like we do in Windows? @@ -41,6 +40,7 @@ have disables the InnoDB inlining in this file. */ #include <hash.h> #include <myisampack.h> #include <mysys_err.h> +#include <my_sys.h> #define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1)) @@ -117,6 +117,9 @@ uint innobase_flush_log_at_trx_commit = 1; my_bool innobase_log_archive = FALSE;/* unused */ my_bool innobase_use_native_aio = FALSE; my_bool innobase_fast_shutdown = TRUE; +my_bool innobase_very_fast_shutdown = FALSE; /* this can be set to + 1 just prior calling + innobase_end() */ my_bool innobase_file_per_table = FALSE; my_bool innobase_locks_unsafe_for_binlog = FALSE; my_bool innobase_create_status_file = FALSE; @@ -299,7 +302,7 @@ convert_error_code_to_mysql( } else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) { - return(HA_ERR_CANNOT_ADD_FOREIGN); /* TODO: This is a bit + return(HA_ERR_ROW_IS_REFERENCED); /* TODO: This is a bit misleading, a new MySQL error code should be introduced */ } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) { @@ -426,6 +429,36 @@ innobase_mysql_print_thd( putc('\n', f); } +/********************************************************************** +Compares NUL-terminated UTF-8 strings case insensitively. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +int +innobase_strcasecmp( +/*================*/ + /* out: 0 if a=b, <0 if a<b, >1 if a>b */ + const char* a, /* in: first string to compare */ + const char* b) /* in: second string to compare */ +{ + return(my_strcasecmp(system_charset_info, a, b)); +} + +/********************************************************************** +Makes all characters in a NUL-terminated UTF-8 string lower case. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +void +innobase_casedn_str( +/*================*/ + char* a) /* in/out: string to put in lower case */ +{ + my_casedn_str(system_charset_info, a); +} + /************************************************************************* Creates a temporary file. */ extern "C" @@ -684,14 +717,7 @@ innobase_query_caching_of_table_permitted( separator between db and table */ norm_name[full_name_len] = '\0'; #ifdef __WIN__ - /* Put to lower case */ - - char* ptr = norm_name; - - while (*ptr != '\0') { - *ptr = tolower(*ptr); - ptr++; - } + innobase_casedn_str(norm_name); #endif /* The call of row_search_.. will start a new transaction if it is not yet started */ @@ -736,15 +762,35 @@ innobase_invalidate_query_cache( } /********************************************************************* -Get the quote character to be used in SQL identifiers. */ +Get the quote character to be used in SQL identifiers. +This definition must match the one in innobase/ut/ut0ut.c! */ extern "C" -char -mysql_get_identifier_quote_char(void) -/*=================================*/ +int +mysql_get_identifier_quote_char( +/*============================*/ /* out: quote character to be - used in SQL identifiers */ + used in SQL identifiers; EOF if none */ + trx_t* trx, /* in: transaction */ + const char* name, /* in: name to print */ + ulint namelen)/* in: length of name */ +{ + if (!trx || !trx->mysql_thd) { + return(EOF); + } + return(get_quote_char_for_identifier((THD*) trx->mysql_thd, + name, namelen)); +} + +/************************************************************************** +Obtain a pointer to the MySQL THD object, as in current_thd(). This +definition must match the one in sql/ha_innodb.cc! */ +extern "C" +void* +innobase_current_thd(void) +/*======================*/ + /* out: MySQL THD object */ { - return '`'; + return(current_thd); } /********************************************************************* @@ -782,6 +828,10 @@ ha_innobase::init_table_handle_for_HANDLER(void) trx_assign_read_view(prebuilt->trx); + /* Set the MySQL flag to mark that there is an active transaction */ + + current_thd->transaction.all.innodb_active_trans = 1; + /* We did the necessary inits in this function, no need to repeat them in row_search_for_mysql */ @@ -791,6 +841,7 @@ ha_innobase::init_table_handle_for_HANDLER(void) if the trx isolation level would have been specified as SERIALIZABLE */ prebuilt->select_lock_type = LOCK_NONE; + prebuilt->stored_select_lock_type = LOCK_NONE; /* Always fetch all columns in the index record */ @@ -829,7 +880,7 @@ innobase_init(void) Note that when using the embedded server, the datadirectory is not necessarily the current directory of this program. */ - if (mysql_embedded) { + if (mysqld_embedded) { default_path = mysql_real_data_home; fil_path_to_mysql_datadir = mysql_real_data_home; } else { @@ -967,7 +1018,7 @@ innobase_init(void) srv_max_n_open_files = (ulint) innobase_open_files; srv_innodb_status = (ibool) innobase_create_status_file; - srv_print_verbose_log = mysql_embedded ? 0 : 1; + srv_print_verbose_log = mysqld_embedded ? 0 : 1; /* Store the default charset-collation number of this MySQL installation */ @@ -1043,6 +1094,15 @@ innobase_end(void) #endif if (innodb_inited) { + if (innobase_very_fast_shutdown) { + srv_very_fast_shutdown = TRUE; + fprintf(stderr, +"InnoDB: MySQL has requested a very fast shutdown without flushing\n" +"InnoDB: the InnoDB buffer pool to data files. At the next mysqld startup\n" +"InnoDB: InnoDB will do a crash recovery!\n"); + + } + innodb_inited= 0; if (innobase_shutdown_for_mysql() != DB_SUCCESS) err= 1; @@ -1100,6 +1160,48 @@ innobase_commit_low( } /********************************************************************* +Creates an InnoDB transaction struct for the thd if it does not yet have one. +Starts a new InnoDB transaction if a transaction is not yet started. And +assigns a new snapshot for a consistent read if the transaction does not yet +have one. */ + +int +innobase_start_trx_and_assign_read_view( +/*====================================*/ + /* out: 0 */ + THD* thd) /* in: MySQL thread handle of the user for whom + the transaction should be committed */ +{ + trx_t* trx; + + DBUG_ENTER("innobase_start_trx_and_assign_read_view"); + + /* Create a new trx struct for thd, if it does not yet have one */ + + trx = check_trx_exists(thd); + + /* This is just to play safe: release a possible FIFO ticket and + search latch. Since we will reserve the kernel mutex, we have to + release the search system latch first to obey the latching order. */ + + innobase_release_stat_resources(trx); + + /* If the transaction is not started yet, start it */ + + trx_start_if_not_started_noninline(trx); + + /* Assign a read view if the transaction does not have it yet */ + + trx_assign_read_view(trx); + + /* Set the MySQL flag to mark that there is an active transaction */ + + current_thd->transaction.all.innodb_active_trans = 1; + + DBUG_RETURN(0); +} + +/********************************************************************* Commits a transaction in an InnoDB database or marks an SQL statement ended. */ @@ -1130,8 +1232,10 @@ innobase_commit( 1. ::external_lock(), 2. ::start_stmt(), - 3. innobase_query_caching_of_table_permitted(), and + 3. innobase_query_caching_of_table_permitted(), 4. innobase_savepoint(), + 5. ::init_table_handle_for_HANDLER(), + 6. innobase_start_trx_and_assign_read_view() and it is only set to 0 in a commit or a rollback. If it is 0 we know there cannot be resources to be freed and we could return immediately. @@ -1460,14 +1564,7 @@ normalize_table_name( norm_name[name_ptr - db_ptr - 1] = '/'; #ifdef __WIN__ - /* Put to lower case */ - - ptr = norm_name; - - while (*ptr != '\0') { - *ptr = tolower(*ptr); - ptr++; - } + innobase_casedn_str(norm_name); #endif } @@ -1485,12 +1582,14 @@ ha_innobase::open( { dict_table_t* ib_table; char norm_name[1000]; + THD* thd; DBUG_ENTER("ha_innobase::open"); UT_NOT_USED(mode); UT_NOT_USED(test_if_locked); + thd = current_thd; normalize_table_name(norm_name, name); user_thd = NULL; @@ -1540,7 +1639,7 @@ ha_innobase::open( DBUG_RETURN(1); } - if (ib_table->ibd_file_missing && !current_thd->tablespace_op) { + if (ib_table->ibd_file_missing && !thd->tablespace_op) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB error:\n" "MySQL is trying to open a table handle but the .ibd file for\n" @@ -2106,11 +2205,6 @@ build_template( templ = prebuilt->mysql_template + n_requested_fields; field = table->field[i]; - /* TODO: Check if the query_id is now right also in prepared - and executed SQL statements. Previously, MySQL-4.1 failed to - update field->query_id so that the formula - thd->query_id == field->query_id did not work. */ - ibool index_contains_field= dict_index_contains_col_or_prefix(index, i); @@ -2221,8 +2315,8 @@ ha_innobase::write_row( statistic_increment(current_thd->status_var.ha_write_count, &LOCK_status); - if (table->timestamp_default_now) - update_timestamp(record + table->timestamp_default_now - 1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); if (last_query_id != user_thd->query_id) { prebuilt->sql_stat_start = TRUE; @@ -2504,8 +2598,8 @@ ha_innobase::update_row( ut_ad(prebuilt->trx == (trx_t*) current_thd->transaction.all.innobase_tid); - if (table->timestamp_on_update_now) - update_timestamp(new_row + table->timestamp_on_update_now - 1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); if (last_query_id != user_thd->query_id) { prebuilt->sql_stat_start = TRUE; @@ -2778,7 +2872,7 @@ ha_innobase::index_read( (ulint)upd_and_key_val_buff_len, index, (byte*) key_ptr, - (ulint) key_len); + (ulint) key_len, prebuilt->trx); } else { /* We position the cursor to the last or the first entry in the index */ @@ -3068,7 +3162,7 @@ ha_innobase::index_last( { int error; - DBUG_ENTER("index_first"); + DBUG_ENTER("index_last"); statistic_increment(current_thd->status_var.ha_read_last_count, &LOCK_status); @@ -3261,7 +3355,15 @@ create_table_def( trx_t* trx, /* in: InnoDB transaction handle */ TABLE* form, /* in: information on table columns and indexes */ - const char* table_name) /* in: table name */ + const char* table_name, /* in: table name */ + const char* path_of_temp_table)/* in: if this is a table explicitly + created by the user with the + TEMPORARY keyword, then this + parameter is the dir path where the + table should be placed if we create + an .ibd file for it (no .ibd extension + in the path, though); otherwise this + is NULL */ { Field* field; dict_table_t* table; @@ -3284,6 +3386,11 @@ create_table_def( table = dict_mem_table_create((char*) table_name, 0, n_cols); + if (path_of_temp_table) { + table->dir_path_of_temp_table = + mem_heap_strdup(table->heap, path_of_temp_table); + } + for (i = 0; i < n_cols; i++) { field = form->field[i]; @@ -3364,8 +3471,7 @@ create_index( ind_type = 0; - if (key_num == form->primary_key) - { + if (key_num == form->primary_key) { ind_type = ind_type | DICT_CLUSTERED; } @@ -3392,9 +3498,9 @@ create_index( field = form->field[j]; - if (0 == ut_cmp_in_lower_case( - (char*)field->field_name, - (char*)key_part->field->field_name)) { + if (0 == innobase_strcasecmp( + field->field_name, + key_part->field->field_name)) { /* Found the corresponding column */ break; @@ -3425,10 +3531,6 @@ create_index( prefix_len = 0; } - if (prefix_len >= DICT_MAX_COL_PREFIX_LEN) { - DBUG_RETURN(-1); - } - /* We assume all fields should be sorted in ascending order, hence the '0': */ @@ -3534,7 +3636,7 @@ ha_innobase::create( srv_lower_case_table_names = FALSE; } - fn_format(name2, name, "", "",2); // Remove the .frm extension + fn_format(name2, name, "", "", 2); // Remove the .frm extension normalize_table_name(norm_name, name2); @@ -3546,8 +3648,13 @@ ha_innobase::create( /* Create the table definition in InnoDB */ - error = create_table_def(trx, form, norm_name); - + if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { + + error = create_table_def(trx, form, norm_name, name2); + } else { + error = create_table_def(trx, form, norm_name, NULL); + } + if (error) { innobase_commit_low(trx); @@ -3622,8 +3729,8 @@ ha_innobase::create( } if (current_thd->query != NULL) { - LEX_STRING q; + if (thd->convert_string(&q, system_charset_info, current_thd->query, current_thd->query_length, @@ -3835,7 +3942,7 @@ innobase_drop_database( namebuf[len] = '/'; namebuf[len + 1] = '\0'; #ifdef __WIN__ - my_casedn_str(system_charset_info, namebuf); + innobase_casedn_str(namebuf); #endif trx = trx_allocate_for_mysql(); trx->mysql_thd = current_thd; @@ -3997,14 +4104,16 @@ ha_innobase::records_in_range( index, (byte*) (min_key ? min_key->key : (const mysql_byte*) 0), - (ulint) (min_key ? min_key->length : 0)); + (ulint) (min_key ? min_key->length : 0), + prebuilt->trx); row_sel_convert_mysql_key_to_innobase( range_end, (byte*) key_val_buff2, buff2_len, index, (byte*) (max_key ? max_key->key : (const mysql_byte*) 0), - (ulint) (max_key ? max_key->length : 0)); + (ulint) (max_key ? max_key->length : 0), + prebuilt->trx); mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag : HA_READ_KEY_EXACT); @@ -4038,7 +4147,7 @@ Gives an UPPER BOUND to the number of rows in a table. This is used in filesort.cc. */ ha_rows -ha_innobase::estimate_number_of_rows(void) +ha_innobase::estimate_rows_upper_bound(void) /*======================================*/ /* out: upper bound of rows */ { @@ -4047,7 +4156,7 @@ ha_innobase::estimate_number_of_rows(void) ulonglong estimate; ulonglong local_data_file_length; - DBUG_ENTER("estimate_number_of_rows"); + DBUG_ENTER("estimate_rows_upper_bound"); /* We do not know if MySQL can call this function before calling external_lock(). To be safe, update the thd of the current table @@ -4127,7 +4236,7 @@ ha_innobase::read_time( time_for_scan = scan_time(); - if ((total_rows = estimate_number_of_rows()) < rows) + if ((total_rows = estimate_rows_upper_bound()) < rows) return time_for_scan; return (ranges + (double) rows / (double) total_rows * time_for_scan); @@ -4148,6 +4257,8 @@ ha_innobase::info( ha_rows rec_per_key; ulong j; ulong i; + char path[FN_REFLEN]; + os_file_stat_t stat_info; DBUG_ENTER("info"); @@ -4157,7 +4268,7 @@ ha_innobase::info( if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { - return; + DBUG_VOID_RETURN; } /* We do not know if MySQL can call this function before calling @@ -4185,6 +4296,26 @@ ha_innobase::info( prebuilt->trx->op_info = (char*) "returning various info to MySQL"; + + if (ib_table->space != 0) { + my_snprintf(path, sizeof(path), "%s/%s%s", + mysql_data_home, ib_table->name, + ".ibd"); + unpack_filename(path,path); + } else { + my_snprintf(path, sizeof(path), "%s/%s%s", + mysql_data_home, ib_table->name, + reg_ext); + + unpack_filename(path,path); + } + + /* Note that we do not know the access time of the table, + nor the CHECK TABLE time, nor the UPDATE or INSERT time. */ + + if (os_file_get_status(path,&stat_info)) { + create_time = stat_info.ctime; + } } if (flag & HA_STATUS_VARIABLE) { @@ -4391,7 +4522,8 @@ ha_innobase::update_table_comment( (ulong) fsp_get_available_space_in_free_extents( prebuilt->table->space)); - dict_print_info_on_foreign_keys(FALSE, file, prebuilt->table); + dict_print_info_on_foreign_keys(FALSE, file, + prebuilt->trx, prebuilt->table); flen = ftell(file); if(length + flen + 3 > 64000) { flen = 64000 - 3 - length; @@ -4457,7 +4589,8 @@ ha_innobase::get_foreign_key_create_info(void) trx_search_latch_release_if_reserved(prebuilt->trx); /* output the data to a temporary file */ - dict_print_info_on_foreign_keys(TRUE, file, prebuilt->table); + dict_print_info_on_foreign_keys(TRUE, file, + prebuilt->trx, prebuilt->table); prebuilt->trx->op_info = (char*)""; flen = ftell(file); @@ -4632,23 +4765,41 @@ ha_innobase::start_stmt( prepared for an update of a row */ prebuilt->select_lock_type = LOCK_X; - } else { - if (thd->lex->sql_command == SQLCOM_SELECT - && thd->lex->lock_option == TL_READ) { - - /* For other than temporary tables, we obtain - no lock for consistent read (plain SELECT) */ - - prebuilt->select_lock_type = LOCK_NONE; - } else { - /* Not a consistent read: use LOCK_X as the - select_lock_type value (TODO: how could we know - whether it should be LOCK_S, LOCK_X, or LOCK_NONE?) */ - - prebuilt->select_lock_type = LOCK_X; - } - } + } else { + if (trx->isolation_level != TRX_ISO_SERIALIZABLE + && thd->lex->sql_command == SQLCOM_SELECT + && thd->lex->lock_option == TL_READ) { + /* For other than temporary tables, we obtain + no lock for consistent read (plain SELECT). */ + + prebuilt->select_lock_type = LOCK_NONE; + } else { + /* Not a consistent read: restore the + select_lock_type value. The value of + stored_select_lock_type was decided in: + 1) ::store_lock(), + 2) ::external_lock(), and + 3) ::init_table_handle_for_HANDLER(). */ + + prebuilt->select_lock_type = + prebuilt->stored_select_lock_type; + } + + if (prebuilt->stored_select_lock_type != LOCK_S + && prebuilt->stored_select_lock_type != LOCK_X) { + fprintf(stderr, +"InnoDB: Error: stored_select_lock_type is %lu inside ::start_stmt()!\n", + prebuilt->stored_select_lock_type); + + /* Set the value to LOCK_X: this is just fault + tolerance, we do not know what the correct value + should be! */ + + prebuilt->select_lock_type = LOCK_X; + } + } + /* Set the MySQL flag to mark that there is an active transaction */ thd->transaction.all.innodb_active_trans = 1; @@ -4723,6 +4874,7 @@ ha_innobase::external_lock( /* If this is a SELECT, then it is in UPDATE TABLE ... or SELECT ... FOR UPDATE */ prebuilt->select_lock_type = LOCK_X; + prebuilt->stored_select_lock_type = LOCK_X; } if (lock_type != F_UNLCK) { @@ -4758,7 +4910,8 @@ ha_innobase::external_lock( } if (prebuilt->select_lock_type != LOCK_NONE) { - if (thd->in_lock_tables) { + if (thd->in_lock_tables && + thd->variables.innodb_table_locks) { ulint error; error = row_lock_table_for_mysql(prebuilt); @@ -4975,14 +5128,22 @@ ha_innobase::store_lock( { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - if (lock_type == TL_READ_WITH_SHARED_LOCKS || + if ((lock_type == TL_READ && thd->in_lock_tables) || + (lock_type == TL_READ_HIGH_PRIORITY && thd->in_lock_tables) || + lock_type == TL_READ_WITH_SHARED_LOCKS || lock_type == TL_READ_NO_INSERT) { - /* This is a SELECT ... IN SHARE MODE, or - we are doing a complex SQL statement like + /* The OR cases above are in this order: + 1) MySQL is doing LOCK TABLES ... READ LOCAL, or + 2) (we do not know when TL_READ_HIGH_PRIORITY is used), or + 3) this is a SELECT ... IN SHARE MODE, or + 4) we are doing a complex SQL statement like INSERT INTO ... SELECT ... and the logical logging (MySQL - binlog) requires the use of a locking read */ + binlog) requires the use of a locking read, or + MySQL is doing LOCK TABLES ... READ. */ prebuilt->select_lock_type = LOCK_S; + prebuilt->stored_select_lock_type = LOCK_S; + } else if (lock_type != TL_IGNORE) { /* In ha_berkeley.cc there is a comment that MySQL @@ -4993,6 +5154,7 @@ ha_innobase::store_lock( here even if this would be SELECT ... FOR UPDATE */ prebuilt->select_lock_type = LOCK_NONE; + prebuilt->stored_select_lock_type = LOCK_NONE; } if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) { @@ -5155,14 +5317,13 @@ ha_innobase::get_auto_increment() } /*********************************************************************** -This function stores binlog offset and flushes logs */ +This function stores the binlog offset and flushes logs. */ void innobase_store_binlog_offset_and_flush_log( -/*=============================*/ +/*=======================================*/ char *binlog_name, /* in: binlog name */ - longlong offset /* in: binlog offset */ -) + longlong offset) /* in: binlog offset */ { mtr_t mtr; @@ -5172,7 +5333,7 @@ innobase_store_binlog_offset_and_flush_log( mtr_start_noninline(&mtr); /* Update the latest MySQL binlog name and offset info - in trx sys header */ + in trx sys header */ trx_sys_update_mysql_binlog_offset( binlog_name, @@ -5235,18 +5396,121 @@ ha_innobase::cmp_ref( return 0; } -char *ha_innobase::get_mysql_bin_log_name() +char* +ha_innobase::get_mysql_bin_log_name() { - return trx_sys_mysql_bin_log_name; + return(trx_sys_mysql_bin_log_name); } -ulonglong ha_innobase::get_mysql_bin_log_pos() +ulonglong +ha_innobase::get_mysql_bin_log_pos() { - /* - trx... is ib_longlong, which is a typedef for a 64-bit integer (__int64 or - longlong) so it's ok to cast it to ulonglong. - */ - return trx_sys_mysql_bin_log_pos; + /* trx... is ib_longlong, which is a typedef for a 64-bit integer + (__int64 or longlong) so it's ok to cast it to ulonglong. */ + + return(trx_sys_mysql_bin_log_pos); +} + +extern "C" { +/********************************************************************** +This function is used to find the storage length in bytes of the first n +characters for prefix indexes using a multibyte character set. The function +finds charset information and returns length of prefix_len characters in the +index field in bytes. + +NOTE: the prototype of this function is copied to data0type.c! If you change +this function, you MUST change also data0type.c! */ + +ulint +innobase_get_at_most_n_mbchars( +/*===========================*/ + /* out: number of bytes occupied by the first + n characters */ + ulint charset_id, /* in: character set id */ + ulint prefix_len, /* in: prefix length in bytes of the index + (this has to be divided by mbmaxlen to get the + number of CHARACTERS n in the prefix) */ + ulint data_len, /* in: length of the string in bytes */ + const char* str) /* in: character string */ +{ + ulint char_length; /* character length in bytes */ + ulint n_chars; /* number of characters in prefix */ + CHARSET_INFO* charset; /* charset used in the field */ + + charset = get_charset(charset_id, MYF(MY_WME)); + + ut_ad(charset); + ut_ad(charset->mbmaxlen); + + /* Calculate how many characters at most the prefix index contains */ + + n_chars = prefix_len / charset->mbmaxlen; + + /* If the charset is multi-byte, then we must find the length of the + first at most n chars in the string. If the string contains less + characters than n, then we return the length to the end of the last + character. */ + + if (charset->mbmaxlen > 1) { + /* my_charpos() returns the byte length of the first n_chars + characters, or a value bigger than the length of str, if + there were not enough full characters in str. + + Why does the code below work: + Suppose that we are looking for n UTF-8 characters. + + 1) If the string is long enough, then the prefix contains at + least n complete UTF-8 characters + maybe some extra + characters + an incomplete UTF-8 character. No problem in + this case. The function returns the pointer to the + end of the nth character. + + 2) If the string is not long enough, then the string contains + the complete value of a column, that is, only complete UTF-8 + characters, and we can store in the column prefix index the + whole string. */ + + char_length = my_charpos(charset, str, + str + data_len, n_chars); + if (char_length > data_len) { + char_length = data_len; + } + } else { + if (data_len < prefix_len) { + char_length = data_len; + } else { + char_length = prefix_len; + } + } + + return(char_length); +} +} + +extern "C" { +/********************************************************************** +This function returns true if SQL-query in the current thread +is either REPLACE or LOAD DATA INFILE REPLACE. +NOTE that /mysql/innobase/row/row0ins.c must contain the +prototype for this function ! */ + +ibool +innobase_query_is_replace(void) +/*===========================*/ +{ + THD* thd; + + thd = (THD *)innobase_current_thd(); + + if ( thd->lex->sql_command == SQLCOM_REPLACE || + thd->lex->sql_command == SQLCOM_REPLACE_SELECT || + ( thd->lex->sql_command == SQLCOM_LOAD && + thd->lex->duplicates == DUP_REPLACE )) { + return true; + } else { + return false; + } +} } #endif /* HAVE_INNOBASE_DB */ |