diff options
Diffstat (limited to 'sql')
61 files changed, 2163 insertions, 1276 deletions
diff --git a/sql/convert.cc b/sql/convert.cc index 7a06208759c..75af13e0939 100644 --- a/sql/convert.cc +++ b/sql/convert.cc @@ -456,7 +456,7 @@ bool CONVERT::store(String *packet,const char *from,uint length) packet->realloc(packet_length+5+length)) return 1; char *to=(char*) net_store_length((char*) packet->ptr()+packet_length, - length); + (ulonglong)length); for (const char *end=from+length ; from != end ; from++) *to++= to_map[(uchar) *from]; diff --git a/sql/field.cc b/sql/field.cc index 2a0d0160d00..32679f549ba 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1713,6 +1713,11 @@ void Field_float::store(double nr) float j; if (dec < NOT_FIXED_DEC) nr=floor(nr*log_10[dec]+0.5)/log_10[dec]; // To fixed point + if (unsigned_flag && nr < 0) + { + current_thd->cuted_fields++; + nr=0; + } if (nr < -FLT_MAX) { j= -FLT_MAX; @@ -1739,6 +1744,11 @@ void Field_float::store(double nr) void Field_float::store(longlong nr) { float j= (float) nr; + if (unsigned_flag && j < 0) + { + current_thd->cuted_fields++; + j=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1945,6 +1955,11 @@ void Field_double::store(const char *from,uint len) double j= atof(tmp_str.c_ptr()); if (errno || current_thd->count_cuted_fields && !test_if_real(from,len)) current_thd->cuted_fields++; + if (unsigned_flag && j < 0) + { + current_thd->cuted_fields++; + j=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1960,6 +1975,11 @@ void Field_double::store(double nr) { if (dec < NOT_FIXED_DEC) nr=floor(nr*log_10[dec]+0.5)/log_10[dec]; // To fixed point + if (unsigned_flag && nr < 0) + { + current_thd->cuted_fields++; + nr=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1974,6 +1994,11 @@ void Field_double::store(double nr) void Field_double::store(longlong nr) { double j= (double) nr; + if (unsigned_flag && j < 0) + { + current_thd->cuted_fields++; + j=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { diff --git a/sql/filesort.cc b/sql/filesort.cc index a5f42d5731e..7e3d1c96f57 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -452,10 +452,7 @@ static void make_sortkey(register SORTPARAM *param, { if (field->is_null()) { - if (sort_field->reverse) - bfill(to,sort_field->length+1,(char) 255); - else - bzero((char*) to,sort_field->length+1); + bzero((char*) to,sort_field->length+1); to+= sort_field->length+1; continue; } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index f99e677913e..aa8ce0f3c18 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & InnoDB Oy +/* Copyright (C) 2000 MySQL AB & InnoDB Oy 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 @@ -76,25 +76,40 @@ bool innodb_skip = 0; uint innobase_init_flags = 0; ulong innobase_cache_size = 0; +/* The default values for the following, type long, start-up parameters +are declared in mysqld.cc: */ + long innobase_mirrored_log_groups, innobase_log_files_in_group, innobase_log_file_size, innobase_log_buffer_size, innobase_buffer_pool_size, innobase_additional_mem_pool_size, innobase_file_io_threads, innobase_lock_wait_timeout, - innobase_thread_concurrency, innobase_force_recovery; + innobase_thread_concurrency, innobase_force_recovery; + +/* The default values for the following char* start-up parameters +are determined in innobase_init below: */ + +char* innobase_data_home_dir = NULL; +char* innobase_log_group_home_dir = NULL; +char* innobase_log_arch_dir = NULL; +char* innobase_unix_file_flush_method = NULL; + +/* Below we have boolean-valued start-up parameters, and their default +values */ -char *innobase_data_home_dir; -char *innobase_log_group_home_dir, *innobase_log_arch_dir; -char *innobase_unix_file_flush_method; -bool innobase_flush_log_at_trx_commit, innobase_log_archive, - innobase_use_native_aio, innobase_fast_shutdown; +my_bool innobase_flush_log_at_trx_commit = FALSE; +my_bool innobase_log_archive = FALSE; +my_bool innobase_use_native_aio = FALSE; +my_bool innobase_fast_shutdown = TRUE; /* - Set default InnoDB size to 64M, to let users use InnoDB without having - to specify any startup options. + Set default InnoDB size to 64M and let it be auto-extending. Thus users + can use InnoDB without having to specify any startup options. */ -char *innobase_data_file_path= (char*) "ibdata1:64M"; -char *internal_innobase_data_file_path=0; +/* innobase_data_file_path=ibdata:15,idata2:1,... */ + +char *innobase_data_file_path= (char*) "ibdata1:64M:autoextend"; +static char *internal_innobase_data_file_path=0; /* The following counter is used to convey information to InnoDB about server activity: in selects it is not sensible to call @@ -235,7 +250,7 @@ convert_error_code_to_mysql( extern "C" { /***************************************************************** Prints info of a THD object (== user session thread) to the -standatd output. NOTE that mysql/innobase/trx/trx0trx.c must contain +standard output. NOTE that mysql/innobase/trx/trx0trx.c must contain the prototype for this function! */ void @@ -304,6 +319,8 @@ check_trx_exists( thd->transaction.stmt.innobase_tid = (void*)&innodb_dummy_stmt_trx_handle; + } else { + ut_a(trx->magic_n == TRX_MAGIC_N); } return(trx); @@ -336,227 +353,6 @@ ha_innobase::update_thd( } /************************************************************************* -Reads the data files and their sizes from a character string given in -the .cnf file. */ -static -bool -innobase_parse_data_file_paths_and_sizes(void) -/*==========================================*/ - /* out: TRUE if ok, FALSE if parsing - error */ -{ - char* str; - char* endp; - char* path; - ulint size; - ulint i = 0; - - str = internal_innobase_data_file_path; - - /* First calculate the number of data files and check syntax: - path:size[M];path:size[M]... . Note that a Windows path may - contain a drive name and a ':'. */ - - while (*str != '\0') { - path = str; - - while ((*str != ':' && *str != '\0') - || (*str == ':' - && (*(str + 1) == '\\' || *(str + 1) == '/'))) { - str++; - } - - if (*str == '\0') { - return(FALSE); - } - - str++; - - size = strtoul(str, &endp, 10); - - str = endp; - - if ((*str != 'M') && (*str != 'G')) { - size = size / (1024 * 1024); - } else if (*str == 'G') { - size = size * 1024; - str++; - } else { - str++; - } - - if (strlen(str) >= 6 - && *str == 'n' - && *(str + 1) == 'e' - && *(str + 2) == 'w') { - str += 3; - } - - if (strlen(str) >= 3 - && *str == 'r' - && *(str + 1) == 'a' - && *(str + 2) == 'w') { - str += 3; - } - - if (size == 0) { - return(FALSE); - } - - i++; - - if (*str == ';') { - str++; - } else if (*str != '\0') { - - return(FALSE); - } - } - - srv_data_file_names = (char**)ut_malloc(i * sizeof(void*)); - srv_data_file_sizes = (ulint*)ut_malloc(i * sizeof(ulint)); - srv_data_file_is_raw_partition = (ulint*)ut_malloc(i * sizeof(ulint)); - - srv_n_data_files = i; - - /* Then store the actual values to our arrays */ - - str = internal_innobase_data_file_path; - i = 0; - - while (*str != '\0') { - path = str; - - /* Note that we must ignore the ':' in a Windows path */ - - while ((*str != ':' && *str != '\0') - || (*str == ':' - && (*(str + 1) == '\\' || *(str + 1) == '/'))) { - str++; - } - - if (*str == ':') { - /* Make path a null-terminated string */ - *str = '\0'; - str++; - } - - size = strtoul(str, &endp, 10); - - str = endp; - - if ((*str != 'M') && (*str != 'G')) { - size = size / (1024 * 1024); - } else if (*str == 'G') { - size = size * 1024; - str++; - } else { - str++; - } - - srv_data_file_is_raw_partition[i] = 0; - - if (strlen(str) >= 6 - && *str == 'n' - && *(str + 1) == 'e' - && *(str + 2) == 'w') { - str += 3; - srv_data_file_is_raw_partition[i] = SRV_NEW_RAW; - } - - if (strlen(str) >= 3 - && *str == 'r' - && *(str + 1) == 'a' - && *(str + 2) == 'w') { - str += 3; - - if (srv_data_file_is_raw_partition[i] == 0) { - srv_data_file_is_raw_partition[i] = SRV_OLD_RAW; - } - } - - srv_data_file_names[i] = path; - srv_data_file_sizes[i] = size; - - i++; - - if (*str == ';') { - str++; - } - } - - return(TRUE); -} - -/************************************************************************* -Reads log group home directories from a character string given in -the .cnf file. */ -static -bool -innobase_parse_log_group_home_dirs(void) -/*====================================*/ - /* out: TRUE if ok, FALSE if parsing - error */ -{ - char* str; - char* path; - ulint i = 0; - - str = innobase_log_group_home_dir; - - /* First calculate the number of directories and check syntax: - path;path;... */ - - while (*str != '\0') { - path = str; - - while (*str != ';' && *str != '\0') { - str++; - } - - i++; - - if (*str == ';') { - str++; - } else if (*str != '\0') { - - return(FALSE); - } - } - - if (i != (ulint) innobase_mirrored_log_groups) { - - return(FALSE); - } - - srv_log_group_home_dirs = (char**) ut_malloc(i * sizeof(void*)); - - /* Then store the actual values to our array */ - - str = innobase_log_group_home_dir; - i = 0; - - while (*str != '\0') { - path = str; - - while (*str != ';' && *str != '\0') { - str++; - } - - if (*str == ';') { - *str = '\0'; - str++; - } - - srv_log_group_home_dirs[i] = path; - - i++; - } - - return(TRUE); -} - -/************************************************************************* Opens an InnoDB database. */ bool @@ -579,9 +375,9 @@ innobase_init(void) else { /* It's better to use current lib, to keep path's short */ - current_lib[0]=FN_CURLIB; - current_lib[1]=FN_LIBCHAR; - current_lib[2]=0; + current_lib[0] = FN_CURLIB; + current_lib[1] = FN_LIBCHAR; + current_lib[2] = 0; default_path=current_lib; } @@ -603,12 +399,17 @@ innobase_init(void) srv_data_home = (innobase_data_home_dir ? innobase_data_home_dir : default_path); - srv_logs_home = (char*) ""; srv_arch_dir = (innobase_log_arch_dir ? innobase_log_arch_dir : default_path); - ret = innobase_parse_data_file_paths_and_sizes(); - + ret = (bool) + srv_parse_data_file_paths_and_sizes(internal_innobase_data_file_path, + &srv_data_file_names, + &srv_data_file_sizes, + &srv_data_file_is_raw_partition, + &srv_n_data_files, + &srv_auto_extend_last_data_file, + &srv_last_file_size_max); if (ret == FALSE) { sql_print_error("InnoDB: syntax error in innodb_data_file_path"); DBUG_RETURN(TRUE); @@ -616,12 +417,18 @@ innobase_init(void) if (!innobase_log_group_home_dir) innobase_log_group_home_dir= default_path; - ret = innobase_parse_log_group_home_dirs(); - if (ret == FALSE) { - DBUG_RETURN(TRUE); - } + ret = (bool) + srv_parse_log_group_home_dirs(innobase_log_group_home_dir, + &srv_log_group_home_dirs); + + if (ret == FALSE || innobase_mirrored_log_groups != 1) { + fprintf(stderr, + "InnoDB: syntax error in innodb_log_group_home_dir\n" + "InnoDB: or a wrong number of mirrored log groups\n"); + DBUG_RETURN(TRUE); + } srv_unix_file_flush_method_str = (innobase_unix_file_flush_method ? innobase_unix_file_flush_method : (char*)"fdatasync"); @@ -662,10 +469,11 @@ innobase_init(void) if (err != DB_SUCCESS) { - DBUG_RETURN(1); + DBUG_RETURN(1); } + (void) hash_init(&innobase_open_tables,32,0,0, - (hash_get_key) innobase_get_key,0,0); + (hash_get_key) innobase_get_key,0,0); pthread_mutex_init(&innobase_mutex,MY_MUTEX_INIT_FAST); DBUG_RETURN(0); } @@ -841,6 +649,7 @@ innobase_close_connection( whose transaction should be rolled back */ { if (NULL != thd->transaction.all.innobase_tid) { + trx_rollback_for_mysql((trx_t*) (thd->transaction.all.innobase_tid)); trx_free_for_mysql((trx_t*) @@ -984,7 +793,10 @@ ha_innobase::open( /* Get pointer to a table object in InnoDB dictionary cache */ - if (NULL == (ib_table = dict_table_get(norm_name, NULL))) { + ib_table = dict_table_get_and_increment_handle_count( + norm_name, NULL); + + if (NULL == ib_table) { sql_print_error("InnoDB error:\n\ Cannot find table %s from the internal data dictionary\n\ @@ -1347,33 +1159,43 @@ build_template( clust_index = dict_table_get_first_index_noninline(prebuilt->table); - if (!prebuilt->in_update_remember_pos) { + if (!prebuilt->hint_no_need_to_fetch_extra_cols) { + /* We have a hint that we should at least fetch all + columns in the key, or all columns in the table */ + if (prebuilt->read_just_key) { + /* MySQL has instructed us that it is enough to + fetch the columns in the key */ + fetch_all_in_key = TRUE; } else { /* We are building a temporary table: fetch all - columns */ + columns; the reason is that MySQL may use the + clustered index key to store rows, but the mechanism + we use below to detect required columns does not + reveal that. Actually, it might be enough to + fetch only all in the key also in this case! */ templ_type = ROW_MYSQL_WHOLE_ROW; } } if (prebuilt->select_lock_type == LOCK_X) { - /* TODO: should fix the code in sql_update so that we could do - with fetching only the needed columns */ + /* We always retrieve the whole clustered index record if we + use exclusive row level locks, for example, if the read is + done in an UPDATE statement. */ templ_type = ROW_MYSQL_WHOLE_ROW; } if (templ_type == ROW_MYSQL_REC_FIELDS) { + /* In versions < 3.23.50 we always retrieved the clustered + index record if prebuilt->select_lock_type == LOCK_S, + but there is really not need for that, and in some cases + performance could be seriously degraded because the MySQL + optimizer did not know about our convention! */ - if (prebuilt->select_lock_type != LOCK_NONE) { - /* Let index be the clustered index */ - - index = clust_index; - } else { - index = prebuilt->index; - } + index = prebuilt->index; } else { index = clust_index; } @@ -1469,12 +1291,6 @@ skip_field: (index->table->cols + templ->col_no)->clust_pos; } } - - if (templ_type == ROW_MYSQL_REC_FIELDS - && prebuilt->select_lock_type != LOCK_NONE) { - - prebuilt->need_to_access_clustered = TRUE; - } } /************************************************************************ @@ -1507,7 +1323,9 @@ ha_innobase::write_row( } if (table->next_number_field && record == table->record[0]) { - + /* This is the case where the table has an + auto-increment column */ + /* Fetch the value the user possibly has set in the autoincrement field */ @@ -1591,12 +1409,6 @@ ha_innobase::write_row( } } - /* Set the 'in_update_remember_pos' flag to FALSE to - make sure all columns are fetched in the select done by - update_auto_increment */ - - prebuilt->in_update_remember_pos = FALSE; - update_auto_increment(); if (auto_inc == 0) { @@ -1620,7 +1432,7 @@ ha_innobase::write_row( } /* We have to set sql_stat_start to TRUE because - update_auto_increment has called a select, and + update_auto_increment may have called a select, and has reset that flag; row_insert_for_mysql has to know to set the IX intention lock on the table, something it only does at the start of each statement */ @@ -1860,9 +1672,7 @@ ha_innobase::update_row( /* This is not a delete */ prebuilt->upd_node->is_delete = FALSE; - if (!prebuilt->in_update_remember_pos) { - assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); - } + assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); srv_conc_enter_innodb(prebuilt->trx); @@ -1908,7 +1718,6 @@ ha_innobase::delete_row( /* This is a delete */ prebuilt->upd_node->is_delete = TRUE; - prebuilt->in_update_remember_pos = TRUE; srv_conc_enter_innodb(prebuilt->trx); @@ -2111,26 +1920,28 @@ ha_innobase::change_active_index( InnoDB */ { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - KEY* key; + KEY* key=0; statistic_increment(ha_read_key_count, &LOCK_status); DBUG_ENTER("change_active_index"); active_index = keynr; - if (keynr != MAX_KEY && table->keys > 0) - { + if (keynr != MAX_KEY && table->keys > 0) { key = table->key_info + active_index; - prebuilt->index=dict_table_get_index_noninline(prebuilt->table, key->name); - if (!prebuilt->index) - { - sql_print_error("Innodb could not find key n:o %u with name %s from dict cache for table %s", keynr, key->name, prebuilt->table->name); - return(1); - } + prebuilt->index = dict_table_get_index_noninline( + prebuilt->table, + key->name); + } else { + prebuilt->index = dict_table_get_first_index_noninline( + prebuilt->table); + } + + if (!prebuilt->index) { + sql_print_error("Innodb could not find key n:o %u with name %s from dict cache for table %s", keynr, key ? key->name : "NULL", prebuilt->table->name); + DBUG_RETURN(1); } - else - prebuilt->index = dict_table_get_first_index_noninline(prebuilt->table); assert(prebuilt->search_tuple != 0); @@ -2402,7 +2213,7 @@ ha_innobase::rnd_pos( int error; uint keynr = active_index; DBUG_ENTER("rnd_pos"); - DBUG_DUMP("key", pos, ref_stored_len); + DBUG_DUMP("key", (char*) pos, ref_stored_len); statistic_increment(ha_read_rnd_count, &LOCK_status); @@ -2465,44 +2276,6 @@ ha_innobase::position( ref_stored_len = len; } -/*********************************************************************** -Tells something additional to the handler about how to do things. */ - -int -ha_innobase::extra( -/*===============*/ - /* out: 0 or error number */ - enum ha_extra_function operation) - /* in: HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE */ -{ - row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - - switch (operation) { - case HA_EXTRA_RESET: - case HA_EXTRA_RESET_STATE: - prebuilt->read_just_key = 0; - break; - case HA_EXTRA_NO_KEYREAD: - prebuilt->read_just_key = 0; - break; - case HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE: - prebuilt->in_update_remember_pos = FALSE; - break; - case HA_EXTRA_KEYREAD: - prebuilt->read_just_key = 1; - break; - default:/* Do nothing */ - ; - } - - return(0); -} - -int ha_innobase::reset(void) -{ - return(0); -} - /********************************************************************* Creates a table definition to an InnoDB database. */ @@ -2665,7 +2438,6 @@ ha_innobase::create( dict_table_t* innobase_table; trx_t* trx; int primary_key_no; - KEY* key; uint i; char name2[FN_REFLEN]; char norm_name[FN_REFLEN]; @@ -2680,7 +2452,9 @@ ha_innobase::create( /* Create the table definition in InnoDB */ - if ((error = create_table_def(trx, form, norm_name))) { + error = create_table_def(trx, form, norm_name); + + if (error) { trx_commit_for_mysql(trx); @@ -2866,7 +2640,9 @@ innobase_drop_database( memcpy(namebuf, ptr, len); namebuf[len] = '/'; namebuf[len + 1] = '\0'; - +#ifdef __WIN__ + casedn_str(namebuf); +#endif trx = trx_allocate_for_mysql(); error = row_drop_database_for_mysql(namebuf, trx); @@ -2981,9 +2757,9 @@ ha_innobase::records_in_range( DBUG_ENTER("records_in_range"); - if (prebuilt->trx) { - prebuilt->trx->op_info = (char*) "estimating range size"; - } + /* Warning: since it is not sure that MySQL calls external_lock + before calling this function, the trx field in prebuilt can be + obsolete! */ active_index = keynr; @@ -3017,10 +2793,6 @@ ha_innobase::records_in_range( my_free((char*) key_val_buff2, MYF(0)); - if (prebuilt->trx) { - prebuilt->trx->op_info = (char*) ""; - } - DBUG_RETURN((ha_rows) n_rows); } @@ -3041,10 +2813,9 @@ ha_innobase::estimate_number_of_rows(void) ulonglong estimate; ulonglong data_file_length; - if (prebuilt->trx) { - prebuilt->trx->op_info = - (char*) "estimating upper bound of table size"; - } + /* Warning: since it is not sure that MySQL calls external_lock + before calling this function, the trx field in prebuilt can be + obsolete! */ DBUG_ENTER("info"); @@ -3061,10 +2832,6 @@ ha_innobase::estimate_number_of_rows(void) estimate = 2 * data_file_length / dict_index_calc_min_rec_len(index); - if (prebuilt->trx) { - prebuilt->trx->op_info = (char*) ""; - } - DBUG_RETURN((ha_rows) estimate); } @@ -3080,10 +2847,12 @@ ha_innobase::scan_time() { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - /* In the following formula we assume that scanning 10 pages - takes the same time as a disk seek: */ - - return((double) (prebuilt->table->stat_clustered_index_size / 10)); + /* Since MySQL seems to favor table scans too much over index + searches, we pretend that a sequential read takes the same time + as a random disk read, that is, we do not divide the following + by 10, which would be physically realistic. */ + + return((double) (prebuilt->table->stat_clustered_index_size)); } /************************************************************************* @@ -3104,9 +2873,9 @@ ha_innobase::info( DBUG_ENTER("info"); - if (prebuilt->trx) { - prebuilt->trx->op_info = (char*) "calculating table stats"; - } + /* Warning: since it is not sure that MySQL calls external_lock + before calling this function, the trx field in prebuilt can be + obsolete! */ ib_table = prebuilt->table; @@ -3154,25 +2923,17 @@ ha_innobase::info( index->stat_n_diff_key_vals[j + 1]); } + /* Since MySQL seems to favor table scans + too much over index searches, we pretend + index selectivity is 2 times better than + our estimate: */ + + rec_per_key = rec_per_key / 2; + if (rec_per_key == 0) { rec_per_key = 1; } - /* Since the MySQL optimizer is often too - pessimistic in the assumption that a table - does not fit in the buffer pool, we - increase the attractiveness of indexes - by assuming the selectivity of any prefix - of an index is 1 / 100 or better. - (Actually, we should look at the table - size, and if the table is smaller than - the buffer pool, we should uniformly - increase the attractiveness of indexes, - regardless of the estimated selectivity.) */ - - if (rec_per_key > records / 100) { - rec_per_key = records / 100; - } table->key_info[i].rec_per_key[j] = rec_per_key; } @@ -3188,15 +2949,13 @@ ha_innobase::info( pointer and cause a seg fault. */ if (flag & HA_STATUS_ERRKEY) { + ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N); + errkey = (unsigned int) row_get_mysql_key_number_for_index( (dict_index_t*) trx_get_error_info(prebuilt->trx)); } - if (prebuilt->trx) { - prebuilt->trx->op_info = (char*) ""; - } - DBUG_VOID_RETURN; } @@ -3217,6 +2976,8 @@ ha_innobase::check( row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; ulint ret; + ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N); + if (prebuilt->mysql_template == NULL) { /* Build the template; we will use a dummy template in index scans done in checking */ @@ -3250,6 +3011,10 @@ ha_innobase::update_table_comment( char* str = my_malloc(length + 550, MYF(0)); char* pos; + /* Warning: since it is not sure that MySQL calls external_lock + before calling this function, the trx field in prebuilt can be + obsolete! */ + if (!str) { return((char*)comment); } @@ -3264,11 +3029,104 @@ ha_innobase::update_table_comment( pos += sprintf(pos, "InnoDB free: %lu kB", (ulong) innobase_get_free_space()); - /* We assume 150 bytes of space to print info */ + /* We assume 450 - length bytes of space to print info */ + + if (length < 450) { + dict_print_info_on_foreign_keys(FALSE, pos, 450 - length, + prebuilt->table); + } + + return(str); +} + +/*********************************************************************** +Gets the foreign key create info for a table stored in InnoDB. */ + +char* +ha_innobase::get_foreign_key_create_info(void) +/*==========================================*/ + /* out, own: character string in the form which + can be inserted to the CREATE TABLE statement, + MUST be freed with ::free_foreign_key_create_info */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt; + char* str; + + if (prebuilt == NULL) { + fprintf(stderr, +"InnoDB: Error: cannot get create info for foreign keys\n"); + + return(NULL); + } + + str = (char*)ut_malloc(10000); - dict_print_info_on_foreign_keys(pos, 500, prebuilt->table); + str[0] = '\0'; + + dict_print_info_on_foreign_keys(TRUE, str, 9000, prebuilt->table); return(str); +} + +/*********************************************************************** +Frees the foreign key create info for a table stored in InnoDB, if it is +non-NULL. */ + +void +ha_innobase::free_foreign_key_create_info( +/*======================================*/ + char* str) /* in, own: create info string to free */ +{ + if (str) { + ut_free(str); + } +} + +/*********************************************************************** +Tells something additional to the handler about how to do things. */ + +int +ha_innobase::extra( +/*===============*/ + /* out: 0 or error number */ + enum ha_extra_function operation) + /* in: HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + + /* Warning: since it is not sure that MySQL calls external_lock + before calling this function, the trx field in prebuilt can be + obsolete! */ + + switch (operation) { + case HA_EXTRA_RESET: + case HA_EXTRA_RESET_STATE: + prebuilt->read_just_key = 0; + break; + case HA_EXTRA_NO_KEYREAD: + prebuilt->read_just_key = 0; + break; + case HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE: + prebuilt->hint_no_need_to_fetch_extra_cols = FALSE; + break; + case HA_EXTRA_KEYREAD: + prebuilt->read_just_key = 1; + break; + default:/* Do nothing */ + ; + } + + return(0); +} + +/********************************************************************** +????????????? */ + +int +ha_innobase::reset(void) +/*====================*/ +{ + return(0); } /********************************************************************** @@ -3296,7 +3154,7 @@ ha_innobase::external_lock( trx = prebuilt->trx; prebuilt->sql_stat_start = TRUE; - prebuilt->in_update_remember_pos = TRUE; + prebuilt->hint_no_need_to_fetch_extra_cols = TRUE; prebuilt->read_just_key = 0; @@ -3315,6 +3173,16 @@ ha_innobase::external_lock( thd->transaction.all.innodb_active_trans = 1; trx->n_mysql_tables_in_use++; + if (thd->tx_isolation == ISO_SERIALIZABLE + && prebuilt->select_lock_type == LOCK_NONE) { + + /* To get serializable execution we let InnoDB + conceptually add 'LOCK IN SHARE MODE' to all SELECTs + which otherwise would have been consistent reads */ + + prebuilt->select_lock_type = LOCK_S; + } + if (prebuilt->select_lock_type != LOCK_NONE) { trx->mysql_n_tables_locked++; @@ -3422,8 +3290,8 @@ ha_innobase::store_lock( lock_type == TL_READ_NO_INSERT) { /* This is a SELECT ... IN SHARE MODE, or we are doing a complex SQL statement like - INSERT INTO ... SELECT ... and the logical logging - requires the use of a locking read */ + INSERT INTO ... SELECT ... and the logical logging (MySQL + binlog) requires the use of a locking read */ prebuilt->select_lock_type = LOCK_S; } else { @@ -3463,38 +3331,59 @@ ha_innobase::get_auto_increment() /*=============================*/ /* out: the next auto-increment column value */ { - row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - longlong nr; - int error; + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + longlong nr; + int error; - (void) extra(HA_EXTRA_KEYREAD); - index_init(table->next_number_index); + /* Also SHOW TABLE STATUS calls this function. Previously, when we did + always read the max autoinc key value, setting x-locks, users were + surprised that SHOW TABLE STATUS could end up in a deadlock with + ordinary SQL queries. We avoid these deadlocks if the auto-inc + counter for the table has been initialized by fetching the value + from the table struct in dictionary cache. */ - /* We use an exclusive lock when we read the max key value from the - auto-increment column index. This is because then build_template will - advise InnoDB to fetch all columns. In SHOW TABLE STATUS the query - id of the auto-increment column is not changed, and previously InnoDB - did not fetch it, causing SHOW TABLE STATUS to show wrong values - for the autoinc column. */ + assert(prebuilt->table); + + nr = dict_table_autoinc_read(prebuilt->table); - prebuilt->select_lock_type = LOCK_X; - prebuilt->trx->mysql_n_tables_locked += 1; + if (nr != 0) { - error=index_last(table->record[1]); + return(nr + 1); + } - if (error) { - nr = 1; - } else { - nr = (longlong) table->next_number_field-> - val_int_offset(table->rec_buff_length) + 1; - } + (void) extra(HA_EXTRA_KEYREAD); + index_init(table->next_number_index); - (void) extra(HA_EXTRA_NO_KEYREAD); + /* We use an exclusive lock when we read the max key value from the + auto-increment column index. This is because then build_template will + advise InnoDB to fetch all columns. In SHOW TABLE STATUS the query + id of the auto-increment column is not changed, and previously InnoDB + did not fetch it, causing SHOW TABLE STATUS to show wrong values + for the autoinc column. */ - index_end(); + prebuilt->select_lock_type = LOCK_X; - return(nr); -} + /* Play safe and also give in another way the hint to fetch + all columns in the key: */ + + prebuilt->hint_no_need_to_fetch_extra_cols = FALSE; + + prebuilt->trx->mysql_n_tables_locked += 1; + + error = index_last(table->record[1]); + + if (error) { + nr = 1; + } else { + nr = (longlong) table->next_number_field-> + val_int_offset(table->rec_buff_length) + 1; + } + + (void) extra(HA_EXTRA_NO_KEYREAD); + index_end(); + + return(nr); +} #endif /* HAVE_INNOBASE_DB */ diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index f926f303b26..a0f3ea28d2c 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -158,7 +158,8 @@ class ha_innobase: public handler int rename_table(const char* from, const char* to); int check(THD* thd, HA_CHECK_OPT* check_opt); char* update_table_comment(const char* comment); - + char* get_foreign_key_create_info(); + void free_foreign_key_create_info(char* str); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); longlong get_auto_increment(); @@ -178,8 +179,9 @@ extern long innobase_force_recovery, innobase_thread_concurrency; extern char *innobase_data_home_dir, *innobase_data_file_path; extern char *innobase_log_group_home_dir, *innobase_log_arch_dir; extern char *innobase_unix_file_flush_method; -extern bool innobase_flush_log_at_trx_commit, innobase_log_archive, - innobase_use_native_aio, innobase_fast_shutdown; +/* The following variables have to be my_bool for SHOW VARIABLES to work */ +extern my_bool innobase_flush_log_at_trx_commit, innobase_log_archive, + innobase_use_native_aio, innobase_fast_shutdown; extern TYPELIB innobase_lock_typelib; diff --git a/sql/ha_isam.cc b/sql/ha_isam.cc index 4b8c40f8fe6..7c9c7a64c9d 100644 --- a/sql/ha_isam.cc +++ b/sql/ha_isam.cc @@ -201,7 +201,7 @@ void ha_isam::info(uint flag) sortkey = info.sortkey; block_size=nisam_block_size; table->keys = min(table->keys,info.keys); - table->keys_in_use= (((key_map) 1) << table->keys)- (key_map) 1; + table->keys_in_use= set_bits(key_map,table->keys); table->db_options_in_use= info.options; table->db_record_offset= (table->db_options_in_use & diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 78ac9f3b309..df55cdd0033 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -289,7 +289,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) error = chk_key(¶m, file); if (!error) { - if ((!check_opt->quick && + if ((!(param.testflag & T_QUICK) && ((share->options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) || (param.testflag & (T_EXTEND | T_MEDIUM)))) || @@ -330,7 +330,6 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) mi_mark_crashed(file); file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED; } - check_opt->retry_without_quick=param.retry_without_quick; thd->proc_info=old_proc_info; return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK; @@ -397,8 +396,7 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt) } tmp_check_opt.init(); - tmp_check_opt.quick = 1; - tmp_check_opt.flags |= T_VERY_SILENT | T_CALC_CHECKSUM; + tmp_check_opt.flags |= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK; DBUG_RETURN(repair(thd, &tmp_check_opt)); err: @@ -488,24 +486,23 @@ int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt) myisamchk_init(¶m); param.thd = thd; param.op_name = (char*) "repair"; - param.testflag = ((check_opt->flags & ~(T_EXTEND)) | + param.testflag = ((check_opt->flags & ~(T_EXTEND)) | T_SILENT | T_FORCE_CREATE | (check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT)); - if (check_opt->quick) - param.opt_rep_quick++; param.sort_buffer_length= check_opt->sort_buffer_size; start_records=file->state->records; while ((error=repair(thd,param,0)) && param.retry_repair) { param.retry_repair=0; - if (param.retry_without_quick && param.opt_rep_quick) + if ((param.testflag & T_RETRY_WITHOUT_QUICK) && + (param.testflag & T_QUICK)) { - param.opt_rep_quick=0; + param.testflag&= ~T_RETRY_WITHOUT_QUICK; sql_print_error("Warning: Retrying repair of: '%s' without quick", table->path); continue; } - param.opt_rep_quick=0; // Safety + param.testflag&= ~T_QUICK; if ((param.testflag & T_REP_BY_SORT)) { param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP; @@ -537,8 +534,6 @@ int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt) param.op_name = (char*) "optimize"; param.testflag = (check_opt->flags | T_SILENT | T_FORCE_CREATE | T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX); - if (check_opt->quick) - param.opt_rep_quick++; param.sort_buffer_length= check_opt->sort_buffer_size; return repair(thd,param,1); } @@ -573,7 +568,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) if (!optimize || ((file->state->del || share->state.split != file->state->records) && - (!param.opt_rep_quick || + (!(param.testflag & T_QUICK) || !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS)))) { ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ? @@ -587,13 +582,15 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) param.testflag|= T_STATISTICS; // We get this for free thd->proc_info="Repair by sorting"; statistics_done=1; - error = mi_repair_by_sort(¶m, file, fixed_name, param.opt_rep_quick); + error = mi_repair_by_sort(¶m, file, fixed_name, + param.testflag & T_QUICK); } else { thd->proc_info="Repair with keycache"; param.testflag &= ~T_REP_BY_SORT; - error= mi_repair(¶m, file, fixed_name, param.opt_rep_quick); + error= mi_repair(¶m, file, fixed_name, + param.testflag & T_QUICK); } param.testflag=testflag; optimize_done=1; @@ -691,17 +688,16 @@ bool ha_myisam::activate_all_index(THD *thd) mi_extra(file, HA_EXTRA_BULK_INSERT_END); if (enable_activate_all_index && - share->state.key_map != ((ulonglong) 1L << share->base.keys)-1) + share->state.key_map != set_bits(ulonglong, share->base.keys)) { const char *save_proc_info=thd->proc_info; thd->proc_info="Creating index"; myisamchk_init(¶m); param.op_name = (char*) "recreating_index"; - param.testflag = (T_SILENT | T_REP_BY_SORT | + param.testflag = (T_SILENT | T_REP_BY_SORT | T_QUICK | T_CREATE_MISSING_KEYS | T_TRUST_HEADER); param.myf_rw&= ~MY_WAIT_IF_FULL; param.sort_buffer_length= myisam_sort_buffer_size; - param.opt_rep_quick++; // Don't copy data file param.tmpdir=mysql_tmpdir; error=repair(thd,param,0) != HA_ADMIN_OK; thd->proc_info=save_proc_info; @@ -723,16 +719,16 @@ bool ha_myisam::check_and_repair(THD *thd) check_opt.flags= T_MEDIUM | T_AUTO_REPAIR; // Don't use quick if deleted rows if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK)) - check_opt.quick=1; + check_opt.flags|=T_QUICK; sql_print_error("Warning: Checking table: '%s'",table->path); if ((marked_crashed=mi_is_crashed(file)) || check(thd, &check_opt)) { sql_print_error("Warning: Recovering table: '%s'",table->path); - check_opt.quick= !check_opt.retry_without_quick && !marked_crashed; - check_opt.flags=(((myisam_recover_options & HA_RECOVER_BACKUP) ? - T_BACKUP_DATA : 0) | - (!(myisam_recover_options & HA_RECOVER_FORCE) ? - T_SAFE_REPAIR : 0)) | T_AUTO_REPAIR; + check_opt.flags= + ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) | + (marked_crashed ? 0 : T_QUICK) | + (myisam_recover_options & HA_RECOVER_FORCE ? 0 : T_SAFE_REPAIR) | + T_AUTO_REPAIR); if (repair(thd, &check_opt)) error=1; } diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 63a23fb708f..4e09c87f341 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -46,7 +46,7 @@ int ha_myisammrg::open(const char *name, int mode, uint test_if_locked) return (my_errno ? my_errno : -1); } DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc...")) - myrg_extrafunc(file, &query_cache_invalidate_by_MyISAM_filename); + myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref); if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED || test_if_locked == HA_OPEN_ABORT_IF_LOCKED)) myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK); @@ -189,7 +189,7 @@ void ha_myisammrg::info(uint flag) deleted = (ha_rows) info.deleted; data_file_length=info.data_file_length; errkey = info.errkey; - table->keys_in_use=(((key_map) 1) << table->keys)- (key_map) 1; + table->keys_in_use= set_bits(key_map, table->keys); table->db_options_in_use = info.options; table->is_view=1; mean_rec_length=info.reclength; diff --git a/sql/handler.cc b/sql/handler.cc index 1e879c55b5e..178f631569d 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -45,6 +45,7 @@ static int NEAR_F delete_file(const char *name,const char *ext,int extflag); ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count, ha_read_key_count, ha_read_next_count, ha_read_prev_count, ha_read_first_count, ha_read_last_count, + ha_commit_count, ha_rollback_count, ha_read_rnd_count, ha_read_rnd_next_count; const char *ha_table_type[] = { @@ -221,28 +222,31 @@ int ha_autocommit_or_rollback(THD *thd, int error) DBUG_RETURN(error); } -/* This function is called when MySQL writes the log segment of a transaction -to the binlog. It is called when the LOCK_log mutex is reserved. Here we -communicate to transactional table handlers whta binlog position corresponds -to the current transaction. The handler can store it and in recovery print -to the user, so that the user knows from what position in the binlog to -start possible roll-forward, for example, if the crashed server was a slave -in replication. This function also calls the commit of the table handler, -because the order of trasnactions in the log of the table handler must be -the same as in the binlog. */ +/* + This function is called when MySQL writes the log segment of a + transaction to the binlog. It is called when the LOCK_log mutex is + reserved. Here we communicate to transactional table handlers what + binlog position corresponds to the current transaction. The handler + can store it and in recovery print to the user, so that the user + knows from what position in the binlog to start possible + roll-forward, for example, if the crashed server was a slave in + replication. This function also calls the commit of the table + handler, because the order of transactions in the log of the table + handler must be the same as in the binlog. + + arguments: + log_file_name: latest binlog file name + end_offset: the offset in the binlog file up to which we wrote +*/ -int ha_report_binlog_offset_and_commit( - THD *thd, /* in: user thread */ - char *log_file_name, /* in: latest binlog file name */ - my_off_t end_offset) /* in: the offset in the binlog file - up to which we wrote */ +int ha_report_binlog_offset_and_commit(THD *thd, + char *log_file_name, + my_off_t end_offset) { + int error= 0; +#ifdef HAVE_INNOBASE_DB THD_TRANS *trans; - int error = 0; - trans = &thd->transaction.all; - -#ifdef HAVE_INNOBASE_DB if (trans->innobase_tid) { if ((error=innobase_report_binlog_offset_and_commit(thd, @@ -256,7 +260,6 @@ int ha_report_binlog_offset_and_commit( trans->innodb_active_trans=0; } #endif - return error; } @@ -267,6 +270,8 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) #ifdef USING_TRANSACTIONS if (opt_using_transactions) { + bool operation_done= 0; + bool transaction_commited= 0; /* Update the binary log if we have cached some queries */ if (trans == &thd->transaction.all && mysql_bin_log.is_open() && my_b_tell(&thd->transaction.trans_log)) @@ -284,6 +289,8 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) my_error(ER_ERROR_DURING_COMMIT, MYF(0), error); error=1; } + else + transaction_commited= 1; trans->bdb_tid=0; } #endif @@ -297,12 +304,19 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) } trans->innodb_active_trans=0; if (trans == &thd->transaction.all) - query_cache.invalidate(Query_cache_table::INNODB); + operation_done= transaction_commited= 1; + } #endif +#ifdef HAVE_QUERY_CACHE + if (transaction_commited) + query_cache.invalidate(thd->transaction.changed_tables); +#endif /*HAVE_QUERY_CACHE*/ if (error && trans == &thd->transaction.all && mysql_bin_log.is_open()) sql_print_error("Error: Got error during commit; Binlog is not up to date!"); thd->tx_isolation=thd->session_tx_isolation; + if (operation_done) + statistic_increment(ha_commit_count,&LOCK_status); } #endif // using transactions DBUG_RETURN(error); @@ -316,6 +330,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) #ifdef USING_TRANSACTIONS if (opt_using_transactions) { + bool operation_done=0; #ifdef HAVE_BERKELEY_DB if (trans->bdb_tid) { @@ -325,6 +340,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) error=1; } trans->bdb_tid=0; + operation_done=1; } #endif #ifdef HAVE_INNOBASE_DB @@ -336,6 +352,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) error=1; } trans->innodb_active_trans=0; + operation_done=1; } #endif if (trans == &thd->transaction.all) @@ -343,6 +360,8 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) WRITE_CACHE, (my_off_t) 0, 0, 1); thd->transaction.trans_log.end_of_file= max_binlog_cache_size; thd->tx_isolation=thd->session_tx_isolation; + if (operation_done) + statistic_increment(ha_rollback_count,&LOCK_status); } #endif /* USING_TRANSACTIONS */ DBUG_RETURN(error); @@ -527,7 +546,8 @@ int handler::read_first_row(byte * buf, uint primary_key) If there is very few deleted rows in the table, find the first row by scanning the table. */ - if (deleted < 10 || primary_key >= MAX_KEY) + if (deleted < 10 || primary_key >= MAX_KEY || + !(option_flag() & HA_READ_ORDER)) { (void) rnd_init(); while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ; diff --git a/sql/handler.h b/sql/handler.h index b083465afc5..868badf4d49 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -174,14 +174,11 @@ extern ulong myisam_sort_buffer_size; typedef struct st_ha_check_opt { ulong sort_buffer_size; - uint flags; - bool quick; - bool changed_files; - bool optimize; - bool retry_without_quick; + uint flags; /* isam layer flags (e.g. for myisamchk) */ + uint sql_flags; /* sql layer flags - for something myisamchk cannot do */ inline void init() { - flags= 0; quick= optimize= retry_without_quick=0; + flags= sql_flags= 0; sort_buffer_size = myisam_sort_buffer_size; } } HA_CHECK_OPT; @@ -311,7 +308,9 @@ public: virtual char *update_table_comment(const char * comment) { return (char*) comment;} virtual void append_create_info(String *packet) {} - + virtual char* get_foreign_key_create_info() + { return(NULL);} /* gets foreign key create string from InnoDB */ + virtual void free_foreign_key_create_info(char* str) {} /* The following can be called without an open handler */ virtual const char *table_type() const =0; virtual const char **bas_ext() const =0; diff --git a/sql/item.cc b/sql/item.cc index c081fd9dd5f..dac10eafafb 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -59,12 +59,28 @@ void Item::set_name(char *str,uint length) } } -bool Item::eq(const Item *item) const // Only doing this on conds +/* + This function is only called when comparing items in the WHERE clause +*/ + +bool Item::eq(const Item *item, bool binary_cmp) const { return type() == item->type() && name && item->name && !my_strcasecmp(name,item->name); } +bool Item_string::eq(const Item *item, bool binary_cmp) const +{ + if (type() == item->type()) + { + if (binary_cmp) + return !stringcmp(&str_value, &item->str_value); + return !sortcmp(&str_value, &item->str_value); + } + return 0; +} + + /* Get the value of the function as a TIME structure. As a extra convenience the time structure is reset on error! @@ -203,7 +219,7 @@ longlong Item_field::val_int_result() return result_field->val_int(); } -bool Item_field::eq(const Item *item) const +bool Item_field::eq(const Item *item, bool binary_cmp) const { return item->type() == FIELD_ITEM && ((Item_field*) item)->field == field; } @@ -262,7 +278,8 @@ void Item_string::print(String *str) str->append('\''); } -bool Item_null::eq(const Item *item) const { return item->type() == type(); } +bool Item_null::eq(const Item *item, bool binary_cmp) const +{ return item->type() == type(); } double Item_null::val() { null_value=1; return 0.0; } longlong Item_null::val_int() { null_value=1; return 0; } /* ARGSUSED */ diff --git a/sql/item.h b/sql/item.h index 5028f25c6b8..575af197e7d 100644 --- a/sql/item.h +++ b/sql/item.h @@ -57,7 +57,7 @@ public: virtual void save_org_in_field(Field *field) { (void) save_in_field(field); } virtual bool send(THD *thd, String *str); - virtual bool eq(const Item *) const; + virtual bool eq(const Item *, bool binary_cmp) const; virtual Item_result result_type () const { return REAL_RESULT; } virtual enum Type type() const =0; virtual double val()=0; @@ -111,7 +111,7 @@ public: {} Item_field(Field *field); enum Type type() const { return FIELD_ITEM; } - bool eq(const Item *item) const; + bool eq(const Item *item, bool binary_cmp) const; double val(); longlong val_int(); String *val_str(String*); @@ -141,7 +141,7 @@ public: Item_null(char *name_par=0) { maybe_null=null_value=TRUE; name= name_par ? name_par : (char*) "NULL";} enum Type type() const { return NULL_ITEM; } - bool eq(const Item *item) const; + bool eq(const Item *item, bool binary_cmp) const; double val(); longlong val_int(); String *val_str(String *str); @@ -264,6 +264,7 @@ public: void make_field(Send_field *field); enum Item_result result_type () const { return STRING_RESULT; } bool basic_const_item() const { return 1; } + bool eq(const Item *item, bool binary_cmp) const; Item *new_item() { return new Item_string(name,str_value.ptr(),max_length); } String *const_string() { return &str_value; } inline void append(char *str,uint length) { str_value.append(str,length); } @@ -323,7 +324,8 @@ public: Item_ref(Item **item, char *table_name_par,char *field_name_par) :Item_ident(NullS,table_name_par,field_name_par),ref(item) {} enum Type type() const { return REF_ITEM; } - bool eq(const Item *item) const { return (*ref)->eq(item); } + bool eq(const Item *item, bool binary_cmp) const + { return (*ref)->eq(item, binary_cmp); } ~Item_ref() { if (ref) delete *ref; } double val() { @@ -343,6 +345,11 @@ public: null_value=(*ref)->null_value; return tmp; } + bool is_null() + { + (void) (*ref)->val_int_result(); + return (*ref)->null_value; + } bool get_date(TIME *ltime,bool fuzzydate) { return (null_value=(*ref)->get_date(ltime,fuzzydate)); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 0c83698e60a..bb773af1ddf 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -790,8 +790,9 @@ String *Item_func_coalesce::val_str(String *str) null_value=0; for (uint i=0 ; i < arg_count ; i++) { - if (args[i]->val_str(str) != NULL) - return args[i]->val_str(str); + String *res; + if ((res=args[i]->val_str(str))) + return res; } null_value=1; return 0; diff --git a/sql/item_func.cc b/sql/item_func.cc index 1818a755a43..90e7a5a6477 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -148,7 +148,7 @@ void Item_func::print_op(String *str) str->append(')'); } -bool Item_func::eq(const Item *item) const +bool Item_func::eq(const Item *item, bool binary_cmp) const { /* Assume we don't have rtti */ if (this == item) @@ -160,7 +160,7 @@ bool Item_func::eq(const Item *item) const func_name() != item_func->func_name()) return 0; for (uint i=0; i < arg_count ; i++) - if (!args[i]->eq(item_func->args[i])) + if (!args[i]->eq(item_func->args[i], binary_cmp)) return 0; return 1; } @@ -1975,7 +1975,7 @@ void Item_func_get_user_var::print(String *str) str->append(')'); } -bool Item_func_get_user_var::eq(const Item *item) const +bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const { /* Assume we don't have rtti */ if (this == item) @@ -2108,12 +2108,14 @@ bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist) key=NO_SUCH_KEY; const_item_cache=0; table=((Item_field *)fields.head())->field->table; + table->fulltext_searched=1; record=table->record[0]; if (key == NO_SUCH_KEY && mode != FT_BOOL) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); return 1; } + return 0; } @@ -2197,7 +2199,7 @@ err: return 1; } -bool Item_func_match::eq(const Item *item) const +bool Item_func_match::eq(const Item *item, bool binary_cmp) const { if (item->type() != FUNC_ITEM) return 0; @@ -2208,7 +2210,7 @@ bool Item_func_match::eq(const Item *item) const Item_func_match *ifm=(Item_func_match*) item; if (key == ifm->key && table == ifm->table && - key_item()->eq(ifm->key_item())) + key_item()->eq(ifm->key_item(), binary_cmp)) return 1; return 0; diff --git a/sql/item_func.h b/sql/item_func.h index 823bfb44a96..c3e437712ee 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -98,7 +98,7 @@ public: void make_field(Send_field *field); table_map used_tables() const; void update_used_tables(); - bool eq(const Item *item) const; + bool eq(const Item *item, bool binary_cmp) const; virtual optimize_type select_optimize() const { return OPTIMIZE_NONE; } virtual bool have_rev_func() const { return 0; } virtual Item *key_item() const { return args[0]; } @@ -889,7 +889,7 @@ public: bool const_item() const { return const_var_flag; } table_map used_tables() const { return const_var_flag ? 0 : RAND_TABLE_BIT; } - bool eq(const Item *item) const; + bool eq(const Item *item, bool binary_cmp) const; }; @@ -930,13 +930,14 @@ public: ft_handler=0; if(join_key) table->file->ft_handler=0; + table->fulltext_searched=0; } if (concat) delete concat; } enum Functype functype() const { return FT_FUNC; } void update_used_tables() {} bool fix_fields(THD *thd,struct st_table_list *tlist); - bool eq(const Item *) const; + bool eq(const Item *, bool binary_cmp) const; longlong val_int() { return val()!=0.0; } double val(); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index c64fdc7a049..602cd7e38be 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1921,18 +1921,21 @@ String* Item_func_inet_ntoa::val_str(String* str) uchar buf[8], *p; ulonglong n = (ulonglong) args[0]->val_int(); char num[4]; - // we do not know if args[0] is NULL until we have called - // some val function on it if args[0] is not a constant! + /* + we do not know if args[0] is NULL until we have called + some val function on it if args[0] is not a constant! + */ if ((null_value=args[0]->null_value)) return 0; // Null value + str->length(0); int8store(buf,n); - // now we can assume little endian - // we handle the possibility of an 8-byte IP address - // however, we do not want to confuse those who are just using - // 4 byte ones - + /* + Now we can assume little endian. + We handle the possibility of an 8-byte IP address however, we do + not want to confuse those who are just using 4 byte ones + */ for (p= buf + 8; p > buf+4 && p[-1] == 0 ; p-- ) ; num[3]='.'; while (p-- > buf) diff --git a/sql/item_sum.h b/sql/item_sum.h index 4840ae8298d..a963799b6a7 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -64,6 +64,7 @@ public: { return new Item_field(field);} table_map used_tables() const { return ~(table_map) 0; } /* Not used */ bool const_item() const { return 0; } + bool is_null() { return null_value; } void update_used_tables() { } void make_field(Send_field *field); void print(String *str); @@ -202,6 +203,7 @@ public: enum Type type() const { return FIELD_AVG_ITEM; } double val(); longlong val_int() { return (longlong) val(); } + bool is_null() { (void) val_int(); return null_value; } String *val_str(String*); void make_field(Send_field *field); void fix_length_and_dec() {} @@ -239,6 +241,7 @@ public: double val(); longlong val_int() { return (longlong) val(); } String *val_str(String*); + bool is_null() { (void) val_int(); return null_value; } void make_field(Send_field *field); void fix_length_and_dec() {} }; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 8f55a02b020..9a003b79609 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -672,7 +672,7 @@ String *Item_func_date_format::val_str(String *str) else size=format_length(format); if (format == str) - str= &str_value; // Save result here + str= &value; // Save result here if (str->alloc(size)) { null_value=1; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 32b85e7f028..aa4140192ab 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -333,6 +333,7 @@ class Item_func_date_format :public Item_str_func { int fixed_length; const bool date_or_time; + String value; public: Item_func_date_format(Item *a,Item *b,bool date_or_time_arg) :Item_str_func(a,b),date_or_time(date_or_time_arg) {} diff --git a/sql/lex.h b/sql/lex.h index 6bee4152e48..8c7beb64b9b 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -62,8 +62,8 @@ static SYMBOL symbols[] = { { "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH),0,0}, { "AUTO_INCREMENT", SYM(AUTO_INC),0,0}, { "AUTOCOMMIT", SYM(AUTOCOMMIT),0,0}, - { "BACKUP", SYM(BACKUP_SYM),0,0}, - { "BEGIN", SYM(BEGIN_SYM),0,0}, + { "BACKUP", SYM(BACKUP_SYM),0,0}, + { "BEGIN", SYM(BEGIN_SYM),0,0}, { "BERKELEYDB", SYM(BERKELEY_DB_SYM),0,0}, { "BDB", SYM(BERKELEY_DB_SYM),0,0}, { "BETWEEN", SYM(BETWEEN_SYM),0,0}, @@ -191,6 +191,7 @@ static SYMBOL symbols[] = { { "INT4", SYM(INT_SYM),0,0}, { "INT8", SYM(BIGINT),0,0}, { "INTO", SYM(INTO),0,0}, + { "IO_THREAD", SYM(IO_THREAD),0,0}, { "IF", SYM(IF),0,0}, { "IS", SYM(IS),0,0}, { "ISOLATION", SYM(ISOLATION),0,0}, @@ -279,6 +280,8 @@ static SYMBOL symbols[] = { { "READ", SYM(READ_SYM),0,0}, { "REAL", SYM(REAL),0,0}, { "REFERENCES", SYM(REFERENCES),0,0}, + { "RELAY_LOG_FILE", SYM(RELAY_LOG_FILE_SYM),0,0}, + { "RELAY_LOG_POS", SYM(RELAY_LOG_POS_SYM),0,0}, { "RELOAD", SYM(RELOAD),0,0}, { "REGEXP", SYM(REGEXP),0,0}, { "RENAME", SYM(RENAME),0,0}, @@ -320,13 +323,14 @@ static SYMBOL symbols[] = { { "SQL_LOG_UPDATE", SYM(SQL_LOG_UPDATE),0,0}, { "SQL_LOW_PRIORITY_UPDATES", SYM(SQL_LOW_PRIORITY_UPDATES),0,0}, { "SQL_MAX_JOIN_SIZE",SYM(SQL_MAX_JOIN_SIZE), 0, 0}, - { "SQL_NO_CACHE", SYM(SQL_NO_CACHE_SYM), 0, 0}, + { "SQL_NO_CACHE", SYM(SQL_NO_CACHE_SYM), 0, 0}, { "SQL_QUERY_CACHE_TYPE",SYM(SQL_QUERY_CACHE_TYPE_SYM), 0, 0}, { "SQL_QUOTE_SHOW_CREATE",SYM(SQL_QUOTE_SHOW_CREATE), 0, 0}, { "SQL_SAFE_UPDATES", SYM(SQL_SAFE_UPDATES),0,0}, { "SQL_SELECT_LIMIT", SYM(SQL_SELECT_LIMIT),0,0}, { "SQL_SLAVE_SKIP_COUNTER", SYM(SQL_SLAVE_SKIP_COUNTER),0,0}, { "SQL_SMALL_RESULT", SYM(SQL_SMALL_RESULT),0,0}, + { "SQL_THREAD", SYM(SQL_THREAD),0,0}, { "SQL_WARNINGS", SYM(SQL_WARNINGS),0,0}, { "SSL", SYM(SSL_SYM),0,0}, { "STRAIGHT_JOIN", SYM(STRAIGHT_JOIN),0,0}, @@ -359,6 +363,7 @@ static SYMBOL symbols[] = { { "UNLOCK", SYM(UNLOCK_SYM),0,0}, { "UNSIGNED", SYM(UNSIGNED),0,0}, { "USE", SYM(USE_SYM),0,0}, + { "USE_FRM", SYM(USE_FRM),0,0}, { "USING", SYM(USING),0,0}, { "UPDATE", SYM(UPDATE_SYM),0,0}, { "USAGE", SYM(USAGE),0,0}, diff --git a/sql/lock.cc b/sql/lock.cc index 5d2856278b6..db849757741 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -88,6 +88,13 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count) thd->locked=0; break; } + else if (!thd->open_tables) + { + // Only using temporary tables, no need to unlock + thd->some_tables_deleted=0; + thd->locked=0; + break; + } /* some table was altered or deleted. reopen tables marked deleted */ mysql_unlock_tables(thd,sql_lock); diff --git a/sql/log.cc b/sql/log.cc index 892780d3882..c393d2eb413 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -83,7 +83,7 @@ static int find_uniq_filename(char *name) MYSQL_LOG::MYSQL_LOG(): last_time(0), query_start(0),index_file(-1), name(0), log_type(LOG_CLOSED),write_error(0), inited(0), file_id(1),no_rotate(0), - need_start_event(1) + need_start_event(1),bytes_written(0) { /* We don't want to intialize LOCK_Log here as the thread system may @@ -99,6 +99,7 @@ MYSQL_LOG::~MYSQL_LOG() { (void) pthread_mutex_destroy(&LOCK_log); (void) pthread_mutex_destroy(&LOCK_index); + (void) pthread_cond_destroy(&update_cond); } } @@ -233,17 +234,14 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, } else if (log_type == LOG_BIN) { - bool error; - /* - Explanation of the boolean black magic: - if we are supposed to write magic number try write - clean up if failed - then if index_file has not been previously opened, try to open it - clean up if failed - */ - if ((do_magic && my_b_write(&log_file, (byte*) BINLOG_MAGIC, 4)) || + bool error; + if (do_magic) + { + if (my_b_write(&log_file, (byte*) BINLOG_MAGIC, 4) || open_index(O_APPEND | O_RDWR | O_CREAT)) - goto err; + goto err; + bytes_written += 4; + } if (need_start_event && !no_auto_events) { @@ -461,12 +459,30 @@ err: my_delete(fname, MYF(0)); // do not report error if the file is not there else { + MY_STAT s; my_close(index_file, MYF(MY_WME)); + if (!my_stat(rli->relay_log_name,&s,MYF(0))) + { + sql_print_error("The first log %s failed to stat during purge", + rli->relay_log_name); + error=1; + goto err; + } if (my_rename(fname,index_file_name,MYF(MY_WME)) || (index_file=my_open(index_file_name,O_BINARY|O_RDWR|O_APPEND, MYF(MY_WME)))<0 || my_delete(rli->relay_log_name, MYF(MY_WME))) error=1; + + pthread_mutex_lock(&rli->log_space_lock); + rli->log_space_total -= s.st_size; + fprintf(stderr,"purge_first_log: %ld\n", rli->log_space_total); + pthread_mutex_unlock(&rli->log_space_lock); + // ok to broadcast after the critical region as there is no risk of + // the mutex being destroyed by this thread later - this helps save + // context switches + pthread_cond_broadcast(&rli->log_space_cond); + if ((error=find_first_log(&rli->linfo,"",0/*no mutex*/))) { char buff[22]; @@ -478,6 +494,7 @@ err: rli->relay_log_pos = 4; strnmov(rli->relay_log_name,rli->linfo.log_file_name, sizeof(rli->relay_log_name)); + flush_relay_log_info(rli); } /* No need to free io_buf because we allocated both fname and io_buf in @@ -693,6 +710,7 @@ void MYSQL_LOG::new_file(bool inside_mutex) if (thd && thd->slave_thread) r.flags |= LOG_EVENT_FORCED_ROTATE_F; r.write(&log_file); + bytes_written += r.get_event_len(); } // update needs to be signaled even if there is no rotate event // log rotation should give the waiting thread a signal to @@ -726,6 +744,7 @@ bool MYSQL_LOG::append(Log_event* ev) error=1; goto err; } + bytes_written += ev->get_event_len(); if ((uint)my_b_append_tell(&log_file) > max_binlog_size) { new_file(1); @@ -752,6 +771,7 @@ bool MYSQL_LOG::appendv(const char* buf, uint len,...) error = 1; break; } + bytes_written += len; } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint))); if ((uint) my_b_append_tell(&log_file) > max_binlog_size) diff --git a/sql/log_event.cc b/sql/log_event.cc index 648e9175e13..fd04f8dbbaa 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -556,6 +556,8 @@ Log_event* Log_event::read_log_event(const char* buf, int event_len, ev = new Query_log_event(buf, event_len, old_format); break; case LOAD_EVENT: + ev = new Create_file_log_event(buf, event_len, old_format); + break; case NEW_LOAD_EVENT: ev = new Load_log_event(buf, event_len, old_format); break; @@ -566,7 +568,7 @@ Log_event* Log_event::read_log_event(const char* buf, int event_len, ev = new Slave_log_event(buf, event_len); break; case CREATE_FILE_EVENT: - ev = new Create_file_log_event(buf, event_len); + ev = new Create_file_log_event(buf, event_len, old_format); break; case APPEND_BLOCK_EVENT: ev = new Append_block_log_event(buf, event_len); @@ -959,6 +961,12 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format) if (use_new_format) { empty_flags=0; + /* the code below assumes that buf will not disappear from + under our feet during the lifetime of the event. This assumption + holds true in the slave thread if the log is in new format, but is not + the case when we have old format because we will be reusing net buffer + to read the actual file before we write out the Create_file event + */ if (read_str(buf, buf_end, field_term, field_term_len) || read_str(buf, buf_end, enclosed, enclosed_len) || read_str(buf, buf_end, line_term, line_term_len) || @@ -970,11 +978,11 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format) else { field_term_len= enclosed_len= line_term_len= line_start_len= escaped_len=1; - *field_term=*buf++; - *enclosed= *buf++; - *line_term= *buf++; - *line_start=*buf++; - *escaped= *buf++; + field_term = buf++; + enclosed= buf++; + line_term= buf++; + line_start= buf++; + escaped= buf++; opt_flags = *buf++; empty_flags=*buf++; if (empty_flags & FIELD_TERM_EMPTY) @@ -1095,7 +1103,9 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len, db_len = (uint)data_head[L_DB_LEN_OFFSET]; num_fields = uint4korr(data_head + L_NUM_FIELDS_OFFSET); - int body_offset = get_data_body_offset(); + int body_offset = (buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ? + LOAD_HEADER_LEN + OLD_HEADER_LEN : get_data_body_offset(); + if ((int) event_len < body_offset) return 1; //sql_ex.init() on success returns the pointer to the first byte after @@ -1117,7 +1127,6 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len, table_name = fields + field_block_len; db = table_name + table_name_len + 1; fname = db + db_len + 1; - int type_code = get_type_code(); fname_len = strlen(fname); // null termination is accomplished by the caller doing buf[event_len]=0 return 0; @@ -1367,20 +1376,29 @@ int Create_file_log_event::write_base(IO_CACHE* file) return res; } -Create_file_log_event::Create_file_log_event(const char* buf, int len): - Load_log_event(buf,0,0),fake_base(0),block(0) +Create_file_log_event::Create_file_log_event(const char* buf, int len, + bool old_format): + Load_log_event(buf,0,old_format),fake_base(0),block(0),inited_from_old(0) { int block_offset; - if (copy_log_event(buf,len,0)) - return; - file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + - + LOAD_HEADER_LEN + CF_FILE_ID_OFFSET); - block_offset = LOG_EVENT_HEADER_LEN + Load_log_event::get_data_size() + - CREATE_FILE_HEADER_LEN + 1; // 1 for \0 terminating fname - if (len < block_offset) + if (copy_log_event(buf,len,old_format)) return; - block = (char*)buf + block_offset; - block_len = len - block_offset; + if (!old_format) + { + file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + + + LOAD_HEADER_LEN + CF_FILE_ID_OFFSET); + block_offset = LOG_EVENT_HEADER_LEN + Load_log_event::get_data_size() + + CREATE_FILE_HEADER_LEN + 1; // 1 for \0 terminating fname + if (len < block_offset) + return; + block = (char*)buf + block_offset; + block_len = len - block_offset; + } + else + { + sql_ex.force_new_format(); + inited_from_old = 1; + } } @@ -1568,6 +1586,7 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) int expected_error,actual_error = 0; init_sql_alloc(&thd->mem_root, 8192,0); thd->db = rewrite_db((char*)db); + DBUG_ASSERT(q_len == strlen(query)); if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { thd->query = (char*)query; @@ -1588,7 +1607,9 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) { mysql_parse(thd, thd->query, q_len); if (expected_error != - (actual_error = thd->net.last_errno) && expected_error) + (actual_error = thd->net.last_errno) && expected_error && + !ignored_error_code(actual_error) && + !ignored_error_code(expected_error)) { const char* errmsg = "Slave: did not get the expected error\ running query from master - expected: '%s' (%d), got '%s' (%d)"; @@ -1739,11 +1760,12 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli) int Start_log_event::exec_event(struct st_relay_log_info* rli) { + close_temporary_tables(thd); + // if we have old format, load_tmpdir is cleaned up by the I/O thread + // TODO: cleanup_load_tmpdir() needs to remove only the files associated + // with the server id that has just started if (!rli->mi->old_format) - { - close_temporary_tables(thd); cleanup_load_tmpdir(); - } return Log_event::exec_event(rli); } diff --git a/sql/log_event.h b/sql/log_event.h index 089d9589763..a29c3952d46 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -64,6 +64,8 @@ struct old_sql_ex char empty_flags; }; +#define NUM_LOAD_DELIM_STRS 5 + struct sql_ex_info { @@ -153,8 +155,8 @@ struct sql_ex_info #define L_THREAD_ID_OFFSET 0 #define L_EXEC_TIME_OFFSET 4 #define L_SKIP_LINES_OFFSET 8 -#define L_DB_LEN_OFFSET 12 -#define L_TBL_LEN_OFFSET 13 +#define L_TBL_LEN_OFFSET 12 +#define L_DB_LEN_OFFSET 13 #define L_NUM_FIELDS_OFFSET 14 #define L_SQL_EX_OFFSET 18 #define L_DATA_OFFSET LOAD_HEADER_LEN @@ -570,6 +572,7 @@ public: char* block; uint block_len; uint file_id; + bool inited_from_old; #ifndef MYSQL_CLIENT Create_file_log_event(THD* thd, sql_exchange* ex, const char* db_arg, const char* table_name_arg, @@ -578,7 +581,7 @@ public: char* block_arg, uint block_len_arg); #endif - Create_file_log_event(const char* buf, int event_len); + Create_file_log_event(const char* buf, int event_len, bool old_format); ~Create_file_log_event() { } @@ -591,7 +594,7 @@ public: 4 + 1 + block_len;} int get_data_body_offset() { return fake_base ? LOAD_EVENT_OVERHEAD: LOAD_EVENT_OVERHEAD + CREATE_FILE_HEADER_LEN; } - bool is_valid() { return block != 0; } + bool is_valid() { return inited_from_old || block != 0; } int write_data_header(IO_CACHE* file); int write_data_body(IO_CACHE* file); int write_base(IO_CACHE* file); // cut out Create_file extentions and diff --git a/sql/my_lock.c b/sql/my_lock.c index 9b4ac502e57..4d451fcff22 100644 --- a/sql/my_lock.c +++ b/sql/my_lock.c @@ -26,10 +26,6 @@ #include <thr_alarm.h> #include <errno.h> -#ifdef HAVE_FCNTL -static struct flock lock; /* Must be static for sun-sparc */ -#endif - /* Lock a part of a file */ int my_lock(File fd,int locktype,my_off_t start,my_off_t length,myf MyFlags) @@ -37,24 +33,25 @@ int my_lock(File fd,int locktype,my_off_t start,my_off_t length,myf MyFlags) thr_alarm_t alarmed; ALARM alarm_buff; uint wait_for_alarm; + struct flock m_lock; DBUG_ENTER("my_lock"); DBUG_PRINT("my",("Fd: %d Op: %d start: %ld Length: %ld MyFlags: %d", fd,locktype,(ulong) start,(ulong) length,MyFlags)); if (my_disable_locking) DBUG_RETURN(0); /* purecov: inspected */ - lock.l_type=(short) locktype; - lock.l_whence=0L; - lock.l_start=(long) start; - lock.l_len=(long) length; + m_lock.l_type=(short) locktype; + m_lock.l_whence=0L; + m_lock.l_start=(long) start; + m_lock.l_len=(long) length; wait_for_alarm=(MyFlags & MY_DONT_WAIT ? MY_HOW_OFTEN_TO_ALARM : (uint) 12*60*60); - if (fcntl(fd,F_SETLK,&lock) != -1) /* Check if we can lock */ + if (fcntl(fd,F_SETLK,&m_lock) != -1) /* Check if we can lock */ DBUG_RETURN(0); /* Ok, file locked */ DBUG_PRINT("info",("Was locked, trying with alarm")); if (!thr_alarm(&alarmed,wait_for_alarm,&alarm_buff)) { int value; - while ((value=fcntl(fd,F_SETLKW,&lock)) && !thr_got_alarm(&alarmed) && + while ((value=fcntl(fd,F_SETLKW,&m_lock)) && !thr_got_alarm(&alarmed) && errno == EINTR) ; thr_end_alarm(&alarmed); if (value != -1) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 81604a7ecfd..12249d2292c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -48,6 +48,7 @@ char *sql_strmake(const char *str,uint len); gptr sql_memdup(const void * ptr,unsigned size); void sql_element_free(void *ptr); void kill_one_thread(THD *thd, ulong id); +int net_request_file(NET* net, const char* fname); char* query_table_status(THD *thd,const char *db,const char *table_name); #define x_free(A) { my_free((gptr) (A),MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); } @@ -159,8 +160,8 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define OPTION_LOW_PRIORITY_UPDATES 8192 #define OPTION_WARNINGS 16384 #define OPTION_AUTO_IS_NULL 32768 -#define OPTION_ANSI_MODE 65536L -#define OPTION_SAFE_UPDATES OPTION_ANSI_MODE*2 +#define OPTION_FOUND_COMMENT 65536L +#define OPTION_SAFE_UPDATES OPTION_FOUND_COMMENT*2 #define OPTION_BUFFER_RESULT OPTION_SAFE_UPDATES*2 #define OPTION_BIN_LOG OPTION_BUFFER_RESULT*2 #define OPTION_NOT_AUTO_COMMIT OPTION_BIN_LOG*2 @@ -180,7 +181,7 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define SELECT_NO_UNLOCK (QUERY_NO_GOOD_INDEX_USED*2) #define TMP_TABLE_ALL_COLUMNS (SELECT_NO_UNLOCK*2) - +/* Bits for different SQL modes modes (including ANSI mode) */ #define MODE_REAL_AS_FLOAT 1 #define MODE_PIPES_AS_CONCAT 2 #define MODE_ANSI_QUOTES 4 @@ -250,7 +251,33 @@ inline THD *_current_thd(void) #include "item.h" #include "sql_class.h" #include "opt_range.h" + +#ifdef HAVE_QUERY_CACHE #include "sql_cache.h" +#define query_cache_store_query(A, B) query_cache.store_query(A, B) +#define query_cache_destroy() query_cache.destroy() +#define query_cache_result_size_limit(A) query_cache.result_size_limit(A) +#define query_cache_resize(A) query_cache.resize(A) +#define query_cache_invalidate3(A, B, C) query_cache.invalidate(A, B, C) +#define query_cache_invalidate1(A) query_cache.invalidate(A) +#define query_cache_send_result_to_client(A, B, C) \ + query_cache.send_result_to_client(A, B, C) +#define query_cache_invalidate_by_MyISAM_filename_ref \ + &query_cache_invalidate_by_MyISAM_filename +#else +#define query_cache_store_query(A, B) +#define query_cache_destroy() +#define query_cache_result_size_limit(A) +#define query_cache_resize(A) +#define query_cache_invalidate3(A, B, C) +#define query_cache_invalidate1(A) +#define query_cache_send_result_to_client(A, B, C) 0 +#define query_cache_invalidate_by_MyISAM_filename_ref NULL + +#define query_cache_abort(A) +#define query_cache_end_of_result(A) +#define query_cache_invalidate_by_MyISAM_filename_ref NULL +#endif /*HAVE_QUERY_CACHE*/ int mysql_create_db(THD *thd, char *db, uint create_info, bool silent); int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent); @@ -582,11 +609,13 @@ extern pthread_cond_t COND_refresh,COND_thread_count; extern pthread_attr_t connection_attrib; extern bool opt_endinfo, using_udf_functions, locked_in_memory, opt_using_transactions, use_temp_pool, mysql_embedded; +extern bool opt_local_infile; extern char f_fyllchar; extern ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count, ha_read_key_count, ha_read_next_count, ha_read_prev_count, ha_read_first_count, ha_read_last_count, - ha_read_rnd_count, ha_read_rnd_next_count; + ha_read_rnd_count, ha_read_rnd_next_count, + ha_commit_count, ha_rollback_count; extern MY_BITMAP temp_pool; extern uchar *days_in_month; extern DATE_FORMAT dayord; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index d5c3e6f1c6f..f787181174a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -38,7 +38,17 @@ #define ONE_THREAD #endif -/* do stack traces are only supported on linux intel */ +#ifdef SAFEMALLOC +#define SHUTDOWN_THD shutdown_th=pthread_self(); +#define MAIN_THD main_th=pthread_self(); +#define SIGNAL_THD signal_th=pthread_self(); +#else +#define SHUTDOWN_THD +#define MAIN_THD +#define SIGNAL_THD +#endif + +/* stack traces are only supported on linux intel */ #if defined(__linux__) && defined(__i386__) && defined(USE_PSTACK) #define HAVE_STACK_TRACE_ON_SEGV #include "../pstack/pstack.h" @@ -162,6 +172,7 @@ static SECURITY_DESCRIPTOR sdPipeDescriptor; static HANDLE hPipe = INVALID_HANDLE_VALUE; static pthread_cond_t COND_handler_count; static uint handler_count; +static bool opt_enable_named_pipe = 0; #endif #ifdef __WIN__ static bool opt_console=0,start_mode=0; @@ -209,7 +220,11 @@ SHOW_COMP_OPTION have_openssl=SHOW_OPTION_YES; SHOW_COMP_OPTION have_openssl=SHOW_OPTION_NO; #endif SHOW_COMP_OPTION have_symlink=SHOW_OPTION_YES; - +#ifdef HAVE_QUERY_CACHE +SHOW_COMP_OPTION have_query_cache=SHOW_OPTION_YES; +#else +SHOW_COMP_OPTION have_query_cache=SHOW_OPTION_NO; +#endif bool opt_skip_slave_start = 0; // If set, slave is not autostarted static bool opt_do_pstack = 0; @@ -276,7 +291,11 @@ ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, ulong com_stat[(uint) SQLCOM_END], com_other; ulong slave_net_timeout; ulong thread_cache_size=0, binlog_cache_size=0, max_binlog_cache_size=0; -ulong query_cache_size=0, query_cache_limit=0, query_cache_startup_type=1; +#ifdef HAVE_QUERY_CACHE +ulong query_cache_size=0, query_cache_limit=0, query_cache_startup_type=1; +Query_cache query_cache; +#endif + volatile ulong cached_thread_count=0; // replication parameters, if master_host is not NULL, we are a slave @@ -297,6 +316,7 @@ ulong bytes_sent = 0L, bytes_received = 0L; bool opt_endinfo,using_udf_functions,low_priority_updates, locked_in_memory; bool opt_using_transactions, using_update_log, opt_warnings=0; +bool opt_local_infile=1; bool volatile abort_loop,select_thread_in_use,grant_option; bool volatile ready_to_exit,shutdown_in_progress; ulong refresh_version=1L,flush_version=1L; /* Increments on each reload */ @@ -369,8 +389,6 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, LOCK_server_id, LOCK_user_conn, LOCK_slave_list, LOCK_active_mi; -Query_cache query_cache; - pthread_cond_t COND_refresh,COND_thread_count,COND_binlog_update, COND_slave_stopped, COND_slave_start; pthread_cond_t COND_thread_cache,COND_flush_thread_cache; @@ -481,13 +499,27 @@ static void close_connections(void) } } #ifdef __NT__ - if ( hPipe != INVALID_HANDLE_VALUE ) + if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) { - HANDLE hTempPipe = &hPipe; + HANDLE temp; DBUG_PRINT( "quit", ("Closing named pipes") ); - hPipe = INVALID_HANDLE_VALUE; - DisconnectNamedPipe( hTempPipe ); - CloseHandle( hTempPipe ); + + /* Create connection to the handle named pipe handler to break the loop */ + if ((temp = CreateFile(szPipeName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL )) != INVALID_HANDLE_VALUE) + { + WaitNamedPipe(szPipeName, 1000); + DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; + SetNamedPipeHandleState(temp, &dwMode, NULL, NULL); + CancelIo(temp); + DisconnectNamedPipe(temp); + CloseHandle(temp); + } } #endif #ifdef HAVE_SYS_UN_H @@ -679,6 +711,7 @@ static void __cdecl kill_server(int sig_ptr) sql_print_error(ER(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */ #if defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2) + SHUTDOWN_THD; my_thread_init(); // If this is a new thread #endif close_connections(); @@ -694,6 +727,7 @@ static void __cdecl kill_server(int sig_ptr) #ifdef USE_ONE_SIGNAL_HAND static pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) { + SHUTDOWN_THD; my_thread_init(); // Initialize new thread kill_server(0); my_thread_end(); // Normally never reached @@ -708,8 +742,9 @@ static pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) static sig_handler print_signal_warning(int sig) { - sql_print_error("Warning: Got signal %d from thread %d", - sig,my_thread_id()); + if (opt_warnings) + sql_print_error("Warning: Got signal %d from thread %d", + sig,my_thread_id()); #ifdef DONT_REMEMBER_SIGNAL sigset(sig,print_signal_warning); /* int. thread system calls */ #endif @@ -749,7 +784,7 @@ void clean_up(bool print_message) bitmap_free(&slave_error_mask); acl_free(1); grant_free(); - query_cache.destroy(); + query_cache_destroy(); table_cache_free(); hostname_cache_free(); item_user_lock_free(); @@ -769,8 +804,10 @@ void clean_up(bool print_message) my_free(opt_ssl_ca,MYF(MY_ALLOW_ZERO_PTR)); my_free(opt_ssl_capath,MYF(MY_ALLOW_ZERO_PTR)); my_free(opt_ssl_cipher,MYF(MY_ALLOW_ZERO_PTR)); + my_free((gptr) ssl_acceptor_fd, MYF(MY_ALLOW_ZERO_PTR)); opt_ssl_key=opt_ssl_cert=opt_ssl_ca=opt_ssl_capath=0; #endif /* HAVE_OPENSSL */ + free_defaults(defaults_argv); my_free(charsets_list, MYF(MY_ALLOW_ZERO_PTR)); my_free(allocated_mysql_tmpdir,MYF(MY_ALLOW_ZERO_PTR)); @@ -856,20 +893,33 @@ static void set_user(const char *user) if (!strcmp(user,"root")) return; // Avoid problem with dynamic libraries + uid_t uid; if (!(ent = getpwnam(user))) { - fprintf(stderr,"Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); - unireg_abort(1); + // allow a numeric uid to be used + const char *pos; + for (pos=user; isdigit(*pos); pos++) ; + if (*pos) // Not numeric id + { + fprintf(stderr,"Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); + unireg_abort(1); + } + uid=atoi(user); // Use numberic uid } + else + { #ifdef HAVE_INITGROUPS - initgroups((char*) user,ent->pw_gid); + initgroups((char*) user,ent->pw_gid); #endif - if (setgid(ent->pw_gid) == -1) - { - sql_perror("setgid"); - unireg_abort(1); + if (setgid(ent->pw_gid) == -1) + { + sql_perror("setgid"); + unireg_abort(1); + } + uid=ent->pw_uid; } - if (setuid(ent->pw_uid) == -1) + + if (setuid(uid) == -1) { sql_perror("setuid"); unireg_abort(1); @@ -939,18 +989,21 @@ static void server_init(void) unireg_abort(1); } if (listen(ip_sock,(int) back_log) < 0) - sql_print_error("Warning: listen() on TCP/IP failed with error %d", + { + sql_print_error("Error: listen() on TCP/IP failed with error %d", socket_errno); + unireg_abort(1); + } } if (mysqld_chroot) set_root(mysqld_chroot); - - set_user(mysqld_user); // set_user now takes care of mysqld_user==NULL + set_user(mysqld_user); // Works also with mysqld_user==NULL #ifdef __NT__ /* create named pipe */ - if (Service.IsNT() && mysql_unix_port[0] && !opt_bootstrap) + if (Service.IsNT() && mysql_unix_port[0] && !opt_bootstrap && + opt_enable_named_pipe) { sprintf( szPipeName, "\\\\.\\pipe\\%s", mysql_unix_port ); ZeroMemory( &saPipeSecurity, sizeof(saPipeSecurity) ); @@ -1221,6 +1274,7 @@ static void init_signals(void) signal(SIGALRM, SIG_IGN); signal(SIGBREAK,SIG_IGN); signal_thread = pthread_self(); + SIGNAL_THD; } static void start_signal_handler(void) @@ -1346,6 +1400,7 @@ static void init_signals(void) sigaction(SIGBUS, &sa, NULL); #endif sigaction(SIGILL, &sa, NULL); + sigaction(SIGFPE, &sa, NULL); } (void) sigemptyset(&set); #ifdef THREAD_SPECIFIC_SIGPIPE @@ -1413,7 +1468,7 @@ static void *signal_hand(void *arg __attribute__((unused))) int sig; my_thread_init(); // Init new thread DBUG_ENTER("signal_hand"); - + SIGNAL_THD; /* Setup alarm handler */ init_thr_alarm(max_connections+max_insert_delayed_threads); #if SIGINT != THR_KILL_SIGNAL @@ -1468,7 +1523,10 @@ static void *signal_hand(void *arg __attribute__((unused))) else while ((error=my_sigwait(&set,&sig)) == EINTR) ; if (cleanup_done) + { + my_thread_end(); pthread_exit(0); // Safety + } switch (sig) { case SIGTERM: case SIGQUIT: @@ -1562,6 +1620,7 @@ int uname(struct utsname *a) pthread_handler_decl(handle_shutdown,arg) { MSG msg; + SHUTDOWN_THD; my_thread_init(); /* this call should create the message queue for this thread */ @@ -1588,6 +1647,7 @@ int __stdcall handle_kill(ulong ctrl_type) #ifdef OS2 pthread_handler_decl(handle_shutdown,arg) { + SHUTDOWN_THD; my_thread_init(); // wait semaphore @@ -1659,6 +1719,7 @@ int main(int argc, char **argv) my_umask=0660; // Default umask for new files my_umask_dir=0700; // Default umask for new directories + MAIN_THD; MY_INIT(argv[0]); // init my_sys library & pthreads tzset(); // Set tzname @@ -1689,7 +1750,7 @@ int main(int argc, char **argv) if (gethostname(glob_hostname,sizeof(glob_hostname)-4) < 0) strmov(glob_hostname,"mysql"); - strmov(pidfile_name,glob_hostname); + strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5); strmov(strcend(pidfile_name,'.'),".pid"); // Add extension #ifndef DBUG_OFF strxmov(strend(server_version),MYSQL_SERVER_SUFFIX,"-debug",NullS); @@ -1760,7 +1821,8 @@ int main(int argc, char **argv) if (opt_use_ssl) { ssl_acceptor_fd = new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert, - opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); + opt_ssl_ca, opt_ssl_capath, + opt_ssl_cipher); DBUG_PRINT("info",("ssl_acceptor_fd: %p",ssl_acceptor_fd)); if (!ssl_acceptor_fd) opt_use_ssl = 0; @@ -1832,8 +1894,8 @@ int main(int argc, char **argv) server_init(); table_cache_init(); hostname_cache_init(); - query_cache.result_size_limit(query_cache_limit); - query_cache.resize(query_cache_size); + query_cache_result_size_limit(query_cache_limit); + query_cache_resize(query_cache_size); randominit(&sql_rand,(ulong) start_time,(ulong) start_time/2); reset_floating_point_exceptions(); init_thr_lock(); @@ -1849,48 +1911,17 @@ int main(int argc, char **argv) using_update_log=1; } - init_slave(); - - if (opt_bin_log && !server_id) - { - server_id= !master_host ? 1 : 2; - switch (server_id) { -#ifdef EXTRA_DEBUG - case 1: - sql_print_error("\ -Warning: You have enabled the binary log, but you haven't set server-id:\n\ -Updates will be logged to the binary log, but connections to slaves will\n\ -not be accepted."); - break; -#endif - case 2: - sql_print_error("\ -Warning: You should set server-id to a non-0 value if master_host is set.\n\ -The server will not act as a slave."); - break; - } - } - if (opt_bin_log) - { - if (!opt_bin_logname) - { - char tmp[FN_REFLEN]; - /* TODO: The following should be using fn_format(); We just need to - first change fn_format() to cut the file name if it's too long. - */ - strmake(tmp,glob_hostname,FN_REFLEN-5); - strmov(strcend(tmp,'.'),"-bin"); - opt_bin_logname=my_strdup(tmp,MYF(MY_WME)); - } - mysql_bin_log.set_index_file_name(opt_binlog_index_name); - open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin", - LOG_BIN); - using_update_log=1; - } - if (opt_slow_log) open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log", LOG_NORMAL); +#ifdef __WIN__ +#define MYSQL_ERR_FILE "mysql.err" + if (!opt_console) + { + freopen(MYSQL_ERR_FILE,"a+",stdout); + freopen(MYSQL_ERR_FILE,"a+",stderr); + } +#endif if (ha_init()) { sql_print_error("Can't init databases"); @@ -1916,13 +1947,8 @@ The server will not act as a slave."); ft_init_stopwords(ft_precompiled_stopwords); #ifdef __WIN__ -#define MYSQL_ERR_FILE "mysql.err" if (!opt_console) - { - freopen(MYSQL_ERR_FILE,"a+",stdout); - freopen(MYSQL_ERR_FILE,"a+",stderr); FreeConsole(); // Remove window - } #endif /* @@ -1955,6 +1981,46 @@ The server will not act as a slave."); if (!opt_noacl) udf_init(); #endif + /* init_slave() must be called after the thread keys are created */ + init_slave(); + + if (opt_bin_log && !server_id) + { + server_id= !master_host ? 1 : 2; + switch (server_id) { +#ifdef EXTRA_DEBUG + case 1: + sql_print_error("\ +Warning: You have enabled the binary log, but you haven't set server-id:\n\ +Updates will be logged to the binary log, but connections to slaves will\n\ +not be accepted."); + break; +#endif + case 2: + sql_print_error("\ +Warning: You should set server-id to a non-0 value if master_host is set.\n\ +The server will not act as a slave."); + break; + } + } + if (opt_bin_log) + { + if (!opt_bin_logname) + { + char tmp[FN_REFLEN]; + /* TODO: The following should be using fn_format(); We just need to + first change fn_format() to cut the file name if it's too long. + */ + strmake(tmp,glob_hostname,FN_REFLEN-5); + strmov(strcend(tmp,'.'),"-bin"); + opt_bin_logname=my_strdup(tmp,MYF(MY_WME)); + } + mysql_bin_log.set_index_file_name(opt_binlog_index_name); + open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin", + LOG_BIN); + using_update_log=1; + } + if (opt_bootstrap) { @@ -2006,9 +2072,11 @@ The server will not act as a slave."); fflush(stdout); #ifdef __NT__ - if (hPipe == INVALID_HANDLE_VALUE && !have_tcpip) + if (hPipe == INVALID_HANDLE_VALUE && + (!have_tcpip || opt_disable_networking)) { - sql_print_error("TCP/IP must be installed on Win98 platforms"); + sql_print_error("TCP/IP or --enable-named-pipe should be configured on NT OS"); + unireg_abort(1); } else { @@ -2017,7 +2085,7 @@ The server will not act as a slave."); { pthread_t hThread; handler_count=0; - if ( hPipe != INVALID_HANDLE_VALUE ) + if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) { handler_count++; if (pthread_create(&hThread,&connection_attrib, @@ -2067,42 +2135,26 @@ The server will not act as a slave."); #ifdef EXTRA_DEBUG2 sql_print_error("After lock_thread_count"); #endif -#else -#if !defined(EMBEDDED_LIBRARY) - if (Service.IsNT()) - { - if(start_mode) - { - if (WaitForSingleObject(hEventShutdown,1000)==WAIT_TIMEOUT) - Service.Stop(); - } - else - { - Service.SetShutdownEvent(0); - if(hEventShutdown) CloseHandle(hEventShutdown); - } - } - else - { - Service.SetShutdownEvent(0); - if(hEventShutdown) CloseHandle(hEventShutdown); - } -#endif -#endif -#ifdef HAVE_OPENSSL - my_free((gptr)ssl_acceptor_fd,MYF(MY_ALLOW_ZERO_PTR)); -#endif /* HAVE_OPENSSL */ +#endif /* __WIN__ */ + /* Wait until cleanup is done */ (void) pthread_mutex_lock(&LOCK_thread_count); - DBUG_PRINT("quit", ("Got thread_count mutex for clean up wait")); - while (!ready_to_exit) { - DBUG_PRINT("quit", ("not yet ready to exit")); pthread_cond_wait(&COND_thread_count,&LOCK_thread_count); } - DBUG_PRINT("quit", ("ready to exit")); (void) pthread_mutex_unlock(&LOCK_thread_count); + +#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) + if (Service.IsNT() && start_mode) + Service.Stop(); + else + { + Service.SetShutdownEvent(0); + if (hEventShutdown) + CloseHandle(hEventShutdown); + } +#endif my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(0); return(0); /* purecov: deadcode */ @@ -2441,6 +2493,7 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) struct request_info req; signal(SIGCHLD, SIG_DFL); request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, new_sock, NULL); +#ifndef __linux__ fromhost(&req); if (!hosts_access(&req)) { @@ -2450,6 +2503,12 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) clean_exit() - same stupid thing ... */ syslog(deny_severity, "refused connect from %s", eval_client(&req)); +#else + fromhost(); + if (!hosts_access()) + { + syslog(deny_severity, "refused connect from %s", eval_client()); +#endif if (req.sink) ((void (*)(int))req.sink)(req.fd); @@ -2543,9 +2602,9 @@ pthread_handler_decl(handle_connections_namedpipes,arg) fConnected = ConnectNamedPipe( hPipe, NULL ); if (abort_loop) break; - if ( !fConnected ) + if (!fConnected) fConnected = GetLastError() == ERROR_PIPE_CONNECTED; - if ( !fConnected ) + if (!fConnected) { CloseHandle( hPipe ); if ((hPipe = CreateNamedPipe(szPipeName, @@ -2583,7 +2642,7 @@ pthread_handler_decl(handle_connections_namedpipes,arg) continue; // We have to try again } - if ( !(thd = new THD)) + if (!(thd = new THD)) { DisconnectNamedPipe( hConnectedPipe ); CloseHandle( hConnectedPipe ); @@ -2667,13 +2726,14 @@ enum options { OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL, OPT_SAFE_USER_CREATE, OPT_SQL_MODE, + OPT_HAVE_NAMED_PIPE, OPT_DO_PSTACK, OPT_REPORT_HOST, OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT, OPT_SHOW_SLAVE_AUTH_INFO, OPT_OLD_RPL_COMPAT, OPT_SLAVE_LOAD_TMPDIR, OPT_NO_MIX_TYPE, OPT_RPL_RECOVERY_RANK,OPT_INIT_RPL_ROLE, OPT_RELAY_LOG, OPT_RELAY_LOG_INDEX, OPT_RELAY_LOG_INFO_FILE, - OPT_SLAVE_SKIP_ERRORS, OPT_DES_KEY_FILE + OPT_SLAVE_SKIP_ERRORS, OPT_DES_KEY_FILE, OPT_LOCAL_INFILE }; static struct option long_options[] = { @@ -2707,6 +2767,7 @@ static struct option long_options[] = { {"delay-key-write-for-all-tables", no_argument, 0, (int) OPT_DELAY_KEY_WRITE}, {"enable-locking", no_argument, 0, (int) OPT_ENABLE_LOCK}, + {"enable-named-pipe", no_argument, 0, (int) OPT_HAVE_NAMED_PIPE}, {"enable-pstack", no_argument, 0, (int) OPT_DO_PSTACK}, {"exit-info", optional_argument, 0, 'T'}, {"flush", no_argument, 0, (int) OPT_FLUSH}, @@ -2735,6 +2796,7 @@ static struct option long_options[] = { {"init-file", required_argument, 0, (int) OPT_INIT_FILE}, {"log", optional_argument, 0, 'l'}, {"language", required_argument, 0, 'L'}, + {"local-infile", optional_argument, 0, (int) OPT_LOCAL_INFILE}, {"log-bin", optional_argument, 0, (int) OPT_BIN_LOG}, {"log-bin-index", required_argument, 0, (int) OPT_BIN_LOG_INDEX}, {"log-isam", optional_argument, 0, (int) OPT_ISAM_LOG}, @@ -2970,16 +3032,20 @@ CHANGEABLE_VAR changeable_vars[] = { 0, 0, 65535, 0, 1}, { "query_buffer_size", (long*) &query_buff_size, 0, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, IO_SIZE }, +#ifdef HAVE_QUERY_CACHE { "query_cache_limit", (long*) &query_cache_limit, 1024*1024L, 0, ULONG_MAX, 0, 1}, { "query_cache_size", (long*) &query_cache_size, 0, 0, ULONG_MAX, 0, 1}, { "query_cache_startup_type",(long*) &query_cache_startup_type, 1, 0, 2, 0, 1}, +#endif /*HAVE_QUERY_CACHE*/ { "record_buffer", (long*) &my_default_record_cache_size, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE }, { "record_rnd_buffer", (long*) &record_rnd_cache_size, 0, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE }, + { "relay_log_space_limit", (long*) &relay_log_space_limit, 0L, 0L,ULONG_MAX, + 0, 1}, { "slave_net_timeout", (long*) &slave_net_timeout, SLAVE_NET_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, { "slow_launch_time", (long*) &slow_launch_time, @@ -3037,6 +3103,7 @@ struct show_var_st init_vars[]= { {"have_raid", (char*) &have_raid, SHOW_HAVE}, {"have_symlink", (char*) &have_symlink, SHOW_HAVE}, {"have_openssl", (char*) &have_openssl, SHOW_HAVE}, + {"have_query_cache", (char*) &have_query_cache, SHOW_HAVE}, {"init_file", (char*) &opt_init_file, SHOW_CHAR_PTR}, #ifdef HAVE_INNOBASE_DB {"innodb_additional_mem_pool_size", (char*) &innobase_additional_mem_pool_size, SHOW_LONG }, @@ -3070,7 +3137,7 @@ struct show_var_st init_vars[]= { {"log_update", (char*) &opt_update_log, SHOW_BOOL}, {"log_bin", (char*) &opt_bin_log, SHOW_BOOL}, {"log_slave_updates", (char*) &opt_log_slave_updates, SHOW_BOOL}, - {"log_long_queries", (char*) &opt_slow_log, SHOW_BOOL}, + {"log_slow_queries", (char*) &opt_slow_log, SHOW_BOOL}, {"long_query_time", (char*) &long_query_time, SHOW_LONG}, {"low_priority_updates", (char*) &low_priority_updates, SHOW_BOOL}, {"lower_case_table_names", (char*) &lower_case_table_names, SHOW_LONG}, @@ -3092,6 +3159,9 @@ struct show_var_st init_vars[]= { {"myisam_max_sort_file_size",(char*) &myisam_max_sort_file_size, SHOW_LONG}, {"myisam_recover_options", (char*) &myisam_recover_options_str, SHOW_CHAR_PTR}, {"myisam_sort_buffer_size", (char*) &myisam_sort_buffer_size, SHOW_LONG}, +#ifdef __NT__ + {"named_pipe", (char*) &opt_enable_named_pipe, SHOW_BOOL}, +#endif {"net_buffer_length", (char*) &net_buffer_length, SHOW_LONG}, {"net_read_timeout", (char*) &net_read_timeout, SHOW_LONG}, {"net_retry_count", (char*) &mysqld_net_retry_count, SHOW_LONG}, @@ -3104,9 +3174,11 @@ struct show_var_st init_vars[]= { {"record_rnd_buffer", (char*) &record_rnd_cache_size, SHOW_LONG}, {"rpl_recovery_rank", (char*) &rpl_recovery_rank, SHOW_LONG}, {"query_buffer_size", (char*) &query_buff_size, SHOW_LONG}, +#ifdef HAVE_QUERY_CACHE {"query_cache_limit", (char*) &query_cache.query_cache_limit, SHOW_LONG}, {"query_cache_size", (char*) &query_cache.query_cache_size, SHOW_LONG}, {"query_cache_startup_type",(char*) &query_cache_startup_type, SHOW_LONG}, +#endif /*HAVE_QUERY_CACHE*/ {"safe_show_database", (char*) &opt_safe_show_db, SHOW_BOOL}, {"server_id", (char*) &server_id, SHOW_LONG}, {"slave_net_timeout", (char*) &slave_net_timeout, SHOW_LONG}, @@ -3213,6 +3285,7 @@ struct show_var_st status_vars[]= { {"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG}, {"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG}, {"Flush_commands", (char*) &refresh_version, SHOW_LONG_CONST}, + {"Handler_commit", (char*) &ha_commit_count, SHOW_LONG}, {"Handler_delete", (char*) &ha_delete_count, SHOW_LONG}, {"Handler_read_first", (char*) &ha_read_first_count, SHOW_LONG}, {"Handler_read_key", (char*) &ha_read_key_count, SHOW_LONG}, @@ -3220,6 +3293,7 @@ struct show_var_st status_vars[]= { {"Handler_read_prev", (char*) &ha_read_prev_count, SHOW_LONG}, {"Handler_read_rnd", (char*) &ha_read_rnd_count, SHOW_LONG}, {"Handler_read_rnd_next", (char*) &ha_read_rnd_next_count, SHOW_LONG}, + {"Handler_rollback", (char*) &ha_rollback_count, SHOW_LONG}, {"Handler_update", (char*) &ha_update_count, SHOW_LONG}, {"Handler_write", (char*) &ha_write_count, SHOW_LONG}, {"Key_blocks_used", (char*) &_my_blocks_used, SHOW_LONG_CONST}, @@ -3235,6 +3309,7 @@ struct show_var_st status_vars[]= { {"Open_streams", (char*) &my_stream_opened, SHOW_INT_CONST}, {"Opened_tables", (char*) &opened_tables, SHOW_LONG}, {"Questions", (char*) 0, SHOW_QUESTION}, +#ifdef HAVE_QUERY_CACHE {"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_CONST}, {"Qcache_inserts", (char*) &query_cache.inserts, SHOW_LONG}, {"Qcache_hits", (char*) &query_cache.hits, SHOW_LONG}, @@ -3245,6 +3320,7 @@ struct show_var_st status_vars[]= { SHOW_LONG_CONST}, {"Qcache_total_blocks", (char*) &query_cache.total_blocks, SHOW_LONG_CONST}, +#endif /*HAVE_QUERY_CACHE*/ {"Rpl_status", (char*) 0, SHOW_RPL_STATUS}, {"Select_full_join", (char*) &select_full_join_count, SHOW_LONG}, {"Select_full_range_join", (char*) &select_full_range_join_count, SHOW_LONG}, @@ -3309,10 +3385,11 @@ static void use_help(void) static void usage(void) { print_version(); - puts("Copyright (C) 2000 MySQL AB & MySQL Finland AB, by Monty and others"); - puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,"); - puts("and you are welcome to modify and redistribute it under the GPL license\n"); - puts("Starts the MySQL server\n"); + puts("\ +Copyright (C) 2000 MySQL AB, by Monty and others\n\ +This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\ +and you are welcome to modify and redistribute it under the GPL license\n\ +Starts the MySQL server\n"); printf("Usage: %s [OPTIONS]\n", my_progname); puts("\n\ @@ -3358,6 +3435,7 @@ static void usage(void) --init-file=file Read SQL commands from this file at startup\n\ -L, --language=... Client error messages in given language. May be\n\ given as a full path\n\ + --local-infile=[1|0] Enable/disable LOAD DATA LOCAL INFILE\n\ -l, --log[=file] Log connections and queries to file\n\ --log-bin[=file] Log queries in new binary format (for replication)\n\ --log-bin-index=file File that holds the names for last binary log files\n\ @@ -3422,10 +3500,12 @@ static void usage(void) -W, --warnings Log some not critical warnings to the log file\n"); #ifdef __WIN__ puts("NT and Win32 specific options:\n\ - --console Don't remove the console window\n\ - --install Install mysqld as a service (NT)\n\ - --remove Remove mysqld from the service list (NT)\n\ - --standalone Dummy option to start as a standalone program (NT)\ + --console Don't remove the console window\n\ + --install Install the default service (NT)\n\ + --install-manual Install the default service started manually (NT)\n\ + --remove Remove the default service from the service list (NT)\n\ + --enable-named-pipe Enable the named pipe (NT)\n\ + --standalone Dummy option to start as a standalone program (NT)\ "); #ifdef USE_SYMDIR puts("--use-symbolic-links Enable symbolic link support"); @@ -3512,9 +3592,10 @@ static void set_options(void) opt_specialflag |= SPECIAL_NO_PRIOR; #endif - (void) strmov( default_charset, MYSQL_CHARSET); - (void) strmov( language, LANGUAGE); - (void) strmov( mysql_real_data_home, get_relative_path(DATADIR)); + (void) strmake(default_charset, MYSQL_CHARSET, sizeof(default_charset)-1); + (void) strmake(language, LANGUAGE, sizeof(language)-1); + (void) strmake(mysql_real_data_home, get_relative_path(DATADIR), + sizeof(mysql_real_data_home-1)); #ifdef __WIN__ /* Allow Win32 users to move MySQL anywhere */ { @@ -3525,9 +3606,9 @@ static void set_options(void) } #else const char *tmpenv; - if ( !(tmpenv = getenv("MY_BASEDIR_VERSION"))) + if (!(tmpenv = getenv("MY_BASEDIR_VERSION"))) tmpenv = DEFAULT_MYSQL_HOME; - (void) strmov( mysql_home, tmpenv ); + (void) strmake(mysql_home, tmpenv, sizeof(mysql_home)-1); #endif #if defined( HAVE_mit_thread ) || defined( __WIN__ ) || defined( HAVE_LINUXTHREADS ) @@ -3573,17 +3654,17 @@ static void get_options(int argc,char **argv) default_tx_isolation= ISO_SERIALIZABLE; break; case 'b': - strmov(mysql_home,optarg); + strmake(mysql_home,optarg,sizeof(mysql_home)-1); break; case 'l': opt_log=1; opt_logname=optarg; // Use hostname.log if null break; case 'h': - strmov(mysql_real_data_home,optarg); + strmake(mysql_real_data_home,optarg, sizeof(mysql_real_data_home)-1); break; case 'L': - strmov(language,optarg); + strmake(language, optarg, sizeof(language)-1); break; case 'n': opt_specialflag|= SPECIAL_NEW_FUNC; @@ -3601,6 +3682,9 @@ static void get_options(int argc,char **argv) case 'P': mysql_port= (unsigned int) atoi(optarg); break; + case OPT_LOCAL_INFILE: + opt_local_infile= test(!optarg || atoi(optarg) != 0); + break; case OPT_SLAVE_SKIP_ERRORS: init_slave_skip_errors(optarg); break; @@ -3845,7 +3929,9 @@ static void get_options(int argc,char **argv) my_use_symdir=0; have_symlink=SHOW_OPTION_DISABLED; ha_open_options&= ~HA_OPEN_ABORT_IF_CRASHED; +#ifdef HAVE_QUERY_CACHE query_cache_size=0; +#endif break; case (int) OPT_SAFE: opt_specialflag|= SPECIAL_SAFE_MODE; @@ -3938,11 +4024,16 @@ static void get_options(int argc,char **argv) } break; case (int) OPT_PID_FILE: - strmov(pidfile_name,optarg); + strmake(pidfile_name, optarg, sizeof(pidfile_name)-1); break; case (int) OPT_INIT_FILE: opt_init_file=optarg; break; + case (int) OPT_HAVE_NAMED_PIPE: +#if __NT__ + opt_enable_named_pipe=1; +#endif + break; #ifdef __WIN__ case (int) OPT_STANDALONE: /* Dummy option for NT */ break; @@ -3988,10 +4079,10 @@ static void get_options(int argc,char **argv) myisam_delay_key_write=0; break; case 'C': - strmov(default_charset,optarg); + strmake(default_charset, optarg, sizeof(default_charset)-1); break; case OPT_CHARSETS_DIR: - strmov(mysql_charsets_dir, optarg); + strmake(mysql_charsets_dir, optarg, sizeof(mysql_charsets_dir)-1); charsets_dir = mysql_charsets_dir; break; #ifdef TO_BE_DONE @@ -4263,16 +4354,17 @@ static void fix_paths(void) char buff[FN_REFLEN],*sharedir=get_relative_path(SHAREDIR); if (test_if_hard_path(sharedir)) - strmov(buff,sharedir); /* purecov: tested */ + strmake(buff,sharedir,sizeof(buff)-1); /* purecov: tested */ else - strxmov(buff,mysql_home,sharedir,NullS); + strxnmov(buff,sizeof(buff)-1,mysql_home,sharedir,NullS); convert_dirname(buff,buff,NullS); (void) my_load_path(language,language,buff); /* If --character-sets-dir isn't given, use shared library dir */ if (charsets_dir != mysql_charsets_dir) { - strmov(strmov(mysql_charsets_dir,buff),CHARSET_DIR); + strxnmov(mysql_charsets_dir, sizeof(mysql_charsets_dir)-1, buff, + CHARSET_DIR, NullS); charsets_dir=mysql_charsets_dir; } diff --git a/sql/net_pkg.cc b/sql/net_pkg.cc index 64c1b07a493..1ab3e18424f 100644 --- a/sql/net_pkg.cc +++ b/sql/net_pkg.cc @@ -108,7 +108,13 @@ net_printf(NET *net, uint errcode, ...) thd->query_error = 1; // if we are here, something is wrong :-) query_cache_abort(net); // Safety va_start(args,errcode); - format=ER(errcode); + /* + The following is needed to make net_printf() work with 0 argument for + errorcode and use the argument after that as the format string. This + is useful for rare errors that are not worth the hassle to put in + errmsg.sys, but at the same time, the message is not fixed text + */ + format=errcode ? ER(errcode) : va_arg(args,char*); offset= net->return_errno ? 2 : 0; text_pos=(char*) net->buff+head_length+offset+1; (void) vsprintf(my_const_cast(char*) (text_pos),format,args); diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 7a1d25e980d..9884adf9b46 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -329,10 +329,12 @@ net_real_write(NET *net,const char *packet,ulong len) my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); -#ifdef MYSQL_SERVER +#ifdef MYSQL_SERVER +#ifdef HAVE_QUERY_CACHE if (net->query_cache_query != 0) query_cache_insert(net, packet, len); #endif +#endif if (net->error == 2) DBUG_RETURN(-1); /* socket can't be used */ @@ -814,3 +816,14 @@ my_net_read(NET *net) #endif /* HAVE_COMPRESS */ return len; } + +int net_request_file(NET* net, const char* fname) +{ + char tmp [FN_REFLEN+1],*end; + DBUG_ENTER("net_request_file"); + tmp[0] = (char) 251; /* NULL_LENGTH */ + end=strnmov(tmp+1,fname,sizeof(tmp)-2); + DBUG_RETURN(my_net_write(net,tmp,(uint) (end-tmp)) || + net_flush(net)); +} + diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc index 6930800982e..8c705a94f55 100644 --- a/sql/nt_servc.cc +++ b/sql/nt_servc.cc @@ -28,7 +28,7 @@ NTService::NTService() //time-out variables nStartTimeOut = 15000; - nStopTimeOut = 15000; + nStopTimeOut = 86400000; nPauseTimeOut = 5000; nResumeTimeOut = 5000; @@ -253,7 +253,7 @@ void NTService::ServiceMain(DWORD argc, LPTSTR *argv) WaitForSingleObject (pService->hExitEvent, INFINITE); // wait for thread to exit - if (WaitForSingleObject (pService->hThreadHandle, 1000) == WAIT_TIMEOUT) + if (WaitForSingleObject (pService->hThreadHandle, INFINITE) == WAIT_TIMEOUT) CloseHandle(pService->hThreadHandle); pService->Exit(0); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index c3f4c91b718..20f198182f4 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2523,13 +2523,13 @@ int QUICK_SELECT::cmp_next(QUICK_RANGE *range) /* - * This is a hack: we inherit from QUICK_SELECT so that we can use the - * get_next() interface, but we have to hold a pointer to the original - * QUICK_SELECT because its data are used all over the place. What - * should be done is to factor out the data that is needed into a base - * class (QUICK_SELECT), and then have two subclasses (_ASC and _DESC) - * which handle the ranges and implement the get_next() function. But - * for now, this seems to work right at least. + This is a hack: we inherit from QUICK_SELECT so that we can use the + get_next() interface, but we have to hold a pointer to the original + QUICK_SELECT because its data are used all over the place. What + should be done is to factor out the data that is needed into a base + class (QUICK_SELECT), and then have two subclasses (_ASC and _DESC) + which handle the ranges and implement the get_next() function. But + for now, this seems to work right at least. */ QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts) @@ -2538,6 +2538,7 @@ QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts) bool not_read_after_key = file->option_flag() & HA_NOT_READ_AFTER_KEY; QUICK_RANGE *r; + it.rewind(); for (r = it++; r; r = it++) { rev_ranges.push_front(r); diff --git a/sql/opt_range.h b/sql/opt_range.h index 83eb10235ea..f48a3936a17 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -77,6 +77,7 @@ public: void reset(void) { next=0; it.rewind(); } int init() { return error=file->index_init(index); } virtual int get_next(); + virtual bool reverse_sorted() { return 0; } int cmp_next(QUICK_RANGE *range); bool unique_key_range(); }; @@ -87,6 +88,7 @@ class QUICK_SELECT_DESC: public QUICK_SELECT public: QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts); int get_next(); + bool reverse_sorted() { return 1; } private: int cmp_prev(QUICK_RANGE *range); bool range_reads_after_key(QUICK_RANGE *range); @@ -96,6 +98,7 @@ private: List_iterator<QUICK_RANGE> rev_it; }; + class SQL_SELECT :public Sql_alloc { public: QUICK_SELECT *quick; // If quick-select used diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 6cc7ef7404b..257418d1682 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -828,6 +828,7 @@ int load_master_data(THD* thd) active_mi->rli.master_log_pos = active_mi->master_log_pos; strnmov(active_mi->rli.master_log_name,active_mi->master_log_name, sizeof(active_mi->rli.master_log_name)); + flush_relay_log_info(&active_mi->rli); pthread_cond_broadcast(&active_mi->rli.data_cond); pthread_mutex_unlock(&active_mi->rli.data_lock); thd->proc_info = "starting slave"; diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am index cb3056b5f5a..c70ac9ccf57 100644 --- a/sql/share/Makefile.am +++ b/sql/share/Makefile.am @@ -5,6 +5,7 @@ dist-hook: test -d $(distdir)/$$dir || mkdir $(distdir)/$$dir; \ $(INSTALL_DATA) $(srcdir)/$$dir/*.* $(distdir)/$$dir; \ done; \ + sleep 1 ; touch $(srcdir)/*/errmsg.sys $(INSTALL_DATA) $(srcdir)/charsets/README $(distdir)/charsets $(INSTALL_DATA) $(srcdir)/charsets/Index $(distdir)/charsets diff --git a/sql/slave.cc b/sql/slave.cc index 8884e5de778..25b29732000 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -29,6 +29,8 @@ bool use_slave_mask = 0; MY_BITMAP slave_error_mask; +typedef bool (*CHECK_KILLED_FUNC)(THD*,void*); + volatile bool slave_sql_running = 0, slave_io_running = 0; char* slave_load_tmpdir = 0; MASTER_INFO main_mi; @@ -40,6 +42,8 @@ bool do_table_inited = 0, ignore_table_inited = 0; bool wild_do_table_inited = 0, wild_ignore_table_inited = 0; bool table_rules_on = 0; static TABLE* save_temporary_tables = 0; +ulong relay_log_space_limit = 0; /* TODO: fix variables to access ulonglong + values and make it ulonglong */ // when slave thread exits, we need to remember the temporary tables so we // can re-use them on slave start @@ -55,16 +59,20 @@ typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE; void skip_load_data_infile(NET* net); static int process_io_rotate(MASTER_INFO* mi, Rotate_log_event* rev); +static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev); static int queue_old_event(MASTER_INFO* mi, const char* buf, uint event_len); -static inline bool slave_killed(THD* thd,MASTER_INFO* mi); -static inline bool slave_killed(THD* thd,RELAY_LOG_INFO* rli); +static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli); +static inline bool io_slave_killed(THD* thd,MASTER_INFO* mi); +static inline bool sql_slave_killed(THD* thd,RELAY_LOG_INFO* rli); +static int count_relay_log_space(RELAY_LOG_INFO* rli); static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type); static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, bool reconnect); -static int safe_sleep(THD* thd, MASTER_INFO* mi, int sec); +static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, + void* thread_killed_arg); static int request_table_dump(MYSQL* mysql, const char* db, const char* table); static int create_table_from_dump(THD* thd, NET* net, const char* db, const char* table_name); @@ -162,6 +170,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, ulonglong pos, bool need_data_lock, const char** errmsg) { + *errmsg=0; if (rli->log_pos_current) return 0; pthread_mutex_t *log_lock=rli->relay_log.get_log_lock(); @@ -259,8 +268,9 @@ void init_slave_skip_errors(char* arg) // are not running int purge_relay_logs(RELAY_LOG_INFO* rli, bool just_reset, const char** errmsg) { + DBUG_ENTER("purge_relay_logs"); if (!rli->inited) - return 0; /* successfully do nothing */ + DBUG_RETURN(0); /* successfully do nothing */ DBUG_ASSERT(rli->slave_running == 0); DBUG_ASSERT(rli->mi->slave_running == 0); int error=0; @@ -277,14 +287,20 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, bool just_reset, const char** errmsg) goto err; } strnmov(rli->relay_log_name,rli->linfo.log_file_name, - sizeof(rli->relay_log_name)); + sizeof(rli->relay_log_name)-1); + rli->log_space_total=4; //just first log with magic number and nothing else rli->relay_log_pos=4; + rli->relay_log.reset_bytes_written(); rli->log_pos_current=0; if (!just_reset) error = init_relay_log_pos(rli,0,0,0/*do not need data lock*/,errmsg); -err: +err: +#ifndef DBUG_OFF + char buf[22]; +#endif + DBUG_PRINT("info",("log_space_total=%s",llstr(rli->log_space_total,buf))); pthread_mutex_unlock(&rli->data_lock); - return error; + DBUG_RETURN(error); } int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) @@ -341,7 +357,14 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, } } DBUG_ASSERT(thd != 0); - KICK_SLAVE(thd); + /* is is criticate to test if the slave is running. Otherwise, we might + be referening freed memory trying to kick it + */ + THD_CHECK_SENTRY(thd); + if (*slave_running) + { + KICK_SLAVE(thd); + } while (*slave_running) { /* there is a small chance that slave thread might miss the first @@ -360,9 +383,12 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, abstime.tv_sec=tv.tv_sec+2; abstime.tv_nsec=tv.tv_usec*1000; #endif + DBUG_ASSERT_LOCK(cond_lock); pthread_cond_timedwait(term_cond, cond_lock, &abstime); if (*slave_running) + { KICK_SLAVE(thd); + } } if (term_lock) pthread_mutex_unlock(term_lock); @@ -603,14 +629,14 @@ void end_slave() free_string_array(&replicate_wild_ignore_table); } -static inline bool slave_killed(THD* thd, MASTER_INFO* mi) +static bool io_slave_killed(THD* thd, MASTER_INFO* mi) { DBUG_ASSERT(mi->io_thd == thd); DBUG_ASSERT(mi->slave_running == 1); // tracking buffer overrun return mi->abort_slave || abort_loop || thd->killed; } -static inline bool slave_killed(THD* thd, RELAY_LOG_INFO* rli) +static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli) { DBUG_ASSERT(rli->sql_thd == thd); DBUG_ASSERT(rli->slave_running == 1);// tracking buffer overrun @@ -654,7 +680,7 @@ char* rewrite_db(char* db) int db_ok(const char* db, I_List<i_string> &do_list, I_List<i_string> &ignore_list ) { - if(do_list.is_empty() && ignore_list.is_empty()) + if (do_list.is_empty() && ignore_list.is_empty()) return 1; // ok to replicate if the user puts no constraints // if the user has specified restrictions on which databases to replicate @@ -854,8 +880,7 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, } check_opt.init(); - check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM; - check_opt.quick = 1; + check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK; thd->proc_info = "Rebuilding the index on master dump table"; // we do not want repair() to spam us with messages // just send them to the error log, and report the failure in case of @@ -939,14 +964,14 @@ void end_master_info(MASTER_INFO* mi) int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) { + DBUG_ENTER("init_relay_log_info"); if (rli->inited) - return 0; + DBUG_RETURN(0); MY_STAT stat_area; char fname[FN_REFLEN+128]; int info_fd; const char* msg = 0; int error = 0; - fn_format(fname, info_fname, mysql_data_home, "", 4+32); pthread_mutex_lock(&rli->data_lock); @@ -955,6 +980,10 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) rli->cur_log_fd = -1; rli->slave_skip_counter=0; rli->log_pos_current=0; + rli->abort_pos_wait=0; + rli->skip_log_purge=0; + rli->log_space_limit = relay_log_space_limit; + rli->log_space_total = 0; // TODO: make this work with multi-master if (!opt_relay_logname) { @@ -986,7 +1015,7 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) my_close(info_fd, MYF(0)); rli->info_fd=-1; pthread_mutex_unlock(&rli->data_lock); - return 1; + DBUG_RETURN(1); } if (init_relay_log_pos(rli,"",4,0/*no data mutex*/,&msg)) goto err; @@ -1006,7 +1035,7 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) my_close(info_fd, MYF(0)); rli->info_fd=-1; pthread_mutex_unlock(&rli->data_lock); - return 1; + DBUG_RETURN(1); } rli->info_fd = info_fd; @@ -1037,8 +1066,13 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) // before flush_relay_log_info reinit_io_cache(&rli->info_file, WRITE_CACHE,0L,0,1); error=test(flush_relay_log_info(rli)); + if (count_relay_log_space(rli)) + { + msg="Error counting relay log space"; + goto err; + } pthread_mutex_unlock(&rli->data_lock); - return error; + DBUG_RETURN(error); err: sql_print_error(msg); @@ -1046,9 +1080,66 @@ err: my_close(info_fd, MYF(0)); rli->info_fd=-1; pthread_mutex_unlock(&rli->data_lock); - return 1; + DBUG_RETURN(1); +} + +static inline int add_relay_log(RELAY_LOG_INFO* rli,LOG_INFO* linfo) +{ + MY_STAT s; + DBUG_ENTER("add_relay_log"); + if (!my_stat(linfo->log_file_name,&s,MYF(0))) + { + sql_print_error("log %s listed in the index, but failed to stat", + linfo->log_file_name); + DBUG_RETURN(1); + } + rli->log_space_total += s.st_size; +#ifndef DBUG_OFF + char buf[22]; +#endif + DBUG_PRINT("info",("log_space_total: %s", llstr(rli->log_space_total,buf))); + DBUG_RETURN(0); } +static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) +{ + bool slave_killed; + MASTER_INFO* mi = rli->mi; + const char* save_proc_info; + THD* thd = mi->io_thd; + DBUG_ENTER("wait_for_relay_log_space"); + pthread_mutex_lock(&rli->log_space_lock); + save_proc_info = thd->proc_info; + thd->proc_info = "Waiting for relay log space to free"; + while (rli->log_space_limit < rli->log_space_total && + !(slave_killed=io_slave_killed(thd,mi))) + { + pthread_cond_wait(&rli->log_space_cond, &rli->log_space_lock); + } + thd->proc_info = save_proc_info; + pthread_mutex_unlock(&rli->log_space_lock); + DBUG_RETURN(slave_killed); +} + +static int count_relay_log_space(RELAY_LOG_INFO* rli) +{ + LOG_INFO linfo; + DBUG_ENTER("count_relay_log_space"); + rli->log_space_total = 0; + if (rli->relay_log.find_first_log(&linfo,"")) + { + sql_print_error("Could not find first log while counting relay log space"); + DBUG_RETURN(1); + } + if (add_relay_log(rli,&linfo)) + DBUG_RETURN(1); + while (!rli->relay_log.find_next_log(&linfo)) + { + if (add_relay_log(rli,&linfo)) + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} int init_master_info(MASTER_INFO* mi, const char* master_info_fname, const char* slave_info_fname) @@ -1058,6 +1149,8 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname, if (init_relay_log_info(&mi->rli, slave_info_fname)) return 1; mi->rli.mi = mi; + mi->mysql=0; + mi->file_id=1; mi->ignore_stop_event=0; int fd,error; MY_STAT stat_area; @@ -1225,6 +1318,7 @@ int show_master_info(THD* thd, MASTER_INFO* mi) field_list.push_back(new Item_empty_string("Last_error", 20)); field_list.push_back(new Item_empty_string("Skip_counter", 12)); field_list.push_back(new Item_empty_string("Exec_master_log_pos", 12)); + field_list.push_back(new Item_empty_string("Relay_log_space", 12)); if(send_fields(thd, field_list, 1)) DBUG_RETURN(-1); @@ -1251,6 +1345,7 @@ int show_master_info(THD* thd, MASTER_INFO* mi) net_store_data(packet, mi->rli.last_slave_error); net_store_data(packet, mi->rli.slave_skip_counter); net_store_data(packet, (longlong) mi->rli.master_log_pos); + net_store_data(packet, (longlong) mi->rli.log_space_total); pthread_mutex_unlock(&mi->rli.data_lock); pthread_mutex_unlock(&mi->data_lock); @@ -1283,9 +1378,16 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, bool pos_reached = 0; int event_count = 0; pthread_mutex_lock(&data_lock); - while (!thd->killed) + abort_pos_wait=0; // abort only if master info changes during wait + while (!thd->killed || !abort_pos_wait) { int cmp_result; + if (abort_pos_wait) + { + abort_pos_wait=0; + pthread_mutex_unlock(&data_lock); + return -1; + } DBUG_ASSERT(*master_log_name || master_log_pos == 0); if (*master_log_name) { @@ -1337,10 +1439,7 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) thd->thread_id = thread_id++; pthread_mutex_unlock(&LOCK_thread_count); - if (init_thr_lock() || - my_pthread_setspecific_ptr(THR_THD, thd) || - my_pthread_setspecific_ptr(THR_MALLOC, &thd->mem_root) || - my_pthread_setspecific_ptr(THR_NET, &thd->net)) + if (init_thr_lock() || thd->store_globals()) { end_thread(thd,0); DBUG_RETURN(-1); @@ -1354,7 +1453,6 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); #endif - thd->mem_root.free=thd->mem_root.used=0; // Probably not needed if (thd->max_join_size == (ulong) ~0L) thd->options |= OPTION_BIG_SELECTS; @@ -1368,11 +1466,11 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) } thd->version=refresh_version; thd->set_time(); - DBUG_RETURN(0); } -static int safe_sleep(THD* thd, MASTER_INFO* mi, int sec) +static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, + void* thread_killed_arg) { thr_alarm_t alarmed; thr_alarm_init(&alarmed); @@ -1395,7 +1493,7 @@ static int safe_sleep(THD* thd, MASTER_INFO* mi, int sec) if (thr_alarm_in_use(&alarmed)) thr_end_alarm(&alarmed); - if (slave_killed(thd,mi)) + if ((*thread_killed)(thd,thread_killed_arg)) return 1; start_time=time((time_t*) 0); } @@ -1525,7 +1623,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) DBUG_ASSERT(rli->sql_thd==thd); Log_event * ev = next_event(rli); DBUG_ASSERT(rli->sql_thd==thd); - if (slave_killed(thd,rli)) + if (sql_slave_killed(thd,rli)) return 1; if (ev) { @@ -1535,10 +1633,6 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) if (ev->server_id == ::server_id || (rli->slave_skip_counter && type_code != ROTATE_EVENT)) { - /* - TODO: I/O thread must handle skipping file delivery for - old load data infile events - */ /* TODO: I/O thread should not even log events with the same server id */ rli->inc_pos(ev->get_event_len(), type_code != STOP_EVENT ? ev->log_pos : LL(0), @@ -1601,6 +1695,7 @@ slave_begin: my_thread_init(); thd = new THD; // note that contructor of THD uses DBUG_ ! DBUG_ENTER("handle_slave_io"); + THD_CHECK_SENTRY(thd); pthread_detach_this_thread(); if (init_slave_thread(thd, SLAVE_THD_IO)) @@ -1621,7 +1716,7 @@ slave_begin: DBUG_PRINT("info",("master info: log_file_name=%s, position=%s", mi->master_log_name, llstr(mi->master_log_pos,llbuff))); - if (!(mysql = mc_mysql_init(NULL))) + if (!(mi->mysql = mysql = mc_mysql_init(NULL))) { sql_print_error("Slave I/O thread: error in mc_mysql_init()"); goto err; @@ -1662,13 +1757,13 @@ connected: goto err; } - while (!slave_killed(thd,mi)) + while (!io_slave_killed(thd,mi)) { thd->proc_info = "Requesting binlog dump"; if (request_dump(mysql, mi)) { sql_print_error("Failed on request_dump()"); - if(slave_killed(thd,mi)) + if(io_slave_killed(thd,mi)) { sql_print_error("Slave I/O thread killed while requesting master \ dump"); @@ -1683,11 +1778,12 @@ dump"); hopefuly the admin can fix the problem sometime */ if (retried_once) - safe_sleep(thd, mi, mi->connect_retry); + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*)mi); else retried_once = 1; - if (slave_killed(thd,mi)) + if (io_slave_killed(thd,mi)) { sql_print_error("Slave I/O thread killed while retrying master \ dump"); @@ -1698,7 +1794,7 @@ dump"); sql_print_error("Slave I/O thread: failed dump request, \ reconnecting to try again, log '%s' at postion %s", IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); - if (safe_reconnect(thd, mysql, mi) || slave_killed(thd,mi)) + if (safe_reconnect(thd, mysql, mi) || io_slave_killed(thd,mi)) { sql_print_error("Slave I/O thread killed during or \ after reconnect"); @@ -1708,11 +1804,11 @@ after reconnect"); goto connected; } - while (!slave_killed(thd,mi)) + while (!io_slave_killed(thd,mi)) { thd->proc_info = "Reading master update"; ulong event_len = read_event(mysql, mi); - if (slave_killed(thd,mi)) + if (io_slave_killed(thd,mi)) { sql_print_error("Slave I/O thread killed while reading event"); goto err; @@ -1732,11 +1828,12 @@ is correct, restart the server with a higher value of max_allowed_packet", thd->proc_info = "Waiting to reconnect after a failed read"; mc_end_server(mysql); if (retried_once) // punish repeat offender with sleep - safe_sleep(thd,mi,mi->connect_retry); + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*)mi); else retried_once = 1; - if (slave_killed(thd,mi)) + if (io_slave_killed(thd,mi)) { sql_print_error("Slave I/O thread killed while waiting to \ reconnect after a failed read"); @@ -1746,7 +1843,7 @@ reconnect after a failed read"); sql_print_error("Slave I/O thread: Failed reading log event, \ reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME, llstr(mi->master_log_pos, llbuff)); - if (safe_reconnect(thd, mysql, mi) || slave_killed(thd,mi)) + if (safe_reconnect(thd, mysql, mi) || io_slave_killed(thd,mi)) { sql_print_error("Slave I/O thread killed during or after a \ reconnect done to recover from failed read"); @@ -1763,6 +1860,15 @@ reconnect done to recover from failed read"); from master"); goto err; } + flush_master_info(mi); + if (mi->rli.log_space_limit && mi->rli.log_space_limit < + mi->rli.log_space_total) + if (wait_for_relay_log_space(&mi->rli)) + { + sql_print_error("Slave I/O thread aborted while waiting for relay \ +log space"); + goto err; + } // TODO: check debugging abort code #ifndef DBUG_OFF if (abort_slave_event_count && !--events_till_abort) @@ -1771,8 +1877,8 @@ from master"); goto err; } #endif - } // while(!slave_killed(thd,mi)) - read/exec loop - } // while(!slave_killed(thd,mi)) - slave loop + } + } // error = 0; err: @@ -1780,8 +1886,11 @@ err: sql_print_error("Slave I/O thread exiting, read up to log '%s', position %s", IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); thd->query = thd->db = 0; // extra safety - if(mysql) + if (mysql) + { mc_mysql_close(mysql); + mi->mysql=0; + } thd->proc_info = "Waiting for slave mutex on exit"; pthread_mutex_lock(&mi->run_lock); mi->slave_running = 0; @@ -1790,13 +1899,14 @@ err: change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); mi->abort_slave = 0; // TODO: check if this is needed DBUG_ASSERT(thd->net.buff != 0); - net_end(&thd->net); // destructor will not free it, because we are weird + net_end(&thd->net); // destructor will not free it, because net.vio is 0 pthread_mutex_lock(&LOCK_thread_count); + THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); + my_thread_end(); // clean-up before broadcast pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done pthread_mutex_unlock(&mi->run_lock); - my_thread_end(); #ifndef DBUG_OFF if(abort_slave_event_count && !events_till_abort) goto slave_begin; @@ -1832,7 +1942,8 @@ slave_begin: my_thread_init(); thd = new THD; // note that contructor of THD uses DBUG_ ! DBUG_ENTER("handle_slave_sql"); - + THD_CHECK_SENTRY(thd); + pthread_detach_this_thread(); if (init_slave_thread(thd, SLAVE_THD_SQL)) { @@ -1845,6 +1956,7 @@ slave_begin: sql_print_error("Failed during slave thread initialization"); goto err; } + THD_CHECK_SENTRY(thd); thd->thread_stack = (char*)&thd; // remember where our stack is thd->temporary_tables = rli->save_temporary_tables; // restore temp tables threads.append(thd); @@ -1871,14 +1983,15 @@ slave_begin: log '%s' at position %s,relay log: name='%s',pos='%s'", RPL_LOG_NAME, llstr(rli->master_log_pos,llbuff),rli->relay_log_name, llstr(rli->relay_log_pos,llbuff1)); - while (!slave_killed(thd,rli)) + while (!sql_slave_killed(thd,rli)) { thd->proc_info = "Processing master log event"; DBUG_ASSERT(rli->sql_thd == thd); + THD_CHECK_SENTRY(thd); if (exec_relay_log_event(thd,rli)) { // do not scare the user if SQL thread was simply killed or stopped - if (!slave_killed(thd,rli)) + if (!sql_slave_killed(thd,rli)) sql_print_error("\ Error running query, slave SQL thread aborted. Fix the problem, and restart \ the slave SQL thread with \"SLAVE START\". We stopped at log \ @@ -1886,7 +1999,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ RPL_LOG_NAME, llstr(rli->master_log_pos, llbuff)); goto err; } - } // while(!slave_killed(thd,rli)) - read/exec loop + } // while(!sql_slave_killed(thd,rli)) - read/exec loop // error = 0; err: @@ -1910,14 +2023,16 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because we are weird DBUG_ASSERT(rli->sql_thd == thd); + THD_CHECK_SENTRY(thd); rli->sql_thd = 0; pthread_mutex_lock(&LOCK_thread_count); + THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); + my_thread_end(); // clean-up before broadcasting termination pthread_cond_broadcast(&rli->stop_cond); // tell the world we are done pthread_mutex_unlock(&rli->run_lock); - my_thread_end(); #ifndef DBUG_OFF // TODO: reconsider the code below if (abort_slave_event_count && !rli->events_till_abort) goto slave_begin; @@ -1926,11 +2041,104 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ DBUG_RETURN(0); // Can't return anything here } +static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) +{ + int error = 1; + ulong num_bytes; + bool cev_not_written; + THD* thd; + NET* net = &mi->mysql->net; + + if (unlikely(!cev->is_valid())) + return 1; + /* + TODO: fix to honor table rules, not only db rules + */ + if (!db_ok(cev->db, replicate_do_db, replicate_ignore_db)) + { + skip_load_data_infile(net); + return 0; + } + DBUG_ASSERT(cev->inited_from_old); + thd = mi->io_thd; + thd->file_id = cev->file_id = mi->file_id++; + thd->server_id = cev->server_id; + cev_not_written = 1; + + if (unlikely(net_request_file(net,cev->fname))) + { + sql_print_error("Slave I/O: failed requesting download of '%s'", + cev->fname); + goto err; + } + + /* this dummy block is so we could instantiate Append_block_log_event + once and then modify it slightly instead of doing it multiple times + in the loop + */ + { + Append_block_log_event aev(thd,0,0); + + for (;;) + { + if (unlikely((num_bytes=my_net_read(net)) == packet_error)) + { + sql_print_error("Network read error downloading '%s' from master", + cev->fname); + goto err; + } + if (unlikely(!num_bytes)) /* eof */ + { + send_ok(net); /* 3.23 master wants it */ + Execute_load_log_event xev(thd); + xev.log_pos = mi->master_log_pos; + if (unlikely(mi->rli.relay_log.append(&xev))) + { + sql_print_error("Slave I/O: error writing Exec_load event to \ +relay log"); + goto err; + } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + break; + } + if (unlikely(cev_not_written)) + { + cev->block = (char*)net->read_pos; + cev->block_len = num_bytes; + cev->log_pos = mi->master_log_pos; + if (unlikely(mi->rli.relay_log.append(cev))) + { + sql_print_error("Slave I/O: error writing Create_file event to \ +relay log"); + goto err; + } + cev_not_written=0; + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + } + else + { + aev.block = (char*)net->read_pos; + aev.block_len = num_bytes; + aev.log_pos = mi->master_log_pos; + if (unlikely(mi->rli.relay_log.append(&aev))) + { + sql_print_error("Slave I/O: error writing Append_block event to \ +relay log"); + goto err; + } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total) ; + } + } + } + error=0; +err: + return error; +} // We assume we already locked mi->data_lock static int process_io_rotate(MASTER_INFO* mi, Rotate_log_event* rev) { - if (!rev->is_valid()) + if (unlikely(!rev->is_valid())) return 1; DBUG_ASSERT(rev->ident_len<sizeof(mi->master_log_name)); memcpy(mi->master_log_name,rev->new_log_ident, @@ -1961,6 +2169,21 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, const char *errmsg = 0; bool inc_pos = 1; bool processed_stop_event = 0; + char* tmp_buf = 0; + /* if we get Load event, we need to pass a non-reusable buffer + to read_log_event, so we do a trick + */ + if (buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) + { + if (unlikely(!(tmp_buf=(char*)my_malloc(event_len+1,MYF(MY_WME))))) + { + sql_print_error("Slave I/O: out of memory for Load event"); + return 1; + } + memcpy(tmp_buf,buf,event_len); + tmp_buf[event_len]=0; // Create_file constructor wants null-term buffer + buf = (const char*)tmp_buf; + } Log_event *ev = Log_event::read_log_event(buf,event_len, &errmsg, 1 /*old format*/ ); if (unlikely(!ev)) @@ -1968,6 +2191,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, sql_print_error("Read invalid event from master: '%s',\ master could be corrupt but a more likely cause of this is a bug", errmsg); + my_free((char*)tmp_buf, MYF(MY_ALLOW_ZERO_PTR)); return 1; } pthread_mutex_lock(&mi->data_lock); @@ -1978,6 +2202,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, { delete ev; pthread_mutex_unlock(&mi->data_lock); + DBUG_ASSERT(!tmp_buf); return 1; } mi->ignore_stop_event=1; @@ -1986,12 +2211,16 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, case STOP_EVENT: processed_stop_event=1; break; - case LOAD_EVENT: - // TODO: actually process it - mi->master_log_pos += event_len; + case CREATE_FILE_EVENT: + { + int error = process_io_create_file(mi,(Create_file_log_event*)ev); delete ev; + mi->master_log_pos += event_len; pthread_mutex_unlock(&mi->data_lock); - return 0; + DBUG_ASSERT(tmp_buf); + my_free((char*)tmp_buf, MYF(0)); + return error; + } default: mi->ignore_stop_event=0; break; @@ -2002,8 +2231,10 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, { delete ev; pthread_mutex_unlock(&mi->data_lock); + DBUG_ASSERT(!tmp_buf); return 1; } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); } delete ev; if (likely(inc_pos)) @@ -2011,6 +2242,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, if (unlikely(processed_stop_event)) mi->ignore_stop_event=1; pthread_mutex_unlock(&mi->data_lock); + DBUG_ASSERT(!tmp_buf); return 0; } @@ -2056,6 +2288,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) { if (likely(inc_pos)) mi->master_log_pos += event_len; + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); } if (unlikely(processed_stop_event)) mi->ignore_stop_event=1; @@ -2108,7 +2341,7 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, #ifndef DBUG_OFF events_till_disconnect = disconnect_slave_event_count; #endif - while (!(slave_was_killed = slave_killed(thd,mi)) && + while (!(slave_was_killed = io_slave_killed(thd,mi)) && (reconnect ? mc_mysql_reconnect(mysql) != 0 : !mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0, mi->port, 0, 0))) @@ -2122,7 +2355,8 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, mc_mysql_error(mysql), last_errno=mc_mysql_errno(mysql), mi->connect_retry); } - safe_sleep(thd,mi,mi->connect_retry); + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*)mi); /* By default we try forever. The reason is that failure will trigger master election, so if the user did not set master_retry_count we @@ -2173,7 +2407,7 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) int flush_relay_log_info(RELAY_LOG_INFO* rli) { - IO_CACHE* file = &rli->info_file; + register IO_CACHE* file = &rli->info_file; char lbuf[22],lbuf1[22]; my_b_seek(file, 0L); @@ -2217,7 +2451,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli) */ pthread_mutex_lock(&rli->data_lock); - for (; !(was_killed=slave_killed(thd,rli)) ;) + for (; !(was_killed=sql_slave_killed(thd,rli)) ;) { /* We can have two kinds of log reading: @@ -2251,7 +2485,10 @@ Log_event* next_event(RELAY_LOG_INFO* rli) } DBUG_ASSERT(my_b_tell(cur_log) >= 4); DBUG_ASSERT(my_b_tell(cur_log) == rli->relay_log_pos + rli->pending); - if ((ev=Log_event::read_log_event(cur_log,0,rli->mi->old_format))) + /* relay log is always in new format - if the master is 3.23, the + I/O thread will convert the format for us + */ + if ((ev=Log_event::read_log_event(cur_log,0,(bool)0/*new format*/))) { DBUG_ASSERT(thd==rli->sql_thd); if (hot_log) @@ -2296,13 +2533,35 @@ Log_event* next_event(RELAY_LOG_INFO* rli) end_io_cache(cur_log); DBUG_ASSERT(rli->cur_log_fd >= 0); my_close(rli->cur_log_fd, MYF(MY_WME)); - rli->cur_log_fd = -1; + rli->cur_log_fd = -1; - // purge_first_log will properly set up relay log coordinates in rli - if (rli->relay_log.purge_first_log(rli)) + // TODO: make skip_log_purge a start-up option. At this point this + // is not critical priority + if (!rli->skip_log_purge) { - errmsg = "Error purging processed log"; - goto err; + // purge_first_log will properly set up relay log coordinates in rli + if (rli->relay_log.purge_first_log(rli)) + { + errmsg = "Error purging processed log"; + goto err; + } + } + else + { + // TODO: verify that no lock is ok here. At this point, if we + // get this wrong, this is actually no big deal - the only time + // this code will ever be executed is if we are recovering from + // a bug when a full reload of the slave is not feasible or + // desirable. + if (rli->relay_log.find_next_log(&rli->linfo,0/*no lock*/)) + { + errmsg = "error switching to the next log"; + goto err; + } + rli->relay_log_pos = 4; + strnmov(rli->relay_log_name,rli->linfo.log_file_name, + sizeof(rli->relay_log_name)); + flush_relay_log_info(rli); } // next log is hot @@ -2344,9 +2603,12 @@ Log_event* next_event(RELAY_LOG_INFO* rli) sql_print_error("Slave SQL thread: I/O error reading \ event(errno=%d,cur_log->error=%d)", my_errno,cur_log->error); + // set read position to the beginning of the event + my_b_seek(cur_log,rli->relay_log_pos+rli->pending); // no need to hog the mutex while we sleep pthread_mutex_unlock(&rli->data_lock); - safe_sleep(rli->sql_thd,rli->mi,1); + safe_sleep(rli->sql_thd,1,(CHECK_KILLED_FUNC)sql_slave_killed, + (void*)rli); pthread_mutex_lock(&rli->data_lock); } } diff --git a/sql/slave.h b/sql/slave.h index f60f2ce2954..354fc46e99d 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -31,6 +31,7 @@ extern char* slave_load_tmpdir; extern my_string master_info_file,relay_log_info_file; extern my_string opt_relay_logname, opt_relaylog_index_name; extern bool opt_skip_slave_start; +extern ulong relay_log_space_limit; struct st_master_info; /* @@ -151,25 +152,36 @@ typedef struct st_relay_log_info char last_slave_error[MAX_SLAVE_ERRMSG]; THD* sql_thd; bool log_pos_current; + bool abort_pos_wait; + bool skip_log_purge; + ulonglong log_space_limit,log_space_total; + pthread_mutex_t log_space_lock; + pthread_cond_t log_space_cond; st_relay_log_info():info_fd(-1),cur_log_fd(-1),inited(0), cur_log_init_count(0), - log_pos_current(0) + log_pos_current(0),abort_pos_wait(0), + skip_log_purge(0) { relay_log_name[0] = master_log_name[0] = 0; + bzero(&info_file,sizeof(info_file)); pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&log_space_lock, MY_MUTEX_INIT_FAST); pthread_cond_init(&data_cond, NULL); pthread_cond_init(&start_cond, NULL); pthread_cond_init(&stop_cond, NULL); + pthread_cond_init(&log_space_cond, NULL); } ~st_relay_log_info() { pthread_mutex_destroy(&run_lock); pthread_mutex_destroy(&data_lock); + pthread_mutex_destroy(&log_space_lock); pthread_cond_destroy(&data_cond); pthread_cond_destroy(&start_cond); pthread_cond_destroy(&stop_cond); + pthread_cond_destroy(&log_space_cond); } inline void inc_pending(ulonglong val) { @@ -254,6 +266,8 @@ typedef struct st_master_info pthread_mutex_t data_lock,run_lock; pthread_cond_t data_cond,start_cond,stop_cond; THD *io_thd; + MYSQL* mysql; + uint32 file_id; // for 3.23 load data infile RELAY_LOG_INFO rli; uint port; uint connect_retry; @@ -269,6 +283,7 @@ typedef struct st_master_info st_master_info():fd(-1), io_thd(0), inited(0), old_format(0) { host[0] = 0; user[0] = 0; password[0] = 0; + bzero(&file,sizeof(file)); pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); pthread_cond_init(&data_cond, NULL); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 577084a650a..104b431bdbb 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -221,6 +221,8 @@ int acl_init(bool dont_read_acl_tables) user.x509_issuer=get_field(&mem, table, 19); user.x509_subject=get_field(&mem, table, 20); } + else + user.ssl_type=SSL_TYPE_NONE; #endif /* HAVE_OPENSSL */ if (user.password && (length=(uint) strlen(user.password)) == 8 && protocol_version == PROTOCOL_VERSION) @@ -920,14 +922,10 @@ bool acl_check_host(const char *host, const char *ip) bool change_password(THD *thd, const char *host, const char *user, char *new_password) { + uint length=0; DBUG_ENTER("change_password"); DBUG_PRINT("enter",("thd=%x, host='%s', user='%s', new_password='%s'",thd,host,user,new_password)); - uint length=0; - if (!user[0]) - { - send_error(&thd->net, ER_PASSWORD_ANONYMOUS_USER); - DBUG_RETURN(1); - } + if (!initialized) { send_error(&thd->net, ER_PASSWORD_NOT_ALLOWED); /* purecov: inspected */ @@ -939,16 +937,21 @@ bool change_password(THD *thd, const char *host, const char *user, length=(uint) strlen(new_password); new_password[length & 16]=0; - if (!thd || (!thd->slave_thread && ( strcmp(thd->user,user) || - my_strcasecmp(host,thd->host ? thd->host : thd->ip)))) + if (!thd->slave_thread && + (strcmp(thd->user,user) || + my_strcasecmp(host,thd->host ? thd->host : thd->ip))) { if (check_access(thd, UPDATE_ACL, "mysql",0,1)) DBUG_RETURN(1); } + if (!thd->slave_thread && !thd->user[0]) + { + send_error(&thd->net, ER_PASSWORD_ANONYMOUS_USER); + DBUG_RETURN(1); + } VOID(pthread_mutex_lock(&acl_cache->lock)); ACL_USER *acl_user; - DBUG_PRINT("info",("host=%s, user=%s",host,user)); - if (!(acl_user= find_acl_user(host,user)) || !acl_user->user) + if (!(acl_user= find_acl_user(host,user))) { send_error(&thd->net, ER_PASSWORD_NO_MATCH); VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -956,7 +959,8 @@ bool change_password(THD *thd, const char *host, const char *user, } if (update_user_table(thd, acl_user->host.hostname ? acl_user->host.hostname : "", - acl_user->user, new_password)) + acl_user->user ? acl_user->user : "", + new_password)) { VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */ send_error(&thd->net,0); /* purecov: deadcode */ @@ -976,7 +980,7 @@ bool change_password(THD *thd, const char *host, const char *user, qinfo.q_len = my_sprintf(buff, (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"", - acl_user->user, + acl_user->user ? acl_user->user : "", acl_user->host.hostname ? acl_user->host.hostname : "", new_password)); mysql_update_log.write(thd,buff,qinfo.q_len); @@ -1201,7 +1205,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, /* We write down SSL related ACL stuff */ DBUG_PRINT("info",("table->fields=%d",table->fields)); if (table->fields >= 21) /* From 4.0.0 we have more fields */ - { + { table->field[18]->store("",0); table->field[19]->store("",0); table->field[20]->store("",0); @@ -2687,6 +2691,13 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) #endif /* HAVE_OPENSSL */ if (want_access & GRANT_ACL) global.append(" WITH GRANT OPTION",18); + else if (acl_user->questions) + { + char buff[65], *p; // just as in int2str + global.append(" WITH MAX_QUERIES_PER_HOUR = ",29); + p=int2str(acl_user->questions,buff,10); + global.append(buff,p-buff); + } thd->packet.length(0); net_store_data(&thd->packet,global.ptr(),global.length()); if (my_net_write(&thd->net,(char*) thd->packet.ptr(), @@ -2734,9 +2745,9 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } } - db.append (" ON ",4); + db.append (" ON '",5); db.append(acl_db->db); - db.append (".* TO '",7); + db.append ("'.* TO '",8); db.append(lex_user->user.str,lex_user->user.length); db.append ("'@'",3); db.append(lex_user->host.str, lex_user->host.length); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index a5116fa0e20..88854396ae3 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -430,6 +430,7 @@ void close_thread_tables(THD *thd, bool locked) while (thd->open_tables) found_old_table|=close_thread_table(thd, &thd->open_tables); + thd->some_tables_deleted=0; /* Free tables to hold down open files */ while (open_cache.records > table_cache_size && unused_tables) @@ -708,11 +709,11 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) if (open_unireg_entry(thd, table, db, table_name, table_name, 1) || !(table->table_cache_key =memdup_root(&table->mem_root,(char*) key, key_length))) - { - closefrm(table); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); - } + { + closefrm(table); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(0); + } table->key_length=key_length; table->version=0; @@ -1692,7 +1693,7 @@ find_item_in_list(Item *find,List<Item> &items) { if (found) { - if ((*found)->eq(item)) + if ((*found)->eq(item,0)) continue; // Same field twice (Access?) if (current_thd->where) my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0), @@ -1708,7 +1709,7 @@ find_item_in_list(Item *find,List<Item> &items) } } } - else if (!table_name && (item->eq(find) || + else if (!table_name && (item->eq(find,0) || find->name && !my_strcasecmp(item->name,find->name))) { @@ -2175,8 +2176,8 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, int setup_ftfuncs(THD *thd) { - List_iterator<Item_func_match> li(thd->lex.select_lex.ftfunc_list), - lj(thd->lex.select_lex.ftfunc_list); + List_iterator<Item_func_match> li(thd->lex.select->ftfunc_list), + lj(thd->lex.select->ftfunc_list); Item_func_match *ftf, *ftf2; while ((ftf=li++)) @@ -2186,7 +2187,7 @@ int setup_ftfuncs(THD *thd) lj.rewind(); while ((ftf2=lj++) != ftf) { - if (ftf->eq(ftf2) && !ftf2->master) + if (ftf->eq(ftf2,1) && !ftf2->master) ftf2->master=ftf; } } @@ -2196,9 +2197,9 @@ int setup_ftfuncs(THD *thd) int init_ftfuncs(THD *thd, bool no_order) { - if (thd->lex.select_lex.ftfunc_list.elements) + if (thd->lex.select->ftfunc_list.elements) { - List_iterator<Item_func_match> li(thd->lex.select_lex.ftfunc_list); + List_iterator<Item_func_match> li(thd->lex.select->ftfunc_list); Item_func_match *ifm; DBUG_PRINT("info",("Performing FULLTEXT search")); thd->proc_info="FULLTEXT initialization"; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index c5ebeead05a..588d60462b0 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -271,8 +271,6 @@ If join_results allocated new block(s) then we need call pack_cache again. TODO list: - - Invalidate queries that use innoDB tables changed in transaction & remove - invalidation by table type - Delayed till after-parsing qache answer (for column rights processing) - Optimize cache resizing - if new_size < old_size then pack & shrink @@ -280,11 +278,10 @@ TODO list: - Move MRG_MYISAM table type processing to handlers, something like: tables_used->table->file->register_used_filenames(callback, first_argument); - - In Query_cache::insert_table eliminate strlen(). To do this we have to - add db_len to the TABLE_LIST and TABLE structures. */ #include "mysql_priv.h" +#ifdef HAVE_QUERY_CACHE #include <m_ctype.h> #include <my_dir.h> #include <hash.h> @@ -1030,7 +1027,8 @@ err: Remove all cached queries that uses any of the tables in the list */ -void Query_cache::invalidate(TABLE_LIST *tables_used) +void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, + my_bool using_transactions) { DBUG_ENTER("Query_cache::invalidate (table list)"); if (query_cache_size > 0) @@ -1039,54 +1037,76 @@ void Query_cache::invalidate(TABLE_LIST *tables_used) if (query_cache_size > 0) { DUMP(this); + + using_transactions = using_transactions && + (thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)); for ( ; tables_used; tables_used=tables_used->next) - invalidate_table(tables_used); + { + DBUG_ASSERT(!using_transactions || tables_used->table!=0); + if (using_transactions && + tables_used->table->file->has_transactions()) + /* + Tables_used->table can't be 0 in transaction. + Only 'drop' invalidate not opened table, but 'drop' + force transaction finish. + */ + thd->add_changed_table(tables_used->table); + else + invalidate_table(tables_used); + } } STRUCT_UNLOCK(&structure_guard_mutex); } DBUG_VOID_RETURN; } -/* - Remove all cached queries that uses the given table -*/ - -void Query_cache::invalidate(TABLE *table) +void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) { - DBUG_ENTER("Query_cache::invalidate (table)"); - if (query_cache_size > 0) + DBUG_ENTER("Query_cache::invalidate (changed table list)"); + if (query_cache_size > 0 && tables_used) { STRUCT_LOCK(&structure_guard_mutex); if (query_cache_size > 0) - invalidate_table(table); + { + DUMP(this); + for ( ; tables_used; tables_used=tables_used->next) + { + invalidate_table(tables_used->key, tables_used->key_length); + DBUG_PRINT("qcache", (" db %s, table %s", tables_used->key, + tables_used->table_name)); + } + } STRUCT_UNLOCK(&structure_guard_mutex); } DBUG_VOID_RETURN; } /* - Remove all cached queries that uses the given table type. + Remove all cached queries that uses the given table */ -void Query_cache::invalidate(Query_cache_table::query_cache_table_type type) +void Query_cache::invalidate(THD *thd, TABLE *table, + my_bool using_transactions) { - DBUG_ENTER("Query_cache::invalidate (type)"); + DBUG_ENTER("Query_cache::invalidate (table)"); + if (query_cache_size > 0) { STRUCT_LOCK(&structure_guard_mutex); - DUMP(this); if (query_cache_size > 0) { - /* invalidate_table reduce list while only root of list remain */ - while (tables_blocks[type] != 0) - invalidate_table(tables_blocks[type]); + using_transactions = using_transactions && + (thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)); + if (using_transactions && table->file->has_transactions()) + thd->add_changed_table(table); + else + invalidate_table(table); } STRUCT_UNLOCK(&structure_guard_mutex); } DBUG_VOID_RETURN; } - /* Remove all cached queries that uses the given database */ @@ -1100,12 +1120,9 @@ void Query_cache::invalidate(char *db) if (query_cache_size > 0) { DUMP(this); - for (int i=0 ; i < (int) Query_cache_table::TYPES_NUMBER; i++) - { /* invalidate_table reduce list while only root of list remain */ - while (tables_blocks[i] !=0 ) - invalidate_table(tables_blocks[i]); - } + while (tables_blocks !=0 ) + invalidate_table(tables_blocks); } STRUCT_UNLOCK(&structure_guard_mutex); } @@ -1120,7 +1137,8 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename) { /* Calculate the key outside the lock to make the lock shorter */ char key[MAX_DBKEY_LENGTH]; - uint key_length= filename_2_table_key(key, filename); + uint32 db_length; + uint key_length= filename_2_table_key(key, filename, &db_length); STRUCT_LOCK(&structure_guard_mutex); if (query_cache_size > 0) // Safety if cache removed { @@ -1801,10 +1819,14 @@ void Query_cache::invalidate_table(TABLE_LIST *table_list) void Query_cache::invalidate_table(TABLE *table) { + invalidate_table((byte*) table->table_cache_key, table->key_length); +} + +void Query_cache::invalidate_table(byte * key, uint32 key_length) +{ Query_cache_block *table_block; if ((table_block = ((Query_cache_block*) - hash_search(&tables, (byte*) table->table_cache_key, - table->key_length)))) + hash_search(&tables, key, key_length)))) invalidate_table(table_block); } @@ -1842,8 +1864,7 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, block_table->n=n; if (!insert_table(tables_used->table->key_length, tables_used->table->table_cache_key, block_table, - Query_cache_table::type_convertion(tables_used->table-> - db_type))) + tables_used->db_length)) break; if (tables_used->table->db_type == DB_TYPE_MRG_MYISAM) @@ -1855,10 +1876,12 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, table++) { char key[MAX_DBKEY_LENGTH]; - uint key_length =filename_2_table_key(key, table->table->filename); + uint32 db_length; + uint key_length =filename_2_table_key(key, table->table->filename, + &db_length); (++block_table)->n= ++n; if (!insert_table(key_length, key, block_table, - Query_cache_table::type_convertion(DB_TYPE_MYISAM))) + db_length)) goto err; } } @@ -1885,7 +1908,7 @@ err: my_bool Query_cache::insert_table(uint key_len, char *key, Query_cache_block_table *node, - Query_cache_table::query_cache_table_type type) + uint32 db_length) { DBUG_ENTER("Query_cache::insert_table"); DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d", @@ -1909,9 +1932,8 @@ Query_cache::insert_table(uint key_len, char *key, DBUG_RETURN(0); } Query_cache_table *header = table_block->table(); - header->type(type); double_linked_list_simple_include(table_block, - &tables_blocks[type]); + &tables_blocks); Query_cache_block_table *list_root = table_block->table(0); list_root->n = 0; list_root->next = list_root->prev = list_root; @@ -1923,7 +1945,7 @@ Query_cache::insert_table(uint key_len, char *key, DBUG_RETURN(0); } char *db = header->db(); - header->table(db + strlen(db) + 1); + header->table(db + db_length + 1); } Query_cache_block_table *list_root = table_block->table(0); @@ -1947,7 +1969,7 @@ void Query_cache::unlink_table(Query_cache_block_table *node) // list is empty (neighbor is root of list) Query_cache_block *table_block = neighbour->block(); double_linked_list_exclude(table_block, - &tables_blocks[table_block->table()->type()]); + &tables_blocks); hash_delete(&tables,(byte *) table_block); free_memory_block(table_block); } @@ -2033,7 +2055,7 @@ Query_cache::get_free_block(ulong len, my_bool not_less, ulong min) block=block->prev; n++; } - if(block->length < len) + if (block->length < len) block=block->next; } } @@ -2513,8 +2535,8 @@ my_bool Query_cache::move_by_type(byte **border, new_block->n_tables=1; memmove((char*) new_block->data(), data, len-new_block->headers_len()); relink(block, new_block, next, prev, pnext, pprev); - if (tables_blocks[new_block->table()->type()] == block) - tables_blocks[new_block->table()->type()] = new_block; + if (tables_blocks == block) + tables_blocks = new_block; Query_cache_block_table *nlist_root = new_block->table(0); nlist_root->n = 0; @@ -2771,10 +2793,10 @@ my_bool Query_cache::join_results(ulong join_limit) } -uint Query_cache::filename_2_table_key (char *key, const char *path) +uint Query_cache::filename_2_table_key (char *key, const char *path, + uint32 *db_length) { char tablename[FN_REFLEN+2], *filename, *dbname; - uint db_length; DBUG_ENTER("Query_cache::filename_2_table_key"); /* Safety if filename didn't have a directory name */ @@ -2785,10 +2807,10 @@ uint Query_cache::filename_2_table_key (char *key, const char *path) filename= tablename + dirname_length(tablename + 2) + 2; /* Find start of databasename */ for (dbname= filename - 2 ; dbname[-1] != FN_LIBCHAR ; dbname--) ; - db_length= (filename - dbname) - 1; - DBUG_PRINT("qcache", ("table '%-.*s.%s'", db_length, dbname, filename)); + *db_length= (filename - dbname) - 1; + DBUG_PRINT("qcache", ("table '%-.*s.%s'", *db_length, dbname, filename)); - DBUG_RETURN((uint) (strmov(strmake(key, dbname, db_length) + 1, + DBUG_RETURN((uint) (strmov(strmake(key, dbname, *db_length) + 1, filename) -key) + 1); } @@ -2975,22 +2997,18 @@ void Query_cache::tables_dump() DBUG_PRINT("qcache", ("--------------------")); DBUG_PRINT("qcache", ("TABLES")); DBUG_PRINT("qcache", ("--------------------")); - for (int i=0; i < (int) Query_cache_table::TYPES_NUMBER; i++) + if (tables_blocks != 0) { - DBUG_PRINT("qcache", ("--- type %u", i)); - if (tables_blocks[i] != 0) + Query_cache_block *table_block = tables_blocks; + do { - Query_cache_block *table_block = tables_blocks[i]; - do - { - Query_cache_table *table = table_block->table(); - DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table())); - table_block = table_block->next; - } while ( table_block != tables_blocks[i]); - } - else - DBUG_PRINT("qcache", ("no tables in list")); + Query_cache_table *table = table_block->table(); + DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table())); + table_block = table_block->next; + } while ( table_block != tables_blocks); } + else + DBUG_PRINT("qcache", ("no tables in list")); DBUG_PRINT("qcache", ("--------------------")); } @@ -3082,7 +3100,7 @@ my_bool Query_cache::check_integrity(bool not_locked) break; } case Query_cache_block::TABLE: - if (in_list(tables_blocks[block->table()->type()], block, "tables")) + if (in_list(tables_blocks, block, "tables")) result = 1; if (in_table_list(block->table(0), block->table(0), "table list root")) result = 1; @@ -3197,28 +3215,25 @@ my_bool Query_cache::check_integrity(bool not_locked) } DBUG_PRINT("qcache", ("check tables ...")); - for (i=0 ; (int) i < (int) Query_cache_table::TYPES_NUMBER; i++) + if ((block = tables_blocks)) { - if ((block = tables_blocks[i])) + do { - do + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + uint length; + byte *key = query_cache_table_get_key((byte*) block, &length, 0); + gptr val = hash_search(&tables, key, length); + if (((gptr)block) != val) { - DBUG_PRINT("qcache", ("block 0x%lx, type %u...", - (ulong) block, (uint) block->type)); - uint length; - byte *key = query_cache_table_get_key((byte*) block, &length, 0); - gptr val = hash_search(&tables, key, length); - if (((gptr)block) != val) - { - DBUG_PRINT("error", ("block 0x%lx found in tables hash like 0x%lx", - (ulong) block, (ulong) val)); - } - - if (in_blocks(block)) - result = 1; - block=block->next; - } while (block != tables_blocks[i]); - } + DBUG_PRINT("error", ("block 0x%lx found in tables hash like 0x%lx", + (ulong) block, (ulong) val)); + } + + if (in_blocks(block)) + result = 1; + block=block->next; + } while (block != tables_blocks); } DBUG_PRINT("qcache", ("check free blocks")); @@ -3446,3 +3461,5 @@ err2: } #endif /* DBUG_OFF */ + +#endif /*HAVE_QUERY_CACHE*/ diff --git a/sql/sql_cache.h b/sql/sql_cache.h index b1d8eb23198..81ea80669b8 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -148,17 +148,8 @@ struct Query_cache_query struct Query_cache_table { - enum query_cache_table_type {OTHER=0, INNODB=1, TYPES_NUMBER=2}; - inline static query_cache_table_type type_convertion(db_type type) - { - return (type == DB_TYPE_INNODB ? INNODB : OTHER); - } - char *tbl; - query_cache_table_type tp; - inline query_cache_table_type type() { return tp; } - inline void type(query_cache_table_type t) { tp = t;} inline char *db() { return (char *) data(); } inline char *table() { return tbl; } inline void table(char *table) { tbl = table; } @@ -248,7 +239,7 @@ protected: byte *cache; // cache memory Query_cache_block *first_block; // physical location block list Query_cache_block *queries_blocks; // query list (LIFO) - Query_cache_block *tables_blocks[Query_cache_table::TYPES_NUMBER]; + Query_cache_block *tables_blocks; Query_cache_memory_bin *bins; // free block lists Query_cache_memory_bin_step *steps; // bins spacing info @@ -270,7 +261,8 @@ protected: Query_cache_block *tail_head); /* Table key generation */ - static uint filename_2_table_key (char *key, const char *filename); + static uint filename_2_table_key (char *key, const char *filename, + uint32 *db_langth); /* The following functions require that structure_guard_mutex is locked */ void flush_cache(); @@ -282,13 +274,14 @@ protected: my_bool first_block); void invalidate_table(TABLE_LIST *table); void invalidate_table(TABLE *table); + void invalidate_table(byte *key, uint32 key_length); void invalidate_table(Query_cache_block *table_block); my_bool register_all_tables(Query_cache_block *block, TABLE_LIST *tables_used, TABLE_COUNTER_TYPE tables); my_bool insert_table(uint key_len, char *key, Query_cache_block_table *node, - Query_cache_table::query_cache_table_type type); + uint32 db_length); void unlink_table(Query_cache_block_table *node); Query_cache_block *get_free_block (ulong len, my_bool not_less, ulong min); @@ -369,11 +362,10 @@ protected: int send_result_to_client(THD *thd, char *query, uint query_length); /* Remove all queries that uses any of the listed following tables */ - void invalidate(TABLE_LIST *tables_used); - void invalidate(TABLE *table); - - /* Remove all queries that uses tables of pointed type*/ - void invalidate(Query_cache_table::query_cache_table_type type); + void invalidate(THD* thd, TABLE_LIST *tables_used, + my_bool using_transactions); + void invalidate(CHANGED_TABLE_LIST *tables_used); + void invalidate(THD* thd, TABLE *table, my_bool using_transactions); /* Remove all queries that uses any of the tables in following database */ void invalidate(char *db); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 795e7bcef00..03bb8ae2c97 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -34,6 +34,8 @@ #ifdef __WIN__ #include <io.h> #endif +#include <mysys_err.h> +#include <assert.h> /***************************************************************************** ** Instansiate templates @@ -102,9 +104,13 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), cond_count=0; convert_set=0; mysys_var=0; +#ifndef DBUG_OFF + dbug_sentry=THD_SENTRY_MAGIC; +#endif net.vio=0; ull=0; system_thread=cleanup_done=0; + transaction.changed_tables = 0; #ifdef __WIN__ real_id = 0; #endif @@ -119,10 +125,14 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), server_id = ::server_id; slave_net = 0; log_pos = 0; - server_status=SERVER_STATUS_AUTOCOMMIT; + server_status= SERVER_STATUS_AUTOCOMMIT; update_lock_default= low_priority_updates ? TL_WRITE_LOW_PRIORITY : TL_WRITE; - options=thd_startup_options; - query_cache_type = (byte) query_cache_startup_type; + options= thd_startup_options; +#ifdef HAVE_QUERY_CACHE + query_cache_type= (byte) query_cache_startup_type; +#else + query_cache_type= 0; //Safety +#endif sql_mode=(uint) opt_sql_mode; inactive_timeout=net_wait_timeout; open_options=ha_open_options; @@ -136,12 +146,13 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), /* Initialize sub structures */ bzero((char*) &mem_root,sizeof(mem_root)); + bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root)); + user_connect=(UC *)0; hash_init(&user_vars, USER_VARS_HASH_SIZE, 0, 0, (hash_get_key) get_var_key, (void (*)(void*)) free_var,0); #ifdef USING_TRANSACTIONS bzero((char*) &transaction,sizeof(transaction)); - user_connect=(UC *)0; if (opt_using_transactions) { if (open_cached_file(&transaction.trans_log, @@ -183,6 +194,7 @@ void THD::cleanup(void) THD::~THD() { + THD_CHECK_SENTRY(this); DBUG_ENTER("~THD()"); /* Close connection */ if (net.vio) @@ -211,15 +223,20 @@ THD::~THD() safeFree(db); safeFree(ip); free_root(&mem_root,MYF(0)); + free_root(&transaction.mem_root,MYF(0)); mysys_var=0; // Safety (shouldn't be needed) #ifdef SIGNAL_WITH_VIO_CLOSE pthread_mutex_destroy(&active_vio_lock); +#endif +#ifndef DBUG_OFF + dbug_sentry = THD_SENTRY_GONE; #endif DBUG_VOID_RETURN; } void THD::awake(bool prepare_to_die) { + THD_CHECK_SENTRY(this); if (prepare_to_die) killed = 1; thr_alarm_kill(real_id); @@ -255,6 +272,88 @@ bool THD::store_globals() my_pthread_setspecific_ptr(THR_NET, &net)); } +/* routings to adding tables to list of changed in transaction tables */ + +inline static void list_include(CHANGED_TABLE_LIST** prev, + CHANGED_TABLE_LIST* curr, + CHANGED_TABLE_LIST* new_table) +{ + if (new_table) + { + *prev = new_table; + (*prev)->next = curr; + } +} + +/* add table to list of changed in transaction tables */ +void THD::add_changed_table(TABLE *table) +{ + DBUG_ENTER("THD::add_changed_table (table)"); + + DBUG_ASSERT((options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)) && + table->file->has_transactions()); + + CHANGED_TABLE_LIST** prev = &transaction.changed_tables; + CHANGED_TABLE_LIST* curr = transaction.changed_tables; + + for(; curr; prev = &(curr->next), curr = curr->next) + { + int cmp = (long)curr->key_length - (long)table->key_length; + if (cmp < 0) + { + list_include(prev, curr, changed_table_dup(table)); + DBUG_PRINT("info", + ("key_length %u %u", table->key_length, (*prev)->key_length)); + DBUG_VOID_RETURN; + } + else if (cmp == 0) + { + cmp = memcmp(curr->key ,table->table_cache_key, curr->key_length); + if (cmp < 0) + { + list_include(prev, curr, changed_table_dup(table)); + DBUG_PRINT("info", + ("key_length %u %u", table->key_length, (*prev)->key_length)); + DBUG_VOID_RETURN; + } + else if (cmp == 0) + { + DBUG_PRINT("info", ("already in list")); + DBUG_VOID_RETURN; + } + } + } + *prev = changed_table_dup(table); + DBUG_PRINT("info", ("key_length %u %u", table->key_length, (*prev)->key_length)); + DBUG_VOID_RETURN; +} + +CHANGED_TABLE_LIST* THD::changed_table_dup(TABLE *table) +{ + CHANGED_TABLE_LIST* new_table = + (CHANGED_TABLE_LIST*) trans_alloc(ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))+ + table->key_length + 1); + if (!new_table) + { + my_error(EE_OUTOFMEMORY, MYF(ME_BELL), + ALIGN_SIZE(sizeof(TABLE_LIST)) + table->key_length + 1); + killed= 1; + return 0; + } + + new_table->key = (char *) (((byte*)new_table)+ + ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))); + new_table->next = 0; + new_table->key_length = table->key_length; + uint32 db_len = ((new_table->table_name = + ::strmake(new_table->key, table->table_cache_key, + table->key_length) + 1) - new_table->key); + ::memcpy(new_table->key + db_len, table->table_cache_key + db_len, + table->key_length - db_len); + return new_table; +} + + /***************************************************************************** ** Functions to provide a interface to select results *****************************************************************************/ diff --git a/sql/sql_class.h b/sql/sql_class.h index a1423cfcdf1..d8824e80686 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -78,12 +78,29 @@ class MYSQL_LOG { bool need_start_event; pthread_cond_t update_cond; bool no_auto_events; // for relay binlog + ulonglong bytes_written; friend class Log_event; public: MYSQL_LOG(); ~MYSQL_LOG(); pthread_mutex_t* get_log_lock() { return &LOCK_log; } + void reset_bytes_written() + { + bytes_written = 0; + } + void harvest_bytes_written(ulonglong* counter) + { +#ifndef DBUG_OFF + char buf1[22],buf2[22]; +#endif + DBUG_ENTER("harvest_bytes_written"); + (*counter)+=bytes_written; + DBUG_PRINT("info",("counter=%s,bytes_written=%s", llstr(*counter,buf1), + llstr(bytes_written,buf2))); + bytes_written=0; + DBUG_VOID_RETURN; + } IO_CACHE* get_log_file() { return &log_file; } void signal_update() { pthread_cond_broadcast(&update_cond);} void wait_for_update(THD* thd); @@ -251,6 +268,11 @@ public: class delayed_insert; +#define THD_SENTRY_MAGIC 0xfeedd1ff +#define THD_SENTRY_GONE 0xdeadbeef + +#define THD_CHECK_SENTRY(thd) DBUG_ASSERT(thd->dbug_sentry == THD_SENTRY_MAGIC) + /* For each client connection we create a separate thread with THD serving as a thread/connection descriptor */ @@ -258,7 +280,7 @@ class THD :public ilink { public: NET net; // client connection descriptor LEX lex; // parse tree descriptor - MEM_ROOT mem_root; // memory allocation pool + MEM_ROOT mem_root; // 1 command-life memory allocation pool HASH user_vars; // hash for user variables String packet; // dynamic string buffer used for network I/O struct sockaddr_in remote; // client socket address @@ -312,6 +334,9 @@ public: // TODO: document the variables below MYSQL_LOCK *lock,*locked_tables; ULL *ull; +#ifndef DBUG_OFF + uint dbug_sentry; // watch out for memory corruption +#endif struct st_my_thread_var *mysys_var; enum enum_server_command command; uint32 server_id; @@ -326,6 +351,19 @@ public: THD_TRANS all; // Trans since BEGIN WORK THD_TRANS stmt; // Trans for current statement uint bdb_lock_count; + + /* + Tables changed in transaction (that must be invalidated in query cache). + List contain only transactional tables, that not invalidated in query + cache (instead of full list of changed in transaction tables). + */ + CHANGED_TABLE_LIST* changed_tables; + MEM_ROOT mem_root; // Transaction-life memory allocation pool + void cleanup() + { + changed_tables = 0; + free_root(&mem_root,MYF(MY_KEEP_PREALLOC)); + } } transaction; Item *free_list, *handler_items; CONVERT *convert_set; @@ -374,7 +412,7 @@ public: ulong slave_proxy_id; NET* slave_net; // network connection from slave -> m. my_off_t log_pos; - + THD(); ~THD(); void cleanup(void); @@ -471,9 +509,18 @@ public: memcpy(ptr,str,size); return ptr; } + inline gptr trans_alloc(unsigned int size) + { + return alloc_root(&transaction.mem_root,size); + } + void add_changed_table(TABLE *table); + CHANGED_TABLE_LIST * changed_table_dup(TABLE *table); }; - +/* + Used to hold information about file and file structure in exchainge + via non-DB file (...INTO OUTFILE..., ...LOAD DATA...) +*/ class sql_exchange :public Sql_alloc { public: diff --git a/sql/sql_db.cc b/sql/sql_db.cc index dd8ed634011..9f521ac5ffd 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -159,7 +159,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) if ((deleted=mysql_rm_known_files(thd, dirp, db, path,0)) >= 0 && thd) { ha_drop_database(path); - query_cache.invalidate(db); + query_cache_invalidate1(db); if (!silent) { if (!thd->query) diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index a155abc522b..89e30f31fd5 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -182,7 +182,7 @@ cleanup: thd->lock=0; } if (deleted) - query_cache.invalidate(table_list); + query_cache_invalidate3(thd, table_list, 1); delete select; if (error >= 0) // Fatal error send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN: 0); @@ -470,7 +470,7 @@ bool multi_delete::send_eof() VOID(ha_autocommit_or_rollback(thd,error > 0)); } if (deleted) - query_cache.invalidate(delete_tables); + query_cache_invalidate3(thd, delete_tables, 1); ::send_ok(&thd->net,deleted); return 0; } @@ -548,7 +548,7 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) bzero((char*) &create_info,sizeof(create_info)); *fn_ext(path)=0; // Remove the .frm extension error= ha_create_table(path,&create_info,1) ? -1 : 0; - query_cache.invalidate(table_list); + query_cache_invalidate3(thd, table_list, 0); if (!dont_send_ok) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 0898ad4bffb..235adcc02c1 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -311,7 +311,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, } thd->proc_info="end"; if (info.copied || info.deleted) - query_cache.invalidate(table_list); + query_cache_invalidate3(thd, table_list, 1); table->time_stamp=save_time_stamp; // Restore auto timestamp ptr table->next_number_field=0; thd->count_cuted_fields=0; @@ -1217,7 +1217,7 @@ bool delayed_insert::handle_inserts(void) sql_print_error("%s",thd.net.last_error); goto err; } - query_cache.invalidate(table); + query_cache_invalidate3(&thd, table, 1); if (thr_reschedule_write_lock(*thd.lock->locks)) { /* This should never happen */ @@ -1242,7 +1242,7 @@ bool delayed_insert::handle_inserts(void) sql_print_error("%s",thd.net.last_error); goto err; } - query_cache.invalidate(table); + query_cache_invalidate3(&thd, table, 1); pthread_mutex_lock(&mutex); DBUG_RETURN(0); @@ -1330,7 +1330,7 @@ void select_insert::send_error(uint errcode,const char *err) table->file->activate_all_index(thd); ha_rollback_stmt(thd); if (info.copied || info.deleted) - query_cache.invalidate(table); + query_cache_invalidate3(thd, table, 1); } @@ -1343,7 +1343,7 @@ bool select_insert::send_eof() if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error) error=error2; if (info.copied || info.deleted) - query_cache.invalidate(table); + query_cache_invalidate3(thd, table, 1); if (error) { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d61e47d0883..42a8a700da3 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -150,6 +150,8 @@ LEX *lex_start(THD *thd, uchar *buf,uint length) lex->convert_set=(lex->thd=thd)->convert_set; lex->yacc_yyss=lex->yacc_yyvs=0; lex->ignore_space=test(thd->sql_mode & MODE_IGNORE_SPACE); + lex->slave_thd_opt=0; + bzero(&lex->mi,sizeof(lex->mi)); return lex; } @@ -768,6 +770,7 @@ int yylex(void *arg) return(TEXT_STRING); case STATE_COMMENT: // Comment + lex->select_lex.options|= OPTION_FOUND_COMMENT; while ((c = yyGet()) != '\n' && c) ; yyUnget(); // Safety against eof state = STATE_START; // Try again @@ -779,6 +782,7 @@ int yylex(void *arg) break; } yySkip(); // Skip '*' + lex->select_lex.options|= OPTION_FOUND_COMMENT; if (yyPeek() == '!') // MySQL command in comment { ulong version=MYSQL_VERSION_ID; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6a966336ad7..6961ab8c712 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -57,6 +57,7 @@ enum enum_sql_command { SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ, SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_MULTI_UPDATE, SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO, + SQLCOM_EMPTY_QUERY, SQLCOM_END }; @@ -97,6 +98,8 @@ typedef struct st_lex_master_info uint port, connect_retry; ulonglong pos; ulong server_id; + char* relay_log_name; + ulong relay_log_pos; } LEX_MASTER_INFO; @@ -185,7 +188,7 @@ typedef struct st_lex { thr_lock_type lock_option; bool drop_primary,drop_if_exists,local_file; bool in_comment,ignore_space,verbose,simple_alter, option_type; - + uint slave_thd_opt; } LEX; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index abc9fa5a121..419e3fccabd 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -145,14 +145,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (read_file_from_client && handle_duplicates == DUP_ERROR) handle_duplicates=DUP_IGNORE; - if (read_file_from_client && (thd->client_capabilities & CLIENT_LOCAL_FILES)) + if (read_file_from_client) { - char tmp [FN_REFLEN+1],*end; - DBUG_PRINT("info",("reading local file")); - tmp[0] = (char) 251; /* NULL_LENGTH */ - end=strnmov(tmp+1,ex->file_name,sizeof(tmp)-2); - (void) my_net_write(&thd->net,tmp,(uint) (end-tmp)); - (void) net_flush(&thd->net); + (void)net_request_file(&thd->net,ex->file_name); file = -1; } else diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cf84c815973..dc89888a1a5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -48,6 +48,10 @@ #endif /* HAVE_OPENSSL */ #define SCRAMBLE_LENGTH 8 +#define MEM_ROOT_BLOCK_SIZE 8192 +#define MEM_ROOT_PREALLOC 8192 +#define TRANS_MEM_ROOT_BLOCK_SIZE 4096 +#define TRANS_MEM_ROOT_PREALLOC 4096 extern int yyparse(void); extern "C" pthread_mutex_t THR_LOCK_keycache; @@ -120,8 +124,9 @@ inline bool end_active_trans(THD *thd) static HASH hash_user_connections; extern pthread_mutex_t LOCK_user_conn; -static int get_or_create_user_conn(THD *thd, const char *user, const char *host, - uint max_questions) +static int get_or_create_user_conn(THD *thd, const char *user, + const char *host, + uint max_questions) { int return_val=0; uint temp_len; @@ -134,19 +139,18 @@ static int get_or_create_user_conn(THD *thd, const char *user, const char *host, temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user)-1, user, "@", host, NullS) - temp_user); (void) pthread_mutex_lock(&LOCK_user_conn); - uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len); - if (!uc) + if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len))) { - uc= ((struct user_conn*) - my_malloc(sizeof(struct user_conn) + temp_len+1, - MYF(MY_WME))); - if (!uc) + /* First connection for user; Create a user connection object */ + if (!(uc= ((struct user_conn*) + my_malloc(sizeof(struct user_conn) + temp_len+1, + MYF(MY_WME))))) { send_error(¤t_thd->net, 0, NullS); // Out of memory return_val=1; goto end; - } + } uc->user=(char*) (uc+1); memcpy(uc->user,temp_user,temp_len+1); uc->len = temp_len; @@ -233,8 +237,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, db ? db : (char*) ""); thd->db_access=0; /* Don't allow user to connect if he has done too many queries */ - if ((max_questions || max_user_connections) && - get_or_create_user_conn(thd,user,thd->host_or_ip,max_questions)) + if ((max_questions || max_user_connections) && get_or_create_user_conn(thd,user,thd->host_or_ip,max_questions)) return -1; if (max_user_connections && thd->user_connect && check_for_max_user_connections(thd->user_connect)) @@ -280,9 +283,6 @@ static int check_for_max_user_connections(UC *uc) { int error=0; DBUG_ENTER("check_for_max_user_connections"); -// DBUG_PRINT("enter",("user: '%s' host: '%s'", user, host)); - - DBUG_ASSERT(uc != 0); if (max_user_connections <= (uint) uc->connections) { @@ -303,8 +303,6 @@ static void decrease_user_connections(UC *uc) return; DBUG_ENTER("decrease_user_connections"); - DBUG_ASSERT(uc != 0); -// DBUG_PRINT("enter",("user: '%s' host: '%s'", user, host)); if (!--uc->connections && !mqh_used) { @@ -326,6 +324,10 @@ void free_max_user_conn(void) /* Check if maximum queries per hour limit has been reached returns 0 if OK. + + In theory we would need a mutex in the UC structure for this to be 100 % + safe, but as the worst scenario is that we would miss counting a couple of + queries, this isn't critical. */ static bool check_mqh(THD *thd) @@ -335,15 +337,14 @@ static bool check_mqh(THD *thd) UC *uc=thd->user_connect; DBUG_ASSERT(uc != 0); - /* TODO: Add username + host to THD for faster execution */ bool my_start = thd->start_time != 0; time_t check_time = (my_start) ? thd->start_time : time(NULL); if (check_time - uc->intime >= 3600) { -// (void) pthread_mutex_lock(&LOCK_user_conn); - uc->questions=(uint) my_start; + (void) pthread_mutex_lock(&LOCK_user_conn); + uc->questions=1; uc->intime=check_time; -// (void) pthread_mutex_unlock(&LOCK_user_conn); + (void) pthread_mutex_unlock(&LOCK_user_conn); } else if (uc->max_questions && ++(uc->questions) > uc->max_questions) { @@ -357,27 +358,22 @@ end: DBUG_RETURN(error); } -static void reset_mqh(THD *thd,LEX_USER *lu, uint mq) + +static void reset_mqh(THD *thd, LEX_USER *lu, uint mq) { - char user[USERNAME_LENGTH+1]; - char host[USERNAME_LENGTH+1]; - char *where; + (void) pthread_mutex_lock(&LOCK_user_conn); if (lu) // for GRANT { UC *uc; - uint temp_len; + uint temp_len=lu->user.length+lu->host.length+2; char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; - memcpy(user,lu->user.str,lu->user.length); - user[lu->user.length]='\0'; - memcpy(host,lu->host.str,lu->host.length); - host[lu->host.length]='\0'; - temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user)-1, user, "@", host, - NullS) - temp_user); - uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len); - if (uc) + memcpy(temp_user,lu->user.str,lu->user.length); + memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length); + temp_user[lu->user.length]=temp_user[temp_len-1]=0; + if ((uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len))) { uc->questions=0; uc->max_questions=mq; @@ -385,21 +381,20 @@ static void reset_mqh(THD *thd,LEX_USER *lu, uint mq) } else // for FLUSH PRIVILEGES { - (void) pthread_mutex_lock(&LOCK_user_conn); - for (uint idx=0;idx<hash_user_connections.records;idx++) + for (uint idx=0;idx < hash_user_connections.records; idx++) { - HASH_LINK *data=dynamic_element(&hash_user_connections.array,idx,HASH_LINK*); - UC *uc=(struct user_conn *)data->data; + char user[USERNAME_LENGTH+1]; + char *where; + UC *uc=(struct user_conn *) hash_element(&hash_user_connections, idx); where=strchr(uc->user,'@'); - memcpy(user,uc->user,where - uc->user); - user[where-uc->user]='\0'; where++; - strcpy(host,where); - uc->max_questions=get_mqh(user,host); + strmake(user,uc->user,where - uc->user); + uc->max_questions=get_mqh(user,where+1); } - (void) pthread_mutex_unlock(&LOCK_user_conn); } + (void) pthread_mutex_unlock(&LOCK_user_conn); } + /* Check connnetion and get priviliges Returns 0 on ok, -1 < if error is given > 0 on error. @@ -624,7 +619,9 @@ pthread_handler_decl(handle_one_connection,arg) thd->command=COM_SLEEP; thd->version=refresh_version; thd->set_time(); - init_sql_alloc(&thd->mem_root,8192,8192); + init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + init_sql_alloc(&thd->transaction.mem_root, + TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC); while (!net->error && net->vio != 0 && !thd->killed) { if (do_command(thd)) @@ -697,7 +694,9 @@ pthread_handler_decl(handle_bootstrap,arg) thd->priv_user=thd->user=(char*)"boot"; buff= (char*) thd->net.buff; - init_sql_alloc(&thd->mem_root,8192,8192); + init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + init_sql_alloc(&thd->transaction.mem_root, + TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC); while (fgets(buff, thd->net.max_packet, file)) { uint length=(uint) strlen(buff); @@ -721,6 +720,7 @@ pthread_handler_decl(handle_bootstrap,arg) if (thd->fatal_error) break; free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); + free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC)); } thd->priv_user=thd->user=0; @@ -834,8 +834,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { NET *net= &thd->net; bool error=0; - // commands which will always take a long time should be marked with - // this so that they will not get logged to the slow query log + /* + Commands which will always take a long time should be marked with + this so that they will not get logged to the slow query log + */ bool slow_command=FALSE; DBUG_ENTER("dispatch_command"); @@ -913,7 +915,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } if (max_connections && save_uc) - decrease_user_connections (save_uc); + decrease_user_connections(save_uc); x_free((gptr) save_db); x_free((gptr) save_user); thd->password=test(passwd[0]); @@ -948,8 +950,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, DBUG_PRINT("query",("%s",thd->query)); if (thd->user_connect && check_mqh(thd)) { - error = TRUE; - net->error = 0; + error = TRUE; // Abort client + net->error = 0; // Don't give abort message break; } /* thd->query_length is set by mysql_parse() */ @@ -1072,11 +1074,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, else send_eof(net); if (mqh_used) - { - if (hash_user_connections.array.buffer == 0) - init_max_user_conn(); - reset_mqh(thd,(LEX_USER *)NULL,0); - } + reset_mqh(thd,(LEX_USER *) NULL, 0); break; } case COM_SHUTDOWN: @@ -1095,6 +1093,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, close_connection(net); close_thread_tables(thd); // Free before kill free_root(&thd->mem_root,MYF(0)); + free_root(&thd->transaction.mem_root,MYF(0)); kill_mysql(); error=TRUE; break; @@ -1311,7 +1310,7 @@ mysql_execute_command(void) if (!(res=open_and_lock_tables(thd,tables))) { - query_cache.store_query(thd, tables); + query_cache_store_query(thd, tables); res=handle_select(thd, lex, result); } else @@ -1322,6 +1321,10 @@ mysql_execute_command(void) res=mysql_do(thd, *lex->insert_list); break; + case SQLCOM_EMPTY_QUERY: + send_ok(&thd->net); + break; + case SQLCOM_PURGE: { if (check_process_priv(thd)) @@ -1627,7 +1630,7 @@ mysql_execute_command(void) goto error; } } - query_cache.invalidate(tables); + query_cache_invalidate3(thd, tables, 0); if (end_active_trans(thd)) res= -1; else if (mysql_rename_tables(thd,tables)) @@ -1666,7 +1669,7 @@ mysql_execute_command(void) check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) goto error; /* purecov: inspected */ res = mysql_repair_table(thd, tables, &lex->check_opt); - query_cache.invalidate(tables); + query_cache_invalidate3(thd, tables, 0); break; } case SQLCOM_CHECK: @@ -1675,7 +1678,7 @@ mysql_execute_command(void) check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) goto error; /* purecov: inspected */ res = mysql_check_table(thd, tables, &lex->check_opt); - query_cache.invalidate(tables); + query_cache_invalidate3(thd, tables, 0); break; } case SQLCOM_ANALYZE: @@ -2104,13 +2107,20 @@ mysql_execute_command(void) { uint privilege= (lex->duplicates == DUP_REPLACE ? INSERT_ACL | UPDATE_ACL | DELETE_ACL : INSERT_ACL); - if (!(lex->local_file && (thd->client_capabilities & CLIENT_LOCAL_FILES))) + + if (!lex->local_file) { if (check_access(thd,privilege | FILE_ACL,tables->db)) goto error; } else { + if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) || + ! opt_local_infile) + { + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); + goto error; + } if (check_access(thd,privilege,tables->db,&tables->grant.privilege) || grant_option && check_grant(thd,privilege,tables)) goto error; @@ -2306,17 +2316,12 @@ mysql_execute_command(void) Query_log_event qinfo(thd, thd->query); mysql_bin_log.write(&qinfo); } - if (mqh_used) + if (mqh_used && lex->mqh) { - if (hash_user_connections.array.buffer == 0) - init_max_user_conn(); - if (lex->mqh) - { - List_iterator <LEX_USER> str_list (lex->users_list); - LEX_USER *Str; - str_list.rewind(); - reset_mqh(thd,str_list++,lex->mqh); - } + List_iterator <LEX_USER> str_list(lex->users_list); + LEX_USER *user; + while ((user=str_list++)) + reset_mqh(thd,user,lex->mqh); } } } @@ -2397,6 +2402,7 @@ mysql_execute_command(void) } else res= -1; + thd->transaction.cleanup(); break; } case SQLCOM_ROLLBACK: @@ -2411,6 +2417,7 @@ mysql_execute_command(void) else res= -1; thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->transaction.cleanup(); break; default: /* Impossible */ send_ok(&thd->net); @@ -2715,7 +2722,7 @@ mysql_parse(THD *thd,char *inBuf,uint length) mysql_init_query(thd); thd->query_length = length; - if (query_cache.send_result_to_client(thd, inBuf, length) <= 0) + if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) { LEX *lex=lex_start(thd, (uchar*) inBuf, length); if (!yyparse() && ! thd->fatal_error) @@ -2983,7 +2990,7 @@ bool add_field_to_list(char *field_name, enum_field_types type, if (new_field->length >= MAX_FIELD_WIDTH || (!new_field->length && !(new_field->flags & BLOB_FLAG) && - type != FIELD_TYPE_STRING)) + type != FIELD_TYPE_STRING && type != FIELD_TYPE_VAR_STRING)) { net_printf(&thd->net,ER_TOO_BIG_FIELDLENGTH,field_name, MAX_FIELD_WIDTH-1); /* purecov: inspected */ @@ -3113,7 +3120,22 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(0); /* purecov: inspected */ - ptr->db= table->db.str ? table->db.str : (thd->db ? thd->db : (char*) ""); + if (table->db.str) + { + ptr->db= table->db.str; + ptr->db_length= table->db.length; + } + else if (thd->db) + { + ptr->db= thd->db; + ptr->db_length= thd->db_length; + } + else + { + ptr->db= (char*) ""; + ptr->db_length= 0; + } + ptr->name=alias_str; if (lower_case_table_names) { @@ -3121,6 +3143,7 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, casedn_str(table->table.str); } ptr->real_name=table->table.str; + ptr->real_name_length=table->table.length; ptr->lock_type=flags; ptr->updating=updating; if (use_index) @@ -3259,6 +3282,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) if (ha_flush_logs()) result=1; } +#ifdef HAVE_QUERY_CACHE if (options & REFRESH_QUERY_CACHE_FREE) { query_cache.pack(); // FLUSH QUERY CACHE @@ -3268,6 +3292,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) { query_cache.flush(); // RESET QUERY CACHE } +#endif /*HAVE_QUERY_CACHE*/ if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) { if ((options & REFRESH_READ_LOCK) && thd) diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index f2a0351361b..305491c7346 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -126,7 +126,7 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) new_table=ren_table->next; sprintf(name,"%s/%s/%s%s",mysql_data_home, - new_table->db,new_table->name, + new_table->db,new_table->real_name, reg_ext); if (!access(name,F_OK)) { @@ -134,7 +134,7 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) DBUG_RETURN(ren_table); // This can't be skipped } sprintf(name,"%s/%s/%s%s",mysql_data_home, - ren_table->db,ren_table->name, + ren_table->db,ren_table->real_name, reg_ext); if ((table_type=get_table_type(name)) == DB_TYPE_UNKNOWN) { @@ -143,11 +143,11 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) DBUG_RETURN(ren_table); } else if (mysql_rename_table(table_type, - ren_table->db, ren_table->name, - new_table->db, new_table->name)) + ren_table->db, ren_table->real_name, + new_table->db, new_table->real_name)) { if (!skip_error) - return ren_table; + DBUG_RETURN(ren_table); } } DBUG_RETURN(0); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 0b408920703..398ff443ad4 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -562,6 +562,8 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) return 1; lock_slave_threads(mi); // this allows us to cleanly read slave_running init_thread_mask(&thread_mask,mi,1 /* inverse */); + if (thd->lex.slave_thd_opt) + thread_mask &= thd->lex.slave_thd_opt; if (thread_mask) { if (server_id_supplied && (!mi->inited || (mi->inited && *mi->host))) @@ -602,6 +604,8 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) int thread_mask; lock_slave_threads(mi); init_thread_mask(&thread_mask,mi,0 /* not inverse*/); + if (thd->lex.slave_thd_opt) + thread_mask &= thd->lex.slave_thd_opt; slave_errno = (thread_mask) ? terminate_slave_threads(mi,thread_mask, 1 /*skip lock */) : ER_SLAVE_NOT_RUNNING; @@ -686,6 +690,7 @@ int change_master(THD* thd, MASTER_INFO* mi) { int error=0,restart_thread_mask; const char* errmsg=0; + bool need_relay_log_purge=1; // kill slave thread lock_slave_threads(mi); @@ -709,7 +714,10 @@ int change_master(THD* thd, MASTER_INFO* mi) return 1; } - pthread_mutex_lock(&mi->data_lock); + /* data lock not needed since we have already stopped the running threads, + and we have the hold on the run locks which will keep all threads that + could possibly modify the data structures from running + */ if ((lex_mi->host || lex_mi->port) && !lex_mi->log_file_name && !lex_mi->pos) { // if we change host or port, we must reset the postion @@ -738,23 +746,56 @@ int change_master(THD* thd, MASTER_INFO* mi) if (lex_mi->connect_retry) mi->connect_retry = lex_mi->connect_retry; + if (lex_mi->relay_log_name) + { + need_relay_log_purge = 0; + mi->rli.skip_log_purge=1; + strnmov(mi->rli.relay_log_name,lex_mi->relay_log_name, + sizeof(mi->rli.relay_log_name)-1); + } + + if (lex_mi->relay_log_pos) + { + need_relay_log_purge=0; + mi->rli.relay_log_pos=lex_mi->relay_log_pos; + } + flush_master_info(mi); - pthread_mutex_unlock(&mi->data_lock); - thd->proc_info="purging old relay logs"; - if (purge_relay_logs(&mi->rli,0 /* not only reset, but also reinit*/, - &errmsg)) + if (need_relay_log_purge) { - send_error(&thd->net, 0, "Failed purging old relay logs"); - unlock_slave_threads(mi); - return 1; + mi->rli.skip_log_purge=0; + thd->proc_info="purging old relay logs"; + if (purge_relay_logs(&mi->rli,0 /* not only reset, but also reinit*/, + &errmsg)) + { + net_printf(&thd->net, 0, "Failed purging old relay logs: %s",errmsg); + return 1; + } + } + else + { + const char* msg; + if (init_relay_log_pos(&mi->rli,0/*log already inited*/, + 0 /*pos already inited*/, + 0 /*no data lock*/, + &msg)) + { + //Sasha: note that I had to change net_printf() to make this work + net_printf(&thd->net,0,"Failed initializing relay log position: %s",msg); + unlock_slave_threads(mi); + return 1; + } + } - pthread_mutex_lock(&mi->rli.data_lock); mi->rli.master_log_pos = mi->master_log_pos; strnmov(mi->rli.master_log_name,mi->master_log_name, sizeof(mi->rli.master_log_name)); if (!mi->rli.master_log_name[0]) // uninitialized case mi->rli.master_log_pos=0; - pthread_cond_broadcast(&mi->rli.data_cond); + + pthread_mutex_lock(&mi->rli.data_lock); + mi->rli.abort_pos_wait = 1; + pthread_cond_broadcast(&mi->data_cond); pthread_mutex_unlock(&mi->rli.data_lock); thd->proc_info = "starting slave"; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0911ea57515..32f2e274132 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -144,7 +144,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab); static void init_sum_functions(Item_sum **func); static bool update_sum_func(Item_sum **func); static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, - bool distinct); + bool distinct, const char *message=NullS); static void describe_info(THD *thd, const char *info); /* @@ -187,7 +187,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, TABLE *tmp_table; int error, tmp_error; bool need_tmp,hidden_group_fields; - bool simple_order,simple_group,no_order, skip_sort_order; + bool simple_order,simple_group,no_order, skip_sort_order, buffer_result; Item::cond_result cond_value; SQL_SELECT *select; DYNAMIC_ARRAY keyuse; @@ -195,11 +195,14 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, Procedure *procedure; List<Item> all_fields(fields); bool select_distinct; + SELECT_LEX *select_lex = &(thd->lex.select_lex); + SELECT_LEX *cur_sel = thd->lex.select; DBUG_ENTER("mysql_select"); /* Check that all tables, fields, conds and order are ok */ select_distinct=test(select_options & SELECT_DISTINCT); + buffer_result=test(select_options & OPTION_BUFFER_RESULT) && !test(select_options & OPTION_FOUND_ROWS); tmp_table=0; select=0; no_order=skip_sort_order=0; @@ -350,10 +353,13 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, } if (cond_value == Item::COND_FALSE || !thd->select_limit) { /* Impossible cond */ - error=return_zero_rows(result, tables, fields, - join.tmp_table_param.sum_func_count != 0 && !group, - select_options,"Impossible WHERE",having, - procedure); + if (select_options & SELECT_DESCRIBE && select_lex->next) + select_describe(&join,false,false,false,"Impossible WHERE"); + else + error=return_zero_rows(result, tables, fields, + join.tmp_table_param.sum_func_count != 0 && !group, + select_options,"Impossible WHERE",having, + procedure); delete procedure; DBUG_RETURN(error); } @@ -366,17 +372,23 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, { if (res < 0) { - error=return_zero_rows(result, tables, fields, !group, - select_options,"No matching min/max row", - having,procedure); + if (select_options & SELECT_DESCRIBE && select_lex->next) + select_describe(&join,false,false,false,"No matching min/max row"); + else + error=return_zero_rows(result, tables, fields, !group, + select_options,"No matching min/max row", + having,procedure); delete procedure; DBUG_RETURN(error); } if (select_options & SELECT_DESCRIBE) { - describe_info(thd,"Select tables optimized away"); + if (select_lex->next) + select_describe(&join,false,false,false,"Select tables optimized away"); + else + describe_info(thd,"Select tables optimized away"); delete procedure; - DBUG_RETURN(0); + DBUG_RETURN(error); } tables=0; // All tables resolved } @@ -385,7 +397,12 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, { // Only test of functions error=0; if (select_options & SELECT_DESCRIBE) - describe_info(thd,"No tables used"); + { + if (select_lex->next) + select_describe(&join,false,false,false,"No tables used"); + else + describe_info(thd,"No tables used"); + } else { result->send_fields(fields,1); @@ -463,11 +480,14 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, } if (make_join_select(&join,select,conds)) { - error=return_zero_rows(result,tables,fields, - join.tmp_table_param.sum_func_count != 0 && !group, - select_options, - "Impossible WHERE noticed after reading const tables", - having,procedure); + if (select_options & SELECT_DESCRIBE && select_lex->next) + select_describe(&join,false,false,false,"Impossible WHERE noticed after reading const tables"); + else + error=return_zero_rows(result,tables,fields, + join.tmp_table_param.sum_func_count != 0 && !group, + select_options, + "Impossible WHERE noticed after reading const tables", + having,procedure); goto err; } @@ -528,14 +548,13 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, need_tmp= (join.const_tables != join.tables && ((select_distinct || !simple_order || !simple_group) || - (group && order) || - test(select_options & OPTION_BUFFER_RESULT))); + (group && order) || buffer_result)); // No cache for MATCH make_join_readinfo(&join, (select_options & (SELECT_DESCRIBE | SELECT_NO_JOIN_CACHE)) | - (thd->lex.select_lex.ftfunc_list.elements ? SELECT_NO_JOIN_CACHE : 0)); + (cur_sel->ftfunc_list.elements ? SELECT_NO_JOIN_CACHE : 0)); /* Need to tell Innobase that to play it safe, it should fetch all columns of the tables: this is because MySQL @@ -594,8 +613,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, HA_POS_ERROR : thd->select_limit,0)))) order=0; select_describe(&join,need_tmp, - (order != 0 && - (!need_tmp || order != group || simple_group)), + order != 0 && !skip_sort_order, select_distinct); error=0; goto err; @@ -964,7 +982,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, s->dependent=(table_map) 0; s->key_dependent=(table_map) 0; if ((table->system || table->file->records <= 1) && ! s->dependent && - !(table->file->option_flag() & HA_NOT_EXACT_COUNT)) + !(table->file->option_flag() & HA_NOT_EXACT_COUNT) && + !table->fulltext_searched) { set_position(join,const_count++,s,(KEYUSE*) 0); } @@ -1095,7 +1114,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, } while (keyuse->table == table && keyuse->key == key); if (eq_part == PREV_BITS(uint,table->key_info[key].key_parts) && - (table->key_info[key].flags & HA_NOSAME)) + (table->key_info[key].flags & HA_NOSAME) && + !table->fulltext_searched) { if (const_ref == eq_part) { // Found everything for ref. @@ -1228,14 +1248,14 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, { if (new_fields->val->used_tables()) { - if (old->val->eq(new_fields->val)) + if (old->val->eq(new_fields->val, old->field->binary())) { old->level=old->const_level=and_level; old->exists_optimize&=new_fields->exists_optimize; } } - else if (old->val->eq(new_fields->val) && old->eq_func && - new_fields->eq_func) + else if (old->val->eq(new_fields->val, old->field->binary()) && + old->eq_func && new_fields->eq_func) { old->level=old->const_level=and_level; old->exists_optimize&=new_fields->exists_optimize; @@ -1597,7 +1617,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, add_key_part(keyuse,field); } - if (thd->lex.select_lex.ftfunc_list.elements) + if (thd->lex.select->ftfunc_list.elements) { add_ft_keys(keyuse,join_tab,cond,normal_tables); } @@ -2516,6 +2536,7 @@ static void make_join_readinfo(JOIN *join,uint options) { uint i; + SELECT_LEX *select_lex = &(join->thd->lex.select_lex); DBUG_ENTER("make_join_readinfo"); for (i=join->const_tables ; i < join->tables ; i++) @@ -2598,7 +2619,7 @@ make_join_readinfo(JOIN *join,uint options) /* These init changes read_record */ if (tab->use_quick == 2) { - join->thd->lex.select_lex.options|=QUERY_NO_GOOD_INDEX_USED; + select_lex->options|=QUERY_NO_GOOD_INDEX_USED; tab->read_first_record= join_init_quick_read_record; statistic_increment(select_range_check_count, &LOCK_status); } @@ -2613,7 +2634,7 @@ make_join_readinfo(JOIN *join,uint options) } else { - join->thd->lex.select_lex.options|=QUERY_NO_INDEX_USED; + select_lex->options|=QUERY_NO_INDEX_USED; statistic_increment(select_scan_count, &LOCK_status); } } @@ -2625,7 +2646,7 @@ make_join_readinfo(JOIN *join,uint options) } else { - join->thd->lex.select_lex.options|=QUERY_NO_INDEX_USED; + select_lex->options|=QUERY_NO_INDEX_USED; statistic_increment(select_full_join_count, &LOCK_status); } } @@ -2749,7 +2770,7 @@ eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab) ORDER *order; for (order=start_order ; order ; order=order->next) { - if ((*ref_item)->eq(order->item[0])) + if ((*ref_item)->eq(order->item[0],0)) break; } if (order) @@ -2913,7 +2934,7 @@ return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields, DBUG_ENTER("return_zero_rows"); if (select_options & SELECT_DESCRIBE) - { + { describe_info(current_thd, info); DBUG_RETURN(0); } @@ -2929,17 +2950,17 @@ return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields, if (having && having->val_int() == 0) send_row=0; } - if (!tables || !(result->send_fields(fields,1))) + if (!(result->send_fields(fields,1))) { if (send_row) result->send_data(fields); - if (tables) // Not from do_select() + if (tables) // Not from do_select() { /* Close open cursors */ for (TABLE_LIST *table=tables; table ; table=table->next) table->table->file->index_end(); - result->send_eof(); // Should be safe } + result->send_eof(); // Should be safe } DBUG_RETURN(0); } @@ -3006,7 +3027,7 @@ change_cond_ref_to_const(I_List<COND_CMP> *save_list,Item *and_father, Item *right_item= func->arguments()[1]; Item_func::Functype functype= func->functype(); - if (right_item->eq(field) && left_item != value) + if (right_item->eq(field,0) && left_item != value) { Item *tmp=value->new_item(); if (tmp) @@ -3025,7 +3046,7 @@ change_cond_ref_to_const(I_List<COND_CMP> *save_list,Item *and_father, func->arguments()[1]->result_type())); } } - else if (left_item->eq(field) && right_item != value) + else if (left_item->eq(field,0) && right_item != value) { Item *tmp=value->new_item(); if (tmp) @@ -3266,7 +3287,7 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) { // boolan compare function Item *left_item= ((Item_func*) cond)->arguments()[0]; Item *right_item= ((Item_func*) cond)->arguments()[1]; - if (left_item->eq(right_item)) + if (left_item->eq(right_item,1)) { if (!left_item->maybe_null || ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC) @@ -3311,22 +3332,22 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) return 0; Item *left_item= ((Item_func*) cond)->arguments()[0]; Item *right_item= ((Item_func*) cond)->arguments()[1]; - if (left_item->eq(comp_item)) + if (left_item->eq(comp_item,1)) { if (right_item->const_item()) { if (*const_item) - return right_item->eq(*const_item); + return right_item->eq(*const_item, 1); *const_item=right_item; return 1; } } - else if (right_item->eq(comp_item)) + else if (right_item->eq(comp_item,1)) { if (left_item->const_item()) { if (*const_item) - return left_item->eq(*const_item); + return left_item->eq(*const_item, 1); *const_item=left_item; return 1; } @@ -4665,7 +4686,7 @@ join_read_prev_same(READ_RECORD *info) tab->ref.key_length)) { table->status=STATUS_NOT_FOUND; - error= 1; + error= -1; } return error; } @@ -4877,8 +4898,10 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), { JOIN_TAB *jt=join->join_tab; if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group - && !join->send_group_parts && !join->having && !jt->select_cond) + && !join->send_group_parts && !join->having && !jt->select_cond && + !(jt->table->file->option_flag() & HA_NOT_EXACT_COUNT)) { + /* Join over all rows in table; Return number of found rows */ join->select_options ^= OPTION_FOUND_ROWS; join->send_records = jt->records; } @@ -5245,7 +5268,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) if (!field->table->const_table && !field->table->maybe_null) { Item *ref_item=part_of_refkey(field->table,field); - if (ref_item && ref_item->eq(right_item)) + if (ref_item && ref_item->eq(right_item,1)) { if (right_item->type() == Item::FIELD_ITEM) return (field->eq_def(((Item_field *) right_item)->field)); @@ -5429,7 +5452,16 @@ static uint find_shortest_key(TABLE *table, key_map usable_keys) } -/* Return 1 if we don't have to do file sorting */ +/* + Test if we can skip the ORDER BY by using an index. + + If we can use an index, the JOIN_TAB / tab->select struct + is changed to use the index. + + Return: + 0 We have to use filesort to do the sorting + 1 We can use an index. +*/ static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, @@ -5475,15 +5507,22 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, { if (select && select->quick) { - // ORDER BY range_key DESC - QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick, - used_key_parts); - if (!tmp || tmp->error) + /* + Don't reverse the sort order, if it's already done. + (In some cases test_if_order_by_key() can be called multiple times + */ + if (!select->quick->reverse_sorted()) { - delete tmp; - DBUG_RETURN(0); // Reverse sort not supported + // ORDER BY range_key DESC + QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick, + used_key_parts); + if (!tmp || tmp->error) + { + delete tmp; + DBUG_RETURN(0); // Reverse sort not supported + } + select->quick=tmp; } - select->quick=tmp; DBUG_RETURN(1); } if (tab->ref.key_parts < used_key_parts) @@ -5603,6 +5642,7 @@ create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) table->file->info(HA_STATUS_VARIABLE); // Get record count table->found_records=filesort(table,sortorder,length, select, 0L, select_limit, &examined_rows); + tab->records=table->found_records; // For SQL_CALC_ROWS delete select; // filesort did select tab->select=0; tab->select_cond=0; @@ -5959,10 +5999,10 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length) /***************************************************************************** -** Fill join cache with packed records -** Records are stored in tab->cache.buffer and last record in -** last record is stored with pointers to blobs to support very big -** records + Fill join cache with packed records + Records are stored in tab->cache.buffer and last record in + last record is stored with pointers to blobs to support very big + records ******************************************************************************/ static int @@ -6024,7 +6064,7 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count) if (null_fields && tables[i].table->null_fields) { /* must copy null bits */ copy->str=(char*) tables[i].table->null_flags; - copy->length=(tables[i].table->null_fields+7)/8; + copy->length=tables[i].table->null_bytes; copy->strip=0; copy->blob_field=0; length+=copy->length; @@ -6474,7 +6514,7 @@ test_if_subpart(ORDER *a,ORDER *b) { for (; a && b; a=a->next,b=b->next) { - if ((*a->item)->eq(*b->item)) + if ((*a->item)->eq(*b->item,1)) a->asc=b->asc; else return 0; @@ -6501,7 +6541,7 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables) for (; a && b; a=a->next,b=b->next) { - if (!(*a->item)->eq(*b->item)) + if (!(*a->item)->eq(*b->item,1)) DBUG_RETURN(0); map|=a->item[0]->used_tables(); } @@ -6937,16 +6977,20 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) ****************************************************************************/ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, - bool distinct) + bool distinct,const char *message) { List<Item> field_list; Item *item; + List<Item> item_list; THD *thd=join->thd; + MYSQL_LOCK *save_lock; + SELECT_LEX *select_lex = &(join->thd->lex.select_lex); + select_result *result=join->result; DBUG_ENTER("select_describe"); /* Don't log this into the slow query log */ - join->thd->lex.select_lex.options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED); - if (join->thd->lex.select == &join->thd->lex.select_lex) + select_lex->options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED); + if (thd->lex.select == select_lex) { field_list.push_back(new Item_empty_string("table",NAME_LEN)); field_list.push_back(new Item_empty_string("type",10)); @@ -6962,141 +7006,162 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, item->maybe_null=1; field_list.push_back(new Item_real("rows",0.0,0,10)); field_list.push_back(new Item_empty_string("Extra",255)); - if (send_fields(thd,field_list,1)) + if (result->send_fields(field_list,1)) return; } - char buff[512],*buff_ptr; - String tmp(buff,sizeof(buff)),*packet= &thd->packet; - table_map used_tables=0; - for (uint i=0 ; i < join->tables ; i++) - { - JOIN_TAB *tab=join->join_tab+i; - TABLE *table=tab->table; - if (tab->type == JT_ALL && tab->select && tab->select->quick) - tab->type= JT_RANGE; - packet->length(0); - net_store_data(packet,table->table_name); - net_store_data(packet,join_type_str[tab->type]); - tmp.length(0); - key_map bits; - uint j; - for (j=0,bits=tab->keys ; bits ; j++,bits>>=1) - { - if (bits & 1) + if (message) + { + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_string(message,strlen(message))); + if (result->send_data(item_list)) + result->send_error(0,NullS); + } + else + { + table_map used_tables=0; + for (uint i=0 ; i < join->tables ; i++) + { + JOIN_TAB *tab=join->join_tab+i; + TABLE *table=tab->table; + char buff[512],*buff_ptr=buff; + char buff1[512], buff2[512], bufff[512]; + String tmp1(buff1,sizeof(buff1)); + String tmp2(buff2,sizeof(buff2)); + item_list.empty(); + if (tab->type == JT_ALL && tab->select && tab->select->quick) + tab->type= JT_RANGE; + item_list.push_back(new Item_string(table->table_name,strlen(table->table_name))); + item_list.push_back(new Item_string(join_type_str[tab->type],strlen(join_type_str[tab->type]))); + tmp1.length(0); tmp2.length(0); + key_map bits; + uint j; + for (j=0,bits=tab->keys ; bits ; j++,bits>>=1) { - if (tmp.length()) - tmp.append(','); - tmp.append(table->key_info[j].name); + if (bits & 1) + { + if (tmp1.length()) + tmp1.append(','); + tmp1.append(table->key_info[j].name); + } } - } - if (tmp.length()) - net_store_data(packet,tmp.ptr(),tmp.length()); - else - net_store_null(packet); - if (tab->ref.key_parts) - { - net_store_data(packet,table->key_info[tab->ref.key].name); - net_store_data(packet,(uint32) tab->ref.key_length); - tmp.length(0); - for (store_key **ref=tab->ref.key_copy ; *ref ; ref++) + if (tmp1.length()) + item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length())); + else + item_list.push_back(new Item_null()); + if (tab->ref.key_parts) { - if (tmp.length()) - tmp.append(','); - tmp.append((*ref)->name()); + item_list.push_back(new Item_string(table->key_info[tab->ref.key].name, + strlen(table->key_info[tab->ref.key].name))); + item_list.push_back(new Item_int((int32) tab->ref.key_length)); + for (store_key **ref=tab->ref.key_copy ; *ref ; ref++) + { + if (tmp2.length()) + tmp2.append(','); + tmp2.append((*ref)->name()); + } + item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length())); } - net_store_data(packet,tmp.ptr(),tmp.length()); - } - else if (tab->type == JT_NEXT) - { - net_store_data(packet,table->key_info[tab->index].name); - net_store_data(packet,(uint32) table->key_info[tab->index].key_length); - net_store_null(packet); - } - else if (tab->select && tab->select->quick) - { - net_store_data(packet,table->key_info[tab->select->quick->index].name);; - net_store_data(packet,(uint32) tab->select->quick->max_used_key_length); - net_store_null(packet); - } - else - { - net_store_null(packet); - net_store_null(packet); - net_store_null(packet); - } - sprintf(buff,"%.0f",join->best_positions[i].records_read); - net_store_data(packet,buff); - my_bool key_read=table->key_read; - if (tab->type == JT_NEXT && - ((table->used_keys & ((key_map) 1 << tab->index)))) - key_read=1; - - buff_ptr=buff; - if (tab->info) - net_store_data(packet,tab->info); - else if (tab->select) - { - if (tab->use_quick == 2) + else if (tab->type == JT_NEXT) { - sprintf(buff_ptr,"range checked for each record (index map: %u)", - tab->keys); - buff_ptr=strend(buff_ptr); + item_list.push_back(new Item_string(table->key_info[tab->index].name,strlen(table->key_info[tab->index].name))); + item_list.push_back(new Item_int((int32) table->key_info[tab->index].key_length)); + item_list.push_back(new Item_null()); + } + else if (tab->select && tab->select->quick) + { + item_list.push_back(new Item_string(table->key_info[tab->select->quick->index].name,strlen(table->key_info[tab->select->quick->index].name))); + item_list.push_back(new Item_int((int32) tab->select->quick->max_used_key_length)); + item_list.push_back(new Item_null()); } else - buff_ptr=strmov(buff_ptr,"where used"); - } - if (key_read) - { - if (buff != buff_ptr) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + item_list.push_back(new Item_null()); + item_list.push_back(new Item_null()); + item_list.push_back(new Item_null()); } - buff_ptr=strmov(buff_ptr,"Using index"); - } - if (table->reginfo.not_exists_optimize) - { - if (buff != buff_ptr) + sprintf(bufff,"%.0f",join->best_positions[i].records_read); + item_list.push_back(new Item_string(bufff,strlen(bufff))); + my_bool key_read=table->key_read; + if (tab->type == JT_NEXT && + ((table->used_keys & ((key_map) 1 << tab->index)))) + key_read=1; + + if (tab->info) + item_list.push_back(new Item_string(tab->info,strlen(tab->info))); + else if (tab->select) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + if (tab->use_quick == 2) + { + sprintf(buff_ptr,"range checked for each record (index map: %u)", + tab->keys); + buff_ptr=strend(buff_ptr); + } + else + buff_ptr=strmov(buff_ptr,"where used"); } - buff_ptr=strmov(buff_ptr,"Not exists"); - } - if (need_tmp_table) - { - need_tmp_table=0; - if (buff != buff_ptr) + if (key_read) + { + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Using index"); + } + if (table->reginfo.not_exists_optimize) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Not exists"); } - buff_ptr=strmov(buff_ptr,"Using temporary"); - } - if (need_order) - { - need_order=0; - if (buff != buff_ptr) + if (need_tmp_table) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + need_tmp_table=0; + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Using temporary"); } - buff_ptr=strmov(buff_ptr,"Using filesort"); - } - if (distinct & test_all_bits(used_tables,thd->used_tables)) - { - if (buff != buff_ptr) + if (need_order) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + need_order=0; + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Using filesort"); + } + if (distinct & test_all_bits(used_tables,thd->used_tables)) + { + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Distinct"); } - buff_ptr=strmov(buff_ptr,"Distinct"); + item_list.push_back(new Item_string(buff,(uint) (buff_ptr - buff))); + // For next iteration + used_tables|=table->map; + if (result->send_data(item_list)) + result->send_error(0,NullS); } - net_store_data(packet,buff,(uint) (buff_ptr - buff)); - if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length())) - DBUG_VOID_RETURN; /* purecov: inspected */ - - // For next iteration - used_tables|=table->map; } if (!join->thd->lex.select->next) - send_eof(&thd->net); + { + save_lock=thd->lock; + thd->lock=(MYSQL_LOCK *)0; + result->send_eof(); + thd->lock=save_lock; + } DBUG_VOID_RETURN; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 131266a11d6..08c17c2e25d 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -908,9 +908,21 @@ store_create_info(THD *thd, TABLE *table, String *packet) } packet->append(')'); } - packet->append("\n)", 2); handler *file = table->file; + + /* Get possible foreign key definitions stored in InnoDB and append them + to the CREATE TABLE statement */ + + char* for_str = file->get_foreign_key_create_info(); + + if (for_str) { + packet->append(for_str, strlen(for_str)); + + file->free_foreign_key_create_info(for_str); + } + + packet->append("\n)", 2); packet->append(" TYPE=", 6); packet->append(file->table_type()); char buff[128]; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 14daf8c1924..cda5d8c9b6b 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -161,7 +161,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, } if (some_tables_deleted) { - query_cache.invalidate(tables); + query_cache_invalidate3(thd, tables, 0); if (!dont_log_query) { mysql_update_log.write(thd, thd->query,thd->query_length); @@ -825,7 +825,7 @@ bool close_cached_table(THD *thd,TABLE *table) /* Mark all tables that are in use as 'old' */ mysql_lock_abort(thd,table); // end threads waiting on lock -#ifdef REMOVE_LOCKS +#if defined(USING_TRANSACTIONS) || defined( __WIN__) || defined( __EMX__) || !defined(OS2) /* Wait until all there are no other threads that has this table open */ while (remove_table_from_cache(thd,table->table_cache_key, table->table_name)) @@ -870,7 +870,8 @@ static int send_check_errmsg(THD* thd, TABLE_LIST* table, return 1; } -static int prepare_for_restore(THD* thd, TABLE_LIST* table) +static int prepare_for_restore(THD* thd, TABLE_LIST* table, + HA_CHECK_OPT *check_opt) { DBUG_ENTER("prepare_for_restore"); @@ -911,16 +912,73 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table) "Failed generating table from .frm file")); } } + + // now we should be able to open the partially restored table + // to finish the restore in the handler later on + if (!(table->table = reopen_name_locked_table(thd, table))) + unlock_table_name(thd, table); DBUG_RETURN(0); } +static int prepare_for_repair(THD* thd, TABLE_LIST* table, + HA_CHECK_OPT *check_opt) +{ + DBUG_ENTER("prepare_for_repair"); + + if (!(check_opt->sql_flags & TT_USEFRM)) + { + DBUG_RETURN(0); + } + else + { + + char from[FN_REFLEN],to[FN_REFLEN]; + char* db = thd->db ? thd->db : table->db; + + sprintf(from, "%s/%s/%s", mysql_real_data_home, db, table->name); + fn_format(from, from, "", MI_NAME_DEXT, 4); + sprintf(to,"%s-%lx_%lx", from, current_pid, thd->thread_id); + + + my_rename(to, from, MYF(MY_WME)); + + if (lock_and_wait_for_table_name(thd,table)) + DBUG_RETURN(-1); + + if (my_rename(from, to, MYF(MY_WME))) + { + unlock_table_name(thd, table); + DBUG_RETURN(send_check_errmsg(thd, table, "repair", + "Failed renaming .MYD file")); + } + if (mysql_truncate(thd, table, 1)) + { + unlock_table_name(thd, table); + DBUG_RETURN(send_check_errmsg(thd, table, "repair", + "Failed generating table from .frm file")); + } + if (my_rename(to, from, MYF(MY_WME))) + { + unlock_table_name(thd, table); + DBUG_RETURN(send_check_errmsg(thd, table, "repair", + "Failed restoring .MYD file")); + } + } + + // now we should be able to open the partially repaired table + // to finish the repair in the handler later on + if (!(table->table = reopen_name_locked_table(thd, table))) + unlock_table_name(thd, table); + DBUG_RETURN(0); +} static int mysql_admin_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt, const char *operator_name, thr_lock_type lock_type, - bool open_for_modify, bool restore, + bool open_for_modify, uint extra_open_options, + int (*prepare_func)(THD *, TABLE_LIST *, HA_CHECK_OPT *), int (handler::*operator_func) (THD *, HA_CHECK_OPT *)) { @@ -952,18 +1010,13 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, table->table = open_ltable(thd, table, lock_type); thd->open_options&= ~extra_open_options; packet->length(0); - if (restore) + if (prepare_func) { - switch (prepare_for_restore(thd, table)) { - case 1: continue; // error, message written to net - case -1: goto err; // error, message could be written to net - default: ;// should be 0 otherwise + switch ((*prepare_func)(thd, table, check_opt)) { + case 1: continue; // error, message written to net + case -1: goto err; // error, message could be written to net + default: ; // should be 0 otherwise } - - // now we should be able to open the partially restored table - // to finish the restore in the handler later on - if (!(table->table = reopen_name_locked_table(thd, table))) - unlock_table_name(thd, table); } if (!table->table) @@ -1096,7 +1149,8 @@ int mysql_restore_table(THD* thd, TABLE_LIST* table_list) { DBUG_ENTER("mysql_restore_table"); DBUG_RETURN(mysql_admin_table(thd, table_list, 0, - "restore", TL_WRITE, 1, 1,0, + "restore", TL_WRITE, 1, 0, + &prepare_for_restore, &handler::restore)); } @@ -1104,7 +1158,8 @@ int mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { DBUG_ENTER("mysql_repair_table"); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, - "repair", TL_WRITE, 1, 0, HA_OPEN_FOR_REPAIR, + "repair", TL_WRITE, 1, HA_OPEN_FOR_REPAIR, + &prepare_for_repair, &handler::repair)); } @@ -1143,7 +1198,7 @@ int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt) DBUG_ENTER("mysql_check_table"); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, "check", lock_type, - 0, 0, HA_OPEN_FOR_REPAIR, + 0, HA_OPEN_FOR_REPAIR, 0, &handler::check)); } @@ -1157,7 +1212,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, ORDER *order, bool drop_primary, enum enum_duplicates handle_duplicates, - enum enum_enable_or_disable keys_onoff, + enum enum_enable_or_disable keys_onoff, bool simple_alter) { TABLE *table,*new_table; @@ -1672,20 +1727,29 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } } -#if defined( __WIN__) || defined( __EMX__) || defined( OS2) - // Win32 can't rename an open table, so we must close the org table! - table_name=thd->strdup(table_name); // must be saved - if (close_cached_table(thd,table)) - { // Aborted - VOID(quick_rm_table(new_db_type,new_db,tmp_name)); - VOID(pthread_mutex_unlock(&LOCK_open)); - goto err; +#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2)) + if (table->file->has_transactions()) +#endif + { + /* + Win32 and InnoDB can't drop a table that is in use, so we must + close all the original table at before doing the rename + */ + table_name=thd->strdup(table_name); // must be saved + if (close_cached_table(thd,table)) + { // Aborted + VOID(quick_rm_table(new_db_type,new_db,tmp_name)); + VOID(pthread_mutex_unlock(&LOCK_open)); + goto err; + } + table=0; // Marker that table is closed } - table=0; // Marker for win32 version -#else - table->file->extra(HA_EXTRA_FORCE_REOPEN); // Don't use this file anymore +#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2)) + else + table->file->extra(HA_EXTRA_FORCE_REOPEN); // Don't use this file anymore #endif + error=0; if (mysql_rename_table(old_db_type,db,table_name,db,old_name)) { @@ -1763,7 +1827,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(pthread_cond_broadcast(&COND_refresh)); VOID(pthread_mutex_unlock(&LOCK_open)); table_list->table=0; // For query cache - query_cache.invalidate(table_list); + query_cache_invalidate3(thd, table_list, 0); end_temporary: sprintf(tmp_name,ER(ER_INSERT_INFO),(ulong) (copied+deleted), diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 0d8a41e9966..541b2383e8d 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -31,10 +31,12 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) ORDER *order; List<Item> item_list; TABLE *table; + int describe=(lex->select_lex.options & SELECT_DESCRIBE) ? 1 : 0; + int res; TABLE_LIST result_table_list; + TABLE_LIST *first_table=(TABLE_LIST *)lex->select_lex.table_list.first; TMP_TABLE_PARAM tmp_table_param; select_union *union_result; - int res; DBUG_ENTER("mysql_union"); /* Fix tables 'to-be-unioned-from' list to point at opened tables */ @@ -57,8 +59,9 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) the ORDER BY and LIMIT parameter for the whole UNION */ lex_sl= sl; - last_sl->next=0; // Remove this extra element order= (ORDER *) lex_sl->order_list.first; + if (!order || !describe) + last_sl->next=0; // Remove this extra element } else if (!last_sl->braces) { @@ -70,27 +73,26 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) lex_sl=0; order=0; } - - if (lex->select_lex.options & SELECT_DESCRIBE) + + if (describe) { - for (sl= &lex->select_lex; sl; sl=sl->next) - { - lex->select=sl; - res=mysql_select(thd, (TABLE_LIST*) sl->table_list.first, - sl->item_list, - sl->where, - ((sl->braces) ? - (ORDER *) sl->order_list.first : (ORDER *) 0), - (ORDER*) sl->group_list.first, - sl->having, - (ORDER*) NULL, - (sl->options | thd->options | SELECT_NO_UNLOCK | - SELECT_DESCRIBE), - result); - } - DBUG_RETURN(0); + Item *item; + item_list.push_back(new Item_empty_string("table",NAME_LEN)); + item_list.push_back(new Item_empty_string("type",10)); + item_list.push_back(item=new Item_empty_string("possible_keys", + NAME_LEN*MAX_KEY)); + item->maybe_null=1; + item_list.push_back(item=new Item_empty_string("key",NAME_LEN)); + item->maybe_null=1; + item_list.push_back(item=new Item_int("key_len",0,3)); + item->maybe_null=1; + item_list.push_back(item=new Item_empty_string("ref", + NAME_LEN*MAX_REF_PARTS)); + item->maybe_null=1; + item_list.push_back(new Item_real("rows",0.0,0,10)); + item_list.push_back(new Item_empty_string("Extra",255)); } - + else { Item *item; List_iterator<Item> it(lex->select_lex.item_list); @@ -107,7 +109,7 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); tmp_table_param.field_count=item_list.elements; if (!(table=create_tmp_table(thd, &tmp_table_param, item_list, - (ORDER*) 0, !lex->union_option, + (ORDER*) 0, !describe & !lex->union_option, 1, 0, (lex->select_lex.options | thd->options | TMP_TABLE_ALL_COLUMNS)))) @@ -124,8 +126,11 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) res= -1; goto exit; } + union_result->save_time_stamp=!describe; + for (sl= &lex->select_lex; sl; sl=sl->next) { + lex->select=sl; thd->offset_limit=sl->offset_limit; thd->select_limit=sl->select_limit+sl->offset_limit; if (thd->select_limit < sl->select_limit) @@ -133,14 +138,14 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) if (thd->select_limit == HA_POS_ERROR) sl->options&= ~OPTION_FOUND_ROWS; - res=mysql_select(thd, (TABLE_LIST*) sl->table_list.first, + res=mysql_select(thd, (describe && sl->linkage==NOT_A_SELECT) ? first_table : (TABLE_LIST*) sl->table_list.first, sl->item_list, sl->where, (sl->braces) ? (ORDER *)sl->order_list.first : (ORDER *) 0, (ORDER*) sl->group_list.first, sl->having, (ORDER*) NULL, - sl->options | thd->options | SELECT_NO_UNLOCK, + sl->options | thd->options | SELECT_NO_UNLOCK | ((describe) ? SELECT_DESCRIBE : 0), union_result); if (res) goto exit; @@ -153,6 +158,7 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) delete union_result; /* Send result to 'result' */ + lex->select = &lex->select_lex; res =-1; { /* Create a list of fields in the temporary table */ @@ -181,8 +187,15 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) if (thd->select_limit == HA_POS_ERROR) thd->options&= ~OPTION_FOUND_ROWS; } + else + { + thd->offset_limit= 0; + thd->select_limit= thd->default_select_limit; + } + if (describe) + thd->select_limit= HA_POS_ERROR; // no limit res=mysql_select(thd,&result_table_list, - item_list, NULL, /*ftfunc_list,*/ order, + item_list, NULL, (describe) ? 0 : order, (ORDER*) NULL, NULL, (ORDER*) NULL, thd->options, result); } @@ -216,7 +229,7 @@ select_union::~select_union() int select_union::prepare(List<Item> &list) { - if (list.elements != table->fields) + if (save_time_stamp && list.elements != table->fields) { my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0)); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 6c868b542d1..db520af61c1 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -324,7 +324,8 @@ int mysql_update(THD *thd, thd->lock=0; } if (updated) - query_cache.invalidate(table_list); + query_cache_invalidate3(thd, table_list, 1); + delete select; if (error >= 0) send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN : 0); /* purecov: inspected */ @@ -787,7 +788,8 @@ bool multi_update::send_eof() sprintf(buff,ER(ER_UPDATE_INFO), (long) found, (long) updated, (long) thd->cuted_fields); if (updated) - query_cache.invalidate(update_tables); + query_cache_invalidate3(thd, update_tables, 1); + ::send_ok(&thd->net, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, thd->insert_id_used ? thd->insert_id() : 0L,buff); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 91cf0ae5fc9..8012768e508 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -32,7 +32,7 @@ extern void yyerror(const char*); int yylex(void *yylval); -#define yyoverflow(A,B,C,D,E,F) if (my_yyoverflow((B),(D),(F))) { yyerror((char*) (A)); return 2; } +#define yyoverflow(A,B,C,D,E,F) if (my_yyoverflow((B),(D),(int*) (F))) { yyerror((char*) (A)); return 2; } inline Item *or_or_concat(Item* A, Item* B) { @@ -123,6 +123,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token RESET_SYM %token PURGE %token SLAVE +%token IO_THREAD +%token SQL_THREAD %token START_SYM %token STOP_SYM %token TRUNCATE_SYM @@ -239,6 +241,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MASTER_PORT_SYM %token MASTER_CONNECT_RETRY_SYM %token MASTER_SERVER_ID_SYM +%token RELAY_LOG_FILE_SYM +%token RELAY_LOG_POS_SYM %token MATCH %token MAX_ROWS %token MAX_QUERIES_PER_HOUR @@ -322,6 +326,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token UNION_SYM %token UNIQUE_SYM %token USAGE +%token USE_FRM %token USE_SYM %token USING %token VALUES @@ -596,9 +601,17 @@ END_OF_INPUT query: END_OF_INPUT { - if (!current_thd->bootstrap) + THD *thd=current_thd; + if (!thd->bootstrap && + (!(thd->lex.select_lex.options & OPTION_FOUND_COMMENT))) + { send_error(¤t_thd->net,ER_EMPTY_QUERY); - YYABORT; + YYABORT; + } + else + { + thd->lex.sql_command = SQLCOM_EMPTY_QUERY; + } } | verb_clause END_OF_INPUT {} @@ -690,6 +703,16 @@ master_def: { Lex->mi.connect_retry = $3; } + | + RELAY_LOG_FILE_SYM EQ TEXT_STRING + { + Lex->mi.relay_log_name = $3.str; + } + | + RELAY_LOG_POS_SYM EQ ULONG_NUM + { + Lex->mi.relay_log_pos = $3; + } /* create a table */ @@ -776,7 +799,7 @@ opt_table_options: table_options: table_option { $$=$1; } - | table_option table_options { $$= $1 | $2 } + | table_option table_options { $$= $1 | $2; } table_option: TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; } @@ -1248,20 +1271,34 @@ opt_to: | AS {} slave: - SLAVE START_SYM + SLAVE START_SYM slave_thread_opts { LEX *lex=Lex; lex->sql_command = SQLCOM_SLAVE_START; lex->type = 0; } | - SLAVE STOP_SYM + SLAVE STOP_SYM slave_thread_opts { LEX *lex=Lex; lex->sql_command = SQLCOM_SLAVE_STOP; lex->type = 0; }; +slave_thread_opts: slave_thread_opt + | slave_thread_opts ',' slave_thread_opt + +slave_thread_opt: + /*empty*/ {} + | SQL_THREAD + { + Lex->slave_thd_opt|=SLAVE_SQL; + } + | IO_THREAD + { + Lex->slave_thd_opt|=SLAVE_IO; + } + restore: RESTORE_SYM table_or_tables { @@ -1281,7 +1318,6 @@ backup: Lex->backup_dir = $6.str; } - repair: REPAIR table_or_tables { @@ -1289,24 +1325,20 @@ repair: lex->sql_command = SQLCOM_REPAIR; lex->check_opt.init(); } - table_list opt_mi_check_type + table_list opt_mi_repair_type - -opt_mi_check_type: +opt_mi_repair_type: /* empty */ { Lex->check_opt.flags = T_MEDIUM; } - | TYPE_SYM EQ mi_check_types {} - | mi_check_types {} + | mi_repair_types {} -mi_check_types: - mi_check_type {} - | mi_check_type mi_check_types {} +mi_repair_types: + mi_repair_type {} + | mi_repair_type mi_repair_types {} -mi_check_type: - QUICK { Lex->check_opt.quick = 1; } - | FAST_SYM { Lex->check_opt.flags|= T_FAST; } - | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } +mi_repair_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } - | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } + | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; } analyze: ANALYZE_SYM table_or_tables @@ -1326,6 +1358,21 @@ check: } table_list opt_mi_check_type +opt_mi_check_type: + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_check_types {} + +mi_check_types: + mi_check_type {} + | mi_check_type mi_check_types {} + +mi_check_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } + | FAST_SYM { Lex->check_opt.flags|= T_FAST; } + | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } + optimize: OPTIMIZE table_or_tables { @@ -1395,22 +1442,22 @@ select_option_list: select_option: STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } - | HIGH_PRIORITY { Lex->lock_option= TL_READ_HIGH_PRIORITY; } + | HIGH_PRIORITY { if (Select != &Lex->select_lex) YYABORT; Lex->lock_option= TL_READ_HIGH_PRIORITY; } | DISTINCT { Select->options|= SELECT_DISTINCT; } | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } - | SQL_BUFFER_RESULT { Select->options|= OPTION_BUFFER_RESULT; } - | SQL_CALC_FOUND_ROWS { Select->options|= OPTION_FOUND_ROWS; } - | SQL_NO_CACHE_SYM { current_thd->safe_to_cache_query=0; } - | SQL_CACHE_SYM { Select->options |= OPTION_TO_QUERY_CACHE; } + | SQL_BUFFER_RESULT { if (Select != &Lex->select_lex) YYABORT; Select->options|= OPTION_BUFFER_RESULT; } + | SQL_CALC_FOUND_ROWS { if (Select != &Lex->select_lex) YYABORT; Select->options|= OPTION_FOUND_ROWS; } + | SQL_NO_CACHE_SYM { if (Select != &Lex->select_lex) YYABORT; current_thd->safe_to_cache_query=0; } + | SQL_CACHE_SYM { if (Select != &Lex->select_lex) YYABORT; Select->options |= OPTION_TO_QUERY_CACHE; } | ALL {} select_lock_type: /* empty */ | FOR_SYM UPDATE_SYM - { Lex->lock_option= TL_WRITE; current_thd->safe_to_cache_query=0; } + { if (Select != &Lex->select_lex) YYABORT; Lex->lock_option= TL_WRITE; current_thd->safe_to_cache_query=0; } | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM - { Lex->lock_option= TL_READ_WITH_SHARED_LOCKS; current_thd->safe_to_cache_query=0; } + { if (Select != &Lex->select_lex) YYABORT; Lex->lock_option= TL_READ_WITH_SHARED_LOCKS; current_thd->safe_to_cache_query=0; } select_item_list: select_item_list ',' select_item @@ -1606,7 +1653,7 @@ simple_expr: | BINARY expr %prec NEG { $$= new Item_func_binary($2); } | CAST_SYM '(' expr AS cast_type ')' { $$= create_func_cast($3, $5); } | CASE_SYM opt_expr WHEN_SYM when_list opt_else END - { $$= new Item_func_case(* $4, $2, $5 ) } + { $$= new Item_func_case(* $4, $2, $5 ); } | CONVERT_SYM '(' expr ',' cast_type ')' { $$= create_func_cast($3, $5); } | FUNC_ARG0 '(' ')' { $$= ((Item*(*)(void))($1.symbol->create_func))();} @@ -1872,7 +1919,7 @@ sum_expr: { $$=new Item_sum_sum($3); } in_sum_expr: - { Select->in_sum_expr++ } + { Select->in_sum_expr++; } expr { Select->in_sum_expr--; @@ -1920,7 +1967,7 @@ opt_else: | ELSE expr { $$= $2; } when_list: - { Select->when_list.push_front(new List<Item>) } + { Select->when_list.push_front(new List<Item>); } when_list2 { $$= Select->when_list.pop(); } @@ -1945,7 +1992,7 @@ opt_pad: join_table_list: '(' join_table_list ')' { $$=$2; } | join_table { $$=$1; } - | join_table_list normal_join join_table { $$=$3 } + | join_table_list normal_join join_table { $$=$3; } | join_table_list STRAIGHT_JOIN join_table { $$=$3 ; $$->straight=1; } | join_table_list INNER_SYM JOIN_SYM join_table ON expr { add_join_on($4,$6); $$=$4; } @@ -2023,7 +2070,7 @@ opt_key_definition: } key_usage_list: - key_or_index { Select->interval_list.empty() } '(' key_usage_list2 ')' + key_or_index { Select->interval_list.empty(); } '(' key_usage_list2 ')' { $$= &Select->interval_list; } key_usage_list2: @@ -2629,7 +2676,7 @@ describe: YYABORT; } opt_describe_column - | describe_command select { Lex->select_lex.options|= SELECT_DESCRIBE }; + | describe_command select { Lex->select_lex.options|= SELECT_DESCRIBE; } describe_command: @@ -2828,7 +2875,7 @@ literal: | FLOAT_NUM { $$ = new Item_float($1.str, $1.length); } | NULL_SYM { $$ = new Item_null(); Lex->next_state=STATE_OPERATOR_OR_IDENT;} - | HEX_NUM { $$ = new Item_varbinary($1.str,$1.length)}; + | HEX_NUM { $$ = new Item_varbinary($1.str,$1.length);} | DATE_SYM text_literal { $$ = $2; } | TIME_SYM text_literal { $$ = $2; } | TIMESTAMP text_literal { $$ = $2; } @@ -2978,6 +3025,7 @@ keyword: | ISSUER_SYM {} | INNOBASE_SYM {} | INSERT_METHOD {} + | IO_THREAD {} | LAST_SYM {} | LEVEL_SYM {} | LOCAL_SYM {} @@ -3020,6 +3068,8 @@ keyword: | RAID_CHUNKSIZE {} | RAID_STRIPED_SYM {} | RAID_TYPE {} + | RELAY_LOG_FILE_SYM {} + | RELAY_LOG_POS_SYM {} | RELOAD {} | REPAIR {} | REPEATABLE_SYM {} @@ -3039,6 +3089,7 @@ keyword: | SQL_CACHE_SYM {} | SQL_NO_CACHE_SYM {} | SQL_QUERY_CACHE_TYPE_SYM {} + | SQL_THREAD {} | START_SYM {} | STATUS_SYM {} | STOP_SYM {} @@ -3053,10 +3104,10 @@ keyword: | TYPE_SYM {} | UDF_SYM {} | UNCOMMITTED_SYM {} + | USE_FRM {} | VARIABLES {} | WORK_SYM {} | YEAR_SYM {} - | SLAVE {} /* Option functions */ @@ -3069,7 +3120,7 @@ set: lex->select->select_limit=lex->thd->default_select_limit; lex->tx_isolation=lex->thd->tx_isolation; lex->option_type=0; - lex->option_list.empty() + lex->option_list.empty(); } option_value_list diff --git a/sql/table.cc b/sql/table.cc index 9aae9e17e5a..023d4d85df9 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -130,8 +130,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0))); if (read_string(file,(gptr*) &disk_buff,(uint) uint2korr(head+28))) goto err_not_open; /* purecov: inspected */ - outparam->keys=keys=disk_buff[0]; - outparam->keys_in_use= (((key_map) 1) << keys)- (key_map) 1; + outparam->keys=keys= disk_buff[0]; + outparam->keys_in_use= set_bits(key_map, keys); outparam->key_parts=key_parts=disk_buff[1]; n_length=keys*sizeof(KEY)+key_parts*sizeof(KEY_PART_INFO); @@ -576,6 +576,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, delete outparam->file; outparam->file=0; // For easyer errorchecking outparam->db_stat=0; + hash_free(&outparam->name_hash); free_root(&outparam->mem_root,MYF(0)); my_free(outparam->table_name,MYF(MY_ALLOW_ZERO_PTR)); DBUG_RETURN (error); diff --git a/sql/table.h b/sql/table.h index 259c34030b2..209333c24b7 100644 --- a/sql/table.h +++ b/sql/table.h @@ -95,6 +95,7 @@ struct st_table { my_bool db_low_byte_first; /* Portable row format */ my_bool locked_by_flush; my_bool locked_by_name; + my_bool fulltext_searched; my_bool crashed; my_bool is_view; my_bool no_keyread; @@ -111,7 +112,7 @@ struct st_table { char *table_name,*real_name,*path; uint key_length; /* Length of key */ uint tablenr,used_fields,null_bytes; - table_map map; + table_map map; /* ID bit of table (1,2,4,8,16...) */ ulong version,flush_version; uchar *null_flags; IO_CACHE *io_cache; /* If sorted trough file*/ @@ -137,18 +138,26 @@ struct st_table { typedef struct st_table_list { struct st_table_list *next; char *db,*name,*real_name; + uint32 db_length, real_name_length; Item *on_expr; /* Used with outer join */ struct st_table_list *natural_join; /* natural join on this table*/ - List<String> *use_index,*ignore_index; + /* ... join ... USE INDEX ... IGNORE INDEX */ + List<String> *use_index,*ignore_index; TABLE *table; GRANT_INFO grant; thr_lock_type lock_type; uint outer_join; /* Which join type */ bool straight; /* optimize with prev table */ - bool updating; /* for replicate-do/ignore table */ + bool updating; /* for replicate-do/ignore table */ bool shared; /* Used twice in union */ } TABLE_LIST; +typedef struct st_changed_table_list { + struct st_changed_table_list *next; + char *key, *table_name; + uint32 key_length; +} CHANGED_TABLE_LIST; + typedef struct st_open_table_list { struct st_open_table_list *next; diff --git a/sql/unireg.cc b/sql/unireg.cc index 8c72dfc2855..16ba8c7d58b 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -486,11 +486,11 @@ static bool pack_fields(File file,List<create_field> &create_fields) if (field->interval_id > int_count) { int_count=field->interval_id; - tmp.append('\377'); + tmp.append(NAMES_SEP_CHAR); for (const char **pos=field->interval->type_names ; *pos ; pos++) { tmp.append(*pos); - tmp.append('\377'); + tmp.append(NAMES_SEP_CHAR); } tmp.append('\0'); // End of intervall } diff --git a/sql/unireg.h b/sql/unireg.h index 9b220f87918..c4d2052d1da 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -70,11 +70,6 @@ #define FERR -1 /* Error from my_functions */ #define CREATE_MODE 0 /* Default mode on new files */ #define NAMES_SEP_CHAR '\377' /* Char to sep. names */ -#ifdef MSDOS -#define EXTRA_FIELD_CHAR (char) '\234' /* Interchangebly with '#' */ -#else -#define EXTRA_FIELD_CHAR '#' /* Interchangebly with '#' */ -#endif #define READ_RECORD_BUFFER (uint) (IO_SIZE*8) /* Pointer_buffer_size */ #define DISK_BUFFER_SIZE (uint) (IO_SIZE*16) /* Size of diskbuffer */ |