diff options
author | unknown <bell@laptop.sanja.is.com.ua> | 2003-10-06 20:55:06 +0300 |
---|---|---|
committer | unknown <bell@laptop.sanja.is.com.ua> | 2003-10-06 20:55:06 +0300 |
commit | ff8c8fa1efc4a622e571381c34c5a4575d0f3b5e (patch) | |
tree | f658418875ab7ed1c8c3198dc5135387bf23a7a6 /sql | |
parent | 12b769418af82303239d43447def128ed933b440 (diff) | |
parent | 6fc8b48c4652d404b38146844d17ed2a01a3b327 (diff) | |
download | mariadb-git-ff8c8fa1efc4a622e571381c34c5a4575d0f3b5e.tar.gz |
Merge
mysql-test/r/union.result:
Auto merged
mysql-test/t/union.test:
Auto merged
sql/item.cc:
Auto merged
sql/item.h:
Auto merged
sql/item_cmpfunc.cc:
Auto merged
sql/item_cmpfunc.h:
Auto merged
sql/item_subselect.cc:
Auto merged
sql/item_subselect.h:
Auto merged
sql/item_sum.cc:
Auto merged
sql/item_sum.h:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_class.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_derived.cc:
Auto merged
sql/sql_union.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
mysql-test/r/subselect.result:
SCCS merged
mysql-test/t/subselect.test:
SCCS merged
Diffstat (limited to 'sql')
101 files changed, 5007 insertions, 3132 deletions
diff --git a/sql/derror.cc b/sql/derror.cc index 7ebe6e4b3c5..78efdcc33f3 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -49,6 +49,7 @@ static void read_texts(const char *file_name,const char ***point, char name[FN_REFLEN]; const char *buff; uchar head[32],*pos; + CHARSET_INFO *cset; DBUG_ENTER("read_texts"); *point=0; // If something goes wrong @@ -65,6 +66,21 @@ static void read_texts(const char *file_name,const char ***point, head[2] != 2 || head[3] != 1) goto err; /* purecov: inspected */ textcount=head[4]; + + if (!head[30]) + { + sql_print_error("Character set information not found in '%s'. \ +Please install the latest version of this file.",name); + goto err1; + } + + if (!(cset= get_charset(head[30],MYF(MY_WME)))) + { + sql_print_error("Character set #%d is not supported for messagefile '%s'", + (int)head[30],name); + goto err1; + } + length=uint2korr(head+6); count=uint2korr(head+8); if (count < error_messages) @@ -114,9 +130,10 @@ err: buff="Can't find messagefile '%s'"; break; } + sql_print_error(buff,name); +err1: if (file != FERR) VOID(my_close(file,MYF(MY_WME))); - sql_print_error(buff,name); unireg_abort(1); } /* read_texts */ diff --git a/sql/field.cc b/sql/field.cc index 051a4fa8057..68c9922e887 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -771,7 +771,7 @@ int Field_decimal::store(double nr) char buff[320]; fyllchar = zerofill ? (char) '0' : (char) ' '; -#ifdef HAVE_SNPRINTF_ +#ifdef HAVE_SNPRINTF buff[sizeof(buff)-1]=0; // Safety snprintf(buff,sizeof(buff)-1, "%.*f",(int) dec,nr); length=(uint) strlen(buff); @@ -2016,9 +2016,16 @@ double Field_longlong::val_real(void) else #endif longlongget(j,ptr); - return unsigned_flag ? ulonglong2double((ulonglong) j) : (double) j; + /* The following is open coded to avoid a bug in gcc 3.3 */ + if (unsigned_flag) + { + ulonglong tmp= (ulonglong) j; + return ulonglong2double(tmp); + } + return (double) j; } + longlong Field_longlong::val_int(void) { longlong j; @@ -3968,12 +3975,8 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { char *end=ptr+field_length; -#ifdef WANT_TRUE_BINARY_STRINGS - if (!binary) -#endif - while (end > ptr && end[-1] == ' ') - end--; - val_ptr->set((const char*) ptr,(uint) (end - ptr),field_charset); + uint length= field_charset->cset->lengthsp(field_charset, ptr, field_length); + val_ptr->set((const char*) ptr, length, field_charset); return val_ptr; } @@ -4413,8 +4416,11 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) Field_blob::store_length(length); if (table->copy_blobs || length <= MAX_FIELD_WIDTH) { // Must make a copy - value.copy(from,length,charset()); - from=value.ptr(); + if (from != value.ptr()) // For valgrind + { + value.copy(from,length,charset()); + from=value.ptr(); + } } bmove(ptr+packlength,(char*) &from,sizeof(char*)); } @@ -5480,6 +5486,27 @@ create_field::create_field(Field *old_field,Field *orig_field) if (flags & BLOB_FLAG) pack_length= (pack_length- old_field->table->blob_ptr_size + portable_sizeof_char_ptr); + + switch (sql_type) + { + case FIELD_TYPE_BLOB: + switch (pack_length - portable_sizeof_char_ptr) + { + case 1: sql_type= FIELD_TYPE_TINY_BLOB; break; + case 2: sql_type= FIELD_TYPE_BLOB; break; + case 3: sql_type= FIELD_TYPE_MEDIUM_BLOB; break; + default: sql_type= FIELD_TYPE_LONG_BLOB; break; + } + length /= charset->mbmaxlen; + break; + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + length /= charset->mbmaxlen; + break; + default: + break; + } + decimals= old_field->decimals(); if (sql_type == FIELD_TYPE_STRING) { diff --git a/sql/field.h b/sql/field.h index 794b4a89542..fe5141e9d80 100644 --- a/sql/field.h +++ b/sql/field.h @@ -125,6 +125,13 @@ public: { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : table->null_row; } inline bool is_real_null(uint row_offset=0) { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : 0; } + inline bool is_null_in_record(const uchar *record) + { + if (!null_ptr) + return 0; + return test(record[(uint) (null_ptr - (uchar*) table->record[0])] & + null_bit); + } inline void set_null(int row_offset=0) { if (null_ptr) null_ptr[row_offset]|= null_bit; } inline void set_notnull(int row_offset=0) @@ -305,6 +312,11 @@ public: unireg_check_arg, field_name_arg, table_arg, dec_arg, zero_arg,unsigned_arg) {} + Field_decimal(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg,bool unsigned_arg) + :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, table_arg,0,0,unsigned_arg) + {} enum_field_types type() const { return FIELD_TYPE_DECIMAL;} enum ha_base_keytype key_type() const { return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; } @@ -334,6 +346,11 @@ public: unireg_check_arg, field_name_arg, table_arg, 0, zero_arg,unsigned_arg) {} + Field_tiny(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg,bool unsigned_arg) + :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, table_arg,0,0,unsigned_arg) + {} enum Item_result result_type () const { return INT_RESULT; } enum_field_types type() const { return FIELD_TYPE_TINY;} enum ha_base_keytype key_type() const @@ -364,6 +381,11 @@ public: unireg_check_arg, field_name_arg, table_arg, 0, zero_arg,unsigned_arg) {} + Field_short(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg,bool unsigned_arg) + :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, table_arg,0,0,unsigned_arg) + {} enum Item_result result_type () const { return INT_RESULT; } enum_field_types type() const { return FIELD_TYPE_SHORT;} enum ha_base_keytype key_type() const @@ -497,6 +519,11 @@ public: unireg_check_arg, field_name_arg, table_arg, dec_arg, zero_arg,unsigned_arg) {} + Field_float(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg, uint8 dec_arg) + :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, + NONE, field_name_arg, table_arg,dec_arg,0,0) + {} enum_field_types type() const { return FIELD_TYPE_FLOAT;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; } int store(const char *to,uint length,CHARSET_INFO *charset); @@ -558,6 +585,11 @@ public: :Field_str(ptr_arg, len_arg, null, 1, unireg_check_arg, field_name_arg, table_arg, cs) {} + Field_null(uint32 len_arg, const char *field_name_arg, + struct st_table *table_arg, CHARSET_INFO *cs) + :Field_str((char*) 0, len_arg, null, 1, + NONE, field_name_arg, table_arg, cs) + {} enum_field_types type() const { return FIELD_TYPE_NULL;} int store(const char *to, uint length, CHARSET_INFO *cs) { null[0]=1; return 0; } int store(double nr) { null[0]=1; return 0; } @@ -627,6 +659,10 @@ public: :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, 1, 1) {} + Field_year(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg) + :Field_tiny(len_arg,maybe_null_arg,field_name_arg,table_arg,1) + {} enum_field_types type() const { return FIELD_TYPE_YEAR;} int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); diff --git a/sql/filesort.cc b/sql/filesort.cc index 13989bdae8f..1b481924690 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1,4 +1,3 @@ - /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc index dc4f23db3b2..7126b7ee577 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -430,18 +430,9 @@ int main(int argc,char **argv) exit(1); printf("/* Copyright (C) 2001 MySQL AB\n\ - This program is free software; you can redistribute it and/or modify\n\ - it under the terms of the GNU General Public License as published by\n\ - the Free Software Foundation; either version 2 of the License, or\n\ - (at your option) any later version.\n\n\ - This program is distributed in the hope that it will be useful,\n\ - but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ - GNU General Public License for more details.\n\n\ - You should have received a copy of the GNU General Public License\n\ - along with this program; if not, write to the Free Software\n\ - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307\ - USA */\n\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\ + \n*/\n\n"); printf("/* This code is generated by gen_lex_hash.cc that seeks for\ a perfect\nhash function */\n\n"); diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index dbed955c0a9..1b99efeaa43 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -304,11 +304,13 @@ void berkeley_cleanup_log_files(void) char **names; int error; +// by HF. Sometimes it crashes. TODO - find out why +#ifndef EMBEDDED_LIBRARY /* XXX: Probably this should be done somewhere else, and * should be tunable by the user. */ if ((error = db_env->txn_checkpoint(db_env, 0, 0, 0))) my_error(ER_ERROR_DURING_CHECKPOINT, MYF(0), error); /* purecov: inspected */ - +#endif if ((error = db_env->log_archive(db_env, &names, DB_ARCH_ABS)) != 0) { DBUG_PRINT("error", ("log_archive failed (error %d)", error)); /* purecov: inspected */ @@ -1433,6 +1435,8 @@ int ha_berkeley::index_read(byte * buf, const byte * key, } if (key_len == key_info->key_length) { + if (find_flag == HA_READ_AFTER_KEY) + key_info->handler.bdb_return_if_eq= 1; error=read_row(cursor->c_get(cursor, pack_key(&last_key, active_index, key_buff, @@ -1441,6 +1445,7 @@ int ha_berkeley::index_read(byte * buf, const byte * key, (find_flag == HA_READ_KEY_EXACT ? DB_SET : DB_SET_RANGE)), (char*) buf, active_index, &row, (DBT*) 0, 0); + key_info->handler.bdb_return_if_eq= 0; } else { @@ -2263,7 +2268,7 @@ static BDB_SHARE *get_share(const char *table_name, TABLE *table) strmov(share->table_name,table_name); share->key_file = key_file; share->key_type = key_type; - if (hash_insert(&bdb_open_tables, (byte*) share)) + if (my_hash_insert(&bdb_open_tables, (byte*) share)) { pthread_mutex_unlock(&bdb_mutex); /* purecov: inspected */ my_free((gptr) share,0); /* purecov: inspected */ diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index d870d0debfd..4ff5aba4a32 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -28,7 +28,6 @@ InnoDB */ #include "mysql_priv.h" #include "slave.h" -#include "sql_cache.h" #ifdef HAVE_INNOBASE_DB #include <m_ctype.h> @@ -394,7 +393,7 @@ check_trx_exists( { trx_t* trx; - ut_a(thd == current_thd); + DBUG_ASSERT(thd == current_thd); trx = (trx_t*) thd->transaction.all.innobase_tid; @@ -1541,82 +1540,6 @@ ha_innobase::close(void) DBUG_RETURN(0); } -/* The following accessor functions should really be inside MySQL code! */ - -/****************************************************************** -Gets field offset for a field in a table. */ -inline -uint -get_field_offset( -/*=============*/ - /* out: offset */ - TABLE* table, /* in: MySQL table object */ - Field* field) /* in: MySQL field object */ -{ - return((uint) (field->ptr - (char*) table->record[0])); -} - -/****************************************************************** -Checks if a field in a record is SQL NULL. Uses the record format -information in table to track the null bit in record. */ -inline -uint -field_in_record_is_null( -/*====================*/ - /* out: 1 if NULL, 0 otherwise */ - TABLE* table, /* in: MySQL table object */ - Field* field, /* in: MySQL field object */ - char* record) /* in: a row in MySQL format */ -{ - int null_offset; - - if (!field->null_ptr) { - - return(0); - } - - null_offset = (uint) ((char*) field->null_ptr - - (char*) table->record[0]); - - if (record[null_offset] & field->null_bit) { - - return(1); - } - - return(0); -} - -/****************************************************************** -Sets a field in a record to SQL NULL. Uses the record format -information in table to track the null bit in record. */ -inline -void -set_field_in_record_to_null( -/*========================*/ - TABLE* table, /* in: MySQL table object */ - Field* field, /* in: MySQL field object */ - char* record) /* in: a row in MySQL format */ -{ - int null_offset; - - null_offset = (uint) ((char*) field->null_ptr - - (char*) table->record[0]); - - record[null_offset] = record[null_offset] | field->null_bit; -} - -/****************************************************************** -Resets SQL NULL bits in a record to zero. */ -inline -void -reset_null_bits( -/*============*/ - TABLE* table, /* in: MySQL table object */ - char* record) /* in: a row in MySQL format */ -{ - bzero(record, table->null_bytes); -} - extern "C" { /***************************************************************** InnoDB uses this function is to compare two data fields for which the @@ -1826,11 +1749,10 @@ ha_innobase::store_key_val_for_row( blob_data = row_mysql_read_blob_ref(&blob_len, (byte*) (record - + (ulint)get_field_offset(table, field)), + + (ulint) field->offset()), (ulint) field->pack_length()); - ut_a(get_field_offset(table, field) - == key_part->offset); + ut_a(field->offset() == key_part->offset); if (blob_len > key_part->length) { blob_len = key_part->length; } @@ -1885,7 +1807,7 @@ build_template( ibool fetch_all_in_key = FALSE; ulint i; - ut_a(templ_type != ROW_MYSQL_REC_FIELDS || thd == current_thd); + DBUG_ASSERT(templ_type != ROW_MYSQL_REC_FIELDS || thd == current_thd); clust_index = dict_table_get_first_index_noninline(prebuilt->table); @@ -2010,9 +1932,7 @@ build_template( templ->mysql_null_bit_mask = 0; } - templ->mysql_col_offset = (ulint) - get_field_offset(table, field); - + templ->mysql_col_offset = (ulint) field->offset(); templ->mysql_col_len = (ulint) field->pack_length(); templ->type = get_innobase_type_from_mysql_type(field); templ->is_unsigned = (ulint) (field->flags & UNSIGNED_FLAG); @@ -2058,8 +1978,8 @@ ha_innobase::write_row( DBUG_ENTER("ha_innobase::write_row"); - ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + DBUG_ASSERT(prebuilt->trx == + (trx_t*) current_thd->transaction.all.innobase_tid); statistic_increment(ha_write_count, &LOCK_status); @@ -2349,8 +2269,8 @@ calc_row_difference( /* goto skip_field; }*/ - o_ptr = (byte*) old_row + get_field_offset(table, field); - n_ptr = (byte*) new_row + get_field_offset(table, field); + o_ptr = (byte*) old_row + field->offset(); + n_ptr = (byte*) new_row + field->offset(); o_len = field->pack_length(); n_len = field->pack_length(); @@ -2375,13 +2295,11 @@ calc_row_difference( } if (field->null_ptr) { - if (field_in_record_is_null(table, field, - (char*) old_row)) { + if (field->is_null_in_record((uchar*) old_row)) { o_len = UNIV_SQL_NULL; } - if (field_in_record_is_null(table, field, - (char*) new_row)) { + if (field->is_null_in_record((uchar*) new_row)) { n_len = UNIV_SQL_NULL; } } @@ -2433,8 +2351,8 @@ ha_innobase::update_row( DBUG_ENTER("ha_innobase::update_row"); - ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + DBUG_ASSERT(prebuilt->trx == + (trx_t*) current_thd->transaction.all.innobase_tid); if (table->time_stamp) { update_timestamp(new_row + table->time_stamp - 1); @@ -2495,8 +2413,8 @@ ha_innobase::delete_row( DBUG_ENTER("ha_innobase::delete_row"); - ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + DBUG_ASSERT(prebuilt->trx == + (trx_t*) current_thd->transaction.all.innobase_tid); if (last_query_id != user_thd->query_id) { prebuilt->sql_stat_start = TRUE; @@ -2673,8 +2591,8 @@ ha_innobase::index_read( DBUG_ENTER("index_read"); - ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + DBUG_ASSERT(prebuilt->trx == + (trx_t*) current_thd->transaction.all.innobase_tid); statistic_increment(ha_read_key_count, &LOCK_status); @@ -2787,12 +2705,9 @@ ha_innobase::change_active_index( statistic_increment(ha_read_key_count, &LOCK_status); DBUG_ENTER("change_active_index"); + DBUG_ASSERT(user_thd == current_thd); ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); - ut_a(user_thd == current_thd); - - ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + (trx_t*) user_thd->transaction.all.innobase_tid); active_index = keynr; @@ -2877,8 +2792,8 @@ ha_innobase::general_fetch( DBUG_ENTER("general_fetch"); - ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + DBUG_ASSERT(prebuilt->trx == + (trx_t*) current_thd->transaction.all.innobase_tid); innodb_srv_conc_enter_innodb(prebuilt->trx); @@ -3111,8 +3026,8 @@ ha_innobase::rnd_pos( statistic_increment(ha_read_rnd_count, &LOCK_status); - ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + DBUG_ASSERT(prebuilt->trx == + (trx_t*) current_thd->transaction.all.innobase_tid); if (prebuilt->clust_index_was_generated) { /* No primary key was defined for the table and we @@ -3160,8 +3075,8 @@ ha_innobase::position( row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; uint len; - ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + DBUG_ASSERT(prebuilt->trx == + (trx_t*) current_thd->transaction.all.innobase_tid); if (prebuilt->clust_index_was_generated) { /* No primary key was defined for the table and we @@ -3443,7 +3358,7 @@ ha_innobase::create( /* Get the transaction associated with the current thd, or create one if not yet created */ - parent_trx = check_trx_exists(current_thd); + parent_trx = check_trx_exists(thd); /* In case MySQL calls this in the middle of a SELECT query, release possible adaptive hash latch to avoid deadlocks of threads */ @@ -3556,10 +3471,10 @@ ha_innobase::create( } } - if (current_thd->query != NULL) { + if (thd->query != NULL) { error = row_table_add_foreign_constraints(trx, - current_thd->query, norm_name); + thd->query, norm_name); error = convert_error_code_to_mysql(error, NULL); @@ -3616,13 +3531,13 @@ ha_innobase::delete_table( trx_t* parent_trx; trx_t* trx; char norm_name[1000]; - + THD *thd= current_thd; DBUG_ENTER("ha_innobase::delete_table"); /* Get the transaction associated with the current thd, or create one if not yet created */ - parent_trx = check_trx_exists(current_thd); + parent_trx = check_trx_exists(thd); /* In case MySQL calls this in the middle of a SELECT query, release possible adaptive hash latch to avoid deadlocks of threads */ @@ -3637,8 +3552,8 @@ ha_innobase::delete_table( trx = trx_allocate_for_mysql(); - trx->mysql_thd = current_thd; - trx->mysql_query_str = &((*current_thd).query); + trx->mysql_thd = thd; + trx->mysql_query_str = &(thd->query); name_len = strlen(name); @@ -3691,11 +3606,12 @@ innobase_drop_database( char* ptr; int error; char namebuf[10000]; + THD *thd= current_thd; /* Get the transaction associated with the current thd, or create one if not yet created */ - parent_trx = check_trx_exists(current_thd); + parent_trx = check_trx_exists(thd); /* In case MySQL calls this in the middle of a SELECT query, release possible adaptive hash latch to avoid deadlocks of threads */ @@ -3718,8 +3634,8 @@ innobase_drop_database( my_casedn_str(system_charset_info, namebuf); #endif trx = trx_allocate_for_mysql(); - trx->mysql_thd = current_thd; - trx->mysql_query_str = &((*current_thd).query); + trx->mysql_thd = thd; + trx->mysql_query_str = &(thd->query); error = row_drop_database_for_mysql(namebuf, trx); @@ -3759,13 +3675,14 @@ ha_innobase::rename_table( trx_t* trx; char norm_from[1000]; char norm_to[1000]; + THD *thd= current_thd; DBUG_ENTER("ha_innobase::rename_table"); /* Get the transaction associated with the current thd, or create one if not yet created */ - parent_trx = check_trx_exists(current_thd); + parent_trx = check_trx_exists(thd); /* In case MySQL calls this in the middle of a SELECT query, release possible adaptive hash latch to avoid deadlocks of threads */ @@ -3779,8 +3696,8 @@ ha_innobase::rename_table( } trx = trx_allocate_for_mysql(); - trx->mysql_thd = current_thd; - trx->mysql_query_str = &((*current_thd).query); + trx->mysql_thd = thd; + trx->mysql_query_str = &(thd->query); name_len1 = strlen(from); name_len2 = strlen(to); @@ -4666,7 +4583,7 @@ static INNOBASE_SHARE *get_share(const char *table_name) share->table_name_length=length; share->table_name=(char*) (share+1); strmov(share->table_name,table_name); - if (hash_insert(&innobase_open_tables, (mysql_byte*) share)) + if (my_hash_insert(&innobase_open_tables, (mysql_byte*) share)) { pthread_mutex_unlock(&innobase_mutex); my_free((gptr) share,0); diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index e8e4798c2b2..cec1aefa2d5 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -236,6 +236,8 @@ int ha_myisam::open(const char *name, int mode, uint test_if_locked) VOID(mi_extra(file, HA_EXTRA_WAIT_LOCK, 0)); if (!table->db_record_offset) int_table_flags|=HA_REC_NOT_IN_SEQ; + if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)) + int_table_flags|=HA_HAS_CHECKSUM; return (0); } @@ -1109,7 +1111,7 @@ THR_LOCK_DATA **ha_myisam::store_lock(THD *thd, void ha_myisam::update_create_info(HA_CREATE_INFO *create_info) { - table->file->info(HA_STATUS_AUTO | HA_STATUS_CONST); + ha_myisam::info(HA_STATUS_AUTO | HA_STATUS_CONST); if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) { create_info->auto_increment_value=auto_increment_value; @@ -1414,3 +1416,9 @@ int ha_myisam::ft_read(byte * buf) table->status=error ? STATUS_NOT_FOUND: 0; return error; } + +uint ha_myisam::checksum() const +{ + return (uint)file->s->state.checksum; +} + diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index 8486e25556b..e4e3192af10 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -64,6 +64,7 @@ class ha_myisam: public handler uint max_keys() const { return MI_MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } uint max_key_length() const { return MI_MAX_KEY_LENGTH; } + uint checksum() const; int open(const char *name, int mode, uint test_if_locked); int close(void); diff --git a/sql/handler.cc b/sql/handler.cc index b4d370491bb..e14e326792d 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -359,7 +359,10 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) if (trans == &thd->transaction.all && mysql_bin_log.is_open() && my_b_tell(&thd->transaction.trans_log)) { - mysql_bin_log.write(thd, &thd->transaction.trans_log); + if (wait_if_global_read_lock(thd, 0)) + DBUG_RETURN(1); + mysql_bin_log.write(thd, &thd->transaction.trans_log, 1); + start_waiting_global_read_lock(thd); reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE, (my_off_t) 0, 0, 1); thd->transaction.trans_log.end_of_file= max_binlog_cache_size; @@ -442,9 +445,21 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) } #endif if (trans == &thd->transaction.all) + { + /* + Update the binary log with a BEGIN/ROLLBACK block if we have cached some + queries and we updated some non-transactional table. Such cases should + be rare (updating a non-transactional table inside a transaction...). + */ + if (unlikely((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && + mysql_bin_log.is_open() && + my_b_tell(&thd->transaction.trans_log))) + mysql_bin_log.write(thd, &thd->transaction.trans_log, 0); + /* Flushed or not, empty the binlog cache */ reinit_io_cache(&thd->transaction.trans_log, - WRITE_CACHE, (my_off_t) 0, 0, 1); - thd->transaction.trans_log.end_of_file= max_binlog_cache_size; + WRITE_CACHE, (my_off_t) 0, 0, 1); + thd->transaction.trans_log.end_of_file= max_binlog_cache_size; + } thd->variables.tx_isolation=thd->session_tx_isolation; if (operation_done) { @@ -458,9 +473,27 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) /* -Rolls the current transaction back to a savepoint. -Return value: 0 if success, 1 if there was not a savepoint of the given -name. + Rolls the current transaction back to a savepoint. + Return value: 0 if success, 1 if there was not a savepoint of the given + name. + NOTE: how do we handle this (unlikely but legal) case: + [transaction] + [update to non-trans table] + [rollback to savepoint] ? + The problem occurs when a savepoint is before the update to the + non-transactional table. Then when there's a rollback to the savepoint, if we + simply truncate the binlog cache, we lose the part of the binlog cache where + the update is. If we want to not lose it, we need to write the SAVEPOINT + command and the ROLLBACK TO SAVEPOINT command to the binlog cache. The latter + is easy: it's just write at the end of the binlog cache, but the former should + be *inserted* to the place where the user called SAVEPOINT. The solution is + that when the user calls SAVEPOINT, we write it to the binlog cache (so no + need to later insert it). As transactions are never intermixed in the binary log + (i.e. they are serialized), we won't have conflicts with savepoint names when + using mysqlbinlog or in the slave SQL thread. + Then when ROLLBACK TO SAVEPOINT is called, if we updated some + non-transactional table, we don't truncate the binlog cache but instead write + ROLLBACK TO SAVEPOINT to it; otherwise we truncate the binlog cache (which + will chop the SAVEPOINT command from the binlog cache, which is good as in + that case there is no need to have it in the binlog). */ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name) @@ -485,8 +518,24 @@ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name) error=1; } else - reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE, - binlog_cache_pos, 0, 0); + { + /* + Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some + non-transactional table. Otherwise, truncate the binlog cache starting + from the SAVEPOINT command. + */ + if (unlikely((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && + mysql_bin_log.is_open() && + my_b_tell(&thd->transaction.trans_log))) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE); + if (mysql_bin_log.write(&qinfo)) + error= 1; + } + else + reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE, + binlog_cache_pos, 0, 0); + } operation_done=1; #endif if (operation_done) @@ -515,6 +564,13 @@ int ha_savepoint(THD *thd, char *savepoint_name) #ifdef HAVE_INNOBASE_DB innobase_savepoint(thd,savepoint_name, binlog_cache_pos); #endif + /* Write it to the binary log (see comments of ha_rollback_to_savepoint). */ + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE); + if (mysql_bin_log.write(&qinfo)) + error= 1; + } } #endif /* USING_TRANSACTIONS */ DBUG_RETURN(error); @@ -1051,14 +1107,25 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info, void ha_key_cache(void) { - if (keybuff_size) - (void) init_key_cache((ulong) keybuff_size); + /* + The following mutex is not really needed as long as keybuff_size is + treated as a long value, but we use the mutex here to guard for future + changes. + */ + pthread_mutex_lock(&LOCK_global_system_variables); + long tmp= (long) keybuff_size; + pthread_mutex_unlock(&LOCK_global_system_variables); + if (tmp) + (void) init_key_cache(tmp); } void ha_resize_key_cache(void) { - (void) resize_key_cache((ulong) keybuff_size); + pthread_mutex_lock(&LOCK_global_system_variables); + long tmp= (long) keybuff_size; + pthread_mutex_unlock(&LOCK_global_system_variables); + (void) resize_key_cache(tmp); } diff --git a/sql/handler.h b/sql/handler.h index b1b5cfb02b0..b74e06c6edf 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -42,38 +42,39 @@ #define HA_ADMIN_INVALID -5 /* Bits in table_flags() to show what database can do */ -#define HA_READ_RND_SAME 1 /* Read RND-record to KEY-record - (To update with RND-read) */ -#define HA_KEYPOS_TO_RNDPOS 2 /* ha_info gives pos to record */ -#define HA_TABLE_SCAN_ON_INDEX 4 /* No separate data/index file */ -#define HA_REC_NOT_IN_SEQ 8 /* ha_info don't return recnumber; - It returns a position to ha_r_rnd */ -#define HA_HAS_GEOMETRY 16 -#define HA_NO_INDEX 32 /* No index needed for next/prev */ -#define HA_KEY_READ_WRONG_STR 64 /* keyread returns converted strings */ -#define HA_NULL_KEY 128 /* One can have keys with NULL */ -#define HA_DUPP_POS 256 /* ha_position() gives dupp row */ -#define HA_NO_BLOBS 512 /* Doesn't support blobs */ -#define HA_BLOB_KEY (HA_NO_BLOBS*2) /* key on blob */ -#define HA_AUTO_PART_KEY (HA_BLOB_KEY*2) -#define HA_REQUIRE_PRIMARY_KEY (HA_AUTO_PART_KEY*2) -#define HA_NOT_EXACT_COUNT (HA_REQUIRE_PRIMARY_KEY*2) -#define HA_NO_WRITE_DELAYED (HA_NOT_EXACT_COUNT*2) -#define HA_PRIMARY_KEY_IN_READ_INDEX (HA_NO_WRITE_DELAYED*2) -#define HA_DROP_BEFORE_CREATE (HA_PRIMARY_KEY_IN_READ_INDEX*2) -#define HA_NOT_READ_AFTER_KEY (HA_DROP_BEFORE_CREATE*2) -#define HA_NOT_DELETE_WITH_CACHE (HA_NOT_READ_AFTER_KEY*2) -#define HA_NO_TEMP_TABLES (HA_NOT_DELETE_WITH_CACHE*2) -#define HA_NO_PREFIX_CHAR_KEYS (HA_NO_TEMP_TABLES*2) -#define HA_CAN_FULLTEXT (HA_NO_PREFIX_CHAR_KEYS*2) -#define HA_CAN_SQL_HANDLER (HA_CAN_FULLTEXT*2) -#define HA_NO_AUTO_INCREMENT (HA_CAN_SQL_HANDLER*2) +#define HA_READ_RND_SAME 1 /* Read RND-record to KEY-record + (To update with RND-read) */ +#define HA_KEYPOS_TO_RNDPOS 2 /* ha_info gives pos to record */ +#define HA_TABLE_SCAN_ON_INDEX 4 /* No separate data/index file */ +#define HA_REC_NOT_IN_SEQ 8 /* ha_info don't return recnumber; + It returns a position to ha_r_rnd */ +#define HA_HAS_GEOMETRY (1 << 4) +#define HA_NO_INDEX (1 << 5) /* No index needed for next/prev */ +#define HA_KEY_READ_WRONG_STR (1 << 6) /* keyread returns converted strings */ +#define HA_NULL_KEY (1 << 7) /* One can have keys with NULL */ +#define HA_DUPP_POS (1 << 8) /* ha_position() gives dupp row */ +#define HA_NO_BLOBS (1 << 9) /* Doesn't support blobs */ +#define HA_BLOB_KEY (1 << 10) /* key on blob */ +#define HA_AUTO_PART_KEY (1 << 11) +#define HA_REQUIRE_PRIMARY_KEY (1 << 12) +#define HA_NOT_EXACT_COUNT (1 << 13) +#define HA_NO_WRITE_DELAYED (1 << 14) +#define HA_PRIMARY_KEY_IN_READ_INDEX (1 << 15) +#define HA_DROP_BEFORE_CREATE (1 << 16) +#define HA_NOT_READ_AFTER_KEY (1 << 17) +#define HA_NOT_DELETE_WITH_CACHE (1 << 18) +#define HA_NO_TEMP_TABLES (1 << 19) +#define HA_NO_PREFIX_CHAR_KEYS (1 << 20) +#define HA_CAN_FULLTEXT (1 << 21) +#define HA_CAN_SQL_HANDLER (1 << 22) +#define HA_NO_AUTO_INCREMENT (1 << 23) +#define HA_HAS_CHECKSUM (1 << 24) /* Next record gives next record according last record read (even if database is updated after read). Not used at this point. */ -#define HA_LASTKEY_ORDER (HA_NO_AUTO_INCREMENT*2) +#define HA_LASTKEY_ORDER (1 << 25) /* bits in index_flags(index_number) for what you can do with index */ @@ -306,8 +307,8 @@ public: virtual bool check_and_repair(THD *thd) {return 1;} virtual int optimize(THD* thd,HA_CHECK_OPT* check_opt); virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt); - virtual int backup(THD* thd, HA_CHECK_OPT* check_opt); virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt); + virtual int backup(THD* thd, HA_CHECK_OPT* check_opt); /* restore assumes .frm file must exist, and that generate_table() has been called; It will just copy the data file and run repair. @@ -325,8 +326,8 @@ public: virtual char* get_foreign_key_create_info() { return(NULL);} /* gets foreign key create string from InnoDB */ virtual void init_table_handle_for_HANDLER() - { return; } /* prepare InnoDB for HANDLER */ - virtual void free_foreign_key_create_info(char* str) {} + { return; } /* prepare InnoDB for HANDLER */ + 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; @@ -342,6 +343,7 @@ public: virtual uint max_key_part_length() { return 255; } virtual uint min_record_length(uint options) const { return 1; } virtual bool low_byte_first() const { return 1; } + virtual uint checksum() const { return 0; } virtual bool is_crashed() const { return 0; } virtual bool auto_repair() const { return 0; } @@ -355,13 +357,12 @@ public: /* Type of table for caching query */ virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; } - /* - Is query with this cable cachable (have sense only for ASKTRANSACT + /* + Is query with this table cachable (have sense only for ASKTRANSACT tables) */ - static bool caching_allowed(THD* thd, char* table_key, + static bool caching_allowed(THD* thd, char* table_key, uint key_length, uint8 cahe_type); - }; /* Some extern variables used with handlers */ @@ -390,7 +391,7 @@ int ha_delete_table(enum db_type db_type, const char *path); void ha_drop_database(char* path); void ha_key_cache(void); void ha_resize_key_cache(void); -int ha_start_stmt(THD *thd); +int ha_start_stmt(THD *thd); int ha_report_binlog_offset_and_commit(THD *thd, char *log_file_name, my_off_t end_offset); int ha_commit_complete(THD *thd); diff --git a/sql/hash_filo.h b/sql/hash_filo.h index 92cd2658967..d1672e1a60c 100644 --- a/sql/hash_filo.h +++ b/sql/hash_filo.h @@ -116,7 +116,7 @@ public: last_link=last_link->prev_used; hash_delete(&cache,(byte*) tmp); } - if (hash_insert(&cache,(byte*) entry)) + if (my_hash_insert(&cache,(byte*) entry)) { if (free_element) (*free_element)(entry); // This should never happen diff --git a/sql/item.cc b/sql/item.cc index 1a071174210..29cdbaa1e10 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -23,7 +23,8 @@ #include <m_ctype.h> #include "my_dir.h" -static void mark_as_dependent(SELECT_LEX *last, SELECT_LEX *current, +static void mark_as_dependent(THD *thd, + SELECT_LEX *last, SELECT_LEX *current, Item_ident *item); /***************************************************************************** @@ -489,27 +490,38 @@ String *Item_null::val_str(String *str) /* Item_param related */ void Item_param::set_null() -{ - maybe_null=null_value=1; +{ + DBUG_ENTER("Item_param::set_null"); + maybe_null= null_value= 1; + DBUG_VOID_RETURN; } void Item_param::set_int(longlong i) -{ - int_value=(longlong)i; - item_type = INT_ITEM; +{ + DBUG_ENTER("Item_param::set_int"); + int_value= (longlong)i; + item_type= INT_ITEM; + DBUG_PRINT("info", ("integer: %lld", int_value)); + DBUG_VOID_RETURN; } void Item_param::set_double(double value) -{ +{ + DBUG_ENTER("Item_param::set_double"); real_value=value; - item_type = REAL_ITEM; + item_type= REAL_ITEM; + DBUG_PRINT("info", ("double: %lg", real_value)); + DBUG_VOID_RETURN; } void Item_param::set_value(const char *str, uint length) -{ - str_value.set(str,length,default_charset()); - item_type = STRING_ITEM; +{ + DBUG_ENTER("Item_param::set_value"); + str_value.copy(str,length,default_charset()); + item_type= STRING_ITEM; + DBUG_PRINT("info", ("string: %s", str_value.ptr())); + DBUG_VOID_RETURN; } @@ -751,17 +763,29 @@ bool Item_ref_null_helper::get_date(TIME *ltime, bool fuzzydate) SYNOPSIS mark_as_dependent() + thd - thread handler last - select from which current item depend current - current select item - item which should be marked */ -static void mark_as_dependent(SELECT_LEX *last, SELECT_LEX *current, +static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, Item_ident *item) { // store pointer on SELECT_LEX from wich item is dependent item->depended_from= last; current->mark_as_dependent(last); + if (thd->lex.describe) + { + char warn_buff[MYSQL_ERRMSG_SIZE]; + sprintf(warn_buff, ER(ER_WARN_FIELD_RESOLVED), + (item->db_name?item->db_name:""), (item->db_name?".":""), + (item->table_name?item->table_name:""), (item->table_name?".":""), + item->field_name, + current->select_number, last->select_number); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_WARN_FIELD_RESOLVED, warn_buff); + } } @@ -844,12 +868,12 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) if (rf->fix_fields(thd, tables, ref) || rf->check_cols(1)) return 1; - mark_as_dependent(last, cursel, rf); + mark_as_dependent(thd, last, cursel, rf); return 0; } else { - mark_as_dependent(last, cursel, this); + mark_as_dependent(thd, last, cursel, this); if (last->having_fix_field) { Item_ref *rf; @@ -903,6 +927,13 @@ void Item::make_field(Send_field *tmp_field) init_make_field(tmp_field, field_type()); } + +void Item_empty_string::make_field(Send_field *tmp_field) +{ + init_make_field(tmp_field,FIELD_TYPE_VAR_STRING); +} + + enum_field_types Item::field_type() const { return ((result_type() == STRING_RESULT) ? FIELD_TYPE_VAR_STRING : @@ -910,6 +941,73 @@ enum_field_types Item::field_type() const FIELD_TYPE_DOUBLE); } +Field *Item::tmp_table_field_from_field_type(TABLE *table) +{ + switch (field_type()) + { + case MYSQL_TYPE_DECIMAL: + return new Field_decimal(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_TINY: + return new Field_tiny(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_SHORT: + return new Field_short(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_LONG: + return new Field_long(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_FLOAT: + return new Field_float(max_length, maybe_null, name, table, decimals); + case MYSQL_TYPE_DOUBLE: + return new Field_double(max_length, maybe_null, name, table, decimals); + case MYSQL_TYPE_NULL: + return new Field_null(max_length, name, table, &my_charset_bin); +#ifdef HAVE_LONG_LONG + case MYSQL_TYPE_LONGLONG: + return new Field_longlong(max_length, maybe_null, name, table, + unsigned_flag); +#endif + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_INT24: + return new Field_long(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_DATE: + return new Field_date(maybe_null, name, table, &my_charset_bin); + case MYSQL_TYPE_TIME: + return new Field_time(maybe_null, name, table, &my_charset_bin); + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + return new Field_datetime(maybe_null, name, table, &my_charset_bin); + case MYSQL_TYPE_YEAR: + return new Field_year(max_length, maybe_null, name, table); + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + return new Field_long(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_GEOMETRY: + return new Field_blob(max_length, maybe_null, name, table, collation.collation); + case MYSQL_TYPE_VAR_STRING: + if (max_length > 255) + return new Field_blob(max_length, maybe_null, name, table, collation.collation); + else + return new Field_varstring(max_length, maybe_null, name, table, collation.collation); + case MYSQL_TYPE_STRING: + if (max_length > 255) + return new Field_blob(max_length, maybe_null, name, table, collation.collation); + else + return new Field_string(max_length, maybe_null, name, table, collation.collation); + default: + // This case should never be choosen + DBUG_ASSERT(0); + return 0; + } +} + /* ARGSUSED */ void Item_field::make_field(Send_field *tmp_field) { @@ -1324,7 +1422,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) Item_field* fld; if (!((*reference)= fld= new Item_field(tmp))) return 1; - mark_as_dependent(last, thd->lex.current_select, fld); + mark_as_dependent(thd, last, thd->lex.current_select, fld); return 0; } else @@ -1335,7 +1433,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) "forward reference in item list"); return -1; } - mark_as_dependent(last, thd->lex.current_select, + mark_as_dependent(thd, last, thd->lex.current_select, this); ref= last->ref_pointer_array + counter; } diff --git a/sql/item.h b/sql/item.h index 4a1c6ac37b6..a126a61e32e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 +/* Copyright (C) 2000-2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 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 @@ -120,6 +120,9 @@ public: Constructor used by Item_field, Item_ref & agregate (sum) functions. Used for duplicating lists in processing queries with temporary tables + Also it used for Item_cond_and/Item_cond_or for creating + top AND/OR ctructure of WHERE clause to protect it of + optimisation changes in prepared statements */ Item(THD *thd, Item &item); virtual ~Item() { name=0; } /*lint -e1509 */ @@ -140,7 +143,7 @@ public: virtual double val()=0; virtual longlong val_int()=0; virtual String *val_str(String*)=0; - virtual Field *tmp_table_field() { return 0; } + virtual Field *get_tmp_table_field() { return 0; } virtual Field *tmp_table_field(TABLE *t_arg) { return 0; } virtual const char *full_name() const { return name ? name : "???"; } virtual double val_result() { return val(); } @@ -184,6 +187,7 @@ public: virtual void save_in_result_field(bool no_conversions) {} virtual void no_rows_in_result() {} virtual Item *copy_or_same(THD *thd) { return this; } + virtual Item *copy_andor_structure(THD *thd) { return this; } virtual Item *real_item() { return this; } virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); } @@ -206,6 +210,8 @@ public: virtual bool null_inside() { return 0; } // used in row subselects to get value of elements virtual void bring_value() {} + + Field *tmp_table_field_from_field_type(TABLE *table); }; @@ -266,7 +272,7 @@ public: { return field->type(); } - Field *tmp_table_field() { return result_field; } + Field *get_tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return result_field; } bool get_date(TIME *ltime,bool fuzzydate); bool get_date_result(TIME *ltime,bool fuzzydate); @@ -341,7 +347,11 @@ public: void set_time(TIME *tm, timestamp_type type); bool get_time(TIME *tm); void reset() {} +#ifndef EMBEDDED_LIBRARY void (*setup_param_func)(Item_param *param, uchar **pos); +#else + void (*setup_param_func)(Item_param *param, uchar **pos, ulong data_len); +#endif enum Item_result result_type () const { return item_result_type; } String *query_val_str(String *str); @@ -504,6 +514,7 @@ public: Item_empty_string(const char *header,uint length) :Item_string("",0, &my_charset_bin) { name=(char*) header; max_length=length;} + void make_field(Send_field *field); }; class Item_return_int :public Item_int @@ -540,12 +551,12 @@ class Item_result_field :public Item /* Item with result field */ public: Field *result_field; /* Save result here */ Item_result_field() :result_field(0) {} - // Constructor used for Item_sum (see Item comment) + // Constructor used for Item_sum/Item_cond_and/or (see Item comment) Item_result_field(THD *thd, Item_result_field &item): Item(thd, item), result_field(item.result_field) {} ~Item_result_field() {} /* Required with gcc 2.95 */ - Field *tmp_table_field() { return result_field; } + Field *get_tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return result_field; } table_map used_tables() const { return 1; } virtual void fix_length_and_dec()=0; @@ -863,6 +874,7 @@ public: { value= item->val_int_result(); null_value= item->null_value; + collation.set(item->collation); } double val() { return (double) value; } longlong val_int() { return value; } @@ -880,6 +892,7 @@ public: { value= item->val_result(); null_value= item->null_value; + collation.set(item->collation); } double val() { return value; } longlong val_int() { return (longlong) (value+(value > 0 ? 0.5 : -0.5)); } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 01428dbf995..37a644256f4 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 +/* Copyright (C) 2000-2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 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 @@ -561,6 +561,7 @@ void Item_func_interval::fix_length_and_dec() used_tables_cache|= row->used_tables(); not_null_tables_cache&= row->not_null_tables(); with_sum_func= with_sum_func || row->with_sum_func; + const_item_cache&= row->const_item(); } @@ -714,8 +715,21 @@ Item_func_ifnull::fix_length_and_dec() agg_arg_collations(collation, args, arg_count); else if (cached_result_type != REAL_RESULT) decimals= 0; + + cached_field_type= args[0]->field_type(); + if (cached_field_type != args[1]->field_type()) + cached_field_type= Item_func::field_type(); } +enum_field_types Item_func_ifnull::field_type() const +{ + return cached_field_type; +} + +Field *Item_func_ifnull::tmp_table_field(TABLE *table) +{ + return tmp_table_field_from_field_type(table); +} double Item_func_ifnull::val() @@ -1461,17 +1475,20 @@ void Item_func_in::fix_length_and_dec() DBUG_ASSERT(0); return; } - uint j=0; - for (uint i=1 ; i < arg_count ; i++) + if (array && !(current_thd->is_fatal_error)) // If not EOM { - array->set(j,args[i]); - if (!args[i]->null_value) // Skip NULL values - j++; - else - have_null= 1; + uint j=0; + for (uint i=1 ; i < arg_count ; i++) + { + array->set(j,args[i]); + if (!args[i]->null_value) // Skip NULL values + j++; + else + have_null= 1; + } + if ((array->used_count=j)) + array->sort(); } - if ((array->used_count=j)) - array->sort(); } else { @@ -1552,6 +1569,31 @@ longlong Item_func_bit_and::val_int() return (longlong) (arg1 & arg2); } +Item_cond::Item_cond(THD *thd, Item_cond &item) + :Item_bool_func(thd, item), + abort_on_null(item.abort_on_null), + and_tables_cache(item.and_tables_cache) +{ + /* + here should be following text: + + List_iterator_fast<Item*> li(item.list); + while(Item *it= li++) + list.push_back(it); + + but it do not need, + because this constructor used only for AND/OR and + argument list will be copied by copy_andor_arguments call + */ + +} + +void Item_cond::copy_andor_arguments(THD *thd, Item_cond *item) +{ + List_iterator_fast<Item> li(item->list); + while(Item *it= li++) + list.push_back(it->copy_andor_structure(thd)); +} bool Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) @@ -1570,7 +1612,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) and_tables_cache= ~(table_map) 0; if (thd && check_stack_overrun(thd,buff)) - return 0; // Fatal error flag is set! + return 1; // Fatal error flag is set! while ((item=li++)) { table_map tmp_table_map; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index f705d046229..42b73c48606 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -82,6 +82,7 @@ public: Item_bool_func() :Item_int_func() {} Item_bool_func(Item *a) :Item_int_func(a) {} Item_bool_func(Item *a,Item *b) :Item_int_func(a,b) {} + Item_bool_func(THD *thd, Item_bool_func &item) :Item_int_func(thd, item) {} void fix_length_and_dec() { decimals=0; max_length=1; } }; @@ -115,8 +116,8 @@ protected: String tmp_value1,tmp_value2; public: - Item_bool_func2(Item *a,Item *b): - Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1) {} + Item_bool_func2(Item *a,Item *b) + :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1) {} void fix_length_and_dec(); void set_cmp_func() { @@ -168,7 +169,7 @@ public: class Item_func_eq :public Item_bool_rowready_func2 { public: - Item_func_eq(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {}; + Item_func_eq(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {} longlong val_int(); enum Functype functype() const { return EQ_FUNC; } enum Functype rev_functype() const { return EQ_FUNC; } @@ -290,6 +291,8 @@ public: class Item_func_ifnull :public Item_func { enum Item_result cached_result_type; + enum_field_types cached_field_type; + bool field_type_defined; public: Item_func_ifnull(Item *a,Item *b) :Item_func(a,b), cached_result_type(INT_RESULT) @@ -298,8 +301,10 @@ public: longlong val_int(); String *val_str(String *str); enum Item_result result_type () const { return cached_result_type; } + enum_field_types field_type() const; void fix_length_and_dec(); const char *func_name() const { return "ifnull"; } + Field *tmp_table_field(TABLE *table); table_map not_null_tables() const { return 0; } }; @@ -797,8 +802,13 @@ protected: public: /* Item_cond() is only used to create top level items */ Item_cond() : Item_bool_func(), abort_on_null(1) { const_item_cache=0; } - Item_cond(Item *i1,Item *i2) :Item_bool_func(), abort_on_null(0) - { list.push_back(i1); list.push_back(i2); } + Item_cond(Item *i1,Item *i2) + :Item_bool_func(), abort_on_null(0) + { + list.push_back(i1); + list.push_back(i2); + } + Item_cond(THD *thd, Item_cond &item); ~Item_cond() { list.delete_elements(); } bool add(Item *item) { return list.push_back(item); } bool fix_fields(THD *, struct st_table_list *, Item **ref); @@ -811,6 +821,7 @@ public: void split_sum_func(Item **ref_pointer_array, List<Item> &fields); friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds); void top_level_item() { abort_on_null=1; } + void copy_andor_arguments(THD *thd, Item_cond *item); bool walk(Item_processor processor, byte *arg); }; @@ -821,9 +832,17 @@ class Item_cond_and :public Item_cond public: Item_cond_and() :Item_cond() {} Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {} + Item_cond_and(THD *thd, Item_cond_and &item) :Item_cond(thd, item) {} enum Functype functype() const { return COND_AND_FUNC; } longlong val_int(); const char *func_name() const { return "and"; } + Item* copy_andor_structure(THD *thd) + { + Item_cond_and *item; + if((item= new Item_cond_and(thd, *this))) + item->copy_andor_arguments(thd, this); + return item; + } }; class Item_cond_or :public Item_cond @@ -831,10 +850,18 @@ class Item_cond_or :public Item_cond public: Item_cond_or() :Item_cond() {} Item_cond_or(Item *i1,Item *i2) :Item_cond(i1,i2) {} + Item_cond_or(THD *thd, Item_cond_or &item) :Item_cond(thd, item) {} enum Functype functype() const { return COND_OR_FUNC; } longlong val_int(); const char *func_name() const { return "or"; } table_map not_null_tables() const { return and_tables_cache; } + Item* copy_andor_structure(THD *thd) + { + Item_cond_or *item; + if((item= new Item_cond_or(thd, *this))) + item->copy_andor_arguments(thd, this); + return item; + } }; diff --git a/sql/item_create.cc b/sql/item_create.cc index e3e3c021a1e..b1173b9c7b8 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -52,13 +52,6 @@ Item *create_func_ord(Item* a) return new Item_func_ord(a); } -Item *create_func_old_password(Item* a) -{ - return new Item_func_old_password(a); -} - - - Item *create_func_asin(Item* a) { return new Item_func_asin(a); @@ -107,14 +100,6 @@ Item *create_func_cot(Item* a) new Item_func_tan(a)); } - -#ifdef HAVE_COMPRESS -Item *create_func_crc32(Item* a) -{ - return new Item_func_crc32(a); -} -#endif - Item *create_func_date_format(Item* a,Item *b) { return new Item_func_date_format(a,b,0); @@ -332,11 +317,6 @@ Item *create_func_quarter(Item* a) return new Item_func_quarter(a); } -Item *create_func_password(Item* a) -{ - return new Item_func_password(a); -} - Item *create_func_radians(Item *a) { return new Item_func_units((char*) "radians",a,M_PI/180,0.0); @@ -666,13 +646,10 @@ Item *create_func_point(Item *a, Item *b) return new Item_func_point(a, b); } -#if !defined(HAVE_COMPRESS) - -Item *create_func_compress (Item*a __attribute__((unused))){return 0;} -Item *create_func_uncompress (Item*a __attribute__((unused))){return 0;} -Item *create_func_uncompressed_length(Item*a __attribute__((unused))){return 0;} - -#else +Item *create_func_crc32(Item* a) +{ + return new Item_func_crc32(a); +} Item *create_func_compress(Item* a) { @@ -689,8 +666,6 @@ Item *create_func_uncompressed_length(Item* a) return new Item_func_uncompressed_length(a); } -#endif - Item *create_func_datediff(Item *a, Item *b) { return new Item_func_minus(new Item_func_to_days(a), diff --git a/sql/item_create.h b/sql/item_create.h index 16c4fccf709..c75f4404bad 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -33,9 +33,7 @@ Item *create_func_connection_id(void); Item *create_func_conv(Item* a, Item *b, Item *c); Item *create_func_cos(Item* a); Item *create_func_cot(Item* a); -#ifdef HAVE_COMPRESS Item *create_func_crc32(Item* a); -#endif Item *create_func_date_format(Item* a,Item *b); Item *create_func_dayname(Item* a); Item *create_func_dayofmonth(Item* a); @@ -69,14 +67,12 @@ Item *create_func_monthname(Item* a); Item *create_func_nullif(Item* a, Item *b); Item *create_func_oct(Item *); Item *create_func_ord(Item* a); -Item *create_func_old_password(Item* a); Item *create_func_period_add(Item* a, Item *b); Item *create_func_period_diff(Item* a, Item *b); Item *create_func_pi(void); Item *create_func_pow(Item* a, Item *b); Item *create_func_current_user(void); Item *create_func_quarter(Item* a); -Item *create_func_password(Item* a); Item *create_func_radians(Item *a); Item *create_func_release_lock(Item* a); Item *create_func_repeat(Item* a, Item *b); diff --git a/sql/item_func.cc b/sql/item_func.cc index 4bda8ae78cd..0f9ee512be1 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -27,22 +27,18 @@ #include <hash.h> #include <time.h> #include <ft_global.h> -#ifdef HAVE_COMPRESS -#include <zlib.h> -#endif static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname) { my_error(ER_CANT_AGGREGATE_2COLLATIONS,MYF(0), - c1.collation->name,c1.derivation_name(), + c1.collation->name,c1.derivation_name(), c2.collation->name,c2.derivation_name(), fname); } - -static void my_coll_agg_error(DTCollation &c1, +static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, DTCollation &c3, const char *fname) @@ -134,6 +130,27 @@ Item_func::Item_func(List<Item> &list) set_arguments(list); } +Item_func::Item_func(THD *thd, Item_func &item) + :Item_result_field(thd, item), + allowed_arg_cols(item.allowed_arg_cols), + arg_count(item.arg_count), + used_tables_cache(item.used_tables_cache), + not_null_tables_cache(item.not_null_tables_cache), + const_item_cache(item.const_item_cache) +{ + if (arg_count) + { + if (arg_count <=2) + args= tmp_arg; + else + { + if (!(args=(Item**) thd->alloc(sizeof(Item*)*arg_count))) + return; + } + memcpy((char*) args, (char*) item.args, sizeof(Item*)*arg_count); + } +} + /* Resolve references to table column for a function and it's argument @@ -173,13 +190,15 @@ bool Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { Item **arg,**arg_end; +#ifndef EMBEDDED_LIBRARY // Avoid compiler warning char buff[STACK_BUFF_ALLOC]; // Max argument in function +#endif used_tables_cache= not_null_tables_cache= 0; const_item_cache=1; if (thd && check_stack_overrun(thd,buff)) - return 0; // Fatal error if flag is set! + return 1; // Fatal error if flag is set! if (arg_count) { // Print purify happy for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++) @@ -1085,36 +1104,6 @@ longlong Item_func_min_max::val_int() return value; } - -#ifdef HAVE_COMPRESS -longlong Item_func_crc32::val_int() -{ - String *res=args[0]->val_str(&value); - if (!res) - { - null_value=1; - return 0; /* purecov: inspected */ - } - null_value=0; - return (longlong) crc32(0L, (Bytef*)res->ptr(), res->length()); -} - - -longlong Item_func_uncompressed_length::val_int() -{ - String *res= args[0]->val_str(&value); - if (!res) - { - null_value=1; - return 0; /* purecov: inspected */ - } - null_value=0; - if (res->is_empty()) return 0; - return uint4korr(res->c_ptr()) & 0x3FFFFFFF; -} -#endif /* HAVE_COMPRESS */ - - longlong Item_func_length::val_int() { String *res=args[0]->val_str(&value); @@ -1164,7 +1153,6 @@ longlong Item_func_locate::val_int() { String *a=args[0]->val_str(&value1); String *b=args[1]->val_str(&value2); - bool binary_cmp= (cmp_collation.collation->state & MY_CS_BINSORT) ? 1 : 0; if (!a || !b) { null_value=1; @@ -1172,55 +1160,27 @@ longlong Item_func_locate::val_int() } null_value=0; uint start=0; -#ifdef USE_MB uint start0=0; -#endif + my_match_t match; + if (arg_count == 3) { - start=(uint) args[2]->val_int()-1; -#ifdef USE_MB - if (use_mb(cmp_collation.collation)) - { - start0=start; - if (!binary_cmp) - start=a->charpos(start); - } -#endif + start0= start =(uint) args[2]->val_int()-1; + start=a->charpos(start); + if (start > a->length() || start+b->length() > a->length()) return 0; } + if (!b->length()) // Found empty string at start return (longlong) (start+1); -#ifdef USE_MB - if (use_mb(cmp_collation.collation) && !binary_cmp) - { - const char *ptr=a->ptr()+start; - const char *search=b->ptr(); - const char *strend = ptr+a->length(); - const char *end=strend-b->length()+1; - const char *search_end=search+b->length(); - register uint32 l; - while (ptr < end) - { - if (*ptr == *search) - { - register char *i,*j; - i=(char*) ptr+1; j=(char*) search+1; - while (j != search_end) - if (*i++ != *j++) goto skipp; - return (longlong) start0+1; - } - skipp: - if ((l=my_ismbchar(cmp_collation.collation,ptr,strend))) - ptr+=l; - else ++ptr; - ++start0; - } + + if (!cmp_collation.collation->coll->instr(cmp_collation.collation, + a->ptr()+start, a->length()-start, + b->ptr(), b->length(), + &match, 1)) return 0; - } -#endif /* USE_MB */ - return (longlong) (binary_cmp ? a->strstr(*b,start) : - (a->strstr_case(*b,start)))+1; + return (longlong) match.mblen + start0 + 1; } @@ -1431,13 +1391,15 @@ bool udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, uint arg_count, Item **arguments) { +#ifndef EMBEDDED_LIBRARY // Avoid compiler warning char buff[STACK_BUFF_ALLOC]; // Max argument in function +#endif DBUG_ENTER("Item_udf_func::fix_fields"); if (thd) { if (check_stack_overrun(thd,buff)) - return 0; // Fatal error flag is set! + DBUG_RETURN(1); // Fatal error flag is set! } else thd=current_thd; // In WHERE / const clause @@ -1751,7 +1713,7 @@ public: pthread_cond_init(&cond,NULL); if (key) { - if (hash_insert(&hash_user_locks,(byte*) this)) + if (my_hash_insert(&hash_user_locks,(byte*) this)) { my_free((gptr) key,MYF(0)); key=0; @@ -2109,10 +2071,20 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name, entry->value=0; entry->length=0; entry->update_query_id=0; + /* + If we are here, we were called from a SET or a query which sets a + variable. Imagine it is this: + INSERT INTO t SELECT @a:=10, @a:=@a+1. + Then when we have a Item_func_get_user_var (because of the @a+1) so we + think we have to write the value of @a to the binlog. But before that, + we have a Item_func_set_user_var to create @a (@a:=10), in this we mark + the variable as "already logged" (line below) so that it won't be logged + by Item_func_get_user_var (because that's not necessary). + */ entry->used_query_id=current_thd->query_id; entry->type=STRING_RESULT; memcpy(entry->name.str, name.str, name.length+1); - if (hash_insert(hash,(byte*) entry)) + if (my_hash_insert(hash,(byte*) entry)) { my_free((char*) entry,MYF(0)); return 0; @@ -2121,7 +2093,10 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name, return entry; } - +/* + When a user variable is updated (in a SET command or a query like SELECT @a:= + ). +*/ bool Item_func_set_user_var::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) @@ -2131,6 +2106,11 @@ bool Item_func_set_user_var::fix_fields(THD *thd, TABLE_LIST *tables, !(entry= get_variable(&thd->user_vars, name, 1))) return 1; entry->type= cached_result_type; + /* + Remember the last query which updated it, this way a query can later know + if this variable is a constant item in the query (it is if update_query_id + is different from query_id). + */ entry->update_query_id=thd->query_id; return 0; } @@ -2353,53 +2333,92 @@ longlong Item_func_get_user_var::val_int() } +/* + When a user variable is invoked from an update query (INSERT, UPDATE etc), + stores this variable and its value in thd->user_var_events, so that it can be + written to the binlog (will be written just before the query is written, see + log.cc). +*/ + void Item_func_get_user_var::fix_length_and_dec() { - BINLOG_USER_VAR_EVENT *user_var_event; THD *thd=current_thd; + BINLOG_USER_VAR_EVENT *user_var_event; maybe_null=1; decimals=NOT_FIXED_DEC; max_length=MAX_BLOB_WIDTH; - if ((var_entry= get_variable(&thd->user_vars, name, 0))) + var_entry= get_variable(&thd->user_vars, name, 0); + + if (!(opt_bin_log && is_update_query(thd->lex.sql_command))) + return; + + if (!var_entry) { - if (opt_bin_log && is_update_query(thd->lex.sql_command) && - var_entry->used_query_id != thd->query_id) - { - uint size; - /* - First we need to store value of var_entry, when the next situation - appers: - > set @a:=1; - > insert into t1 values (@a), (@a:=@a+1), (@a:=@a+1); - We have to write to binlog value @a= 1; - */ - size= ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)) + var_entry->length; - if (!(user_var_event= (BINLOG_USER_VAR_EVENT *) thd->alloc(size))) - goto err; - - user_var_event->value= (char*) user_var_event + - ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)); - user_var_event->user_var_event= var_entry; - user_var_event->type= var_entry->type; - user_var_event->charset_number= var_entry->collation.collation->number; - if (!var_entry->value) - { - /* NULL value*/ - user_var_event->length= 0; - user_var_event->value= 0; - } - else - { - user_var_event->length= var_entry->length; - memcpy(user_var_event->value, var_entry->value, - var_entry->length); - } - var_entry->used_query_id= thd->query_id; - if (insert_dynamic(&thd->user_var_events, (gptr) &user_var_event)) - goto err; - } + /* + If the variable does not exist, it's NULL, but we want to create it so + that it gets into the binlog (if it didn't, the slave could be + influenced by a variable of the same name previously set by another + thread). + We create it like if it had been explicitely set with SET before. + The 'new' mimicks what sql_yacc.yy does when 'SET @a=10;'. + sql_set_variables() is what is called from 'case SQLCOM_SET_OPTION' + in dispatch_command()). Instead of building a one-element list to pass to + sql_set_variables(), we could instead manually call check() and update(); + this would save memory and time; but calling sql_set_variables() makes one + unique place to maintain (sql_set_variables()). + */ + + List<set_var_base> tmp_var_list; + tmp_var_list.push_back(new set_var_user(new Item_func_set_user_var(name, + new Item_null()))); + if (sql_set_variables(thd, &tmp_var_list)) /* this will create the variable */ + goto err; + if (!(var_entry= get_variable(&thd->user_vars, name, 0))) + goto err; + } + /* + If this variable was already stored in user_var_events by this query + (because it's used in more than one place in the query), don't store + it. + */ + else if (var_entry->used_query_id == thd->query_id) + return; + + uint size; + /* + First we need to store value of var_entry, when the next situation + appers: + > set @a:=1; + > insert into t1 values (@a), (@a:=@a+1), (@a:=@a+1); + We have to write to binlog value @a= 1; + */ + size= ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)) + var_entry->length; + if (!(user_var_event= (BINLOG_USER_VAR_EVENT *) thd->alloc(size))) + goto err; + + user_var_event->value= (char*) user_var_event + + ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)); + user_var_event->user_var_event= var_entry; + user_var_event->type= var_entry->type; + user_var_event->charset_number= var_entry->collation.collation->number; + if (!var_entry->value) + { + /* NULL value*/ + user_var_event->length= 0; + user_var_event->value= 0; } + else + { + user_var_event->length= var_entry->length; + memcpy(user_var_event->value, var_entry->value, + var_entry->length); + } + /* Mark that this variable has been used by this query */ + var_entry->used_query_id= thd->query_id; + if (insert_dynamic(&thd->user_var_events, (gptr) &user_var_event)) + goto err; + return; err: diff --git a/sql/item_func.h b/sql/item_func.h index 7fedbcf48ee..86cf19d92f3 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -104,6 +104,8 @@ public: } } Item_func(List<Item> &list); + // Constructor used for Item_cond_and/or (see Item comment) + Item_func(THD *thd, Item_func &item); ~Item_func() {} /* Nothing to do; Items are freed automaticly */ bool fix_fields(THD *,struct st_table_list *, Item **ref); table_map used_tables() const; @@ -196,6 +198,7 @@ public: Item_int_func(Item *a,Item *b) :Item_func(a,b) { max_length=21; } Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) { max_length=21; } Item_int_func(List<Item> &list) :Item_func(list) { max_length=21; } + Item_int_func(THD *thd, Item_int_func &item) :Item_func(thd, item) {} double val() { return (double) val_int(); } String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } @@ -538,6 +541,7 @@ public: String *val_str(String *); void fix_length_and_dec(); enum Item_result result_type () const { return cmp_type; } + table_map not_null_tables() const { return 0; } }; class Item_func_min :public Item_func_min_max @@ -555,27 +559,6 @@ public: }; -#ifdef HAVE_COMPRESS -class Item_func_crc32 :public Item_int_func -{ - String value; -public: - Item_func_crc32(Item *a) :Item_int_func(a) {} - longlong val_int(); - const char *func_name() const { return "crc32"; } - void fix_length_and_dec() { max_length=10; } -}; -class Item_func_uncompressed_length : public Item_int_func -{ - String value; -public: - Item_func_uncompressed_length(Item *a):Item_int_func(a){} - longlong val_int(); - const char *func_name() const{return "uncompressed_length";} - void fix_length_and_dec() { max_length=10; } -}; -#endif - class Item_func_length :public Item_int_func { String value; diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index d86914eb6c5..79e45cca26f 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -167,30 +167,6 @@ public: const char *func_name() const { return "multipoint"; } }; -#ifdef HAVE_COMPRESS - -class Item_func_compress: public Item_str_func -{ - String buffer; -public: - Item_func_compress(Item *a):Item_str_func(a){} - String *val_str(String *); - void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;} - const char *func_name() const{return "compress";} -}; - -class Item_func_uncompress: public Item_str_func -{ - String buffer; -public: - Item_func_uncompress(Item *a): Item_str_func(a){} - String *val_str(String *); - void fix_length_and_dec(){max_length= MAX_BLOB_WIDTH;} - const char *func_name() const{return "uncompress";} -}; - -#endif - /* Spatial relations */ diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index b0b9cc01bf1..86efde096c5 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -33,8 +33,9 @@ #include "md5.h" #include "sha1.h" #include "my_aes.h" +#include "../mysys/my_static.h" // For soundex_map -String empty_string("",default_charset_info); +String my_empty_string("",default_charset_info); static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname) { @@ -359,7 +360,7 @@ String *Item_func_des_encrypt::val_str(String *str) if ((null_value=args[0]->null_value)) return 0; if ((res_length=res->length()) == 0) - return &empty_string; + return &my_empty_string; if (arg_count == 1) { @@ -520,7 +521,7 @@ String *Item_func_concat_ws::val_str(String *str) if ((res= args[i]->val_str(str))) break; if (i == arg_count) - return &empty_string; + return &my_empty_string; for (i++; i < arg_count ; i++) { @@ -661,7 +662,7 @@ String *Item_func_reverse::val_str(String *str) return 0; /* An empty string is a special case as the string pointer may be null */ if (!res->length()) - return &empty_string; + return &my_empty_string; res=copy_if_not_alloced(str,res,res->length()); ptr = (char *) res->ptr(); end=ptr+res->length(); @@ -914,7 +915,7 @@ String *Item_func_left::val_str(String *str) if ((null_value=args[0]->null_value)) return 0; if (length <= 0) - return &empty_string; + return &my_empty_string; length= res->charpos(length); if (res->length() > (ulong) length) { // Safe even if const arg @@ -958,7 +959,7 @@ String *Item_func_right::val_str(String *str) if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ if (length <= 0) - return &empty_string; /* purecov: inspected */ + return &my_empty_string; /* purecov: inspected */ if (res->length() <= (uint) length) return res; /* purecov: inspected */ @@ -991,7 +992,7 @@ String *Item_func_substr::val_str(String *str) start=res->charpos(start); length=res->charpos(length,start); if (start < 0 || (uint) start+1 > res->length() || length <= 0) - return &empty_string; + return &my_empty_string; tmp_length=(int32) res->length()-start; length=min(length,tmp_length); @@ -1051,7 +1052,7 @@ String *Item_func_substr_index::val_str(String *str) null_value=0; uint delimeter_length=delimeter->length(); if (!res->length() || !delimeter_length || !count) - return &empty_string; // Wrong parameters + return &my_empty_string; // Wrong parameters res->set_charset(collation.collation); @@ -1327,95 +1328,49 @@ void Item_func_trim::fix_length_and_dec() } - - -void Item_func_password::fix_length_and_dec() -{ - /* - If PASSWORD() was called with only one argument, it depends on a random - number so we need to save this random number into the binary log. - If called with two arguments, it is repeatable. - */ - if (arg_count == 1) - { - THD *thd= current_thd; - thd->rand_used= 1; - thd->rand_saved_seed1= thd->rand.seed1; - thd->rand_saved_seed2= thd->rand.seed2; - } - max_length= get_password_length(use_old_passwords); -} - -/* - Password() function has 2 arguments. Second argument can be used - to make results repeatable -*/ +/* Item_func_password */ String *Item_func_password::val_str(String *str) { - struct rand_struct rand_st; // local structure for 2 param version - ulong seed=0; // seed to initialise random generator to - - String *res =args[0]->val_str(str); + String *res= args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; - - if (arg_count == 1) - { - if (res->length() == 0) - return &empty_string; - make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords, - ¤t_thd->rand); - str->set(tmp_value,get_password_length(use_old_passwords),res->charset()); - return str; - } - else - { - /* We'll need the buffer to get second parameter */ - char key_buff[80]; - String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info); - String *key =args[1]->val_str(&tmp_key_value); - - /* Check second argument for NULL value. First one is already checked */ - if ((null_value=args[1]->null_value)) - return 0; - - /* This shall be done after checking for null for proper results */ - if (res->length() == 0) - return &empty_string; - - /* Generate the seed first this allows to avoid double allocation */ - char* seed_ptr=key->c_ptr(); - while (*seed_ptr) - { - seed=(seed*211+*seed_ptr) & 0xffffffffL; /* Use simple hashing */ - seed_ptr++; - } - - /* Use constants which allow nice random values even with small seed */ - randominit(&rand_st, - (ulong) ((ulonglong) seed*111111+33333333L) & (ulong) 0xffffffff, - (ulong) ((ulonglong) seed*1111+55555555L) & (ulong) 0xffffffff); - - make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords, - &rand_st); - str->set(tmp_value,get_password_length(use_old_passwords),res->charset()); - return str; - } + if (res->length() == 0) + return &my_empty_string; + make_scrambled_password(tmp_value, res->c_ptr()); + str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, res->charset()); + return str; +} + +char *Item_func_password::alloc(THD *thd, const char *password) +{ + char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1); + if (buff) + make_scrambled_password(buff, password); + return buff; } +/* Item_func_old_password */ + String *Item_func_old_password::val_str(String *str) { - String *res =args[0]->val_str(str); + String *res= args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; if (res->length() == 0) - return &empty_string; - make_scrambled_password(tmp_value,res->c_ptr(),1,¤t_thd->rand); - str->set(tmp_value,16,res->charset()); + return &my_empty_string; + make_scrambled_password_323(tmp_value, res->c_ptr()); + str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, res->charset()); return str; } +char *Item_func_old_password::alloc(THD *thd, const char *password) +{ + char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1); + if (buff) + make_scrambled_password_323(buff, password); + return buff; +} #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') @@ -1429,7 +1384,7 @@ String *Item_func_encrypt::val_str(String *str) if ((null_value=args[0]->null_value)) return 0; if (res->length() == 0) - return &empty_string; + return &my_empty_string; if (arg_count == 1) { // generate random salt @@ -1500,8 +1455,8 @@ String *Item_func_database::val_str(String *str) THD *thd= current_thd; if (!thd->db) { - str->length(0); - str->set_charset(system_charset_info); + null_value= 1; + return 0; } else str->copy((const char*) thd->db,(uint) strlen(thd->db),system_charset_info); @@ -1519,7 +1474,7 @@ String *Item_func_user::val_str(String *str) // For system threads (e.g. replication SQL thread) user may be empty if (!thd->user) - return &empty_string; + return &my_empty_string; res_length= (strlen(thd->user)+strlen(host)+2) * cs->mbmaxlen; if (str->alloc(res_length)) @@ -1542,15 +1497,11 @@ void Item_func_soundex::fix_length_and_dec() } - /* - If alpha, map input letter to soundex code. - If not alpha and remove_garbage is set then skip to next char - else return 0 - */ - -extern "C" { -extern const char *soundex_map; // In mysys/static.c -} +/* + If alpha, map input letter to soundex code. + If not alpha and remove_garbage is set then skip to next char + else return 0 +*/ static char get_scode(CHARSET_INFO *cs,char *ptr) { @@ -1582,7 +1533,7 @@ String *Item_func_soundex::val_str(String *str) while (from != end && my_isspace(cs,*from)) // Skip pre-space from++; /* purecov: inspected */ if (from == end) - return &empty_string; // No alpha characters. + return &my_empty_string; // No alpha characters. *to++ = my_toupper(cs,*from); // Copy first letter last_ch = get_scode(cs,from); // code of the first letter // for the first 'double-letter check. @@ -1764,7 +1715,7 @@ String *Item_func_make_set::val_str(String *str) ulonglong bits; bool first_found=0; Item **ptr=args; - String *result=&empty_string; + String *result=&my_empty_string; bits=item->val_int(); if ((null_value=item->null_value)) @@ -1788,7 +1739,7 @@ String *Item_func_make_set::val_str(String *str) else { if (tmp_str.copy(*res)) // Don't use 'str' - return &empty_string; + return &my_empty_string; result= &tmp_str; } } @@ -1798,11 +1749,11 @@ String *Item_func_make_set::val_str(String *str) { // Copy data to tmp_str if (tmp_str.alloc(result->length()+res->length()+1) || tmp_str.copy(*result)) - return &empty_string; + return &my_empty_string; result= &tmp_str; } if (tmp_str.append(',') || tmp_str.append(*res)) - return &empty_string; + return &my_empty_string; } } } @@ -1899,7 +1850,7 @@ String *Item_func_repeat::val_str(String *str) goto err; // string and/or delim are null null_value=0; if (count <= 0) // For nicer SQL code - return &empty_string; + return &my_empty_string; if (count == 1) // To avoid reallocs return res; length=res->length(); @@ -1934,7 +1885,7 @@ void Item_func_rpad::fix_length_and_dec() if (args[1]->const_item()) { - uint32 length= (uint32) args[1]->val_int(); + uint32 length= (uint32) args[1]->val_int() * collation.collation->mbmaxlen; max_length=max(args[0]->max_length,length); if (max_length >= MAX_BLOB_WIDTH) { @@ -1952,36 +1903,46 @@ void Item_func_rpad::fix_length_and_dec() String *Item_func_rpad::val_str(String *str) { - uint32 res_length,length_pad; + uint32 res_byte_length,res_char_length,pad_char_length,pad_byte_length; char *to; const char *ptr_pad; int32 count= (int32) args[1]->val_int(); + int32 byte_count= count * collation.collation->mbmaxlen; String *res =args[0]->val_str(str); String *rpad = args[2]->val_str(str); if (!res || args[1]->null_value || !rpad || count < 0) goto err; null_value=0; - if (count <= (int32) (res_length=res->length())) + if (count <= (int32) (res_char_length=res->numchars())) { // String to pad is big enough - res->length(count); // Shorten result if longer + res->length(res->charpos(count)); // Shorten result if longer return (res); } - length_pad= rpad->length(); - if ((ulong) count > current_thd->variables.max_allowed_packet || - args[2]->null_value || !length_pad) + pad_char_length= rpad->numchars(); + if ((ulong) byte_count > current_thd->variables.max_allowed_packet || + args[2]->null_value || !pad_char_length) goto err; - if (!(res= alloc_buffer(res,str,&tmp_value,count))) + res_byte_length= res->length(); /* Must be done before alloc_buffer */ + if (!(res= alloc_buffer(res,str,&tmp_value,byte_count))) goto err; - to= (char*) res->ptr()+res_length; + to= (char*) res->ptr()+res_byte_length; ptr_pad=rpad->ptr(); - for (count-= res_length; (uint32) count > length_pad; count-= length_pad) + pad_byte_length= rpad->length(); + count-= res_char_length; + for ( ; (uint32) count > pad_char_length; count-= pad_char_length) { - memcpy(to,ptr_pad,length_pad); - to+= length_pad; + memcpy(to,ptr_pad,pad_byte_length); + to+= pad_byte_length; } - memcpy(to,ptr_pad,(size_t) count); + if (count) + { + pad_byte_length= rpad->charpos(count); + memcpy(to,ptr_pad,(size_t) pad_byte_length); + to+= pad_byte_length; + } + res->length(to- (char*) res->ptr()); return (res); err: @@ -2000,7 +1961,7 @@ void Item_func_lpad::fix_length_and_dec() if (args[1]->const_item()) { - uint32 length= (uint32) args[1]->val_int(); + uint32 length= (uint32) args[1]->val_int() * collation.collation->mbmaxlen; max_length=max(args[0]->max_length,length); if (max_length >= MAX_BLOB_WIDTH) { @@ -2018,57 +1979,52 @@ void Item_func_lpad::fix_length_and_dec() String *Item_func_lpad::val_str(String *str) { - uint32 res_length,length_pad; - char *to; - const char *ptr_pad; - ulong count= (long) args[1]->val_int(); - String *res= args[0]->val_str(str); - String *lpad= args[2]->val_str(str); + uint32 res_byte_length,res_char_length,pad_byte_length,pad_char_length; + ulong count= (long) args[1]->val_int(), byte_count; + String a1,a3; + String *res= args[0]->val_str(&a1); + String *pad= args[2]->val_str(&a3); - if (!res || args[1]->null_value || !lpad) + if (!res || args[1]->null_value || !pad) goto err; + null_value=0; - if (count <= (res_length=res->length())) - { // String to pad is big enough - res->length(count); // Shorten result if longer - return (res); + res_byte_length= res->length(); + res_char_length= res->numchars(); + + if (count <= res_char_length) + { + res->length(res->charpos(count)); + return res; } - length_pad= lpad->length(); - if (count > current_thd->variables.max_allowed_packet || - args[2]->null_value || !length_pad) + + pad_byte_length= pad->length(); + pad_char_length= pad->numchars(); + byte_count= count * collation.collation->mbmaxlen; + + if (byte_count > current_thd->variables.max_allowed_packet || + args[2]->null_value || !pad_char_length || str->alloc(byte_count)) goto err; - - if (res->alloced_length() < count) + + str->length(0); + str->set_charset(collation.collation); + count-= res_char_length; + while (count >= pad_char_length) { - if (str->alloced_length() >= count) - { - memcpy((char*) str->ptr()+(count-res_length),res->ptr(),res_length); - res=str; - } - else - { - if (tmp_value.alloc(count)) - goto err; - memcpy((char*) tmp_value.ptr()+(count-res_length),res->ptr(),res_length); - res=&tmp_value; - } + str->append(*pad); + count-= pad_char_length; } - else - bmove_upp((char*) res->ptr()+count,res->ptr()+res_length,res_length); - res->length(count); - - to= (char*) res->ptr(); - ptr_pad= lpad->ptr(); - for (count-= res_length; count > length_pad; count-= length_pad) + if (count > 0) { - memcpy(to,ptr_pad,length_pad); - to+= length_pad; + pad->length(pad->charpos(count)); + str->append(*pad); } - memcpy(to,ptr_pad,(size_t) count); - return (res); + str->append(*res); + null_value= 0; + return str; - err: - null_value=1; +err: + null_value= 1; return 0; } @@ -2096,7 +2052,7 @@ String *Item_func_conv::val_str(String *str) dec= (longlong) my_strntoull(res->charset(),res->ptr(),res->length(),from_base,&endptr,&err); ptr= longlong2str(dec,ans,to_base); if (str->copy(ans,(uint32) (ptr-ans), default_charset())) - return &empty_string; + return &my_empty_string; return str; } @@ -2286,7 +2242,7 @@ String *Item_func_hex::val_str(String *str) return 0; ptr= longlong2str(dec,ans,16); if (str->copy(ans,(uint32) (ptr-ans),default_charset())) - return &empty_string; // End of memory + return &my_empty_string; // End of memory return str; } @@ -2323,7 +2279,9 @@ String *Item_load_file::val_str(String *str) DBUG_ENTER("load_file"); if (!(file_name= args[0]->val_str(str)) || +#ifndef NO_EMBEDDED_ACCESS_CHECKS !(current_thd->master_access & FILE_ACL) || +#endif !my_stat(file_name->c_ptr(), &stat_info, MYF(MY_WME))) goto err; if (!(stat_info.st_mode & S_IROTH)) @@ -2554,12 +2512,42 @@ null: return 0; } +longlong Item_func_uncompressed_length::val_int() +{ + String *res= args[0]->val_str(&value); + if (!res) + { + null_value=1; + return 0; /* purecov: inspected */ + } + null_value=0; + if (res->is_empty()) return 0; + return uint4korr(res->c_ptr()) & 0x3FFFFFFF; +} + +longlong Item_func_crc32::val_int() +{ + String *res=args[0]->val_str(&value); + if (!res) + { + null_value=1; + return 0; /* purecov: inspected */ + } + null_value=0; + return (longlong) crc32(0L, (uchar*)res->ptr(), res->length()); +} + #ifdef HAVE_COMPRESS #include "zlib.h" String *Item_func_compress::val_str(String *str) { String *res= args[0]->val_str(str); + if (!res) + { + null_value= 1; + return 0; + } if (res->is_empty()) return res; int err= Z_OK; @@ -2581,7 +2569,7 @@ String *Item_func_compress::val_str(String *str) buffer.realloc((uint32)new_size + 4 + 1); Byte *body= ((Byte*)buffer.c_ptr()) + 4; - + if ((err= compress(body, &new_size, (const Bytef*)res->c_ptr(), res->length())) != Z_OK) { @@ -2603,19 +2591,24 @@ String *Item_func_compress::val_str(String *str) } buffer.length((uint32)new_size + 4); - + return &buffer; } String *Item_func_uncompress::val_str(String *str) { String *res= args[0]->val_str(str); + if (!res) + { + null_value= 1; + return 0; + } if (res->is_empty()) return res; ulong new_size= uint4korr(res->c_ptr()) & 0x3FFFFFFF; int err= Z_OK; uint code; - + if (new_size > MAX_BLOB_WIDTH) { push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, @@ -2624,21 +2617,20 @@ String *Item_func_uncompress::val_str(String *str) null_value= 0; return 0; } - + buffer.realloc((uint32)new_size); - - if ((err= uncompress((Byte*)buffer.c_ptr(), &new_size, + + if ((err= uncompress((Byte*)buffer.c_ptr(), &new_size, ((const Bytef*)res->c_ptr())+4,res->length())) == Z_OK) { buffer.length((uint32)new_size); return &buffer; } - - code= err==Z_BUF_ERROR ? ER_ZLIB_Z_BUF_ERROR : + + code= err==Z_BUF_ERROR ? ER_ZLIB_Z_BUF_ERROR : err==Z_MEM_ERROR ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_DATA_ERROR; push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,code,ER(code)); null_value= 1; return 0; } - #endif diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index f64a145b136..b82dacb4fe0 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -254,30 +254,45 @@ public: }; +/* + Item_func_password -- new (4.1.1) PASSWORD() function implementation. + Returns strcat('*', octet2hex(sha1(sha1(password)))). '*' stands for new + password format, sha1(sha1(password) is so-called hash_stage2 value. + Length of returned string is always 41 byte. To find out how entire + authentification procedure works, see comments in password.c. +*/ + class Item_func_password :public Item_str_func { - char tmp_value[64]; /* This should be enough for new password format */ + char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH+1]; public: Item_func_password(Item *a) :Item_str_func(a) {} - Item_func_password(Item *a, Item *b) :Item_str_func(a,b) {} - String *val_str(String *); - void fix_length_and_dec(); + String *val_str(String *str); + void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; } const char *func_name() const { return "password"; } + static char *alloc(THD *thd, const char *password); }; +/* + Item_func_old_password -- PASSWORD() implementation used in MySQL 3.21 - 4.0 + compatibility mode. This item is created in sql_yacc.yy when + 'old_passwords' session variable is set, and to handle OLD_PASSWORD() + function. +*/ + class Item_func_old_password :public Item_str_func { - char tmp_value[17]; /* old password length +1 */ + char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1]; public: Item_func_old_password(Item *a) :Item_str_func(a) {} - String *val_str(String *); - void fix_length_and_dec() { max_length = get_password_length(1); } + String *val_str(String *str); + void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; } const char *func_name() const { return "old_password"; } + static char *alloc(THD *thd, const char *password); }; - class Item_func_des_encrypt :public Item_str_func { String tmp_value; @@ -624,9 +639,56 @@ public: Item_func_collation(Item *a) :Item_str_func(a) {} String *val_str(String *); const char *func_name() const { return "collation"; } - void fix_length_and_dec() + void fix_length_and_dec() { max_length=40; // should be enough collation.set(system_charset_info); }; }; + +class Item_func_crc32 :public Item_int_func +{ + String value; +public: + Item_func_crc32(Item *a) :Item_int_func(a) {} + const char *func_name() const { return "crc32"; } + void fix_length_and_dec() { max_length=10; } + longlong val_int(); +}; + +class Item_func_uncompressed_length : public Item_int_func +{ + String value; +public: + Item_func_uncompressed_length(Item *a):Item_int_func(a){} + const char *func_name() const{return "uncompressed_length";} + void fix_length_and_dec() { max_length=10; } + longlong val_int(); +}; + +#ifdef HAVE_COMPRESS +#define ZLIB_DEPENDED_FUNCTION ; +#else +#define ZLIB_DEPENDED_FUNCTION { null_value=1; return 0; } +#endif + +class Item_func_compress: public Item_str_func +{ + String buffer; +public: + Item_func_compress(Item *a):Item_str_func(a){} + void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;} + const char *func_name() const{return "compress";} + String *val_str(String *) ZLIB_DEPENDED_FUNCTION +}; + +class Item_func_uncompress: public Item_str_func +{ + String buffer; +public: + Item_func_uncompress(Item *a): Item_str_func(a){} + void fix_length_and_dec(){max_length= MAX_BLOB_WIDTH;} + const char *func_name() const{return "uncompress";} + String *val_str(String *) ZLIB_DEPENDED_FUNCTION +}; + diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 5c36ad42f98..324b745778a 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -72,7 +72,7 @@ Item_subselect::trans_res Item_subselect::select_transformer(JOIN *join) { DBUG_ENTER("Item_subselect::select_transformer"); - DBUG_RETURN(OK); + DBUG_RETURN(RES_OK); } @@ -227,14 +227,14 @@ Item_singlerow_subselect::select_transformer(JOIN *join) cond= join->having; else if (!(cond= new Item_cond_and(join->conds, join->having))) - return ERROR; + return RES_ERROR; if (!(substitution= new Item_func_if(cond, substitution, new Item_null()))) - return ERROR; + return RES_ERROR; } - return REDUCE; + return RES_REDUCE; } - return OK; + return RES_OK; } void Item_singlerow_subselect::store(uint i, Item *item) @@ -582,7 +582,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) { thd->lex.current_select= current; - DBUG_RETURN(ERROR); + DBUG_RETURN(RES_ERROR); } thd->lex.current_select= current; @@ -602,7 +602,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (select_lex->item_list.elements > 1) { my_error(ER_CARDINALITY_COL, MYF(0), 1); - DBUG_RETURN(ERROR); + DBUG_RETURN(RES_ERROR); } item= (Item*) select_lex->item_list.head(); @@ -620,7 +620,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (join->having->fix_fields(thd, join->tables_list, &join->having)) { select_lex->having_fix_field= 0; - DBUG_RETURN(ERROR); + DBUG_RETURN(RES_ERROR); } select_lex->having_fix_field= 0; } @@ -644,15 +644,16 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (join->having->fix_fields(thd, join->tables_list, &join->having)) { select_lex->having_fix_field= 0; - DBUG_RETURN(ERROR); + DBUG_RETURN(RES_ERROR); } select_lex->having_fix_field= 0; item= new Item_cond_or(item, new Item_func_isnull(isnull)); } + item->name= (char *)in_additional_cond; join->conds= and_items(join->conds, item); if (join->conds->fix_fields(thd, join->tables_list, &join->conds)) - DBUG_RETURN(ERROR); + DBUG_RETURN(RES_ERROR); } else { @@ -666,7 +667,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (join->having->fix_fields(thd, join->tables_list, &join->having)) { select_lex->having_fix_field= 0; - DBUG_RETURN(ERROR); + DBUG_RETURN(RES_ERROR); } select_lex->having_fix_field= 0; } @@ -684,11 +685,11 @@ Item_in_subselect::single_value_transformer(JOIN *join, push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } - DBUG_RETURN(REDUCE); + DBUG_RETURN(RES_REDUCE); } } } - DBUG_RETURN(OK); + DBUG_RETURN(RES_OK); } Item_subselect::trans_res @@ -714,7 +715,7 @@ Item_in_subselect::row_value_transformer(JOIN *join, if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) { thd->lex.current_select= current; - DBUG_RETURN(ERROR); + DBUG_RETURN(RES_ERROR); } thd->lex.current_select= current; @@ -753,7 +754,7 @@ Item_in_subselect::row_value_transformer(JOIN *join, if (join->having->fix_fields(thd, join->tables_list, &join->having)) { select_lex->having_fix_field= 0; - DBUG_RETURN(ERROR); + DBUG_RETURN(RES_ERROR); } select_lex->having_fix_field= 0; } @@ -761,9 +762,9 @@ Item_in_subselect::row_value_transformer(JOIN *join, { join->conds= and_items(join->conds, item); if (join->conds->fix_fields(thd, join->tables_list, &join->having)) - DBUG_RETURN(ERROR); + DBUG_RETURN(RES_ERROR); } - DBUG_RETURN(OK); + DBUG_RETURN(RES_OK); } Item_subselect::trans_res @@ -847,7 +848,7 @@ int subselect_union_engine::prepare() return unit->prepare(thd, result, 0); } -int subselect_simplein_engine::prepare() +int subselect_uniquesubquery_engine::prepare() { //this never should be called DBUG_ASSERT(0); @@ -921,7 +922,7 @@ void subselect_union_engine::fix_length_and_dec(Item_cache **row) SELECT_LEX *sl= unit->first_select(); bool fake= 0; res_type= set_row(sl, item, row, &fake); - for (sl= sl->next_select(); sl; sl->next_select()) + for (sl= sl->next_select(); sl; sl= sl->next_select()) { List_iterator_fast<Item> li(sl->item_list); Item *sel_item; @@ -934,7 +935,7 @@ void subselect_union_engine::fix_length_and_dec(Item_cache **row) } } -void subselect_simplein_engine::fix_length_and_dec(Item_cache **row) +void subselect_uniquesubquery_engine::fix_length_and_dec(Item_cache **row) { //this never should be called DBUG_ASSERT(0); @@ -993,9 +994,9 @@ int subselect_union_engine::exec() return res; } -int subselect_simplein_engine::exec() +int subselect_uniquesubquery_engine::exec() { - DBUG_ENTER("subselect_simplein_engine::exec"); + DBUG_ENTER("subselect_uniquesubquery_engine::exec"); int error; TABLE *table= tab->table; if ((tab->ref.key_err= (*tab->ref.key_copy)->copy())) @@ -1023,9 +1024,9 @@ int subselect_simplein_engine::exec() DBUG_RETURN(end_exec(table) || (error != 0)); } -int subselect_simplein_engine::end_exec(TABLE *table) +int subselect_uniquesubquery_engine::end_exec(TABLE *table) { - DBUG_ENTER("subselect_simplein_engine::end_exec"); + DBUG_ENTER("subselect_uniquesubquery_engine::end_exec"); int error=0, tmp; if ((tmp= table->file->extra(HA_EXTRA_NO_CACHE))) { @@ -1042,10 +1043,11 @@ int subselect_simplein_engine::end_exec(TABLE *table) DBUG_RETURN(error != 0); } -int subselect_indexin_engine::exec() +int subselect_indexsubquery_engine::exec() { - DBUG_ENTER("subselect_indexin_engine::exec"); + DBUG_ENTER("subselect_indexsubselect_engine::exec"); int error; + bool null_finding= 0; TABLE *table= tab->table; ((Item_in_subselect *) item)->value= 0; @@ -1077,29 +1079,30 @@ int subselect_indexin_engine::exec() { if (!cond || cond->val_int()) { - if (check_null && *tab->null_ref_key) + if (null_finding) ((Item_in_subselect *) item)->was_null= 1; else ((Item_in_subselect *) item)->value= 1; goto finish; } + error= table->file->index_next_same(table->record[0], + tab->ref.key_buff, + tab->ref.key_length); + if (error && error != HA_ERR_END_OF_FILE) + { + error= report_error(table, error); + goto finish; + } } else { - if (!check_null || *tab->null_ref_key) + if (!check_null || null_finding) goto finish; *tab->null_ref_key= 1; + null_finding= 1; if (safe_index_read(tab)) goto finish; } - error= table->file->index_next_same(table->record[0], - tab->ref.key_buff, - tab->ref.key_length); - if (error && error != HA_ERR_KEY_NOT_FOUND) - { - error= report_error(table, error); - goto finish; - } } } } @@ -1147,7 +1150,7 @@ void subselect_union_engine::exclude() unit->exclude_level(); } -void subselect_simplein_engine::exclude() +void subselect_uniquesubquery_engine::exclude() { //this never should be called DBUG_ASSERT(0); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 9e4c5485405..1b5b9e9bd38 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -51,7 +51,7 @@ public: /* changed engine indicator */ bool engine_changed; - enum trans_res {OK, REDUCE, ERROR}; + enum trans_res {RES_OK, RES_REDUCE, RES_ERROR}; enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS, EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS}; @@ -193,8 +193,8 @@ public: void fix_length_and_dec(); friend class select_exists_subselect; - friend class subselect_simplein_engine; - friend class subselect_indexin_engine; + friend class subselect_uniquesubquery_engine; + friend class subselect_indexsubquery_engine; }; /* IN subselect */ @@ -240,7 +240,7 @@ public: friend class Item_ref_null_helper; friend class Item_is_not_null_test; - friend class subselect_indexin_engine; + friend class subselect_indexsubquery_engine; }; /* ALL/ANY/SOME subselect */ @@ -327,15 +327,15 @@ public: }; struct st_join_table; -class subselect_simplein_engine: public subselect_engine +class subselect_uniquesubquery_engine: public subselect_engine { protected: st_join_table *tab; Item *cond; public: - subselect_simplein_engine(THD *thd, st_join_table *tab_arg, - Item_subselect *subs, Item *where) + subselect_uniquesubquery_engine(THD *thd, st_join_table *tab_arg, + Item_subselect *subs, Item *where) :subselect_engine(thd, subs, 0), tab(tab_arg), cond(where) {} @@ -349,14 +349,14 @@ public: static int end_exec(TABLE *table); }; -class subselect_indexin_engine: public subselect_simplein_engine +class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine { bool check_null; public: - subselect_indexin_engine(THD *thd, st_join_table *tab_arg, - Item_subselect *subs, Item *where, - bool chk_null) - :subselect_simplein_engine(thd, tab_arg, subs, where), + subselect_indexsubquery_engine(THD *thd, st_join_table *tab_arg, + Item_subselect *subs, Item *where, + bool chk_null) + :subselect_uniquesubquery_engine(thd, tab_arg, subs, where), check_null(chk_null) {} int exec(); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b029bcf2b87..967abbc5ab8 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -70,6 +70,8 @@ void Item_sum::make_field(Send_field *tmp_field) tmp_field->db_name=(char*)""; tmp_field->org_table_name=tmp_field->table_name=(char*)""; tmp_field->org_col_name=tmp_field->col_name=name; + if (maybe_null) + tmp_field->flags&= ~NOT_NULL_FLAG; } else init_make_field(tmp_field, field_type()); @@ -212,7 +214,8 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) max_length=item->max_length; } decimals=item->decimals; - maybe_null=item->maybe_null; + /* MIN/MAX can return NULL for empty set indepedent of the used column */ + maybe_null= 1; unsigned_flag=item->unsigned_flag; collation.set(item->collation); result_field=0; @@ -238,10 +241,9 @@ Item *Item_sum_sum::copy_or_same(THD* thd) } -bool Item_sum_sum::reset() +void Item_sum_sum::clear() { null_value=1; sum=0.0; - return Item_sum_sum::add(); } @@ -266,10 +268,9 @@ Item *Item_sum_count::copy_or_same(THD* thd) } -bool Item_sum_count::reset() +void Item_sum_count::clear() { - count=0; - return add(); + count= 0; } @@ -301,10 +302,9 @@ Item *Item_sum_avg::copy_or_same(THD* thd) } -bool Item_sum_avg::reset() +void Item_sum_avg::clear() { sum=0.0; count=0; - return Item_sum_avg::add(); } @@ -357,11 +357,10 @@ Item *Item_sum_variance::copy_or_same(THD* thd) } -bool Item_sum_variance::reset() +void Item_sum_variance::clear() { sum=sum_sqr=0.0; count=0; - return Item_sum_variance::add(); } bool Item_sum_variance::add() @@ -407,15 +406,15 @@ void Item_sum_variance::reset_field() } } -void Item_sum_variance::update_field(int offset) +void Item_sum_variance::update_field() { double nr,old_nr,old_sqr; longlong field_count; char *res=result_field->ptr; - float8get(old_nr,res+offset); - float8get(old_sqr,res+offset+sizeof(double)); - field_count=sint8korr(res+offset+sizeof(double)*2); + float8get(old_nr, res); + float8get(old_sqr, res+sizeof(double)); + field_count=sint8korr(res+sizeof(double)*2); nr=args[0]->val(); if (!args[0]->null_value) @@ -607,10 +606,9 @@ longlong Item_sum_bit::val_int() } -bool Item_sum_bit::reset() +void Item_sum_bit::clear() { - bits=reset_bits; - return add(); + bits= reset_bits; } Item *Item_sum_or::copy_or_same(THD* thd) @@ -773,12 +771,12 @@ void Item_sum_bit::reset_field() ** calc next value and merge it with field_value */ -void Item_sum_sum::update_field(int offset) +void Item_sum_sum::update_field() { double old_nr,nr; char *res=result_field->ptr; - float8get(old_nr,res+offset); + float8get(old_nr,res); nr=args[0]->val(); if (!args[0]->null_value) { @@ -789,12 +787,12 @@ void Item_sum_sum::update_field(int offset) } -void Item_sum_count::update_field(int offset) +void Item_sum_count::update_field() { longlong nr; char *res=result_field->ptr; - nr=sint8korr(res+offset); + nr=sint8korr(res); if (!args[0]->maybe_null) nr++; else @@ -807,14 +805,14 @@ void Item_sum_count::update_field(int offset) } -void Item_sum_avg::update_field(int offset) +void Item_sum_avg::update_field() { double nr,old_nr; longlong field_count; char *res=result_field->ptr; - float8get(old_nr,res+offset); - field_count=sint8korr(res+offset+sizeof(double)); + float8get(old_nr,res); + field_count=sint8korr(res+sizeof(double)); nr=args[0]->val(); if (!args[0]->null_value) @@ -827,77 +825,65 @@ void Item_sum_avg::update_field(int offset) int8store(res,field_count); } -void Item_sum_hybrid::update_field(int offset) +void Item_sum_hybrid::update_field() { if (hybrid_type == STRING_RESULT) - min_max_update_str_field(offset); + min_max_update_str_field(); else if (hybrid_type == INT_RESULT) - min_max_update_int_field(offset); + min_max_update_int_field(); else - min_max_update_real_field(offset); + min_max_update_real_field(); } void -Item_sum_hybrid::min_max_update_str_field(int offset) +Item_sum_hybrid::min_max_update_str_field() { String *res_str=args[0]->val_str(&value); - if (args[0]->null_value) - result_field->copy_from_tmp(offset); // Use old value - else + if (!args[0]->null_value) { res_str->strip_sp(); - result_field->ptr+=offset; // Get old max/min result_field->val_str(&tmp_value,&tmp_value); - result_field->ptr-=offset; if (result_field->is_null() || (cmp_sign * sortcmp(res_str,&tmp_value,cmp_charset)) < 0) result_field->store(res_str->ptr(),res_str->length(),res_str->charset()); - else - { // Use old value - char *res=result_field->ptr; - memcpy(res,res+offset,result_field->pack_length()); - } result_field->set_notnull(); } } void -Item_sum_hybrid::min_max_update_real_field(int offset) +Item_sum_hybrid::min_max_update_real_field() { double nr,old_nr; - result_field->ptr+=offset; old_nr=result_field->val_real(); nr=args[0]->val(); if (!args[0]->null_value) { - if (result_field->is_null(offset) || + if (result_field->is_null(0) || (cmp_sign > 0 ? old_nr > nr : old_nr < nr)) old_nr=nr; result_field->set_notnull(); } - else if (result_field->is_null(offset)) + else if (result_field->is_null(0)) result_field->set_null(); - result_field->ptr-=offset; result_field->store(old_nr); } void -Item_sum_hybrid::min_max_update_int_field(int offset) +Item_sum_hybrid::min_max_update_int_field() { longlong nr,old_nr; - result_field->ptr+=offset; old_nr=result_field->val_int(); nr=args[0]->val_int(); if (!args[0]->null_value) { - if (result_field->is_null(offset)) + if (result_field->is_null(0)) old_nr=nr; else { @@ -910,30 +896,29 @@ Item_sum_hybrid::min_max_update_int_field(int offset) } result_field->set_notnull(); } - else if (result_field->is_null(offset)) + else if (result_field->is_null(0)) result_field->set_null(); - result_field->ptr-=offset; result_field->store(old_nr); } -void Item_sum_or::update_field(int offset) +void Item_sum_or::update_field() { ulonglong nr; char *res=result_field->ptr; - nr=uint8korr(res+offset); + nr=uint8korr(res); nr|= (ulonglong) args[0]->val_int(); int8store(res,nr); } -void Item_sum_and::update_field(int offset) +void Item_sum_and::update_field() { ulonglong nr; char *res=result_field->ptr; - nr=uint8korr(res+offset); + nr=uint8korr(res); nr&= (ulonglong) args[0]->val_int(); int8store(res,nr); } @@ -1295,7 +1280,7 @@ Item *Item_sum_count_distinct::copy_or_same(THD* thd) } -bool Item_sum_count_distinct::reset() +void Item_sum_count_distinct::clear() { if (use_tree) reset_tree(tree); @@ -1305,7 +1290,6 @@ bool Item_sum_count_distinct::reset() table->file->delete_all_rows(); table->file->extra(HA_EXTRA_WRITE_CACHE); } - return add(); } bool Item_sum_count_distinct::add() @@ -1368,11 +1352,11 @@ longlong Item_sum_count_distinct::val_int() #ifdef HAVE_DLOPEN -bool Item_udf_sum::reset() +void Item_udf_sum::clear() { - DBUG_ENTER("Item_udf_sum::reset"); - udf.reset(&null_value); - DBUG_RETURN(0); + DBUG_ENTER("Item_udf_sum::clear"); + udf.clear(); + DBUG_VOID_RETURN; } bool Item_udf_sum::add() @@ -1481,7 +1465,7 @@ int group_concat_key_cmp_with_distinct(void* arg, byte* key1, for (uint i= 0; i < item->arg_count_field; i++) { Item *field_item= item->args[i]; - Field *field= field_item->tmp_table_field(); + Field *field= field_item->real_item()->get_tmp_table_field(); if (field) { uint offset= field->abs_offset; @@ -1512,7 +1496,7 @@ int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2) { ORDER *order_item= item->order[i]; Item *item= *order_item->item; - Field *field= item->tmp_table_field(); + Field *field= item->real_item()->get_tmp_table_field(); if (field) { uint offset= field->abs_offset; @@ -1563,7 +1547,7 @@ int dump_leaf_key(byte* key, uint32 count __attribute__((unused)), Item *show_item= group_concat_item->args[i]; if (!show_item->const_item()) { - Field *f= show_item->tmp_table_field(); + Field *f= show_item->real_item()->get_tmp_table_field(); char *sv= f->ptr; f->ptr= (char *)key + f->abs_offset; String *res= f->val_str(&tmp,&tmp2); @@ -1700,7 +1684,7 @@ Item *Item_func_group_concat::copy_or_same(THD* thd) } -bool Item_func_group_concat::reset() +void Item_func_group_concat::clear() { result.length(0); result.copy(); @@ -1714,7 +1698,6 @@ bool Item_func_group_concat::reset() } if (tree_mode) reset_tree(tree); - return add(); } @@ -1731,7 +1714,7 @@ bool Item_func_group_concat::add() Item *show_item= args[i]; if (!show_item->const_item()) { - Field *f= show_item->tmp_table_field(); + Field *f= show_item->real_item()->get_tmp_table_field(); if (!f->is_null()) { record_is_null= FALSE; @@ -1953,6 +1936,3 @@ String* Item_func_group_concat::val_str(String* str) } return &result; } - - - diff --git a/sql/item_sum.h b/sql/item_sum.h index 651124e65f5..d6184bcdbac 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -62,10 +62,11 @@ public: enum Type type() const { return SUM_FUNC_ITEM; } virtual enum Sumfunctype sum_func () const=0; - virtual bool reset()=0; + inline bool reset() { clear(); return add(); }; + virtual void clear()= 0; virtual bool add()=0; virtual void reset_field()=0; - virtual void update_field(int offset)=0; + virtual void update_field()=0; virtual bool keep_field_type(void) const { return 0; } virtual void fix_length_and_dec() { maybe_null=1; null_value=1; } virtual const char *func_name() const { return "?"; } @@ -126,11 +127,11 @@ class Item_sum_sum :public Item_sum_num Item_sum_sum(THD *thd, Item_sum_sum &item) :Item_sum_num(thd, item), sum(item.sum) {} enum Sumfunctype sum_func () const {return SUM_FUNC;} - bool reset(); + void clear(); bool add(); double val(); void reset_field(); - void update_field(int offset); + void update_field(); void no_rows_in_result() {} const char *func_name() const { return "sum"; } Item *copy_or_same(THD* thd); @@ -153,13 +154,13 @@ class Item_sum_count :public Item_sum_int table_map used_tables() const { return used_table_cache; } bool const_item() const { return !used_table_cache; } enum Sumfunctype sum_func () const { return COUNT_FUNC; } - bool reset(); + void clear(); void no_rows_in_result() { count=0; } bool add(); void make_const(longlong count_arg) { count=count_arg; used_table_cache=0; } longlong val_int(); void reset_field(); - void update_field(int offset); + void update_field(); const char *func_name() const { return "count"; } Item *copy_or_same(THD* thd); }; @@ -227,11 +228,11 @@ class Item_sum_count_distinct :public Item_sum_int table_map used_tables() const { return used_table_cache; } enum Sumfunctype sum_func () const { return COUNT_DISTINCT_FUNC; } - bool reset(); + void clear(); bool add(); longlong val_int(); void reset_field() { return ;} // Never called - void update_field(int offset) { return ; } // Never called + void update_field() { return ; } // Never called const char *func_name() const { return "count_distinct"; } bool setup(THD *thd); void make_unique(); @@ -271,11 +272,11 @@ class Item_sum_avg :public Item_sum_num Item_sum_avg(THD *thd, Item_sum_avg &item) :Item_sum_num(thd, item), sum(item.sum), count(item.count) {} enum Sumfunctype sum_func () const {return AVG_FUNC;} - bool reset(); + void clear(); bool add(); double val(); void reset_field(); - void update_field(int offset); + void update_field(); Item *result_item(Field *field) { return new Item_avg_field(this); } const char *func_name() const { return "avg"; } @@ -324,11 +325,11 @@ class Item_sum_variance : public Item_sum_num Item_sum_num(thd, item), sum(item.sum), sum_sqr(item.sum_sqr), count(item.count) {} enum Sumfunctype sum_func () const { return VARIANCE_FUNC; } - bool reset(); + void clear(); bool add(); double val(); void reset_field(); - void update_field(int offset); + void update_field(); Item *result_item(Field *field) { return new Item_variance_field(this); } const char *func_name() const { return "variance"; } @@ -387,19 +388,18 @@ class Item_sum_hybrid :public Item_sum Item_sum_hybrid(THD *thd, Item_sum_hybrid &item): Item_sum(thd, item), value(item.value), tmp_value(item.tmp_value), sum(item.sum), sum_int(item.sum_int), hybrid_type(item.hybrid_type), - cmp_sign(item.cmp_sign), used_table_cache(used_table_cache), - cmp_charset(item.cmp_charset) {} + hybrid_field_type(item.hybrid_field_type),cmp_sign(item.cmp_sign), + used_table_cache(item.used_table_cache), cmp_charset(item.cmp_charset) {} bool fix_fields(THD *, TABLE_LIST *, Item **); table_map used_tables() const { return used_table_cache; } bool const_item() const { return !used_table_cache; } - bool reset() + void clear() { sum=0.0; sum_int=0; value.length(0); null_value=1; - return add(); } double val(); longlong val_int(); @@ -409,10 +409,10 @@ class Item_sum_hybrid :public Item_sum bool keep_field_type(void) const { return 1; } enum Item_result result_type () const { return hybrid_type; } enum enum_field_types field_type() const { return hybrid_field_type; } - void update_field(int offset); - void min_max_update_str_field(int offset); - void min_max_update_real_field(int offset); - void min_max_update_int_field(int offset); + void update_field(); + void min_max_update_str_field(); + void min_max_update_real_field(); + void min_max_update_int_field(); }; @@ -453,7 +453,7 @@ class Item_sum_bit :public Item_sum_int Item_sum_bit(THD *thd, Item_sum_bit &item): Item_sum_int(thd, item), reset_bits(item.reset_bits), bits(item.bits) {} enum Sumfunctype sum_func () const {return SUM_BIT_FUNC;} - bool reset(); + void clear(); longlong val_int(); void reset_field(); void fix_length_and_dec() @@ -467,7 +467,7 @@ class Item_sum_or :public Item_sum_bit Item_sum_or(Item *item_par) :Item_sum_bit(item_par,LL(0)) {} Item_sum_or(THD *thd, Item_sum_or &item) :Item_sum_bit(thd, item) {} bool add(); - void update_field(int offset); + void update_field(); const char *func_name() const { return "bit_or"; } Item *copy_or_same(THD* thd); }; @@ -479,7 +479,7 @@ class Item_sum_and :public Item_sum_bit Item_sum_and(Item *item_par) :Item_sum_bit(item_par, ~(ulonglong) LL(0)) {} Item_sum_and(THD *thd, Item_sum_and &item) :Item_sum_bit(thd, item) {} bool add(); - void update_field(int offset); + void update_field(); const char *func_name() const { return "bit_and"; } Item *copy_or_same(THD* thd); }; @@ -511,10 +511,10 @@ public: enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } virtual bool have_field_update(void) const { return 0; } - bool reset(); + void clear(); bool add(); void reset_field() {}; - void update_field(int offset_arg) {}; + void update_field() {}; }; @@ -593,9 +593,9 @@ class Item_sum_udf_float :public Item_sum_num ~Item_sum_udf_float() {} enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } double val() { return 0.0; } - bool reset() { return 0; } + void clear() {} bool add() { return 0; } - void update_field(int offset) {} + void update_field() {} }; @@ -610,9 +610,9 @@ public: enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } longlong val_int() { return 0; } double val() { return 0; } - bool reset() { return 0; } + void clear() {} bool add() { return 0; } - void update_field(int offset) {} + void update_field() {} }; @@ -630,9 +630,9 @@ public: enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec() { maybe_null=1; max_length=0; } enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } - bool reset() { return 0; } + void clear() {} bool add() { return 0; } - void update_field(int offset) {} + void update_field() {} }; #endif /* HAVE_DLOPEN */ @@ -716,13 +716,13 @@ class Item_func_group_concat : public Item_sum const char *func_name() const { return "group_concat"; } enum Type type() const { return SUM_FUNC_ITEM; } virtual Item_result result_type () const { return STRING_RESULT; } - bool reset(); + void clear(); bool add(); void reset_field(); bool fix_fields(THD *, TABLE_LIST *, Item **); bool setup(THD *thd); void make_unique(); - virtual void update_field(int offset) {}; + virtual void update_field() {} double val() { String *res; res=val_str(&str_value); @@ -735,4 +735,5 @@ class Item_func_group_concat : public Item_sum } String* val_str(String* str); Item *copy_or_same(THD* thd); + void no_rows_in_result() {} }; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index d84267a5066..6dcf7d00ce1 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -327,7 +327,6 @@ public: max_length=10*default_charset()->mbmaxlen; } int save_in_field(Field *to, bool no_conversions); - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_date(maybe_null, name, t_arg, default_charset())); @@ -342,7 +341,6 @@ public: Item_date_func(Item *a) :Item_str_func(a) {} Item_date_func(Item *a,Item *b) :Item_str_func(a,b) {} enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_datetime(maybe_null, name, t_arg, default_charset())); @@ -366,7 +364,6 @@ public: longlong val_int() { return value; } String *val_str(String *str); void fix_length_and_dec(); - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_time(maybe_null, name, t_arg, default_charset())); @@ -533,7 +530,6 @@ public: } enum_field_types field_type() const { return MYSQL_TYPE_TIME; } const char *func_name() const { return "sec_to_time"; } - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_time(maybe_null, name, t_arg, default_charset())); @@ -634,7 +630,6 @@ public: bool get_date(TIME *ltime, bool fuzzy_date); const char *func_name() const { return "date"; } enum_field_types field_type() const { return MYSQL_TYPE_DATE; } - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_date(maybe_null, name, t_arg, default_charset())); @@ -650,7 +645,6 @@ public: bool get_time(TIME *ltime); const char *func_name() const { return "time"; } enum_field_types field_type() const { return MYSQL_TYPE_TIME; } - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_time(maybe_null, name, t_arg, default_charset())); @@ -665,7 +659,6 @@ public: String *val_str(String *str); const char *func_name() const { return "datetime"; } enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_datetime(maybe_null, name, t_arg, default_charset())); @@ -684,7 +677,6 @@ public: decimals=0; max_length=8*MY_CHARSET_BIN_MB_MAXLEN; } - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); @@ -711,7 +703,6 @@ public: Change this when we support microseconds in TIME/DATETIME */ - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { if (cached_field_type == MYSQL_TYPE_TIME) @@ -735,7 +726,6 @@ public: decimals=0; max_length=17*MY_CHARSET_BIN_MB_MAXLEN; } - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); @@ -755,7 +745,6 @@ public: decimals=0; max_length=8*MY_CHARSET_BIN_MB_MAXLEN; } - Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); diff --git a/sql/item_uniq.h b/sql/item_uniq.h index f2c64c4bde9..9b370b70181 100644 --- a/sql/item_uniq.h +++ b/sql/item_uniq.h @@ -41,10 +41,10 @@ public: :Item_sum_num(thd, item) {} double val() { return 0.0; } enum Sumfunctype sum_func () const {return UNIQUE_USERS_FUNC;} - bool reset() { return 0;} + void clear() {} bool add() { return 0; } void reset_field() {} - void update_field(int offset) {} + void update_field() {} bool fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) { fixed= 1; diff --git a/sql/lex.h b/sql/lex.h index 24bfe796cc0..e11b50ed16d 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -249,6 +249,12 @@ static SYMBOL symbols[] = { { "MASTER_PASSWORD", SYM(MASTER_PASSWORD_SYM),0,0}, { "MASTER_PORT", SYM(MASTER_PORT_SYM),0,0}, { "MASTER_SERVER_ID", SYM(MASTER_SERVER_ID_SYM),0,0}, + { "MASTER_SSL", SYM(MASTER_SSL_SYM),0,0}, + { "MASTER_SSL_CA", SYM(MASTER_SSL_CA_SYM),0,0}, + { "MASTER_SSL_CAPATH",SYM(MASTER_SSL_CAPATH_SYM),0,0}, + { "MASTER_SSL_CERT", SYM(MASTER_SSL_CERT_SYM),0,0}, + { "MASTER_SSL_CIPHER",SYM(MASTER_SSL_CIPHER_SYM),0,0}, + { "MASTER_SSL_KEY", SYM(MASTER_SSL_KEY_SYM),0,0}, { "MASTER_USER", SYM(MASTER_USER_SYM),0,0}, { "MAX_ROWS", SYM(MAX_ROWS),0,0}, { "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR), 0,0}, @@ -287,7 +293,9 @@ static SYMBOL symbols[] = { { "NOT", SYM(NOT),0,0}, { "NULL", SYM(NULL_SYM),0,0}, { "NUMERIC", SYM(NUMERIC_SYM),0,0}, + { "NVARCHAR", SYM(NVARCHAR_SYM),0,0}, { "OFFSET", SYM(OFFSET_SYM),0,0}, + { "OLD_PASSWORD", SYM(OLD_PASSWORD),0,0}, { "ON", SYM(ON),0,0}, { "OPEN", SYM(OPEN_SYM),0,0}, { "OPTIMIZE", SYM(OPTIMIZE),0,0}, @@ -404,6 +412,7 @@ static SYMBOL symbols[] = { { "USE", SYM(USE_SYM),0,0}, { "USE_FRM", SYM(USE_FRM),0,0}, { "USER", SYM(USER),0,0}, + { "UNTIL", SYM(UNTIL_SYM),0,0}, { "USING", SYM(USING),0,0}, { "UTC_DATE", SYM(UTC_DATE_SYM),0,0}, { "UTC_TIME", SYM(UTC_TIME_SYM),0,0}, @@ -473,9 +482,7 @@ static SYMBOL sql_functions[] = { { "COUNT", SYM(COUNT_SYM),0,0}, { "COS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)}, { "COT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)}, -#ifdef HAVE_COMPRESS { "CRC32", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_crc32)}, -#endif { "CROSSES", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_crosses)}, { "CURDATE", SYM(CURDATE),0,0}, { "CURTIME", SYM(CURTIME),0,0}, @@ -590,7 +597,6 @@ static SYMBOL sql_functions[] = { { "NUMPOINTS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_numpoints)}, { "OCTET_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)}, { "OCT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_oct)}, - { "OLD_PASSWORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_old_password)}, { "ORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ord)}, { "OVERLAPS", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_overlaps)}, { "PERIOD_ADD", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_add)}, diff --git a/sql/lock.cc b/sql/lock.cc index 82004298453..29795415720 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -75,8 +75,6 @@ TODO: #include "../myisammrg/myrg_def.h" #endif -extern HASH open_cache; - static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count, bool unlock, TABLE **write_locked); static int lock_external(THD *thd, TABLE **table,uint count); @@ -526,7 +524,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) table->locked_by_name=1; table_list->table=table; - if (hash_insert(&open_cache, (byte*) table)) + if (my_hash_insert(&open_cache, (byte*) table)) { my_free((gptr) table,MYF(0)); DBUG_RETURN(-1); diff --git a/sql/log.cc b/sql/log.cc index d3ba5b63e19..608a7e94431 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -31,7 +31,6 @@ #include <m_ctype.h> // For test_if_number MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log; -extern I_List<i_string> binlog_do_db, binlog_ignore_db; static bool test_if_number(const char *str, long *res, bool allow_wildcards); @@ -270,7 +269,8 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, an extension for the binary log files. In this case we write a standard header to it. */ - if (my_b_write(&log_file, (byte*) BINLOG_MAGIC, BIN_LOG_HEADER_SIZE)) + if (my_b_safe_write(&log_file, (byte*) BINLOG_MAGIC, + BIN_LOG_HEADER_SIZE)) goto err; bytes_written += BIN_LOG_HEADER_SIZE; write_file_name_to_index_file=1; @@ -704,6 +704,7 @@ int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) rli->group_relay_log_pos = BIN_LOG_HEADER_SIZE; strmake(rli->group_relay_log_name,rli->linfo.log_file_name, sizeof(rli->group_relay_log_name)-1); + rli->notify_group_relay_log_name_update(); } /* Store where we are in the new file for the execution thread */ @@ -1091,8 +1092,11 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, if (thd) { // Normal thread - if ((thd->options & OPTION_LOG_OFF) && - (thd->master_access & SUPER_ACL)) + if ((thd->options & OPTION_LOG_OFF) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + && (thd->master_access & SUPER_ACL) +#endif +) { VOID(pthread_mutex_unlock(&LOCK_log)); return 0; // No logging @@ -1175,14 +1179,24 @@ bool MYSQL_LOG::write(Log_event* event_info) */ if (is_open()) { - const char *local_db = event_info->get_db(); + const char *local_db= event_info->get_db(); + IO_CACHE *file= &log_file; #ifdef USING_TRANSACTIONS - IO_CACHE *file = ((event_info->get_cache_stmt()) ? - &thd->transaction.trans_log : - &log_file); -#else - IO_CACHE *file = &log_file; -#endif + /* + Should we write to the binlog cache or to the binlog on disk? + Write to the binlog cache if: + - it is already not empty (meaning we're in a transaction; note that the + present event could be about a non-transactional table, but still we need + to write to the binlog cache in that case to handle updates to mixed + trans/non-trans table types the best possible in binlogging) + - or if the event asks for it (cache_stmt == true). + */ + if (opt_using_transactions && + (event_info->get_cache_stmt() || + (thd && my_b_tell(&thd->transaction.trans_log)))) + file= &thd->transaction.trans_log; +#endif + DBUG_PRINT("info",("event type=%d",event_info->get_type_code())); #ifdef HAVE_REPLICATION /* In the future we need to add to the following if tests like @@ -1401,6 +1415,13 @@ uint MYSQL_LOG::next_file_id() /* Write a cached log entry to the binary log + SYNOPSIS + write() + thd + cache The cache to copy to the binlog + commit_or_rollback If true, will write "COMMIT" in the end, if false will + write "ROLLBACK". + NOTE - We only come here if there is something in the cache. - The thing in the cache is always a complete transaction @@ -1408,10 +1429,13 @@ uint MYSQL_LOG::next_file_id() IMPLEMENTATION - To support transaction over replication, we wrap the transaction - with BEGIN/COMMIT in the binary log. + with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log. + We want to write a BEGIN/ROLLBACK block when a non-transactional table was + updated in a transaction which was rolled back. This is to ensure that the + same updates are run on the slave. */ -bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache) +bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback) { VOID(pthread_mutex_lock(&LOCK_log)); DBUG_ENTER("MYSQL_LOG::write(cache"); @@ -1465,7 +1489,10 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache) */ { - Query_log_event qinfo(thd, "COMMIT", 6, TRUE); + Query_log_event qinfo(thd, + commit_or_rollback ? "COMMIT" : "ROLLBACK", + commit_or_rollback ? 6 : 8, + TRUE); qinfo.set_log_pos(this); if (qinfo.write(&log_file) || flush_io_cache(&log_file)) goto err; @@ -1528,8 +1555,11 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, int tmp_errno=0; char buff[80],*end; end=buff; - if (!(thd->options & OPTION_UPDATE_LOG) && - (thd->master_access & SUPER_ACL)) + if (!(thd->options & OPTION_UPDATE_LOG) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + && (thd->master_access & SUPER_ACL) +#endif + ) { VOID(pthread_mutex_unlock(&LOCK_log)); return 0; @@ -1556,8 +1586,8 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, tmp_errno=errno; } if (my_b_printf(&log_file, "# User@Host: %s[%s] @ %s [%s]\n", - thd->priv_user, - thd->user, + thd->priv_user ? thd->priv_user : "", + thd->user ? thd->user : "", thd->host ? thd->host : "", thd->ip ? thd->ip : "") == (uint) -1) tmp_errno=errno; @@ -1642,6 +1672,9 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, SYNOPSIS wait_for_update() thd Thread variable + master_or_slave If 0, the caller is the Binlog_dump thread from master; + if 1, the caller is the SQL thread from the slave. This + influences only thd->proc_info. NOTES One must have a lock on LOCK_log before calling this function. @@ -1653,11 +1686,15 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, If you don't do it this way, you will get a deadlock in THD::awake() */ -void MYSQL_LOG:: wait_for_update(THD* thd) +void MYSQL_LOG:: wait_for_update(THD* thd, bool master_or_slave) { safe_mutex_assert_owner(&LOCK_log); const char* old_msg = thd->enter_cond(&update_cond, &LOCK_log, - "Slave: waiting for binlog update"); + master_or_slave ? + "Has read all relay log; waiting for \ +the I/O slave thread to update it" : + "Has sent all binlog to slave; \ +waiting for binlog to be updated"); pthread_cond_wait(&update_cond, &LOCK_log); pthread_mutex_unlock(&LOCK_log); // See NOTES thd->exit_cond(old_msg); diff --git a/sql/log_event.cc b/sql/log_event.cc index 8a52ad9ebca..b5fd78c06a9 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -27,25 +27,6 @@ #define log_cs &my_charset_latin1 /* - my_b_safe_write() -*/ - -inline int my_b_safe_write(IO_CACHE* file, const byte *buf, - int len) -{ - /* - Sasha: We are not writing this with the ? operator to avoid hitting - a possible compiler bug. At least gcc 2.95 cannot deal with - several layers of ternary operators that evaluated comma(,) operator - expressions inside - I do have a test case if somebody wants it - */ - if (file->type == SEQ_READ_APPEND) - return my_b_append(file, buf,len); - return my_b_write(file, buf,len); -} - - -/* pretty_print_str() */ @@ -239,12 +220,11 @@ const char* Log_event::get_type_str() #ifndef MYSQL_CLIENT Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans) - :temp_buf(0), exec_time(0), cached_event_len(0), flags(flags_arg), - thd(thd_arg) + :log_pos(0), temp_buf(0), exec_time(0), cached_event_len(0), + flags(flags_arg), thd(thd_arg) { server_id= thd->server_id; when= thd->start_time; - log_pos= thd->log_pos; cache_stmt= (using_trans && (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))); } @@ -331,7 +311,6 @@ int Log_event::exec_event(struct st_relay_log_info* rli) handler and MyISAM and STOP SLAVE is issued in the middle of the "transaction". START SLAVE will resume at BEGIN while the MyISAM table has already been updated. - */ if ((thd->options & OPTION_BEGIN) && opt_using_transactions) rli->inc_event_relay_log_pos(get_event_len()); @@ -605,6 +584,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; @@ -894,7 +875,6 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db) 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= (char*) rewrite_db(db); /* @@ -988,14 +968,14 @@ Default database: '%s'", */ } /* End of if (db_ok(... */ -end: - VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->db= 0; // prevent db from being freed thd->query= 0; // just to be sure VOID(pthread_mutex_unlock(&LOCK_thread_count)); // assume no convert for next query unless set explictly - //thd->variables.convert_set = 0; +#ifdef TO_BE_REMOVED + thd->variables.convert_set = 0; +#endif close_thread_tables(thd); free_root(&thd->mem_root,0); return (thd->query_error ? thd->query_error : Log_event::exec_event(rli)); @@ -1091,15 +1071,6 @@ int Start_log_event::write_data(IO_CACHE* file) the use of a bit of memory for a user lock which will not be used anymore. If the user lock is later used, the old one will be released. In other words, no deadlock problem. - - If we have an active transaction at this point, the master died - in the middle while writing the transaction to the binary log. - In this case we should stop the slave. - Guilhem 2003-06: I don't think we should. As the binlog is written before - the table changes are committed, rollback has occured on the master; we - should rather rollback on the slave and go on. If we don't rollback, and - the next query is not BEGIN, then it will be considered as part of the - unfinished transaction, and so will be rolled back at next BEGIN, which - is a bug. */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) @@ -1113,14 +1084,25 @@ int Start_log_event::exec_event(struct st_relay_log_info* rli) This is 4.x, so a Start_log_event is only at master startup, so we are sure the master has restarted and cleared his temp tables. */ - - /* - If the master died before writing the COMMIT to the binlog, rollback; - otherwise it does not hurt to rollback. - */ - ha_rollback(thd); close_temporary_tables(thd); cleanup_load_tmpdir(); + /* + As a transaction NEVER spans on 2 or more binlogs: + if we have an active transaction at this point, the master died while + writing the transaction to the binary log, i.e. while flushing the binlog + cache to the binlog. As the write was started, the transaction had been + committed on the master, so we lack of information to replay this + transaction on the slave; all we can do is stop with error. + */ + if (thd->options & OPTION_BEGIN) + { + slave_print_error(rli, 0, + "there is an unfinished transaction in the relay log \ +(could find neither COMMIT nor ROLLBACK in the relay log); it could be that \ +the master died while writing the transaction to its binary log. Now the slave \ +is rolling back the transaction."); + return(1); + } break; /* @@ -1471,6 +1453,13 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len, #ifdef MYSQL_CLIENT void Load_log_event::print(FILE* file, bool short_form, char* last_db) { + print(file, short_form, last_db, 0); +} + + +void Load_log_event::print(FILE* file, bool short_form, char* last_db, + bool commented) +{ if (!short_form) { print_header(file); @@ -1486,9 +1475,12 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) } if (db && db[0] && !same_db) - fprintf(file, "use %s;\n", db); + fprintf(file, "%suse %s;\n", + commented ? "# " : "", + db); - fprintf(file, "LOAD DATA "); + fprintf(file, "%sLOAD DATA ", + commented ? "# " : ""); if (check_fname_outside_temp_buf()) fprintf(file, "LOCAL "); fprintf(file, "INFILE '%-*s' ", fname_len, fname); @@ -1535,8 +1527,8 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) pretty_print_str(file, sql_ex.line_start, sql_ex.line_start_len); } - if ((int)skip_lines > 0) - fprintf(file, " IGNORE %ld LINES ", (long) skip_lines); + if ((long) skip_lines > 0) + fprintf(file, " IGNORE %ld LINES", (long) skip_lines); if (num_fields) { @@ -1607,12 +1599,23 @@ void Load_log_event::set_fields(List<Item> &field_list) int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, bool use_rli_only_for_errors) { - init_sql_alloc(&thd->mem_root, 8192,0); thd->db= (char*) rewrite_db(db); DBUG_ASSERT(thd->query == 0); thd->query = 0; // Should not be needed thd->query_error = 0; + /* + We test replicate_*_db rules. Note that we have already prepared the file + to load, even if we are going to ignore and delete it now. So it is + possible that we did a lot of disk writes for nothing. In other words, a + big LOAD DATA INFILE on the master will still consume a lot of space on + the slave (space in the relay log + space of temp files: twice the space + of the file to load...) even if it will finally be ignored. + TODO: fix this; this can be done by testing rules in + Create_file_log_event::exec_event() and then discarding Append_block and + al. Another way is do the filtering in the I/O thread (more efficient: no + disk writes at all). + */ if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { thd->set_time((time_t)when); @@ -1642,20 +1645,22 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, else if (sql_ex.opt_flags & IGNORE_FLAG) handle_dup= DUP_IGNORE; else + { /* - Note that when replication is running fine, if it was DUP_ERROR on the + When replication is running fine, if it was DUP_ERROR on the master then we could choose DUP_IGNORE here, because if DUP_ERROR suceeded on master, and data is identical on the master and slave, then there should be no uniqueness errors on slave, so DUP_IGNORE is the same as DUP_ERROR. But in the unlikely case of uniqueness errors - (because the data on the master and slave happen to be different (user - error or bug), we want LOAD DATA to print an error message on the - slave to discover the problem. + (because the data on the master and slave happen to be different + (user error or bug), we want LOAD DATA to print an error message on + the slave to discover the problem. If reading from net (a 3.23 master), mysql_load() will change this to DUP_IGNORE. */ handle_dup= DUP_ERROR; + } sql_exchange ex((char*)fname, sql_ex.opt_flags & DUMPFILE_FLAG); String field_term(sql_ex.field_term,sql_ex.field_term_len,log_cs); @@ -1731,7 +1736,7 @@ Slave: load data infile on table '%s' at log position %s in log \ err=ER(sql_errno); } slave_print_error(rli,sql_errno,"\ -Error '%s' running lOAD DATA INFILE on table '%s'. Default database: '%s'", +Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'", err, (char*)table_name, print_slave_db_safe(db)); free_root(&thd->mem_root,0); return 1; @@ -1762,14 +1767,13 @@ Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'", #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Rotate_log_event::pack_info(Protocol *protocol) { - char *buf, *b_pos; - if (!(buf= my_malloc(ident_len + 45, MYF(MY_WME)))) - return; - memcpy(buf, new_log_ident, ident_len); - b_pos= strmov(buf + ident_len, ";pos="); - b_pos= longlong10_to_str(pos, b_pos, 10); - protocol->store(buf, (uint) (b_pos-buf), &my_charset_bin); - my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); + char buf1[256], buf[22]; + String tmp(buf1, sizeof(buf1), log_cs); + tmp.length(0); + tmp.append(new_log_ident, ident_len); + tmp.append(";pos="); + tmp.append(llstr(pos,buf)); + protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin); } #endif @@ -1865,16 +1869,31 @@ int Rotate_log_event::write_data(IO_CACHE* file) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) int Rotate_log_event::exec_event(struct st_relay_log_info* rli) { - char* log_name = rli->group_master_log_name; DBUG_ENTER("Rotate_log_event::exec_event"); pthread_mutex_lock(&rli->data_lock); - memcpy(log_name, new_log_ident, ident_len+1); - rli->group_master_log_pos = pos; rli->event_relay_log_pos += get_event_len(); - rli->group_relay_log_pos = rli->event_relay_log_pos; - DBUG_PRINT("info", ("group_master_log_pos: %lu", - (ulong) rli->group_master_log_pos)); + /* + If we are in a transaction: the only normal case is when the I/O thread was + copying a big transaction, then it was stopped and restarted: we have this + in the relay log: + BEGIN + ... + ROTATE (a fake one) + ... + COMMIT or ROLLBACK + In that case, we don't want to touch the coordinates which correspond to the + beginning of the transaction. + */ + if (!(thd->options & OPTION_BEGIN)) + { + memcpy(rli->group_master_log_name, new_log_ident, ident_len+1); + rli->notify_group_master_log_name_update(); + rli->group_master_log_pos = pos; + rli->group_relay_log_pos = rli->event_relay_log_pos; + DBUG_PRINT("info", ("group_master_log_pos: %lu", + (ulong) rli->group_master_log_pos)); + } pthread_mutex_unlock(&rli->data_lock); pthread_cond_broadcast(&rli->data_cond); flush_relay_log_info(rli); @@ -1894,8 +1913,8 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Intvar_log_event::pack_info(Protocol *protocol) { - char buf[64], *pos; - pos= strmov(buf, get_var_type_name()); + char buf[256], *pos; + pos= strmake(buf, get_var_type_name(), sizeof(buf)-23); *pos++= '='; pos= longlong10_to_str(val, pos, -10); protocol->store(buf, (uint) (pos-buf), &my_charset_bin); @@ -2462,8 +2481,8 @@ void Stop_log_event::print(FILE* file, bool short_form, char* last_db) Stop_log_event::exec_event() The master stopped. - We used to clean up all temporary tables but this is useless as, as the master - has shut down properly, it has written all DROP TEMPORARY TABLE and DO + We used to clean up all temporary tables but this is useless as, as the + master has shut down properly, it has written all DROP TEMPORARY TABLE and DO RELEASE_LOCK (prepared statements' deletion is TODO). We used to clean up slave_load_tmpdir, but this is useless as it has been cleared at the end of LOAD DATA INFILE. @@ -2605,10 +2624,12 @@ void Create_file_log_event::print(FILE* file, bool short_form, if (enable_local) { - if (!check_fname_outside_temp_buf()) - fprintf(file, "#"); - Load_log_event::print(file, 1, last_db); - fprintf(file, "#"); + Load_log_event::print(file, 1, last_db, !check_fname_outside_temp_buf()); + /* + That one is for "file_id: etc" below: in mysqlbinlog we want the #, in + SHOW BINLOG EVENTS we don't. + */ + fprintf(file, "#"); } fprintf(file, " file_id: %d block_len: %d\n", file_id, block_len); @@ -2641,7 +2662,7 @@ void Create_file_log_event::pack_info(Protocol *protocol) pos= int10_to_str((long) block_len, pos, 10); protocol->store(buf, (uint) (pos-buf), &my_charset_bin); } -#endif +#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */ /* @@ -2665,7 +2686,7 @@ int Create_file_log_event::exec_event(struct st_relay_log_info* rli) init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0, MYF(MY_WME|MY_NABP))) { - slave_print_error(rli,my_errno, "Could not open file '%s'", fname_buf); + slave_print_error(rli,my_errno, "Error in Create_file event: could not open file '%s'", fname_buf); goto err; } @@ -2676,7 +2697,9 @@ int Create_file_log_event::exec_event(struct st_relay_log_info* rli) if (write_base(&file)) { strmov(p, ".info"); // to have it right in the error message - slave_print_error(rli,my_errno, "Could not write to file '%s'", fname_buf); + slave_print_error(rli,my_errno, + "Error in Create_file event: could not write to file '%s'", + fname_buf); goto err; } end_io_cache(&file); @@ -2686,16 +2709,14 @@ int Create_file_log_event::exec_event(struct st_relay_log_info* rli) if ((fd = my_open(fname_buf, O_WRONLY|O_CREAT|O_BINARY|O_TRUNC, MYF(MY_WME))) < 0) { - slave_print_error(rli,my_errno, "Could not open file '%s'", fname_buf); + slave_print_error(rli,my_errno, "Error in Create_file event: could not open file '%s'", fname_buf); goto err; } if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP))) { - slave_print_error(rli,my_errno, "Write to '%s' failed", fname_buf); + slave_print_error(rli,my_errno, "Error in Create_file event: write to '%s' failed", fname_buf); goto err; } - if (mysql_bin_log.is_open()) - mysql_bin_log.write(this); error=0; // Everything is ok err: @@ -2705,7 +2726,7 @@ err: my_close(fd, MYF(0)); return error ? 1 : Log_event::exec_event(rli); } -#endif +#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */ /************************************************************************** @@ -2717,11 +2738,12 @@ err: */ #ifndef MYSQL_CLIENT -Append_block_log_event::Append_block_log_event(THD* thd_arg, char* block_arg, +Append_block_log_event::Append_block_log_event(THD* thd_arg, const char* db_arg, + char* block_arg, uint block_len_arg, bool using_trans) :Log_event(thd_arg,0, using_trans), block(block_arg), - block_len(block_len_arg), file_id(thd_arg->file_id) + block_len(block_len_arg), file_id(thd_arg->file_id), db(db_arg) { } #endif @@ -2787,7 +2809,7 @@ void Append_block_log_event::pack_info(Protocol *protocol) block_len)); protocol->store(buf, length, &my_charset_bin); } -#endif +#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */ /* @@ -2805,16 +2827,14 @@ int Append_block_log_event::exec_event(struct st_relay_log_info* rli) memcpy(p, ".data", 6); if ((fd = my_open(fname, O_WRONLY|O_APPEND|O_BINARY, MYF(MY_WME))) < 0) { - slave_print_error(rli,my_errno, "Could not open file '%s'", fname); + slave_print_error(rli,my_errno, "Error in Append_block event: could not open file '%s'", fname); goto err; } if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP))) { - slave_print_error(rli,my_errno, "Write to '%s' failed", fname); + slave_print_error(rli,my_errno, "Error in Append_block event: write to '%s' failed", fname); goto err; } - if (mysql_bin_log.is_open()) - mysql_bin_log.write(this); error=0; err: @@ -2834,8 +2854,9 @@ err: */ #ifndef MYSQL_CLIENT -Delete_file_log_event::Delete_file_log_event(THD *thd_arg, bool using_trans) - :Log_event(thd_arg, 0, using_trans),file_id(thd_arg->file_id) +Delete_file_log_event::Delete_file_log_event(THD *thd_arg, const char* db_arg, + bool using_trans) + :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg) { } #endif @@ -2908,11 +2929,9 @@ int Delete_file_log_event::exec_event(struct st_relay_log_info* rli) (void) my_delete(fname, MYF(MY_WME)); memcpy(p, ".info", 6); (void) my_delete(fname, MYF(MY_WME)); - if (mysql_bin_log.is_open()) - mysql_bin_log.write(this); return Log_event::exec_event(rli); } -#endif +#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */ /************************************************************************** @@ -2924,8 +2943,9 @@ int Delete_file_log_event::exec_event(struct st_relay_log_info* rli) */ #ifndef MYSQL_CLIENT -Execute_load_log_event::Execute_load_log_event(THD *thd_arg, bool using_trans) - :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id) +Execute_load_log_event::Execute_load_log_event(THD *thd_arg, const char* db_arg, + bool using_trans) + :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg) { } #endif @@ -2997,7 +3017,6 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli) char *p= slave_load_file_stem(fname, file_id, server_id); int fd; int error = 1; - ulong save_options; IO_CACHE file; Load_log_event* lev = 0; @@ -3006,7 +3025,7 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli) init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0, MYF(MY_WME|MY_NABP))) { - slave_print_error(rli,my_errno, "Could not open file '%s'", fname); + slave_print_error(rli,my_errno, "Error in Exec_load event: could not open file '%s'", fname); goto err; } if (!(lev = (Load_log_event*)Log_event::read_log_event(&file, @@ -3014,21 +3033,16 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli) (bool)0)) || lev->get_type_code() != NEW_LOAD_EVENT) { - slave_print_error(rli,0, "File '%s' appears corrupted", fname); + slave_print_error(rli,0, "Error in Exec_load event: file '%s' appears corrupted", fname); goto err; } - /* - We are going to create a Load_log_event to finally load into the table. - This event should not go into the binlog: in the binlog we only want the - Create_file, Append_blocks and Execute_load. We disable binary logging and - restore the thread's options just after finishing the load. - */ - save_options = thd->options; - thd->options &= ~ (ulong) (OPTION_BIN_LOG); + lev->thd = thd; /* lev->exec_event should use rli only for errors - i.e. should not advance rli's position + i.e. should not advance rli's position. + lev->exec_event is the place where the table is loaded (it calls + mysql_load()). */ if (lev->exec_event(0,rli,1)) { @@ -3049,15 +3063,11 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli) tmp, fname); my_free(tmp,MYF(0)); } - thd->options= save_options; goto err; } - thd->options = save_options; (void) my_delete(fname, MYF(MY_WME)); memcpy(p, ".data", 6); (void) my_delete(fname, MYF(MY_WME)); - if (mysql_bin_log.is_open()) - mysql_bin_log.write(this); error = 0; err: diff --git a/sql/log_event.h b/sql/log_event.h index a58479e2589..8ba5d0379a0 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -590,6 +590,7 @@ public: #endif /* HAVE_REPLICATION */ #else void print(FILE* file, bool short_form = 0, char* last_db = 0); + void print(FILE* file, bool short_form, char* last_db, bool commented); #endif Load_log_event(const char* buf, int event_len, bool old_format); @@ -934,9 +935,20 @@ public: char* block; uint block_len; uint file_id; - + /* + 'db' is filled when the event is created in mysql_load() (the event needs to + have a 'db' member to be well filtered by binlog-*-db rules). 'db' is not + written to the binlog (it's not used by Append_block_log_event::write()), so + it can't be read in the Append_block_log_event(const char* buf, int + event_len) constructor. + In other words, 'db' is used only for filtering by binlog-*-db rules. + Create_file_log_event is different: its 'db' (which is inherited from + Load_log_event) is written to the binlog and can be re-read. + */ + const char* db; + #ifndef MYSQL_CLIENT - Append_block_log_event(THD* thd, char* block_arg, + Append_block_log_event(THD* thd, const char* db_arg, char* block_arg, uint block_len_arg, bool using_trans); #ifdef HAVE_REPLICATION int exec_event(struct st_relay_log_info* rli); @@ -952,6 +964,7 @@ public: int get_data_size() { return block_len + APPEND_BLOCK_HEADER_LEN ;} bool is_valid() { return block != 0; } int write_data(IO_CACHE* file); + const char* get_db() { return db; } }; /***************************************************************************** @@ -963,9 +976,10 @@ class Delete_file_log_event: public Log_event { public: uint file_id; + const char* db; /* see comment in Append_block_log_event */ #ifndef MYSQL_CLIENT - Delete_file_log_event(THD* thd, bool using_trans); + Delete_file_log_event(THD* thd, const char* db_arg, bool using_trans); #ifdef HAVE_REPLICATION void pack_info(Protocol* protocol); int exec_event(struct st_relay_log_info* rli); @@ -981,6 +995,7 @@ public: int get_data_size() { return DELETE_FILE_HEADER_LEN ;} bool is_valid() { return file_id != 0; } int write_data(IO_CACHE* file); + const char* get_db() { return db; } }; /***************************************************************************** @@ -992,9 +1007,10 @@ class Execute_load_log_event: public Log_event { public: uint file_id; - + const char* db; /* see comment in Append_block_log_event */ + #ifndef MYSQL_CLIENT - Execute_load_log_event(THD* thd, bool using_trans); + Execute_load_log_event(THD* thd, const char* db_arg, bool using_trans); #ifdef HAVE_REPLICATION void pack_info(Protocol* protocol); int exec_event(struct st_relay_log_info* rli); @@ -1009,6 +1025,7 @@ public: int get_data_size() { return EXEC_LOAD_HEADER_LEN ;} bool is_valid() { return file_id != 0; } int write_data(IO_CACHE* file); + const char* get_db() { return db; } }; #ifdef MYSQL_CLIENT diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index a28d343ffd6..7961d9c3468 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -74,9 +74,6 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; ****************************************************************************/ #define ACL_CACHE_SIZE 256 -/* Password lengh for 4.1 version previous versions had 16 bytes password hash */ -#define HASH_PASSWORD_LENGTH 45 -#define HASH_OLD_PASSWORD_LENGTH 16 #define MAX_PASSWORD_LENGTH 32 #define HOST_CACHE_SIZE 128 #define MAX_ACCEPT_RETRY 10 // Test accept this many times @@ -212,16 +209,17 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; #define MODE_NOT_USED 16 #define MODE_ONLY_FULL_GROUP_BY 32 #define MODE_NO_UNSIGNED_SUBTRACTION 64 -#define MODE_POSTGRESQL 128 -#define MODE_ORACLE 256 -#define MODE_MSSQL 512 -#define MODE_DB2 1024 -#define MODE_SAPDB 2048 -#define MODE_NO_KEY_OPTIONS 4096 -#define MODE_NO_TABLE_OPTIONS 8192 -#define MODE_NO_FIELD_OPTIONS 16384 -#define MODE_MYSQL323 32768 -#define MODE_MYSQL40 65536 +#define MODE_NO_DIR_IN_CREATE 128 +#define MODE_POSTGRESQL 256 +#define MODE_ORACLE 512 +#define MODE_MSSQL 1024 +#define MODE_DB2 2048 +#define MODE_SAPDB 4096 +#define MODE_NO_KEY_OPTIONS 8192 +#define MODE_NO_TABLE_OPTIONS 16384 +#define MODE_NO_FIELD_OPTIONS 32768 +#define MODE_MYSQL323 65536 +#define MODE_MYSQL40 (MODE_MYSQL323*2) #define MODE_ANSI (MODE_MYSQL40*2) #define MODE_NO_AUTO_VALUE_ON_ZERO (MODE_ANSI*2) @@ -324,6 +322,15 @@ typedef compare_func_creator (*chooser_compare_func_creator)(bool invert); #include "opt_range.h" #ifdef HAVE_QUERY_CACHE +struct Query_cache_query_flags +{ + unsigned int client_long_flag:1; + uint character_set_client_num; + uint character_set_results_num; + uint collation_connection_num; + ha_rows limit; +}; +#define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags) #include "sql_cache.h" #define query_cache_store_query(A, B) query_cache.store_query(A, B) #define query_cache_destroy() query_cache.destroy() @@ -337,6 +344,7 @@ typedef compare_func_creator (*chooser_compare_func_creator)(bool invert); #define query_cache_invalidate_by_MyISAM_filename_ref \ &query_cache_invalidate_by_MyISAM_filename #else +#define QUERY_CACHE_FLAGS_SIZE 0 #define query_cache_store_query(A, B) #define query_cache_destroy() #define query_cache_result_size_limit(A) @@ -395,22 +403,30 @@ bool check_stack_overrun(THD *thd,char *dummy); #define check_stack_overrun(A, B) 0 #endif -bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, - bool *write_to_binlog); void table_cache_init(void); void table_cache_free(void); uint cached_tables(void); void kill_mysql(void); void close_connection(THD *thd, uint errcode, bool lock); -bool check_access(THD *thd, ulong access, const char *db=0, ulong *save_priv=0, - bool no_grant=0, bool no_errors=0); +bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, + bool *write_to_binlog); +#ifndef NO_EMBEDDED_ACCESS_CHECKS +bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv, + bool no_grant, bool no_errors); bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables, - bool no_errors=0); + bool no_errors); bool check_global_access(THD *thd, ulong want_access); +#else +#define check_access(thd, access, db, save_priv, no_grant, no_errors) false +#define check_table_access(thd, want_access, tables, no_errors) false +#define check_global_access(thd, want_access) false +#endif int mysql_backup_table(THD* thd, TABLE_LIST* table_list); int mysql_restore_table(THD* thd, TABLE_LIST* table_list); +int mysql_checksum_table(THD* thd, TABLE_LIST* table_list, + HA_CHECK_OPT* check_opt); int mysql_check_table(THD* thd, TABLE_LIST* table_list, HA_CHECK_OPT* check_opt); int mysql_repair_table(THD* thd, TABLE_LIST* table_list, @@ -558,7 +574,8 @@ void mysqld_list_processes(THD *thd,const char *user,bool verbose); int mysqld_show_status(THD *thd); int mysqld_show_variables(THD *thd,const char *wild); int mysqld_show(THD *thd, const char *wild, show_var_st *variables, - enum enum_var_type value_type); + enum enum_var_type value_type, + pthread_mutex_t *mutex); int mysqld_show_charsets(THD *thd,const char *wild); int mysqld_show_collations(THD *thd,const char *wild); int mysqld_show_table_types(THD *thd); @@ -576,6 +593,7 @@ void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); int check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, List<Item> &values, ulong counter); +void setup_param_functions(Item_param *param, uchar param_type); /* sql_error.cc */ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code, @@ -597,7 +615,8 @@ void set_item_name(Item *item,char *pos,uint length); bool add_field_to_list(THD *thd, char *field_name, enum enum_field_types type, char *length, char *decimal, uint type_modifier, - Item *default_value, Item *comment, + Item *default_value, + LEX_STRING *comment, char *change, TYPELIB *interval,CHARSET_INFO *cs, uint uint_geom_type); void store_position_for_column(const char *name); @@ -705,6 +724,9 @@ bool open_log(MYSQL_LOG *log, const char *hostname, enum_log_type type, bool read_append, bool no_auto_events, ulong max_size); +/* mysqld.cc */ +extern void yyerror(const char*); + /* External variables */ @@ -715,10 +737,10 @@ extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH], #define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list)) extern MY_TMPDIR mysql_tmpdir_list; extern const char *command_name[]; -extern const char *first_keyword, *localhost, *delayed_user, *binary_keyword; +extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword; extern const char **errmesg; /* Error messages */ extern const char *myisam_recover_options_str; -extern const char *in_left_expr_name; +extern const char *in_left_expr_name, *in_additional_cond; extern uchar *days_in_month; extern char language[LIBLEN],reg_ext[FN_EXTLEN]; extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN]; @@ -747,6 +769,7 @@ extern ulong ha_read_first_count, ha_read_last_count; extern ulong ha_read_rnd_count, ha_read_rnd_next_count; extern ulong ha_commit_count, ha_rollback_count,table_cache_size; extern ulong max_connections,max_connect_errors, connect_timeout; +extern ulong slave_net_timeout; extern ulong max_insert_delayed_threads, max_user_connections; extern ulong long_query_count, what_to_log,flush_time; extern ulong query_buff_size, thread_stack,thread_stack_min; @@ -772,7 +795,7 @@ extern my_bool opt_safe_show_db, opt_local_infile, lower_case_table_names; extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern my_bool opt_readonly; extern my_bool opt_enable_named_pipe; -extern my_bool opt_old_passwords, use_old_passwords; +extern my_bool opt_secure_auth; extern char *shared_memory_base_name, *mysqld_unix_port; extern bool opt_enable_shared_memory; @@ -784,7 +807,7 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_error_log, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone, LOCK_slave_list, LOCK_active_mi, LOCK_manager, - LOCK_global_system_variables; + LOCK_global_system_variables, LOCK_user_conn; extern rw_lock_t LOCK_grant; extern pthread_cond_t COND_refresh, COND_thread_count, COND_manager; extern pthread_attr_t connection_attrib; @@ -792,7 +815,8 @@ extern I_List<THD> threads; extern I_List<NAMED_LIST> key_caches; extern MY_BITMAP temp_pool; extern DATE_FORMAT dayord; -extern String empty_string; +extern String my_empty_string; +extern String my_null_string; extern SHOW_VAR init_vars[],status_vars[], internal_vars[]; extern struct show_table_type_st table_type_vars[]; extern SHOW_COMP_OPTION have_isam; @@ -801,6 +825,11 @@ extern SHOW_COMP_OPTION have_berkeley_db; extern struct system_variables global_system_variables; extern struct system_variables max_system_variables; extern struct rand_struct sql_rand; +extern HASH open_cache; +extern TABLE *unused_tables; +extern I_List<i_string> binlog_do_db, binlog_ignore_db; +extern const char* any_db; +extern struct my_option my_long_options[]; /* optional things, have_* variables */ @@ -935,6 +964,14 @@ bool flush_error_log(void); void free_list(I_List <i_string_pair> *list); void free_list(I_List <i_string> *list); +/* sql_yacc.cc */ +extern int yyparse(void *thd); + +/* frm_crypt.cc */ +#ifdef HAVE_CRYPTED_FRM +SQL_CRYPT *get_crypt_for_frm(void); +#endif + /* Some inline functions for more speed */ inline bool add_item_to_list(THD *thd, Item *item) @@ -964,6 +1001,12 @@ inline void mark_as_null_row(TABLE *table) bfill(table->null_flags,table->null_bytes,255); } +inline void table_case_convert(char * name, uint length) +{ + if (lower_case_table_names) + my_casedn(files_charset_info, name, length); +} + compare_func_creator comp_eq_creator(bool invert); compare_func_creator comp_ge_creator(bool invert); compare_func_creator comp_gt_creator(bool invert); @@ -980,6 +1023,7 @@ compare_func_creator comp_ne_creator(bool invert); table_list TABLE_LIST structure pointer (owner of TABLE) tablenr - table number */ + inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr) { table->used_fields= 0; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 41bd9706fc3..6213c43b80b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -210,6 +210,7 @@ const char *sql_mode_names[] = { "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE", "?", "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION", + "NO_DIR_IN_CREATE", "POSTGRESQL", "ORACLE", "MSSQL", "DB2", "SAPDB", "NO_KEY_OPTIONS", "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", "ANSI", "NO_AUTO_VALUE_ON_ZERO", NullS @@ -217,7 +218,7 @@ const char *sql_mode_names[] = TYPELIB sql_mode_typelib= { array_elements(sql_mode_names)-1,"", sql_mode_names }; const char *first_keyword= "first", *binary_keyword= "BINARY"; -const char *localhost= "localhost", *delayed_user= "DELAYED"; +const char *my_localhost= "localhost", *delayed_user= "DELAYED"; #if SIZEOF_OFF_T > 4 && defined(BIG_TABLES) #define GET_HA_ROWS GET_ULL #else @@ -257,9 +258,10 @@ my_bool opt_local_infile, opt_external_locking, opt_slave_compressed_protocol; my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool lower_case_table_names, opt_old_rpl_compat; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; -my_bool opt_log_slave_updates= 0, opt_old_passwords=0, use_old_passwords=0; +my_bool opt_log_slave_updates= 0; my_bool opt_console= 0, opt_bdb, opt_innodb, opt_isam; my_bool opt_readonly, use_temp_pool, relay_log_purge; +my_bool opt_secure_auth= 0; volatile bool mqh_used = 0; uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options; @@ -319,6 +321,8 @@ const char *myisam_recover_options_str="OFF"; const char *sql_mode_str="OFF"; /* name of reference on left espression in rewritten IN subquery */ const char *in_left_expr_name= "<left expr>"; +/* name of additional condition */ +const char *in_additional_cond= "<IN COND>"; FILE *bootstrap_file; @@ -362,15 +366,15 @@ pthread_t signal_thread; pthread_attr_t connection_attrib; /* replication parameters, if master_host is not NULL, we are a slave */ -my_bool master_ssl; uint master_port= MYSQL_PORT, master_connect_retry = 60; uint report_port= MYSQL_PORT; ulong master_retry_count=0; char *master_user, *master_password, *master_host, *master_info_file; -char *relay_log_info_file, *master_ssl_key, *master_ssl_cert; -char *master_ssl_capath, *master_ssl_cipher, *report_user; -char *report_password, *report_host; +char *relay_log_info_file, *report_user, *report_password, *report_host; char *opt_relay_logname = 0, *opt_relaylog_index_name=0; +my_bool master_ssl; +char *master_ssl_key, *master_ssl_cert; +char *master_ssl_ca, *master_ssl_capath, *master_ssl_cipher; /* Static variables */ @@ -871,8 +875,10 @@ void clean_up(bool print_message) if (use_slave_mask) bitmap_free(&slave_error_mask); #endif +#ifndef NO_EMBEDDED_ACCESS_CHECKS acl_free(1); grant_free(); +#endif query_cache_destroy(); table_cache_free(); hostname_cache_free(); @@ -1119,7 +1125,14 @@ static void server_init(void) IPaddr.sin_family = AF_INET; IPaddr.sin_addr.s_addr = my_bind_addr; IPaddr.sin_port = (unsigned short) htons((unsigned short) mysqld_port); + +#ifndef __WIN__ + /* + We should not use SO_REUSEADDR on windows as this would enable a + user to open two mysqld servers with the same TCP/IP port. + */ (void) setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,sizeof(arg)); +#endif /* __WIN__ */ if (bind(ip_sock, my_reinterpret_cast(struct sockaddr *) (&IPaddr), sizeof(IPaddr)) < 0) { @@ -1661,6 +1674,7 @@ static void init_signals(void) } +#ifndef EMBEDDED_LIBRARY static void start_signal_handler(void) { int error; @@ -1823,6 +1837,7 @@ extern "C" void *signal_hand(void *arg __attribute__((unused))) } return(0); /* purecov: deadcode */ } +#endif /*!EMBEDDED_LIBRARY*/ static void check_data_home(const char *path) {} @@ -2050,7 +2065,10 @@ static int init_common_variables(const char *conf_file_name, int argc, max_connections,table_cache_size)); sql_print_error("Warning: Changed limits: max_connections: %ld table_cache: %ld",max_connections,table_cache_size); } + open_files_limit= files; } +#else + open_files_limit= 0; /* Can't set or detect limit */ #endif unireg_init(opt_specialflag); /* Set up extern variabels */ init_errmessage(); /* Read error messages from file */ @@ -2080,11 +2098,11 @@ static int init_common_variables(const char *conf_file_name, int argc, } default_charset_info= default_collation; } - global_system_variables.character_set_server= default_charset_info; - global_system_variables.character_set_database= default_charset_info; + global_system_variables.collation_server= default_charset_info; + global_system_variables.collation_database= default_charset_info; + global_system_variables.collation_connection= default_charset_info; global_system_variables.character_set_results= default_charset_info; global_system_variables.character_set_client= default_charset_info; - global_system_variables.collation_connection= default_charset_info; if (use_temp_pool && bitmap_init(&temp_pool,1024,1)) return 1; @@ -2217,7 +2235,9 @@ Now disabling --log-slave-updates."); opt_error_log= 1; // Too long file name else { +#ifndef EMBEDDED_LIBRARY if (freopen(log_error_file, "a+", stdout)) +#endif freopen(log_error_file, "a+", stderr); } } @@ -2749,7 +2769,6 @@ static int bootstrap(FILE *file) { int error= 0; DBUG_ENTER("bootstrap"); -#ifndef EMBEDDED_LIBRARY // TODO: Enable this THD *thd= new THD; thd->bootstrap=1; @@ -2761,6 +2780,7 @@ static int bootstrap(FILE *file) thread_count++; bootstrap_file=file; +#ifndef EMBEDDED_LIBRARY // TODO: Enable this if (pthread_create(&thd->real_id,&connection_attrib,handle_bootstrap, (void*) thd)) { @@ -2775,11 +2795,17 @@ static int bootstrap(FILE *file) DBUG_PRINT("quit",("One thread died (count=%u)",thread_count)); } (void) pthread_mutex_unlock(&LOCK_thread_count); +#else + thd->mysql= 0; + handle_bootstrap((void *)thd); +#endif + error= thd->is_fatal_error; +#ifndef EMBEDDED_LIBRARY net_end(&thd->net); +#endif thd->cleanup(); delete thd; -#endif /* EMBEDDED_LIBRARY */ DBUG_RETURN(error); } @@ -2819,12 +2845,6 @@ static void create_new_thread(THD *thd) if (thread_count-delayed_insert_threads > max_used_connections) max_used_connections=thread_count-delayed_insert_threads; thd->thread_id=thread_id++; - for (uint i=0; i < 8 ; i++) // Generate password teststring - thd->scramble[i]= (char) (my_rnd(&sql_rand)*94+33); - thd->scramble[8]=0; - // Back it up as old clients may need it - memcpy(thd->old_scramble,thd->scramble,9); - thd->real_id=pthread_self(); // Keep purify happy @@ -3103,7 +3123,13 @@ extern "C" pthread_handler_decl(handle_connections_sockets, continue; } if (sock == unix_sock) - thd->host=(char*) localhost; + thd->host=(char*) my_localhost; +#ifdef __WIN__ + /* Set default wait_timeout */ + ulong wait_timeout= global_system_variables.net_wait_timeout * 1000; + (void) setsockopt(new_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&wait_timeout, + sizeof(wait_timeout)); +#endif create_new_thread(thd); } @@ -3187,7 +3213,7 @@ extern "C" pthread_handler_decl(handle_connections_namedpipes,arg) continue; } /* host name is unknown */ - thd->host = my_strdup(localhost,MYF(0)); /* Host is unknown */ + thd->host = my_strdup(my_localhost,MYF(0)); /* Host is unknown */ create_new_thread(thd); } @@ -3396,7 +3422,7 @@ errorconn: if (!event_client_read) CloseHandle(event_client_read); continue; } - thd->host = my_strdup(localhost,MYF(0)); /* Host is unknown */ + thd->host = my_strdup(my_localhost,MYF(0)); /* Host is unknown */ create_new_thread(thd); uint4korr(connect_number++); } @@ -3445,7 +3471,7 @@ enum options OPT_MASTER_RETRY_COUNT, OPT_MASTER_SSL, OPT_MASTER_SSL_KEY, OPT_MASTER_SSL_CERT, OPT_MASTER_SSL_CAPATH, - OPT_MASTER_SSL_CIPHER, + OPT_MASTER_SSL_CIPHER, OPT_MASTER_SSL_CA, OPT_SQL_BIN_UPDATE_SAME, OPT_REPLICATE_DO_DB, OPT_REPLICATE_IGNORE_DB, OPT_LOG_SLAVE_UPDATES, OPT_BINLOG_DO_DB, OPT_BINLOG_IGNORE_DB, @@ -3535,7 +3561,8 @@ enum options OPT_EXPIRE_LOGS_DAYS, OPT_DEFAULT_WEEK_FORMAT, OPT_GROUP_CONCAT_MAX_LEN, - OPT_DEFAULT_COLLATION + OPT_DEFAULT_COLLATION, + OPT_SECURE_AUTH }; @@ -3783,31 +3810,32 @@ thread is in the master's binlogs.", (gptr*) &master_info_file, (gptr*) &master_info_file, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"master-ssl", OPT_MASTER_SSL, - "Planned to enable the slave to connect to the master using SSL. Does nothing yet.", + "Enable the slave to connect to the master using SSL.", (gptr*) &master_ssl, (gptr*) &master_ssl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"master-ssl-key", OPT_MASTER_SSL_KEY, - "Master SSL keyfile name. Only applies if you have enabled master-ssl. Does \ -nothing yet.", + "Master SSL keyfile name. Only applies if you have enabled master-ssl.", (gptr*) &master_ssl_key, (gptr*) &master_ssl_key, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"master-ssl-cert", OPT_MASTER_SSL_CERT, "Master SSL certificate file name. Only applies if you have enabled \ -master-ssl. Does nothing yet.", +master-ssl", (gptr*) &master_ssl_cert, (gptr*) &master_ssl_cert, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"master-ssl-ca", OPT_MASTER_SSL_CA, + "Master SSL CA file. Only applies if you have enabled master-ssl.", + (gptr*) &master_ssl_ca, (gptr*) &master_ssl_ca, 0, GET_STR, OPT_ARG, + 0, 0, 0, 0, 0, 0}, {"master-ssl-capath", OPT_MASTER_SSL_CAPATH, - "Master SSL CA path. Only applies if you have enabled master-ssl. \ -Does nothing yet.", + "Master SSL CA path. Only applies if you have enabled master-ssl.", (gptr*) &master_ssl_capath, (gptr*) &master_ssl_capath, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"master-ssl-cipher", OPT_MASTER_SSL_CIPHER, - "Master SSL cipher. Only applies if you have enabled master-ssl. \ -Does nothing yet.", + "Master SSL cipher. Only applies if you have enabled master-ssl.", (gptr*) &master_ssl_cipher, (gptr*) &master_ssl_capath, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"myisam-recover", OPT_MYISAM_RECOVER, - "Syntax: myisam-recover[=option[,option...]], where option can be DEFAULT, BACKUP or FORCE.", + "Syntax: myisam-recover[=option[,option...]], where option can be DEFAULT, BACKUP, FORCE or QUICK.", (gptr*) &myisam_recover_options_str, (gptr*) &myisam_recover_options_str, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"memlock", OPT_MEMLOCK, "Lock mysqld in memory.", (gptr*) &locked_in_memory, @@ -3844,9 +3872,10 @@ Does nothing yet.", (gptr*) &opt_no_mix_types, (gptr*) &opt_no_mix_types, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif - {"old-protocol", 'o', "Use the old (3.20) protocol client/server protocol.", - (gptr*) &protocol_version, (gptr*) &protocol_version, 0, GET_UINT, NO_ARG, - PROTOCOL_VERSION, 0, 0, 0, 0, 0}, + {"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients).", + (gptr*) &global_system_variables.old_passwords, + (gptr*) &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, {"old-rpl-compat", OPT_OLD_RPL_COMPAT, "Use old LOAD DATA format in the binary log (don't save data in file).", (gptr*) &opt_old_rpl_compat, (gptr*) &opt_old_rpl_compat, 0, GET_BOOL, @@ -3913,8 +3942,6 @@ relay logs.", GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients).", - (gptr*) &opt_old_passwords, (gptr*) &opt_old_passwords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifndef TO_BE_DELETED {"safe-show-database", OPT_SAFE_SHOW_DB, "Deprecated option; One should use GRANT SHOW DATABASES instead...", @@ -3924,6 +3951,9 @@ relay logs.", "Don't allow new user creation by the user who has no write privileges to the mysql.user table.", (gptr*) &opt_safe_user_create, (gptr*) &opt_safe_user_create, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"secure-auth", OPT_SECURE_AUTH, "Disallow authentication for accounts that have old (pre-4.1) passwords.", + (gptr*) &opt_secure_auth, (gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG, + my_bool(0), 0, 0, 0, 0, 0}, {"server-id", OPT_SERVER_ID, "Uniquely identifies the server instance in the community of replication partners.", (gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, @@ -4196,7 +4226,7 @@ replicating a LOAD DATA INFILE command.", "Max packetlength to send/receive from to server.", (gptr*) &global_system_variables.max_allowed_packet, (gptr*) &max_system_variables.max_allowed_packet, 0, GET_ULONG, - REQUIRED_ARG, 1024*1024L, 80, 1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0}, + REQUIRED_ARG, 1024*1024L, 1024, 1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0}, {"max_binlog_cache_size", OPT_MAX_BINLOG_CACHE_SIZE, "Can be used to restrict the total size used to cache a multi-transaction query.", (gptr*) &max_binlog_cache_size, (gptr*) &max_binlog_cache_size, 0, @@ -4452,14 +4482,15 @@ struct show_var_st status_vars[]= { {"Bytes_received", (char*) &bytes_received, SHOW_LONG}, {"Bytes_sent", (char*) &bytes_sent, SHOW_LONG}, {"Com_admin_commands", (char*) &com_other, SHOW_LONG}, - {"Com_alter_table", (char*) (com_stat+(uint) SQLCOM_ALTER_TABLE),SHOW_LONG}, {"Com_alter_db", (char*) (com_stat+(uint) SQLCOM_ALTER_DB),SHOW_LONG}, + {"Com_alter_table", (char*) (com_stat+(uint) SQLCOM_ALTER_TABLE),SHOW_LONG}, {"Com_analyze", (char*) (com_stat+(uint) SQLCOM_ANALYZE),SHOW_LONG}, {"Com_backup_table", (char*) (com_stat+(uint) SQLCOM_BACKUP_TABLE),SHOW_LONG}, {"Com_begin", (char*) (com_stat+(uint) SQLCOM_BEGIN),SHOW_LONG}, {"Com_change_db", (char*) (com_stat+(uint) SQLCOM_CHANGE_DB),SHOW_LONG}, {"Com_change_master", (char*) (com_stat+(uint) SQLCOM_CHANGE_MASTER),SHOW_LONG}, {"Com_check", (char*) (com_stat+(uint) SQLCOM_CHECK),SHOW_LONG}, + {"Com_checksum", (char*) (com_stat+(uint) SQLCOM_CHECKSUM),SHOW_LONG}, {"Com_commit", (char*) (com_stat+(uint) SQLCOM_COMMIT),SHOW_LONG}, {"Com_create_db", (char*) (com_stat+(uint) SQLCOM_CREATE_DB),SHOW_LONG}, {"Com_create_function", (char*) (com_stat+(uint) SQLCOM_CREATE_FUNCTION),SHOW_LONG}, @@ -4472,6 +4503,7 @@ struct show_var_st status_vars[]= { {"Com_drop_function", (char*) (com_stat+(uint) SQLCOM_DROP_FUNCTION),SHOW_LONG}, {"Com_drop_index", (char*) (com_stat+(uint) SQLCOM_DROP_INDEX),SHOW_LONG}, {"Com_drop_table", (char*) (com_stat+(uint) SQLCOM_DROP_TABLE),SHOW_LONG}, + {"Com_drop_user", (char*) (com_stat+(uint) SQLCOM_DROP_USER),SHOW_LONG}, {"Com_flush", (char*) (com_stat+(uint) SQLCOM_FLUSH),SHOW_LONG}, {"Com_grant", (char*) (com_stat+(uint) SQLCOM_GRANT),SHOW_LONG}, {"Com_ha_close", (char*) (com_stat+(uint) SQLCOM_HA_CLOSE),SHOW_LONG}, @@ -4486,6 +4518,7 @@ struct show_var_st status_vars[]= { {"Com_load_master_table", (char*) (com_stat+(uint) SQLCOM_LOAD_MASTER_TABLE),SHOW_LONG}, {"Com_lock_tables", (char*) (com_stat+(uint) SQLCOM_LOCK_TABLES),SHOW_LONG}, {"Com_optimize", (char*) (com_stat+(uint) SQLCOM_OPTIMIZE),SHOW_LONG}, + {"Com_preload_keys", (char*) (com_stat+(uint) SQLCOM_PRELOAD_KEYS),SHOW_LONG}, {"Com_purge", (char*) (com_stat+(uint) SQLCOM_PURGE),SHOW_LONG}, {"Com_purge_before_date", (char*) (com_stat+(uint) SQLCOM_PURGE_BEFORE),SHOW_LONG}, {"Com_rename_table", (char*) (com_stat+(uint) SQLCOM_RENAME_TABLE),SHOW_LONG}, @@ -4495,13 +4528,15 @@ struct show_var_st status_vars[]= { {"Com_reset", (char*) (com_stat+(uint) SQLCOM_RESET),SHOW_LONG}, {"Com_restore_table", (char*) (com_stat+(uint) SQLCOM_RESTORE_TABLE),SHOW_LONG}, {"Com_revoke", (char*) (com_stat+(uint) SQLCOM_REVOKE),SHOW_LONG}, + {"Com_revoke_all", (char*) (com_stat+(uint) SQLCOM_REVOKE_ALL),SHOW_LONG}, {"Com_rollback", (char*) (com_stat+(uint) SQLCOM_ROLLBACK),SHOW_LONG}, {"Com_savepoint", (char*) (com_stat+(uint) SQLCOM_SAVEPOINT),SHOW_LONG}, {"Com_select", (char*) (com_stat+(uint) SQLCOM_SELECT),SHOW_LONG}, {"Com_set_option", (char*) (com_stat+(uint) SQLCOM_SET_OPTION),SHOW_LONG}, - {"Com_show_binlog_events", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOG_EVENTS),SHOW_LONG}, {"Com_show_binlogs", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOGS),SHOW_LONG}, + {"Com_show_binlog_events", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOG_EVENTS),SHOW_LONG}, {"Com_show_charsets", (char*) (com_stat+(uint) SQLCOM_SHOW_CHARSETS),SHOW_LONG}, + {"Com_show_collations", (char*) (com_stat+(uint) SQLCOM_SHOW_COLLATIONS),SHOW_LONG}, {"Com_show_column_types", (char*) (com_stat+(uint) SQLCOM_SHOW_COLUMN_TYPES),SHOW_LONG}, {"Com_show_create_table", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE),SHOW_LONG}, {"Com_show_create_db", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE_DB),SHOW_LONG}, @@ -4509,6 +4544,7 @@ struct show_var_st status_vars[]= { {"Com_show_errors", (char*) (com_stat+(uint) SQLCOM_SHOW_ERRORS),SHOW_LONG}, {"Com_show_fields", (char*) (com_stat+(uint) SQLCOM_SHOW_FIELDS),SHOW_LONG}, {"Com_show_grants", (char*) (com_stat+(uint) SQLCOM_SHOW_GRANTS),SHOW_LONG}, + {"Com_show_innodb_status", (char*) (com_stat+(uint) SQLCOM_SHOW_INNODB_STATUS),SHOW_LONG}, {"Com_show_keys", (char*) (com_stat+(uint) SQLCOM_SHOW_KEYS),SHOW_LONG}, {"Com_show_logs", (char*) (com_stat+(uint) SQLCOM_SHOW_LOGS),SHOW_LONG}, {"Com_show_master_status", (char*) (com_stat+(uint) SQLCOM_SHOW_MASTER_STAT),SHOW_LONG}, @@ -4519,7 +4555,6 @@ struct show_var_st status_vars[]= { {"Com_show_slave_hosts", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_HOSTS),SHOW_LONG}, {"Com_show_slave_status", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_STAT),SHOW_LONG}, {"Com_show_status", (char*) (com_stat+(uint) SQLCOM_SHOW_STATUS),SHOW_LONG}, - {"Com_show_innodb_status", (char*) (com_stat+(uint) SQLCOM_SHOW_INNODB_STATUS),SHOW_LONG}, {"Com_show_tables", (char*) (com_stat+(uint) SQLCOM_SHOW_TABLES),SHOW_LONG}, {"Com_show_table_types", (char*) (com_stat+(uint) SQLCOM_SHOW_TABLE_TYPES),SHOW_LONG}, {"Com_show_variables", (char*) (com_stat+(uint) SQLCOM_SHOW_VARIABLES),SHOW_LONG}, @@ -4708,7 +4743,8 @@ static void mysql_init_variables(void) opt_log= opt_update_log= opt_bin_log= opt_slow_log= 0; opt_disable_networking= opt_skip_show_db=0; opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname=0; - opt_bootstrap= opt_myisam_log= use_old_passwords= 0; + opt_secure_auth= 0; + opt_bootstrap= opt_myisam_log= 0; mqh_used= 0; segfaulted= kill_in_progress= 0; cleanup_done= 0; @@ -4787,8 +4823,9 @@ static void mysql_init_variables(void) master_user= (char*) "test"; master_password= master_host= 0; master_info_file= (char*) "master.info", - relay_log_info_file= (char*) "relay-log.info", - master_ssl_key= master_ssl_cert= master_ssl_capath= master_ssl_cipher= 0; + relay_log_info_file= (char*) "relay-log.info"; + master_ssl_key= master_ssl_cert= master_ssl_ca= + master_ssl_capath= master_ssl_cipher= 0; report_user= report_password = report_host= 0; /* TO BE DELETED */ opt_relay_logname= opt_relaylog_index_name= 0; @@ -4800,17 +4837,18 @@ static void mysql_init_variables(void) /* Set default values for some option variables */ - global_system_variables.character_set_server= default_charset_info; - global_system_variables.character_set_database= default_charset_info; + global_system_variables.collation_server= default_charset_info; + global_system_variables.collation_database= default_charset_info; + global_system_variables.collation_connection= default_charset_info; global_system_variables.character_set_results= default_charset_info; global_system_variables.character_set_client= default_charset_info; - global_system_variables.collation_connection= default_charset_info; global_system_variables.table_type= DB_TYPE_MYISAM; global_system_variables.tx_isolation= ISO_REPEATABLE_READ; global_system_variables.select_limit= (ulonglong) HA_POS_ERROR; max_system_variables.select_limit= (ulonglong) HA_POS_ERROR; global_system_variables.max_join_size= (ulonglong) HA_POS_ERROR; max_system_variables.max_join_size= (ulonglong) HA_POS_ERROR; + global_system_variables.old_passwords= 0; /* Variables that depends on compile options */ #ifndef DBUG_OFF @@ -4932,9 +4970,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'L': strmake(language, argument, sizeof(language)-1); break; - case 'o': - protocol_version=PROTOCOL_VERSION-1; - break; #ifdef HAVE_REPLICATION case OPT_SLAVE_SKIP_ERRORS: init_slave_skip_errors(argument); @@ -5157,11 +5192,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), my_use_symdir=0; break; case (int) OPT_BIND_ADDRESS: - if (argument && my_isdigit(mysqld_charset, argument[0])) - { - my_bind_addr = (ulong) inet_addr(argument); - } - else + if (!argument || (my_bind_addr= (ulong) inet_addr(argument)) == INADDR_NONE) { struct hostent *ent; if (argument || argument[0]) @@ -5563,6 +5594,19 @@ static void fix_paths(void) } +/* + set how many open files we want to be able to handle + + SYNOPSIS + set_maximum_open_files() + max_file_limit Files to open + + NOTES + The request may not fulfilled becasue of system limitations + + RETURN + Files available to open +*/ #ifdef SET_RLIMIT_NOFILE static uint set_maximum_open_files(uint max_file_limit) diff --git a/sql/net_serv.cc b/sql/net_serv.cc index f12705572d6..7944a528b83 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -88,10 +88,15 @@ void sql_print_error(const char *format,...); #ifdef MYSQL_SERVER #define USE_QUERY_CACHE +/* + The following variables/functions should really not be declared + extern, but as it's hard to include mysql_priv.h here, we have to + live with this for a while. +*/ extern uint test_flags; -extern void query_cache_insert(NET *net, const char *packet, ulong length); extern ulong bytes_sent, bytes_received, net_big_packet_count; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; +extern void query_cache_insert(NET *net, const char *packet, ulong length); #else #undef statistic_add #undef statistic_increment @@ -240,10 +245,12 @@ my_bool net_flush(NET *net) *****************************************************************************/ /* -** Write a logical packet with packet header -** Format: Packet length (3 bytes), packet number(1 byte) -** When compression is used a 3 byte compression length is added -** NOTE: If compression is used the original package is modified! + Write a logical packet with packet header + Format: Packet length (3 bytes), packet number(1 byte) + When compression is used a 3 byte compression length is added + + NOTE + If compression is used the original package is modified! */ my_bool @@ -364,8 +371,8 @@ net_write_command(NET *net,uchar command, The cached buffer can be sent as it is with 'net_flush()'. In this code we have to be careful to not send a packet longer than - MAX_PACKET_LENGTH to net_real_write() if we are using the compressed protocol - as we store the length of the compressed packet in 3 bytes. + MAX_PACKET_LENGTH to net_real_write() if we are using the compressed + protocol as we store the length of the compressed packet in 3 bytes. RETURN 0 ok @@ -458,7 +465,7 @@ net_real_write(NET *net,const char *packet,ulong len) #ifdef MYSQL_SERVER net->last_errno= ER_OUT_OF_RESOURCES; net->error= 2; - //TODO is it needed to set this variable if we have no socket + /* TODO is it needed to set this variable if we have no socket */ net->report_error= 1; #endif net->reading_or_writing= 0; @@ -483,6 +490,7 @@ net_real_write(NET *net,const char *packet,ulong len) thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff); #else alarmed=0; + vio_timeout(net->vio, net->write_timeout); #endif /* NO_ALARM */ pos=(char*) packet; end=pos+len; @@ -674,6 +682,8 @@ my_real_read(NET *net, ulong *complen) #ifndef NO_ALARM if (net_blocking) thr_alarm(&alarmed,net->read_timeout,&alarm_buff); +#else + vio_timeout(net->vio, net->read_timeout); #endif /* NO_ALARM */ pos = net->buff + net->where_b; /* net->packet -4 */ @@ -877,20 +887,23 @@ my_net_read(NET *net) { /* We are using the compressed protocol */ - ulong buf_length= net->buf_length; - ulong start_of_packet= net->buf_length - net->remain_in_buf; - ulong first_packet_offset=start_of_packet; + ulong buf_length; + ulong start_of_packet; + ulong first_packet_offset; uint read_length, multi_byte_packet=0; if (net->remain_in_buf) { + buf_length= net->buf_length; /* Data left in old packet */ + first_packet_offset= start_of_packet= (net->buf_length - + net->remain_in_buf); /* Restore the character that was overwritten by the end 0 */ - net->buff[start_of_packet]=net->save_char; + net->buff[start_of_packet]= net->save_char; } else { /* reuse buffer, as there is nothing in it that we need */ - buf_length=start_of_packet=first_packet_offset=0; + buf_length= start_of_packet= first_packet_offset= 0; } for (;;) { diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 23fb36c0c91..5b1e2c98001 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -341,8 +341,8 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables, DBUG_RETURN(0); if (!(select= new SQL_SELECT)) { - *error= 1; - DBUG_RETURN(0); /* purecov: inspected */ + *error= 1; // out of memory + DBUG_RETURN(0); /* purecov: inspected */ } select->read_tables=read_tables; select->const_tables=const_tables; @@ -456,15 +456,17 @@ SEL_ARG *SEL_ARG::clone(SEL_ARG *new_parent,SEL_ARG **next_arg) SEL_ARG *tmp; if (type != KEY_RANGE) { - tmp=new SEL_ARG(type); + if(!(tmp=new SEL_ARG(type))) + return 0; // out of memory tmp->prev= *next_arg; // Link into next/prev chain (*next_arg)->next=tmp; (*next_arg)= tmp; } else { - tmp=new SEL_ARG(field,part, min_value,max_value, - min_flag, max_flag, maybe_flag); + if(!(tmp=new SEL_ARG(field,part, min_value,max_value, + min_flag, max_flag, maybe_flag))) + return 0; // out of memory tmp->parent=new_parent; tmp->next_key_part=next_key_part; if (left != &null_element) @@ -616,6 +618,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, SEL_TREE *tree; KEY_PART *key_parts; PARAM param; + THD *thd= current_thd; /* set up parameter that is passed to all functions */ param.baseflag=basflag; @@ -626,13 +629,13 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, param.keys=0; param.mem_root= &alloc; - current_thd->no_errors=1; // Don't warn about NULL + thd->no_errors=1; // Don't warn about NULL init_sql_alloc(&alloc,2048,0); if (!(param.key_parts = (KEY_PART*) alloc_root(&alloc, sizeof(KEY_PART)* head->key_parts))) { - current_thd->no_errors=0; + thd->no_errors=0; free_root(&alloc,MYF(0)); // Return memory & allocator DBUG_RETURN(0); // Can't use range } @@ -689,7 +692,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, uint keynr= param.real_keynr[idx]; if ((*key)->type == SEL_ARG::MAYBE_KEY || (*key)->maybe_flag) - needed_reg|= (key_map) 1 << keynr; + needed_reg|= (key_map) 1 << keynr; found_records=check_quick_select(¶m, idx, *key); if (found_records != HA_POS_ERROR && found_records > 2 && @@ -713,7 +716,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, param.range_count, found_records)+ (double) found_records / TIME_FOR_COMPARE); - if (read_time > found_read_time) + if (read_time > found_read_time && found_records != HA_POS_ERROR) { read_time=found_read_time; records=found_records; @@ -734,7 +737,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, } free_root(&alloc,MYF(0)); // Return memory & allocator my_pthread_setspecific_ptr(THR_MALLOC,old_root); - current_thd->no_errors=0; + thd->no_errors=0; } DBUG_EXECUTE("info",print_quick(quick,needed_reg);); /* @@ -762,6 +765,8 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) while ((item=li++)) { SEL_TREE *new_tree=get_mm_tree(param,item); + if (current_thd->is_fatal_error) + DBUG_RETURN(0); // out of memory tree=tree_and(param,tree,new_tree); if (tree && tree->type == SEL_TREE::IMPOSSIBLE) break; @@ -777,7 +782,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) { SEL_TREE *new_tree=get_mm_tree(param,item); if (!new_tree) - DBUG_RETURN(0); + DBUG_RETURN(0); // out of memory tree=tree_or(param,tree,new_tree); if (!tree || tree->type == SEL_TREE::ALWAYS) break; @@ -793,18 +798,16 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) DBUG_RETURN(new SEL_TREE(SEL_TREE::ALWAYS)); DBUG_RETURN(new SEL_TREE(SEL_TREE::IMPOSSIBLE)); } + table_map ref_tables=cond->used_tables(); - if (ref_tables & ~(param->prev_tables | param->read_tables | - param->current_table)) - DBUG_RETURN(0); // Can't be calculated yet if (cond->type() != Item::FUNC_ITEM) { // Should be a field - if (ref_tables & param->current_table) + if ((ref_tables & param->current_table) || + (ref_tables & ~(param->prev_tables | param->read_tables))) DBUG_RETURN(0); DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE)); } - if (!(ref_tables & param->current_table)) - DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE)); // This may be false or true + Item_func *cond_func= (Item_func*) cond; if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE) DBUG_RETURN(0); // Can't be calculated @@ -815,12 +818,13 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) { Field *field=((Item_field*) (cond_func->arguments()[0]))->field; Item_result cmp_type=field->cmp_type(); - tree= get_mm_parts(param,field,Item_func::GE_FUNC, - cond_func->arguments()[1],cmp_type); - DBUG_RETURN(tree_and(param,tree, + DBUG_RETURN(tree_and(param, + get_mm_parts(param, field, + Item_func::GE_FUNC, + cond_func->arguments()[1], cmp_type), get_mm_parts(param, field, Item_func::LE_FUNC, - cond_func->arguments()[2],cmp_type))); + cond_func->arguments()[2], cmp_type))); } DBUG_RETURN(0); } @@ -846,6 +850,12 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) DBUG_RETURN(0); // Can't optimize this IN } + if (ref_tables & ~(param->prev_tables | param->read_tables | + param->current_table)) + DBUG_RETURN(0); // Can't be calculated yet + if (!(ref_tables & param->current_table)) + DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE)); // This may be false or true + /* check field op const */ /* btw, ft_func's arguments()[0] isn't FIELD_ITEM. SerG*/ if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) @@ -877,14 +887,15 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) static SEL_TREE * -get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, - Item_result cmp_type) +get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, + Item *value, Item_result cmp_type) { DBUG_ENTER("get_mm_parts"); if (field->table != param->table) DBUG_RETURN(0); - KEY_PART *key_part = param->key_parts,*end=param->key_parts_end; + KEY_PART *key_part = param->key_parts; + KEY_PART *end = param->key_parts_end; SEL_TREE *tree=0; if (value && value->used_tables() & ~(param->prev_tables | param->read_tables)) @@ -894,8 +905,8 @@ get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, if (field->eq(key_part->field)) { SEL_ARG *sel_arg=0; - if (!tree) - tree=new SEL_TREE(); + if (!tree && !(tree=new SEL_TREE())) + DBUG_RETURN(0); // out of memory if (!value || !(value->used_tables() & ~param->read_tables)) { sel_arg=get_mm_leaf(param,key_part->field,key_part,type,value); @@ -907,8 +918,11 @@ get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, DBUG_RETURN(tree); } } - else - sel_arg=new SEL_ARG(SEL_ARG::MAYBE_KEY);// This key may be used later + else { + // This key may be used later + if(!(sel_arg=new SEL_ARG(SEL_ARG::MAYBE_KEY))) + DBUG_RETURN(0); // out of memory + } sel_arg->part=(uchar) key_part->part; tree->keys[key_part->key]=sel_add(tree->keys[key_part->key],sel_arg); } @@ -995,9 +1009,8 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, DBUG_RETURN(0); if (!maybe_null) // Not null field DBUG_RETURN(type == Item_func::ISNULL_FUNC ? &null_element : 0); - tree=new SEL_ARG(field,is_null_string,is_null_string); - if (!tree) - DBUG_RETURN(0); + if (!(tree=new SEL_ARG(field,is_null_string,is_null_string))) + DBUG_RETURN(0); // out of memory if (type == Item_func::ISNOTNULL_FUNC) { tree->min_flag=NEAR_MIN; /* IS NOT NULL -> X > NULL */ @@ -1035,7 +1048,7 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, field->get_key_image(str+maybe_null,key_part->part_length, field->charset(),key_part->image_type); if (!(tree=new SEL_ARG(field,str,str))) - DBUG_RETURN(0); + DBUG_RETURN(0); // out of memory switch (type) { case Item_func::LT_FUNC: @@ -1475,7 +1488,8 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) SEL_ARG *key2_next=key2->next; if (key2_shared) { - key2=new SEL_ARG(*key2); + if(!(key2=new SEL_ARG(*key2))) + return 0; // out of memory key2->increment_use_count(key1->use_count+1); key2->next=key2_next; // New copy of key2 } @@ -2319,16 +2333,16 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key, } /* Get range for retrieving rows in QUICK_SELECT::get_next */ - range= new QUICK_RANGE(param->min_key, - (uint) (tmp_min_key - param->min_key), - param->max_key, - (uint) (tmp_max_key - param->max_key), - flag); + if(!(range= new QUICK_RANGE(param->min_key, + (uint) (tmp_min_key - param->min_key), + param->max_key, + (uint) (tmp_max_key - param->max_key), + flag))) + return 1; // out of memory + set_if_bigger(quick->max_used_key_length,range->min_length); set_if_bigger(quick->max_used_key_length,range->max_length); set_if_bigger(quick->used_key_parts, (uint) key_tree->part+1); - if (!range) // Not enough memory - return 1; quick->ranges.push_back(range); end: @@ -2389,17 +2403,17 @@ QUICK_SELECT *get_quick_select_for_ref(TABLE *table, TABLE_REF *ref) uint part; if (!quick) - return 0; + return 0; /* no ranges found */ if (cp_buffer_from_ref(ref)) { if (current_thd->is_fatal_error) - return 0; // End of memory + return 0; // out of memory return quick; // empty range } QUICK_RANGE *range= new QUICK_RANGE(); if (!range) - goto err; + goto err; // out of memory range->min_key=range->max_key=(char*) ref->key_buff; range->min_length=range->max_length=ref->key_length; diff --git a/sql/password.c b/sql/password.c index 257547671e5..9f4910d8c60 100644 --- a/sql/password.c +++ b/sql/password.c @@ -29,28 +29,33 @@ The password is saved (in user.password) by using the PASSWORD() function in mysql. + This is .c file because it's used in libmysqlclient, which is entirely in C. + (we need it to be portable to a variety of systems). Example: update user set password=PASSWORD("hello") where user="test" This saves a hashed number as a string in the password field. + The new autentication is performed in following manner: - New in MySQL 4.1 authentication works even more secure way. - At the first step client sends user name to the sever, and password if - it is empty. So in case of empty password authentication is as fast as before. - At the second stap servers sends scramble to client, which is encoded with - password stage2 hash stored in the password database as well as salt, needed - for client to build stage2 password to decrypt scramble. - Client decrypts the scramble and encrypts it once again with stage1 password. - This information is sent to server. - Server decrypts the scramble to get stage1 password and hashes it to get - stage2 hash. This hash is when compared to hash stored in the database. + SERVER: public_seed=create_random_string() + send(public_seed) - This authentication needs 2 packet round trips instead of one but it is much - stronger. Now if one will steal mysql database content he will not be able - to break into MySQL. + CLIENT: recv(public_seed) + hash_stage1=sha1("password") + hash_stage2=sha1(hash_stage1) + reply=xor(hash_stage1, sha1(public_seed,hash_stage2) - New Password handling functions by Peter Zaitsev + // this three steps are done in scramble() + send(reply) + + + SERVER: recv(reply) + hash_stage1=xor(reply, sha1(public_seed,hash_stage2)) + candidate_hash2=sha1(hash_stage1) + check(candidate_hash2==hash_stage2) + + // this three steps are done in check_scramble() *****************************************************************************/ @@ -60,31 +65,21 @@ #include <sha1.h> #include "mysql.h" - - -/* Character to use as version identifier for version 4.1 */ -#define PVERSION41_CHAR '*' - -/* Scramble length for new password version */ - +/************ MySQL 3.23-4.0 authentification routines: untouched ***********/ /* New (MySQL 3.21+) random generation structure initialization - SYNOPSIS randominit() rand_st OUT Structure to initialize seed1 IN First initialization parameter seed2 IN Second initialization parameter - - RETURN - none */ -void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) -{ /* For mysql 3.21.# */ +void randominit(struct rand_struct *rand_st, ulong seed1, ulong seed2) +{ /* For mysql 3.21.# */ #ifdef HAVE_purify - bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */ + bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */ #endif rand_st->max_value= 0x3FFFFFFFL; rand_st->max_value_dbl=(double) rand_st->max_value; @@ -94,35 +89,12 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) /* - Old (MySQL 3.20) random generation structure initialization - - SYNOPSIS - old_randominit() - rand_st OUT Structure to initialize - seed1 IN First initialization parameter - - RETURN - none -*/ - -static void old_randominit(struct rand_struct *rand_st,ulong seed1) -{ /* For mysql 3.20.# */ - rand_st->max_value= 0x01FFFFFFL; - rand_st->max_value_dbl=(double) rand_st->max_value; - seed1%=rand_st->max_value; - rand_st->seed1=seed1 ; rand_st->seed2=seed1/2; -} - - -/* - Generate Random number - + Generate random number. SYNOPSIS my_rnd() rand_st INOUT Structure used for number generation - - RETURN - Generated pseudo random number + RETURN VALUE + generated pseudo random number */ double my_rnd(struct rand_struct *rand_st) @@ -134,73 +106,24 @@ double my_rnd(struct rand_struct *rand_st) /* - Generate String of printable random characters of requested length - String will not be zero terminated. - + Generate binary hash from raw text string + Used for Pre-4.1 password handling SYNOPSIS - create_random_string() - length IN Lenght of - rand_st INOUT Structure used for number generation - target OUT Buffer for generation - - RETURN - none + hash_password() + result OUT store hash in this location + password IN plain text password to build hash + password_len IN password length (password may be not null-terminated) */ -void create_random_string(int length,struct rand_struct *rand_st,char *target) -{ - char *end=target+length; - /* Use pointer arithmetics as it is faster way to do so. */ - for (; target<end ; target++) - *target= (char) (my_rnd(rand_st)*94+33); -} - - -/* - Encrypt/Decrypt function used for password encryption in authentication - Simple XOR is used here but it is OK as we crypt random strings - - SYNOPSIS - password_crypt() - from IN Data for encryption - to OUT Encrypt data to the buffer (may be the same) - password IN Password used for encryption (same length) - length IN Length of data to encrypt - - RETURN - none -*/ - -void password_crypt(const char *from,char *to, const char *password,int length) -{ - const char *from_end=from+length; - - while (from < from_end) - *to++= *(from++) ^* (password++); -} - - -/* - Generate binary hash from raw text password - Used for Pre-4.1 Password handling - - SYNOPSIS - hash_pasword() - result OUT Store hash in this location - password IN Plain text password to build hash - - RETURN - none -*/ - -void hash_password(ulong *result, const char *password) +void hash_password(ulong *result, const char *password, uint password_len) { register ulong nr=1345345333L, add=7, nr2=0x12345671L; ulong tmp; - for (; *password ; password++) + const char *password_end= password + password_len; + for (; password < password_end; password++) { if (*password == ' ' || *password == '\t') - continue; /* skipp space in password */ + continue; /* skip space in password */ tmp= (ulong) (uchar) *password; nr^= (((nr & 63)+add)*tmp)+ (nr << 8); nr2+=(nr2 << 8) ^ nr; @@ -208,519 +131,388 @@ void hash_password(ulong *result, const char *password) } result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */; result[1]=nr2 & (((ulong) 1L << 31) -1L); - return; } /* - Stage one password hashing. - Used in MySQL 4.1 password handling - + Create password to be stored in user database from raw string + Used for pre-4.1 password handling SYNOPSIS - password_hash_stage1() - to OUT Store stage one hash to this location - password IN Plain text password to build hash - - RETURN - none + make_scrambled_password_323() + to OUT store scrambled password here + password IN user-supplied password */ -void password_hash_stage1(char *to, const char *password) +void make_scrambled_password_323(char *to, const char *password) { - SHA1_CONTEXT context; - sha1_reset(&context); - for (; *password ; password++) - { - if (*password == ' ' || *password == '\t') - continue;/* skip space in password */ - sha1_input(&context,(uint8*) &password[0],1); - } - sha1_result(&context,(uint8*)to); + ulong hash_res[2]; + hash_password(hash_res, password, strlen(password)); + sprintf(to, "%08lx%08lx", hash_res[0], hash_res[1]); } /* - Stage two password hashing. - Used in MySQL 4.1 password handling - + Scramble string with password. + Used in pre 4.1 authentication phase. SYNOPSIS - password_hash_stage2() - to INOUT Use this as stage one hash and store stage two hash here - salt IN Salt used for stage two hashing - - RETURN - none + scramble_323() + to OUT Store scrambled message here. Buffer must be at least + SCRAMBLE_LENGTH_323+1 bytes long + message IN Message to scramble. Message must be at least + SRAMBLE_LENGTH_323 bytes long. + password IN Password to use while scrambling */ -void password_hash_stage2(char *to, const char *salt) +void scramble_323(char *to, const char *message, const char *password) { - SHA1_CONTEXT context; - sha1_reset(&context); - sha1_input(&context,(uint8*) salt, 4); - sha1_input(&context,(uint8*) to, SHA1_HASH_SIZE); - sha1_result(&context,(uint8*) to); + struct rand_struct rand_st; + ulong hash_pass[2], hash_message[2]; + + if (password && password[0]) + { + char extra, *to_start=to; + const char *message_end= message + SCRAMBLE_LENGTH_323; + hash_password(hash_pass,password, strlen(password)); + hash_password(hash_message, message, SCRAMBLE_LENGTH_323); + randominit(&rand_st,hash_pass[0] ^ hash_message[0], + hash_pass[1] ^ hash_message[1]); + for (; message < message_end; message++) + *to++= (char) (floor(my_rnd(&rand_st)*31)+64); + extra=(char) (floor(my_rnd(&rand_st)*31)); + while (to_start != to) + *(to_start++)^=extra; + } + *to= 0; } /* - Create password to be stored in user database from raw string - Handles both MySQL 4.1 and Pre-MySQL 4.1 passwords - + Check scrambled message + Used in pre 4.1 password handling SYNOPSIS - make_scramble_password() - to OUT Store scrambled password here - password IN Raw string password - force_old_scramle - IN Force generation of old scramble variant - rand_st INOUT Structure for temporary number generation. - RETURN - none + check_scramble_323() + scrambled scrambled message to check. + message original random message which was used for scrambling; must + be exactly SCRAMBLED_LENGTH_323 bytes long and + NULL-terminated. + hash_pass password which should be used for scrambling + All params are IN. + + RETURN VALUE + 0 - password correct + !0 - password invalid */ -void make_scrambled_password(char *to,const char *password, - my_bool force_old_scramble, - struct rand_struct *rand_st) +my_bool +check_scramble_323(const char *scrambled, const char *message, + ulong *hash_pass) { - ulong hash_res[2]; /* Used for pre 4.1 password hashing */ - unsigned short salt; /* Salt for 4.1 version password */ - uint8 digest[SHA1_HASH_SIZE]; - if (force_old_scramble) /* Pre 4.1 password encryption */ - { - hash_password(hash_res,password); - sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); - } - else /* New password 4.1 password scrambling */ + struct rand_struct rand_st; + ulong hash_message[2]; + char buff[16],*to,extra; /* Big enough for check */ + const char *pos; + + hash_password(hash_message, message, SCRAMBLE_LENGTH_323); + randominit(&rand_st,hash_pass[0] ^ hash_message[0], + hash_pass[1] ^ hash_message[1]); + to=buff; + for (pos=scrambled ; *pos ; pos++) + *to++=(char) (floor(my_rnd(&rand_st)*31)+64); + extra=(char) (floor(my_rnd(&rand_st)*31)); + to=buff; + while (*scrambled) { - to[0]=PVERSION41_CHAR; /* New passwords have version prefix */ - /* Rnd returns number from 0 to 1 so this would be good salt generation.*/ - salt=(unsigned short) (my_rnd(rand_st)*65535+1); - /* Use only 2 first bytes from it */ - sprintf(to+1,"%04x",salt); - /* First hasing is done without salt */ - password_hash_stage1((char*) digest, password); - /* Second stage is done with salt */ - password_hash_stage2((char*) digest,(char*)to+1), - /* Print resulting hash into the password*/ - sprintf(to+5, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - digest[0],digest[1],digest[2],digest[3],digest[4],digest[5],digest[6], - digest[7],digest[8],digest[9],digest[10],digest[11],digest[12],digest[13], - digest[14],digest[15],digest[16],digest[17],digest[18],digest[19]); + if (*scrambled++ != (char) (*to++ ^ extra)) + return 1; /* Wrong password */ } + return 0; } +static inline uint8 char_val(uint8 X) +{ + return (uint) (X >= '0' && X <= '9' ? X-'0' : + X >= 'A' && X <= 'Z' ? X-'A'+10 : X-'a'+10); +} -/* - Convert password from binary string form to salt form - Used for MySQL 4.1 password handling +/* + Convert password from hex string (as stored in mysql.user) to binary form. SYNOPSIS - get_salt_from_bin_password() - res OUT Store salt form password here - password IN Binary password to be converted - salt IN hashing-salt to be used for salt form generation - - RETURN - none + get_salt_from_password_323() + res OUT store salt here + password IN password string as stored in mysql.user + NOTE + This function does not have length check for passwords. It will just crash + Password hashes in old format must have length divisible by 8 */ -void get_salt_from_bin_password(ulong *res,unsigned char *password,ulong salt) +void get_salt_from_password_323(ulong *res, const char *password) { - unsigned char *password_end=password+SCRAMBLE41_LENGTH; - *res=salt; - res++; - - /* Process password of known length*/ - while (password<password_end) + res[0]= res[1]= 0; + if (password) { - ulong val=0; - uint i; - for (i=0 ; i < 4 ; i++) - val=(val << 8)+(*password++); - *res++=val; + while (*password) + { + ulong val=0; + uint i; + for (i=0 ; i < 8 ; i++) + val=(val << 4)+char_val(*password++); + *res++=val; + } } } /* - Validate password for MySQL 4.1 password handling. - + Convert scrambled password from binary form to asciiz hex string. SYNOPSIS - validate_password() - password IN Encrypted Scramble which we got from the client - message IN Original scramble which we have sent to the client before - salt IN Password in the salted form to match to - - RETURN - 0 for correct password - !0 for invalid password + make_password_from_salt_323() + to OUT store resulting string password here, at least 17 bytes + salt IN password in salt format, 2 ulongs */ -my_bool validate_password(const char *password, const char *message, - ulong *salt) +void make_password_from_salt_323(char *to, const ulong *salt) { - char buffer[SCRAMBLE41_LENGTH]; /* Used for password validation */ - char tmpsalt[8]; /* Temporary value to convert salt to string form */ - ulong salt_candidate[6]; /* Computed candidate salt */ - ulong *sc=salt_candidate; /* we need to be able to increment */ - ulong *salt_end; - - /* Now we shall get stage1 encrypted password in buffer*/ - password_crypt(password,buffer,message,SCRAMBLE41_LENGTH); - - /* For compatibility reasons we use ulong to store salt while we need char */ - sprintf(tmpsalt,"%04x",(unsigned short)salt[0]); - - password_hash_stage2(buffer,tmpsalt); - /* Convert password to salt to compare */ - get_salt_from_bin_password(salt_candidate,(uchar*) buffer,salt[0]); - - /* Now we shall get exactly the same password as we have stored for user */ - for (salt_end=salt+5 ; salt < salt_end; ) - if (*++salt != *++sc) - return 1; - - /* Or password correct*/ - return 0; + sprintf(to,"%08lx%08lx", salt[0], salt[1]); } /* - Get length of password string which is stored in mysql.user table + **************** MySQL 4.1.1 authentification routines ************* +*/ +/* + Generate string of printable random characters of requested length SYNOPSIS - get_password_length() - force_old_scramble IN If we wish to use pre 4.1 scramble format - - RETURN - password length >0 + create_random_string() + to OUT buffer for generation; must be at least length+1 bytes + long; result string is always null-terminated + length IN how many random characters to put in buffer + rand_st INOUT structure used for number generation */ -int get_password_length(my_bool force_old_scramble) +void create_random_string(char *to, uint length, struct rand_struct *rand_st) { - return (force_old_scramble) ? 16 : SHA1_HASH_SIZE*2+4+1; + char *end= to + length; + /* Use pointer arithmetics as it is faster way to do so. */ + for (; to < end; to++) + *to= (char) (my_rnd(rand_st)*94+33); + *to= '\0'; } -/* - Get version of the password based on mysql.user password string - - SYNOPSIS - get_password_version() - password IN Password string as stored in mysql.user - - RETURN - 0 for pre 4.1 passwords - !0 password version char for newer passwords -*/ +/* Character to use as version identifier for version 4.1 */ -char get_password_version(const char *password) -{ - if (password==NULL) return 0; - if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR; - return 0; -} +#define PVERSION41_CHAR '*' /* - Get integer value of Hex character - + Convert given octet sequence to asciiz string of hex characters; + str..str+len and 'to' may not overlap. SYNOPSIS - char_val() - X IN Character to find value for - - RETURN - Appropriate integer value + octet2hex() + buf OUT output buffer. Must be at least 2*len+1 bytes + str, len IN the beginning and the length of the input string */ - - -static inline unsigned int char_val(char X) +static void +octet2hex(char *to, const uint8 *str, uint len) { - return (uint) (X >= '0' && X <= '9' ? X-'0' : - X >= 'A' && X <= 'Z' ? X-'A'+10 : - X-'a'+10); + const uint8 *str_end= str + len; + for (; str != str_end; ++str) + { + *to++= _dig_vec[(*str & 0xF0) >> 4]; + *to++= _dig_vec[*str & 0x0F]; + } + *to= '\0'; } /* - Get Binary salt from password as in mysql.user format - + Convert given asciiz string of hex (0..9 a..f) characters to octet + sequence. SYNOPSIS - get_salt_from_password() - res OUT Store binary salt here - password IN Password string as stored in mysql.user - - RETURN - none - - NOTE - This function does not have length check for passwords. It will just crash - Password hashes in old format must have length divisible by 8 -*/ - -void get_salt_from_password(ulong *res,const char *password) + hex2octet() + to OUT buffer to place result; must be at least len/2 bytes + str, len IN begin, length for character string; str and to may not + overlap; len % 2 == 0 +*/ + +static void +hex2octet(uint8 *to, const char *str, uint len) { - if (password) /* zero salt corresponds to empty password */ + const char *str_end= str + len; + while (str < str_end) { - if (password[0]==PVERSION41_CHAR) /* if new password */ - { - uint val=0; - uint i; - password++; /* skip version identifier */ - - /*get hashing salt from password and store in in the start of array */ - for (i=0 ; i < 4 ; i++) - val=(val << 4)+char_val(*password++); - *res++=val; - } - /* We process old passwords the same way as new ones in other case */ -#ifdef EXTRA_DEBUG - if (strlen(password)%8!=0) - fprintf(stderr,"Warning: Incorrect password length for salting: %d\n", - strlen(password)); -#endif - while (*password) - { - ulong val=0; - uint i; - for (i=0 ; i < 8 ; i++) - val=(val << 4)+char_val(*password++); - *res++=val; - } + register char tmp= char_val(*str++); + *to++= (tmp << 4) | char_val(*str++); } - return; } /* - Get string version as stored in mysql.user from salt form - + Encrypt/Decrypt function used for password encryption in authentication. + Simple XOR is used here but it is OK as we crypt random strings. Note, + that XOR(s1, XOR(s1, s2)) == s2, XOR(s1, s2) == XOR(s2, s1) SYNOPSIS - make_password_from_salt() - to OUT Store resulting string password here - hash_res IN Password in salt format - password_version - IN According to which version salt should be treated - - RETURN - none + my_crypt() + to OUT buffer to hold crypted string; must be at least len bytes + long; to and s1 (or s2) may be the same. + s1, s2 IN input strings (of equal length) + len IN length of s1 and s2 */ -void make_password_from_salt(char *to, ulong *hash_res,uint8 password_version) +static void +my_crypt(char *to, const uchar *s1, const uchar *s2, uint len) { - if (!password_version) /* Handling of old passwords. */ - sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); - else - if (password_version==PVERSION41_CHAR) - sprintf(to,"%c%04x%08lx%08lx%08lx%08lx%08lx",PVERSION41_CHAR,(unsigned short)hash_res[0],hash_res[1], - hash_res[2],hash_res[3],hash_res[4],hash_res[5]); - else /* Just use empty password if we can't handle it. This should not happen */ - to[0]='\0'; + const uint8 *s1_end= s1 + len; + while (s1 < s1_end) + *to++= *s1++ ^ *s2++; } /* - Convert password in salted form to binary string password and hash-salt - For old password this involes one more hashing - + MySQL 4.1.1 password hashing: SHA conversion (see RFC 2289, 3174) twice + applied to the password string, and then produced octet sequence is + converted to hex string. + The result of this function is used as return value from PASSWORD() and + is stored in the database. SYNOPSIS - get_hash_and_password() - salt IN Salt to convert from - pversion IN Password version to use - hash OUT Store zero ended hash here - bin_password OUT Store binary password here (no zero at the end) - - RETURN - 0 for pre 4.1 passwords - !0 password version char for newer passwords + make_scrambled_password() + buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string + password IN NULL-terminated password string */ -void get_hash_and_password(ulong *salt, uint8 pversion, char *hash, - unsigned char *bin_password) +void +make_scrambled_password(char *to, const char *password) { - int t; - ulong* salt_end; - ulong val; - SHA1_CONTEXT context; - - if (pversion) /* New password version assumed */ - { - salt_end=salt+5; - sprintf(hash,"%04x",(unsigned short)salt[0]); - while (salt<salt_end) - { - val=*(++salt); - for (t=3; t>=0; t--) - { - bin_password[t]= (char) (val & 255); - val>>=8; /* Scroll 8 bits to get next part*/ - } - bin_password+=4; /* Get to next 4 chars*/ - } - } - else - { - unsigned char *bp= bin_password; /* Binary password loop pointer */ - - /* Use zero starting hash as an indication of old password */ - hash[0]=0; - salt_end=salt+2; - /* Encode salt using SHA1 here */ - sha1_reset(&context); - while (salt<salt_end) /* Iterate over these elements*/ - { - val= *salt; - for (t=3;t>=0;t--) - { - bp[t]= (uchar) (val & 255); - val>>=8; /* Scroll 8 bits to get next part*/ - } - bp+= 4; /* Get to next 4 chars*/ - salt++; - } - /* Use 8 bytes of binary password for hash */ - sha1_input(&context,(uint8*)bin_password,8); - sha1_result(&context,(uint8*)bin_password); - } + SHA1_CONTEXT sha1_context; + uint8 hash_stage2[SHA1_HASH_SIZE]; + + sha1_reset(&sha1_context); + /* stage 1: hash password */ + sha1_input(&sha1_context, (uint8 *) password, strlen(password)); + sha1_result(&sha1_context, (uint8 *) to); + /* stage 2: hash stage1 output */ + sha1_reset(&sha1_context); + sha1_input(&sha1_context, (uint8 *) to, SHA1_HASH_SIZE); + /* separate buffer is used to pass 'to' in octet2hex */ + sha1_result(&sha1_context, hash_stage2); + /* convert hash_stage2 to hex string */ + *to++= PVERSION41_CHAR; + octet2hex(to, hash_stage2, SHA1_HASH_SIZE); } - + /* - Create key from old password to decode scramble - Used in 4.1 authentication with passwords stored old way - + Produce an obscure octet sequence from password and random + string, recieved from the server. This sequence corresponds to the + password, but password can not be easily restored from it. The sequence + is then sent to the server for validation. Trailing zero is not stored + in the buf as it is not needed. + This function is used by client to create authenticated reply to the + server's greeting. SYNOPSIS - create_key_from_old_password() - passwd IN Password used for key generation - key OUT Created 20 bytes key - - RETURN - None + scramble() + buf OUT store scrambled string here. The buf must be at least + SHA1_HASH_SIZE bytes long. + message IN random message, must be exactly SCRAMBLE_LENGTH long and + NULL-terminated. + password IN users' password */ - -void create_key_from_old_password(const char *passwd, char *key) +void +scramble(char *to, const char *message, const char *password) { - char buffer[SCRAMBLE41_LENGTH]; /* Buffer for various needs */ - ulong salt[6]; /* Salt (large for safety) */ - /* At first hash password to the string stored in password */ - make_scrambled_password(buffer,passwd,1,(struct rand_struct *)NULL); - /* Now convert it to the salt form */ - get_salt_from_password(salt,buffer); - /* Finally get hash and bin password from salt */ - get_hash_and_password(salt,0,buffer,(unsigned char*) key); + SHA1_CONTEXT sha1_context; + uint8 hash_stage1[SHA1_HASH_SIZE]; + uint8 hash_stage2[SHA1_HASH_SIZE]; + + sha1_reset(&sha1_context); + /* stage 1: hash password */ + sha1_input(&sha1_context, (uint8 *) password, strlen(password)); + sha1_result(&sha1_context, hash_stage1); + /* stage 2: hash stage 1; note that hash_stage2 is stored in the database */ + sha1_reset(&sha1_context); + sha1_input(&sha1_context, hash_stage1, SHA1_HASH_SIZE); + sha1_result(&sha1_context, hash_stage2); + /* create crypt string as sha1(message, hash_stage2) */; + sha1_reset(&sha1_context); + sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH); + sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE); + /* xor allows 'from' and 'to' overlap: lets take advantage of it */ + sha1_result(&sha1_context, (uint8 *) to); + my_crypt(to, (const uchar *) to, hash_stage1, SCRAMBLE_LENGTH); } /* - Scramble string with password - Used at pre 4.1 authentication phase. - + Check that scrambled message corresponds to the password; the function + is used by server to check that recieved reply is authentic. + This function does not check lengths of given strings: message must be + null-terminated, reply and hash_stage2 must be at least SHA1_HASH_SIZE + long (if not, something fishy is going on). SYNOPSIS - scramble() - to OUT Store scrambled message here - message IN Message to scramble - password IN Password to use while scrambling - old_ver IN Forse old version random number generator - - RETURN - End of scrambled string + check_scramble() + scramble clients' reply, presumably produced by scramble() + message original random string, previously sent to client + (presumably second argument of scramble()), must be + exactly SCRAMBLE_LENGTH long and NULL-terminated. + hash_stage2 hex2octet-decoded database entry + All params are IN. + + RETURN VALUE + 0 password is correct + !0 password is invalid */ -char *scramble(char *to,const char *message,const char *password, - my_bool old_ver) +my_bool +check_scramble(const char *scramble, const char *message, + const uint8 *hash_stage2) { - struct rand_struct rand_st; - ulong hash_pass[2],hash_message[2]; - char message_buffer[9]; /* Real message buffer */ - char *msg=message_buffer; - - /* We use special message buffer now as new server can provide longer hash */ - - memcpy(message_buffer,message,8); - message_buffer[8]=0; - - if (password && password[0]) - { - char *to_start=to; - hash_password(hash_pass,password); - hash_password(hash_message,message_buffer); - if (old_ver) - old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); - else - randominit(&rand_st,hash_pass[0] ^ hash_message[0], - hash_pass[1] ^ hash_message[1]); - while (*msg++) - *to++= (char) (floor(my_rnd(&rand_st)*31)+64); - if (!old_ver) - { /* Make it harder to break */ - char extra=(char) (floor(my_rnd(&rand_st)*31)); - while (to_start != to) - *(to_start++)^=extra; - } - } - *to=0; - return to; + SHA1_CONTEXT sha1_context; + uint8 buf[SHA1_HASH_SIZE]; + uint8 hash_stage2_reassured[SHA1_HASH_SIZE]; + + sha1_reset(&sha1_context); + /* create key to encrypt scramble */ + sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH); + sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE); + sha1_result(&sha1_context, buf); + /* encrypt scramble */ + my_crypt((char *) buf, buf, (const uchar *) scramble, SCRAMBLE_LENGTH); + /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */ + sha1_reset(&sha1_context); + sha1_input(&sha1_context, buf, SHA1_HASH_SIZE); + sha1_result(&sha1_context, hash_stage2_reassured); + return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE); } /* - Check scrambled message - Used for pre 4.1 password handling + Convert scrambled password from asciiz hex string to binary form. + SYNOPSIS + get_salt_from_password() + res OUT buf to hold password. Must be at least SHA1_HASH_SIZE + bytes long. + password IN 4.1.1 version value of user.password +*/ + +void get_salt_from_password(uint8 *hash_stage2, const char *password) +{ + hex2octet(hash_stage2, password+1 /* skip '*' */, SHA1_HASH_SIZE * 2); +} +/* + Convert scrambled password from binary form to asciiz hex string. SYNOPSIS - scramble() - scrambled IN Scrambled message to check - message IN Original message which was scramble - hash_pass IN Password which should be used for scrambling - old_ver IN Forse old version random number generator - - RETURN - 0 Password correct - !0 Password invalid + make_password_from_salt() + to OUT store resulting string here, 2*SHA1_HASH_SIZE+2 bytes + salt IN password in salt format */ -my_bool check_scramble(const char *scrambled, const char *message, - ulong *hash_pass, my_bool old_ver) +void make_password_from_salt(char *to, const uint8 *hash_stage2) { - struct rand_struct rand_st; - ulong hash_message[2]; - char buff[16],*to,extra; /* Big enough for check */ - const char *pos; - char message_buffer[SCRAMBLE_LENGTH+1]; /* Copy of message */ - - /* We need to copy the message as this function can be called for MySQL 4.1 - scramble which is not zero ended and can have zeroes inside - We could just write zero to proper place in original message but - this would make it harder to understand code for next generations - */ - - memcpy(message_buffer,message,SCRAMBLE_LENGTH); /* Ignore the rest */ - message_buffer[SCRAMBLE_LENGTH]=0; - - /* Check if this exactly N bytes. Overwise this is something fishy */ - if (strlen(message_buffer)!=SCRAMBLE_LENGTH) - return 1; /* Wrong password */ - - hash_password(hash_message,message_buffer); - if (old_ver) - old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); - else - randominit(&rand_st,hash_pass[0] ^ hash_message[0], - hash_pass[1] ^ hash_message[1]); - to=buff; - for (pos=scrambled ; *pos ; pos++) - *to++=(char) (floor(my_rnd(&rand_st)*31)+64); - if (old_ver) - extra=0; - else - extra=(char) (floor(my_rnd(&rand_st)*31)); - to=buff; - while (*scrambled) - { - if (*scrambled++ != (char) (*to++ ^ extra)) - return 1; /* Wrong password */ - } - return 0; + *to++= PVERSION41_CHAR; + octet2hex(to, hash_stage2, SHA1_HASH_SIZE); } diff --git a/sql/protocol.cc b/sql/protocol.cc index e90aa7585e2..79420fb71d5 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -28,6 +28,9 @@ #ifndef EMBEDDED_LIBRARY bool Protocol::net_store_data(const char *from, uint length) +#else +bool Protocol_prep::net_store_data(const char *from, uint length) +#endif { ulong packet_length=packet->length(); /* @@ -43,7 +46,6 @@ bool Protocol::net_store_data(const char *from, uint length) packet->length((uint) (to+length-packet->ptr())); return 0; } -#endif /* Send a error string to client */ @@ -349,10 +351,35 @@ send_eof(THD *thd, bool no_flush) } DBUG_VOID_RETURN; } + +/* + Please client to send scrambled_password in old format. + SYNOPSYS + send_old_password_request() + thd thread handle + + RETURN VALUE + 0 ok + !0 error +*/ + +bool send_old_password_request(THD *thd) +{ + static char buff[1]= { (char) 254 }; + NET *net= &thd->net; + return my_net_write(net, buff, 1) || net_flush(net); +} + #endif /* EMBEDDED_LIBRARY */ /* - Faster net_store_length when we know length is a 32 bit integer + Faster net_store_length when we know that length is less than 65536. + We keep a separate version for that range because it's widely used in + libmysql. + uint is used as agrument type because of MySQL type conventions: + uint for 0..65536 + ulong for 0..4294967296 + ulonglong for bigger numbers. */ char *net_store_length(char *pkg, uint length) @@ -1105,3 +1132,12 @@ bool Protocol_prep::store_time(TIME *tm) buff[0]=(char) length; // Length is stored first return packet->append(buff, length+1, PACKET_BUFFET_EXTRA_ALLOC); } + +#ifdef EMBEDDED_LIBRARY +/* Should be removed when we define the Protocol_cursor's future */ +bool Protocol_cursor::write() +{ + return Protocol_simple::write(); +} +#endif + diff --git a/sql/protocol.h b/sql/protocol.h index 05aee12d3d9..94fd303e259 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -53,7 +53,11 @@ public: bool store(const char *from, CHARSET_INFO *cs); String *storage_packet() { return packet; } inline void free() { packet->free(); } +#ifndef EMBEDDED_LIBRARY bool write(); +#else + virtual bool write(); +#endif inline bool store(uint32 from) { return store_long((longlong) from); } inline bool store(longlong from) @@ -121,6 +125,10 @@ public: Protocol_prep(THD *thd) :Protocol(thd) {} virtual bool prepare_for_send(List<Item> *item_list); virtual void prepare_for_resend(); +#ifdef EMBEDDED_LIBRARY + virtual bool write(); + bool net_store_data(const char *from, uint length); +#endif virtual bool store_null(); virtual bool store_tiny(longlong from); virtual bool store_short(longlong from); @@ -164,8 +172,15 @@ void net_printf(THD *thd,uint sql_errno, ...); void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, const char *info=0); void send_eof(THD *thd, bool no_flush=0); +bool send_old_password_request(THD *thd); char *net_store_length(char *packet,ulonglong length); char *net_store_length(char *packet,uint length); char *net_store_data(char *to,const char *from, uint length); char *net_store_data(char *to,int32 from); char *net_store_data(char *to,longlong from); + +#ifdef EMBEDDED_LIBRARY +bool setup_params_data(struct st_prep_stmt *stmt); +bool setup_params_data_withlog(struct st_prep_stmt *stmt); +#endif + diff --git a/sql/records.cc b/sql/records.cc index 72a6d480356..7ba9ff0f42f 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -1,4 +1,3 @@ - /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index e9c3b1ed0b0..7c943d4ae53 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -34,7 +34,6 @@ RPL_STATUS rpl_status=RPL_NULL; pthread_mutex_t LOCK_rpl_status; pthread_cond_t COND_rpl_status; HASH slave_list; -extern const char* any_db; const char *rpl_role_type[] = {"MASTER","SLAVE",NullS}; TYPELIB rpl_role_typelib = {array_elements(rpl_role_type)-1,"", @@ -154,9 +153,8 @@ int register_slave(THD* thd, uchar* packet, uint packet_length) SLAVE_INFO *si; uchar *p= packet, *p_end= packet + packet_length; - if (check_access(thd, REPL_SLAVE_ACL, any_db)) + if (check_access(thd, REPL_SLAVE_ACL, any_db,0,0,0)) return 1; - if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME)))) goto err2; @@ -177,7 +175,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length) pthread_mutex_lock(&LOCK_slave_list); unregister_slave(thd,0,0); - res= hash_insert(&slave_list, (byte*) si); + res= my_hash_insert(&slave_list, (byte*) si); pthread_mutex_unlock(&LOCK_slave_list); return res; @@ -250,6 +248,18 @@ static int find_target_pos(LEX_MASTER_INFO *mi, IO_CACHE *log, char *errmsg) /* Impossible */ } +/* + Before 4.0.15 we had a member of THD called log_pos, it was meant for + failsafe replication code in repl_failsafe.cc which is disabled until + it is reworked. Event's log_pos used to be preserved through + log-slave-updates to make code in repl_failsafe.cc work (this + function, SHOW NEW MASTER); but on the other side it caused unexpected + values in Exec_master_log_pos in A->B->C replication setup, + synchronization problems in master_pos_wait(), ... So we + (Dmitri & Guilhem) removed it. + + So for now this function is broken. +*/ int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg) { @@ -415,6 +425,9 @@ static Slave_log_event* find_slave_event(IO_CACHE* log, return (Slave_log_event*)ev; } +/* + This function is broken now. See comment for translate_master(). + */ int show_new_master(THD* thd) { @@ -525,7 +538,7 @@ HOSTS"; goto err; } si->server_id = server_id; - hash_insert(&slave_list, (byte*)si); + my_hash_insert(&slave_list, (byte*)si); } strmake(si->host, row[1], sizeof(si->host)-1); si->port = atoi(row[port_ind]); @@ -670,6 +683,17 @@ int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi) } mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout); mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (char *) &slave_net_timeout); + +#ifdef HAVE_OPENSSL + if (mi->ssl) + mysql_ssl_set(mysql, + mi->ssl_key[0]?mi->ssl_key:0, + mi->ssl_cert[0]?mi->ssl_cert:0, + mi->ssl_ca[0]?mi->ssl_ca:0, + mi->ssl_capath[0]?mi->ssl_capath:0, + mi->ssl_cipher[0]?mi->ssl_cipher:0); +#endif + mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset_info->csname); mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir); if (!mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0, @@ -903,6 +927,12 @@ int load_master_data(THD* thd) strmake(active_mi->rli.group_master_log_name,active_mi->master_log_name, sizeof(active_mi->rli.group_master_log_name)-1); /* + Cancel the previous START SLAVE UNTIL, as the fact to download + a new copy logically makes UNTIL irrelevant. + */ + clear_until_condition(&active_mi->rli); + + /* No need to update rli.event* coordinates, they will be when the slave threads start ; only rli.group* coordinates are necessary here. */ diff --git a/sql/set_var.cc b/sql/set_var.cc index 91583759c70..a44ee96ea0e 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -116,6 +116,8 @@ sys_var_character_set_client sys_character_set_client("character_set_client"); sys_var_character_set_connection sys_character_set_connection("character_set_connection"); sys_var_character_set_results sys_character_set_results("character_set_results"); sys_var_collation_connection sys_collation_connection("collation_connection"); +sys_var_collation_database sys_collation_database("collation_database"); +sys_var_collation_server sys_collation_server("collation_server"); sys_var_bool_ptr sys_concurrent_insert("concurrent_insert", &myisam_concurrent_insert); sys_var_long_ptr sys_connect_timeout("connect_timeout", @@ -216,6 +218,7 @@ sys_var_thd_ulong sys_net_retry_count("net_retry_count", &SV::net_retry_count, fix_net_retry_count); sys_var_thd_bool sys_new_mode("new", &SV::new_mode); +sys_var_thd_bool sys_old_passwords("old_passwords", &SV::old_passwords); sys_var_thd_ulong sys_preload_buff_size("preload_buffer_size", &SV::preload_buff_size); sys_var_thd_ulong sys_read_buff_size("read_buffer_size", @@ -242,6 +245,7 @@ sys_var_thd_enum sys_query_cache_type("query_cache_type", &SV::query_cache_type, &query_cache_type_typelib); #endif /* HAVE_QUERY_CACHE */ +sys_var_bool_ptr sys_secure_auth("secure_auth", &opt_secure_auth); sys_var_long_ptr sys_server_id("server_id",&server_id); sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol", &opt_slave_compressed_protocol); @@ -379,6 +383,8 @@ sys_var *sys_variables[]= &sys_character_set_connection, &sys_character_set_results, &sys_collation_connection, + &sys_collation_database, + &sys_collation_server, &sys_concurrent_insert, &sys_connect_timeout, &sys_default_week_format, @@ -432,6 +438,7 @@ sys_var *sys_variables[]= &sys_net_wait_timeout, &sys_net_write_timeout, &sys_new_mode, + &sys_old_passwords, &sys_preload_buff_size, &sys_pseudo_thread_id, &sys_query_cache_size, @@ -450,6 +457,7 @@ sys_var *sys_variables[]= #endif &sys_rpl_recovery_rank, &sys_safe_updates, + &sys_secure_auth, &sys_select_limit, &sys_server_id, #ifdef HAVE_REPLICATION @@ -505,6 +513,8 @@ struct show_var_st init_vars[]= { {sys_character_set_connection.name,(char*) &sys_character_set_connection,SHOW_SYS}, {sys_character_set_results.name,(char*) &sys_character_set_results, SHOW_SYS}, {sys_collation_connection.name,(char*) &sys_collation_connection, SHOW_SYS}, + {sys_collation_database.name,(char*) &sys_collation_database, SHOW_SYS}, + {sys_collation_server.name,(char*) &sys_collation_server, SHOW_SYS}, {sys_concurrent_insert.name,(char*) &sys_concurrent_insert, SHOW_SYS}, {sys_connect_timeout.name, (char*) &sys_connect_timeout, SHOW_SYS}, {"datadir", mysql_real_data_home, SHOW_CHAR}, @@ -608,6 +618,7 @@ struct show_var_st init_vars[]= { {sys_net_retry_count.name, (char*) &sys_net_retry_count, SHOW_SYS}, {sys_net_write_timeout.name,(char*) &sys_net_write_timeout, SHOW_SYS}, {sys_new_mode.name, (char*) &sys_new_mode, SHOW_SYS}, + {sys_old_passwords.name, (char*) &sys_old_passwords, SHOW_SYS}, {"open_files_limit", (char*) &open_files_limit, SHOW_LONG}, {"pid_file", (char*) pidfile_name, SHOW_CHAR}, {"log_error", (char*) log_error_file, SHOW_CHAR}, @@ -615,20 +626,21 @@ struct show_var_st init_vars[]= { {"protocol_version", (char*) &protocol_version, SHOW_INT}, {sys_preload_buff_size.name, (char*) &sys_preload_buff_size, SHOW_SYS}, {sys_pseudo_thread_id.name, (char*) &sys_pseudo_thread_id, SHOW_SYS}, - {sys_read_buff_size.name, (char*) &sys_read_buff_size, SHOW_SYS}, - {sys_readonly.name, (char*) &sys_readonly, SHOW_SYS}, - {sys_read_rnd_buff_size.name,(char*) &sys_read_rnd_buff_size, SHOW_SYS}, -#ifdef HAVE_REPLICATION - {sys_relay_log_purge.name, (char*) &sys_relay_log_purge, SHOW_SYS}, -#endif - {sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS}, #ifdef HAVE_QUERY_CACHE {sys_query_cache_limit.name,(char*) &sys_query_cache_limit, SHOW_SYS}, {sys_query_cache_min_res_unit.name, (char*) &sys_query_cache_min_res_unit, SHOW_SYS}, {sys_query_cache_size.name, (char*) &sys_query_cache_size, SHOW_SYS}, {sys_query_cache_type.name, (char*) &sys_query_cache_type, SHOW_SYS}, + {"secure_auth", (char*) &sys_secure_auth, SHOW_SYS}, #endif /* HAVE_QUERY_CACHE */ + {sys_read_buff_size.name, (char*) &sys_read_buff_size, SHOW_SYS}, + {sys_readonly.name, (char*) &sys_readonly, SHOW_SYS}, + {sys_read_rnd_buff_size.name,(char*) &sys_read_rnd_buff_size, SHOW_SYS}, +#ifdef HAVE_REPLICATION + {sys_relay_log_purge.name, (char*) &sys_relay_log_purge, SHOW_SYS}, +#endif + {sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS}, #ifdef HAVE_SMEM {"shared_memory", (char*) &opt_enable_shared_memory, SHOW_MY_BOOL}, {"shared_memory_base_name", (char*) &shared_memory_base_name, SHOW_CHAR_PTR}, @@ -845,10 +857,12 @@ void fix_max_relay_log_size(THD *thd, enum_var_type type) bool sys_var_long_ptr::update(THD *thd, set_var *var) { ulonglong tmp= var->value->val_int(); + pthread_mutex_lock(&LOCK_global_system_variables); if (option_limits) *value= (ulong) getopt_ull_limit_value(tmp, option_limits); else *value= (ulong) tmp; + pthread_mutex_unlock(&LOCK_global_system_variables); return 0; } @@ -862,17 +876,21 @@ void sys_var_long_ptr::set_default(THD *thd, enum_var_type type) bool sys_var_ulonglong_ptr::update(THD *thd, set_var *var) { ulonglong tmp= var->value->val_int(); + pthread_mutex_lock(&LOCK_global_system_variables); if (option_limits) *value= (ulonglong) getopt_ull_limit_value(tmp, option_limits); else *value= (ulonglong) tmp; + pthread_mutex_unlock(&LOCK_global_system_variables); return 0; } void sys_var_ulonglong_ptr::set_default(THD *thd, enum_var_type type) { + pthread_mutex_lock(&LOCK_global_system_variables); *value= (ulonglong) option_limits->def_value; + pthread_mutex_unlock(&LOCK_global_system_variables); } @@ -1164,9 +1182,21 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) case SHOW_LONG: return new Item_uint((int32) *(ulong*) value_ptr(thd, var_type, base)); case SHOW_LONGLONG: - return new Item_int(*(longlong*) value_ptr(thd, var_type, base)); + { + longlong value; + pthread_mutex_lock(&LOCK_global_system_variables); + value= *(longlong*) value_ptr(thd, var_type, base); + pthread_mutex_unlock(&LOCK_global_system_variables); + return new Item_int(value); + } case SHOW_HA_ROWS: - return new Item_int((longlong) *(ha_rows*) value_ptr(thd, var_type, base)); + { + ha_rows value; + pthread_mutex_lock(&LOCK_global_system_variables); + value= *(ha_rows*) value_ptr(thd, var_type, base); + pthread_mutex_unlock(&LOCK_global_system_variables); + return new Item_int((longlong) value); + } case SHOW_MY_BOOL: return new Item_int((int32) *(my_bool*) value_ptr(thd, var_type, base),1); case SHOW_CHAR: @@ -1272,7 +1302,7 @@ bool sys_var_collation::check(THD *thd, set_var *var) String str(buff,sizeof(buff), system_charset_info), *res; if (!(res=var->value->val_str(&str))) - res= &empty_string; + res= &my_empty_string; if (!(tmp=get_charset_by_name(res->c_ptr(),MYF(0)))) { @@ -1313,6 +1343,7 @@ bool sys_var_character_set::check(THD *thd, set_var *var) bool sys_var_character_set::update(THD *thd, set_var *var) { ci_ptr(thd,var->type)[0]= var->save_result.charset; + thd->update_charset(); return 0; } @@ -1398,20 +1429,19 @@ CHARSET_INFO ** sys_var_character_set_server::ci_ptr(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) - return &global_system_variables.character_set_server; + return &global_system_variables.collation_server; else - return &thd->variables.character_set_server; + return &thd->variables.collation_server; } void sys_var_character_set_server::set_default(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) - global_system_variables.character_set_server= default_charset_info; + global_system_variables.collation_server= default_charset_info; else { - thd->variables.character_set_server= (global_system_variables. - character_set_server); + thd->variables.collation_server= global_system_variables.collation_server; thd->update_charset(); } } @@ -1421,19 +1451,19 @@ CHARSET_INFO ** sys_var_character_set_database::ci_ptr(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) - return &global_system_variables.character_set_database; + return &global_system_variables.collation_database; else - return &thd->variables.character_set_database; + return &thd->variables.collation_database; } void sys_var_character_set_database::set_default(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) - global_system_variables.character_set_database= default_charset_info; + global_system_variables.collation_database= default_charset_info; else { - thd->variables.character_set_database= thd->db_charset; + thd->variables.collation_database= thd->db_charset; thd->update_charset(); } } @@ -1474,6 +1504,77 @@ void sys_var_collation_connection::set_default(THD *thd, enum_var_type type) } } +bool sys_var_collation_database::update(THD *thd, set_var *var) +{ + if (var->type == OPT_GLOBAL) + global_system_variables.collation_database= var->save_result.charset; + else + { + thd->variables.collation_database= var->save_result.charset; + thd->update_charset(); + } + return 0; +} + + +byte *sys_var_collation_database::value_ptr(THD *thd, enum_var_type type, + LEX_STRING *base) +{ + CHARSET_INFO *cs= ((type == OPT_GLOBAL) ? + global_system_variables.collation_database : + thd->variables.collation_database); + return cs ? (byte*) cs->name : (byte*) "NULL"; +} + + +void sys_var_collation_database::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + global_system_variables.collation_database= default_charset_info; + else + { + thd->variables.collation_database= (global_system_variables. + collation_database); + thd->update_charset(); + } +} + + +bool sys_var_collation_server::update(THD *thd, set_var *var) +{ + if (var->type == OPT_GLOBAL) + global_system_variables.collation_server= var->save_result.charset; + else + { + thd->variables.collation_server= var->save_result.charset; + thd->update_charset(); + } + return 0; +} + + +byte *sys_var_collation_server::value_ptr(THD *thd, enum_var_type type, + LEX_STRING *base) +{ + CHARSET_INFO *cs= ((type == OPT_GLOBAL) ? + global_system_variables.collation_server : + thd->variables.collation_server); + return cs ? (byte*) cs->name : (byte*) "NULL"; +} + + +void sys_var_collation_server::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + global_system_variables.collation_server= default_charset_info; + else + { + thd->variables.collation_server= (global_system_variables. + collation_server); + thd->update_charset(); + } +} + bool sys_var_key_buffer_size::update(THD *thd, set_var *var) { @@ -1622,6 +1723,7 @@ byte *sys_var_insert_id::value_ptr(THD *thd, enum_var_type type, bool sys_var_pseudo_thread_id::check(THD *thd, set_var *var) { +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (thd->master_access & SUPER_ACL) return 0; else @@ -1629,6 +1731,9 @@ bool sys_var_pseudo_thread_id::check(THD *thd, set_var *var) my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); return 1; } +#else + return 0; +#endif } @@ -1812,8 +1917,6 @@ static byte *get_sys_var_length(const sys_var *var, uint *length, void set_var_init() { - extern struct my_option my_long_options[]; // From mysqld - hash_init(&system_variable_hash, system_charset_info, array_elements(sys_variables),0,0, (hash_get_key) get_sys_var_length,0,0); @@ -1824,7 +1927,7 @@ void set_var_init() { (*var)->name_length= strlen((*var)->name); (*var)->option_limits= find_option(my_long_options, (*var)->name); - hash_insert(&system_variable_hash, (byte*) *var); + my_hash_insert(&system_variable_hash, (byte*) *var); } /* Special cases @@ -1927,7 +2030,6 @@ int set_var::check(THD *thd) } if ((type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL))) return 1; - /* value is a NULL pointer if we are using SET ... = DEFAULT */ if (!value) { @@ -1990,17 +2092,25 @@ int set_var_user::update(THD *thd) int set_var_password::check(THD *thd) { +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (!user->host.str) user->host.str= (char*) thd->host_or_ip; /* Returns 1 as the function sends error to client */ return check_change_password(thd, user->host.str, user->user.str) ? 1 : 0; +#else + return 0; +#endif } int set_var_password::update(THD *thd) { +#ifndef NO_EMBEDDED_ACCESS_CHECKS /* Returns 1 as the function sends error to client */ return (change_password(thd, user->host.str, user->user.str, password) ? 1 : 0); +#else + return 0; +#endif } /**************************************************************************** diff --git a/sql/set_var.h b/sql/set_var.h index f06b5ed22d3..812bd6c9420 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -523,6 +523,23 @@ public: byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; +class sys_var_collation_server :public sys_var_collation +{ +public: + sys_var_collation_server(const char *name_arg) :sys_var_collation(name_arg) {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); +}; + +class sys_var_collation_database :public sys_var_collation +{ +public: + sys_var_collation_database(const char *name_arg) :sys_var_collation(name_arg) {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); +}; class sys_var_key_buffer_size :public sys_var { @@ -689,6 +706,9 @@ public: } }; +/* updated in sql_acl.cc */ + +extern sys_var_thd_bool sys_old_passwords; /* For sql_yacc */ struct sys_var_with_base diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am index 35202ff4722..662159a9c63 100644 --- a/sql/share/Makefile.am +++ b/sql/share/Makefile.am @@ -30,7 +30,7 @@ install-data-local: fix_errors: for lang in @AVAILABLE_LANGUAGES@; \ do \ - ../../extra/comp_err $(srcdir)/$$lang/errmsg.txt $(srcdir)/$$lang/errmsg.sys; \ + ../../extra/comp_err -C$(srcdir)/charsets/ $(srcdir)/$$lang/errmsg.txt $(srcdir)/$$lang/errmsg.sys; \ done # Don't update the files from bitkeeper diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 1beffa81671..5a3976822e5 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -1,4 +1,4 @@ -v/* +/* Modifikoval Petr -B©najdr, snajdr@pvt.net, snajdr@cpress.cz v.0.01 ISO LATIN-8852-2 Dal-B¹í verze Jan Pazdziora, adelton@fi.muni.cz @@ -10,6 +10,8 @@ v/* Thu Nov 30 14:02:52 MET 2000 podle 3.23.28 */ +character-set=latin2 + "hashchk", "isamchk", "NE", @@ -251,7 +253,7 @@ v/* "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -263,13 +265,18 @@ v/* "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -278,3 +285,9 @@ v/* "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index c13141fc669..6f43e3eb7f1 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -4,6 +4,8 @@ /* Knud Riishøjgård knudriis@post.tele.dk 99 && Carsten H. Pedersen, carsten.pedersen@bitbybit.dk oct. 1999 / aug. 2001. */ +character-set=latin1 + "hashchk", "isamchk", "NEJ", @@ -245,7 +247,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -257,13 +259,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -272,3 +279,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index b454566f3ba..d372e1c9ff6 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -12,6 +12,8 @@ Translated new error messages. */ +character-set=latin1 + "hashchk", "isamchk", "NEE", @@ -253,7 +255,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -265,13 +267,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -280,3 +287,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 5655ad32d3d..9506e672a05 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -1,6 +1,8 @@ /* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB This file is public domain and comes with NO WARRANTY of any kind */ +character-set=latin1 + "hashchk", "isamchk", "NO", @@ -242,7 +244,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -254,8 +256,8 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client" "All parts of a SPATIAL KEY must be NOT NULL" "COLLATION '%s' is not valid for CHARACTER SET '%s'" -"The slave was already running" -"The slave was already stopped" +"Slave is already running" +"Slave has already been stopped" "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)" "Z_BUF_ERROR: Not enough memory available for zlib" "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)" @@ -274,3 +276,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format", +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 8e7a7121dfa..8fbf084320e 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -6,6 +6,8 @@ */ +character-set=latin7 + "hashchk", "isamchk", "EI", @@ -247,7 +249,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -259,13 +261,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -274,3 +281,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 8a6d786bee8..3eeecce6c33 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -1,6 +1,8 @@ /* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB This file is public domain and comes with NO WARRANTY of any kind */ +character-set=latin1 + "hashchk", "isamchk", "NON", @@ -242,7 +244,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -254,13 +256,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -269,3 +276,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index db20cc5dad9..ebedaad6e94 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -10,6 +10,8 @@ 2002-12-11 */ +character-set=latin1 + "hashchk", "isamchk", "Nein", @@ -263,13 +265,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -278,3 +285,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index f844ee59dab..dfcd8710a15 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -1,6 +1,8 @@ /* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB This file is public domain and comes with NO WARRANTY of any kind */ +character-set=greek + "hashchk", "isamchk", "Ï×É", @@ -242,7 +244,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -254,13 +256,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -269,3 +276,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 7e60515aace..71877f252f9 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -3,6 +3,8 @@ Updated May, 2000 This file is public domain and comes with NO WARRANTY of any kind */ +character-set=latin2 + "hashchk", "isamchk", "NEM", @@ -244,7 +246,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -256,13 +258,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -271,3 +278,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index a624d90661d..501630aa3ef 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -1,6 +1,8 @@ /* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB This file is public domain and comes with NO WARRANTY of any kind */ +character-set=latin1 + "hashchk", "isamchk", "NO", @@ -242,7 +244,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -254,13 +256,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -269,3 +276,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index b5693fe6e41..4a066e2e13f 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -3,6 +3,8 @@ 3.22.10-beta euc-japanese (ujis) text */ +character-set=ujis + "hashchk", "isamchk", "NO", @@ -244,7 +246,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -256,13 +258,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -271,3 +278,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index bc2d6eb9caa..333da3967dc 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -1,6 +1,8 @@ /* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB This ÈÀÏ is public domain and comes with NO WARRANTY of any kind */ +character-set=euckr + "hashchk", "isamchk", "¾Æ´Ï¿À", @@ -242,7 +244,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -254,13 +256,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -269,3 +276,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 9bf0d4845c7..7899ac2862b 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -3,6 +3,8 @@ /* Roy-Magne Mo rmo@www.hivolda.no 97 */ +character-set=latin1 + "hashchk", "isamchk", "NEI", @@ -244,7 +246,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -256,13 +258,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -271,3 +278,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 4a025e7218f..171d5eb7b3a 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -3,6 +3,8 @@ /* Roy-Magne Mo rmo@www.hivolda.no 97 */ +character-set=latin1 + "hashchk", "isamchk", "NEI", @@ -244,7 +246,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -256,13 +258,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -271,3 +278,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 1a6634fb96c..3e1a101b9d1 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -5,6 +5,8 @@ Charset ISO-8859-2 */ +character-set=latin2 + "hashchk", "isamchk", "NIE", @@ -246,7 +248,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -258,13 +260,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -273,3 +280,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 99a940e62ab..af4e210a5b7 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -1,6 +1,9 @@ /* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB This file is public domain and comes with NO WARRANTY of any kind */ /* Updated by Thiago Delgado Pinto - thiagodp@ieg.com.br - 06.07.2002 */ + +character-set=latin1 + "hashchk", "isamchk", "NÃO", @@ -219,26 +222,26 @@ "Não pode acrescentar uma restrição de chave estrangeira", "Não pode acrescentar uma linha filha: uma restrição de chave estrangeira falhou", "Não pode apagar uma linha pai: uma restrição de chave estrangeira falhou", -"Erro connectando para o master: %-.128s", +"Erro conectando com o master: %-.128s", "Erro rodando consulta no master: %-.128s", "Erro quando executando comando %s: %-.128s", "Uso errado de %s e %s", -"Os comandos SELECT usados têm diferentes números de colunas", -"Não pode executar a consulta porque você tem um conflitante travamento de leitura", -"Combinação de tabelas transacionais e não transacionais está desativada", +"Os comandos SELECT usados têm diferente número de colunas", +"Não posso executar a consulta porque você tem um conflito de travamento de leitura", +"Mistura de tabelas transacional e não-transacional está desabilitada", "Opção '%s' usada duas vezes no comando", -"Usuário '%-.64s' há excedido o '%s' de recursos (atual valor: %ld)", -"Acesso negado. Você necessita o privilégio %-.128s para essa operação", +"Usuário '%-.64s' tem excedido o '%s' recurso (atual valor: %ld)", +"Acesso negado. Você precisa o privilégio %-.128s para essa operação", "Variável '%-.64s' é uma LOCAL variável e não pode ser usada com SET GLOBAL", -"Variável '%-.64s' é uma GLOBAL variável e deve ser configurada com SET GLOBAL", -"Variável '%-.64s' não tem um valor default (padrão)", +"Variável '%-.64s' é uma GLOBAL variável e deve ser configurada com SET GLOBAL", +"Variável '%-.64s' não tem um valor padrão", "Variável '%-.64s' não pode ser configurada para o valor de '%-.64s'", -"Tipo de argumento errado para a variável '%-.64s'", +"Tipo errado de argumento para variável '%-.64s'", "Variável '%-.64s' somente pode ser configurada, não lida", -"Uso/localização errada de '%s'", +"Errado uso/colocação de '%s'", "Esta versão de MySQL não suporta ainda '%s'", -"Obteve fatal error %d: '%-.128s' a partir do master quando lendo dados do binary log", -"Slave SQL thread ignored the query because of replicate-*-table rules", +"Obteve fatal erro %d: '%-.128s' do master quando lendo dados do binary log", +"Slave SQL thread ignorado a consulta devido às normas de replicação-*-tabela" "Definição errada da chave estrangeira para '%-.64s': %s", "Referência da chave e referência da tabela não coincidem", "Error de cardinalidade (mais/menos que %d colunas)", @@ -261,6 +264,11 @@ "Z_MEM_ERROR: Não suficiente espaço no buffer emissor para zlib (provavelmente, o comprimento dos dados descomprimidos está corrupto)", "Z_DATA_ERROR: Dados de entrada está corrupto para zlib", "%d linha(s) foi(foram) cortada(s) por group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Usando engine de armazenamento %s para tabela '%s'", "Combinação ilegal de collations (%s,%s) e (%s,%s) para operação '%s'", "Can't drop one or more of the requested users" @@ -269,3 +277,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 61591d54d40..0dffb09e9f7 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -5,6 +5,8 @@ e-mail: tzoompy@cs.washington.edu */ +character-set=latin2 + "hashchk", "isamchk", "NU", @@ -246,7 +248,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -258,13 +260,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -273,3 +280,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index b1876f83008..389cb581717 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -3,6 +3,8 @@ This file is public domain and comes with NO WARRANTY of any kind */ /* charset: KOI8-R */ +character-set=koi8r + "hashchk", "isamchk", "îåô", @@ -256,13 +258,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -271,3 +278,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"óÅÒ×ÅÒ ÚÁÐÕÝÅÎ × ÒÅÖÉÍÅ --secure-auth (ÂÅÚÏÐÁÓÎÏÊ Á×ÔÏÒÉÚÁÃÉÉ), ÎÏ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%s@%s' ÐÁÒÏÌØ ÓÏÈÒÁÎ£Î × ÓÔÁÒÏÍ ÆÏÒÍÁÔÅ; ÎÅÏÂÈÏÄÉÍÏ ÏÂÎÏ×ÉÔØ ÆÏÒÍÁÔ ÐÁÒÏÌÑ" +"ðÏÌÅ ÉÌÉ ÓÓÙÌËÁ '%-.64s%s%-.64s%s%-.64s' ÉÚ SELECTÁ #%d ÂÙÌÁ ÎÁÊÄÅÎÁ × SELECTÅ #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 9e2a37e4053..0f461a9e718 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -7,6 +7,8 @@ Charset: cp1250 */ +character-set=cp1250 + "hashchk", "isamchk", "NE", @@ -237,7 +239,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -249,13 +251,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -264,3 +271,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index a96d512cb72..dc07eb0a54e 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -9,6 +9,8 @@ Date: Streda 11. November 1998 20:58:15 */ +character-set=latin2 + "hashchk", "isamchk", "NIE", @@ -250,7 +252,7 @@ "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -262,13 +264,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -277,3 +284,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index df18b8bf7f0..a1b36887282 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -2,6 +2,9 @@ This file is public domain and comes with NO WARRANTY of any kind Traduccion por Miguel Angel Fernandez Roiz -- LoboCom Sistemas, s.l. From June 28, 2001 translated by Miguel Solorzano miguel@mysql.com */ + +character-set=latin1 + "hashchk", "isamchk", "NO", @@ -223,27 +226,27 @@ "Error de coneccion a master: %-128s", "Error executando el query en master: %-128%", "Error de %s: %-128%", -"Wrong usage of %s and %s", -"The used SELECT statements have a different number of columns", -"Can't execute the query because you have a conflicting read lock", -"Mixing of transactional and non-transactional tables is disabled", -"Option '%s' used twice in statement", -"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", -"Access denied. You need the %-.128s privilege for this operation", -"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", -"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", -"Variable '%-.64s' doesn't have a default value", -"Variable '%-.64s' can't be set to the value of '%-.64s'", -"Wrong argument type to variable '%-.64s'", -"Variable '%-.64s' can only be set, not read", -"Wrong usage/placement of '%s'", -"This version of MySQL doesn't yet support '%s'", -"Got fatal error %d: '%-.128s' from master when reading data from binary log", -"Slave SQL thread ignored the query because of replicate-*-table rules", +"Equivocado uso de %s y %s", +"El comando SELECT usado tiene diferente número de columnas", +"No puedo ejecutar el query porque usted tiene conflicto de traba de lectura", +"Mezla de transancional y no-transancional tablas está deshabilitada", +"Opción '%s' usada dos veces en el comando", +"Usuario '%-.64s' ha excedido el recurso '%s' (actual valor: %ld)", +"Acceso negado. Usted necesita el privilegio %-.128s para esta operación", +"Variable '%-.64s' es una LOCAL variable y no puede ser usada con SET GLOBAL", +"Variable '%-.64s' es una GLOBAL variable y no puede ser configurada con SET GLOBAL", +"Variable '%-.64s' no tiene un valor patrón", +"Variable '%-.64s' no puede ser configurada para el valor de '%-.64s'", +"Tipo de argumento equivocado para variable '%-.64s'", +"Variable '%-.64s' solamente puede ser configurada, no leída", +"Equivocado uso/colocación de '%s'", +"Esta versión de MySQL no soporta todavia '%s'", +"Recibió fatal error %d: '%-.128s' del master cuando leyendo datos del binary log", +"Slave SQL thread ignorado el query debido a las reglas de replicación-*-tabla" "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Cardinality error (more/less than %d columns)", -"Subselect returns more than 1 record", +"Subquery returns more than 1 row", "Unknown prepared statement handler (%ld) given to %s", "Help database is corrupt or does not exist", "Cyclic reference on subqueries", @@ -255,13 +258,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -270,3 +278,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 07187f7ca34..e0d0bea47d2 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -1,6 +1,8 @@ /* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB This file is public domain and comes with NO WARRANTY of any kind */ +character-set=latin1 + "hashchk", "isamchk", "NO", @@ -242,10 +244,10 @@ "Felaktig FOREIGN KEY-definition för '%-.64s': %s", "Nyckelreferensen och tabellreferensen stämmer inte överens", "Kardinalitetsfel (fler/färre än %d kolumner)", -"Subselect returnerade mer än 1 rad", +"Subquery returnerade mer än 1 rad", "Okänd PREPARED STATEMENT id (%ld) var given till %s", "Hjälpdatabasen finns inte eller är skadad", -"Cyklisk referens i subselect", +"Cyklisk referens i subqueries", "Konvertar kolumn '%s' från %s till %s", "Referens '%-.64s' stöds inte (%s)", "Varje 'derived table' måste ha sitt eget alias", @@ -261,6 +263,11 @@ "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d rad(er) kapades av group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Använder handler %s för tabell '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -269,3 +276,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index a0604ba862c..a2133049741 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -6,6 +6,8 @@ * Version: 13/09/2001 mysql-3.23.41 */ +character-set=koi8u + "hashchk", "isamchk", "î¶", @@ -259,13 +261,18 @@ "Client does not support authentication protocol requested by server; consider upgrading MySQL client", "All parts of a SPATIAL KEY must be NOT NULL", "COLLATION '%s' is not valid for CHARACTER SET '%s'", -"The slave was already running", -"The slave was already stopped", +"Slave is already running", +"Slave has already been stopped", "Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", "Z_BUF_ERROR: Not enough memory available for zlib", "Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", +"Record count is fewer than the column count at row %ld"; +"Record count is more than the column count at row %ld"; +"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld"; +"Data truncated, out of range for column '%s' at row %ld"; +"Data truncated for column '%s' at row %ld" "Using storage engine %s for table '%s'", "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", "Can't drop one or more of the requested users" @@ -274,3 +281,9 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started." +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" +"óÔÏ×ÂÅÃØ ÁÂÏ ÐÏÓÉÌÁÎÎÑ '%-.64s%s%-.64s%s%-.64s' ¦Ú SELECTÕ #%d ÂÕÌÏ ÚÎÁÊÄÅÎÅ Õ SELECT¦ #%d", +"Wrong parameter or combination of parameters for START SLAVE UNTIL" +"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart" +"SQL thread is not to be started so UNTIL options are ignored" diff --git a/sql/slave.cc b/sql/slave.cc index d45cf1aa8b9..641707aab2f 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -238,10 +238,12 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, *errmsg=0; pthread_mutex_t *log_lock=rli->relay_log.get_log_lock(); - pthread_mutex_lock(log_lock); + if (need_data_lock) pthread_mutex_lock(&rli->data_lock); + pthread_mutex_lock(log_lock); + /* Close log file and free buffers if it's already open */ if (rli->cur_log_fd >= 0) { @@ -304,10 +306,12 @@ err: if (!relay_log_purge) rli->log_space_limit= 0; pthread_cond_broadcast(&rli->data_cond); + + pthread_mutex_unlock(log_lock); + if (need_data_lock) pthread_mutex_unlock(&rli->data_lock); - pthread_mutex_unlock(log_lock); DBUG_RETURN ((*errmsg) ? 1 : 0); } @@ -786,7 +790,7 @@ int add_table_rule(HASH* h, const char* table_spec) e->tbl_name = e->db + (dot - table_spec) + 1; e->key_len = len; memcpy(e->db, table_spec, len); - (void)hash_insert(h, (byte*)e); + (void)my_hash_insert(h, (byte*)e); return 0; } @@ -1505,11 +1509,10 @@ static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) 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"; save_proc_info= thd->enter_cond(&rli->log_space_cond, &rli->log_space_lock, - "Waiting for relay log space to free"); + "\ +Waiting for the SQL slave thread to free enough relay log space"); while (rli->log_space_limit < rli->log_space_total && !(slave_killed=io_slave_killed(thd,mi)) && !rli->ignore_log_space_limit) @@ -1544,6 +1547,7 @@ static int count_relay_log_space(RELAY_LOG_INFO* rli) DBUG_RETURN(0); } + void init_master_info_with_options(MASTER_INFO* mi) { mi->master_log_name[0] = 0; @@ -1554,11 +1558,24 @@ void init_master_info_with_options(MASTER_INFO* mi) if (master_user) strmake(mi->user, master_user, sizeof(mi->user) - 1); if (master_password) - strmake(mi->password, master_password, HASH_PASSWORD_LENGTH); + strmake(mi->password, master_password, MAX_PASSWORD_LENGTH); mi->port = master_port; mi->connect_retry = master_connect_retry; + + mi->ssl= master_ssl; + if (master_ssl_ca) + strmake(mi->ssl_ca, master_ssl_ca, sizeof(mi->ssl_ca)-1); + if (master_ssl_capath) + strmake(mi->ssl_capath, master_ssl_capath, sizeof(mi->ssl_capath)-1); + if (master_ssl_cert) + strmake(mi->ssl_cert, master_ssl_cert, sizeof(mi->ssl_cert)-1); + if (master_ssl_cipher) + strmake(mi->ssl_cipher, master_ssl_cipher, sizeof(mi->ssl_cipher)-1); + if (master_ssl_key) + strmake(mi->ssl_key, master_ssl_key, sizeof(mi->ssl_key)-1); } + void clear_last_slave_error(RELAY_LOG_INFO* rli) { //Clear the errors displayed by SHOW SLAVE STATUS @@ -1566,6 +1583,24 @@ void clear_last_slave_error(RELAY_LOG_INFO* rli) rli->last_slave_errno=0; } + +/* + Reset UNTIL condition for RELAY_LOG_INFO + SYNOPSYS + clear_until_condition() + rli - RELAY_LOG_INFO structure where UNTIL condition should be reset + */ +void clear_until_condition(RELAY_LOG_INFO* rli) +{ + rli->until_condition= RELAY_LOG_INFO::UNTIL_NONE; + rli->until_log_name[0]= 0; + rli->until_log_pos= 0; +} + + +#define LINES_IN_MASTER_INFO_WITH_SSL 14 + + int init_master_info(MASTER_INFO* mi, const char* master_info_fname, const char* slave_info_fname, bool abort_if_no_master_info_file) @@ -1643,25 +1678,87 @@ file '%s')", fname); } mi->fd = fd; - int port, connect_retry, master_log_pos; - + int port, connect_retry, master_log_pos, ssl= 0, lines; + char *first_non_digit; + + /* + Starting from 4.1.x master.info has new format. Now its + first line contains number of lines in file. By reading this + number we will be always distinguish to which version our + master.info corresponds to. We can't simply count lines in + file since versions before 4.1.x could generate files with more + lines than needed. + If first line doesn't contain a number or contain number less than + 14 then such file is treated like file from pre 4.1.1 version. + There is no ambiguity when reading an old master.info, as before + 4.1.1, the first line contained the binlog's name, which is either + empty or has an extension (contains a '.'), so can't be confused + with an integer. + + So we're just reading first line and trying to figure which version + is this. + */ + + /* + The first row is temporarily stored in mi->master_log_name, + if it is line count and not binlog name (new format) it will be + overwritten by the second row later. + */ if (init_strvar_from_file(mi->master_log_name, sizeof(mi->master_log_name), &mi->file, - "") || - init_intvar_from_file(&master_log_pos, &mi->file, 4) || + "")) + goto errwithmsg; + + lines= strtoul(mi->master_log_name, &first_non_digit, 10); + + if (mi->master_log_name[0]!='\0' && + *first_non_digit=='\0' && lines >= LINES_IN_MASTER_INFO_WITH_SSL) + { // Seems to be new format + if (init_strvar_from_file(mi->master_log_name, + sizeof(mi->master_log_name), &mi->file, "")) + goto errwithmsg; + } + else + lines= 7; + + if (init_intvar_from_file(&master_log_pos, &mi->file, 4) || init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, master_host) || init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, master_user) || - init_strvar_from_file(mi->password, HASH_PASSWORD_LENGTH+1, &mi->file, - master_password) || + init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1, + &mi->file, master_password) || init_intvar_from_file(&port, &mi->file, master_port) || init_intvar_from_file(&connect_retry, &mi->file, master_connect_retry)) - { - sql_print_error("Error reading master configuration"); - goto err; - } + goto errwithmsg; + + /* + If file has ssl part use it even if we have server without + SSL support. But these option will be ignored later when + slave will try connect to master, so in this case warning + is printed. + */ + if (lines >= LINES_IN_MASTER_INFO_WITH_SSL && + (init_intvar_from_file(&ssl, &mi->file, master_ssl) || + init_strvar_from_file(mi->ssl_ca, sizeof(mi->ssl_ca), + &mi->file, master_ssl_ca) || + init_strvar_from_file(mi->ssl_capath, sizeof(mi->ssl_capath), + &mi->file, master_ssl_capath) || + init_strvar_from_file(mi->ssl_cert, sizeof(mi->ssl_cert), + &mi->file, master_ssl_cert) || + init_strvar_from_file(mi->ssl_cipher, sizeof(mi->ssl_cipher), + &mi->file, master_ssl_cipher) || + init_strvar_from_file(mi->ssl_key, sizeof(mi->ssl_key), + &mi->file, master_ssl_key))) + goto errwithmsg; +#ifndef HAVE_OPENSSL + if (ssl) + sql_print_error("SSL information in the master info file " + "('%s') are ignored because this MySQL slave was compiled " + "without SSL support.", fname); +#endif /* HAVE_OPENSSL */ + /* This has to be handled here as init_intvar_from_file can't handle my_off_t types @@ -1669,6 +1766,7 @@ file '%s')", fname); mi->master_log_pos= (my_off_t) master_log_pos; mi->port= (uint) port; mi->connect_retry= (uint) connect_retry; + mi->ssl= (my_bool) ssl; } DBUG_PRINT("master_info",("log_file_name: %s position: %ld", mi->master_log_name, @@ -1685,7 +1783,10 @@ file '%s')", fname); sql_print_error("Failed to flush master info file"); pthread_mutex_unlock(&mi->data_lock); DBUG_RETURN(error); - + +errwithmsg: + sql_print_error("Error reading master configuration"); + err: if (fd >= 0) { @@ -1820,6 +1921,22 @@ int show_master_info(THD* thd, MASTER_INFO* mi) MYSQL_TYPE_LONGLONG)); field_list.push_back(new Item_return_int("Relay_log_space", 10, MYSQL_TYPE_LONGLONG)); + field_list.push_back(new Item_empty_string("Until_condition", 6)); + field_list.push_back(new Item_empty_string("Until_Log_File", FN_REFLEN)); + field_list.push_back(new Item_return_int("Until_Log_pos", 10, + MYSQL_TYPE_LONGLONG)); + field_list.push_back(new Item_empty_string("Master_SSL_Allowed", 7)); + field_list.push_back(new Item_empty_string("Master_SSL_CA_File", + sizeof(mi->ssl_ca))); + field_list.push_back(new Item_empty_string("Master_SSL_CA_Path", + sizeof(mi->ssl_capath))); + field_list.push_back(new Item_empty_string("Master_SSL_Cert", + sizeof(mi->ssl_cert))); + field_list.push_back(new Item_empty_string("Master_SSL_Cipher", + sizeof(mi->ssl_cipher))); + field_list.push_back(new Item_empty_string("Master_SSL_Key", + sizeof(mi->ssl_key))); + if (protocol->send_fields(&field_list, 1)) DBUG_RETURN(-1); @@ -1868,6 +1985,25 @@ int show_master_info(THD* thd, MASTER_INFO* mi) protocol->store((uint32) mi->rli.slave_skip_counter); protocol->store((ulonglong) mi->rli.group_master_log_pos); protocol->store((ulonglong) mi->rli.log_space_total); + + protocol->store( + mi->rli.until_condition==RELAY_LOG_INFO::UNTIL_NONE ? "None": + ( mi->rli.until_condition==RELAY_LOG_INFO::UNTIL_MASTER_POS? "Master": + "Relay"), &my_charset_bin); + protocol->store(mi->rli.until_log_name, &my_charset_bin); + protocol->store((ulonglong) mi->rli.until_log_pos); + +#ifdef HAVE_OPENSSL + protocol->store(mi->ssl? "Yes":"No", &my_charset_bin); +#else + protocol->store(mi->ssl? "Ignored":"No", &my_charset_bin); +#endif + protocol->store(mi->ssl_ca, &my_charset_bin); + protocol->store(mi->ssl_capath, &my_charset_bin); + protocol->store(mi->ssl_cert, &my_charset_bin); + protocol->store(mi->ssl_cipher, &my_charset_bin); + protocol->store(mi->ssl_key, &my_charset_bin); + pthread_mutex_unlock(&mi->rli.data_lock); pthread_mutex_unlock(&mi->data_lock); @@ -1886,11 +2022,22 @@ bool flush_master_info(MASTER_INFO* mi) DBUG_ENTER("flush_master_info"); DBUG_PRINT("enter",("master_pos: %ld", (long) mi->master_log_pos)); + /* + In certain cases this code may create master.info files that seems + corrupted, because of extra lines filled with garbage in the end + file (this happens if new contents take less space than previous + contents of file). But because of number of lines in the first line + of file we don't care about this garbage. + */ + my_b_seek(file, 0L); - my_b_printf(file, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n", - mi->master_log_name, llstr(mi->master_log_pos, lbuf), + my_b_printf(file, "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n", + LINES_IN_MASTER_INFO_WITH_SSL, + mi->master_log_name, llstr(mi->master_log_pos, lbuf), mi->host, mi->user, - mi->password, mi->port, mi->connect_retry); + mi->password, mi->port, mi->connect_retry, + (int)(mi->ssl), mi->ssl_ca, mi->ssl_capath, mi->ssl_cert, + mi->ssl_cipher, mi->ssl_key); flush_io_cache(file); DBUG_RETURN(0); } @@ -1901,11 +2048,11 @@ st_relay_log_info::st_relay_log_info() cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0), ignore_log_space_limit(0), slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0), sql_thd(0), last_slave_errno(0), inited(0), abort_slave(0), - slave_running(0) + slave_running(0), until_condition(UNTIL_NONE), until_log_pos(0) { group_relay_log_name[0]= event_relay_log_name[0]= group_master_log_name[0]= 0; - last_slave_error[0]=0; - + last_slave_error[0]=0; until_log_name[0]= 0; + bzero((char*) &info_file, sizeof(info_file)); bzero((char*) &cache_buf, sizeof(cache_buf)); pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); @@ -2058,7 +2205,8 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, DBUG_PRINT("info",("Waiting for master update")); const char* msg = thd->enter_cond(&data_cond, &data_lock, - "Waiting for master update"); + "Waiting for the SQL slave thread to \ +advance position"); /* We are going to pthread_cond_(timed)wait(); if the SQL thread stops it will wake us up. @@ -2125,7 +2273,13 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) thd->master_access= ~0; thd->priv_user = 0; thd->slave_thread = 1; - thd->options = (((opt_log_slave_updates) ? OPTION_BIN_LOG:0) | OPTION_AUTO_IS_NULL) ; + thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) | + OPTION_AUTO_IS_NULL; + /* + It's nonsense to constraint the slave threads with max_join_size; if a + query succeeded on master, we HAVE to execute it. + */ + thd->variables.max_join_size= HA_POS_ERROR; thd->client_capabilities = CLIENT_LOCAL_FILES; thd->real_id=pthread_self(); pthread_mutex_lock(&LOCK_thread_count); @@ -2145,11 +2299,8 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); #endif - if (thd->variables.max_join_size == HA_POS_ERROR) - thd->options |= OPTION_BIG_SELECTS; - if (thd_type == SLAVE_THD_SQL) - thd->proc_info= "Waiting for the next event in slave queue"; + thd->proc_info= "Waiting for the next event in relay log"; else thd->proc_info= "Waiting for master update"; thd->version=refresh_version; @@ -2303,9 +2454,10 @@ server_errno=%d)", return packet_error; } - if (len == 1) + /* Check if eof packet */ + if (len < 8 && mysql->net.read_pos[0] == 254) { - sql_print_error("Slave: received 0 length packet from server, apparent\ + sql_print_error("Slave: received end packet from server, apparent\ master shutdown: %s", mysql_error(mysql)); return packet_error; @@ -2337,13 +2489,126 @@ point. If you are sure that your master is ok, run this query manually on the\ } } +/* + Check if condition stated in UNTIL clause of START SLAVE is reached. + SYNOPSYS + st_relay_log_info::is_until_satisfied() + DESCRIPTION + Checks if UNTIL condition is reached. Uses caching result of last + comparison of current log file name and target log file name. So cached + value should be invalidated if current log file name changes + (see st_relay_log_info::notify_... functions). + + This caching is needed to avoid of expensive string comparisons and + strtol() conversions needed for log names comparison. We don't need to + compare them each time this function is called, we only need to do this + when current log name changes. If we have UNTIL_MASTER_POS condition we + need to do this only after Rotate_log_event::exec_event() (which is + rare, so caching gives real benifit), and if we have UNTIL_RELAY_POS + condition then we should invalidate cached comarison value after + inc_group_relay_log_pos() which called for each group of events (so we + have some benefit if we have something like queries that use + autoincrement or if we have transactions). + + Should be called ONLY if until_condition != UNTIL_NONE ! + RETURN VALUE + true - condition met or error happened (condition seems to have + bad log file name) + false - condition not met +*/ + +bool st_relay_log_info::is_until_satisfied() +{ + const char *log_name; + ulonglong log_pos; + + DBUG_ASSERT(until_condition != UNTIL_NONE); + + if (until_condition == UNTIL_MASTER_POS) + { + log_name= group_master_log_name; + log_pos= group_master_log_pos; + } + else + { /* until_condition == UNTIL_RELAY_POS */ + log_name= group_relay_log_name; + log_pos= group_relay_log_pos; + } + + if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN) + { + /* + We have no cached comaprison results so we should compare log names + and cache result + */ + + DBUG_ASSERT(*log_name || log_pos == 0); + + if (*log_name) + { + const char *basename= log_name + dirname_length(log_name); + + const char *q= (const char*)(fn_ext(basename)+1); + if (strncmp(basename, until_log_name, (int)(q-basename)) == 0) + { + /* Now compare extensions. */ + char *q_end; + ulong log_name_extension= strtoul(q, &q_end, 10); + if (log_name_extension < until_log_name_extension) + until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_LESS; + else + until_log_names_cmp_result= + (log_name_extension > until_log_name_extension) ? + UNTIL_LOG_NAMES_CMP_GREATER : UNTIL_LOG_NAMES_CMP_EQUAL ; + } + else + { + /* Probably error so we aborting */ + sql_print_error("Slave SQL thread is stopped because UNTIL " + "condition is bad."); + return true; + } + } + else + return until_log_pos == 0; + } + + return ((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL && + log_pos >= until_log_pos) || + until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER); +} + static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) { + /* + We acquire this mutex since we need it for all operations except + event execution. But we will release it in places where we will + wait for something for example inside of next_event(). + */ + pthread_mutex_lock(&rli->data_lock); + + if (rli->until_condition!=RELAY_LOG_INFO::UNTIL_NONE && + rli->is_until_satisfied()) + { + sql_print_error("Slave SQL thread stopped because it reached its" + " UNTIL position"); + /* + Setting abort_slave flag because we do not want additional message about + error in query execution to be printed. + */ + rli->abort_slave= 1; + pthread_mutex_unlock(&rli->data_lock); + return 1; + } + Log_event * ev = next_event(rli); + DBUG_ASSERT(rli->sql_thd==thd); + if (sql_slave_killed(thd,rli)) { + pthread_mutex_unlock(&rli->data_lock); delete ev; return 1; } @@ -2351,7 +2616,6 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) { int type_code = ev->get_type_code(); int exec_res; - pthread_mutex_lock(&rli->data_lock); /* Skip queries originating from this server or number of @@ -2380,7 +2644,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) pthread_mutex_unlock(&rli->data_lock); delete ev; return 0; // avoid infinite update loops - } + } pthread_mutex_unlock(&rli->data_lock); thd->server_id = ev->server_id; // use the original server id for logging @@ -2389,7 +2653,6 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) if (!ev->when) ev->when = time(NULL); ev->thd = thd; - thd->log_pos = ev->log_pos; exec_res = ev->exec_event(rli); DBUG_ASSERT(rli->sql_thd==thd); delete ev; @@ -2397,7 +2660,8 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) } else { - sql_print_error("\ + pthread_mutex_unlock(&rli->data_lock); + slave_print_error(rli, 0, "\ Could not parse relay log event entry. The possible reasons are: the master's \ binary log is corrupted (you can check this by running 'mysqlbinlog' on the \ binary log), the slave's relay log is corrupted (you can check this by running \ @@ -2472,7 +2736,7 @@ slave_begin: } - thd->proc_info = "connecting to master"; + thd->proc_info = "Connecting to master"; // we can get killed during safe_connect if (!safe_connect(thd, mysql, mi)) sql_print_error("Slave I/O thread: connected to master '%s@%s:%d',\ @@ -2519,7 +2783,7 @@ dump"); goto err; } - thd->proc_info = "Waiting to reconnect after a failed dump request"; + thd->proc_info= "Waiting to reconnect after a failed binlog dump request"; end_server(mysql); /* First time retry immediately, assuming that we can recover @@ -2540,7 +2804,7 @@ dump"); goto err; } - thd->proc_info = "Reconnecting after a failed dump request"; + thd->proc_info = "Reconnecting after a failed binlog dump request"; if (!suppress_warnings) sql_print_error("Slave I/O thread: failed dump request, \ reconnecting to try again, log '%s' at postion %s", IO_RPL_LOG_NAME, @@ -2559,7 +2823,13 @@ after reconnect"); while (!io_slave_killed(thd,mi)) { bool suppress_warnings= 0; - thd->proc_info = "Reading master update"; + /* + We say "waiting" because read_event() will wait if there's nothing to + read. But if there's something to read, it will not wait. The important + thing is to not confuse users by saying "reading" whereas we're in fact + receiving nothing. + */ + thd->proc_info = "Waiting for master to send event"; ulong event_len = read_event(mysql, mi, &suppress_warnings); if (io_slave_killed(thd,mi)) { @@ -2586,7 +2856,7 @@ max_allowed_packet", mysql_error(mysql)); goto err; } - thd->proc_info = "Waiting to reconnect after a failed read"; + thd->proc_info = "Waiting to reconnect after a failed master event read"; end_server(mysql); if (retry_count++) { @@ -2602,7 +2872,7 @@ max_allowed_packet", reconnect after a failed read"); goto err; } - thd->proc_info = "Reconnecting after a failed read"; + thd->proc_info = "Reconnecting after a failed master event read"; if (!suppress_warnings) sql_print_error("Slave I/O thread: Failed reading log event, \ reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME, @@ -2619,7 +2889,7 @@ reconnect done to recover from failed read"); } // if (event_len == packet_error) retry_count=0; // ok event, reset retry counter - thd->proc_info = "Queueing event from master"; + thd->proc_info = "Queueing master event to the relay log"; if (queue_event(mi,(const char*)mysql->net.read_pos + 1, event_len)) { @@ -2801,7 +3071,7 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, while (!sql_slave_killed(thd,rli)) { - thd->proc_info = "Processing master log event"; + thd->proc_info = "Reading event from the relay log"; DBUG_ASSERT(rli->sql_thd == thd); THD_CHECK_SENTRY(thd); if (exec_relay_log_event(thd,rli)) @@ -2833,6 +3103,11 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun /* When master_pos_wait() wakes up it will check this and terminate */ rli->slave_running= 0; + /* + Going out of the transaction. Necessary to mark it, in case the user + restarts replication from a non-transactional statement (with CHANGE + MASTER). + */ /* Wake up master_pos_wait() */ pthread_mutex_unlock(&rli->data_lock); DBUG_PRINT("info",("Signaling possibly waiting master_pos_wait() functions")); @@ -2908,7 +3183,7 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) in the loop */ { - Append_block_log_event aev(thd,0,0,0); + Append_block_log_event aev(thd,0,0,0,0); for (;;) { @@ -2921,7 +3196,7 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) if (unlikely(!num_bytes)) /* eof */ { net_write_command(net, 0, "", 0, "", 0);/* 3.23 master wants it */ - Execute_load_log_event xev(thd,0); + Execute_load_log_event xev(thd,0,0); xev.log_pos = mi->master_log_pos; if (unlikely(mi->rli.relay_log.append(&xev))) { @@ -3048,6 +3323,12 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, tmp_buf[event_len]=0; // Create_file constructor wants null-term buffer buf = (const char*)tmp_buf; } + /* + This will transform LOAD_EVENT into CREATE_FILE_EVENT, ask the master to + send the loaded file, and write it to the relay log in the form of + Append_block/Exec_load (the SQL thread needs the data, as that thread is not + connected to the master). + */ Log_event *ev = Log_event::read_log_event(buf,event_len, &errmsg, 1 /*old format*/ ); if (unlikely(!ev)) @@ -3075,6 +3356,12 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, inc_pos= 0; break; case CREATE_FILE_EVENT: + /* + Yes it's possible to have CREATE_FILE_EVENT here, even if we're in + queue_old_event() which is for 3.23 events which don't comprise + CREATE_FILE_EVENT. This is because read_log_event() above has just + transformed LOAD_EVENT into CREATE_FILE_EVENT. + */ { /* We come here when and only when tmp_buf != 0 */ DBUG_ASSERT(tmp_buf); @@ -3273,6 +3560,17 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout); mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (char *) &slave_net_timeout); + +#ifdef HAVE_OPENSSL + if (mi->ssl) + mysql_ssl_set(mysql, + mi->ssl_key[0]?mi->ssl_key:0, + mi->ssl_cert[0]?mi->ssl_cert:0, + mi->ssl_ca[0]?mi->ssl_ca:0, + mi->ssl_capath[0]?mi->ssl_capath:0, + mi->ssl_cipher[0]?mi->ssl_cipher:0); +#endif + mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset_info->csname); /* This one is not strictly needed but we have it here for completeness */ mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir); @@ -3440,17 +3738,18 @@ Log_event* next_event(RELAY_LOG_INFO* rli) pthread_mutex_t *log_lock = rli->relay_log.get_log_lock(); const char* errmsg=0; THD* thd = rli->sql_thd; + DBUG_ENTER("next_event"); DBUG_ASSERT(thd != 0); /* For most operations we need to protect rli members with data_lock, - so we will hold it for the most of the loop below - However, we will release it whenever it is worth the hassle, - and in the cases when we go into a pthread_cond_wait() with the - non-data_lock mutex + so we assume calling function acquired this mutex for us and we will + hold it for the most of the loop below However, we will release it + whenever it is worth the hassle, and in the cases when we go into a + pthread_cond_wait() with the non-data_lock mutex */ - pthread_mutex_lock(&rli->data_lock); + safe_mutex_assert_owner(&rli->data_lock); while (!sql_slave_killed(thd,rli)) { @@ -3509,7 +3808,6 @@ Before assert, my_b_tell(cur_log)=%s rli->event_relay_log_pos=%s", DBUG_ASSERT(thd==rli->sql_thd); if (hot_log) pthread_mutex_unlock(log_lock); - pthread_mutex_unlock(&rli->data_lock); DBUG_RETURN(ev); } DBUG_ASSERT(thd==rli->sql_thd); @@ -3574,7 +3872,7 @@ Before assert, my_b_tell(cur_log)=%s rli->event_relay_log_pos=%s", pthread_mutex_unlock(&rli->log_space_lock); pthread_cond_broadcast(&rli->log_space_cond); // Note that wait_for_update unlocks lock_log ! - rli->relay_log.wait_for_update(rli->sql_thd); + rli->relay_log.wait_for_update(rli->sql_thd, 1); // re-acquire data lock since we released it earlier pthread_mutex_lock(&rli->data_lock); continue; @@ -3685,7 +3983,6 @@ event(errno: %d cur_log->error: %d)", errmsg = "slave SQL thread was killed"; err: - pthread_mutex_unlock(&rli->data_lock); if (errmsg) sql_print_error("Error reading relay log event: %s", errmsg); DBUG_RETURN(0); diff --git a/sql/slave.h b/sql/slave.h index 0cd291a50f8..b52648005d3 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -32,6 +32,30 @@ *****************************************************************************/ +/* + MUTEXES in replication: + + LOCK_active_mi: this is meant for multimaster, when we can switch from a + master to another. It protects active_mi. We don't care of it for the moment, + as active_mi never moves (it's created at startup and deleted at shutdown, + and not changed: it always points to the same MASTER_INFO struct), because we + don't have multimaster. So for the moment, mi does not move, and mi->rli does + not either. + + In MASTER_INFO: run_lock, data_lock + run_lock protects all information about the run state: slave_running, and the + existence of the I/O thread (to stop/start it, you need this mutex). + data_lock protects some moving members of the struct: counters (log name, + position) and relay log (MYSQL_LOG object). + + In RELAY_LOG_INFO: run_lock, data_lock + see MASTER_INFO + + In MYSQL_LOG: LOCK_log, LOCK_index of the binlog and the relay log + LOCK_log: when you write to it. LOCK_index: when you create/delete a binlog + (so that you have to update the .index file). +*/ + extern ulong master_retry_count; extern MY_BITMAP slave_error_mask; extern bool use_slave_mask; @@ -43,10 +67,6 @@ extern my_bool opt_log_slave_updates; extern ulonglong relay_log_space_limit; struct st_master_info; -extern "C" { - extern ulong slave_net_timeout; -}; - enum enum_binlog_formats { BINLOG_FORMAT_CURRENT=0, /* 0 is important for easy 'if (mi->old_format)' */ BINLOG_FORMAT_323_LESS_57, @@ -213,9 +233,55 @@ typedef struct st_relay_log_info bool inited; volatile bool abort_slave, slave_running; + /* + Condition and its parameters from START SLAVE UNTIL clause. + + UNTIL condition is tested with is_until_satisfied() method that is + called by exec_relay_log_event(). is_until_satisfied() caches the result + of the comparison of log names because log names don't change very often; + this cache is invalidated by parts of code which change log names with + notify_*_log_name_updated() methods. (They need to be called only if SQL + thread is running). + */ + + enum {UNTIL_NONE= 0, UNTIL_MASTER_POS, UNTIL_RELAY_POS} until_condition; + char until_log_name[FN_REFLEN]; + ulonglong until_log_pos; + /* extension extracted from log_name and converted to int */ + ulong until_log_name_extension; + /* + Cached result of comparison of until_log_name and current log name + -2 means unitialised, -1,0,1 are comarison results + */ + enum + { + UNTIL_LOG_NAMES_CMP_UNKNOWN= -2, UNTIL_LOG_NAMES_CMP_LESS= -1, + UNTIL_LOG_NAMES_CMP_EQUAL= 0, UNTIL_LOG_NAMES_CMP_GREATER= 1 + } until_log_names_cmp_result; + st_relay_log_info(); ~st_relay_log_info(); + /* + Invalidate cached until_log_name and group_relay_log_name comparison + result. Should be called after any update of group_realy_log_name if + there chances that sql_thread is running. + */ + inline void notify_group_relay_log_name_update() + { + if (until_condition==UNTIL_RELAY_POS) + until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN; + } + + /* + The same as previous but for group_master_log_name. + */ + inline void notify_group_master_log_name_update() + { + if (until_condition==UNTIL_MASTER_POS) + until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN; + } + inline void inc_event_relay_log_pos(ulonglong val) { event_relay_log_pos+= val; @@ -229,6 +295,9 @@ typedef struct st_relay_log_info group_relay_log_pos= event_relay_log_pos; strmake(group_relay_log_name,event_relay_log_name, sizeof(group_relay_log_name)-1); + + notify_group_relay_log_name_update(); + /* If the slave does not support transactions and replicates a transaction, users should not trust group_master_log_pos (which they can display with @@ -248,6 +317,10 @@ typedef struct st_relay_log_info int wait_for_pos(THD* thd, String* log_name, longlong log_pos, longlong timeout); + + /* Check if UNTIL condition is satisfied. See slave.cc for more. */ + bool is_until_satisfied(); + } RELAY_LOG_INFO; @@ -288,16 +361,19 @@ Log_event* next_event(RELAY_LOG_INFO* rli); typedef struct st_master_info { + /* the variables below are needed because we can change masters on the fly */ char master_log_name[FN_REFLEN]; char host[HOSTNAME_LENGTH+1]; char user[USERNAME_LENGTH+1]; char password[MAX_PASSWORD_LENGTH+1]; + my_bool ssl; // enables use of SSL connection if true + char ssl_ca[FN_REFLEN], ssl_capath[FN_REFLEN], ssl_cert[FN_REFLEN]; + char ssl_cipher[FN_REFLEN], ssl_key[FN_REFLEN]; my_off_t master_log_pos; File fd; // we keep the file open, so we need to remember the file pointer IO_CACHE file; - /* the variables below are needed because we can change masters on the fly */ pthread_mutex_t data_lock,run_lock; pthread_cond_t data_cond,start_cond,stop_cond; THD *io_thd; @@ -315,10 +391,13 @@ typedef struct st_master_info volatile ulong slave_run_id; st_master_info() - :fd(-1), io_thd(0), inited(0), old_format(BINLOG_FORMAT_CURRENT), + :ssl(0), fd(-1), io_thd(0), inited(0), old_format(BINLOG_FORMAT_CURRENT), abort_slave(0),slave_running(0), slave_run_id(0) { host[0] = 0; user[0] = 0; password[0] = 0; + ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0; + ssl_cipher[0]= 0; ssl_key[0]= 0; + bzero((char*) &file, sizeof(file)); pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); @@ -431,6 +510,7 @@ void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...); void end_slave(); /* clean up */ void init_master_info_with_options(MASTER_INFO* mi); +void clear_until_condition(RELAY_LOG_INFO* rli); void clear_last_slave_error(RELAY_LOG_INFO* rli); int init_master_info(MASTER_INFO* mi, const char* master_info_fname, const char* slave_info_fname, @@ -468,6 +548,10 @@ extern my_string master_user, master_password, master_host, master_info_file, relay_log_info_file, report_user, report_host, report_password; +extern my_bool master_ssl; +extern my_string master_ssl_ca, master_ssl_capath, master_ssl_cert, + master_ssl_cipher, master_ssl_key; + extern I_List<i_string> replicate_do_db, replicate_ignore_db; extern I_List<i_string_pair> replicate_rewrite_db; extern I_List<THD> threads; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 9738c0c5d9e..179da6096de 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -34,6 +34,7 @@ #include <m_ctype.h> #include <stdarg.h> +#ifndef NO_EMBEDDED_ACCESS_CHECKS class acl_entry :public hash_filo_element { @@ -51,7 +52,7 @@ static byte* acl_entry_get_key(acl_entry *entry,uint *length, return (byte*) entry->key; } -#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+17) +#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+USERNAME_LENGTH+1) static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs; static MEM_ROOT mem, memex; @@ -68,12 +69,54 @@ static ulong get_sort(uint count,...); static void init_check_host(void); static ACL_USER *find_acl_user(const char *host, const char *user); static bool update_user_table(THD *thd, const char *host, const char *user, - const char *new_password); + const char *new_password, uint new_password_len); static void update_hostname(acl_host_and_ip *host, const char *hostname); static bool compare_hostname(const acl_host_and_ip *host,const char *hostname, const char *ip); /* + Convert scrambled password to binary form, according to scramble type, + Binary form is stored in user.salt. +*/ + +static +void +set_user_salt(ACL_USER *acl_user, const char *password, uint password_len) +{ + if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH) + { + get_salt_from_password(acl_user->salt, password); + acl_user->salt_len= SCRAMBLE_LENGTH; + } + else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + { + get_salt_from_password_323((ulong *) acl_user->salt, password); + acl_user->salt_len= SCRAMBLE_LENGTH_323; + } + else + acl_user->salt_len= 0; +} + +/* + This after_update function is used when user.password is less than + SCRAMBLE_LENGTH bytes. +*/ + +static void restrict_update_of_old_passwords_var(THD *thd, + enum_var_type var_type) +{ + if (var_type == OPT_GLOBAL) + { + pthread_mutex_lock(&LOCK_global_system_variables); + global_system_variables.old_passwords= 1; + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + thd->variables.old_passwords= 1; +} + + +/* Read grant privileges from the privilege tables in the 'mysql' database. SYNOPSIS @@ -114,8 +157,6 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) if (!(thd=new THD)) DBUG_RETURN(1); /* purecov: inspected */ thd->store_globals(); - /* Use passwords according to command line option */ - use_old_passwords= opt_old_passwords; acl_cache->clear(1); // Clear locked hostname cache thd->db= my_strdup("mysql",MYF(0)); @@ -172,103 +213,126 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0); VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100)); - if (table->field[2]->field_length == 8 && - protocol_version == PROTOCOL_VERSION) + if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) { - sql_print_error("Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */ - protocol_version=9; /* purecov: tested */ + sql_print_error("Fatal error: mysql.user table is damaged or in " + "unsupported 3.20 format."); + goto end; } DBUG_PRINT("info",("user table fields: %d, password length: %d", table->fields, table->field[2]->field_length)); - if (table->field[2]->field_length < 45 && !use_old_passwords) + + pthread_mutex_lock(&LOCK_global_system_variables); + if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH) + { + if (opt_secure_auth) + { + pthread_mutex_unlock(&LOCK_global_system_variables); + sql_print_error("Fatal error: mysql.user table is in old format, " + "but server started with --secure-auth option."); + goto end; + } + sys_old_passwords.after_update= restrict_update_of_old_passwords_var; + if (global_system_variables.old_passwords) + pthread_mutex_unlock(&LOCK_global_system_variables); + else + { + global_system_variables.old_passwords= 1; + pthread_mutex_unlock(&LOCK_global_system_variables); + sql_print_error("mysql.user table is not updated to new password format; " + "Disabling new password usage until " + "mysql_fix_privilege_tables is run"); + } + thd->variables.old_passwords= 1; + } + else { - sql_print_error("mysql.user table is not updated to new password format; Disabling new password usage until mysql_fix_privilege_tables is run"); - use_old_passwords= 1; + sys_old_passwords.after_update= 0; + pthread_mutex_unlock(&LOCK_global_system_variables); } allow_all_hosts=0; while (!(read_record_info.read_record(&read_record_info))) { ACL_USER user; - uint length=0; - update_hostname(&user.host,get_field(&mem, table->field[0])); - user.user=get_field(&mem, table->field[1]); - user.password=get_field(&mem, table->field[2]); - if (user.password && (length=(uint) strlen(user.password)) == 8 && - protocol_version == PROTOCOL_VERSION) - { - sql_print_error( - "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)", - user.user ? user.user : ""); /* purecov: tested */ - } - else /* non empty and not short passwords */ - { - user.pversion=get_password_version(user.password); - /* Only passwords of specific lengths depending on version are allowed */ - if ((!user.pversion && (length % 8 || length > 16)) || - (user.pversion && length != 45)) - { - sql_print_error( - "Found invalid password for user: '%s'@'%s'; Ignoring user", - user.user ? user.user : "", - user.host.hostname ? user.host.hostname : ""); /* purecov: tested */ - continue; /* purecov: tested */ + update_hostname(&user.host, get_field(&mem, table->field[0])); + user.user= get_field(&mem, table->field[1]); + const char *password= get_field(&mem, table->field[2]); + uint password_len= password ? strlen(password) : 0; + set_user_salt(&user, password, password_len); + if (user.salt_len == 0 && password_len != 0) + { + switch (password_len) { + case 45: /* 4.1: to be removed */ + sql_print_error("Found 4.1 style password for user '%s@%s'. " + "Ignoring user. " + "You should change password for this user.", + user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); + break; + default: + sql_print_error("Found invalid password for user: '%s@%s'; " + "Ignoring user", user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); + break; } } - get_salt_from_password(user.salt,user.password); - user.access=get_access(table,3) & GLOBAL_ACLS; - user.sort=get_sort(2,user.host.hostname,user.user); - user.hostname_length= (user.host.hostname ? - (uint) strlen(user.host.hostname) : 0); - if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */ - { - char *ssl_type=get_field(&mem, table->field[24]); - if (!ssl_type) - user.ssl_type=SSL_TYPE_NONE; - else if (!strcmp(ssl_type, "ANY")) - user.ssl_type=SSL_TYPE_ANY; - else if (!strcmp(ssl_type, "X509")) - user.ssl_type=SSL_TYPE_X509; - else /* !strcmp(ssl_type, "SPECIFIED") */ - user.ssl_type=SSL_TYPE_SPECIFIED; - - user.ssl_cipher= get_field(&mem, table->field[25]); - user.x509_issuer= get_field(&mem, table->field[26]); - user.x509_subject= get_field(&mem, table->field[27]); - - char *ptr = get_field(&mem, table->field[28]); - user.user_resource.questions=atoi(ptr); - ptr = get_field(&mem, table->field[29]); - user.user_resource.updates=atoi(ptr); - ptr = get_field(&mem, table->field[30]); - user.user_resource.connections=atoi(ptr); - if (user.user_resource.questions || user.user_resource.updates || - user.user_resource.connections) - mqh_used=1; - } - else + else // password is correct { - user.ssl_type=SSL_TYPE_NONE; - bzero((char *)&(user.user_resource),sizeof(user.user_resource)); -#ifndef TO_BE_REMOVED - if (table->fields <= 13) - { // Without grant - if (user.access & CREATE_ACL) - user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; + user.access= get_access(table,3) & GLOBAL_ACLS; + user.sort= get_sort(2,user.host.hostname,user.user); + user.hostname_length= (user.host.hostname ? + (uint) strlen(user.host.hostname) : 0); + if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */ + { + char *ssl_type=get_field(&mem, table->field[24]); + if (!ssl_type) + user.ssl_type=SSL_TYPE_NONE; + else if (!strcmp(ssl_type, "ANY")) + user.ssl_type=SSL_TYPE_ANY; + else if (!strcmp(ssl_type, "X509")) + user.ssl_type=SSL_TYPE_X509; + else /* !strcmp(ssl_type, "SPECIFIED") */ + user.ssl_type=SSL_TYPE_SPECIFIED; + + user.ssl_cipher= get_field(&mem, table->field[25]); + user.x509_issuer= get_field(&mem, table->field[26]); + user.x509_subject= get_field(&mem, table->field[27]); + + char *ptr = get_field(&mem, table->field[28]); + user.user_resource.questions=atoi(ptr); + ptr = get_field(&mem, table->field[29]); + user.user_resource.updates=atoi(ptr); + ptr = get_field(&mem, table->field[30]); + user.user_resource.connections=atoi(ptr); + if (user.user_resource.questions || user.user_resource.updates || + user.user_resource.connections) + mqh_used=1; } - /* Convert old privileges */ - user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL; - if (user.access & FILE_ACL) - user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL; - if (user.access & PROCESS_ACL) - user.access|= SUPER_ACL | EXECUTE_ACL; + else + { + user.ssl_type=SSL_TYPE_NONE; + bzero((char *)&(user.user_resource),sizeof(user.user_resource)); +#ifndef TO_BE_REMOVED + if (table->fields <= 13) + { // Without grant + if (user.access & CREATE_ACL) + user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; + } + /* Convert old privileges */ + user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL; + if (user.access & FILE_ACL) + user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL; + if (user.access & PROCESS_ACL) + user.access|= SUPER_ACL | EXECUTE_ACL; #endif + } + VOID(push_dynamic(&acl_users,(gptr) &user)); + if (!user.host.hostname || user.host.hostname[0] == wild_many && + !user.host.hostname[1]) + allow_all_hosts=1; // Anyone can connect } - VOID(push_dynamic(&acl_users,(gptr) &user)); - if (!user.host.hostname || user.host.hostname[0] == wild_many && - !user.host.hostname[1]) - allow_all_hosts=1; // Anyone can connect } qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, sizeof(ACL_USER),(qsort_cmp) acl_compare); @@ -469,136 +533,109 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) /* - Prepare crypted scramble to be sent to the client -*/ - -void prepare_scramble(THD *thd, ACL_USER *acl_user,char* prepared_scramble) -{ - /* Binary password format to be used for generation*/ - char bin_password[SCRAMBLE41_LENGTH]; - /* Generate new long scramble for the thread */ - create_random_string(SCRAMBLE41_LENGTH,&thd->rand,thd->scramble); - thd->scramble[SCRAMBLE41_LENGTH]=0; - /* Get binary form, First 4 bytes of prepared scramble is salt */ - get_hash_and_password(acl_user->salt,acl_user->pversion,prepared_scramble, - (unsigned char*) bin_password); - /* Store "*" as identifier for old passwords */ - if (!acl_user->pversion) - prepared_scramble[0]='*'; - /* Finally encrypt password to get prepared scramble */ - password_crypt(thd->scramble, prepared_scramble+4, bin_password, - SCRAMBLE41_LENGTH); -} - - -/* - Get master privilges for user (priviliges for all tables). - Required before connecting to MySQL + Seek ACL entry for a user, check password, SSL cypher, and if + everything is OK, update THD user data and USER_RESOURCES struct. - As we have 2 stage handshake now we cache user not to lookup - it second time. At the second stage we do not lookup user in case - we already know it; + IMPLEMENTATION + This function does not check if the user has any sensible privileges: + only user's existence and validity is checked. + Note, that entire operation is protected by acl_cache_lock. + SYNOPSIS + acl_getroot() + thd thread handle. If all checks are OK, + thd->priv_user, thd->master_access are updated. + thd->host, thd->ip, thd->user are used for checks. + mqh user resources; on success mqh is reset, else + unchanged + passwd scrambled & crypted password, recieved from client + (to check): thd->scramble or thd->scramble_323 is + used to decrypt passwd, so they must contain + original random string, + passwd_len length of passwd, must be one of 0, 8, + SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH + 'thd' and 'mqh' are updated on success; other params are IN. + + RETURN VALUE + 0 success: thd->priv_user, thd->priv_host, thd->master_access, mqh are + updated + 1 user not found or authentification failure + 2 user found, has long (4.1.1) salt, but passwd is in old (3.23) format. + -1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format. */ -ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, - const char *password,const char *message,char **priv_user, - char *priv_host, bool old_ver, USER_RESOURCES *mqh, - char *prepared_scramble, uint *cur_priv_version, - ACL_USER **cached_user) +int acl_getroot(THD *thd, USER_RESOURCES *mqh, + const char *passwd, uint passwd_len) { - ulong user_access=NO_ACCESS; - *priv_user= (char*) user; - bool password_correct= 0; - int stage= (*cached_user != NULL); /* NULL passed as first stage */ - ACL_USER *acl_user= NULL; + ulong user_access= NO_ACCESS; + int res= 1; + ACL_USER *acl_user= 0; DBUG_ENTER("acl_getroot"); - bzero((char*) mqh, sizeof(USER_RESOURCES)); if (!initialized) { - // If no data allow anything - DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */ + /* + here if mysqld's been started with --skip-grant-tables option. + */ + thd->priv_user= (char *) ""; // privileges for + *thd->priv_host= '\0'; // the user are unknown + thd->master_access= ~NO_ACCESS; // everything is allowed + bzero((char*) mqh, sizeof(*mqh)); + DBUG_RETURN(0); } + VOID(pthread_mutex_lock(&acl_cache->lock)); /* - Get possible access from user_list. This is or'ed to others not - fully specified - - If we have cached user use it, in other case look it up. + Find acl entry in user database. Note, that find_acl_user is not the same, + because it doesn't take into account the case when user is not empty, + but acl_user->user is empty */ - if (stage && (*cur_priv_version == priv_version)) - acl_user= *cached_user; - else + for (uint i=0 ; i < acl_users.elements ; i++) { - for (uint i=0 ; i < acl_users.elements ; i++) + ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*); + if (!acl_user_tmp->user || !strcmp(thd->user, acl_user_tmp->user)) { - ACL_USER *acl_user_search=dynamic_element(&acl_users,i,ACL_USER*); - if (!acl_user_search->user || !strcmp(user,acl_user_search->user)) + if (compare_hostname(&acl_user_tmp->host, thd->host, thd->ip)) { - if (compare_hostname(&acl_user_search->host,host,ip)) + /* check password: it should be empty or valid */ + if (passwd_len == acl_user_tmp->salt_len) { - /* Found mathing user */ - acl_user= acl_user_search; - /* Store it as a cache */ - *cached_user= acl_user; - *cur_priv_version= priv_version; - break; + if (acl_user_tmp->salt_len == 0 || + acl_user_tmp->salt_len == SCRAMBLE_LENGTH && + check_scramble(passwd, thd->scramble, acl_user_tmp->salt) == 0 || + check_scramble_323(passwd, thd->scramble, + (ulong *) acl_user_tmp->salt) == 0) + { + acl_user= acl_user_tmp; + res= 0; + } } + else if (passwd_len == SCRAMBLE_LENGTH && + acl_user_tmp->salt_len == SCRAMBLE_LENGTH_323) + res= -1; + else if (passwd_len == SCRAMBLE_LENGTH_323 && + acl_user_tmp->salt_len == SCRAMBLE_LENGTH) + res= 2; + /* linear search complete: */ + break; } } } - - /* Now we have acl_user found and may start our checks */ + /* + This was moved to separate tree because of heavy HAVE_OPENSSL case. + If acl_user is not null, res is 0. + */ if (acl_user) { - /* Password should present for both or absend for both */ - if (!acl_user->password && !*password) - password_correct=1; - else if (!acl_user->password || !*password) - { - *cached_user= 0; // Impossible to connect - } - else - { - /* New version password is checked differently */ - if (acl_user->pversion) - { - if (stage) /* We check password only on the second stage */ - { - if (!validate_password(password,message,acl_user->salt)) - password_correct=1; - } - else /* First stage - just prepare scramble */ - prepare_scramble(thd,acl_user,prepared_scramble); - } - /* Old way to check password */ - else - { - /* Checking the scramble at any stage. First - old clients */ - if (!check_scramble(password,message,acl_user->salt, - (my_bool) old_ver)) - password_correct=1; - else if (!stage) /* Here if password incorrect */ - { - /* At the first stage - prepare scramble */ - prepare_scramble(thd,acl_user,prepared_scramble); - } - } - } - } - - /* If user not found password_correct will also be zero */ - if (!password_correct) - goto unlock_and_exit; - - /* OK. User found and password checked continue validation */ - - { + /* OK. User found and password checked continue validation */ Vio *vio=thd->net.vio; +#ifdef HAVE_OPENSSL + SSL *ssl= (SSL*) vio->ssl_arg; +#endif + /* At this point we know that user is allowed to connect from given host by given username/password pair. Now @@ -607,26 +644,26 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, */ switch (acl_user->ssl_type) { case SSL_TYPE_NOT_SPECIFIED: // Impossible - case SSL_TYPE_NONE: /* SSL is not required to connect */ - user_access=acl_user->access; + case SSL_TYPE_NONE: // SSL is not required + user_access= acl_user->access; break; #ifdef HAVE_OPENSSL - case SSL_TYPE_ANY: /* Any kind of SSL is good enough */ + case SSL_TYPE_ANY: // Any kind of SSL is ok if (vio_type(vio) == VIO_TYPE_SSL) - user_access=acl_user->access; + user_access= acl_user->access; break; case SSL_TYPE_X509: /* Client should have any valid certificate. */ /* Connections with non-valid certificates are dropped already in sslaccept() anyway, so we do not check validity here. - + We need to check for absence of SSL because without SSL we should reject connection. */ if (vio_type(vio) == VIO_TYPE_SSL && - SSL_get_verify_result(vio->ssl_) == X509_V_OK && - SSL_get_peer_certificate(vio->ssl_)) - user_access=acl_user->access; + SSL_get_verify_result(ssl) == X509_V_OK && + SSL_get_peer_certificate(ssl)) + user_access= acl_user->access; break; case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ /* @@ -636,89 +673,83 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, use. */ if (vio_type(vio) != VIO_TYPE_SSL || - SSL_get_verify_result(vio->ssl_) != X509_V_OK) + SSL_get_verify_result(ssl) != X509_V_OK) break; if (acl_user->ssl_cipher) { DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", - acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_))); - if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_))) - user_access=acl_user->access; + acl_user->ssl_cipher,SSL_get_cipher(ssl))); + if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl))) + user_access= acl_user->access; else { if (global_system_variables.log_warnings) sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'", acl_user->ssl_cipher, - SSL_get_cipher(vio->ssl_)); - user_access=NO_ACCESS; + SSL_get_cipher(ssl)); break; } } /* Prepare certificate (if exists) */ DBUG_PRINT("info",("checkpoint 1")); - X509* cert=SSL_get_peer_certificate(vio->ssl_); + X509* cert=SSL_get_peer_certificate(ssl); DBUG_PRINT("info",("checkpoint 2")); /* If X509 issuer is speified, we check it... */ if (acl_user->x509_issuer) { - DBUG_PRINT("info",("checkpoint 3")); + DBUG_PRINT("info",("checkpoint 3")); char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", acl_user->x509_issuer, ptr)); - if (strcmp(acl_user->x509_issuer, ptr)) - { - if (global_system_variables.log_warnings) - sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'", - acl_user->x509_issuer, ptr); - user_access=NO_ACCESS; - free(ptr); - break; - } - user_access=acl_user->access; - free(ptr); + if (strcmp(acl_user->x509_issuer, ptr)) + { + if (global_system_variables.log_warnings) + sql_print_error("X509 issuer mismatch: should be '%s' " + "but is '%s'", acl_user->x509_issuer, ptr); + free(ptr); + break; + } + user_access= acl_user->access; + free(ptr); } DBUG_PRINT("info",("checkpoint 4")); /* X509 subject is specified, we check it .. */ if (acl_user->x509_subject) { - char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); - DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", - acl_user->x509_subject, ptr)); - if (strcmp(acl_user->x509_subject,ptr)) - { - if (global_system_variables.log_warnings) - sql_print_error("X509 subject mismatch: '%s' vs '%s'", - acl_user->x509_subject, ptr); - user_access=NO_ACCESS; - } - else - user_access=acl_user->access; - free(ptr); + char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); + DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", + acl_user->x509_subject, ptr)); + if (strcmp(acl_user->x509_subject,ptr)) + { + if (global_system_variables.log_warnings) + sql_print_error("X509 subject mismatch: '%s' vs '%s'", + acl_user->x509_subject, ptr); + } + else + user_access= acl_user->access; + free(ptr); } break; -#else /* HAVE_OPENSSL */ +#else /* HAVE_OPENSSL */ default: /* - If we don't have SSL but SSL is required for this user the - authentication should fail. - */ + If we don't have SSL but SSL is required for this user the + authentication should fail. + */ break; #endif /* HAVE_OPENSSL */ } - } - - *mqh=acl_user->user_resource; - if (!acl_user->user) - *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */ + thd->master_access= user_access; + thd->priv_user= acl_user->user ? thd->user : (char *) ""; + *mqh= acl_user->user_resource; - if (acl_user->host.hostname) - strmake(priv_host, acl_user->host.hostname, MAX_HOSTNAME); - else - *priv_host= 0; - -unlock_and_exit: + if (acl_user->host.hostname) + strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME); + else + *thd->priv_host= 0; + } VOID(pthread_mutex_unlock(&acl_cache->lock)); - DBUG_RETURN(user_access); + DBUG_RETURN(res); } @@ -729,8 +760,9 @@ static byte* check_get_key(ACL_USER *buff,uint *length, return (byte*) buff->host.hostname; } + static void acl_update_user(const char *user, const char *host, - const char *password, + const char *password, uint password_len, enum SSL_type ssl_type, const char *ssl_cipher, const char *x509_issuer, @@ -766,20 +798,9 @@ static void acl_update_user(const char *user, const char *host, acl_user->x509_subject= (x509_subject ? strdup_root(&mem,x509_subject) : 0); } - if (password) - { - if (!password[0]) /* If password is empty set it to null */ - { - acl_user->password=0; - acl_user->pversion=0; // just initialize - } - else - { - acl_user->password=(char*) ""; // Just point at something - get_salt_from_password(acl_user->salt,password); - acl_user->pversion=get_password_version(acl_user->password); - } - } + + set_user_salt(acl_user, password, password_len); + /* search complete: */ break; } } @@ -788,7 +809,7 @@ static void acl_update_user(const char *user, const char *host, static void acl_insert_user(const char *user, const char *host, - const char *password, + const char *password, uint password_len, enum SSL_type ssl_type, const char *ssl_cipher, const char *x509_issuer, @@ -799,7 +820,6 @@ static void acl_insert_user(const char *user, const char *host, ACL_USER acl_user; acl_user.user=*user ? strdup_root(&mem,user) : 0; update_hostname(&acl_user.host,strdup_root(&mem,host)); - acl_user.password=0; acl_user.access=privileges; acl_user.user_resource = *mqh; acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user); @@ -809,12 +829,8 @@ static void acl_insert_user(const char *user, const char *host, acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0; acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0; acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0; - if (password) - { - acl_user.password=(char*) ""; // Just point at something - get_salt_from_password(acl_user.salt,password); - acl_user.pversion=get_password_version(password); - } + + set_user_salt(&acl_user, password, password_len); VOID(push_dynamic(&acl_users,(gptr) &acl_user)); if (!acl_user.host.hostname || acl_user.host.hostname[0] == wild_many @@ -970,51 +986,6 @@ exit: return (db_access & host_access); } - -int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr) -{ - reg3 int flag; - DBUG_ENTER("wild_case_compare"); - DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr)); - while (*wildstr) - { - while (*wildstr && *wildstr != wild_many && *wildstr != wild_one) - { - if (*wildstr == wild_prefix && wildstr[1]) - wildstr++; - if (my_toupper(cs, *wildstr++) != - my_toupper(cs, *str++)) DBUG_RETURN(1); - } - if (! *wildstr ) DBUG_RETURN (*str != 0); - if (*wildstr++ == wild_one) - { - if (! *str++) DBUG_RETURN (1); /* One char; skip */ - } - else - { /* Found '*' */ - if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */ - flag=(*wildstr != wild_many && *wildstr != wild_one); - do - { - if (flag) - { - char cmp; - if ((cmp= *wildstr) == wild_prefix && wildstr[1]) - cmp=wildstr[1]; - cmp=my_toupper(cs, cmp); - while (*str && my_toupper(cs, *str) != cmp) - str++; - if (!*str) DBUG_RETURN (1); - } - if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0); - } while (*str++); - DBUG_RETURN(1); - } - } - DBUG_RETURN (*str != '\0'); -} - - /* Check if there are any possible matching entries for this host @@ -1054,7 +1025,7 @@ static void init_check_host(void) else if (!hash_search(&acl_check_hosts,(byte*) &acl_user->host, (uint) strlen(acl_user->host.hostname))) { - if (hash_insert(&acl_check_hosts,(byte*) acl_user)) + if (my_hash_insert(&acl_check_hosts,(byte*) acl_user)) { // End of memory allow_all_hosts=1; // Should never happen DBUG_VOID_RETURN; @@ -1121,7 +1092,7 @@ bool check_change_password(THD *thd, const char *host, const char *user) (strcmp(thd->user,user) || my_strcasecmp(&my_charset_latin1, host, thd->host_or_ip))) { - if (check_access(thd, UPDATE_ACL, "mysql",0,1)) + if (check_access(thd, UPDATE_ACL, "mysql",0,1,0)) return(1); } if (!thd->slave_thread && !thd->user[0]) @@ -1151,7 +1122,6 @@ bool check_change_password(THD *thd, const char *host, const char *user) bool change_password(THD *thd, const char *host, const char *user, char *new_password) { - uint length=0; DBUG_ENTER("change_password"); DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'", host,user,new_password)); @@ -1160,37 +1130,27 @@ bool change_password(THD *thd, const char *host, const char *user, if (check_change_password(thd, host, user)) DBUG_RETURN(1); - /* - password should always be 0,16 or 45 chars; - Simple hack to avoid cracking - */ - length=(uint) strlen(new_password); - if (length != 45) - new_password[length & 16]=0; - VOID(pthread_mutex_lock(&acl_cache->lock)); ACL_USER *acl_user; - if (!(acl_user= find_acl_user(host,user))) + if (!(acl_user= find_acl_user(host, user))) { - send_error(thd, ER_PASSWORD_NO_MATCH); VOID(pthread_mutex_unlock(&acl_cache->lock)); + send_error(thd, ER_PASSWORD_NO_MATCH); DBUG_RETURN(1); } + /* update loaded acl entry: */ + uint new_password_len= new_password ? strlen(new_password) : 0; + set_user_salt(acl_user, new_password, new_password_len); + if (update_user_table(thd, acl_user->host.hostname ? acl_user->host.hostname : "", acl_user->user ? acl_user->user : "", - new_password)) + new_password, new_password_len)) { VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */ send_error(thd,0); /* purecov: deadcode */ DBUG_RETURN(1); /* purecov: deadcode */ } - get_salt_from_password(acl_user->salt,new_password); - acl_user->pversion=get_password_version(new_password); - if (!new_password[0]) - acl_user->password=0; - else - acl_user->password=(char*) ""; // Point at something acl_cache->clear(1); // Clear locked hostname cache VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -1230,7 +1190,7 @@ find_acl_user(const char *host, const char *user) if (!acl_user->user && !user[0] || acl_user->user && !strcmp(user,acl_user->user)) { - if (compare_hostname(&(acl_user->host),host,host)) + if (compare_hostname(&acl_user->host,host,host)) { DBUG_RETURN(acl_user); } @@ -1303,7 +1263,7 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, */ static bool update_user_table(THD *thd, const char *host, const char *user, - const char *new_password) + const char *new_password, uint new_password_len) { TABLE_LIST tables; TABLE *table; @@ -1345,7 +1305,7 @@ static bool update_user_table(THD *thd, const char *host, const char *user, DBUG_RETURN(1); /* purecov: deadcode */ } store_record(table,record[1]); - table->field[2]->store(new_password,(uint) strlen(new_password), &my_charset_latin1); + table->field[2]->store(new_password, new_password_len, &my_charset_latin1); if ((error=table->file->update_row(table->record[1],table->record[0]))) { table->file->print_error(error,MYF(0)); /* purecov: deadcode */ @@ -1393,24 +1353,23 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, { int error = -1; bool old_row_exists=0; - char *password,empty_string[1]; + const char *password= ""; + uint password_len= 0; char what= (revoke_grant) ? 'N' : 'Y'; DBUG_ENTER("replace_user_table"); safe_mutex_assert_owner(&acl_cache->lock); - password=empty_string; - empty_string[0]=0; - if (combo.password.str && combo.password.str[0]) { - if ((combo.password.length != HASH_PASSWORD_LENGTH) - && combo.password.length != HASH_OLD_PASSWORD_LENGTH) + if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH && + combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323) { my_printf_error(ER_PASSWORD_NO_MATCH, - "Password hash should be a %d-digit hexadecimal number", - MYF(0),HASH_PASSWORD_LENGTH); + "Password hash should be a %d-digit hexadecimal number", + MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH); DBUG_RETURN(-1); } + password_len= combo.password.length; password=combo.password.str; } @@ -1435,17 +1394,20 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, goto end; } old_row_exists = 0; - restore_record(table,default_values); // cp empty row from default_values - table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1); - table->field[1]->store(combo.user.str,combo.user.length, &my_charset_latin1); - table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1); + restore_record(table,default_values); // cp empty row from default_values + table->field[0]->store(combo.host.str,combo.host.length, + &my_charset_latin1); + table->field[1]->store(combo.user.str,combo.user.length, + &my_charset_latin1); + table->field[2]->store(password, password_len, + &my_charset_latin1); } else { old_row_exists = 1; store_record(table,record[1]); // Save copy for update if (combo.password.str) // If password given - table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1); + table->field[2]->store(password, password_len, &my_charset_latin1); } /* Update table columns with new privileges */ @@ -1542,10 +1504,8 @@ end: if (!error) { acl_cache->clear(1); // Clear privilege cache - if (!combo.password.str) - password=0; // No password given on command if (old_row_exists) - acl_update_user(combo.user.str,combo.host.str,password, + acl_update_user(combo.user.str, combo.host.str, password, password_len, thd->lex.ssl_type, thd->lex.ssl_cipher, thd->lex.x509_issuer, @@ -1553,7 +1513,7 @@ end: &thd->lex.mqh, rights); else - acl_insert_user(combo.user.str,combo.host.str,password, + acl_insert_user(combo.user.str, combo.host.str, password, password_len, thd->lex.ssl_type, thd->lex.ssl_cipher, thd->lex.x509_issuer, @@ -1686,6 +1646,7 @@ class GRANT_TABLE :public Sql_alloc public: char *host,*db,*user,*tname, *hash_key; ulong privs, cols; + ulong sort; uint key_length; HASH hash_columns; GRANT_TABLE (const char *h, const char *d,const char *u, const char *t, @@ -1695,6 +1656,7 @@ public: host = strdup_root(&memex,h); db = strdup_root(&memex,d); user = strdup_root(&memex,u); + sort= get_sort(3,host,db,user); tname= strdup_root(&memex,t); if (lower_case_table_names) { @@ -1717,7 +1679,8 @@ public: user = get_field(&memex,form->field[2]); if (!user) user=(char*) ""; - tname = get_field(&memex,form->field[3]); + sort= get_sort(3,host,db,user); + tname= get_field(&memex,form->field[3]); if (!host || !db || !tname) { /* Wrong table row; Ignore it */ @@ -1775,7 +1738,7 @@ public: privs = cols = 0; /* purecov: deadcode */ return; /* purecov: deadcode */ } - hash_insert(&hash_columns, (byte *) mem_check); + my_hash_insert(&hash_columns, (byte *) mem_check); } while (!col_privs->file->index_next(col_privs->record[0]) && !key_cmp(col_privs,key,0,key_len)); } @@ -1826,10 +1789,11 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, } else { - if ((host && !wild_case_compare(&my_charset_latin1, - host,grant_table->host)) || - (ip && !wild_case_compare(&my_charset_latin1, - ip,grant_table->host))) + if (((host && !wild_case_compare(&my_charset_latin1, + host,grant_table->host)) || + (ip && !wild_case_compare(&my_charset_latin1, + ip,grant_table->host))) && + (!found || found->sort < grant_table->sort)) found=grant_table; // Host ok } } @@ -1936,7 +1900,7 @@ static int replace_column_table(GRANT_TABLE *g_t, goto end; /* purecov: inspected */ } GRANT_COLUMN *grant_column = new GRANT_COLUMN(xx->column,privileges); - hash_insert(&g_t->hash_columns,(byte*) grant_column); + my_hash_insert(&g_t->hash_columns,(byte*) grant_column); } } table->file->index_end(); @@ -2287,7 +2251,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, result= -1; /* purecov: deadcode */ continue; /* purecov: deadcode */ } - hash_insert(&column_priv_hash,(byte*) grant_table); + my_hash_insert(&column_priv_hash,(byte*) grant_table); } /* If revoke_grant, calculate the new column privilege for tables_priv */ @@ -2530,7 +2494,7 @@ my_bool grant_init(THD *org_thd) { GRANT_TABLE *mem_check; if (!(mem_check=new GRANT_TABLE(t_table,c_table)) || - mem_check->ok() && hash_insert(&column_priv_hash,(byte*) mem_check)) + mem_check->ok() && my_hash_insert(&column_priv_hash,(byte*) mem_check)) { /* This could only happen if we are out memory */ grant_option= FALSE; /* purecov: deadcode */ @@ -3024,12 +2988,15 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) global.append ("'@'",3); global.append(lex_user->host.str,lex_user->host.length); global.append ('\''); - if (acl_user->password) + if (acl_user->salt_len) { - char passd_buff[HASH_PASSWORD_LENGTH+1]; - make_password_from_salt(passd_buff,acl_user->salt,acl_user->pversion); + char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1]; + if (acl_user->salt_len == SCRAMBLE_LENGTH) + make_password_from_salt(passwd_buff, acl_user->salt); + else + make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt); global.append(" IDENTIFIED BY PASSWORD '",25); - global.append(passd_buff); + global.append(passwd_buff); global.append('\''); } /* "show grants" SSL related stuff */ @@ -3083,7 +3050,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) protocol->store(global.ptr(),global.length(),global.charset()); if (protocol->write()) { - error=-1; + error= -1; goto end; } } @@ -3141,7 +3108,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) protocol->store(db.ptr(),db.length(),db.charset()); if (protocol->write()) { - error=-1; + error= -1; goto end; } } @@ -3167,15 +3134,19 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) if ((table_access | grant_table->cols) != 0) { String global(buff,sizeof(buff),&my_charset_latin1); + ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL; + global.length(0); global.append("GRANT ",6); if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL))) global.append("ALL PRIVILEGES",14); + else if (!test_access) + global.append("USAGE",5); else { int found= 0; - ulong j,test_access= (table_access | grant_table->cols) & ~GRANT_ACL; + ulong j; for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1) { @@ -3342,7 +3313,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) } ACL_USER *check_acl_user(LEX_USER *user_name, - uint *acl_user_idx) + uint *acl_acl_userdx) { ACL_USER *acl_user= 0; uint counter; @@ -3362,14 +3333,14 @@ ACL_USER *check_acl_user(LEX_USER *user_name, if (counter == acl_users.elements) return 0; - *acl_user_idx= counter; + *acl_acl_userdx= counter; return acl_user; } int mysql_drop_user(THD *thd, List <LEX_USER> &list) { - uint counter, user_id; + uint counter, acl_userd; int result; ACL_USER *acl_user; ACL_DB *acl_db; @@ -3389,7 +3360,7 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list) { if (!(acl_user= check_acl_user(user_name, &counter))) { - sql_print_error("DROP USER: Can't drop user: '%s'@'%s'", + sql_print_error("DROP USER: Can't drop user: '%s'@'%s'; No such user", user_name->user.str, user_name->host.str); result= -1; @@ -3397,13 +3368,13 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list) } if ((acl_user->access & ~0)) { - sql_print_error("DROP USER: Can't drop user: '%s'@'%s'", + sql_print_error("DROP USER: Can't drop user: '%s'@'%s'; Global privileges exists", user_name->user.str, user_name->host.str); result= -1; continue; } - user_id= counter; + acl_userd= counter; for (counter= 0 ; counter < acl_dbs.elements ; counter++) { @@ -3420,7 +3391,7 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list) } if (counter != acl_dbs.elements) { - sql_print_error("DROP USER: Can't drop user: '%s'@'%s'", + sql_print_error("DROP USER: Can't drop user: '%s'@'%s'; Database privileges exists", user_name->user.str, user_name->host.str); result= -1; @@ -3443,7 +3414,7 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list) } if (counter != column_priv_hash.records) { - sql_print_error("DROP USER: Can't drop user: '%s'@'%s'", + sql_print_error("DROP USER: Can't drop user: '%s'@'%s'; Table privileges exists", user_name->user.str, user_name->host.str); result= -1; @@ -3469,7 +3440,7 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list) tables[0].table->file->index_end(); DBUG_RETURN(-1); } - delete_dynamic_element(&acl_users, user_id); + delete_dynamic_element(&acl_users, acl_userd); } tables[0].table->file->index_end(); } @@ -3590,3 +3561,50 @@ template class List_iterator<LEX_USER>; template class List<LEX_COLUMN>; template class List<LEX_USER>; #endif + +#endif /*NO_EMBEDDED_ACCESS_CHECKS */ + + +int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr) +{ + reg3 int flag; + DBUG_ENTER("wild_case_compare"); + DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr)); + while (*wildstr) + { + while (*wildstr && *wildstr != wild_many && *wildstr != wild_one) + { + if (*wildstr == wild_prefix && wildstr[1]) + wildstr++; + if (my_toupper(cs, *wildstr++) != + my_toupper(cs, *str++)) DBUG_RETURN(1); + } + if (! *wildstr ) DBUG_RETURN (*str != 0); + if (*wildstr++ == wild_one) + { + if (! *str++) DBUG_RETURN (1); /* One char; skip */ + } + else + { /* Found '*' */ + if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */ + flag=(*wildstr != wild_many && *wildstr != wild_one); + do + { + if (flag) + { + char cmp; + if ((cmp= *wildstr) == wild_prefix && wildstr[1]) + cmp=wildstr[1]; + cmp=my_toupper(cs, cmp); + while (*str && my_toupper(cs, *str) != cmp) + str++; + if (!*str) DBUG_RETURN (1); + } + if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0); + } while (*str++); + DBUG_RETURN(1); + } + } + DBUG_RETURN (*str != '\0'); +} + diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 0dc8c058b3d..220eb0d55ad 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -14,7 +14,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #define SELECT_ACL (1L << 0) #define INSERT_ACL (1L << 1) #define UPDATE_ACL (1L << 2) @@ -59,6 +58,8 @@ #define EXTRA_ACL (1L << 29) #define NO_ACCESS (1L << 30) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* Defines to change the above bits to how things are stored in tables This is needed as the 'host' and 'db' table is missing a few privileges @@ -111,9 +112,9 @@ public: acl_host_and_ip host; uint hostname_length; USER_RESOURCES user_resource; - char *user,*password; - ulong salt[6]; // New password has longer length - uint8 pversion; // password version + char *user; + uint8 salt[SCRAMBLE_LENGTH+1]; // scrambled password in binary form + uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 3.23, 20 - 4.1.1 enum SSL_type ssl_type; const char *ssl_cipher, *x509_issuer, *x509_subject; }; @@ -135,11 +136,8 @@ void acl_reload(THD *thd); void acl_free(bool end=0); ulong acl_get(const char *host, const char *ip, const char *bin_ip, const char *user, const char *db, my_bool db_is_pattern); -ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, - const char *password,const char *scramble, - char **priv_user, char *priv_host, - bool old_ver, USER_RESOURCES *max,char* prepared_scramble, - uint *cur_priv_version, ACL_USER **cached_user); +int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd, + uint passwd_len); bool acl_check_host(const char *host, const char *ip); bool check_change_password(THD *thd, const char *host, const char *user); bool change_password(THD *thd, const char *host, const char *user, @@ -165,3 +163,6 @@ void get_privilege_desc(char *to, uint max_length, ulong access); void get_mqh(const char *user, const char *host, USER_CONN *uc); int mysql_drop_user(THD *thd, List <LEX_USER> &list); int mysql_revoke_all(THD *thd, List <LEX_USER> &list); + +#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ + diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2b23b622cc6..4b4e4e7eb8e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -158,7 +158,6 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) table_list.grant.privilege=0; if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1)) continue; - /* need to check if we haven't already listed it */ for (table= open_list ; table ; table=table->next) { @@ -873,7 +872,7 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, table->version=refresh_version; table->flush_version=flush_version; DBUG_PRINT("info", ("inserting table %p into the cache", table)); - VOID(hash_insert(&open_cache,(byte*) table)); + VOID(my_hash_insert(&open_cache,(byte*) table)); } table->in_use=thd; @@ -1696,8 +1695,10 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, else thd->dupp_field=field; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_grants && check_grant_column(thd,table,name,length)) return WRONG_GRANT; +#endif return field; } @@ -2114,11 +2115,12 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, tables->alias) && (!db_name || !strcmp(tables->db,db_name)))) { +#ifndef NO_EMBEDDED_ACCESS_CHECKS /* Ensure that we have access right to all columns */ if (!(table->grant.privilege & SELECT_ACL) && check_grant_all_columns(thd,SELECT_ACL,table)) DBUG_RETURN(-1); - +#endif Field **ptr=table->field,*field; thd->used_tables|=table->map; while ((field = *ptr++)) @@ -2343,7 +2345,7 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) DBUG_ENTER("mysql_create_index"); bzero((char*) &create_info,sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; - create_info.table_charset= thd->variables.character_set_database; + create_info.table_charset= thd->variables.collation_database; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, fields, keys, drop, alter, 0, (ORDER*)0, FALSE, @@ -2360,7 +2362,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop) DBUG_ENTER("mysql_drop_index"); bzero((char*) &create_info,sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; - create_info.table_charset= thd->variables.character_set_database; + create_info.table_charset= thd->variables.collation_database; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, fields, keys, drop, alter, 0, (ORDER*)0, FALSE, diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 64e8be8e224..069887e689e 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -765,13 +765,30 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) &tables_type))) { NET *net= &thd->net; - byte flags= (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); + Query_cache_query_flags flags; + // fill all gaps between fields with 0 to get repeatable key + bzero(&flags, QUERY_CACHE_FLAGS_SIZE); + flags.client_long_flag= (thd->client_capabilities & CLIENT_LONG_FLAG ? + 1 : 0); + flags.character_set_client_num= thd->variables.character_set_client->number; + flags.character_set_results_num= thd->variables.character_set_results->number; + flags.collation_connection_num= thd->variables.collation_connection->number; + flags.limit= thd->variables.select_limit; STRUCT_LOCK(&structure_guard_mutex); if (query_cache_size == 0) + { + STRUCT_UNLOCK(&structure_guard_mutex); DBUG_VOID_RETURN; + } DUMP(this); + if (ask_handler_allowance(thd, tables_used)) + { + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; + } + /* Key is query + database + flag */ if (thd->db_length) { @@ -783,23 +800,19 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) { DBUG_PRINT("qcache", ("No active database")); } + tot_length= thd->query_length + thd->db_length + 1 + + QUERY_CACHE_FLAGS_SIZE; /* - Prepare flags: - most significant bit - CLIENT_LONG_FLAG, - other - charset number (0 no charset convertion) + We should only copy structure (don't use it location directly) + because of alignment issue */ - flags|= (byte) thd->charset()->number; - DBUG_ASSERT(thd->charset()->number < 128); - tot_length= thd->query_length+thd->db_length+2+sizeof(ha_rows); - thd->query[tot_length-1]= (char) flags; - memcpy((void *)(thd->query + (tot_length-sizeof(ha_rows)-1)), - (const void *)&thd->variables.select_limit, sizeof(ha_rows)); + memcpy((void *)(thd->query + (tot_length - QUERY_CACHE_FLAGS_SIZE)), + &flags, QUERY_CACHE_FLAGS_SIZE); /* Check if another thread is processing the same query? */ Query_cache_block *competitor = (Query_cache_block *) hash_search(&queries, (byte*) thd->query, tot_length); - DBUG_PRINT("qcache", ("competitor 0x%lx, flags %x", (ulong) competitor, - flags)); + DBUG_PRINT("qcache", ("competitor 0x%lx", (ulong) competitor)); if (competitor == 0) { /* Query is not in cache and no one is working with it; Store it */ @@ -814,7 +827,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) Query_cache_query *header = query_block->query(); header->init_n_lock(); - if (hash_insert(&queries, (byte*) query_block)) + if (my_hash_insert(&queries, (byte*) query_block)) { refused++; DBUG_PRINT("qcache", ("insertion in query hash")); @@ -886,7 +899,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) Query_cache_block *first_result_block, *result_block; Query_cache_block_table *block_table, *block_table_end; ulong tot_length; - byte flags; + Query_cache_query_flags flags; bool check_tables; DBUG_ENTER("Query_cache::send_result_to_client"); @@ -923,7 +936,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) } Query_cache_block *query_block; - tot_length= query_length+thd->db_length+2+sizeof(ha_rows); + tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE; if (thd->db_length) { memcpy(sql+query_length+1, thd->db, thd->db_length); @@ -934,17 +947,17 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) { DBUG_PRINT("qcache", ("No active database")); } - /* - prepare flags: - Most significant bit - CLIENT_LONG_FLAG, - Other - charset number (0 no charset convertion) - */ - flags= (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); - flags|= (byte) thd->charset()->number; - DBUG_ASSERT(thd->charset()->number < 128); - sql[tot_length-1]= (char) flags; - memcpy((void *)(sql + (tot_length-sizeof(ha_rows)-1)), - (const void *)&thd->variables.select_limit, sizeof(ha_rows)); + + // fill all gaps between fields with 0 to get repeatable key + bzero(&flags, QUERY_CACHE_FLAGS_SIZE); + flags.client_long_flag= (thd->client_capabilities & CLIENT_LONG_FLAG ? + 1 : 0); + flags.character_set_client_num= thd->variables.character_set_client->number; + flags.character_set_results_num= thd->variables.character_set_results->number; + flags.collation_connection_num= thd->variables.collation_connection->number; + flags.limit= thd->variables.select_limit; + memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)), + &flags, QUERY_CACHE_FLAGS_SIZE); query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql, tot_length); /* Quick abort on unlocked data */ @@ -993,6 +1006,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) Query_cache_table *table = block_table->parent; table_list.db = table->db(); table_list.alias= table_list.real_name= table->table(); +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_table_access(thd,SELECT_ACL,&table_list,1)) { DBUG_PRINT("qcache", @@ -1012,6 +1026,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) thd->lex.safe_to_cache_query= 0; // Don't try to cache this goto err_unlock; // Parse query } +#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ if (check_tables && !handler::caching_allowed(thd, table->db(), table->key_length(), table->type())) @@ -2035,7 +2050,7 @@ Query_cache::insert_table(uint key_len, char *key, Query_cache_block_table *list_root = table_block->table(0); list_root->n = 0; list_root->next = list_root->prev = list_root; - if (hash_insert(&tables, (const byte *) table_block)) + if (my_hash_insert(&tables, (const byte *) table_block)) { DBUG_PRINT("qcache", ("Can't insert table to hash")); // write_block_data return locked block @@ -2548,6 +2563,39 @@ TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, DBUG_RETURN(0); } +/* + Check handler allowence to cache query with this tables + + SYNOPSYS + Query_cache::ask_handler_allowance() + thd - thread handlers + tables_used - tables list used in query + + RETURN + 0 - caching allowed + 1 - caching disallowed +*/ +my_bool Query_cache::ask_handler_allowance(THD *thd, + TABLE_LIST *tables_used) +{ + DBUG_ENTER("Query_cache::is_cacheable"); + + for (; tables_used; tables_used= tables_used->next) + { + TABLE *table= tables_used->table; + if (!handler::caching_allowed(thd, table->table_cache_key, + table->key_length, + table->file->table_cache_type())) + { + DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s", + tables_used->db, tables_used->alias)); + thd->lex.safe_to_cache_query= 0; // Don't try to cache this + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + /***************************************************************************** Packing @@ -3055,20 +3103,21 @@ void Query_cache::queries_dump() { uint len; char *str = (char*) query_cache_query_get_key((byte*) block, &len, 0); - len--; // Point at flags - uint flags = (uint) (uchar) str[len]; - str[len]=0; - DBUG_PRINT("qcache", ("%u (%u,%u) '%s' '%s'", - ((flags & QUERY_CACHE_CLIENT_LONG_FLAG_MASK)? 1:0), - (flags & QUERY_CACHE_CHARSET_CONVERT_MASK), len, - str,strend(str)+1)); + len-= QUERY_CACHE_FLAGS_SIZE; // Point at flags + Query_cache_query_flags flags; + memcpy(&flags, str+len, QUERY_CACHE_FLAGS_SIZE); + str[len]= 0; // make zero ending DB name + DBUG_PRINT("qcache", ("F:%u C:%u L:%lu (%u) '%s' '%s'", + flags.client_long_flag, + flags.character_set_client_num, (ulong)flags.limit, + len, str, strend(str)+1)); DBUG_PRINT("qcache", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong) block, (ulong) block->next, (ulong) block->prev, (ulong)block->pnext, (ulong)block->pprev)); - str[len]=(char) flags; - for (TABLE_COUNTER_TYPE t = 0; t < block->n_tables; t++) + memcpy(str + len, &flags, QUERY_CACHE_FLAGS_SIZE); // restore flags + for (TABLE_COUNTER_TYPE t= 0; t < block->n_tables; t++) { - Query_cache_table *table = block->table(t)->parent; + Query_cache_table *table= block->table(t)->parent; DBUG_PRINT("qcache", ("-t- '%s' '%s'", table->db(), table->table())); } Query_cache_query *header = block->query(); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index f6eb7c7a0fb..68e69ab523f 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -345,6 +345,8 @@ protected: TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex, TABLE_LIST *tables_used, uint8 *tables_type); + + static my_bool ask_handler_allowance(THD *thd, TABLE_LIST *tables_used); public: Query_cache(ulong query_cache_limit = ULONG_MAX, diff --git a/sql/sql_class.cc b/sql/sql_class.cc index c57934f32cd..ed4f2be16ec 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -107,7 +107,7 @@ THD::THD():user_time(0), is_fatal_error(0), variables.pseudo_thread_id= 0; file_id = 0; warn_id= 0; - db_charset= global_system_variables.character_set_database; + db_charset= global_system_variables.collation_database; mysys_var=0; #ifndef DBUG_OFF dbug_sentry=THD_SENTRY_MAGIC; @@ -133,11 +133,13 @@ THD::THD():user_time(0), is_fatal_error(0), where="field list"; server_id = ::server_id; slave_net = 0; - log_pos = 0; command=COM_CONNECT; set_query_id=1; +#ifndef NO_EMBEDDED_ACCESS_CHECKS db_access=NO_ACCESS; +#endif version=refresh_version; // For boot + *scramble= '\0'; init(); /* Initialize sub structures */ @@ -316,7 +318,7 @@ THD::~THD() #endif DBUG_PRINT("info", ("freeing host")); - if (host != localhost) // If not pointer to constant + if (host != my_localhost) // If not pointer to constant safeFree(host); if (user != delayed_user) safeFree(user); @@ -583,7 +585,7 @@ sql_exchange::sql_exchange(char *name,bool flag) :file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0) { field_term= &default_field_term; - enclosed= line_start= &empty_string; + enclosed= line_start= &my_empty_string; line_term= &default_line_term; escaped= &default_escaped; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 3de909ee384..4fb12523086 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -129,7 +129,7 @@ public: } void set_max_size(ulong max_size_arg); void signal_update() { pthread_cond_broadcast(&update_cond);} - void wait_for_update(THD* thd); + void wait_for_update(THD* thd, bool master_or_slave); void set_need_start_event() { need_start_event = 1; } void init(enum_log_type log_type_arg, enum cache_type io_cache_type_arg, @@ -146,7 +146,7 @@ public: bool write(THD *thd, const char *query, uint query_length, time_t query_start=0); bool write(Log_event* event_info); // binary log write - bool write(THD *thd, IO_CACHE *cache); + bool write(THD *thd, IO_CACHE *cache, bool commit_or_rollback); /* v stands for vector @@ -341,7 +341,11 @@ typedef struct st_prep_stmt char last_error[MYSQL_ERRMSG_SIZE]; bool error_in_prepare, long_data_used; bool log_full_query; +#ifndef EMBEDDED_LIBRARY bool (*setup_params)(st_prep_stmt *stmt, uchar *pos, uchar *read_pos); +#else + bool (*setup_params_data)(st_prep_stmt *stmt); +#endif } PREP_STMT; @@ -385,6 +389,7 @@ struct system_variables ulong table_type; ulong tmp_table_size; ulong tx_isolation; + /* Determines if which non-standard SQL behaviour should be enabled */ ulong sql_mode; ulong default_week_format; ulong max_seeks_for_key; @@ -398,11 +403,15 @@ struct system_variables my_bool log_warnings; my_bool low_priority_updates; my_bool new_mode; + my_bool old_passwords; - CHARSET_INFO *character_set_server; - CHARSET_INFO *character_set_database; + /* Only charset part of these variables is sensible */ CHARSET_INFO *character_set_client; CHARSET_INFO *character_set_results; + + /* Both charset and collation parts of these variables are important */ + CHARSET_INFO *collation_server; + CHARSET_INFO *collation_database; CHARSET_INFO *collation_connection; }; @@ -417,6 +426,12 @@ class THD :public ilink public: #ifdef EMBEDDED_LIBRARY struct st_mysql *mysql; + struct st_mysql_data *data; + unsigned long client_stmt_id; + unsigned long client_param_count; + struct st_mysql_bind *client_params; + char *extra_data; + ulong extra_length; #endif NET net; // client connection descriptor LEX lex; // parse tree descriptor @@ -459,7 +474,6 @@ public: const char *host_or_ip; ulong client_capabilities; /* What the client supports */ - /* Determines if which non-standard SQL behaviour should be enabled */ ulong max_client_packet_length; ulong master_access; /* Global privileges from mysql.user */ ulong db_access; /* Privileges for current db */ @@ -556,11 +570,10 @@ public: enum_tx_isolation session_tx_isolation; /* for user variables replication*/ DYNAMIC_ARRAY user_var_events; - // extend scramble to handle new auth - char scramble[SCRAMBLE41_LENGTH+1]; - // old scramble is needed to handle old clients - char old_scramble[SCRAMBLE_LENGTH+1]; - uint8 query_cache_type; // type of query cache processing + + /* scramble - random string sent to client on handshake */ + char scramble[SCRAMBLE_LENGTH+1]; + bool slave_thread; bool set_query_id,locked,count_cuted_fields,some_tables_deleted; bool last_cuted_field; @@ -580,7 +593,6 @@ public: */ LOG_INFO* current_linfo; NET* slave_net; // network connection from slave -> m. - my_off_t log_pos; /* Used by the sys_var class to store temporary values */ union { @@ -981,7 +993,6 @@ typedef struct st_sort_buffer { SORT_FIELD *sortorder; } SORT_BUFFER; - /* Structure for db & table in sql_yacc */ class Table_ident :public Sql_alloc diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc index 930ecfffef7..b0b8050e311 100644 --- a/sql/sql_crypt.cc +++ b/sql/sql_crypt.cc @@ -32,7 +32,7 @@ SQL_CRYPT::SQL_CRYPT(const char *password) { ulong rand_nr[2]; - hash_password(rand_nr,password); + hash_password(rand_nr,password, strlen(password)); crypt_init(rand_nr); } diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 34e81402dd0..750dd4ff493 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -62,7 +62,7 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) ulong length; CHARSET_INFO *cs= (create && create->table_charset) ? create->table_charset : - thd->variables.character_set_database; + thd->variables.collation_database; length= my_sprintf(buf,(buf, "default-character-set=%s\ndefault-collation=%s\n", cs->csname,cs->name)); /* Error is written by my_write */ @@ -99,7 +99,7 @@ static bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) uint nbytes; bzero((char*) create,sizeof(*create)); - create->table_charset= global_system_variables.character_set_database; + create->table_charset= global_system_variables.collation_database; if ((file=my_open(path, O_RDONLY | O_SHARE, MYF(0))) >= 0) { IO_CACHE cache; @@ -288,8 +288,8 @@ int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) { thd->db_charset= (create_info && create_info->table_charset) ? create_info->table_charset : - global_system_variables.character_set_database; - thd->variables.character_set_database= thd->db_charset; + global_system_variables.collation_database; + thd->variables.collation_database= thd->db_charset; } mysql_update_log.write(thd,thd->query, thd->query_length); @@ -391,6 +391,33 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) exit: start_waiting_global_read_lock(thd); + /* + If this database was the client's selected database, we silently change the + client's selected database to nothing (to have an empty SELECT DATABASE() in + the future). For this we free() thd->db and set it to 0. But we don't do + free() for the slave thread. Indeed, doing a x_free() on it leads to nasty + problems (i.e. long painful debugging) because in this thread, thd->db is + the same as data_buf and db of the Query_log_event which is dropping the + database. So if you free() thd->db, you're freeing data_buf. You set thd->db + to 0 but not data_buf (thd->db and data_buf are two distinct pointers which + point to the same place). Then in ~Query_log_event(), we have + 'if (data_buf) free(data_buf)' + data_buf is !=0 so this makes a DOUBLE free(). + Side effects of this double free() are, randomly (depends on the machine), + when the slave is replicating a DROP DATABASE: + - garbage characters in the error message: + "Error 'Can't drop database 'test2'; database doesn't exist' on query + 'h4zI¿'" + - segfault + - hang in "free(vio)" (yes!) in the I/O or SQL slave threads (so slave + server hangs at shutdown etc). + */ + if (thd->db && !strcmp(thd->db, db)) + { + if (!(thd->slave_thread)) /* a slave thread will free it itself */ + x_free(thd->db); + thd->db= 0; + } exit2: VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); @@ -587,6 +614,7 @@ bool mysql_change_db(THD *thd, const char *name) DBUG_RETURN(1); } DBUG_PRINT("info",("Use database: %s", dbname)); +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (test_all_bits(thd->master_access,DB_ACLS)) db_access=DB_ACLS; else @@ -606,7 +634,7 @@ bool mysql_change_db(THD *thd, const char *name) my_free(dbname,MYF(0)); DBUG_RETURN(1); } - +#endif (void) sprintf(path,"%s/%s",mysql_data_home,dbname); length=unpack_dirname(path,path); // Convert if not unix if (length && path[length-1] == FN_LIBCHAR) @@ -621,14 +649,15 @@ bool mysql_change_db(THD *thd, const char *name) x_free(thd->db); thd->db=dbname; // THD::~THD will free this thd->db_length=db_length; +#ifndef NO_EMBEDDED_ACCESS_CHECKS thd->db_access=db_access; - +#endif strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); load_db_opt(thd, path, &create); thd->db_charset= create.table_charset ? create.table_charset : - global_system_variables.character_set_database; - thd->variables.character_set_database= thd->db_charset; + global_system_variables.collation_database; + thd->variables.collation_database= thd->db_charset; DBUG_RETURN(0); } @@ -651,6 +680,7 @@ int mysqld_show_create_db(THD *thd, char *dbname, DBUG_RETURN(1); } +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (test_all_bits(thd->master_access,DB_ACLS)) db_access=DB_ACLS; else @@ -669,6 +699,7 @@ int mysqld_show_create_db(THD *thd, char *dbname, dbname); DBUG_RETURN(1); } +#endif (void) sprintf(path,"%s/%s",mysql_data_home, dbname); length=unpack_dirname(path,path); // Convert if not unix diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index fcbb1bcfdeb..13d3cc27376 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -25,8 +25,6 @@ #include "sql_select.h" #include "sql_acl.h" -extern const char *any_db; // Special symbol for check_access - /* Resolve derived tables in all queries @@ -91,13 +89,15 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, We have to do access checks here as this code is executed before any sql command is started to execute. */ +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (tables) - res= check_table_access(thd,SELECT_ACL, tables); + res= check_table_access(thd,SELECT_ACL, tables,0); else - res= check_access(thd, SELECT_ACL, any_db); + res= check_access(thd, SELECT_ACL, any_db,0,0,0); if (res) - DBUG_RETURN(-1); - + DBUG_RETURN(1); +#endif + if (!(res=open_and_lock_tables(thd,tables))) { if (is_union || is_subsel) @@ -206,7 +206,9 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, org_table_list->table=table; table->derived_select_number= select_cursor->select_number; table->tmp_table= TMP_TABLE; +#ifndef NO_EMBEDDED_ACCESS_CHECKS org_table_list->grant.privilege= SELECT_ACL; +#endif if (lex->describe) { // to fix a problem in EXPLAIN diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 947205949f1..af28d48ed8a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -60,9 +60,11 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, MYF(0),counter); return -1; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (grant_option && check_grant_all_columns(thd,INSERT_ACL,table)) return -1; +#endif table->time_stamp=0; // This is saved by caller } else @@ -96,7 +98,9 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, table->time_stamp= table->timestamp_field->offset()+1; } // For the values we need select_priv +#ifndef NO_EMBEDDED_ACCESS_CHECKS table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); +#endif return 0; } @@ -130,14 +134,15 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, thd->lex.select_lex.table_list.first; DBUG_ENTER("mysql_insert"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (thd->master_access & SUPER_ACL) +#endif { if (!(thd->options & OPTION_UPDATE_LOG)) log_on&= ~(int) DELAYED_LOG_UPDATE; if (!(thd->options & OPTION_BIN_LOG)) log_on&= ~(int) DELAYED_LOG_BIN; } - /* in safe mode or with skip-new change delayed insert to be regular if we are told to replace duplicates, the insert cannot be concurrent @@ -172,7 +177,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, else res= (table == 0); else + { + lock_type=TL_WRITE; res= open_and_lock_tables(thd, table_list); + } } else res= open_and_lock_tables(thd, table_list); @@ -623,10 +631,11 @@ public: group_count(0) { thd.user=thd.priv_user=(char*) delayed_user; - thd.host=(char*) localhost; + thd.host=(char*) my_localhost; thd.current_tablenr=0; thd.version=refresh_version; thd.command=COM_DELAYED_INSERT; + thd.lex.current_select= 0; /* for my_message_sql */ bzero((char*) &thd.net,sizeof(thd.net)); // Safety thd.system_thread=1; @@ -1448,6 +1457,8 @@ void select_insert::send_error(uint errcode,const char *err) table->file->has_transactions()); mysql_bin_log.write(&qinfo); } + if (!table->tmp_table) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } if (info.copied || info.deleted) query_cache_invalidate3(thd, table, 1); @@ -1468,7 +1479,11 @@ bool select_insert::send_eof() */ if (info.copied || info.deleted) + { query_cache_invalidate3(thd, table, 1); + if (!(table->file->has_transactions() || table->tmp_table)) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; + } if (last_insert_id) thd->insert_id(last_insert_id); // For binary log @@ -1559,8 +1574,6 @@ bool select_create::send_data(List<Item> &values) return 0; } -extern HASH open_cache; - bool select_create::send_eof() { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d6cfd555c40..80d698dfc26 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -982,6 +982,7 @@ void st_select_lex::init_query() cond_count= with_wild= 0; ref_pointer_array= 0; select_n_having_items= 0; + prep_where= 0; } void st_select_lex::init_select() diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7a7071ae56b..8ce028020c2 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -60,7 +60,7 @@ enum enum_sql_command { SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT, SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION, SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, SQLCOM_PRELOAD_KEYS, - SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE, + SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE, SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT, SQLCOM_COMMIT, SQLCOM_SAVEPOINT, SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP, @@ -73,7 +73,7 @@ enum enum_sql_command { SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO, SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_TABLE_TYPES, SQLCOM_SHOW_PRIVILEGES, - SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, + SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, /* This should be the last !!! */ SQLCOM_END @@ -87,6 +87,13 @@ typedef struct st_lex_master_info uint port, connect_retry; ulonglong pos; ulong server_id; + /* + Variable for MASTER_SSL option. + MASTER_SSL=0 in CHANGE MASTER TO corresponds to SSL_DISABLE + MASTER_SSL=1 corresponds to SSL_ENABLE + */ + enum {SSL_UNCHANGED=0, SSL_DISABLE, SSL_ENABLE} ssl; + char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher; char *relay_log_name; ulong relay_log_pos; } LEX_MASTER_INFO; @@ -272,8 +279,8 @@ protected: select_result *result; int res; - bool describe, found_rows_for_union, - prepared, // prepare phase already performed for UNION (unit) + ulong describe, found_rows_for_union; + bool prepared, // prepare phase already performed for UNION (unit) optimized, // optimize phase already performed for UNION (unit) executed, // already executed t_and_f; // used for transferring tables_and_fields_initied UNIT:: methods @@ -332,6 +339,7 @@ class st_select_lex: public st_select_lex_node public: char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */ Item *where, *having; /* WHERE & HAVING clauses */ + Item *prep_where; /* saved WHERE clause for prepared statement processing */ enum olap_type olap; SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */ List<Item> item_list; /* list of fields & expressions */ @@ -472,15 +480,22 @@ typedef struct st_lex SELECT_LEX *all_selects_list; uchar *ptr,*tok_start,*tok_end,*end_of_query; char *length,*dec,*change,*name; + char *help_arg; char *backup_dir; /* For RESTORE/BACKUP */ char* to_log; /* For PURGE MASTER LOGS TO */ time_t purge_time; /* For PURGE MASTER LOGS BEFORE */ char* x509_subject,*x509_issuer,*ssl_cipher; char* found_colon; /* For multi queries - next query */ - enum SSL_type ssl_type; /* defined in violite.h */ String *wild; sql_exchange *exchange; select_result *result; + Item *default_value; + LEX_STRING *comment; + LEX_USER *grant_user; + gptr yacc_yyss,yacc_yyvs; + THD *thd; + CHARSET_INFO *charset; + SQL_LIST *gorder_list; List<key_part_spec> col_list; List<key_part_spec> ref_list; @@ -499,11 +514,6 @@ typedef struct st_lex TYPELIB *interval; create_field *last_field; char *savepoint_name; // Transaction savepoint id - Item *default_value, *comment; - uint uint_geom_type; - LEX_USER *grant_user; - gptr yacc_yyss,yacc_yyvs; - THD *thd; udf_func udf; HA_CHECK_OPT check_opt; // check/repair options HA_CREATE_INFO create_info; @@ -512,6 +522,7 @@ typedef struct st_lex ulong thread_id,type; enum_sql_command sql_command; thr_lock_type lock_option; + enum SSL_type ssl_type; /* defined in violite.h */ enum my_lex_states next_state; enum enum_duplicates duplicates; enum enum_tx_isolation tx_isolation; @@ -519,17 +530,15 @@ typedef struct st_lex enum ha_rkey_function ha_rkey_mode; enum enum_enable_or_disable alter_keys_onoff; enum enum_var_type option_type; + uint uint_geom_type; uint grant, grant_tot_col, which_columns; uint fk_delete_opt, fk_update_opt, fk_match_option; uint param_count; + uint slave_thd_opt; bool drop_primary, drop_if_exists, drop_temporary, local_file; bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog; bool derived_tables, describe; bool safe_to_cache_query; - uint slave_thd_opt; - CHARSET_INFO *charset; - char *help_arg; - SQL_LIST *gorder_list; st_lex() {} inline void uncacheable() { diff --git a/sql/sql_load.cc b/sql/sql_load.cc index d030eaf617c..60e0a7c7e94 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -77,9 +77,6 @@ static int read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, List<Item> &fields, READ_INFO &read_info, String &enclosed); - -#ifndef EMBEDDED_LIBRARY - int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, List<Item> &fields, enum enum_duplicates handle_duplicates, bool read_file_from_client,thr_lock_type lock_type) @@ -91,7 +88,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, String *field_term=ex->field_term,*escaped=ex->escaped, *enclosed=ex->enclosed; bool is_fifo=0; +#ifndef EMBEDDED_LIBRARY LOAD_FILE_INFO lf_info; +#endif char *db = table_list->db; // This is never null /* If no current database, use database where table is located */ char *tdb= thd->db ? thd->db : db; @@ -171,7 +170,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, else #endif { - read_file_from_client=0; #ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS ex->file_name+=dirname_length(ex->file_name); #endif @@ -184,6 +182,17 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } else { +#ifdef EMBEDDED_LIBRARY + char *chk_name= ex->file_name; + while ((*chk_name == ' ') || (*chk_name == 't')) + chk_name++; + if (*chk_name == FN_CURLIB) + { + sprintf(name, "%s%s", mysql_data_home, ex->file_name); + unpack_filename(name, name); + } + else +#endif /*EMBEDDED_LIBRARY*/ unpack_filename(name,ex->file_name); #if !defined(__WIN__) && !defined(OS2) && ! defined(__NETWARE__) MY_STAT stat_info; @@ -215,7 +224,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, info.handle_duplicates=handle_duplicates; info.escape_char=escaped->length() ? (*escaped)[0] : INT_MAX; - READ_INFO read_info(file,tot_length,thd->variables.character_set_database, + READ_INFO read_info(file,tot_length,thd->variables.collation_database, *field_term,*ex->line_start, *ex->line_term, *enclosed, info.escape_char, read_file_from_client, is_fifo); if (read_info.error) @@ -225,6 +234,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, DBUG_RETURN(-1); // Can't allocate buffers } +#ifndef EMBEDDED_LIBRARY if (!opt_old_rpl_compat && mysql_bin_log.is_open()) { lf_info.thd = thd; @@ -238,6 +248,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, lf_info.log_delayed= log_delayed; read_info.set_io_cache_arg((void*) &lf_info); } +#endif /*!EMBEDDED_LIBRARY*/ + restore_record(table,default_values); thd->count_cuted_fields=1; /* calc cuted fields */ @@ -293,6 +305,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { if (transactional_table) ha_autocommit_or_rollback(thd,error); +#ifndef EMBEDDED_LIBRARY if (!opt_old_rpl_compat && mysql_bin_log.is_open()) { if (lf_info.wrote_create_file) @@ -311,10 +324,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, which is nonsense. */ read_info.end_io_cache(); - Delete_file_log_event d(thd, log_delayed); + Delete_file_log_event d(thd, db, log_delayed); mysql_bin_log.write(&d); } } +#endif /*!EMBEDDED_LIBRARY*/ error= -1; // Error on read goto err; } @@ -327,6 +341,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (!log_delayed) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; +#ifndef EMBEDDED_LIBRARY if (mysql_bin_log.is_open()) { if (opt_old_rpl_compat) @@ -343,11 +358,12 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, read_info.end_io_cache(); // make sure last block gets logged if (lf_info.wrote_create_file) { - Execute_load_log_event e(thd, log_delayed); + Execute_load_log_event e(thd, db, log_delayed); mysql_bin_log.write(&e); } } } +#endif /*!EMBEDDED_LIBRARY*/ if (transactional_table) error=ha_autocommit_or_rollback(thd,error); err: @@ -359,8 +375,6 @@ err: DBUG_RETURN(error); } -#endif /* EMBEDDED_LIBRARY */ - /**************************************************************************** ** Read of rows of fixed size + optional garage + optonal newline ****************************************************************************/ @@ -640,11 +654,12 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, mysys/mf_iocache.c. So we work around the problem with a manual assignment */ + need_end_io_cache = 1; + +#ifndef EMBEDDED_LIBRARY if (get_it_from_net) cache.read_function = _my_b_net_read; - need_end_io_cache = 1; -#ifndef EMBEDDED_LIBRARY if (!opt_old_rpl_compat && mysql_bin_log.is_open()) cache.pre_read = cache.pre_close = (IO_CACHE_CALLBACK) log_loaded_block; @@ -806,9 +821,13 @@ int READ_INFO::read_field() row_end= to; return 0; } - /* Copy the found '"' character */ + /* + The string didn't terminate yet. + Store back next character for the loop + */ PUSH(chr); - chr='"'; + /* copy the found term character to 'to' */ + chr= found_enclosed_char; } else if (chr == field_term_char && found_enclosed_char == INT_MAX) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 33e96cc2776..2c15362d9fa 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -44,7 +44,6 @@ #define MIN_HANDSHAKE_SIZE 6 #endif /* HAVE_OPENSSL */ -extern int yyparse(void *thd); extern "C" pthread_mutex_t THR_LOCK_keycache; #ifdef SOLARIS extern "C" int gethostname(char *name, int namelen); @@ -53,15 +52,19 @@ extern "C" int gethostname(char *name, int namelen); static int check_for_max_user_connections(THD *thd, USER_CONN *uc); static void decrease_user_connections(USER_CONN *uc); static bool check_db_used(THD *thd,TABLE_LIST *tables); +#ifndef NO_EMBEDDED_ACCESS_CHECKS static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); +static bool single_table_command_access(THD *thd, ulong privilege, + TABLE_LIST *tables, int *res); +#else +#define check_merge_table_access(thd, db, tables) false +#define single_table_command_access(thd, privilege, tables, res) false +#endif static void remove_escape(char *name); static void refresh_status(void); static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name); -static bool single_table_command_access(THD *thd, ulong privilege, - TABLE_LIST *tables, int *res); - const char *any_db="*any*"; // Special symbol for check_access const char *command_name[]={ @@ -120,7 +123,6 @@ static 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, @@ -161,7 +163,7 @@ static int get_or_create_user_conn(THD *thd, const char *user, if (max_user_connections && mqh->connections > max_user_connections) uc->user_resources.connections = max_user_connections; uc->intime=thd->thr_create_time; - if (hash_insert(&hash_user_connections, (byte*) uc)) + if (my_hash_insert(&hash_user_connections, (byte*) uc)) { my_free((char*) uc,0); send_error(thd, 0, NullS); // Out of memory @@ -176,154 +178,186 @@ end: } +#ifndef NO_EMBEDDED_ACCESS_CHECKS /* - Check if user is ok - + Check if user exist and password supplied is correct. SYNOPSIS check_user() - thd Thread handle - command Command for connection (for log) - user Name of user trying to connect - passwd Scrambled password sent from client - db Database to connect to - check_count If set to 1, don't allow too many connection - simple_connect If 1 then client is of old type and we should connect - using the old method (no challange) - do_send_error Set to 1 if we should send error to user - prepared_scramble Buffer to store hash password of new connection - had_password Set to 1 if the user gave a password - cur_priv_version Check flag to know if someone flushed the privileges - since last code - hint_user Pointer used by acl_getroot() to remmeber user for - next call - - RETURN - 0 ok - thd->user, thd->master_access, thd->priv_user, thd->db and - thd->db_access are updated - 1 Access denied; Error sent to client - -1 If do_send_error == 1: Failed connect, error sent to client - If do_send_error == 0: Prepare for stage of connect + thd thread handle, thd->{host,user,ip} are used + command originator of the check: now check_user is called + during connect and change user procedures; used for + logging. + passwd scrambled password recieved from client + passwd_len length of scrambled password + db database name to connect to, may be NULL + check_count dont know exactly + + Note, that host, user and passwd may point to communication buffer. + Current implementation does not depened on that, but future changes + should be done with this in mind; 'thd' is INOUT, all other params + are 'IN'. + + RETURN VALUE + 0 OK; thd->user, thd->master_access, thd->priv_user, thd->db and + thd->db_access are updated; OK is sent to client; + -1 access denied or handshake error; error is sent to client; + >0 error, not sent to client */ -static int check_user(THD *thd,enum_server_command command, const char *user, - const char *passwd, const char *db, bool check_count, - bool simple_connect, bool do_send_error, - char *prepared_scramble, bool had_password, - uint *cur_priv_version, ACL_USER** hint_user) +int check_user(THD *thd, enum enum_server_command command, + const char *passwd, uint passwd_len, const char *db, + bool check_count) { - thd->db=0; - thd->db_length=0; - USER_RESOURCES ur; - char tmp_passwd[SCRAMBLE41_LENGTH+1]; DBUG_ENTER("check_user"); + my_bool opt_secure_auth_local; + pthread_mutex_lock(&LOCK_global_system_variables); + opt_secure_auth_local= opt_secure_auth; + pthread_mutex_unlock(&LOCK_global_system_variables); + /* - Move password to temporary buffer as it may be stored in communication - buffer + If the server is running in secure auth mode, short scrambles are + forbidden. */ - strmake(tmp_passwd, passwd, sizeof(tmp_passwd)); - passwd= tmp_passwd; // Use local copy - - /* We shall avoid dupplicate user allocations here */ - if (!thd->user && !(thd->user = my_strdup(user, MYF(0)))) + if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323) { - send_error(thd,ER_OUT_OF_RESOURCES); - DBUG_RETURN(1); + net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); + mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); + DBUG_RETURN(-1); } - thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user, - passwd, thd->scramble, - &thd->priv_user, thd->priv_host, - (protocol_version == 9 || - !(thd->client_capabilities & - CLIENT_LONG_PASSWORD)), - &ur,prepared_scramble, - cur_priv_version,hint_user); - - DBUG_PRINT("info", - ("Capabilities: %d packet_length: %ld Host: '%s' Login user: '%s' Priv_user: '%s' Using password: %s Access: %u db: '%s'", - thd->client_capabilities, thd->max_client_packet_length, - thd->host_or_ip, thd->user, thd->priv_user, - had_password ? "yes": "no", - thd->master_access, thd->db ? thd->db : "*none*")); + if (passwd_len != 0 && + passwd_len != SCRAMBLE_LENGTH && + passwd_len != SCRAMBLE_LENGTH_323) + DBUG_RETURN(ER_HANDSHAKE_ERROR); /* - In case we're going to retry we should not send error message at this - point + Clear thd->db as it points to something, that will be freed when + connection is closed. We don't want to accidently free a wrong pointer + if connect failed. Also in case of 'CHANGE USER' failure, current + database will be switched to 'no database selected'. */ - if (thd->master_access & NO_ACCESS) + thd->db= 0; + thd->db_length= 0; + + USER_RESOURCES ur; + int res= acl_getroot(thd, &ur, passwd, passwd_len); +#ifndef EMBEDDED_LIBRARY + if (res == -1) + { + /* + This happens when client (new) sends password scrambled with + scramble(), but database holds old value (scrambled with + scramble_323()). Here we please client to send scrambled_password + in old format. + */ + NET *net= &thd->net; + if (opt_secure_auth_local) + { + net_printf(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE, + thd->user, thd->host_or_ip); + mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), + thd->user, thd->host_or_ip); + DBUG_RETURN(-1); + } + if (send_old_password_request(thd) || + my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very + { // specific packet size + inc_host_errors(&thd->remote.sin_addr); + DBUG_RETURN(ER_HANDSHAKE_ERROR); + } + /* Final attempt to check the user based on reply */ + /* So as passwd is short, errcode is always >= 0 */ + res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323); + } +#endif /*EMBEDDED_LIBRARY*/ + /* here res is always >= 0 */ + if (res == 0) { - if (do_send_error || !had_password || !*hint_user) + if (!(thd->master_access & NO_ACCESS)) // authentification is OK { - DBUG_PRINT("info",("Access denied")); + DBUG_PRINT("info", + ("Capabilities: %d packet_length: %ld Host: '%s' " + "Login user: '%s' Priv_user: '%s' Using password: %s " + "Access: %u db: '%s'", + thd->client_capabilities, thd->max_client_packet_length, + thd->host_or_ip, thd->user, thd->priv_user, + passwd_len ? "yes": "no", + thd->master_access, thd->db ? thd->db : "*none*")); + + if (check_count) + { + VOID(pthread_mutex_lock(&LOCK_thread_count)); + bool count_ok= thread_count < max_connections + delayed_insert_threads + || (thd->master_access & SUPER_ACL); + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (!count_ok) + { // too many connections + send_error(thd, ER_CON_COUNT_ERROR); + DBUG_RETURN(-1); + } + } + + /* Why logging is performed before all checks've passed? */ + mysql_log.write(thd,command, + (thd->priv_user == thd->user ? + (char*) "%s@%s on %s" : + (char*) "%s@%s as anonymous on %s"), + thd->user, thd->host_or_ip, + db ? db : (char*) ""); + /* - Old client should get nicer error message if password version is - not supported + This is the default access rights for the current database. It's + set to 0 here because we don't have an active database yet (and we + may not have an active database to set. */ - if (simple_connect && *hint_user && (*hint_user)->pversion) + thd->db_access=0; + + /* Don't allow user to connect if he has done too many queries */ + if ((ur.questions || ur.updates || + ur.connections || max_user_connections) && + get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur)) + DBUG_RETURN(1); + if (thd->user_connect && thd->user_connect->user_resources.connections && + check_for_max_user_connections(thd, thd->user_connect)) + DBUG_RETURN(1); + + /* Change database if necessary: OK or FAIL is sent in mysql_change_db */ + if (db && db[0]) { - net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); - mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE)); + if (mysql_change_db(thd, db)) + { + if (thd->user_connect) + decrease_user_connections(thd->user_connect); + DBUG_RETURN(-1); + } } else - { - net_printf(thd, ER_ACCESS_DENIED_ERROR, - thd->user, - thd->host_or_ip, - had_password ? ER(ER_YES) : ER(ER_NO)); - mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), - thd->user, - thd->host_or_ip, - had_password ? ER(ER_YES) : ER(ER_NO)); - } - DBUG_RETURN(1); // Error already given - } - DBUG_PRINT("info",("Prepare for second part of handshake")); - DBUG_RETURN(-1); // no report error in special handshake - } - - if (check_count) - { - VOID(pthread_mutex_lock(&LOCK_thread_count)); - bool tmp=(thread_count - delayed_insert_threads >= max_connections && - !(thd->master_access & SUPER_ACL)); - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - if (tmp) - { // Too many connections - send_error(thd, ER_CON_COUNT_ERROR); - DBUG_RETURN(1); + send_ok(thd); + thd->password= test(passwd_len); // remember for error messages + /* Ready to handle queries */ + DBUG_RETURN(0); } } - mysql_log.write(thd,command, - (thd->priv_user == thd->user ? - (char*) "%s@%s on %s" : - (char*) "%s@%s as anonymous on %s"), - user, - thd->host_or_ip, - db ? db : (char*) ""); - thd->db_access=0; - /* Don't allow user to connect if he has done too many queries */ - if ((ur.questions || ur.updates || ur.connections || max_user_connections) && - get_or_create_user_conn(thd,user,thd->host_or_ip,&ur)) - DBUG_RETURN(1); - if (thd->user_connect && ((thd->user_connect->user_resources.connections) || - max_user_connections) && - check_for_max_user_connections(thd, thd->user_connect)) - DBUG_RETURN(1); - - if (db && db[0]) - { - int error= test(mysql_change_db(thd,db)); - if (error && thd->user_connect) - decrease_user_connections(thd->user_connect); - DBUG_RETURN(error); - } - send_ok(thd); // Ready to handle questions - thd->password= test(passwd[0]); // Remember for error messages - DBUG_RETURN(0); // ok + else if (res == 2) // client gave short hash, server has long hash + { + net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); + mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE)); + DBUG_RETURN(-1); + } + net_printf(thd, ER_ACCESS_DENIED_ERROR, + thd->user, + thd->host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); + mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), + thd->user, + thd->host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); + DBUG_RETURN(-1); } +#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ + /* Check for maximum allowable user connections, if the mysqld server is @@ -433,6 +467,7 @@ bool is_update_query(enum enum_sql_command command) return uc_update_queries[command]; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS /* Check if maximum queries per hour limit has been reached returns 0 if OK. @@ -523,54 +558,44 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0) } (void) pthread_mutex_unlock(&LOCK_user_conn); } - +#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ /* - Check connnectionn and get priviliges - + Perform handshake, authorize client and update thd ACL variables. SYNOPSIS - check_connections - thd Thread handle + check_connection() + thd thread handle RETURN - 0 ok - -1 Error, which is sent to user - > 0 Error code (not sent to user) + 0 success, OK is sent to user, thd is updated. + -1 error, which is sent to user + > 0 error code (not sent to user) */ -#ifndef EMBEDDED_LIBRARY -static int -check_connections(THD *thd) +#ifndef EMBEDDED_LIBRARY +static int check_connection(THD *thd) { - int res; - uint connect_errors=0; - uint cur_priv_version; - bool using_password; + uint connect_errors= 0; NET *net= &thd->net; - char *end, *user, *passwd, *db; - char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble&hash */ - char db_buff[NAME_LEN+1]; - ACL_USER* cached_user=NULL; /* Initialise to NULL for first stage */ - DBUG_PRINT("info",("New connection received on %s", - vio_description(net->vio))); - - /* Remove warning from valgrind. TODO: Fix it in password.c */ - bzero((char*) &prepared_scramble[0], sizeof(prepared_scramble)); + + DBUG_PRINT("info", + ("New connection received on %s", vio_description(net->vio))); + if (!thd->host) // If TCP/IP connection { char ip[30]; if (vio_peer_addr(net->vio, ip, &thd->peer_port)) return (ER_BAD_HOST_ERROR); - if (!(thd->ip = my_strdup(ip,MYF(0)))) + if (!(thd->ip= my_strdup(ip,MYF(0)))) return (ER_OUT_OF_RESOURCES); - thd->host_or_ip=thd->ip; + thd->host_or_ip= thd->ip; #if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread) /* Fast local hostname resolve for Win32 */ if (!strcmp(thd->ip,"127.0.0.1")) { - thd->host= (char*) localhost; - thd->host_or_ip= localhost; + thd->host= (char*) my_localhost; + thd->host_or_ip= my_localhost; } else #endif @@ -600,15 +625,14 @@ check_connections(THD *thd) DBUG_PRINT("info",("Host: %s",thd->host)); thd->host_or_ip= thd->host; thd->ip= 0; - bzero((char*) &thd->remote,sizeof(struct sockaddr)); + bzero((char*) &thd->remote, sizeof(struct sockaddr)); } - /* Ensure that wrong hostnames doesn't cause buffer overflows */ vio_keepalive(net->vio, TRUE); - - ulong pkt_len=0; + ulong pkt_len= 0; + char *end; { /* buff[] needs to big enough to hold the server_version variable */ - char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+64]; + char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64]; ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION); @@ -622,19 +646,34 @@ check_connections(THD *thd) client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */ #endif /* HAVE_OPENSSL */ - end=strnmov(buff,server_version,SERVER_VERSION_LENGTH)+1; - int4store((uchar*) end,thd->thread_id); - end+=4; - memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1); - end+=SCRAMBLE_LENGTH +1; - int2store(end,client_flags); + end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1; + int4store((uchar*) end, thd->thread_id); + end+= 4; + /* + So as check_connection is the only entry point to authorization + procedure, scramble is set here. This gives us new scramble for + each handshake. + */ + create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); + /* + Old clients does not understand long scrambles, but can ignore packet + tail: that's why first part of the scramble is placed here, and second + part at the end of packet. + */ + end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1; + + int2store(end, client_flags); + /* write server characteristics: up to 16 bytes allowed */ end[2]=(char) default_charset_info->number; - int2store(end+3,thd->server_status); - bzero(end+5,13); - end+=18; - - // At this point we write connection message and read reply - if (net_write_command(net,(uchar) protocol_version, "", 0, buff, + int2store(end+3, thd->server_status); + bzero(end+5, 13); + end+= 18; + /* write scramble tail */ + end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323, + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1; + + /* At this point we write connection message and read reply */ + if (net_write_command(net, (uchar) protocol_version, "", 0, buff, (uint) (end-buff)) || (pkt_len= my_net_read(net)) == packet_error || pkt_len < MIN_HANDSHAKE_SIZE) @@ -723,7 +762,7 @@ check_connections(THD *thd) return(ER_HANDSHAKE_ERROR); } DBUG_PRINT("info", ("Reading user information over SSL layer")); - if ((pkt_len=my_net_read(net)) == packet_error || + if ((pkt_len= my_net_read(net)) == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE) { DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", @@ -740,25 +779,6 @@ check_connections(THD *thd) return(ER_HANDSHAKE_ERROR); } - user= end; - passwd= strend(user)+1; - db=0; - using_password= test(passwd[0]); - if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) - { - db= strend(passwd)+1; - uint32 length= copy_and_convert(db_buff, sizeof(db_buff)-1, - system_charset_info, - db, strlen(db), - thd->charset()); - db_buff[length]= 0; - db= db_buff; - } - - /* We can get only old hash at this point */ - if (using_password && strlen(passwd) != SCRAMBLE_LENGTH) - return ER_HANDSHAKE_ERROR; - if (thd->client_capabilities & CLIENT_INTERACTIVE) thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && @@ -766,63 +786,37 @@ check_connections(THD *thd) net->return_status= &thd->server_status; net->read_timeout=(uint) thd->variables.net_read_timeout; - /* Simple connect only for old clients. New clients always use secure auth */ - bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)); - - /* Check user permissions. If password failure we'll get scramble back */ - if ((res=check_user(thd, COM_CONNECT, user, passwd, db, 1, simple_connect, - simple_connect, prepared_scramble, using_password, - &cur_priv_version, - &cached_user)) < 0) - { - /* Store current used and database as they are erased with next packet */ - char tmp_user[USERNAME_LENGTH+1]; - char tmp_db[NAME_LEN+1]; - - /* If the client is old we just have to return error */ - if (simple_connect) - return -1; - - DBUG_PRINT("info",("password challenge")); - - tmp_user[0]= tmp_db[0]= 0; - if (user) - strmake(tmp_user,user,USERNAME_LENGTH); - if (db) - strmake(tmp_db,db,NAME_LEN); + char *user= end; + char *passwd= strend(user)+1; + char *db= passwd; + char db_buff[NAME_LEN+1]; // buffer to store db in utf8 + /* + Old clients send null-terminated string as password; new clients send + the size (1 byte) + string (not null-terminated). Hence in case of empty + password both send '\0'. + */ + uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? + *passwd++ : strlen(passwd); + db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? + db + passwd_len + 1 : 0; + + /* Since 4.1 all database names are stored in utf8 */ + if (db) + { + db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, + system_charset_info, + db, strlen(db), + thd->charset())]= 0; + db= db_buff; + } - /* Write hash and encrypted scramble to client */ - if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) || - net_flush(net)) - { - inc_host_errors(&thd->remote.sin_addr); - return ER_HANDSHAKE_ERROR; - } - /* Reading packet back */ - if ((pkt_len= my_net_read(net)) == packet_error) - { - inc_host_errors(&thd->remote.sin_addr); - return ER_HANDSHAKE_ERROR; - } - /* We have to get very specific packet size */ - if (pkt_len != SCRAMBLE41_LENGTH) - { - inc_host_errors(&thd->remote.sin_addr); - return ER_HANDSHAKE_ERROR; - } - /* Final attempt to check the user based on reply */ - if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos, - tmp_db, 1, 0, 1, prepared_scramble, using_password, - &cur_priv_version, - &cached_user)) - return -1; - } - else if (res) - return -1; // Error sent from check_user() - return 0; + if (thd->user) + x_free(thd->user); + if (!(thd->user= my_strdup(user, MYF(0)))) + return (ER_OUT_OF_RESOURCES); + return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true); } - pthread_handler_decl(handle_one_connection,arg) { THD *thd=(THD*) arg; @@ -876,7 +870,7 @@ pthread_handler_decl(handle_one_connection,arg) NET *net= &thd->net; thd->thread_stack= (char*) &thd; - if ((error=check_connections(thd))) + if ((error=check_connection(thd))) { // Wrong permissions if (error > 0) net_printf(thd,error,thd->host_or_ip); @@ -938,6 +932,8 @@ end_thread: return(0); /* purecov: deadcode */ } +#endif /* EMBEDDED_LIBRARY */ + /* Execute commands from bootstrap_file. Used when creating the initial grant tables @@ -952,12 +948,15 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) /* The following must be called before DBUG_ENTER */ if (my_thread_init() || thd->store_globals()) { +#ifndef EMBEDDED_LIBRARY close_connection(thd, ER_OUT_OF_RESOURCES, 1); +#endif thd->fatal_error(); goto end; } DBUG_ENTER("handle_bootstrap"); +#ifndef EMBEDDED_LIBRARY pthread_detach_this_thread(); thd->thread_stack= (char*) &thd; #if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) @@ -965,6 +964,7 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) VOID(sigemptyset(&set)); // Get mask in use VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); #endif +#endif /* EMBEDDED_LIBRARY */ if (thd->variables.max_join_size == HA_POS_ERROR) thd->options |= OPTION_BIG_SELECTS; @@ -986,6 +986,7 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); thd->query[length] = '\0'; thd->query_id=query_id++; +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END)) { thd->net.error = 0; @@ -993,6 +994,7 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); break; } +#endif mysql_parse(thd,thd->query,length); close_thread_tables(thd); // Free tables if (thd->is_fatal_error) @@ -1003,17 +1005,17 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) /* thd->fatal_error should be set in case something went wrong */ end: +#ifndef EMBEDDED_LIBRARY (void) pthread_mutex_lock(&LOCK_thread_count); thread_count--; (void) pthread_mutex_unlock(&LOCK_thread_count); (void) pthread_cond_broadcast(&COND_thread_count); my_thread_end(); pthread_exit(0); +#endif DBUG_RETURN(0); // Never reached } -#endif /* EMBEDDED_LIBRARY */ - /* This works because items are allocated with sql_alloc() */ void free_items(Item *item) @@ -1048,11 +1050,12 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT))) DBUG_RETURN(1); - if (check_access(thd, SELECT_ACL, db, &table_list->grant.privilege)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd, SELECT_ACL, db, &table_list->grant.privilege,0,0)) goto err; if (grant_option && check_grant(thd, SELECT_ACL, table_list)) goto err; - +#endif thd->free_list = 0; thd->query_length=(uint) strlen(tbl_name); thd->query = tbl_name; @@ -1161,7 +1164,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, mysql_log.write(thd,command,"%s",thd->db); break; } -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case COM_REGISTER_SLAVE: { if (!register_slave(thd, (uchar*)packet, packet_length)) @@ -1188,116 +1191,73 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_CHANGE_USER: { thd->change_user(); - thd->clear_error(); // If errors from rollback + thd->clear_error(); // if errors from rollback - statistic_increment(com_other,&LOCK_status); - char *user= (char*) packet; + statistic_increment(com_other, &LOCK_status); + char *user= (char*) packet; char *passwd= strend(user)+1; - char *db= strend(passwd)+1; - - /* Save user and privileges */ - uint save_master_access=thd->master_access; - uint save_db_access= thd->db_access; - uint save_db_length= thd->db_length; - char *save_user= thd->user; - thd->user=NULL; /* Needed for check_user to allocate new user */ - char *save_priv_user= thd->priv_user; - char *save_db= thd->db; - USER_CONN *save_uc= thd->user_connect; - bool simple_connect; - bool using_password; - char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */ - char tmp_user[USERNAME_LENGTH+1]; - char tmp_db[NAME_LEN+1]; - ACL_USER* cached_user ; /* Cached user */ - uint cur_priv_version; /* Cached grant version */ - int res; - ulong pkt_len= 0; /* Length of reply packet */ - - bzero((char*) prepared_scramble, sizeof(prepared_scramble)); + /* + Old clients send null-terminated string ('\0' for empty string) for + password. New clients send the size (1 byte) + string (not null + terminated, so also '\0' for empty string). + */ + char db_buff[NAME_LEN+1]; // buffer to store db in utf8 + char *db= passwd; + uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? + *passwd++ : strlen(passwd); + db+= passwd_len + 1; /* Small check for incomming packet */ - if ((uint) ((uchar*) db - net->read_pos) > packet_length) - goto restore_user_err; - - /* Now we shall basically perform authentication again */ - - /* We can get only old hash at this point */ - if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH) - goto restore_user_err; - - cached_user= NULL; - - /* Simple connect only for old clients. New clients always use sec. auth*/ - simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)); - - /* Store information if we used password. passwd will be dammaged */ - using_password=test(passwd[0]); + { + send_error(thd, ER_UNKNOWN_COM_ERROR); + break; + } - if (simple_connect) /* Restore scramble for old clients */ - memcpy(thd->scramble,thd->old_scramble,9); + /* Convert database name to utf8 */ + db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, + system_charset_info, db, strlen(db), + thd->charset())]= 0; + db= db_buff; - /* - Check user permissions. If password failure we'll get scramble back - Do not retry if we already have sent error (result>0) - */ - if ((res=check_user(thd,COM_CHANGE_USER, user, passwd, db, 0, - simple_connect, simple_connect, prepared_scramble, - using_password, &cur_priv_version, &cached_user)) < 0) + /* Save user and privileges */ + uint save_master_access= thd->master_access; + uint save_db_access= thd->db_access; + uint save_db_length= thd->db_length; + char *save_user= thd->user; + char *save_priv_user= thd->priv_user; + char *save_db= thd->db; + USER_CONN *save_uc= thd->user_connect; + thd->user= my_strdup(user, MYF(0)); + if (!thd->user) { - /* If the client is old we just have to have auth failure */ - if (simple_connect) - goto restore_user; /* Error is already reported */ - - /* Store current used and database as they are erased with next packet */ - tmp_user[0]= tmp_db[0]= 0; - if (user) - strmake(tmp_user,user,USERNAME_LENGTH); - if (db) - strmake(tmp_db,db,NAME_LEN); - - /* Write hash and encrypted scramble to client */ - if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) || - net_flush(net)) - goto restore_user_err; - - /* Reading packet back */ - if ((pkt_len=my_net_read(net)) == packet_error) - goto restore_user_err; - - /* We have to get very specific packet size */ - if (pkt_len != SCRAMBLE41_LENGTH) - goto restore_user; - - /* Final attempt to check the user based on reply */ - if (check_user(thd,COM_CHANGE_USER, tmp_user, (char*) net->read_pos, - tmp_db, 0, 0, 1, prepared_scramble, using_password, - &cur_priv_version, &cached_user)) - goto restore_user; + thd->user= save_user; + send_error(thd, ER_OUT_OF_RESOURCES); + break; } - else if (res) - goto restore_user; - - /* Finally we've authenticated new user */ - if (max_connections && save_uc) - decrease_user_connections(save_uc); - x_free((gptr) save_db); - x_free((gptr) save_user); - thd->password=using_password; - break; - /* Bad luck we shall restore old user */ -restore_user_err: - send_error(thd, ER_UNKNOWN_COM_ERROR); + int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, false); -restore_user: - x_free(thd->user); - thd->master_access=save_master_access; - thd->db_access=save_db_access; - thd->db=save_db; - thd->db_length=save_db_length; - thd->user=save_user; - thd->priv_user=save_priv_user; + if (res) + { + /* authentification failure, we shall restore old user */ + if (res > 0) + send_error(thd, ER_UNKNOWN_COM_ERROR); + x_free(thd->user); + thd->user= save_user; + thd->priv_user= save_priv_user; + thd->master_access= save_master_access; + thd->db_access= save_db_access; + thd->db= save_db; + thd->db_length= save_db_length; + } + else + { + /* we've authenticated new user */ + if (max_connections && save_uc) + decrease_user_connections(save_uc); + x_free((gptr) save_db); + x_free((gptr) save_user); + } break; } #endif /* EMBEDDED_LIBRARY */ @@ -1395,11 +1355,13 @@ restore_user: my_casedn_str(files_charset_info, table_list.real_name); remove_escape(table_list.real_name); // This can't have wildcards - if (check_access(thd,SELECT_ACL,table_list.db,&thd->col_access)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,SELECT_ACL,table_list.db,&thd->col_access,0,0)) break; table_list.grant.privilege=thd->col_access; if (grant_option && check_grant(thd,SELECT_ACL,&table_list,2)) break; +#endif /*DONT_ALLOW_SHOW_COMMANDS*/ mysqld_list_fields(thd,&table_list,fields); free_items(thd->free_list); break; @@ -1422,7 +1384,7 @@ restore_user: net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); break; } - if (check_access(thd,CREATE_ACL,db,0,1)) + if (check_access(thd,CREATE_ACL,db,0,1,0)) break; mysql_log.write(thd,command,packet); mysql_create_db(thd,db,0,0); @@ -1438,7 +1400,7 @@ restore_user: net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); break; } - if (check_access(thd,DROP_ACL,db,0,1)) + if (check_access(thd,DROP_ACL,db,0,1,0)) break; if (thd->locked_tables || thd->active_transaction()) { @@ -1511,12 +1473,15 @@ restore_user: error=TRUE; break; #endif -#ifndef EMBEDDED_LIBRARY case COM_STATISTICS: { mysql_log.write(thd,command,NullS); statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status); +#ifndef EMBEDDED_LIBRARY char buff[200]; +#else + char *buff= thd->net.last_error; +#endif ulong uptime = (ulong) (thd->start_time - start_time); sprintf((char*) buff, "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f", @@ -1529,23 +1494,32 @@ restore_user: sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK", (sf_malloc_cur_memory+1023L)/1024L, (sf_malloc_max_memory+1023L)/1024L); - #endif +#endif +#ifndef EMBEDDED_LIBRARY VOID(my_net_write(net, buff,(uint) strlen(buff))); VOID(net_flush(net)); +#endif break; } -#endif case COM_PING: statistic_increment(com_other,&LOCK_status); send_ok(thd); // Tell client we are alive break; case COM_PROCESS_INFO: statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status); +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) break; +#endif mysql_log.write(thd,command,NullS); - mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS : - thd->priv_user,0); + mysqld_list_processes(thd, +#ifndef NO_EMBEDDED_ACCESS_CHECKS + thd->master_access & PROCESS_ACL ? + NullS : thd->priv_user +#else + NullS +#endif + ,0); break; case COM_PROCESS_KILL: { @@ -1644,8 +1618,8 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) /* We must allocate some extra memory for query cache */ if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet), packet_length, - thd->db_length+2+ - sizeof(ha_rows)))) + thd->db_length+ 1 + + QUERY_CACHE_FLAGS_SIZE))) return 1; thd->query[packet_length]=0; thd->query_length= packet_length; @@ -1685,7 +1659,7 @@ mysql_execute_command(THD *thd) */ thd->old_total_warn_count= thd->total_warn_count; -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION if (thd->slave_thread) { /* @@ -1711,7 +1685,7 @@ mysql_execute_command(THD *thd) } #endif } -#endif /* EMBEDDED_LIBRARY */ +#endif /* !HAVE_REPLICATION */ /* TODO: make derived tables processing 'inside' SELECT processing. TODO: solve problem with depended derived tables in subselects @@ -1746,7 +1720,11 @@ mysql_execute_command(THD *thd) Except for the replication thread and the 'super' users. */ if (opt_readonly && - !(thd->slave_thread || (thd->master_access & SUPER_ACL)) && + !(thd->slave_thread +#ifndef NO_EMBEDDED_ACCESS_CHECKS + || (thd->master_access & SUPER_ACL) +#endif + ) && (uc_update_queries[lex->sql_command] > 0)) { send_error(thd, ER_CANT_UPDATE_WITH_READLOCK); @@ -1758,29 +1736,34 @@ mysql_execute_command(THD *thd) case SQLCOM_SELECT: { select_result *result=lex->result; +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (tables) { res=check_table_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, - tables); + tables,0); } else res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, - any_db); + any_db,0,0,0); if (res) { res=0; break; // Error message is given } - +#endif + /* + In case of single SELECT unit->global_parameters points on first SELECT + TODO: move counters to SELECT_LEX + */ unit->offset_limit_cnt= (ha_rows) unit->global_parameters->offset_limit; unit->select_limit_cnt= (ha_rows) (unit->global_parameters->select_limit+ unit->global_parameters->offset_limit); if (unit->select_limit_cnt < (ha_rows) unit->global_parameters->select_limit) unit->select_limit_cnt= HA_POS_ERROR; // no limit - if (unit->select_limit_cnt == HA_POS_ERROR) + if (unit->select_limit_cnt == HA_POS_ERROR && !select_lex->next_select()) select_lex->options&= ~OPTION_FOUND_ROWS; if (!(res=open_and_lock_tables(thd,tables))) @@ -1821,8 +1804,9 @@ mysql_execute_command(THD *thd) } break; } + case SQLCOM_DO: - if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) || + if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) || (res= open_and_lock_tables(thd,tables)))) break; @@ -1858,7 +1842,6 @@ mysql_execute_command(THD *thd) break; } #endif - case SQLCOM_SHOW_WARNS: { res= mysqld_show_warnings(thd, (ulong) @@ -1878,6 +1861,7 @@ mysql_execute_command(THD *thd) { if (check_global_access(thd, REPL_SLAVE_ACL)) goto error; + /* This query don't work now. See comment in repl_failsafe.cc */ #ifndef WORKING_NEW_MASTER net_printf(thd, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER"); res= 1; @@ -1887,7 +1871,7 @@ mysql_execute_command(THD *thd) break; } -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case SQLCOM_SHOW_SLAVE_HOSTS: { if (check_global_access(thd, REPL_SLAVE_ACL)) @@ -1907,7 +1891,7 @@ mysql_execute_command(THD *thd) case SQLCOM_BACKUP_TABLE: { if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL, tables) || + check_table_access(thd,SELECT_ACL, tables,0) || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ res = mysql_backup_table(thd, tables); @@ -1917,7 +1901,7 @@ mysql_execute_command(THD *thd) case SQLCOM_RESTORE_TABLE: { if (check_db_used(thd,tables) || - check_table_access(thd, INSERT_ACL, tables) || + check_table_access(thd, INSERT_ACL, tables,0) || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ res = mysql_restore_table(thd, tables); @@ -1926,14 +1910,12 @@ mysql_execute_command(THD *thd) case SQLCOM_PRELOAD_KEYS: { if (check_db_used(thd, tables) || - check_access(thd, INDEX_ACL, tables->db, &tables->grant.privilege)) + check_access(thd, INDEX_ACL, tables->db, &tables->grant.privilege,0,0)) goto error; res = mysql_preload_keys(thd, tables); break; } - - -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case SQLCOM_CHANGE_MASTER: { if (check_global_access(thd, SUPER_ACL)) @@ -1970,8 +1952,7 @@ mysql_execute_command(THD *thd) else res = load_master_data(thd); break; -#endif /* EMBEDDED_LIBRARY */ - +#endif /* HAVE_REPLICATION */ #ifdef HAVE_INNOBASE_DB case SQLCOM_SHOW_INNODB_STATUS: { @@ -1981,13 +1962,13 @@ mysql_execute_command(THD *thd) break; } #endif - -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case SQLCOM_LOAD_MASTER_TABLE: { if (!tables->db) tables->db=thd->db; - if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0)) goto error; /* purecov: inspected */ if (grant_option) { @@ -1999,6 +1980,7 @@ mysql_execute_command(THD *thd) if (error) goto error; } +#endif if (strlen(tables->real_name) > NAME_LEN) { net_printf(thd,ER_WRONG_TABLE_NAME,tables->real_name); @@ -2014,15 +1996,18 @@ mysql_execute_command(THD *thd) UNLOCK_ACTIVE_MI; break; } -#endif /* EMBEDDED_LIBRARY */ +#endif /* HAVE_REPLICATION */ case SQLCOM_CREATE_TABLE: { +#ifndef NO_EMBEDDED_ACCESS_CHECKS ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? CREATE_TMP_ACL : CREATE_ACL); +#endif if (!tables->db) tables->db=thd->db; - if (check_access(thd,want_priv,tables->db,&tables->grant.privilege) || +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,want_priv,tables->db,&tables->grant.privilege,0,0) || check_merge_table_access(thd, tables->db, (TABLE_LIST *) lex->create_info.merge_list.first)) @@ -2037,6 +2022,7 @@ mysql_execute_command(THD *thd) if (error) goto error; } +#endif if (strlen(tables->real_name) > NAME_LEN) { net_printf(thd, ER_WRONG_TABLE_NAME, tables->alias); @@ -2066,11 +2052,13 @@ mysql_execute_command(THD *thd) net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name); DBUG_VOID_RETURN; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (tables->next) { - if (check_table_access(thd, SELECT_ACL, tables->next)) + if (check_table_access(thd, SELECT_ACL, tables->next,0)) goto error; // Error message is given } +#endif select_lex->options|= SELECT_NO_UNLOCK; unit->offset_limit_cnt= select_lex->offset_limit; unit->select_limit_cnt= select_lex->select_limit+ @@ -2100,14 +2088,6 @@ mysql_execute_command(THD *thd) (Table_ident *)lex->name); else { - List_iterator<create_field> fields(lex->create_list); - create_field *field; - while ((field= fields++)) - { - if (!field->charset) - field->charset= lex->create_info.table_charset; - field->create_length_to_internal_length(); - } res= mysql_create_table(thd,tables->db ? tables->db : thd->db, tables->real_name, &lex->create_info, lex->create_list, @@ -2121,17 +2101,19 @@ mysql_execute_command(THD *thd) case SQLCOM_CREATE_INDEX: if (!tables->db) tables->db=thd->db; - if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege,0,0)) goto error; /* purecov: inspected */ if (grant_option && check_grant(thd,INDEX_ACL,tables)) goto error; +#endif if (end_active_trans(thd)) res= -1; else res = mysql_create_index(thd, tables, lex->key_list); break; -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case SQLCOM_SLAVE_START: { LOCK_ACTIVE_MI; @@ -2164,7 +2146,7 @@ mysql_execute_command(THD *thd) UNLOCK_ACTIVE_MI; break; } -#endif +#endif /* HAVE_REPLICATION */ case SQLCOM_ALTER_TABLE: #if defined(DONT_ALLOW_SHOW_COMMANDS) @@ -2183,14 +2165,15 @@ mysql_execute_command(THD *thd) tables->db=thd->db; if (!select_lex->db) select_lex->db=tables->db; - if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege) || - check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv) || + if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) || + check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)|| check_merge_table_access(thd, tables->db, (TABLE_LIST *) lex->create_info.merge_list.first)) goto error; /* purecov: inspected */ if (!tables->db) tables->db=thd->db; +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (grant_option) { if (check_grant(thd,ALTER_ACL,tables)) @@ -2206,6 +2189,7 @@ mysql_execute_command(THD *thd) goto error; } } +#endif /* Don't yet allow changing of symlinks with ALTER TABLE */ lex->create_info.data_file_name=lex->create_info.index_file_name=0; /* ALTER TABLE ends previous transaction */ @@ -2224,18 +2208,19 @@ mysql_execute_command(THD *thd) } break; } -#endif +#endif /*DONT_ALLOW_SHOW_COMMANDS*/ case SQLCOM_RENAME_TABLE: { TABLE_LIST *table; if (check_db_used(thd,tables)) goto error; +#ifndef NO_EMBEDDED_ACCESS_CHECKS for (table=tables ; table ; table=table->next->next) { if (check_access(thd, ALTER_ACL | DROP_ACL, table->db, - &table->grant.privilege) || + &table->grant.privilege,0,0) || check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db, - &table->next->grant.privilege)) + &table->next->grant.privilege,0,0)) goto error; if (grant_option) { @@ -2250,6 +2235,7 @@ mysql_execute_command(THD *thd) goto error; } } +#endif query_cache_invalidate3(thd, tables, 0); if (end_active_trans(thd)) res= -1; @@ -2279,16 +2265,24 @@ mysql_execute_command(THD *thd) { if (check_db_used(thd, tables) || check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db, - &tables->grant.privilege)) + &tables->grant.privilege,0,0)) goto error; res = mysqld_show_create(thd, tables); break; } #endif + case SQLCOM_CHECKSUM: + { + if (check_db_used(thd,tables) || + check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0)) + goto error; /* purecov: inspected */ + res = mysql_checksum_table(thd, tables, &lex->check_opt); + break; + } case SQLCOM_REPAIR: { if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) + check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) goto error; /* purecov: inspected */ res = mysql_repair_table(thd, tables, &lex->check_opt); /* ! we write after unlocking the table */ @@ -2306,7 +2300,7 @@ mysql_execute_command(THD *thd) case SQLCOM_CHECK: { if (check_db_used(thd,tables) || - check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) + check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0)) goto error; /* purecov: inspected */ res = mysql_check_table(thd, tables, &lex->check_opt); break; @@ -2314,7 +2308,7 @@ mysql_execute_command(THD *thd) case SQLCOM_ANALYZE: { if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) + check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) goto error; /* purecov: inspected */ res = mysql_analyze_table(thd, tables, &lex->check_opt); /* ! we write after unlocking the table */ @@ -2334,7 +2328,7 @@ mysql_execute_command(THD *thd) { HA_CREATE_INFO create_info; if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) + check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) goto error; /* purecov: inspected */ if (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) { @@ -2374,7 +2368,6 @@ mysql_execute_command(THD *thd) if (single_table_command_access(thd, UPDATE_ACL, tables, &res)) goto error; - if (select_lex->item_list.elements != lex->value_list.elements) { send_error(thd,ER_WRONG_VALUE_COUNT); @@ -2392,10 +2385,12 @@ mysql_execute_command(THD *thd) res= -1; break; case SQLCOM_UPDATE_MULTI: - if (check_access(thd,UPDATE_ACL,tables->db,&tables->grant.privilege)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,UPDATE_ACL,tables->db,&tables->grant.privilege,0,0)) goto error; if (grant_option && check_grant(thd,UPDATE_ACL,tables)) goto error; +#endif if (select_lex->item_list.elements != lex->value_list.elements) { send_error(thd,ER_WRONG_VALUE_COUNT); @@ -2425,13 +2420,16 @@ mysql_execute_command(THD *thd) case SQLCOM_REPLACE: case SQLCOM_INSERT: { +#ifndef NO_EMBEDDED_ACCESS_CHECKS my_bool update=(lex->value_list.elements ? UPDATE_ACL : 0); ulong privilege= (lex->duplicates == DUP_REPLACE ? INSERT_ACL | DELETE_ACL : INSERT_ACL | update); if (single_table_command_access(thd, privilege, tables, &res)) goto error; - +#else + my_bool update=(lex->value_list.elements ? 1 : 0); +#endif if (select_lex->item_list.elements != lex->value_list.elements) { send_error(thd,ER_WRONG_VALUE_COUNT); @@ -2452,19 +2450,22 @@ mysql_execute_command(THD *thd) Check that we have modify privileges for the first table and select privileges for the rest */ +#ifndef NO_EMBEDDED_ACCESS_CHECKS { ulong privilege= (lex->duplicates == DUP_REPLACE ? INSERT_ACL | DELETE_ACL : INSERT_ACL); TABLE_LIST *save_next=tables->next; tables->next=0; if (check_access(thd, privilege, - tables->db,&tables->grant.privilege) || + tables->db,&tables->grant.privilege,0,0) || (grant_option && check_grant(thd, privilege, tables))) goto error; + tables->next=save_next; - if ((res=check_table_access(thd, SELECT_ACL, save_next))) + if ((res=check_table_access(thd, SELECT_ACL, save_next,0))) goto error; } +#endif /* Don't unlock tables until command is written to binary log */ select_lex->options|= SELECT_NO_UNLOCK; @@ -2498,10 +2499,12 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_TRUNCATE: - if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege,0,0)) goto error; /* purecov: inspected */ if (grant_option && check_grant(thd,DELETE_ACL,tables)) goto error; +#endif /* Don't allow this within a transaction because we want to use re-generate table @@ -2515,11 +2518,12 @@ mysql_execute_command(THD *thd) break; case SQLCOM_DELETE: { +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (single_table_command_access(thd, DELETE_ACL, tables, &res)) goto error; - // Set privilege for the WHERE clause tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); +#endif res = mysql_delete(thd,tables, select_lex->where, (ORDER*) select_lex->order_list.first, select_lex->select_limit, select_lex->options); @@ -2536,8 +2540,8 @@ mysql_execute_command(THD *thd) /* sql_yacc guarantees that tables and aux_tables are not zero */ if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) || - check_table_access(thd,SELECT_ACL, tables) || - check_table_access(thd,DELETE_ACL, aux_tables)) + check_table_access(thd,SELECT_ACL, tables,0) || + check_table_access(thd,DELETE_ACL, aux_tables,0)) goto error; if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where) { @@ -2614,7 +2618,7 @@ mysql_execute_command(THD *thd) { if (!lex->drop_temporary) { - if (check_table_access(thd,DROP_ACL,tables)) + if (check_table_access(thd,DROP_ACL,tables,0)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) { @@ -2641,10 +2645,12 @@ mysql_execute_command(THD *thd) case SQLCOM_DROP_INDEX: if (!tables->db) tables->db=thd->db; - if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege,0,0)) goto error; /* purecov: inspected */ if (grant_option && check_grant(thd,INDEX_ACL,tables)) goto error; +#endif if (end_active_trans(thd)) res= -1; else @@ -2662,10 +2668,18 @@ mysql_execute_command(THD *thd) break; #endif case SQLCOM_SHOW_PROCESSLIST: +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) break; - mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS : - thd->priv_user,lex->verbose); +#endif + mysqld_list_processes(thd, +#ifndef NO_EMBEDDED_ACCESS_CHECKS + thd->master_access & PROCESS_ACL ? NullS : + thd->priv_user +#else + NullS +#endif + ,lex->verbose); break; case SQLCOM_SHOW_TABLE_TYPES: res= mysqld_show_table_types(thd); @@ -2678,11 +2692,12 @@ mysql_execute_command(THD *thd) break; case SQLCOM_SHOW_STATUS: res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars, - OPT_GLOBAL); + OPT_GLOBAL, &LOCK_status); break; case SQLCOM_SHOW_VARIABLES: res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS), - init_vars, lex->option_type); + init_vars, lex->option_type, + &LOCK_global_system_variables); break; case SQLCOM_SHOW_LOGS: #ifdef DONT_ALLOW_SHOW_COMMANDS @@ -2690,8 +2705,10 @@ mysql_execute_command(THD *thd) DBUG_VOID_RETURN; #else { - if (grant_option && check_access(thd, FILE_ACL, any_db)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0)) goto error; +#endif res= mysqld_show_logs(thd); break; } @@ -2715,7 +2732,8 @@ mysql_execute_command(THD *thd) net_printf(thd,ER_WRONG_DB_NAME, db); goto error; } - if (check_access(thd,SELECT_ACL,db,&thd->col_access)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0)) goto error; /* purecov: inspected */ if (!thd->col_access && check_grant_db(thd,db)) { @@ -2725,6 +2743,7 @@ mysql_execute_command(THD *thd) db); goto error; } +#endif /* grant is checked in mysqld_show_tables */ if (select_lex->options & SELECT_DESCRIBE) res= mysqld_extend_show_tables(thd,db, @@ -2758,11 +2777,13 @@ mysql_execute_command(THD *thd) } remove_escape(db); // Fix escaped '_' remove_escape(tables->real_name); - if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,&thd->col_access)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,&thd->col_access,0,0)) goto error; /* purecov: inspected */ tables->grant.privilege=thd->col_access; if (grant_option && check_grant(thd,SELECT_ACL,tables,2)) goto error; +#endif res= mysqld_show_fields(thd,tables, (lex->wild ? lex->wild->ptr() : NullS), lex->verbose); @@ -2785,11 +2806,13 @@ mysql_execute_command(THD *thd) remove_escape(tables->real_name); if (!tables->db) tables->db=thd->db; - if (check_access(thd,SELECT_ACL,db,&thd->col_access)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0)) goto error; /* purecov: inspected */ tables->grant.privilege=thd->col_access; if (grant_option && check_grant(thd,SELECT_ACL,tables,2)) goto error; +#endif res= mysqld_show_keys(thd,tables); break; } @@ -2797,15 +2820,16 @@ mysql_execute_command(THD *thd) case SQLCOM_CHANGE_DB: mysql_change_db(thd,select_lex->db); break; -#ifndef EMBEDDED_LIBRARY + case SQLCOM_LOAD: { +#ifndef NO_EMBEDDED_ACCESS_CHECKS uint privilege= (lex->duplicates == DUP_REPLACE ? INSERT_ACL | DELETE_ACL : INSERT_ACL); if (!lex->local_file) { - if (check_access(thd,privilege | FILE_ACL,tables->db)) + if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0)) goto error; } else @@ -2816,17 +2840,18 @@ mysql_execute_command(THD *thd) send_error(thd,ER_NOT_ALLOWED_COMMAND); goto error; } - if (check_access(thd,privilege,tables->db,&tables->grant.privilege) || + if (check_access(thd,privilege,tables->db,&tables->grant.privilege,0,0) || grant_option && check_grant(thd,privilege,tables)) goto error; } +#endif /*NO_EMBEDDED_ACCESS_CHECKS*/ res=mysql_load(thd, lex->exchange, tables, lex->field_list, lex->duplicates, (bool) lex->local_file, lex->lock_option); break; } -#endif /* EMBEDDED_LIBRARY */ + case SQLCOM_SET_OPTION: - if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) || + if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) || (res= open_and_lock_tables(thd,tables)))) break; fix_tables_pointers(lex->all_selects_list); @@ -2851,7 +2876,7 @@ mysql_execute_command(THD *thd) unlock_locked_tables(thd); if (check_db_used(thd,tables) || end_active_trans(thd)) goto error; - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables)) + if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0)) goto error; thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; @@ -2888,7 +2913,7 @@ mysql_execute_command(THD *thd) break; } #endif - if (check_access(thd,CREATE_ACL,lex->name,0,1)) + if (check_access(thd,CREATE_ACL,lex->name,0,1,0)) break; res=mysql_create_db(thd,lex->name,&lex->create_info,0); break; @@ -2916,7 +2941,7 @@ mysql_execute_command(THD *thd) break; } #endif - if (check_access(thd,DROP_ACL,lex->name,0,1)) + if (check_access(thd,DROP_ACL,lex->name,0,1,0)) break; if (thd->locked_tables || thd->active_transaction()) { @@ -2933,7 +2958,7 @@ mysql_execute_command(THD *thd) net_printf(thd,ER_WRONG_DB_NAME, lex->name); break; } - if (check_access(thd,ALTER_ACL,lex->name,0,1)) + if (check_access(thd,ALTER_ACL,lex->name,0,1,0)) break; if (thd->locked_tables || thd->active_transaction()) { @@ -2950,7 +2975,7 @@ mysql_execute_command(THD *thd) net_printf(thd,ER_WRONG_DB_NAME, lex->name); break; } - if (check_access(thd,DROP_ACL,lex->name,0,1)) + if (check_access(thd,DROP_ACL,lex->name,0,1,0)) break; if (thd->locked_tables || thd->active_transaction()) { @@ -2961,7 +2986,7 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_CREATE_FUNCTION: - if (check_access(thd,INSERT_ACL,"mysql",0,1)) + if (check_access(thd,INSERT_ACL,"mysql",0,1,0)) break; #ifdef HAVE_DLOPEN if (!(res = mysql_create_function(thd,&lex->udf))) @@ -2971,7 +2996,7 @@ mysql_execute_command(THD *thd) #endif break; case SQLCOM_DROP_FUNCTION: - if (check_access(thd,DELETE_ACL,"mysql",0,1)) + if (check_access(thd,DELETE_ACL,"mysql",0,1,0)) break; #ifdef HAVE_DLOPEN if (!(res = mysql_drop_function(thd,&lex->udf.name))) @@ -2980,9 +3005,10 @@ mysql_execute_command(THD *thd) res= -1; #endif break; +#ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_DROP_USER: { - if (check_access(thd, GRANT_ACL,"mysql",0,1)) + if (check_access(thd, GRANT_ACL,"mysql",0,1,0)) break; if (!(res= mysql_drop_user(thd, lex->users_list))) { @@ -2998,7 +3024,7 @@ mysql_execute_command(THD *thd) } case SQLCOM_REVOKE_ALL: { - if (check_access(thd, GRANT_ACL ,"mysql",0,1)) + if (check_access(thd, GRANT_ACL ,"mysql",0,1,0)) break; if (!(res = mysql_revoke_all(thd, lex->users_list))) { @@ -3018,7 +3044,7 @@ mysql_execute_command(THD *thd) if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, tables && tables->db ? tables->db : select_lex->db, tables ? &tables->grant.privilege : 0, - tables ? 0 : 1)) + tables ? 0 : 1,0)) goto error; /* @@ -3038,7 +3064,7 @@ mysql_execute_command(THD *thd) my_strcasecmp(&my_charset_latin1, user->host.str, thd->host_or_ip))) { - if (check_access(thd, UPDATE_ACL, "mysql",0,1)) + if (check_access(thd, UPDATE_ACL, "mysql",0,1,0)) goto error; break; // We are allowed to do changes } @@ -3092,6 +3118,7 @@ mysql_execute_command(THD *thd) } break; } +#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ case SQLCOM_RESET: /* RESET commands are never written to the binary log, so we have to @@ -3131,18 +3158,20 @@ mysql_execute_command(THD *thd) case SQLCOM_KILL: kill_one_thread(thd,lex->thread_id); break; +#ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_SHOW_GRANTS: res=0; if ((thd->priv_user && !strcmp(thd->priv_user,lex->grant_user->user.str)) || - !check_access(thd, SELECT_ACL, "mysql",0,1)) + !check_access(thd, SELECT_ACL, "mysql",0,1,0)) { res = mysql_show_grants(thd,lex->grant_user); } break; +#endif case SQLCOM_HA_OPEN: if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL, tables)) + check_table_access(thd,SELECT_ACL, tables,0)) goto error; res = mysql_ha_open(thd, tables); break; @@ -3204,7 +3233,16 @@ mysql_execute_command(THD *thd) thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (!ha_rollback(thd)) { - if (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) + /* + If a non-transactional table was updated, warn; don't warn if this is a + slave thread (because when a slave thread executes a ROLLBACK, it has + been read from the binary log, so it's 100% sure and normal to produce + error ER_WARNING_NOT_COMPLETE_ROLLBACK. If we sent the warning to the + slave SQL thread, it would not stop the thread but just be printed in + the error log; but we don't want users to wonder why they have this + message in the error log, so we don't send it. + */ + if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread) send_warning(thd,ER_WARNING_NOT_COMPLETE_ROLLBACK,0); else send_ok(thd); @@ -3216,7 +3254,7 @@ mysql_execute_command(THD *thd) case SQLCOM_ROLLBACK_TO_SAVEPOINT: if (!ha_rollback_to_savepoint(thd, lex->savepoint_name)) { - if (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) + if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread) send_warning(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK, 0); else send_ok(thd); @@ -3243,11 +3281,12 @@ error: } +#ifndef NO_EMBEDDED_ACCESS_CHECKS /* Check grants for commands which work only with one table and all other tables belong to subselects. - SYNOPSYS + SYNOPSIS single_table_command_access() thd - Thread handler privilege - asked privelage @@ -3263,7 +3302,7 @@ static bool single_table_command_access(THD *thd, ulong privilege, TABLE_LIST *tables, int *res) { - if (check_access(thd, privilege, tables->db, &tables->grant.privilege)) + if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0)) return 1; // Show only 1 table for check_grant @@ -3276,7 +3315,7 @@ static bool single_table_command_access(THD *thd, ulong privilege, if (subselects_tables) { tables->next= subselects_tables; - if ((*res= check_table_access(thd, SELECT_ACL, subselects_tables))) + if ((*res= check_table_access(thd, SELECT_ACL, subselects_tables,0))) return 1; } return 0; @@ -3447,24 +3486,6 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, return FALSE; } - -static bool check_db_used(THD *thd,TABLE_LIST *tables) -{ - for (; tables ; tables=tables->next) - { - if (!tables->db) - { - if (!(tables->db=thd->db)) - { - send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ - return TRUE; /* purecov: tested */ - } - } - } - return FALSE; -} - - static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list) { @@ -3479,11 +3500,28 @@ static bool check_merge_table_access(THD *thd, char *db, tmp->db=db; } error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, - table_list); + table_list,0); } return error; } +#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ + +static bool check_db_used(THD *thd,TABLE_LIST *tables) +{ + for (; tables ; tables=tables->next) + { + if (!tables->db) + { + if (!(tables->db=thd->db)) + { + send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ + return TRUE; /* purecov: tested */ + } + } + } + return FALSE; +} /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue @@ -3709,12 +3747,14 @@ mysql_parse(THD *thd, char *inBuf, uint length) LEX *lex=lex_start(thd, (uchar*) inBuf, length); if (!yyparse((void *)thd) && ! thd->is_fatal_error) { +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (mqh_used && thd->user_connect && check_mqh(thd, lex->sql_command)) { thd->net.error = 0; } else +#endif { if (thd->net.report_error) send_error(thd, 0, NullS); @@ -3751,7 +3791,7 @@ mysql_parse(THD *thd, char *inBuf, uint length) bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, char *length, char *decimals, uint type_modifier, - Item *default_value, Item *comment, + Item *default_value, LEX_STRING *comment, char *change, TYPELIB *interval, CHARSET_INFO *cs, uint uint_geom_type) { @@ -3825,8 +3865,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, else { /* In this case comment is always of type Item_string */ - new_field->comment.str= (char*) comment->str_value.ptr(); - new_field->comment.length=comment->str_value.length(); + new_field->comment.str= (char*) comment->str; + new_field->comment.length=comment->length; } if (length && !(new_field->length= (uint) atoi(length))) length=0; /* purecov: inspected */ @@ -4319,7 +4359,6 @@ void add_join_natural(TABLE_LIST *a,TABLE_LIST *b) b->natural_join=a; } - /* Reload/resets privileges and the different caches. @@ -4346,6 +4385,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, bool result=0; select_errors=0; /* Write if more errors */ bool tmp_write_to_binlog= 1; +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (options & REFRESH_GRANT) { acl_reload(thd); @@ -4353,6 +4393,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (mqh_used) reset_mqh(thd,(LEX_USER *) NULL,true); } +#endif if (options & REFRESH_LOG) { /* @@ -4418,7 +4459,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, refresh_status(); if (options & REFRESH_THREADS) flush_thread_cache(); -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION if (options & REFRESH_MASTER) { tmp_write_to_binlog= 0; @@ -4433,7 +4474,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, result=load_des_key_file(des_key_file); } #endif -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION if (options & REFRESH_SLAVE) { tmp_write_to_binlog= 0; @@ -4443,14 +4484,15 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, UNLOCK_ACTIVE_MI; } #endif +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (options & REFRESH_USER_RESOURCES) reset_mqh(thd,(LEX_USER *) NULL); +#endif if (write_to_binlog) *write_to_binlog= tmp_write_to_binlog; return result; } - /* kill on thread @@ -4480,14 +4522,18 @@ void kill_one_thread(THD *thd, ulong id) VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (tmp) { +#ifndef NO_EMBEDDED_ACCESS_CHECKS if ((thd->master_access & SUPER_ACL) || !strcmp(thd->user,tmp->user)) +#endif { tmp->awake(1 /*prepare to die*/); error=0; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS else error=ER_KILL_DENIED_ERROR; +#endif pthread_mutex_unlock(&tmp->LOCK_delete); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 550e4bbe086..dd8d5613880 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -76,8 +76,15 @@ Long data handling: #define STMT_QUERY_LOG_LENGTH 8192 -extern int yyparse(void *thd); -static String null_string("NULL", 4, default_charset_info); +#ifdef EMBEDDED_LIBRARY +#define SETUP_PARAM_FUNCTION(fn_name) \ +static void fn_name(Item_param *param, uchar **pos, ulong data_len) +#else +#define SETUP_PARAM_FUNCTION(fn_name) \ +static void fn_name(Item_param *param, uchar **pos) +#endif + +String my_null_string("NULL", 4, default_charset_info); /* Find prepared statement in thd @@ -142,6 +149,7 @@ void free_prep_stmt(PREP_STMT *stmt, TREE_FREE mode, void *not_used) Send prepared stmt info to client after prepare */ +#ifndef EMBEDDED_LIBRARY static bool send_prep_stmt(PREP_STMT *stmt, uint columns) { NET *net=&stmt->thd->net; @@ -150,14 +158,22 @@ static bool send_prep_stmt(PREP_STMT *stmt, uint columns) int4store(buff+1, stmt->stmt_id); int2store(buff+5, columns); int2store(buff+7, stmt->param_count); -#ifndef EMBEDDED_LIBRARY /* This should be fixed to work with prepared statements */ return (my_net_write(net, buff, sizeof(buff)) || net_flush(net)); +} #else - return true; -#endif +static bool send_prep_stmt(PREP_STMT *stmt, uint columns __attribute__((unused))) +{ + THD *thd= stmt->thd; + + thd->client_stmt_id= stmt->stmt_id; + thd->client_param_count= stmt->param_count; + thd->net.last_errno= 0; + + return 0; } +#endif /*!EMBEDDED_LIBRAYR*/ /* Send information about all item parameters @@ -182,6 +198,7 @@ static bool send_item_params(PREP_STMT *stmt) caller by positing the pointer to param data */ +#ifndef EMBEDDED_LIBRARY static ulong get_param_length(uchar **packet) { reg1 uchar *pos= *packet; @@ -203,6 +220,10 @@ static ulong get_param_length(uchar **packet) (*packet)+=9; // Must be 254 when here return (ulong) uint4korr(pos+1); } +#else +#define get_param_length(A) data_len +#endif /*!EMBEDDED_LIBRARY*/ + /* Setup param conversion routines @@ -222,31 +243,31 @@ static ulong get_param_length(uchar **packet) */ -static void setup_param_tiny(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_tiny) { param->set_int((longlong)(**pos)); *pos+= 1; } -static void setup_param_short(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_short) { param->set_int((longlong)sint2korr(*pos)); *pos+= 2; } -static void setup_param_int32(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_int32) { param->set_int((longlong)sint4korr(*pos)); *pos+= 4; } -static void setup_param_int64(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_int64) { param->set_int((longlong)sint8korr(*pos)); *pos+= 8; } -static void setup_param_float(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_float) { float data; float4get(data,*pos); @@ -254,7 +275,7 @@ static void setup_param_float(Item_param *param, uchar **pos) *pos+= 4; } -static void setup_param_double(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_double) { double data; float8get(data,*pos); @@ -262,7 +283,7 @@ static void setup_param_double(Item_param *param, uchar **pos) *pos+= 8; } -static void setup_param_time(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_time) { ulong length; @@ -286,7 +307,7 @@ static void setup_param_time(Item_param *param, uchar **pos) *pos+= length; } -static void setup_param_datetime(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_datetime) { uint length= get_param_length(pos); @@ -316,7 +337,7 @@ static void setup_param_datetime(Item_param *param, uchar **pos) *pos+= length; } -static void setup_param_date(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_date) { ulong length; @@ -338,14 +359,14 @@ static void setup_param_date(Item_param *param, uchar **pos) *pos+= length; } -static void setup_param_str(Item_param *param, uchar **pos) +SETUP_PARAM_FUNCTION(setup_param_str) { ulong len= get_param_length(pos); param->set_value((const char *)*pos, len); *pos+= len; } -static void setup_param_functions(Item_param *param, uchar param_type) +void setup_param_functions(Item_param *param, uchar param_type) { switch (param_type) { case FIELD_TYPE_TINY: @@ -391,6 +412,7 @@ static void setup_param_functions(Item_param *param, uchar param_type) } } +#ifndef EMBEDDED_LIBRARY /* Update the parameter markers by reading data from client packet and if binary/update log is set, generate the valid query. @@ -420,7 +442,7 @@ static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos) if (IS_PARAM_NULL(pos,param_no)) { param->maybe_null= param->null_value= 1; - res= &null_string; + res= &my_null_string; } else { @@ -476,11 +498,7 @@ static bool setup_params_data(PREP_STMT *stmt) Item_param *param; DBUG_ENTER("setup_params_data"); -#ifndef EMBEDDED_LIBRARY uchar *pos=(uchar*) thd->net.read_pos+1+MYSQL_STMT_HEADER; //skip header -#else - uchar *pos= 0; //just to compile TODO code for embedded case -#endif uchar *read_pos= pos+(stmt->param_count+7) / 8; //skip null bits if (*read_pos++) //types supplied / first execute @@ -500,6 +518,8 @@ static bool setup_params_data(PREP_STMT *stmt) DBUG_RETURN(0); } +#endif /*!EMBEDDED_LIBRARY*/ + /* Validate the following information for INSERT statement: - field existance @@ -517,16 +537,18 @@ static bool mysql_test_insert_fields(PREP_STMT *stmt, List_item *values; DBUG_ENTER("mysql_test_insert_fields"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS my_bool update=(thd->lex.value_list.elements ? UPDATE_ACL : 0); ulong privilege= (thd->lex.duplicates == DUP_REPLACE ? INSERT_ACL | DELETE_ACL : INSERT_ACL | update); if (check_access(thd,privilege,table_list->db, - &table_list->grant.privilege) || - (grant_option && check_grant(thd,privilege,table_list)) || - open_and_lock_tables(thd, table_list)) + &table_list->grant.privilege,0,0) || + (grant_option && check_grant(thd,privilege,table_list))) + DBUG_RETURN(1); +#endif + if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(1); - table= table_list->table; if ((values= its++)) @@ -574,12 +596,14 @@ static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list, THD *thd= stmt->thd; DBUG_ENTER("mysql_test_upd_fields"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_access(thd,UPDATE_ACL,table_list->db, - &table_list->grant.privilege) || - (grant_option && check_grant(thd,UPDATE_ACL,table_list)) || - open_and_lock_tables(thd, table_list)) + &table_list->grant.privilege,0,0) || + (grant_option && check_grant(thd,UPDATE_ACL,table_list))) + DBUG_RETURN(1); +#endif + if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(1); - if (setup_tables(table_list) || setup_fields(thd, 0, table_list, fields, 1, 0, 0) || setup_conds(thd, table_list, &conds) || thd->net.report_error) @@ -620,15 +644,16 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables, select_result *result= thd->lex.result; DBUG_ENTER("mysql_test_select_fields"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL; if (tables) { - if (check_table_access(thd, privilege, tables)) + if (check_table_access(thd, privilege, tables,0)) DBUG_RETURN(1); } - else if (check_access(thd, privilege, "*any*")) + else if (check_access(thd, privilege, "*any*",0,0,0)) DBUG_RETURN(1); - +#endif if ((&lex->select_lex != lex->all_selects_list && lex->unit.create_total_list(thd, lex, &tables, 0))) DBUG_RETURN(1); @@ -659,13 +684,13 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables, wild_num, conds, og_num, order, group, having, proc, select_lex, unit, 0)) DBUG_RETURN(1); -#ifndef EMBEDDED_LIBRARY if (send_prep_stmt(stmt, fields.elements) || thd->protocol_simple.send_fields(&fields, 0) || +#ifndef EMBEDDED_LIBRARY net_flush(&thd->net) || +#endif send_item_params(stmt)) DBUG_RETURN(1); -#endif join->cleanup(); } DBUG_RETURN(0); @@ -784,10 +809,18 @@ static bool init_param_items(PREP_STMT *stmt) if (mysql_bin_log.is_open() || mysql_update_log.is_open()) { stmt->log_full_query= 1; +#ifndef EMBEDDED_LIBRARY stmt->setup_params= insert_params_withlog; +#else + stmt->setup_params_data= setup_params_data_withlog; +#endif } else +#ifndef EMBEDDED_LIBRARY stmt->setup_params= insert_params; // not fully qualified query +#else + stmt->setup_params_data= setup_params_data; +#endif if (!stmt->param_count) stmt->param= (Item_param **)0; @@ -873,11 +906,21 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); + + // save WHERE clause pointers to avoid damaging they by optimisation + for (SELECT_LEX *sl= thd->lex.all_selects_list; + sl; + sl= sl->next_select_in_list()) + { + sl->prep_where= sl->where; + } + if (init_param_items(&stmt)) goto err; + - stmt.mem_root= stmt.thd->mem_root; + stmt.mem_root= stmt.thd->mem_root; tree_insert(&thd->prepared_statements, (void *)&stmt, 0, (void *)0); thd->mem_root= thd_root; // restore main mem_root DBUG_RETURN(0); @@ -919,10 +962,25 @@ void mysql_stmt_execute(THD *thd, char *packet) LEX thd_lex= thd->lex; thd->lex= stmt->lex; + + for (SELECT_LEX *sl= stmt->lex.all_selects_list; + sl; + sl= sl->next_select_in_list()) + { + // copy WHERE clause pointers to avoid damaging they by optimisation + if (sl->prep_where) + sl->where= sl->prep_where->copy_andor_structure(thd); + DBUG_ASSERT(sl->join == 0); + } init_stmt_execute(stmt); +#ifndef EMBEDDED_LIBRARY if (stmt->param_count && setup_params_data(stmt)) DBUG_VOID_RETURN; +#else + if (stmt->param_count && (*stmt->setup_params_data)(stmt)) + DBUG_VOID_RETURN; +#endif if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); @@ -1030,16 +1088,17 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) PREP_STMT *stmt; DBUG_ENTER("mysql_stmt_get_longdata"); +#ifndef EMBEDDED_LIBRARY /* The following should never happen */ if (packet_length < MYSQL_LONG_DATA_HEADER+1) { my_error(ER_WRONG_ARGUMENTS, MYF(0), "get_longdata"); DBUG_VOID_RETURN; } +#endif ulong stmt_id= uint4korr(pos); uint param_number= uint2korr(pos+4); - pos+= MYSQL_LONG_DATA_HEADER; // Point to data if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata"))) { @@ -1051,6 +1110,7 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) DBUG_VOID_RETURN; } +#ifndef EMBEDDED_LIBRARY if (param_number >= stmt->param_count) { /* Error will be sent in execute call */ @@ -1059,8 +1119,15 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "get_longdata"); DBUG_VOID_RETURN; } + pos+= MYSQL_LONG_DATA_HEADER; // Point to data +#endif + Item_param *param= *(stmt->param+param_number); +#ifndef EMBEDDED_LIBRARY param->set_longdata(pos, packet_length-MYSQL_LONG_DATA_HEADER-1); +#else + param->set_longdata(thd->extra_data, thd->extra_length); +#endif stmt->long_data_used= 1; DBUG_VOID_RETURN; } diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 3cdf033c477..7bb51989cc3 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -24,8 +24,6 @@ #include "log_event.h" #include <my_dir.h> -extern const char* any_db; - int max_binlog_dump_events = 0; // unlimited my_bool opt_sporadic_binlog_dump_fail = 0; static int binlog_dump_count = 0; @@ -545,7 +543,7 @@ Increase max_allowed_packet on master"; if (!thd->killed) { /* Note that the following call unlocks lock_log */ - mysql_bin_log.wait_for_update(thd); + mysql_bin_log.wait_for_update(thd, 0); } else pthread_mutex_unlock(log_lock); @@ -560,7 +558,7 @@ Increase max_allowed_packet on master"; if (read_packet) { - thd->proc_info = "sending update to slave"; + thd->proc_info = "Sending binlog event to slave"; if (my_net_write(net, (char*)packet->ptr(), packet->length()) ) { errmsg = "Failed on my_net_write()"; @@ -597,7 +595,7 @@ Increase max_allowed_packet on master"; { bool loop_breaker = 0; // need this to break out of the for loop from switch - thd->proc_info = "switching to next log"; + thd->proc_info = "Finished reading one binlog; switching to next binlog"; switch (mysql_bin_log.find_next_log(&linfo, 1)) { case LOG_INFO_EOF: loop_breaker = (flags & BINLOG_DUMP_NON_BLOCK); @@ -636,14 +634,14 @@ end: (void)my_close(file, MYF(MY_WME)); send_eof(thd); - thd->proc_info = "waiting to finalize termination"; + thd->proc_info = "Waiting to finalize termination"; pthread_mutex_lock(&LOCK_thread_count); thd->current_linfo = 0; pthread_mutex_unlock(&LOCK_thread_count); DBUG_VOID_RETURN; err: - thd->proc_info = "waiting to finalize termination"; + thd->proc_info = "Waiting to finalize termination"; end_io_cache(&log); /* Exclude iteration through thread list @@ -663,13 +661,13 @@ err: int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) { - int slave_errno; + int slave_errno= 0; if (!thd) thd = current_thd; int thread_mask; DBUG_ENTER("start_slave"); - if (check_access(thd, SUPER_ACL, any_db)) + if (check_access(thd, SUPER_ACL, any_db,0,0,0)) DBUG_RETURN(1); lock_slave_threads(mi); // this allows us to cleanly read slave_running // Get a mask of _stopped_ threads @@ -687,21 +685,88 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) if (init_master_info(mi,master_info_file,relay_log_info_file, 0)) slave_errno=ER_MASTER_INFO; else if (server_id_supplied && *mi->host) - slave_errno = start_slave_threads(0 /*no mutex */, + { + /* + If we will start SQL thread we will care about UNTIL options + If not and they are specified we will ignore them and warn user + about this fact. + */ + if (thread_mask & SLAVE_SQL) + { + pthread_mutex_lock(&mi->rli.data_lock); + + if (thd->lex.mi.pos) + { + mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_MASTER_POS; + mi->rli.until_log_pos= thd->lex.mi.pos; + /* + We don't check thd->lex.mi.log_file_name for NULL here + since it is checked in sql_yacc.yy + */ + strmake(mi->rli.until_log_name, thd->lex.mi.log_file_name, + sizeof(mi->rli.until_log_name)-1); + } + else if (thd->lex.mi.relay_log_pos) + { + mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_RELAY_POS; + mi->rli.until_log_pos= thd->lex.mi.relay_log_pos; + strmake(mi->rli.until_log_name, thd->lex.mi.relay_log_name, + sizeof(mi->rli.until_log_name)-1); + } + else + clear_until_condition(&mi->rli); + + if (mi->rli.until_condition != RELAY_LOG_INFO::UNTIL_NONE) + { + /* Preparing members for effective until condition checking */ + const char *p= fn_ext(mi->rli.until_log_name); + char *p_end; + if (*p) + { + //p points to '.' + mi->rli.until_log_name_extension= strtoul(++p,&p_end, 10); + /* + p_end points to the first invalid character. If it equals + to p, no digits were found, error. If it contains '\0' it + means conversion went ok. + */ + if(p_end==p || *p_end) + slave_errno=ER_BAD_SLAVE_UNTIL_COND; + } + else + slave_errno=ER_BAD_SLAVE_UNTIL_COND; + + /* mark the cached result of the UNTIL comparison as "undefined" */ + mi->rli.until_log_names_cmp_result= + RELAY_LOG_INFO::UNTIL_LOG_NAMES_CMP_UNKNOWN; + + /* Issuing warning then started without --skip-slave-start */ + if (!opt_skip_slave_start) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_MISSING_SKIP_SLAVE, + ER(ER_MISSING_SKIP_SLAVE)); + } + + pthread_mutex_unlock(&mi->rli.data_lock); + } + else if (thd->lex.mi.pos || thd->lex.mi.relay_log_pos) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNTIL_COND_IGNORED, + ER(ER_UNTIL_COND_IGNORED)); + + + if(!slave_errno) + slave_errno = start_slave_threads(0 /*no mutex */, 1 /* wait for start */, mi, master_info_file,relay_log_info_file, thread_mask); + } else slave_errno = ER_BAD_SLAVE; } else - { //no error if all threads are already started, only a warning - slave_errno= 0; push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SLAVE_WAS_RUNNING, ER(ER_SLAVE_WAS_RUNNING)); - } unlock_slave_threads(mi); @@ -724,7 +789,7 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) if (!thd) thd = current_thd; - if (check_access(thd, SUPER_ACL, any_db)) + if (check_access(thd, SUPER_ACL, any_db,0,0,0)) return 1; thd->proc_info = "Killing slave"; int thread_mask; @@ -814,6 +879,8 @@ int reset_slave(THD *thd, MASTER_INFO* mi) */ init_master_info_with_options(mi); clear_last_slave_error(&mi->rli); + clear_until_condition(&mi->rli); + // close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 end_master_info(mi); // and delete these two files @@ -903,7 +970,7 @@ int change_master(THD* thd, MASTER_INFO* mi) DBUG_RETURN(1); } - thd->proc_info = "changing master"; + thd->proc_info = "Changing master"; LEX_MASTER_INFO* lex_mi = &thd->lex.mi; // TODO: see if needs re-write if (init_master_info(mi, master_info_file, relay_log_info_file, 0)) @@ -949,6 +1016,25 @@ int change_master(THD* thd, MASTER_INFO* mi) mi->port = lex_mi->port; if (lex_mi->connect_retry) mi->connect_retry = lex_mi->connect_retry; + + if (lex_mi->ssl != LEX_MASTER_INFO::SSL_UNCHANGED) + mi->ssl= (lex_mi->ssl == LEX_MASTER_INFO::SSL_ENABLE); + if (lex_mi->ssl_ca) + strmake(mi->ssl_ca, lex_mi->ssl_ca, sizeof(mi->ssl_ca)-1); + if (lex_mi->ssl_capath) + strmake(mi->ssl_capath, lex_mi->ssl_capath, sizeof(mi->ssl_capath)-1); + if (lex_mi->ssl_cert) + strmake(mi->ssl_cert, lex_mi->ssl_cert, sizeof(mi->ssl_cert)-1); + if (lex_mi->ssl_cipher) + strmake(mi->ssl_cipher, lex_mi->ssl_cipher, sizeof(mi->ssl_cipher)-1); + if (lex_mi->ssl_key) + strmake(mi->ssl_key, lex_mi->ssl_key, sizeof(mi->ssl_key)-1); +#ifndef HAVE_OPENSSL + if (lex_mi->ssl || lex_mi->ssl_ca || lex_mi->ssl_capath || + lex_mi->ssl_cert || lex_mi->ssl_cipher || lex_mi->ssl_key ) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_SLAVE_IGNORED_SSL_PARAMS, ER(ER_SLAVE_IGNORED_SSL_PARAMS)); +#endif if (lex_mi->relay_log_name) { @@ -969,7 +1055,7 @@ int change_master(THD* thd, MASTER_INFO* mi) if (need_relay_log_purge) { relay_log_purge= 1; - thd->proc_info="purging old relay logs"; + thd->proc_info="Purging old relay logs"; if (purge_relay_logs(&mi->rli, thd, 0 /* not only reset, but also reinit */, &errmsg)) @@ -1008,6 +1094,7 @@ int change_master(THD* thd, MASTER_INFO* mi) mi->rli.abort_pos_wait++; /* for MASTER_POS_WAIT() to abort */ /* Clear the error, for a clean start. */ clear_last_slave_error(&mi->rli); + clear_until_condition(&mi->rli); /* If we don't write new coordinates to disk now, then old will remain in relay-log.info until START SLAVE is issued; but if mysqld is shutdown @@ -1258,7 +1345,7 @@ int log_loaded_block(IO_CACHE* file) lf_info->last_pos_in_file = file->pos_in_file; if (lf_info->wrote_create_file) { - Append_block_log_event a(lf_info->thd, buffer, block_len, + Append_block_log_event a(lf_info->thd, lf_info->db, buffer, block_len, lf_info->log_delayed); mysql_bin_log.write(&a); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 15d6b3954ff..ff41e9fd067 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -32,7 +32,7 @@ const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref", "MAYBE_REF","ALL","range","index","fulltext", - "ref_or_null","simple_in","index_in" + "ref_or_null","unique_subquery","index_subquery" }; static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); @@ -117,10 +117,8 @@ static Item* part_of_refkey(TABLE *form,Field *field); static uint find_shortest_key(TABLE *table, key_map usable_keys); static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order, ha_rows select_limit, bool no_changes); -static int create_sort_index(THD *thd, JOIN_TAB *tab,ORDER *order, +static int create_sort_index(THD *thd, JOIN *join, ORDER *order, ha_rows filesort_limit, ha_rows select_limit); -static int create_sort_index(THD *thd, JOIN_TAB *tab,ORDER *order, - ha_rows select_limit); static int remove_duplicates(JOIN *join,TABLE *entry,List<Item> &fields, Item *having); static int remove_dup_with_compare(THD *thd, TABLE *entry, Field **field, @@ -160,8 +158,9 @@ static void copy_sum_funcs(Item_sum **func_ptr); static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab); static bool init_sum_functions(Item_sum **func, Item_sum **end); static bool update_sum_func(Item_sum **func); -static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, +static void select_describe(JOIN *join, bool need_tmp_table,bool need_order, bool distinct, const char *message=NullS); +static Item *remove_additional_cond(Item* conds); /* @@ -326,8 +325,8 @@ JOIN::prepare(Item ***rref_pointer_array, { Item_subselect::trans_res res; if ((res= subselect->select_transformer(this)) != - Item_subselect::OK) - DBUG_RETURN((res == Item_subselect::ERROR)); + Item_subselect::RES_OK) + DBUG_RETURN((res == Item_subselect::RES_ERROR)); } } @@ -466,8 +465,10 @@ bool JOIN::test_in_subselect(Item **where) ((class Item_func *)this->conds)->functype() == Item_func::COND_AND_FUNC) { - *where= conds; - join_tab->info= "Using index; Using where"; + if ((*where= remove_additional_cond(conds))) + join_tab->info= "Using index; Using where"; + else + join_tab->info= "Using index"; return 1; } return 0; @@ -477,8 +478,7 @@ bool JOIN::test_in_subselect(Item **where) /* global select optimisation. return 0 - success - 1 - go out - -1 - go out with cleaning + 1 - error error code saved in field 'error' */ int @@ -515,11 +515,9 @@ JOIN::optimize() conds= optimize_cond(conds,&cond_value); if (thd->net.report_error) { - // quick abort - delete procedure; - error= thd->is_fatal_error ? -1 : 1; + error= 1; DBUG_PRINT("error",("Error from optimize_cond")); - DBUG_RETURN(error); + DBUG_RETURN(1); } if (cond_value == Item::COND_FALSE || @@ -542,8 +540,7 @@ JOIN::optimize() { if (res > 1) { - delete procedure; - DBUG_RETURN(-1); + DBUG_RETURN(1); } if (res < 0) { @@ -776,13 +773,14 @@ JOIN::optimize() { if (test_in_subselect(&where)) { - join_tab[0].type= JT_SIMPLE_IN; + join_tab[0].type= JT_UNIQUE_SUBQUERY; error= 0; DBUG_RETURN(unit->item-> - change_engine(new subselect_simplein_engine(thd, - join_tab, - unit->item, - where))); + change_engine(new + subselect_uniquesubquery_engine(thd, + join_tab, + unit->item, + where))); } } else if (join_tab[0].type == JT_REF && @@ -790,14 +788,15 @@ JOIN::optimize() { if (test_in_subselect(&where)) { - join_tab[0].type= JT_INDEX_IN; + join_tab[0].type= JT_INDEX_SUBQUERY; error= 0; DBUG_RETURN(unit->item-> - change_engine(new subselect_indexin_engine(thd, - join_tab, - unit->item, - where, - 0))); + change_engine(new + subselect_indexsubquery_engine(thd, + join_tab, + unit->item, + where, + 0))); } } } else if (join_tab[0].type == JT_REF_OR_NULL && @@ -806,14 +805,20 @@ JOIN::optimize() ((Item_func *) having)->functype() == Item_func::ISNOTNULLTEST_FUNC) { - join_tab[0].type= JT_INDEX_IN; + join_tab[0].type= JT_INDEX_SUBQUERY; error= 0; + + if ((conds= remove_additional_cond(conds))) + join_tab->info= "Using index; Using where"; + else + join_tab->info= "Using index"; + DBUG_RETURN(unit->item-> - change_engine(new subselect_indexin_engine(thd, - join_tab, - unit->item, - conds, - 1))); + change_engine(new subselect_indexsubquery_engine(thd, + join_tab, + unit->item, + conds, + 1))); } } @@ -916,7 +921,7 @@ JOIN::optimize() { DBUG_PRINT("info",("Sorting for group")); thd->proc_info="Sorting for group"; - if (create_sort_index(thd, &join_tab[const_tables], group_list, + if (create_sort_index(thd, this, group_list, HA_POS_ERROR, HA_POS_ERROR) || alloc_group_fields(this, group_list) || make_sum_func_list(all_fields, fields_list, 1)) @@ -931,7 +936,7 @@ JOIN::optimize() { DBUG_PRINT("info",("Sorting for order")); thd->proc_info="Sorting for order"; - if (create_sort_index(thd, &join_tab[const_tables], order, + if (create_sort_index(thd, this, order, HA_POS_ERROR, HA_POS_ERROR)) DBUG_RETURN(1); order=0; @@ -1005,12 +1010,6 @@ JOIN::reinit() /* Reset of sum functions */ first_record= 0; - if (sum_funcs) - { - Item_sum *func, **func_ptr= sum_funcs; - while ((func= *(func_ptr++))) - func->null_value= 1; - } if (exec_tmp_table1) { @@ -1045,6 +1044,7 @@ JOIN::exec() DBUG_ENTER("JOIN::exec"); error= 0; + thd->limit_found_rows= thd->examined_row_count= 0; if (procedure) { if (procedure->change_columns(fields_list) || @@ -1235,7 +1235,7 @@ JOIN::exec() if (curr_join->group_list) { thd->proc_info= "Creating sort index"; - if (create_sort_index(thd, curr_join->join_tab, curr_join->group_list, + if (create_sort_index(thd, curr_join, curr_join->group_list, HA_POS_ERROR, HA_POS_ERROR) || make_group_fields(this, curr_join)) { @@ -1416,7 +1416,7 @@ JOIN::exec() } } } - if (create_sort_index(thd, &curr_join->join_tab[curr_join->const_tables], + if (create_sort_index(thd, curr_join, curr_join->group_list ? curr_join->group_list : curr_join->order, curr_join->select_limit, unit->select_limit_cnt)) @@ -1427,6 +1427,8 @@ JOIN::exec() thd->proc_info="Sending data"; error= thd->net.report_error || do_select(curr_join, curr_fields_list, NULL, procedure); + thd->limit_found_rows= curr_join->send_records; + thd->examined_row_count= curr_join->examined_rows; DBUG_VOID_RETURN; } @@ -1510,7 +1512,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array, conds, og_num, order, group, having, proc_param, select_lex, unit, tables_and_fields_initied)) { - DBUG_RETURN(-1); + goto err; } } join->select_options= select_options; @@ -1525,15 +1527,12 @@ mysql_select(THD *thd, Item ***rref_pointer_array, conds, og_num, order, group, having, proc_param, select_lex, unit, tables_and_fields_initied)) { - DBUG_RETURN(-1); + goto err; } } if ((err= join->optimize())) { - if (err == -1) - DBUG_RETURN(join->error); - DBUG_ASSERT(err == 1); goto err; // 1 } @@ -1549,8 +1548,6 @@ err: (join->tmp_join->error=join->error,join->tmp_join): join); - thd->limit_found_rows= curr_join->send_records; - thd->examined_row_count= curr_join->examined_rows; thd->proc_info="end"; err= join->cleanup(); if (thd->net.report_error) @@ -2053,28 +2050,34 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, static void add_key_field(KEY_FIELD **key_fields,uint and_level, - Field *field,bool eq_func,Item *value, + Field *field,bool eq_func,Item **value, uint num_values, table_map usable_tables) { uint exists_optimize= 0; if (!(field->flags & PART_KEY_FLAG)) { // Don't remove column IS NULL on a LEFT JOIN table - if (!eq_func || !value || value->type() != Item::NULL_ITEM || - !field->table->maybe_null || field->null_ptr) + if (!eq_func || (*value)->type() != Item::NULL_ITEM || + !field->table->maybe_null || field->null_ptr) return; // Not a key. Skip it exists_optimize= KEY_OPTIMIZE_EXISTS; } else { table_map used_tables=0; - if (value && ((used_tables=value->used_tables()) & - (field->table->map | RAND_TABLE_BIT))) + bool optimizable=0; + for (uint i=0; i<num_values; i++) + { + used_tables|=(*value)->used_tables(); + if (!((*value)->used_tables() & (field->table->map | RAND_TABLE_BIT))) + optimizable=1; + } + if (!optimizable) return; if (!(usable_tables & field->table->map)) { - if (!eq_func || !value || value->type() != Item::NULL_ITEM || - !field->table->maybe_null || field->null_ptr) + if (!eq_func || (*value)->type() != Item::NULL_ITEM || + !field->table->maybe_null || field->null_ptr) return; // Can't use left join optimize exists_optimize= KEY_OPTIMIZE_EXISTS; } @@ -2085,12 +2088,6 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, field->table->keys_in_use_for_query); stat[0].keys|= possible_keys; // Add possible keys - if (!value) - { // Probably BETWEEN or IN - stat[0].const_keys |= possible_keys; - return; // Can't be used as eq key - } - /* Save the following cases: Field op constant @@ -2099,26 +2096,38 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Field op formula Field IS NULL Field IS NOT NULL + Field BETWEEN ... + Field IN ... */ stat[0].key_dependent|=used_tables; - if (value->const_item()) - stat[0].const_keys |= possible_keys; + bool is_const=1; + for (uint i=0; i<num_values; i++) + is_const&= (*value)->const_item(); + if (is_const) + stat[0].const_keys |= possible_keys; /* We can't always use indexes when comparing a string index to a - number. cmp_type() is checked to allow compare of dates to numbers - */ + number. cmp_type() is checked to allow compare of dates to numbers. + eq_func is NEVER true when num_values > 1 + */ if (!eq_func || field->result_type() == STRING_RESULT && - value->result_type() != STRING_RESULT && - field->cmp_type() != value->result_type()) + (*value)->result_type() != STRING_RESULT && + field->cmp_type() != (*value)->result_type()) return; } } + DBUG_ASSERT(num_values == 1); + /* + For the moment eq_func is always true. This slot is reserved for future + extensions where we want to remembers other things than just eq comparisons + */ + DBUG_ASSERT(eq_func); /* Store possible eq field */ (*key_fields)->field= field; (*key_fields)->eq_func= eq_func; - (*key_fields)->val= value; + (*key_fields)->val= *value; (*key_fields)->level= and_level; (*key_fields)->optimize= exists_optimize; (*key_fields)++; @@ -2167,12 +2176,14 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, case Item_func::OPTIMIZE_NONE: break; case Item_func::OPTIMIZE_KEY: + // BETWEEN or IN if (cond_func->key_item()->real_item()->type() == Item::FIELD_ITEM && !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) add_key_field(key_fields,*and_level, - ((Item_field*) (cond_func->key_item()->real_item())) - ->field, - 0,(Item*) 0,usable_tables); + ((Item_field*) (cond_func->key_item()->real_item()))-> + field, 0, + cond_func->arguments()+1, cond_func->argument_count()-1, + usable_tables); break; case Item_func::OPTIMIZE_OP: { @@ -2186,7 +2197,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, ((Item_field*) (cond_func->arguments()[0])->real_item()) ->field, equal_func, - (cond_func->arguments()[1]),usable_tables); + cond_func->arguments()+1, 1, usable_tables); } if (cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM && cond_func->functype() != Item_func::LIKE_FUNC && @@ -2196,7 +2207,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, ((Item_field*) (cond_func->arguments()[1])->real_item()) ->field, equal_func, - (cond_func->arguments()[0]),usable_tables); + cond_func->arguments(),1,usable_tables); } break; } @@ -2205,11 +2216,14 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, if (cond_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM && !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) { + Item *tmp=new Item_null; + if (!tmp) // Should never be true + return; add_key_field(key_fields,*and_level, ((Item_field*) (cond_func->arguments()[0])->real_item()) ->field, cond_func->functype() == Item_func::ISNULL_FUNC, - new Item_null, usable_tables); + &tmp, 1, usable_tables); } break; } @@ -2461,7 +2475,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) Constant tables are ignored. To avoid bad matches, we don't make ref_table_rows less than 100. */ - keyuse->ref_table_rows= ~(table_map) 0; // If no ref + keyuse->ref_table_rows= ~(ha_rows) 0; // If no ref if (keyuse->used_tables & (map= (keyuse->used_tables & ~join->const_table_map & ~OUTER_REF_TABLE_BIT))) @@ -3328,13 +3342,33 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) join->best_positions[i].records_read && !(join->select_options & OPTION_FOUND_ROWS))) { + /* Join with outer join condition */ + COND *orig_cond=sel->cond; + sel->cond=and_conds(sel->cond,tab->on_expr); if (sel->test_quick_select(tab->keys, used_tables & ~ current_map, (join->select_options & OPTION_FOUND_ROWS ? HA_POS_ERROR : join->unit->select_limit_cnt)) < 0) - DBUG_RETURN(1); // Impossible range + { + /* + Before reporting "Impossible WHERE" for the whole query + we have to check isn't it only "impossible ON" instead + */ + sel->cond=orig_cond; + if (!tab->on_expr || + sel->test_quick_select(tab->keys, + used_tables & ~ current_map, + (join->select_options & + OPTION_FOUND_ROWS ? + HA_POS_ERROR : + join->unit->select_limit_cnt)) < 0) + DBUG_RETURN(1); // Impossible WHERE + } + else + sel->cond=orig_cond; + /* Fix for EXPLAIN */ if (sel->quick) join->best_positions[i].records_read= sel->quick->records; @@ -3997,9 +4031,42 @@ change_cond_ref_to_const(I_List<COND_CMP> *save_list,Item *and_father, } } +/* + Remove additional condition inserted by IN/ALL/ANY transformation + + SYNOPSIS + remove_additional_cond() + conds - condition for processing + + RETURN VALUES + new conditions +*/ + +static Item *remove_additional_cond(Item* conds) +{ + if (conds->name == in_additional_cond) + return 0; + if (conds->type() == Item::COND_ITEM) + { + Item_cond *cnd= (Item_cond*) conds; + List_iterator<Item> li(*(cnd->argument_list())); + Item *item; + while ((item= li++)) + { + if (item->name == in_additional_cond) + { + li.remove(); + if (cnd->argument_list()->elements == 1) + return cnd->argument_list()->head(); + return conds; + } + } + } + return conds; +} static void -propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_level, +propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_father, COND *cond) { if (cond->type() == Item::COND_ITEM) @@ -4025,7 +4092,7 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_level, cond_cmp->cmp_func->arguments()[1]); } } - else if (and_level != cond && !cond->marker) // In a AND group + else if (and_father != cond && !cond->marker) // In a AND group { if (cond->type() == Item::FUNC_ITEM && (((Item_func*) cond)->functype() == Item_func::EQ_FUNC || @@ -4043,7 +4110,7 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_level, func->arguments()[1]=resolve_const_item(func->arguments()[1], func->arguments()[0]); func->update_used_tables(); - change_cond_ref_to_const(save_list,and_level,and_level, + change_cond_ref_to_const(save_list,and_father,and_father, func->arguments()[0], func->arguments()[1]); } @@ -4052,7 +4119,7 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_level, func->arguments()[0]=resolve_const_item(func->arguments()[0], func->arguments()[1]); func->update_used_tables(); - change_cond_ref_to_const(save_list,and_level,and_level, + change_cond_ref_to_const(save_list,and_father,and_father, func->arguments()[1], func->arguments()[0]); } @@ -4796,7 +4863,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, keyinfo->algorithm= HA_KEY_ALG_UNDEF; for (; group ; group=group->next,key_part_info++) { - Field *field=(*group->item)->tmp_table_field(); + Field *field=(*group->item)->get_tmp_table_field(); bool maybe_null=(*group->item)->maybe_null; key_part_info->null_bit=0; key_part_info->field= field; @@ -5980,8 +6047,7 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (!join->first_record) { /* No matching rows for group function */ - clear_tables(join); - copy_fields(&join->tmp_table_param); + join->clear(); } if (join->having && join->having->val_int() == 0) error= -1; // Didn't satisfy having @@ -6065,7 +6131,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), Item *item= *group->item; if (item->maybe_null) { - Field *field=item->tmp_table_field(); + Field *field=item->get_tmp_table_field(); field->ptr[-1]= (byte) (field->is_null() ? 1 : 0); } } @@ -6247,8 +6313,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (!join->first_record) { /* No matching rows for group function */ - clear_tables(join); - copy_fields(&join->tmp_table_param); + join->clear(); } copy_sum_funcs(join->sum_funcs); if (!join->having || join->having->val_int()) @@ -6770,16 +6835,23 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, */ static int -create_sort_index(THD *thd, JOIN_TAB *tab, ORDER *order, +create_sort_index(THD *thd, JOIN *join, ORDER *order, ha_rows filesort_limit, ha_rows select_limit) { SORT_FIELD *sortorder; uint length; ha_rows examined_rows; - TABLE *table=tab->table; - SQL_SELECT *select=tab->select; + TABLE *table; + SQL_SELECT *select; + JOIN_TAB *tab; DBUG_ENTER("create_sort_index"); + if (join->tables == join->const_tables) + DBUG_RETURN(0); // One row, no need to sort + tab= join->join_tab + join->const_tables; + table= tab->table; + select= tab->select; + if (test_if_skip_sort_order(tab,order,select_limit,0)) DBUG_RETURN(0); if (!(sortorder=make_unireg_sortorder(order,&length))) @@ -6928,7 +7000,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) Item *item; while ((item=it++)) { - if (item->tmp_table_field() && ! item->const_item()) + if (item->get_tmp_table_field() && ! item->const_item()) field_count++; } @@ -7124,7 +7196,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, goto err; } else - (void) hash_insert(&hash, key_pos-key_length); + (void) my_hash_insert(&hash, key_pos-key_length); key_pos+=extra_length; } my_free((char*) key_buffer,MYF(0)); @@ -7164,7 +7236,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length) pos->field= ((Item_field*) (*order->item))->field; else if (order->item[0]->type() == Item::SUM_FUNC_ITEM && !order->item[0]->const_item()) - pos->field= ((Item_sum*) order->item[0])->tmp_table_field(); + pos->field= ((Item_sum*) order->item[0])->get_tmp_table_field(); else if (order->item[0]->type() == Item::COPY_STR_ITEM) { // Blob patch pos->item= ((Item_copy_string*) (*order->item))->item; @@ -7761,7 +7833,7 @@ calc_group_buffer(JOIN *join,ORDER *group) join->group= 1; for (; group ; group=group->next) { - Field *field=(*group->item)->tmp_table_field(); + Field *field=(*group->item)->get_tmp_table_field(); if (field) { if (field->type() == FIELD_TYPE_BLOB) @@ -8105,7 +8177,7 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array, { item_field= item->get_tmp_table_item(thd); } - else if ((field= item->tmp_table_field())) + else if ((field= item->get_tmp_table_field())) { if (item->type() == Item::SUM_FUNC_ITEM && field->table->group) item_field= ((Item_sum*) item)->result_item(field); @@ -8208,7 +8280,7 @@ update_tmptable_sum_func(Item_sum **func_ptr, { Item_sum *func; while ((func= *(func_ptr++))) - func->update_field(0); + func->update_field(); } @@ -8536,6 +8608,26 @@ int JOIN::rollup_send_data(uint idx) return 0; } +/* + clear results if there are not rows found for group + (end_send_group/end_write_group) + + SYNOPSYS + JOIN::clear() +*/ + +void JOIN::clear() +{ + clear_tables(this); + copy_fields(&tmp_table_param); + + if (sum_funcs) + { + Item_sum *func, **func_ptr= sum_funcs; + while ((func= *(func_ptr++))) + func->clear(); + } +} /**************************************************************************** EXPLAIN handling diff --git a/sql/sql_select.h b/sql/sql_select.h index 208eaaea7bd..6c17a646ee6 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -76,7 +76,7 @@ typedef struct st_join_cache { enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF, JT_ALL, JT_RANGE, JT_NEXT, JT_FT, JT_REF_OR_NULL, - JT_SIMPLE_IN, JT_INDEX_IN}; + JT_UNIQUE_SUBQUERY, JT_INDEX_SUBQUERY}; class JOIN; @@ -279,6 +279,7 @@ class JOIN :public Sql_alloc Item_sum ***func); int rollup_send_data(uint idx); bool test_in_subselect(Item **where); + void clear(); }; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 9084269f486..8550973ff88 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -73,10 +73,12 @@ mysqld_show_dbs(THD *thd,const char *wild) while ((file_name=it++)) { +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (thd->master_access & (DB_ACLS | SHOW_DB_ACL) || acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr, thd->priv_user, file_name,0) || (grant_option && !check_grant_db(thd, file_name))) +#endif { protocol->prepare_for_resend(); protocol->store(file_name, system_charset_info); @@ -437,6 +439,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, continue; } } +#ifndef NO_EMBEDDED_ACCESS_CHECKS /* Don't show tables where we don't have any privileges */ if (db && !(col_access & TABLE_ACLS)) { @@ -446,6 +449,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, if (check_grant(thd,TABLE_ACLS,&table_list,1,1)) continue; } +#endif if (files->push_back(thd->strdup(file->name))) { my_dirend(dirp); @@ -501,7 +505,9 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) item->maybe_null=1; field_list.push_back(item=new Item_datetime("Check_time")); item->maybe_null=1; - field_list.push_back(item=new Item_empty_string("Charset",32)); + field_list.push_back(item=new Item_empty_string("Collation",32)); + item->maybe_null=1; + field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21)); item->maybe_null=1; field_list.push_back(item=new Item_empty_string("Create_options",255)); item->maybe_null=1; @@ -588,6 +594,10 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) } str= (table->table_charset ? table->table_charset->name : "default"); protocol->store(str, system_charset_info); + if (file->table_flags() & HA_HAS_CHECKSUM) + protocol->store((ulonglong)file->checksum()); + else + protocol->store_null(); // Checksum { char option_buff[350],*ptr; ptr=option_buff; @@ -668,8 +678,9 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, } file=table->file; file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); +#ifndef NO_EMBEDDED_ACCESS_CHECKS (void) get_table_grant(thd, table_list); - +#endif List<Item> field_list; field_list.push_back(new Item_empty_string("Field",NAME_LEN)); field_list.push_back(new Item_empty_string("Type",40)); @@ -749,6 +760,7 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, { /* Add grant options & comments */ end=tmp; +#ifndef NO_EMBEDDED_ACCESS_CHECKS col_access= get_column_grant(thd,table_list,field) & COL_ACLS; for (uint bitnr=0; col_access ; col_access>>=1,bitnr++) { @@ -758,6 +770,9 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, end=strmov(end,grant_types.type_names[bitnr]); } } +#else + end=strmov(end,""); +#endif protocol->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), system_charset_info); protocol->store(field->comment.str, field->comment.length, @@ -791,9 +806,14 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) DBUG_RETURN(1); } + if (store_create_info(thd, table, &buffer)) + DBUG_RETURN(-1); + List<Item> field_list; field_list.push_back(new Item_empty_string("Table",NAME_LEN)); - field_list.push_back(new Item_empty_string("Create Table", MAX_BLOB_WIDTH)); + // 1024 is for not to confuse old clients + field_list.push_back(new Item_empty_string("Create Table", + max(buffer.length(),1024))); if (protocol->send_fields(&field_list, 1)) DBUG_RETURN(1); @@ -1017,11 +1037,38 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) } } + +/* Append directory name (if exists) to CREATE INFO */ + +static void append_directory(THD *thd, String *packet, const char *dir_type, + const char *filename) +{ + uint length; + if (filename && !(thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)) + { + length= dirname_length(filename); + packet->append(' '); + packet->append(dir_type); + packet->append(" DIRECTORY='", 12); + packet->append(filename, length); + packet->append('\''); + } +} + + #define LIST_PROCESS_HOST_LEN 64 static int store_create_info(THD *thd, TABLE *table, String *packet) { + List<Item> field_list; + char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], *end; + String type(tmp, sizeof(tmp),&my_charset_bin); + Field **ptr,*field; + uint primary_key; + KEY *key_info; + handler *file= table->file; + HA_CREATE_INFO create_info; my_bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | @@ -1037,9 +1084,6 @@ store_create_info(THD *thd, TABLE *table, String *packet) restore_record(table,default_values); // Get empty record - List<Item> field_list; - char tmp[MAX_FIELD_WIDTH]; - String type(tmp, sizeof(tmp),&my_charset_bin); if (table->tmp_table) packet->append("CREATE TEMPORARY TABLE ", 23); else @@ -1047,13 +1091,14 @@ store_create_info(THD *thd, TABLE *table, String *packet) append_identifier(thd,packet, table->real_name, strlen(table->real_name)); packet->append(" (\n", 3); - Field **ptr,*field; for (ptr=table->field ; (field= *ptr); ptr++) { + bool has_default; + uint flags = field->flags; + if (ptr != table->field) packet->append(",\n", 2); - uint flags = field->flags; packet->append(" ", 2); append_identifier(thd,packet,field->field_name, strlen(field->field_name)); packet->append(' '); @@ -1090,9 +1135,9 @@ store_create_info(THD *thd, TABLE *table, String *packet) if (flags & NOT_NULL_FLAG) packet->append(" NOT NULL", 9); - bool has_default = (field->type() != FIELD_TYPE_BLOB && - field->type() != FIELD_TYPE_TIMESTAMP && - field->unireg_check != Field::NEXT_NUMBER); + has_default= (field->type() != FIELD_TYPE_BLOB && + field->type() != FIELD_TYPE_TIMESTAMP && + field->unireg_check != Field::NEXT_NUMBER); if (has_default) { @@ -1122,9 +1167,11 @@ store_create_info(THD *thd, TABLE *table, String *packet) } } - KEY *key_info=table->key_info; - table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME); - uint primary_key = table->primary_key; + key_info= table->key_info; + file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME); + bzero((char*) &create_info, sizeof(create_info)); + file->update_create_info(&create_info); + primary_key= table->primary_key; for (uint i=0 ; i < table->keys ; i++,key_info++) { @@ -1175,9 +1222,10 @@ store_create_info(THD *thd, TABLE *table, String *packet) table->field[key_part->fieldnr-1]->key_length() && !(key_info->flags & HA_FULLTEXT))) { - char buff[64]; buff[0] = '('; - char* end=int10_to_str((long) key_part->length, buff + 1,10); + char* end=int10_to_str((long) key_part->length / + key_part->field->charset()->mbmaxlen, + buff + 1,10); *end++ = ')'; packet->append(buff,(uint) (end-buff)); } @@ -1189,10 +1237,8 @@ store_create_info(THD *thd, TABLE *table, String *packet) Get possible foreign key definitions stored in InnoDB and append them to the CREATE TABLE statement */ - handler *file = table->file; - char* for_str= file->get_foreign_key_create_info(); - if (for_str) + if ((for_str= file->get_foreign_key_create_info())) { packet->append(for_str, strlen(for_str)); file->free_foreign_key_create_info(for_str); @@ -1203,8 +1249,6 @@ store_create_info(THD *thd, TABLE *table, String *packet) { packet->append(" TYPE=", 6); packet->append(file->table_type()); - char buff[128]; - char* p; if (table->table_charset && !(thd->variables.sql_mode & MODE_MYSQL323) && @@ -1222,21 +1266,22 @@ store_create_info(THD *thd, TABLE *table, String *packet) if (table->min_rows) { packet->append(" MIN_ROWS="); - p = longlong10_to_str(table->min_rows, buff, 10); - packet->append(buff, (uint) (p - buff)); + end= longlong10_to_str(table->min_rows, buff, 10); + packet->append(buff, (uint) (end- buff)); } if (table->max_rows) { packet->append(" MAX_ROWS="); - p = longlong10_to_str(table->max_rows, buff, 10); - packet->append(buff, (uint) (p - buff)); + end= longlong10_to_str(table->max_rows, buff, 10); + packet->append(buff, (uint) (end - buff)); } + if (table->avg_row_length) { packet->append(" AVG_ROW_LENGTH="); - p=longlong10_to_str(table->avg_row_length, buff,10); - packet->append(buff, (uint) (p - buff)); + end= longlong10_to_str(table->avg_row_length, buff,10); + packet->append(buff, (uint) (end - buff)); } if (table->db_create_options & HA_OPTION_PACK_KEYS) @@ -1260,12 +1305,15 @@ store_create_info(THD *thd, TABLE *table, String *packet) } if (file->raid_type) { - char buff[100]; - sprintf(buff," RAID_TYPE=%s RAID_CHUNKS=%d RAID_CHUNKSIZE=%ld", - my_raid_type(file->raid_type), file->raid_chunks, - file->raid_chunksize/RAID_BLOCK_SIZE); - packet->append(buff); + uint length; + length= my_snprintf(buff,sizeof(buff), + " RAID_TYPE=%s RAID_CHUNKS=%d RAID_CHUNKSIZE=%ld", + my_raid_type(file->raid_type), file->raid_chunks, + file->raid_chunksize/RAID_BLOCK_SIZE); + packet->append(buff, length); } + append_directory(thd, packet, "DATA", create_info.data_file_name); + append_directory(thd, packet, "INDEX", create_info.index_file_name); } DBUG_RETURN(0); } @@ -1458,11 +1506,15 @@ int mysqld_show_collations(THD *thd, const char *wild) for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ ) { CHARSET_INFO **cl; + if (!cs[0] || !(cs[0]->state & MY_CS_AVAILABLE) || + !(cs[0]->state & MY_CS_PRIMARY)) + continue; for ( cl= all_charsets; cl < all_charsets+255 ;cl ++) { - if (!cs[0] || !cl[0] || !my_charset_same(cs[0],cl[0]) || !(cs[0]->state & MY_CS_PRIMARY)) + if (!cl[0] || !(cl[0]->state & MY_CS_AVAILABLE) || + !my_charset_same(cs[0],cl[0])) continue; - if (cs[0] && !(wild && wild[0] && + if (!(wild && wild[0] && wild_case_compare(system_charset_info,cl[0]->name,wild))) { if (write_collation(protocol, cl[0])) @@ -1506,8 +1558,10 @@ int mysqld_show_charsets(THD *thd, const char *wild) for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ ) { - if (cs[0] && (cs[0]->state & MY_CS_PRIMARY) && !(wild && wild[0] && - wild_case_compare(system_charset_info,cs[0]->name,wild))) + if (cs[0] && (cs[0]->state & MY_CS_PRIMARY) && + (cs[0]->state & MY_CS_AVAILABLE) && + !(wild && wild[0] && + wild_case_compare(system_charset_info,cs[0]->csname,wild))) { if (write_charset(protocol, cs[0])) goto err; @@ -1522,7 +1576,8 @@ err: int mysqld_show(THD *thd, const char *wild, show_var_st *variables, - enum enum_var_type value_type) + enum enum_var_type value_type, + pthread_mutex_t *mutex) { char buff[1024]; List<Item> field_list; @@ -1536,8 +1591,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, DBUG_RETURN(1); /* purecov: inspected */ null_lex_str.str= 0; // For sys_var->value_ptr() - /* pthread_mutex_lock(&THR_LOCK_keycache); */ - pthread_mutex_lock(&LOCK_status); + pthread_mutex_lock(mutex); for (; variables->name; variables++) { if (!(wild && wild[0] && wild_case_compare(system_charset_info, @@ -1625,83 +1679,83 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, case SHOW_SSL_CTX_SESS_ACCEPT: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : SSL_CTX_sess_accept(ssl_acceptor_fd-> - ssl_context_)), + ssl_context)), buff, 10); break; case SHOW_SSL_CTX_SESS_ACCEPT_GOOD: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : SSL_CTX_sess_accept_good(ssl_acceptor_fd-> - ssl_context_)), + ssl_context)), buff, 10); break; case SHOW_SSL_CTX_SESS_CONNECT_GOOD: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : SSL_CTX_sess_connect_good(ssl_acceptor_fd-> - ssl_context_)), + ssl_context)), buff, 10); break; case SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : - SSL_CTX_sess_accept_renegotiate(ssl_acceptor_fd->ssl_context_)), + SSL_CTX_sess_accept_renegotiate(ssl_acceptor_fd->ssl_context)), buff, 10); break; case SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : - SSL_CTX_sess_connect_renegotiate(ssl_acceptor_fd-> ssl_context_)), + SSL_CTX_sess_connect_renegotiate(ssl_acceptor_fd-> ssl_context)), buff, 10); break; case SHOW_SSL_CTX_SESS_CB_HITS: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : SSL_CTX_sess_cb_hits(ssl_acceptor_fd-> - ssl_context_)), + ssl_context)), buff, 10); break; case SHOW_SSL_CTX_SESS_HITS: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : SSL_CTX_sess_hits(ssl_acceptor_fd-> - ssl_context_)), + ssl_context)), buff, 10); break; case SHOW_SSL_CTX_SESS_CACHE_FULL: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : SSL_CTX_sess_cache_full(ssl_acceptor_fd-> - ssl_context_)), + ssl_context)), buff, 10); break; case SHOW_SSL_CTX_SESS_MISSES: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : SSL_CTX_sess_misses(ssl_acceptor_fd-> - ssl_context_)), + ssl_context)), buff, 10); break; case SHOW_SSL_CTX_SESS_TIMEOUTS: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : - SSL_CTX_sess_timeouts(ssl_acceptor_fd->ssl_context_)), + SSL_CTX_sess_timeouts(ssl_acceptor_fd->ssl_context)), buff,10); break; case SHOW_SSL_CTX_SESS_NUMBER: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : - SSL_CTX_sess_number(ssl_acceptor_fd->ssl_context_)), + SSL_CTX_sess_number(ssl_acceptor_fd->ssl_context)), buff,10); break; case SHOW_SSL_CTX_SESS_CONNECT: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : - SSL_CTX_sess_connect(ssl_acceptor_fd->ssl_context_)), + SSL_CTX_sess_connect(ssl_acceptor_fd->ssl_context)), buff,10); break; case SHOW_SSL_CTX_SESS_GET_CACHE_SIZE: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : - SSL_CTX_sess_get_cache_size(ssl_acceptor_fd->ssl_context_)), + SSL_CTX_sess_get_cache_size(ssl_acceptor_fd->ssl_context)), buff,10); break; case SHOW_SSL_CTX_GET_VERIFY_MODE: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : - SSL_CTX_get_verify_mode(ssl_acceptor_fd->ssl_context_)), + SSL_CTX_get_verify_mode(ssl_acceptor_fd->ssl_context)), buff,10); break; case SHOW_SSL_CTX_GET_VERIFY_DEPTH: end= int10_to_str((long) (!ssl_acceptor_fd ? 0 : - SSL_CTX_get_verify_depth(ssl_acceptor_fd->ssl_context_)), + SSL_CTX_get_verify_depth(ssl_acceptor_fd->ssl_context)), buff,10); break; case SHOW_SSL_CTX_GET_SESSION_CACHE_MODE: @@ -1711,7 +1765,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, end= pos+4; break; } - switch (SSL_CTX_get_session_cache_mode(ssl_acceptor_fd->ssl_context_)) + switch (SSL_CTX_get_session_cache_mode(ssl_acceptor_fd->ssl_context)) { case SSL_SESS_CACHE_OFF: pos= "OFF"; @@ -1739,40 +1793,50 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, break; /* First group - functions relying on SSL */ case SHOW_SSL_GET_VERSION: - pos= thd->net.vio->ssl_ ? SSL_get_version(thd->net.vio->ssl_) : ""; + pos= (thd->net.vio->ssl_arg ? + SSL_get_version((SSL*) thd->net.vio->ssl_arg) : ""); end= strend(pos); break; case SHOW_SSL_SESSION_REUSED: - end= int10_to_str((long) (thd->net.vio->ssl_ ? - SSL_session_reused(thd->net.vio->ssl_): - 0), buff, 10); + end= int10_to_str((long) (thd->net.vio->ssl_arg ? + SSL_session_reused((SSL*) thd->net.vio-> + ssl_arg) : + 0), + buff, 10); break; case SHOW_SSL_GET_DEFAULT_TIMEOUT: - end= int10_to_str((long) (thd->net.vio->ssl_ ? - SSL_get_default_timeout(thd->net.vio->ssl_): - 0), buff, 10); + end= int10_to_str((long) (thd->net.vio->ssl_arg ? + SSL_get_default_timeout((SSL*) thd->net.vio-> + ssl_arg) : + 0), + buff, 10); break; case SHOW_SSL_GET_VERIFY_MODE: - end= int10_to_str((long) (thd->net.vio->ssl_ ? - SSL_get_verify_mode(thd->net.vio->ssl_): - 0), buff, 10); + end= int10_to_str((long) (thd->net.vio->ssl_arg ? + SSL_get_verify_mode((SSL*) thd->net.vio-> + ssl_arg): + 0), + buff, 10); break; case SHOW_SSL_GET_VERIFY_DEPTH: - end= int10_to_str((long) (thd->net.vio->ssl_ ? - SSL_get_verify_depth(thd->net.vio->ssl_): - 0), buff, 10); + end= int10_to_str((long) (thd->net.vio->ssl_arg ? + SSL_get_verify_depth((SSL*) thd->net.vio-> + ssl_arg): + 0), + buff, 10); break; case SHOW_SSL_GET_CIPHER: - pos= thd->net.vio->ssl_ ? SSL_get_cipher(thd->net.vio->ssl_) : ""; + pos= (thd->net.vio->ssl_arg ? + SSL_get_cipher((SSL*) thd->net.vio->ssl_arg) : "" ); end= strend(pos); break; case SHOW_SSL_GET_CIPHER_LIST: - if (thd->net.vio->ssl_) + if (thd->net.vio->ssl_arg) { char *to= buff; for (int i=0 ; i++ ;) { - const char *p= SSL_get_cipher_list(thd->net.vio->ssl_,i); + const char *p= SSL_get_cipher_list((SSL*) thd->net.vio->ssl_arg,i); if (p == NULL) break; to= strmov(to, p); @@ -1796,14 +1860,12 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, goto err; /* purecov: inspected */ } } - pthread_mutex_unlock(&LOCK_status); - /* pthread_mutex_unlock(&THR_LOCK_keycache); */ + pthread_mutex_unlock(mutex); send_eof(thd); DBUG_RETURN(0); err: - pthread_mutex_unlock(&LOCK_status); - /* pthread_mutex_unlock(&THR_LOCK_keycache); */ + pthread_mutex_unlock(mutex); DBUG_RETURN(1); } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index e7d7b08c93c..be9404ce0b8 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -28,6 +28,11 @@ #include <floatingpoint.h> #endif +/* + The following extern declarations are ok as these are interface functions + required by the string function +*/ + extern gptr sql_alloc(unsigned size); extern void sql_element_free(void *ptr); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 37f8d0d7f4f..b8f4b360dda 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -29,7 +29,6 @@ #include <io.h> #endif -extern HASH open_cache; static const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); @@ -350,10 +349,10 @@ static int sort_keys(KEY *a, KEY *b) fields List of fields to create keys List of keys to create tmp_table Set to 1 if this is an internal temporary table - (From ALTER TABLE) + (From ALTER TABLE) no_log Don't log the query to binary log. - DESCRIPTION + DESCRIPTION If one creates a temporary table, this is automaticly opened no_log is needed for the case of CREATE ... SELECT, @@ -422,6 +421,10 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, for (field_no=0; (sql_field=it++) ; field_no++) { + if (!sql_field->charset) + sql_field->charset= create_info->table_charset; + sql_field->create_length_to_internal_length(); + /* Don't pack keys in old tables if the user has requested this */ if ((sql_field->flags & BLOB_FLAG) || sql_field->sql_type == FIELD_TYPE_VAR_STRING && @@ -672,11 +675,11 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, /* Make SPATIAL to be RTREE by default SPATIAL only on BLOB or at least BINARY, this - actually should be replaced by special GEOM type + actually should be replaced by special GEOM type in near future when new frm file is ready checking for proper key parts number: */ - + if (key_info->flags == HA_SPATIAL) { if (key_info->key_parts != 1) @@ -699,7 +702,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, MYF(0), "RTREE INDEX"); DBUG_RETURN(-1); } - + List_iterator<key_part_spec> cols(key->columns); for (uint column_nr=0 ; (column=cols++) ; column_nr++) { @@ -725,6 +728,9 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, */ if (key->type == Key::FULLTEXT) column->length=test(f_is_blob(sql_field->pack_flag)); + else + column->length*= sql_field->charset->mbmaxlen; + if (f_is_blob(sql_field->pack_flag)) { if (!(file->table_flags() & HA_BLOB_KEY)) @@ -745,9 +751,9 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, { if (!column->length ) { - /* + /* BAR: 4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case - Lately we'll extend this code to support more dimensions + Lately we'll extend this code to support more dimensions */ column->length=4*sizeof(double); } @@ -798,7 +804,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, { } else if (column->length > length || - ((f_is_packed(sql_field->pack_flag) || + ((f_is_packed(sql_field->pack_flag) || ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) && (key_info->flags & HA_NOSAME))) && column->length != length)) @@ -899,22 +905,27 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); DBUG_RETURN(-1); } + if (wait_if_global_read_lock(thd, 0)) + DBUG_RETURN(error); VOID(pthread_mutex_lock(&LOCK_open)); if (!tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { if (!access(path,F_OK)) { - VOID(pthread_mutex_unlock(&LOCK_open)); if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) - DBUG_RETURN(0); - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); - DBUG_RETURN(-1); + error= 0; + else + my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); + goto end; } } thd->proc_info="creating table"; + if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) + create_info->data_file_name= create_info->index_file_name= 0; create_info->table_options=db_options; + if (rea_create_table(thd, path, create_info, fields, key_count, key_info_buffer)) { @@ -946,6 +957,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, error=0; end: VOID(pthread_mutex_unlock(&LOCK_open)); + start_waiting_global_read_lock(thd); thd->proc_info="After create"; DBUG_RETURN(error); } @@ -1913,19 +1925,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, List<Key> key_list; // Add new keys here create_field *def; - /* - For each column set charset to the table - default if the column charset hasn't been specified - explicitely. Change CREATE length into internal length - */ - def_it.rewind(); - while ((def= def_it++)) - { - if (!def->charset) - def->charset= create_info->table_charset; - def->create_length_to_internal_length(); - } - /* First collect all fields from table which isn't in drop_list */ @@ -2102,10 +2101,11 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, { // Check if sub key if (cfield->field->type() != FIELD_TYPE_BLOB && (cfield->field->pack_length() == key_part_length || - cfield->length != cfield->pack_length || - cfield->pack_length <= key_part_length)) + cfield->length <= key_part_length / + key_part->field->charset()->mbmaxlen)) key_part_length=0; // Use whole field } + key_part_length /= key_part->field->charset()->mbmaxlen; key_parts.push_back(new key_part_spec(cfield->field_name, key_part_length)); } @@ -2558,7 +2558,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, delete_count++; } else - found_count++; + found_count++; } end_read_record(&info); free_io_cache(from); @@ -2590,3 +2590,101 @@ copy_data_between_tables(TABLE *from,TABLE *to, *deleted=delete_count; DBUG_RETURN(error > 0 ? -1 : 0); } + + +int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) +{ + TABLE_LIST *table; + List<Item> field_list; + Item *item; + Protocol *protocol= thd->protocol; + DBUG_ENTER("mysql_admin_table"); + + field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2)); + item->maybe_null= 1; + field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21)); + item->maybe_null= 1; + if (protocol->send_fields(&field_list, 1)) + DBUG_RETURN(-1); + + for (table= tables; table; table= table->next) + { + char table_name[NAME_LEN*2+2]; + bool fatal_error= 0; + TABLE *t; + + strxmov(table_name, table->db ,".", table->real_name, NullS); + + t= table->table= open_ltable(thd, table, TL_READ_NO_INSERT); + thd->clear_error(); // these errors shouldn't get client + + protocol->prepare_for_resend(); + protocol->store(table_name, system_charset_info); + + if (!t) + { + /* Table didn't exist */ + protocol->store_null(); + thd->net.last_error[0]=0; + } + else + { + t->pos_in_table_list= table; + + if (t->file->table_flags() & HA_HAS_CHECKSUM && + !(check_opt->flags & T_EXTEND)) + protocol->store((ulonglong)t->file->checksum()); + else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) && + (check_opt->flags & T_QUICK)) + protocol->store_null(); + else + { + /* calculating table's checksum */ + ha_checksum crc= 0; + if (t->file->rnd_init(1)) + protocol->store_null(); + else + { + while (!t->file->rnd_next(t->record[0])) + { + ha_checksum row_crc= 0; + if (t->record[0] != t->field[0]->ptr) + row_crc= my_checksum(row_crc, t->record[0], + t->field[0]->ptr - t->record[0]); + + for (uint i= 0; i < t->fields; i++ ) + { + Field *f= t->field[i]; + if (f->type() == FIELD_TYPE_BLOB) + { + String tmp; + f->val_str(&tmp,&tmp); + row_crc= my_checksum(row_crc, tmp.ptr(), tmp.length()); + } + else + row_crc= my_checksum(row_crc, f->ptr, f->pack_length()); + } + + crc+= row_crc; + } + protocol->store((ulonglong)crc); + } + } + thd->clear_error(); + close_thread_tables(thd); + table->table=0; // For query cache + } + if (protocol->write()) + goto err; + } + + send_eof(thd); + DBUG_RETURN(0); + + err: + close_thread_tables(thd); // Shouldn't be needed + if (table) + table->table=0; + DBUG_RETURN(-1); +} + diff --git a/sql/sql_test.cc b/sql/sql_test.cc index d2f97640010..f991a09398b 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -41,7 +41,6 @@ static const char *lock_descriptions[] = "High priority write lock", "Highest priority write lock" }; -extern HASH open_cache; #ifndef DBUG_OFF @@ -65,7 +64,6 @@ print_where(COND *cond,const char *info) } /* This is for debugging purposes */ -extern TABLE *unused_tables; void print_cached_tables(void) { diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index d191550f396..c237b023e7b 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -92,10 +92,13 @@ static void init_syms(udf_func *tmp) tmp->func_deinit = dlsym(tmp->dlhandle, nm); if (tmp->type == UDFTYPE_AGGREGATE) { - (void)strmov( end, "_reset" ); - tmp->func_reset = dlsym( tmp->dlhandle, nm ); + (void)strmov( end, "_clear" ); + tmp->func_clear = dlsym( tmp->dlhandle, nm ); (void)strmov( end, "_add" ); tmp->func_add = dlsym( tmp->dlhandle, nm ); + /* Give error if _clear and _add doesn't exists */ + if (!tmp->func_clear || ! tmp->func_add) + tmp->func= 0; } } @@ -342,7 +345,7 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl, tmp->returns = ret; tmp->type = type; tmp->usage_count=1; - if (hash_insert(&udf_hash,(byte*) tmp)) + if (my_hash_insert(&udf_hash,(byte*) tmp)) return 0; using_udf_functions=1; return tmp; @@ -417,7 +420,7 @@ int mysql_create_function(THD *thd,udf_func *udf) u_d->func=udf->func; u_d->func_init=udf->func_init; u_d->func_deinit=udf->func_deinit; - u_d->func_reset=udf->func_reset; + u_d->func_clear=udf->func_clear; u_d->func_add=udf->func_add; /* create entry in mysql/func table */ @@ -429,7 +432,7 @@ int mysql_create_function(THD *thd,udf_func *udf) if (!(table = open_ltable(thd,&tables,TL_WRITE))) goto err; - restore_record(table,default_values); // Get default values for fields + restore_record(table,default_values); // Default values for fields table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info); table->field[1]->store((longlong) u_d->returns); table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info); diff --git a/sql/sql_udf.h b/sql/sql_udf.h index 29a351ac52f..7b10b80f148 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -33,7 +33,7 @@ typedef struct st_udf_func void *func; void *func_init; void *func_deinit; - void *func_reset; + void *func_clear; void *func_add; ulong usage_count; } udf_func; @@ -49,7 +49,7 @@ class udf_handler :public Sql_alloc UDF_ARGS f_args; UDF_INIT initid; char *num_buffer; - uchar error; + uchar error, is_null; bool initialized; Item **args; @@ -57,7 +57,7 @@ class udf_handler :public Sql_alloc table_map used_tables_cache; bool const_item_cache; udf_handler(udf_func *udf_arg) :u_d(udf_arg), buffers(0), error(0), - initialized(0) + is_null(0), initialized(0) {} ~udf_handler(); const char *name() const { return u_d ? u_d->name.str : "?"; } @@ -73,7 +73,6 @@ class udf_handler :public Sql_alloc *null_value=1; return 0.0; } - uchar is_null=0; double (*func)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)= (double (*)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)) u_d->func; double tmp=func(&initid, &f_args, &is_null, &error); @@ -92,7 +91,6 @@ class udf_handler :public Sql_alloc *null_value=1; return LL(0); } - uchar is_null=0; longlong (*func)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)= (longlong (*)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)) u_d->func; longlong tmp=func(&initid, &f_args, &is_null, &error); @@ -104,22 +102,15 @@ class udf_handler :public Sql_alloc *null_value=0; return tmp; } - void reset(my_bool *null_value) + void clear() { - uchar is_null=0; - if (get_arguments()) - { - *null_value=1; - return; - } - void (*func)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)= - (void (*)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)) u_d->func_reset; - func(&initid, &f_args, &is_null, &error); - *null_value= (my_bool) (is_null || error); + is_null= 0; + void (*func)(UDF_INIT *, uchar *, uchar *)= + (void (*)(UDF_INIT *, uchar *, uchar *)) u_d->func_clear; + func(&initid, &is_null, &error); } void add(my_bool *null_value) { - uchar is_null=0; if (get_arguments()) { *null_value=1; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index e5fe5b13b02..3a903d2e896 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -116,21 +116,20 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, bool tables_and_fields_initied) { SELECT_LEX *lex_select_save= thd->lex.current_select; - SELECT_LEX *select_cursor; + SELECT_LEX *select_cursor,*sl; DBUG_ENTER("st_select_lex_unit::prepare"); if (prepared) DBUG_RETURN(0); prepared= 1; res= 0; - found_rows_for_union= test(first_select_in_union()->options - & OPTION_FOUND_ROWS); + found_rows_for_union= first_select_in_union()->options & OPTION_FOUND_ROWS; TMP_TABLE_PARAM tmp_table_param; result= sel_result; t_and_f= tables_and_fields_initied; bzero((char *)&tmp_table_param,sizeof(TMP_TABLE_PARAM)); - thd->lex.current_select= select_cursor= first_select_in_union(); + thd->lex.current_select= sl= select_cursor= first_select_in_union(); /* Global option */ if (t_and_f) { @@ -188,7 +187,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, union_result->not_describe=1; union_result->tmp_table_param=tmp_table_param; - for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) + for (;sl; sl= sl->next_select()) { JOIN *join= new JOIN(thd, sl->item_list, sl->options | thd->options | SELECT_NO_UNLOCK, @@ -198,7 +197,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, select_limit_cnt= sl->select_limit+sl->offset_limit; if (select_limit_cnt < sl->select_limit) select_limit_cnt= HA_POS_ERROR; // no limit - if (select_limit_cnt == HA_POS_ERROR && !sl->braces) + if (select_limit_cnt == HA_POS_ERROR || sl->braces) sl->options&= ~OPTION_FOUND_ROWS; res= join->prepare(&sl->ref_pointer_array, @@ -242,7 +241,7 @@ int st_select_lex_unit::exec() { SELECT_LEX *lex_select_save= thd->lex.current_select; SELECT_LEX *select_cursor=first_select_in_union(); - ha_rows add_rows=0; + ulonglong add_rows=0; DBUG_ENTER("st_select_lex_unit::exec"); if (executed && !(dependent || uncacheable)) @@ -259,29 +258,52 @@ int st_select_lex_unit::exec() } for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { - ha_rows rows= 0; + ha_rows records_at_start= 0; thd->lex.current_select= sl; if (optimized) res= sl->join->reinit(); else { - offset_limit_cnt= sl->offset_limit; - select_limit_cnt= sl->select_limit+sl->offset_limit; + if (sl != global_parameters) + { + offset_limit_cnt= sl->offset_limit; + select_limit_cnt= sl->select_limit+sl->offset_limit; + } + else + { + offset_limit_cnt= 0; + /* + We can't use LIMIT at this stage if we are using ORDER BY for the + whole query + */ + if (sl->order_list.first) + select_limit_cnt= HA_POS_ERROR; + else + select_limit_cnt= sl->select_limit+sl->offset_limit; + } if (select_limit_cnt < sl->select_limit) select_limit_cnt= HA_POS_ERROR; // no limit - if (select_limit_cnt == HA_POS_ERROR) + + /* + When using braces, SQL_CALC_FOUND_ROWS affects the whole query. + We don't calculate found_rows() per union part + */ + if (select_limit_cnt == HA_POS_ERROR || sl->braces) sl->options&= ~OPTION_FOUND_ROWS; - else if (found_rows_for_union) + else { - rows= sl->select_limit; - sl->options|= OPTION_FOUND_ROWS; + /* + We are doing an union without braces. In this case + SQL_CALC_FOUND_ROWS should be done on all sub parts + */ + sl->options|= found_rows_for_union; } - - /* - As far as union share table space we should reassign table map, - which can be spoiled by 'prepare' of JOIN of other UNION parts - if it use same tables + sl->join->select_options=sl->options; + /* + As far as union share table space we should reassign table map, + which can be spoiled by 'prepare' of JOIN of other UNION parts + if it use same tables */ uint tablenr=0; for (TABLE_LIST *table_list= (TABLE_LIST*) sl->table_list.first; @@ -303,8 +325,10 @@ int st_select_lex_unit::exec() } if (!res) { + records_at_start= table->file->records; sl->join->exec(); res= sl->join->error; + offset_limit_cnt= sl->offset_limit; if (!res && union_result->flush()) { thd->lex.current_select= lex_select_save; @@ -316,10 +340,19 @@ int st_select_lex_unit::exec() thd->lex.current_select= lex_select_save; DBUG_RETURN(res); } - if (found_rows_for_union && !sl->braces && - (sl->options & OPTION_FOUND_ROWS)) - add_rows+= (sl->join->send_records > rows) ? - sl->join->send_records - rows : 0; + /* Needed for the following test and for records_at_start in next loop */ + table->file->info(HA_STATUS_VARIABLE); + if (found_rows_for_union & sl->options) + { + /* + This is a union without braces. Remember the number of rows that + could also have been part of the result set. + We get this from the difference of between total number of possible + rows and actual rows added to the temporary table. + */ + add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong) + ((table->file->records - records_at_start))); + } } } optimized= 1; @@ -332,18 +365,15 @@ int st_select_lex_unit::exec() List<Item_func_match> empty_list; empty_list.empty(); - if (!thd->is_fatal_error) // Check if EOM + if (!thd->is_fatal_error) // Check if EOM { ulong options= thd->options; thd->lex.current_select= fake_select_lex; - if (select_cursor->braces) - { - offset_limit_cnt= global_parameters->offset_limit; - select_limit_cnt= global_parameters->select_limit + - global_parameters->offset_limit; - if (select_limit_cnt < global_parameters->select_limit) - select_limit_cnt= HA_POS_ERROR; // no limit - } + offset_limit_cnt= global_parameters->offset_limit; + select_limit_cnt= global_parameters->select_limit + + global_parameters->offset_limit; + if (select_limit_cnt < global_parameters->select_limit) + select_limit_cnt= HA_POS_ERROR; // no limit if (select_limit_cnt == HA_POS_ERROR) options&= ~OPTION_FOUND_ROWS; else if (found_rows_for_union && !describe) @@ -384,12 +414,8 @@ int st_select_lex_unit::exec() (ORDER*) NULL, NULL, (ORDER*) NULL, options | SELECT_NO_UNLOCK, result, this, fake_select_lex, 0); - if (found_rows_for_union && !res) - { - thd->limit_found_rows= table->file->records; - if (!select_cursor->braces) - thd->limit_found_rows+= add_rows; - } + if (!res) + thd->limit_found_rows = (ulonglong)table->file->records + add_rows; /* Mark for slow query log if any of the union parts didn't use indexes efficiently diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 29138f10989..946107049dc 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -86,8 +86,10 @@ int mysql_update(THD *thd, /* Calculate "table->used_keys" based on the WHERE */ table->used_keys=table->keys_in_use; table->quick_keys=0; +#ifndef NO_EMBEDDED_ACCESS_CHECKS want_privilege=table->grant.want_privilege; table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); +#endif bzero((char*) &tables,sizeof(tables)); // For ORDER BY tables.table= table; @@ -122,7 +124,9 @@ int mysql_update(THD *thd, } /* Check the fields we are going to modify */ +#ifndef NO_EMBEDDED_ACCESS_CHECKS table->grant.want_privilege=want_privilege; +#endif if (setup_fields(thd, 0, update_table_list, fields, 1, 0, 0)) DBUG_RETURN(-1); /* purecov: inspected */ if (table->timestamp_field) @@ -134,8 +138,10 @@ int mysql_update(THD *thd, table->timestamp_field->query_id=timestamp_query_id; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS /* Check values */ table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); +#endif if (setup_fields(thd, 0, update_table_list, values, 0, 0, 0)) { free_underlaid_joins(thd, &thd->lex.select_lex); @@ -418,7 +424,9 @@ int mysql_multi_update(THD *thd, TABLE_LIST *tl; DBUG_ENTER("mysql_multi_update"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS table_list->grant.want_privilege=(SELECT_ACL & ~table_list->grant.privilege); +#endif if ((res=open_and_lock_tables(thd,table_list))) DBUG_RETURN(res); fix_tables_pointers(thd->lex.all_selects_list); @@ -600,6 +608,7 @@ multi_update::initialize_tables(JOIN *join) { TABLE *table=table_ref->table; uint cnt= table_ref->shared; + Item_field *If; List<Item> temp_fields= *fields_for_table[cnt]; ORDER group; @@ -623,7 +632,10 @@ multi_update::initialize_tables(JOIN *join) /* ok to be on stack as this is not referenced outside of this func */ Field_string offset(table->file->ref_length, 0, "offset", table, &my_charset_bin); - if (temp_fields.push_front(new Item_field(((Field *) &offset)))) + if (!(If=new Item_field(((Field *) &offset)))) + DBUG_RETURN(1); + If->maybe_null=0; + if (temp_fields.push_front(If)) DBUG_RETURN(1); /* Make an unique key over the first field to avoid duplicated updates */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 543fe1ff2da..5e45a2c4fbb 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -38,7 +38,6 @@ #include <myisam.h> #include <myisammrg.h> -extern void yyerror(const char*); int yylex(void *yylval, void *yythd); #define yyoverflow(A,B,C,D,E,F) if (my_yyoverflow((B),(D),(int*) (F))) { yyerror((char*) (A)); return 2; } @@ -168,6 +167,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SUPER_SYM %token TRUNCATE_SYM %token UNLOCK_SYM +%token UNTIL_SYM %token UPDATE_SYM %token ACTION @@ -277,6 +277,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MASTER_PORT_SYM %token MASTER_CONNECT_RETRY_SYM %token MASTER_SERVER_ID_SYM +%token MASTER_SSL_SYM +%token MASTER_SSL_CA_SYM +%token MASTER_SSL_CAPATH_SYM +%token MASTER_SSL_CERT_SYM +%token MASTER_SSL_CIPHER_SYM +%token MASTER_SSL_KEY_SYM %token RELAY_LOG_FILE_SYM %token RELAY_LOG_POS_SYM %token MATCH @@ -295,6 +301,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token NEW_SYM %token NCHAR_SYM %token NCHAR_STRING +%token NVARCHAR_SYM %token NOT %token NO_SYM %token NULL_SYM @@ -500,6 +507,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MULTIPOINT %token MULTIPOLYGON %token NOW_SYM +%token OLD_PASSWORD %token PASSWORD %token POINTFROMTEXT %token POINT_SYM @@ -670,8 +678,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); insert_values update delete truncate rename show describe load alter optimize preload flush reset purge begin commit rollback savepoint - slave master_def master_defs - repair restore backup analyze check start + slave master_def master_defs master_file_def + repair restore backup analyze check start checksum field_list field_list_item field_spec kill column_def key_def preload_list preload_keys select_item_list select_item values_list no_braces @@ -729,23 +737,26 @@ verb_clause: | begin | change | check + | checksum | commit | create | delete | describe | do | drop + | flush | grant + | handler + | help | insert - | flush + | kill | load | lock - | kill | optimize | preload | purge | rename - | repair + | repair | replace | reset | restore @@ -754,15 +765,14 @@ verb_clause: | savepoint | select | set + | show | slave | start - | show | truncate - | handler | unlock | update | use - | help; + ; /* help */ @@ -807,52 +817,77 @@ master_def: Lex->mi.password = $3.str; } | - MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys - { - Lex->mi.log_file_name = $3.str; - } - | MASTER_PORT_SYM EQ ULONG_NUM { Lex->mi.port = $3; } | - MASTER_LOG_POS_SYM EQ ulonglong_num - { - Lex->mi.pos = $3; - /* - If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it - instead of causing subsequent errors. - We need to do it in this file, because only there we know that - MASTER_LOG_POS has been explicitely specified. On the contrary - in change_master() (sql_repl.cc) we cannot distinguish between 0 - (MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified), - whereas we want to distinguish (specified 0 means "read the binlog - from 0" (4 in fact), unspecified means "don't change the position - (keep the preceding value)"). - */ - Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos); - } - | MASTER_CONNECT_RETRY_SYM EQ ULONG_NUM { Lex->mi.connect_retry = $3; } + | MASTER_SSL_SYM EQ ULONG_NUM + { + Lex->mi.ssl= $3 ? + LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE; + } + | MASTER_SSL_CA_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_ca= $3.str; + } + | MASTER_SSL_CAPATH_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_capath= $3.str; + } + | MASTER_SSL_CERT_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_cert= $3.str; + } + | MASTER_SSL_CIPHER_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_cipher= $3.str; + } + | MASTER_SSL_KEY_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_key= $3.str; + } | - RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys - { - Lex->mi.relay_log_name = $3.str; - } - | - RELAY_LOG_POS_SYM EQ ULONG_NUM + master_file_def + ; + +master_file_def: + MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys { - Lex->mi.relay_log_pos = $3; - /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */ - Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos); + Lex->mi.log_file_name = $3.str; } + | MASTER_LOG_POS_SYM EQ ulonglong_num + { + Lex->mi.pos = $3; + /* + If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it + instead of causing subsequent errors. + We need to do it in this file, because only there we know that + MASTER_LOG_POS has been explicitely specified. On the contrary + in change_master() (sql_repl.cc) we cannot distinguish between 0 + (MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified), + whereas we want to distinguish (specified 0 means "read the binlog + from 0" (4 in fact), unspecified means "don't change the position + (keep the preceding value)"). + */ + Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos); + } + | RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys + { + Lex->mi.relay_log_name = $3.str; + } + | RELAY_LOG_POS_SYM EQ ULONG_NUM + { + Lex->mi.relay_log_pos = $3; + /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */ + Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos); + } ; - /* create a table */ create: @@ -878,7 +913,7 @@ create: bzero((char*) &lex->create_info,sizeof(lex->create_info)); lex->create_info.options=$2 | $4; lex->create_info.db_type= (enum db_type) lex->thd->variables.table_type; - lex->create_info.table_charset= thd->variables.character_set_database; + lex->create_info.table_charset= thd->variables.collation_database; lex->name=0; } create2 @@ -1099,7 +1134,7 @@ opt_select_from: | select_from select_lock_type; udf_func_type: - /* empty */ { $$ = UDFTYPE_FUNCTION; } + /* empty */ { $$ = UDFTYPE_FUNCTION; } | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; }; udf_type: @@ -1163,7 +1198,8 @@ field_spec: { LEX *lex=Lex; lex->length=lex->dec=0; lex->type=0; lex->interval=0; - lex->default_value=lex->comment=0; + lex->default_value=0; + lex->comment=0; lex->charset=NULL; } type opt_attribute @@ -1304,6 +1340,7 @@ varchar: nvarchar: NATIONAL_SYM VARCHAR {} + | NVARCHAR_SYM {} | NCHAR_SYM VARCHAR {} | NATIONAL_SYM CHAR_SYM VARYING {} | NCHAR_SYM VARYING {} @@ -1374,7 +1411,7 @@ attribute: | opt_primary KEY_SYM { Lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG; } | UNIQUE_SYM { Lex->type|= UNIQUE_FLAG; } | UNIQUE_SYM KEY_SYM { Lex->type|= UNIQUE_KEY_FLAG; } - | COMMENT_SYM text_literal { Lex->comment= $2; } + | COMMENT_SYM TEXT_STRING_sys { Lex->comment= &$2; } | COLLATE_SYM collation_name { if (Lex->charset && !my_charset_same(Lex->charset,$2)) @@ -1566,7 +1603,7 @@ opt_ident: opt_component: /* empty */ { $$.str= 0; $$.length= 0; } | '.' ident { $$=$2; }; - + string_list: text_string { Lex->interval_list.push_back($1); } | string_list ',' text_string { Lex->interval_list.push_back($3); }; @@ -1595,7 +1632,7 @@ alter: lex->select_lex.db=lex->name=0; bzero((char*) &lex->create_info,sizeof(lex->create_info)); lex->create_info.db_type= DB_TYPE_DEFAULT; - lex->create_info.table_charset= thd->variables.character_set_database; + lex->create_info.table_charset= thd->variables.collation_database; lex->create_info.row_type= ROW_TYPE_NOT_USED; lex->alter_keys_onoff=LEAVE_AS_IS; lex->simple_alter=1; @@ -1631,7 +1668,8 @@ alter_list_item: { LEX *lex=Lex; lex->length=lex->dec=0; lex->type=0; lex->interval=0; - lex->default_value=lex->comment=0; + lex->default_value=0; + lex->comment=0; lex->charset= NULL; lex->simple_alter=0; } @@ -1714,23 +1752,41 @@ opt_to: | AS {}; /* - The first two deprecate the last two--delete the last two for 4.1 release + SLAVE START and SLAVE STOP are deprecated. We keep them for compatibility. + To use UNTIL, one must use START SLAVE, not SLAVE START. */ slave: - START_SYM SLAVE slave_thread_opts - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_SLAVE_START; - lex->type = 0; - } + START_SYM SLAVE slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; + /* We'll use mi structure for UNTIL options */ + bzero((char*) &lex->mi, sizeof(lex->mi)); + } + slave_until + {} | STOP_SYM SLAVE slave_thread_opts { LEX *lex=Lex; lex->sql_command = SQLCOM_SLAVE_STOP; lex->type = 0; } - ; + | SLAVE START_SYM slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; + } + | SLAVE STOP_SYM slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_STOP; + lex->type = 0; + } + ; + start: START_SYM TRANSACTION_SYM { Lex->sql_command = SQLCOM_BEGIN;} @@ -1740,6 +1796,7 @@ start: slave_thread_opts: { Lex->slave_thd_opt= 0; } slave_thread_opt_list + {} ; slave_thread_opt_list: @@ -1753,6 +1810,28 @@ slave_thread_opt: | RELAY_THREAD { Lex->slave_thd_opt|=SLAVE_IO; } ; +slave_until: + /*empty*/ {} + | UNTIL_SYM slave_until_opts + { + LEX *lex=Lex; + if ((lex->mi.log_file_name || lex->mi.pos) && + (lex->mi.relay_log_name || lex->mi.relay_log_pos) || + !((lex->mi.log_file_name && lex->mi.pos) || + (lex->mi.relay_log_name && lex->mi.relay_log_pos))) + { + send_error(lex->thd, ER_BAD_SLAVE_UNTIL_COND); + YYABORT; + } + + } + ; + +slave_until_opts: + master_file_def + | slave_until_opts ',' master_file_def ; + + restore: RESTORE_SYM table_or_tables { @@ -1773,6 +1852,22 @@ backup: Lex->backup_dir = $6.str; }; +checksum: + CHECKSUM_SYM table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_CHECKSUM; + } + table_list opt_checksum_type + {} + ; + +opt_checksum_type: + /* nothing */ { Lex->check_opt.flags= 0; } + | QUICK { Lex->check_opt.flags= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; } + ; + repair: REPAIR opt_no_write_to_binlog table_or_tables { @@ -1962,8 +2057,9 @@ select_init: YYABORT; } /* select in braces, can't contain global parameters */ - sel->master_unit()->global_parameters= - sel->master_unit()->fake_select_lex; + if (sel->master_unit()->fake_select_lex) + sel->master_unit()->global_parameters= + sel->master_unit()->fake_select_lex; } union_opt; select_init2: @@ -2576,9 +2672,13 @@ simple_expr: | NOW_SYM '(' expr ')' { $$= new Item_func_now_local($3); Lex->safe_to_cache_query=0;} | PASSWORD '(' expr ')' - { $$= new Item_func_password($3); } - | PASSWORD '(' expr ',' expr ')' - { $$= new Item_func_password($3,$5); } + { + $$= YYTHD->variables.old_passwords ? + (Item *) new Item_func_old_password($3) : + (Item *) new Item_func_password($3); + } + | OLD_PASSWORD '(' expr ')' + { $$= new Item_func_old_password($3); } | POINT_SYM '(' expr ',' expr ')' { $$= new Item_func_point($3,$5); } | POINTFROMTEXT '(' expr ')' @@ -2960,6 +3060,13 @@ join_table: | '(' SELECT_SYM select_derived ')' opt_table_alias { LEX *lex=Lex; + if (lex->sql_command == SQLCOM_UPDATE && + &lex->select_lex == lex->current_select->outer_select()) + { + send_error(lex->thd, ER_SYNTAX_ERROR); + YYABORT; + } + SELECT_LEX_UNIT *unit= lex->current_select->master_unit(); lex->current_select= unit->outer_select(); if (!($$= lex->current_select-> @@ -3382,7 +3489,7 @@ do: DO_SYM */ drop: - DROP opt_temporary TABLE_SYM if_exists table_list opt_restrict + DROP opt_temporary table_or_tables if_exists table_list opt_restrict { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_TABLE; @@ -4380,6 +4487,7 @@ keyword: | BOOL_SYM {} | BOOLEAN_SYM {} | BYTE_SYM {} + | BTREE_SYM {} | CACHE_SYM {} | CHANGED {} | CHARSET {} @@ -4408,6 +4516,7 @@ keyword: | DYNAMIC_SYM {} | END {} | ENUM {} + | ERRORS {} | ESCAPE_SYM {} | EVENTS_SYM {} | EXECUTE_SYM {} @@ -4425,6 +4534,7 @@ keyword: | GRANTS {} | GLOBAL_SYM {} | HANDLER_SYM {} + | HASH_SYM {} | HEAP_SYM {} | HELP_SYM {} | HOSTS_SYM {} @@ -4453,6 +4563,12 @@ keyword: | MASTER_USER_SYM {} | MASTER_PASSWORD_SYM {} | MASTER_CONNECT_RETRY_SYM {} + | MASTER_SSL_SYM {} + | MASTER_SSL_CA_SYM {} + | MASTER_SSL_CAPATH_SYM {} + | MASTER_SSL_CERT_SYM {} + | MASTER_SSL_CIPHER_SYM {} + | MASTER_SSL_KEY_SYM {} | MAX_CONNECTIONS_PER_HOUR {} | MAX_QUERIES_PER_HOUR {} | MAX_UPDATES_PER_HOUR {} @@ -4476,7 +4592,9 @@ keyword: | NEW_SYM {} | NO_SYM {} | NONE_SYM {} + | NVARCHAR_SYM {} | OFFSET_SYM {} + | OLD_PASSWORD {} | OPEN_SYM {} | PACK_KEYS_SYM {} | PARTIAL {} @@ -4507,6 +4625,7 @@ keyword: | ROWS_SYM {} | ROW_FORMAT_SYM {} | ROW_SYM {} + | RTREE_SYM {} | SAVEPOINT_SYM {} | SECOND_SYM {} | SERIAL_SYM {} @@ -4539,10 +4658,12 @@ keyword: | UDF_SYM {} | UNCOMMITTED_SYM {} | UNICODE_SYM {} + | UNTIL_SYM {} | USER {} | USE_FRM {} | VARIABLES {} | VALUE_SYM {} + | WARNINGS {} | WORK_SYM {} | X509_SYM {} | YEAR_SYM {} @@ -4624,7 +4745,7 @@ option_value: THD *thd= YYTHD; LEX *lex= Lex; $2= $2 ? $2: global_system_variables.character_set_client; - lex->var_list.push_back(new set_var_collation_client($2,thd->variables.character_set_database,$2)); + lex->var_list.push_back(new set_var_collation_client($2,thd->variables.collation_database,$2)); } | NAMES_SYM charset_name_or_default opt_collate { @@ -4699,15 +4820,15 @@ text_or_password: TEXT_STRING { $$=$1.str;} | PASSWORD '(' TEXT_STRING ')' { - if (!$3.length) - $$=$3.str; - else - { - char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1); - make_scrambled_password(buff,$3.str,use_old_passwords, - &YYTHD->rand); - $$=buff; - } + $$= $3.length ? YYTHD->variables.old_passwords ? + Item_func_old_password::alloc(YYTHD, $3.str) : + Item_func_password::alloc(YYTHD, $3.str) : + $3.str; + } + | OLD_PASSWORD '(' TEXT_STRING ')' + { + $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) : + $3.str; } ; @@ -4883,7 +5004,7 @@ grant_privilege_list: | grant_privilege_list ',' grant_privilege; grant_privilege: - SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {} + SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {} | INSERT { Lex->which_columns = INSERT_ACL;} opt_column_list {} | UPDATE_SYM { Lex->which_columns = UPDATE_ACL; } opt_column_list {} | REFERENCES { Lex->which_columns = REFERENCES_ACL;} opt_column_list {} @@ -5015,14 +5136,24 @@ grant_user: $$=$1; $1->password=$4; if ($4.length) { - char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1); - if (buff) - { - make_scrambled_password(buff,$4.str,use_old_passwords, - &YYTHD->rand); - $1->password.str=buff; - $1->password.length=HASH_PASSWORD_LENGTH; - } + if (YYTHD->variables.old_passwords) + { + char *buff= + (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1); + if (buff) + make_scrambled_password_323(buff, $4.str); + $1->password.str= buff; + $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + } + else + { + char *buff= + (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1); + if (buff) + make_scrambled_password(buff, $4.str); + $1->password.str= buff; + $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH; + } } } | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING @@ -5181,7 +5312,7 @@ union_opt: ; optional_order_or_limit: - /* Empty */ {} + /* Empty */ {} | { THD *thd= YYTHD; @@ -5190,9 +5321,12 @@ optional_order_or_limit: SELECT_LEX *sel= lex->current_select; SELECT_LEX_UNIT *unit= sel->master_unit(); SELECT_LEX *fake= unit->fake_select_lex; - unit->global_parameters= fake; - fake->no_table_names_allowed= 1; - lex->current_select= fake; + if (fake) + { + unit->global_parameters= fake; + fake->no_table_names_allowed= 1; + lex->current_select= fake; + } thd->where= "global ORDER clause"; } order_or_limit @@ -5276,3 +5410,4 @@ subselect_end: LEX *lex=Lex; lex->current_select = lex->current_select->return_after_parsing(); }; + diff --git a/sql/table.cc b/sql/table.cc index 9d12de1f6c7..c31b68fc2dc 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -236,7 +236,6 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, #ifdef HAVE_CRYPTED_FRM else if (*(head+26) == 2) { - extern SQL_CRYPT *get_crypt_for_frm(void); my_pthread_setspecific_ptr(THR_MALLOC,old_root); crypted=get_crypt_for_frm(); my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root); @@ -459,7 +458,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (outparam->timestamp_field == reg_field) outparam->timestamp_field_offset=i; if (use_hash) - (void) hash_insert(&outparam->name_hash,(byte*) *field_ptr); // Will never fail + (void) my_hash_insert(&outparam->name_hash,(byte*) *field_ptr); // Will never fail } *field_ptr=0; // End marker @@ -1207,17 +1206,14 @@ bool get_field(MEM_ROOT *mem, Field *field, String *res) char *get_field(MEM_ROOT *mem, Field *field) { - char buff[MAX_FIELD_WIDTH], *to; + char buff[MAX_FIELD_WIDTH]; String str(buff,sizeof(buff),&my_charset_bin); uint length; field->val_str(&str,&str); if (!(length= str.length())) return NullS; - to= (char*) alloc_root(mem,length+1); - memcpy(to, str.ptr(), (uint) length); - to[length]=0; - return to; + return strmake_root(mem, str.ptr(), length); } diff --git a/sql/udf_example.cc b/sql/udf_example.cc index 7f4417bf8fe..ba056a9d2fd 100644 --- a/sql/udf_example.cc +++ b/sql/udf_example.cc @@ -149,6 +149,7 @@ longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ); void avgcost_deinit( UDF_INIT* initid ); void avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); +void avgcost_clear( UDF_INIT* initid, char* is_null, char *error ); void avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); double avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); } @@ -902,21 +903,29 @@ avgcost_deinit( UDF_INIT* initid ) delete initid->ptr; } + +/* This is only for MySQL 4.0 compability */ void -avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message ) +avgcost_reset(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) { - struct avgcost_data* data = (struct avgcost_data*)initid->ptr; - data->totalprice = 0.0; - data->totalquantity = 0; - data->count = 0; + avgcost_clear(initid, is_null, message); + avgcost_add(initid, args, is_null, message); +} - *is_null = 0; - avgcost_add( initid, args, is_null, message ); +/* This is needed to get things to work in MySQL 4.1.1 and above */ + +void +avgcost_clear(UDF_INIT* initid, char* is_null, char* message) +{ + struct avgcost_data* data = (struct avgcost_data*)initid->ptr; + data->totalprice= 0.0; + data->totalquantity= 0; + data->count= 0; } void -avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message ) +avgcost_add(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) { if (args->args[0] && args->args[1]) { |