diff options
Diffstat (limited to 'sql')
80 files changed, 2641 insertions, 1024 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index d701c18a4d7..1597ad2c4a8 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -153,10 +153,11 @@ sql_yacc.o: sql_yacc.cc sql_yacc.h $(HEADERS) lex_hash.h: gen_lex_hash$(EXEEXT) ./gen_lex_hash$(EXEEXT) > $@ -# For testing of udf_example.so; Works on platforms with gcc -# (This is not part of our build process but only provided as an example) -udf_example.so: udf_example.cc - $(CXXCOMPILE) -shared -o $@ $< +# For testing of udf_example.so +noinst_LTLIBRARIES= udf_example.la +udf_example_la_SOURCES= udf_example.cc +udf_example_la_LDFLAGS= -module -rpath $(pkglibdir) + # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/sql/field.cc b/sql/field.cc index 3a0e788b0c1..eab62cd1958 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5262,7 +5262,7 @@ int Field_date::store(longlong nr, bool unsigned_val) } if (nr >= 19000000000000.0 && nr <= 99991231235959.0) - nr=floor(nr/1000000.0); // Timestamp to date + nr= (longlong) floor(nr/1000000.0); // Timestamp to date if (error) set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, @@ -8213,13 +8213,11 @@ void Field_bit_as_char::sql_type(String &res) const create_field::create_length_to_internal_length() DESCRIPTION - Convert create_field::length from number of characters to number of bytes, - save original value in chars_length. + Convert create_field::length from number of characters to number of bytes. */ void create_field::create_length_to_internal_length(void) { - chars_length= length; switch (sql_type) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: @@ -8271,7 +8269,7 @@ void create_field::init_for_tmp_table(enum_field_types sql_type_arg, { field_name= ""; sql_type= sql_type_arg; - length= length_arg;; + char_length= length= length_arg;; unireg_check= Field::NONE; interval= 0; charset= &my_charset_bin; @@ -8599,6 +8597,8 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, case FIELD_TYPE_DECIMAL: DBUG_ASSERT(0); /* Was obsolete */ } + /* Remember the value of length */ + char_length= length; if (!(flags & BLOB_FLAG) && ((length > max_field_charlength && fld_type != FIELD_TYPE_SET && @@ -8937,6 +8937,7 @@ create_field::create_field(Field *old_field,Field *orig_field) else interval=0; def=0; + char_length= length; if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && old_field->ptr && orig_field && diff --git a/sql/field.h b/sql/field.h index c32f365ec92..f53227e5fd6 100644 --- a/sql/field.h +++ b/sql/field.h @@ -166,8 +166,9 @@ public: virtual int cmp(const char *,const char *)=0; virtual int cmp_binary(const char *a,const char *b, uint32 max_length=~0L) { return memcmp(a,b,pack_length()); } - int cmp_offset(uint row_offset) { return cmp(ptr,ptr+row_offset); } - int cmp_binary_offset(uint row_offset) + virtual int cmp_offset(uint row_offset) + { return cmp(ptr,ptr+row_offset); } + virtual int cmp_binary_offset(uint row_offset) { return cmp_binary(ptr, ptr+row_offset); }; virtual int key_cmp(const byte *a,const byte *b) { return cmp((char*) a,(char*) b); } @@ -1333,6 +1334,8 @@ public: { return cmp_binary((char *) a, (char *) b); } int key_cmp(const byte *str, uint length); int cmp_offset(uint row_offset); + int cmp_binary_offset(uint row_offset) + { return cmp_offset(row_offset); } void get_key_image(char *buff, uint length, imagetype type); void set_key_image(char *buff, uint length) { Field_bit::store(buff, length, &my_charset_bin); } @@ -1392,9 +1395,10 @@ public: */ ulong length; /* - The value of 'length' before a call to create_length_to_internal_length + The value of `length' as set by parser: is the number of characters + for most of the types, or of bytes for BLOBs or numeric types. */ - uint32 chars_length; + uint32 char_length; uint decimals, flags, pack_length, key_length; Field::utype unireg_check; TYPELIB *interval; // Which interval to use diff --git a/sql/field_conv.cc b/sql/field_conv.cc index bbe2dbe5e9f..895f022624c 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -379,6 +379,16 @@ static void do_cut_string_complex(Copy_field *copy) +static void do_expand_binary(Copy_field *copy) +{ + CHARSET_INFO *cs= copy->from_field->charset(); + memcpy(copy->to_ptr,copy->from_ptr,copy->from_length); + cs->cset->fill(cs, copy->to_ptr+copy->from_length, + copy->to_length-copy->from_length, '\0'); +} + + + static void do_expand_string(Copy_field *copy) { CHARSET_INFO *cs= copy->from_field->charset(); @@ -583,7 +593,13 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) return (from->charset()->mbmaxlen == 1 ? do_cut_string : do_cut_string_complex); else if (to_length > from_length) - return do_expand_string; + { + if ((to->flags & BINARY_FLAG) != 0) + return do_expand_binary; + else + return do_expand_string; + } + } else if (to->real_type() != from->real_type() || to_length != from_length || diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index af7c987e477..c6d5c77803b 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -740,7 +740,7 @@ error: ha_federated::ha_federated(TABLE *table_arg) :handler(&federated_hton, table_arg), - mysql(0), stored_result(0), scan_flag(0), + mysql(0), stored_result(0), ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0) {} @@ -2243,7 +2243,7 @@ int ha_federated::rnd_init(bool scan) containing the correct record, hence update the wrong row! */ - scan_flag= scan; + if (scan) { DBUG_PRINT("info", ("share->select_query %s", share->select_query)); @@ -2365,24 +2365,13 @@ void ha_federated::position(const byte *record) int ha_federated::rnd_pos(byte *buf, byte *pos) { DBUG_ENTER("ha_federated::rnd_pos"); - /* - we do not need to do any of this if there has been a scan performed - already, or if this is an update and index_read_idx already has a result - set in which to build it's update query from - */ - if (scan_flag) - { - int retval; - statistic_increment(table->in_use->status_var.ha_read_rnd_count, - &LOCK_status); - memcpy_fixed(¤t_position, pos, sizeof(MYSQL_ROW_OFFSET)); // pos - /* is not aligned */ - stored_result->current_row= 0; - stored_result->data_cursor= current_position; - retval= rnd_next(buf); - DBUG_RETURN(retval); - } - DBUG_RETURN(0); + + statistic_increment(table->in_use->status_var.ha_read_rnd_count, + &LOCK_status); + memcpy_fixed(¤t_position, pos, sizeof(MYSQL_ROW_OFFSET)); + stored_result->current_row= 0; + stored_result->data_cursor= current_position; + DBUG_RETURN(rnd_next(buf)); } diff --git a/sql/ha_federated.h b/sql/ha_federated.h index 08203d7e51d..cafd1fe59a5 100644 --- a/sql/ha_federated.h +++ b/sql/ha_federated.h @@ -153,7 +153,6 @@ class ha_federated: public handler FEDERATED_SHARE *share; /* Shared lock info */ MYSQL *mysql; /* MySQL connection */ MYSQL_RES *stored_result; - bool scan_flag; uint ref_length; uint fetch_num; // stores the fetch num MYSQL_ROW_OFFSET current_position; // Current position used by ::position() diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 1b1326920ad..f656e412e75 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -513,6 +513,13 @@ convert_error_code_to_mysql( return(HA_ERR_NO_SAVEPOINT); } else if (error == (int) DB_LOCK_TABLE_FULL) { + /* Since we rolled back the whole transaction, we must + tell it also to MySQL so that MySQL knows to empty the + cached binlog for this transaction */ + + if (thd) { + ha_rollback(thd); + } return(HA_ERR_LOCK_TABLE_FULL); } else { @@ -6884,8 +6891,8 @@ ha_innobase::store_lock( if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && lock_type <= TL_WRITE) - && (!thd->in_lock_tables - || thd->lex->sql_command == SQLCOM_CALL) + && !(thd->in_lock_tables + && thd->lex->sql_command == SQLCOM_LOCK_TABLES) && !thd->tablespace_op && thd->lex->sql_command != SQLCOM_TRUNCATE && thd->lex->sql_command != SQLCOM_OPTIMIZE diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index da4136def68..9780f163634 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -288,7 +288,27 @@ void ha_myisammrg::info(uint flag) table->s->db_options_in_use= info.options; table->s->is_view= 1; mean_rec_length= info.reclength; - block_size=0; + + /* + The handler::block_size is used all over the code in index scan cost + calculations. It is used to get number of disk seeks required to + retrieve a number of index tuples. + If the merge table has N underlying tables, then (assuming underlying + tables have equal size, the only "simple" approach we can use) + retrieving X index records from a merge table will require N times more + disk seeks compared to doing the same on a MyISAM table with equal + number of records. + In the edge case (file_tables > myisam_block_size) we'll get + block_size==0, and index calculation code will act as if we need one + disk seek to retrieve one index tuple. + + TODO: In 5.2 index scan cost calculation will be factored out into a + virtual function in class handler and we'll be able to remove this hack. + */ + block_size= 0; + if (file->tables) + block_size= myisam_block_size / file->tables; + update_time=0; #if SIZEOF_OFF_T > 4 ref_length=6; // Should be big enough diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 61a6ce75712..692d62574f3 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -1031,6 +1031,7 @@ int ha_ndbcluster::build_index_list(Ndb *ndb, TABLE *tab, enum ILBP phase) NDBDICT *dict= ndb->getDictionary(); DBUG_ENTER("ha_ndbcluster::build_index_list"); + m_has_unique_index= FALSE; // Save information about all known indexes for (i= 0; i < tab->s->keys; i++, key_info++, key_name++) { @@ -1039,6 +1040,7 @@ int ha_ndbcluster::build_index_list(Ndb *ndb, TABLE *tab, enum ILBP phase) m_index[i].type= idx_type; if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX) { + m_has_unique_index= TRUE; strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS); DBUG_PRINT("info", ("Created unique index name \'%s\' for index %d", unique_index_name, i)); @@ -1290,6 +1292,24 @@ int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *rec DBUG_RETURN(0); } +int ha_ndbcluster::set_index_key_from_record(NdbOperation *op, const byte *record, uint keyno) +{ + KEY* key_info= table->key_info + keyno; + KEY_PART_INFO* key_part= key_info->key_part; + KEY_PART_INFO* end= key_part+key_info->key_parts; + uint i; + DBUG_ENTER("set_index_key_from_record"); + + for (i= 0; key_part != end; key_part++, i++) + { + Field* field= key_part->field; + if (set_ndb_key(op, field, m_index[keyno].unique_index_attrid_map[i], + record+key_part->offset)) + ERR_RETURN(m_active_trans->getNdbError()); + } + DBUG_RETURN(0); +} + int ha_ndbcluster::set_index_key(NdbOperation *op, const KEY *key_info, @@ -1443,7 +1463,6 @@ int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data) ERR_RETURN(trans->getNdbError()); } } - if (execute_no_commit(this,trans) != 0) { table->status= STATUS_NOT_FOUND; @@ -1471,30 +1490,136 @@ int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data) } /* - Peek to check if a particular row already exists + * Check that all operations between first and last all + * have gotten the errcode + * If checking for HA_ERR_KEY_NOT_FOUND then update m_dupkey + * for all succeeding operations + */ +bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans, + const NdbOperation *first, + const NdbOperation *last, + uint errcode) +{ + const NdbOperation *op= first; + DBUG_ENTER("ha_ndbcluster::check_all_operations_for_error"); + + while(op) + { + NdbError err= op->getNdbError(); + if (err.status != NdbError::Success) + { + if (ndb_to_mysql_error(&err) != (int) errcode) + DBUG_RETURN(false); + if (op == last) break; + op= trans->getNextCompletedOperation(op); + } + else + { + // We found a duplicate + if (op->getType() == NdbOperation::UniqueIndexAccess) + { + if (errcode == HA_ERR_KEY_NOT_FOUND) + { + NdbIndexOperation *iop= (NdbIndexOperation *) op; + const NDBINDEX *index= iop->getIndex(); + // Find the key_no of the index + for(uint i= 0; i<table->s->keys; i++) + { + if (m_index[i].unique_index == index) + { + m_dupkey= i; + break; + } + } + } + } + else + { + // Must have been primary key access + DBUG_ASSERT(op->getType() == NdbOperation::PrimaryKeyAccess); + if (errcode == HA_ERR_KEY_NOT_FOUND) + m_dupkey= table->s->primary_key; + } + DBUG_RETURN(false); + } + } + DBUG_RETURN(true); +} + +/* + * Peek to check if any rows already exist with conflicting + * primary key or unique index values */ -int ha_ndbcluster::peek_row(const byte *record) +int ha_ndbcluster::peek_indexed_rows(const byte *record) { NdbTransaction *trans= m_active_trans; NdbOperation *op; - DBUG_ENTER("peek_row"); - - NdbOperation::LockMode lm= - (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); - if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || - op->readTuple(lm) != 0) - ERR_RETURN(trans->getNdbError()); - + const NdbOperation *first, *last; + uint i; int res; - if ((res= set_primary_key_from_record(op, record))) - ERR_RETURN(trans->getNdbError()); + DBUG_ENTER("peek_indexed_rows"); + + NdbOperation::LockMode lm= NdbOperation::LM_Read; + first= NULL; + if (table->s->primary_key != MAX_KEY) + { + /* + * Fetch any row with colliding primary key + */ + if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || + op->readTuple(lm) != 0) + ERR_RETURN(trans->getNdbError()); + + first= op; + if ((res= set_primary_key_from_record(op, record))) + ERR_RETURN(trans->getNdbError()); + } + /* + * Fetch any rows with colliding unique indexes + */ + KEY* key_info; + KEY_PART_INFO *key_part, *end; + for (i= 0, key_info= table->key_info; i < table->s->keys; i++, key_info++) + { + if (i != table->s->primary_key && + key_info->flags & HA_NOSAME) + { + // A unique index is defined on table + NdbIndexOperation *iop; + NDBINDEX *unique_index = (NDBINDEX *) m_index[i].unique_index; + key_part= key_info->key_part; + end= key_part + key_info->key_parts; + if (!(iop= trans->getNdbIndexOperation(unique_index, + (const NDBTAB *) m_table)) || + iop->readTuple(lm) != 0) + ERR_RETURN(trans->getNdbError()); - if (execute_no_commit_ie(this,trans) != 0) + if (!first) + first= iop; + if ((res= set_index_key_from_record(iop, record, i))) + ERR_RETURN(trans->getNdbError()); + } + } + last= trans->getLastDefinedOperation(); + if (first) + res= execute_no_commit_ie(this,trans); + else + { + // Table has no keys + table->status= STATUS_NOT_FOUND; + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + if (check_all_operations_for_error(trans, first, last, + HA_ERR_KEY_NOT_FOUND)) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); } + else + { + DBUG_PRINT("info", ("m_dupkey %d", m_dupkey)); + } DBUG_RETURN(0); } @@ -1930,13 +2055,33 @@ int ha_ndbcluster::write_row(byte *record) DBUG_ENTER("write_row"); - if (m_ignore_dup_key && table->s->primary_key != MAX_KEY) + has_auto_increment= (table->next_number_field && record == table->record[0]); + if (table->s->primary_key != MAX_KEY) { - int peek_res= peek_row(record); + /* + * Increase any auto_incremented primary key + */ + if (has_auto_increment) + { + THD *thd= table->in_use; + + m_skip_auto_increment= FALSE; + update_auto_increment(); + /* Ensure that handler is always called for auto_increment values */ + thd->next_insert_id= 0; + m_skip_auto_increment= !auto_increment_column_changed; + } + } + + /* + * If IGNORE the ignore constraint violations on primary and unique keys + */ + if (!m_use_write && m_ignore_dup_key) + { + int peek_res= peek_indexed_rows(record); if (!peek_res) { - m_dupkey= table->s->primary_key; DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY); } if (peek_res != HA_ERR_KEY_NOT_FOUND) @@ -1946,7 +2091,6 @@ int ha_ndbcluster::write_row(byte *record) statistic_increment(thd->status_var.ha_write_count, &LOCK_status); if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) table->timestamp_field->set_time(); - has_auto_increment= (table->next_number_field && record == table->record[0]); if (!(op= trans->getNdbOperation((const NDBTAB *) m_table))) ERR_RETURN(trans->getNdbError()); @@ -1975,17 +2119,6 @@ int ha_ndbcluster::write_row(byte *record) { int res; - if (has_auto_increment) - { - THD *thd= table->in_use; - - m_skip_auto_increment= FALSE; - update_auto_increment(); - /* Ensure that handler is always called for auto_increment values */ - thd->next_insert_id= 0; - m_skip_auto_increment= !auto_increment_column_changed; - } - if ((res= set_primary_key_from_record(op, record))) return res; } @@ -2996,7 +3129,7 @@ int ha_ndbcluster::extra(enum ha_extra_function operation) break; case HA_EXTRA_IGNORE_DUP_KEY: /* Dup keys don't rollback everything*/ DBUG_PRINT("info", ("HA_EXTRA_IGNORE_DUP_KEY")); - if (current_thd->lex->sql_command == SQLCOM_REPLACE) + if (current_thd->lex->sql_command == SQLCOM_REPLACE && !m_has_unique_index) { DBUG_PRINT("info", ("Turning ON use of write instead of insert")); m_use_write= TRUE; @@ -4260,6 +4393,7 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): m_share(0), m_use_write(FALSE), m_ignore_dup_key(FALSE), + m_has_unique_index(FALSE), m_primary_key_update(FALSE), m_retrieve_all_fields(FALSE), m_retrieve_primary_key(FALSE), @@ -6014,7 +6148,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) ndb_util_thread= pthread_self(); thd->thread_stack= (char*)&thd; /* remember where our stack is */ - if (thd->store_globals() && (ndb->init() != -1)) + if (thd->store_globals() || (ndb->init() != 0)) { thd->cleanup(); delete thd; diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index a44604b05b0..d75d7acefd9 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -597,7 +597,11 @@ private: int pk_read(const byte *key, uint key_len, byte *buf); int complemented_pk_read(const byte *old_data, byte *new_data); - int peek_row(const byte *record); + bool check_all_operations_for_error(NdbTransaction *trans, + const NdbOperation *first, + const NdbOperation *last, + uint errcode); + int peek_indexed_rows(const byte *record); int unique_index_read(const byte *key, uint key_len, byte *buf); int ordered_index_scan(const key_range *start_key, @@ -627,6 +631,8 @@ private: int get_ndb_blobs_value(NdbBlob *last_ndb_blob); int set_primary_key(NdbOperation *op, const byte *key); int set_primary_key_from_record(NdbOperation *op, const byte *record); + int set_index_key_from_record(NdbOperation *op, const byte *record, + uint keyno); int set_bounds(NdbIndexScanOperation*, const key_range *keys[2], uint= 0); int key_cmp(uint keynr, const byte * old_row, const byte * new_row); int set_index_key(NdbOperation *, const KEY *key_info, const byte *key_ptr); @@ -686,6 +692,7 @@ private: byte m_ref[NDB_HIDDEN_PRIMARY_KEY_LENGTH]; bool m_use_write; bool m_ignore_dup_key; + bool m_has_unique_index; bool m_primary_key_update; bool m_retrieve_all_fields; bool m_retrieve_primary_key; diff --git a/sql/handler.cc b/sql/handler.cc index 4e128eb5938..006a0eb2407 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -425,6 +425,7 @@ static int ha_init_errors(void) SETMSG(HA_ERR_TABLE_EXIST, ER(ER_TABLE_EXISTS_ERROR)); SETMSG(HA_ERR_NO_CONNECTION, "Could not connect to storage engine"); SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER(ER_TABLE_DEF_CHANGED)); + SETMSG(HA_ERR_TABLE_NEEDS_UPGRADE, ER(ER_TABLE_NEEDS_UPGRADE)); /* Register the error messages for use with my_error(). */ return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST); @@ -1795,6 +1796,9 @@ void handler::print_error(int error, myf errflag) my_error(ER_NO_SUCH_TABLE, MYF(0), db, table->alias); break; } + case HA_ERR_TABLE_NEEDS_UPGRADE: + textno=ER_TABLE_NEEDS_UPGRADE; + break; default: { /* The error was "unknown" to this function. @@ -1836,6 +1840,103 @@ bool handler::get_error_message(int error, String* buf) } +int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt) +{ + KEY *keyinfo, *keyend; + KEY_PART_INFO *keypart, *keypartend; + + if (!table->s->mysql_version) + { + /* check for blob-in-key error */ + keyinfo= table->key_info; + keyend= table->key_info + table->s->keys; + for (; keyinfo < keyend; keyinfo++) + { + keypart= keyinfo->key_part; + keypartend= keypart + keyinfo->key_parts; + for (; keypart < keypartend; keypart++) + { + if (!keypart->fieldnr) + continue; + Field *field= table->field[keypart->fieldnr-1]; + if (field->type() == FIELD_TYPE_BLOB) + { + if (check_opt->sql_flags & TT_FOR_UPGRADE) + check_opt->flags= T_MEDIUM; + return HA_ADMIN_NEEDS_CHECK; + } + } + } + } + return check_for_upgrade(check_opt); +} + + +int handler::check_old_types() +{ + Field** field; + + if (!table->s->mysql_version) + { + /* check for bad DECIMAL field */ + for (field= table->field; (*field); field++) + { + if ((*field)->type() == FIELD_TYPE_NEWDECIMAL) + { + return HA_ADMIN_NEEDS_ALTER; + } + } + } + return 0; +} + + +static bool update_frm_version(TABLE *table, bool needs_lock) +{ + char path[FN_REFLEN]; + File file; + int result= 1; + DBUG_ENTER("update_frm_version"); + + if (table->s->mysql_version != MYSQL_VERSION_ID) + DBUG_RETURN(0); + + strxnmov(path, sizeof(path)-1, mysql_data_home, "/", table->s->db, "/", + table->s->table_name, reg_ext, NullS); + if (!unpack_filename(path, path)) + DBUG_RETURN(1); + + if (needs_lock) + pthread_mutex_lock(&LOCK_open); + + if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0) + { + uchar version[4]; + char *key= table->s->table_cache_key; + uint key_length= table->s->key_length; + TABLE *entry; + HASH_SEARCH_STATE state; + + int4store(version, MYSQL_VERSION_ID); + + if ((result= my_pwrite(file,(byte*) version,4,51L,MYF_RW))) + goto err; + + for (entry=(TABLE*) hash_first(&open_cache,(byte*) key,key_length, &state); + entry; + entry= (TABLE*) hash_next(&open_cache,(byte*) key,key_length, &state)) + entry->s->mysql_version= MYSQL_VERSION_ID; + } +err: + if (file >= 0) + VOID(my_close(file,MYF(MY_WME))); + if (needs_lock) + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(result); +} + + + /* Return key if error because of duplicated keys */ uint handler::get_dup_key(int error) @@ -1903,6 +2004,57 @@ int handler::rename_table(const char * from, const char * to) return error; } + +/* + Performs checks upon the table. + + SYNOPSIS + check() + thd thread doing CHECK TABLE operation + check_opt options from the parser + + NOTES + + RETURN + HA_ADMIN_OK Successful upgrade + HA_ADMIN_NEEDS_UPGRADE Table has structures requiring upgrade + HA_ADMIN_NEEDS_ALTER Table has structures requiring ALTER TABLE + HA_ADMIN_NOT_IMPLEMENTED +*/ + +int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt) +{ + int error; + + if ((table->s->mysql_version >= MYSQL_VERSION_ID) && + (check_opt->sql_flags & TT_FOR_UPGRADE)) + return 0; + + if (table->s->mysql_version < MYSQL_VERSION_ID) + { + if ((error= check_old_types())) + return error; + error= ha_check_for_upgrade(check_opt); + if (error && (error != HA_ADMIN_NEEDS_CHECK)) + return error; + if (!error && (check_opt->sql_flags & TT_FOR_UPGRADE)) + return 0; + } + if ((error= check(thd, check_opt))) + return error; + return update_frm_version(table, 0); +} + + +int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt) +{ + int result; + if ((result= repair(thd, check_opt))) + return result; + return update_frm_version(table, 0); +} + + /* Tell the storage engine that it is allowed to "disable transaction" in the handler. It is a hint that ACID is not required - it is used in NDB for diff --git a/sql/handler.h b/sql/handler.h index 91c5be9ba39..977bd77a54e 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -46,6 +46,9 @@ #define HA_ADMIN_TRY_ALTER -7 #define HA_ADMIN_WRONG_CHECKSUM -8 #define HA_ADMIN_NOT_BASE_TABLE -9 +#define HA_ADMIN_NEEDS_UPGRADE -10 +#define HA_ADMIN_NEEDS_ALTER -11 +#define HA_ADMIN_NEEDS_CHECK -12 /* Bits in table_flags() to show what database can do */ @@ -228,6 +231,7 @@ struct xid_t { long bqual_length; char data[XIDDATASIZE]; // not \0-terminated ! + xid_t() {} /* Remove gcc warning */ bool eq(struct xid_t *xid) { return eq(xid->gtrid_length, xid->bqual_length, xid->data); } bool eq(long g, long b, const char *d) @@ -460,6 +464,7 @@ typedef class Item COND; typedef struct st_ha_check_opt { + st_ha_check_opt() {} /* Remove gcc warning */ ulong sort_buffer_size; uint flags; /* isam layer flags (e.g. for myisamchk) */ uint sql_flags; /* sql layer flags - for something myisamchk cannot do */ @@ -702,10 +707,26 @@ public: { return HA_ERR_WRONG_COMMAND; } virtual void update_create_info(HA_CREATE_INFO *create_info) {} +protected: + /* to be implemented in handlers */ /* admin commands - called from mysql_admin_table */ virtual int check(THD* thd, HA_CHECK_OPT* check_opt) { return HA_ADMIN_NOT_IMPLEMENTED; } + + /* + in these two methods check_opt can be modified + to specify CHECK option to use to call check() + upon the table + */ + virtual int check_for_upgrade(HA_CHECK_OPT *check_opt) + { return 0; } +public: + int ha_check_for_upgrade(HA_CHECK_OPT *check_opt); + int check_old_types(); + /* to be actually called to get 'check()' functionality*/ + int ha_check(THD *thd, HA_CHECK_OPT *check_opt); + virtual int backup(THD* thd, HA_CHECK_OPT* check_opt) { return HA_ADMIN_NOT_IMPLEMENTED; } /* @@ -714,8 +735,11 @@ public: */ virtual int restore(THD* thd, HA_CHECK_OPT* check_opt) { return HA_ADMIN_NOT_IMPLEMENTED; } +protected: virtual int repair(THD* thd, HA_CHECK_OPT* check_opt) { return HA_ADMIN_NOT_IMPLEMENTED; } +public: + int ha_repair(THD* thd, HA_CHECK_OPT* check_opt); virtual int optimize(THD* thd, HA_CHECK_OPT* check_opt) { return HA_ADMIN_NOT_IMPLEMENTED; } virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt) diff --git a/sql/hostname.cc b/sql/hostname.cc index 3b1eeb63d37..c5c337080cf 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -61,7 +61,7 @@ bool hostname_cache_init() if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset, sizeof(struct in_addr),NULL, (hash_free_key) free, - &my_charset_latin1))) + &my_charset_bin))) return 1; hostname_cache->clear(); (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); diff --git a/sql/item.cc b/sql/item.cc index 70fea63ba7e..c48bf19a88b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -375,9 +375,6 @@ void Item::print_item_w_name(String *str) void Item::cleanup() { DBUG_ENTER("Item::cleanup"); - DBUG_PRINT("info", ("Item: 0x%lx, Type: %d, name %s, original name %s", - this, (int)type(), name ? name : "(null)", - orig_name ? orig_name : "null")); fixed=0; marker= 0; if (orig_name) @@ -2645,25 +2642,8 @@ const String *Item_param::query_val_str(String* str) const case STRING_VALUE: case LONG_DATA_VALUE: { - char *buf, *ptr; str->length(0); - if (str->reserve(str_value.length()*2+3)) - break; - - buf= str->c_ptr_quick(); - ptr= buf; - if (value.cs_info.character_set_client->escape_with_backslash_is_dangerous) - { - ptr= str_to_hex(ptr, str_value.ptr(), str_value.length()); - } - else - { - *ptr++= '\''; - ptr+= escape_string_for_mysql(str_value.charset(), ptr, 0, - str_value.ptr(), str_value.length()); - *ptr++='\''; - } - str->length((uint32) (ptr - buf)); + append_query_string(value.cs_info.character_set_client, &str_value, str); break; } case NULL_VALUE: @@ -3211,6 +3191,252 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) /* + Resolve the name of an outer select column reference. + + SYNOPSIS + Item_field::fix_outer_field() + thd [in] current thread + from_field [in/out] found field reference or (Field*)not_found_field + reference [in/out] view column if this item was resolved to a view column + + DESCRIPTION + The method resolves the column reference represented by 'this' as a column + present in outer selects that contain current select. + + NOTES + This is the inner loop of Item_field::fix_fields: + + for each outer query Q_k beginning from the inner-most one + { + search for a column or derived column named col_ref_i + [in table T_j] in the FROM clause of Q_k; + + if such a column is not found + Search for a column or derived column named col_ref_i + [in table T_j] in the SELECT and GROUP clauses of Q_k. + } + + IMPLEMENTATION + In prepared statements, because of cache, find_field_in_tables() + can resolve fields even if they don't belong to current context. + In this case this method only finds appropriate context and marks + current select as dependent. The found reference of field should be + provided in 'from_field'. + + RETURN + 1 - column succefully resolved and fix_fields() should continue. + 0 - column fully fixed and fix_fields() should return FALSE + -1 - error occured +*/ +int +Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) +{ + enum_parsing_place place= NO_MATTER; + bool field_found= (*from_field != not_found_field); + bool upward_lookup= FALSE; + + /* + If there are outer contexts (outer selects, but current select is + not derived table or view) try to resolve this reference in the + outer contexts. + + We treat each subselect as a separate namespace, so that different + subselects may contain columns with the same names. The subselects + are searched starting from the innermost. + */ + Name_resolution_context *last_checked_context= context; + Item **ref= (Item **) not_found_item; + Name_resolution_context *outer_context= context->outer_context; + for (; + outer_context; + outer_context= outer_context->outer_context) + { + SELECT_LEX *select= outer_context->select_lex; + Item_subselect *prev_subselect_item= + last_checked_context->select_lex->master_unit()->item; + last_checked_context= outer_context; + upward_lookup= TRUE; + + place= prev_subselect_item->parsing_place; + /* + If outer_field is set, field was already found by first call + to find_field_in_tables(). Only need to find appropriate context. + */ + if (field_found && outer_context->select_lex != + cached_table->select_lex) + continue; + /* + In case of a view, find_field_in_tables() writes the pointer to + the found view field into '*reference', in other words, it + substitutes this Item_field with the found expression. + */ + if (field_found || (*from_field= find_field_in_tables(thd, this, + outer_context-> + first_name_resolution_table, + outer_context-> + last_name_resolution_table, + reference, + IGNORE_EXCEPT_NON_UNIQUE, + TRUE, TRUE)) != + not_found_field) + { + if (*from_field) + { + if (*from_field != view_ref_found) + { + prev_subselect_item->used_tables_cache|= (*from_field)->table->map; + prev_subselect_item->const_item_cache= 0; + if (thd->lex->in_sum_func && + thd->lex->in_sum_func->nest_level == + thd->lex->current_select->nest_level) + { + Item::Type type= (*reference)->type(); + set_if_bigger(thd->lex->in_sum_func->max_arg_level, + select->nest_level); + set_field(*from_field); + fixed= 1; + mark_as_dependent(thd, last_checked_context->select_lex, + context->select_lex, this, + ((type == REF_ITEM || type == FIELD_ITEM) ? + (Item_ident*) (*reference) : 0)); + return 0; + } + } + else + { + Item::Type type= (*reference)->type(); + prev_subselect_item->used_tables_cache|= + (*reference)->used_tables(); + prev_subselect_item->const_item_cache&= + (*reference)->const_item(); + mark_as_dependent(thd, last_checked_context->select_lex, + context->select_lex, this, + ((type == REF_ITEM || type == FIELD_ITEM) ? + (Item_ident*) (*reference) : + 0)); + /* + A reference to a view field had been found and we + substituted it instead of this Item (find_field_in_tables + does it by assigning the new value to *reference), so now + we can return from this function. + */ + return 0; + } + } + break; + } + + /* Search in SELECT and GROUP lists of the outer select. */ + if (outer_context->resolve_in_select_list) + { + if (!(ref= resolve_ref_in_select_and_group(thd, this, select))) + return -1; /* Some error occurred (e.g. ambiguous names). */ + if (ref != not_found_item) + { + DBUG_ASSERT(*ref && (*ref)->fixed); + prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); + prev_subselect_item->const_item_cache&= (*ref)->const_item(); + break; + } + } + + /* + Reference is not found in this select => this subquery depend on + outer select (or we just trying to find wrong identifier, in this + case it does not matter which used tables bits we set) + */ + prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; + prev_subselect_item->const_item_cache= 0; + } + + DBUG_ASSERT(ref != 0); + if (!*from_field) + return -1; + if (ref == not_found_item && *from_field == not_found_field) + { + if (upward_lookup) + { + // We can't say exactly what absent table or field + my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where); + } + else + { + /* Call find_field_in_tables only to report the error */ + find_field_in_tables(thd, this, + context->first_name_resolution_table, + context->last_name_resolution_table, + reference, REPORT_ALL_ERRORS, + !any_privileges && + TRUE, TRUE); + } + return -1; + } + else if (ref != not_found_item) + { + Item *save; + Item_ref *rf; + + /* Should have been checked in resolve_ref_in_select_and_group(). */ + DBUG_ASSERT(*ref && (*ref)->fixed); + /* + Here, a subset of actions performed by Item_ref::set_properties + is not enough. So we pass ptr to NULL into Item_[direct]_ref + constructor, so no initialization is performed, and call + fix_fields() below. + */ + save= *ref; + *ref= NULL; // Don't call set_properties() + rf= (place == IN_HAVING ? + new Item_ref(context, ref, (char*) table_name, + (char*) field_name) : + new Item_direct_ref(context, ref, (char*) table_name, + (char*) field_name)); + *ref= save; + if (!rf) + return -1; + thd->change_item_tree(reference, rf); + /* + rf is Item_ref => never substitute other items (in this case) + during fix_fields() => we can use rf after fix_fields() + */ + DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() + if (rf->fix_fields(thd, reference) || rf->check_cols(1)) + return -1; + + mark_as_dependent(thd, last_checked_context->select_lex, + context->select_lex, this, + rf); + return 0; + } + else + { + mark_as_dependent(thd, last_checked_context->select_lex, + context->select_lex, + this, this); + if (last_checked_context->select_lex->having_fix_field) + { + Item_ref *rf; + rf= new Item_ref(context, + (cached_table->db[0] ? cached_table->db : 0), + (char*) cached_table->alias, (char*) field_name); + if (!rf) + return -1; + thd->change_item_tree(reference, rf); + /* + rf is Item_ref => never substitute other items (in this case) + during fix_fields() => we can use rf after fix_fields() + */ + DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() + if (rf->fix_fields(thd, reference) || rf->check_cols(1)) + return -1; + return 0; + } + } + return 1; +} + + +/* Resolve the name of a column reference. SYNOPSIS @@ -3257,12 +3483,11 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) bool Item_field::fix_fields(THD *thd, Item **reference) { - enum_parsing_place place= NO_MATTER; DBUG_ASSERT(fixed == 0); if (!field) // If field is not checked { - bool upward_lookup= FALSE; Field *from_field= (Field *)not_found_field; + bool outer_fixed= false; /* In case of view, find_field_in_tables() write pointer to view field expression to 'reference', i.e. it substitute that expression instead @@ -3277,7 +3502,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) TRUE)) == not_found_field) { - + int ret; /* Look up in current select's item_list to find aliased fields */ if (thd->lex->current_select->is_item_list_lookup) { @@ -3292,197 +3517,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference) return 0; } } - - /* - If there are outer contexts (outer selects, but current select is - not derived table or view) try to resolve this reference in the - outer contexts. - - We treat each subselect as a separate namespace, so that different - subselects may contain columns with the same names. The subselects - are searched starting from the innermost. - */ - Name_resolution_context *last_checked_context= context; - Item **ref= (Item **) not_found_item; - Name_resolution_context *outer_context= context->outer_context; - for (; - outer_context; - outer_context= outer_context->outer_context) - { - SELECT_LEX *select= outer_context->select_lex; - Item_subselect *prev_subselect_item= - last_checked_context->select_lex->master_unit()->item; - last_checked_context= outer_context; - upward_lookup= TRUE; - - place= prev_subselect_item->parsing_place; - /* - In case of a view, find_field_in_tables() writes the pointer to - the found view field into '*reference', in other words, it - substitutes this Item_field with the found expression. - */ - if ((from_field= find_field_in_tables(thd, this, - outer_context-> - first_name_resolution_table, - outer_context-> - last_name_resolution_table, - reference, - IGNORE_EXCEPT_NON_UNIQUE, - TRUE, TRUE)) != - not_found_field) - { - if (from_field) - { - if (from_field != view_ref_found) - { - prev_subselect_item->used_tables_cache|= from_field->table->map; - prev_subselect_item->const_item_cache= 0; - if (thd->lex->in_sum_func && - thd->lex->in_sum_func->nest_level == - thd->lex->current_select->nest_level) - { - Item::Type type= (*reference)->type(); - set_if_bigger(thd->lex->in_sum_func->max_arg_level, - select->nest_level); - set_field(from_field); - fixed= 1; - mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, this, - ((type == REF_ITEM || type == FIELD_ITEM) ? - (Item_ident*) (*reference) : 0)); - return FALSE; - } - } - else - { - Item::Type type= (*reference)->type(); - prev_subselect_item->used_tables_cache|= - (*reference)->used_tables(); - prev_subselect_item->const_item_cache&= - (*reference)->const_item(); - mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, this, - ((type == REF_ITEM || type == FIELD_ITEM) ? - (Item_ident*) (*reference) : - 0)); - /* - A reference to a view field had been found and we - substituted it instead of this Item (find_field_in_tables - does it by assigning the new value to *reference), so now - we can return from this function. - */ - return FALSE; - } - } - break; - } - - /* Search in SELECT and GROUP lists of the outer select. */ - if (outer_context->resolve_in_select_list) - { - if (!(ref= resolve_ref_in_select_and_group(thd, this, select))) - goto error; /* Some error occurred (e.g. ambiguous names). */ - if (ref != not_found_item) - { - DBUG_ASSERT(*ref && (*ref)->fixed); - prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); - prev_subselect_item->const_item_cache&= (*ref)->const_item(); - break; - } - } - - /* - Reference is not found in this select => this subquery depend on - outer select (or we just trying to find wrong identifier, in this - case it does not matter which used tables bits we set) - */ - prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; - prev_subselect_item->const_item_cache= 0; - } - - DBUG_ASSERT(ref != 0); - if (!from_field) - goto error; - if (ref == not_found_item && from_field == not_found_field) - { - if (upward_lookup) - { - // We can't say exactly what absent table or field - my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where); - } - else - { - /* Call find_field_in_tables only to report the error */ - find_field_in_tables(thd, this, - context->first_name_resolution_table, - context->last_name_resolution_table, - reference, REPORT_ALL_ERRORS, - !any_privileges && - TRUE, TRUE); - } - goto error; - } - else if (ref != not_found_item) - { - Item *save; - Item_ref *rf; - - /* Should have been checked in resolve_ref_in_select_and_group(). */ - DBUG_ASSERT(*ref && (*ref)->fixed); - /* - Here, a subset of actions performed by Item_ref::set_properties - is not enough. So we pass ptr to NULL into Item_[direct]_ref - constructor, so no initialization is performed, and call - fix_fields() below. - */ - save= *ref; - *ref= NULL; // Don't call set_properties() - rf= (place == IN_HAVING ? - new Item_ref(context, ref, (char*) table_name, - (char*) field_name) : - new Item_direct_ref(context, ref, (char*) table_name, - (char*) field_name)); - *ref= save; - if (!rf) - goto error; - thd->change_item_tree(reference, rf); - /* - rf is Item_ref => never substitute other items (in this case) - during fix_fields() => we can use rf after fix_fields() - */ - DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() - if (rf->fix_fields(thd, reference) || rf->check_cols(1)) - goto error; - - mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, this, - rf); - return FALSE; - } - else - { - mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, - this, this); - if (last_checked_context->select_lex->having_fix_field) - { - Item_ref *rf; - rf= new Item_ref(context, - (cached_table->db[0] ? cached_table->db : 0), - (char*) cached_table->alias, (char*) field_name); - if (!rf) - goto error; - thd->change_item_tree(reference, rf); - /* - rf is Item_ref => never substitute other items (in this case) - during fix_fields() => we can use rf after fix_fields() - */ - DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() - if (rf->fix_fields(thd, reference) || rf->check_cols(1)) - goto error; - return FALSE; - } - } + if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) + goto error; + else if (!ret) + return FALSE; + outer_fixed= TRUE; } else if (!from_field) goto error; @@ -3502,6 +3541,17 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (from_field == view_ref_found) return FALSE; + if (!outer_fixed && cached_table && cached_table->select_lex && + context->select_lex && + cached_table->select_lex != context->select_lex) + { + int ret; + if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) + goto error; + else if (!ret) + return FALSE; + } + set_field(from_field); if (thd->lex->in_sum_func && thd->lex->in_sum_func->nest_level == @@ -4620,6 +4670,25 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) } if (from_field != not_found_field) { + if (cached_table && cached_table->select_lex && + outer_context->select_lex && + cached_table->select_lex != outer_context->select_lex) + { + /* + Due to cache, find_field_in_tables() can return field which + doesn't belong to provided outer_context. In this case we have + to find proper field context in order to fix field correcly. + */ + do + { + outer_context= outer_context->outer_context; + select= outer_context->select_lex; + prev_subselect_item= + last_checked_context->select_lex->master_unit()->item; + last_checked_context= outer_context; + } while (outer_context && outer_context->select_lex && + cached_table->select_lex != outer_context->select_lex); + } prev_subselect_item->used_tables_cache|= from_field->table->map; prev_subselect_item->const_item_cache= 0; break; @@ -5027,9 +5096,9 @@ bool Item_direct_view_ref::eq(const Item *item, bool binary_cmp) const if (item_ref->ref_type() == VIEW_REF) { Item *item_ref_ref= *(item_ref->ref); - DBUG_ASSERT((*ref)->type() == FIELD_ITEM && - (item_ref_ref->type() == FIELD_ITEM)); - return (*ref == item_ref_ref); + DBUG_ASSERT((*ref)->real_item()->type() == FIELD_ITEM && + (item_ref_ref->real_item()->type() == FIELD_ITEM)); + return ((*ref)->real_item() == item_ref_ref->real_item()); } } return FALSE; @@ -5892,7 +5961,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) const char *old_cs, *old_derivation; old_cs= collation.collation->name; old_derivation= collation.derivation_name(); - if (collation.aggregate(item->collation)) + if (collation.aggregate(item->collation, MY_COLL_ALLOW_CONV)) { my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), old_cs, old_derivation, diff --git a/sql/item.h b/sql/item.h index af42ce38b9b..2e3e0acc408 100644 --- a/sql/item.h +++ b/sql/item.h @@ -164,7 +164,8 @@ struct Hybrid_type_traits virtual my_decimal *val_decimal(Hybrid_type *val, my_decimal *buf) const; virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const; static const Hybrid_type_traits *instance(); - Hybrid_type_traits() {}; + Hybrid_type_traits() {} + virtual ~Hybrid_type_traits() {} }; @@ -339,6 +340,7 @@ private: bool save_resolve_in_select_list; public: + Name_resolution_context_state() {} /* Remove gcc warning */ TABLE_LIST *save_next_local; public: @@ -1015,6 +1017,7 @@ bool agg_item_charsets(DTCollation &c, const char *name, class Item_num: public Item { public: + Item_num() {} /* Remove gcc warning */ virtual Item_num *neg()= 0; Item *safe_charset_converter(CHARSET_INFO *tocs); }; @@ -1161,6 +1164,7 @@ public: inline uint32 max_disp_length() { return field->max_length(); } Item_field *filed_for_view_update() { return this; } Item *safe_charset_converter(CHARSET_INFO *tocs); + int fix_outer_field(THD *thd, Field **field, Item **reference); friend class Item_default_value; friend class Item_insert_value; friend class st_select_lex_unit; @@ -1581,7 +1585,6 @@ public: str_value.length(), collation.collation); } Item *safe_charset_converter(CHARSET_INFO *tocs); - String *const_string() { return &str_value; } inline void append(char *str, uint length) { str_value.append(str, length); } void print(String *str); // to prevent drop fixed flag (no need parent cleanup call) @@ -1883,9 +1886,10 @@ class Item_int_with_ref :public Item_int { Item *ref; public: - Item_int_with_ref(longlong i, Item *ref_arg) :Item_int(i), ref(ref_arg) + Item_int_with_ref(longlong i, Item *ref_arg, my_bool unsigned_arg) : + Item_int(i), ref(ref_arg) { - unsigned_flag= ref_arg->unsigned_flag; + unsigned_flag= unsigned_arg; } int save_in_field(Field *field, bool no_conversions) { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index bb3f6e8b231..2d74ea9c518 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -216,7 +216,8 @@ static bool convert_constant_item(THD *thd, Field *field, Item **item) field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; if (!(*item)->save_in_field(field, 1) && !((*item)->null_value)) { - Item *tmp=new Item_int_with_ref(field->val_int(), *item); + Item *tmp=new Item_int_with_ref(field->val_int(), *item, + test(field->flags & UNSIGNED_FLAG)); field->table->in_use->variables.sql_mode= orig_sql_mode; if (tmp) thd->change_item_tree(item, tmp); @@ -697,12 +698,6 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) return 1; cache->setup(args[0]); - /* - If it is preparation PS only then we do not know values of parameters => - cant't get there values and do not need that values. - */ - if (!thd->stmt_arena->is_stmt_prepare()) - cache->store(args[0]); if (cache->cols() == 1) { if ((used_tables_cache= args[0]->used_tables())) @@ -1383,7 +1378,8 @@ Item_func_if::fix_length_and_dec() max_length= (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT) ? (max(args[1]->max_length - args[1]->decimals, - args[2]->max_length - args[2]->decimals) + decimals) : + args[2]->max_length - args[2]->decimals) + decimals + + (unsigned_flag ? 0 : 1) ) : max(args[1]->max_length, args[2]->max_length); } @@ -2551,7 +2547,8 @@ Item_cond::fix_fields(THD *thd, Item **ref) { table_map tmp_table_map; while (item->type() == Item::COND_ITEM && - ((Item_cond*) item)->functype() == functype()) + ((Item_cond*) item)->functype() == functype() && + !((Item_cond*) item)->list.is_empty()) { // Identical function li.replace(((Item_cond*) item)->list); ((Item_cond*) item)->list.empty(); @@ -2566,10 +2563,13 @@ Item_cond::fix_fields(THD *thd, Item **ref) (item= *li.ref())->check_cols(1)) return TRUE; /* purecov: inspected */ used_tables_cache|= item->used_tables(); - tmp_table_map= item->not_null_tables(); - not_null_tables_cache|= tmp_table_map; - and_tables_cache&= tmp_table_map; - const_item_cache&= item->const_item(); + if (!item->const_item()) + { + tmp_table_map= item->not_null_tables(); + not_null_tables_cache|= tmp_table_map; + and_tables_cache&= tmp_table_map; + const_item_cache= FALSE; + } with_sum_func= with_sum_func || item->with_sum_func; if (item->maybe_null) maybe_null=1; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 15aebd2492c..89aafa5721e 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -128,6 +128,8 @@ public: class Comp_creator { public: + Comp_creator() {} /* Remove gcc warning */ + virtual ~Comp_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const = 0; virtual const char* symbol(bool invert) const = 0; virtual bool eqne_op() const = 0; @@ -137,6 +139,8 @@ public: class Eq_creator :public Comp_creator { public: + Eq_creator() {} /* Remove gcc warning */ + virtual ~Eq_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? "<>" : "="; } virtual bool eqne_op() const { return 1; } @@ -146,6 +150,8 @@ public: class Ne_creator :public Comp_creator { public: + Ne_creator() {} /* Remove gcc warning */ + virtual ~Ne_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? "=" : "<>"; } virtual bool eqne_op() const { return 1; } @@ -155,6 +161,8 @@ public: class Gt_creator :public Comp_creator { public: + Gt_creator() {} /* Remove gcc warning */ + virtual ~Gt_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? "<=" : ">"; } virtual bool eqne_op() const { return 0; } @@ -164,6 +172,8 @@ public: class Lt_creator :public Comp_creator { public: + Lt_creator() {} /* Remove gcc warning */ + virtual ~Lt_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? ">=" : "<"; } virtual bool eqne_op() const { return 0; } @@ -173,6 +183,8 @@ public: class Ge_creator :public Comp_creator { public: + Ge_creator() {} /* Remove gcc warning */ + virtual ~Ge_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? "<" : ">="; } virtual bool eqne_op() const { return 0; } @@ -182,6 +194,8 @@ public: class Le_creator :public Comp_creator { public: + Le_creator() {} /* Remove gcc warning */ + virtual ~Le_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? ">" : "<="; } virtual bool eqne_op() const { return 0; } @@ -739,6 +753,7 @@ class cmp_item_int :public cmp_item { longlong value; public: + cmp_item_int() {} /* Remove gcc warning */ void store_value(Item *item) { value= item->val_int(); @@ -759,6 +774,7 @@ class cmp_item_real :public cmp_item { double value; public: + cmp_item_real() {} /* Remove gcc warning */ void store_value(Item *item) { value= item->val_real(); @@ -780,6 +796,7 @@ class cmp_item_decimal :public cmp_item { my_decimal value; public: + cmp_item_decimal() {} /* Remove gcc warning */ void store_value(Item *item); int cmp(Item *arg); int compare(cmp_item *c); diff --git a/sql/item_func.cc b/sql/item_func.cc index a85f05c2e22..f40f868f75f 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2606,7 +2606,7 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func, switch(arguments[i]->type()) { case Item::STRING_ITEM: // Constant string ! { - String *res=arguments[i]->val_str((String *) 0); + String *res=arguments[i]->val_str(&buffers[i]); if (arguments[i]->null_value) continue; f_args.args[i]= (char*) res->ptr(); @@ -2805,9 +2805,6 @@ longlong Item_func_udf_int::val_int() { DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_udf_int::val_int"); - DBUG_PRINT("info",("result_type: %d arg_count: %d", - args[0]->result_type(), arg_count)); - DBUG_RETURN(udf.val_int(&null_value)); } @@ -4881,6 +4878,7 @@ Item_func_sp::fix_length_and_dec() { decimals= result_field->decimals(); max_length= result_field->field_length; + collation.set(result_field->charset()); DBUG_VOID_RETURN; } @@ -4891,6 +4889,7 @@ Item_func_sp::fix_length_and_dec() } decimals= field->decimals(); max_length= field->field_length; + collation.set(field->charset()); maybe_null= 1; delete field; DBUG_VOID_RETURN; diff --git a/sql/item_func.h b/sql/item_func.h index d8fa45fb9c0..ccbbbab1df4 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1421,9 +1421,21 @@ public: String *val_str(String *str) { + String buf; + char buff[20]; + buf.set(buff, 20, str->charset()); + buf.length(0); if (execute(&result_field)) return NULL; - return result_field->val_str(str); + /* + result_field will set buf pointing to internal buffer + of the resul_field. Due to this it will change any time + when SP is executed. In order to prevent occasional + corruption of returned value, we make here a copy. + */ + result_field->val_str(&buf); + str->copy(buf); + return str; } virtual bool change_context_processor(byte *cntx) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index fe02e7c5b49..a3e47154bc3 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1128,9 +1128,9 @@ void Item_func_substr_index::fix_length_and_dec() String *Item_func_substr_index::val_str(String *str) { DBUG_ASSERT(fixed == 1); - String *res =args[0]->val_str(str); - String *delimeter =args[1]->val_str(&tmp_value); - int32 count = (int32) args[2]->val_int(); + String *res= args[0]->val_str(str); + String *delimiter= args[1]->val_str(&tmp_value); + int32 count= (int32) args[2]->val_int(); uint offset; if (args[0]->null_value || args[1]->null_value || args[2]->null_value) @@ -1139,8 +1139,8 @@ String *Item_func_substr_index::val_str(String *str) return 0; } null_value=0; - uint delimeter_length=delimeter->length(); - if (!res->length() || !delimeter_length || !count) + uint delimiter_length= delimiter->length(); + if (!res->length() || !delimiter_length || !count) return &my_empty_string; // Wrong parameters res->set_charset(collation.collation); @@ -1148,11 +1148,11 @@ String *Item_func_substr_index::val_str(String *str) #ifdef USE_MB if (use_mb(res->charset())) { - const char *ptr=res->ptr(); - const char *strend = ptr+res->length(); - const char *end=strend-delimeter_length+1; - const char *search=delimeter->ptr(); - const char *search_end=search+delimeter_length; + const char *ptr= res->ptr(); + const char *strend= ptr+res->length(); + const char *end= strend-delimiter_length+1; + const char *search= delimiter->ptr(); + const char *search_end= search+delimiter_length; int32 n=0,c=count,pass; register uint32 l; for (pass=(count>0);pass<2;++pass) @@ -1167,7 +1167,7 @@ String *Item_func_substr_index::val_str(String *str) if (*i++ != *j++) goto skip; if (pass==0) ++n; else if (!--c) break; - ptr+=delimeter_length; + ptr+= delimiter_length; continue; } skip: @@ -1189,7 +1189,7 @@ String *Item_func_substr_index::val_str(String *str) } else /* return right part */ { - ptr+=delimeter_length; + ptr+= delimiter_length; tmp_value.set(*res,(ulong) (ptr-res->ptr()), (ulong) (strend-ptr)); } } @@ -1200,9 +1200,9 @@ String *Item_func_substr_index::val_str(String *str) { if (count > 0) { // start counting from the beginning - for (offset=0 ;; offset+=delimeter_length) + for (offset=0; ; offset+= delimiter_length) { - if ((int) (offset=res->strstr(*delimeter,offset)) < 0) + if ((int) (offset= res->strstr(*delimiter, offset)) < 0) return res; // Didn't find, return org string if (!--count) { @@ -1223,7 +1223,7 @@ String *Item_func_substr_index::val_str(String *str) address space less than where the found substring is located in res */ - if ((int) (offset=res->strrstr(*delimeter,offset)) < 0) + if ((int) (offset= res->strrstr(*delimiter, offset)) < 0) return res; // Didn't find, return org string /* At this point, we've searched for the substring @@ -1231,13 +1231,19 @@ String *Item_func_substr_index::val_str(String *str) */ if (!++count) { - offset+=delimeter_length; + offset+= delimiter_length; tmp_value.set(*res,offset,res->length()- offset); break; } } } } + /* + We always mark tmp_value as const so that if val_str() is called again + on this object, we don't disrupt the contents of tmp_value when it was + derived from another String. + */ + tmp_value.mark_as_const(); return (&tmp_value); } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 50ec0b36ce8..6a95a9e5d1f 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -471,7 +471,9 @@ public: void fix_length_and_dec() { collation.set(default_charset()); - max_length=args[0]->max_length+(args[0]->max_length-args[0]->decimals)/3; + uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen; + max_length= ((char_length + (char_length-args[0]->decimals)/3) * + collation.collation->mbmaxlen); } const char *func_name() const { return "format"; } void print(String *); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index f1c99f74498..a4dac5bda87 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -125,6 +125,7 @@ public: friend class select_subselect; friend class Item_in_optimizer; friend bool Item_field::fix_fields(THD *, Item **); + friend int Item_field::fix_outer_field(THD *, Field **, Item **); friend bool Item_ref::fix_fields(THD *, Item **); friend void mark_select_range_as_dependent(THD*, st_select_lex*, st_select_lex*, diff --git a/sql/item_sum.cc b/sql/item_sum.cc index a3a25ec8d6f..804adcd022d 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2541,9 +2541,9 @@ bool Item_sum_count_distinct::setup(THD *thd) Field *f= *field; enum enum_field_types type= f->type(); tree_key_length+= f->pack_length(); - if (!f->binary() && (type == MYSQL_TYPE_STRING || - type == MYSQL_TYPE_VAR_STRING || - type == MYSQL_TYPE_VARCHAR)) + if ((type == MYSQL_TYPE_VARCHAR) || + !f->binary() && (type == MYSQL_TYPE_STRING || + type == MYSQL_TYPE_VAR_STRING)) { all_binary= FALSE; break; diff --git a/sql/item_sum.h b/sql/item_sum.h index a8242d76287..a38530a502c 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1116,6 +1116,13 @@ public: enum Sumfunctype sum_func () const {return GROUP_CONCAT_FUNC;} const char *func_name() const { return "group_concat"; } virtual Item_result result_type () const { return STRING_RESULT; } + enum_field_types field_type() const + { + if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB) + return FIELD_TYPE_BLOB; + else + return MYSQL_TYPE_VARCHAR; + } void clear(); bool add(); void reset_field() { DBUG_ASSERT(0); } // not used diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 17f25b49bcc..63a7f1f130b 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1605,9 +1605,15 @@ longlong Item_func_sec_to_time::val_int() void Item_func_date_format::fix_length_and_dec() { + /* + Must use this_item() in case it's a local SP variable + (for ->max_length and ->str_value) + */ + Item *arg1= args[1]->this_item(); + decimals=0; collation.set(&my_charset_bin); - if (args[1]->type() == STRING_ITEM) + if (arg1->type() == STRING_ITEM) { // Optimize the normal case fixed_length=1; @@ -1615,13 +1621,13 @@ void Item_func_date_format::fix_length_and_dec() The result is a binary string (no reason to use collation->mbmaxlen This is becasue make_date_time() only returns binary strings */ - max_length= format_length(((Item_string*) args[1])->const_string()); + max_length= format_length(&arg1->str_value); } else { fixed_length=0; /* The result is a binary string (no reason to use collation->mbmaxlen */ - max_length=min(args[1]->max_length,MAX_BLOB_WIDTH) * 10; + max_length=min(arg1->max_length, MAX_BLOB_WIDTH) * 10; set_if_smaller(max_length,MAX_BLOB_WIDTH); } maybe_null=1; // If wrong date @@ -1631,6 +1637,7 @@ void Item_func_date_format::fix_length_and_dec() bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const { Item_func_date_format *item_func; + if (item->type() != FUNC_ITEM) return 0; if (func_name() != ((Item_func*) item)->func_name()) diff --git a/sql/lex.h b/sql/lex.h index efcb9b84f81..1acfbaac211 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -514,6 +514,7 @@ static SYMBOL symbols[] = { { "UNSIGNED", SYM(UNSIGNED)}, { "UNTIL", SYM(UNTIL_SYM)}, { "UPDATE", SYM(UPDATE_SYM)}, + { "UPGRADE", SYM(UPGRADE_SYM)}, { "USAGE", SYM(USAGE)}, { "USE", SYM(USE_SYM)}, { "USER", SYM(USER)}, diff --git a/sql/log.cc b/sql/log.cc index 6c37cb04c61..85e8c4dae2f 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -132,6 +132,7 @@ static int binlog_commit(THD *thd, bool all) DBUG_RETURN(0); } Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE); + qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) DBUG_RETURN(binlog_end_trans(thd, trans_log, &qev)); } @@ -156,6 +157,7 @@ static int binlog_rollback(THD *thd, bool all) if (unlikely(thd->options & OPTION_STATUS_NO_TRANS_UPDATE)) { Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE); + qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) error= binlog_end_trans(thd, trans_log, &qev); } else @@ -1826,7 +1828,9 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) Imagine this is rollback due to net timeout, after all statements of the transaction succeeded. Then we want a zero-error code in BEGIN. In other words, if there was a really serious error code it's already - in the statement's events. + in the statement's events, there is no need to put it also in this + internally generated event, and as this event is generated late it + would lead to false alarms. This is safer than thd->clear_error() against kills at shutdown. */ qinfo.error_code= 0; diff --git a/sql/log_event.cc b/sql/log_event.cc index a07411971ce..266d6b064bd 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -240,6 +240,37 @@ char *str_to_hex(char *to, const char *from, uint len) } /* + Append a version of the 'from' string suitable for use in a query to + the 'to' string. To generate a correct escaping, the character set + information in 'csinfo' is used. + */ +#ifndef MYSQL_CLIENT +int +append_query_string(CHARSET_INFO *csinfo, + String const *from, String *to) +{ + char *beg, *ptr; + uint32 const orig_len= to->length(); + if (to->reserve(orig_len + from->length()*2+3)) + return 1; + + beg= to->c_ptr_quick() + to->length(); + ptr= beg; + if (csinfo->escape_with_backslash_is_dangerous) + ptr= str_to_hex(ptr, from->ptr(), from->length()); + else + { + *ptr++= '\''; + ptr+= escape_string_for_mysql(from->charset(), ptr, 0, + from->ptr(), from->length()); + *ptr++='\''; + } + to->length(orig_len + ptr - beg); + return 0; +} +#endif + +/* Prints a "session_var=value" string. Used by mysqlbinlog to print some SET commands just before it prints a query. */ @@ -1551,6 +1582,11 @@ void Query_log_event::print_query_header(FILE* file, } if (unlikely(bcmp(print_event_info->charset, charset, 6))) { + CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME)); + if (cs_info) + { + fprintf(file, "/*!\\C %s */;\n", cs_info->csname); /* for mysql client */ + } fprintf(file,"SET " "@@session.character_set_client=%d," "@@session.collation_connection=%d," diff --git a/sql/log_event.h b/sql/log_event.h index 7783a97f03f..0e1eb7cd13c 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -121,6 +121,7 @@ struct old_sql_ex ****************************************************************************/ struct sql_ex_info { + sql_ex_info() {} /* Remove gcc warning */ char* field_term; char* enclosed; char* line_term; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ca2cda0f7c6..9c9d8115402 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -432,6 +432,7 @@ typedef struct st_sql_list { byte *first; byte **next; + st_sql_list() {} /* Remove gcc warning */ inline void empty() { elements=0; @@ -528,8 +529,11 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables); bool insert_precheck(THD *thd, TABLE_LIST *tables); bool create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); +int append_query_string(CHARSET_INFO *csinfo, + String const *from, String *to); -bool get_default_definer(THD *thd, LEX_USER *definer); +void get_default_definer(THD *thd, LEX_USER *definer); +LEX_USER *create_default_definer(THD *thd); LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name); enum enum_mysql_completiontype { @@ -981,9 +985,9 @@ void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); void close_temporary_tables(THD *thd); -void close_tables_for_reopen(THD *thd, TABLE_LIST *tables); +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables); TABLE_LIST *find_table_in_list(TABLE_LIST *table, - uint offset_to_list, + st_table_list *TABLE_LIST::*link, const char *db_name, const char *table_name); TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list); @@ -1024,7 +1028,7 @@ inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table, const char *db_name, const char *table_name) { - return find_table_in_list(table, offsetof(TABLE_LIST, next_global), + return find_table_in_list(table, &TABLE_LIST::next_global, db_name, table_name); } @@ -1032,7 +1036,7 @@ inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table, const char *db_name, const char *table_name) { - return find_table_in_list(table, offsetof(TABLE_LIST, next_local), + return find_table_in_list(table, &TABLE_LIST::next_local, db_name, table_name); } @@ -1091,7 +1095,7 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg); /* mysqld.cc */ -extern void yyerror(const char*); +extern void MYSQLerror(const char*); /* item_func.cc */ extern bool check_reserved_words(LEX_STRING *name); @@ -1241,11 +1245,56 @@ extern const LEX_STRING view_type; /* optional things, have_* variables */ -extern SHOW_COMP_OPTION have_isam, have_innodb, have_berkeley_db; -extern SHOW_COMP_OPTION have_example_db, have_archive_db, have_csv_db; +#ifdef HAVE_INNOBASE_DB +extern handlerton innobase_hton; +#define have_innodb innobase_hton.state +#else +extern SHOW_COMP_OPTION have_innodb; +#endif +#ifdef HAVE_BERKELEY_DB +extern handlerton berkeley_hton; +#define have_berkeley_db berkeley_hton.state +#else +extern SHOW_COMP_OPTION have_berkeley_db; +#endif +#ifdef HAVE_EXAMPLE_DB +extern handlerton example_hton; +#define have_example_db example_hton.state +#else +extern SHOW_COMP_OPTION have_example_db; +#endif +#ifdef HAVE_ARCHIVE_DB +extern handlerton archive_hton; +#define have_archive_db archive_hton.state +#else +extern SHOW_COMP_OPTION have_archive_db; +#endif +#ifdef HAVE_CSV_DB +extern handlerton tina_hton; +#define have_csv_db tina_hton.state +#else +extern SHOW_COMP_OPTION have_csv_db; +#endif +#ifdef HAVE_FEDERATED_DB +extern handlerton federated_hton; +#define have_federated_db federated_hton.state +#else extern SHOW_COMP_OPTION have_federated_db; +#endif +#ifdef HAVE_BLACKHOLE_DB +extern handlerton blackhole_hton; +#define have_blackhole_db blackhole_hton.state +#else extern SHOW_COMP_OPTION have_blackhole_db; +#endif +#ifdef HAVE_NDBCLUSTER_DB +extern handlerton ndbcluster_hton; +#define have_ndbcluster ndbcluster_hton.state +#else extern SHOW_COMP_OPTION have_ndbcluster; +#endif + +extern SHOW_COMP_OPTION have_isam; extern SHOW_COMP_OPTION have_raid, have_openssl, have_symlink; extern SHOW_COMP_OPTION have_query_cache; extern SHOW_COMP_OPTION have_geometry, have_rtree_keys; @@ -1413,7 +1462,7 @@ 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); +extern int MYSQLparse(void *thd); /* frm_crypt.cc */ #ifdef HAVE_CRYPTED_FRM diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 7f835c0e8e0..e9ff220a6a1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -321,6 +321,7 @@ static const char *sql_mode_str= "OFF"; static char *mysqld_user, *mysqld_chroot, *log_error_file_ptr; static char *opt_init_slave, *language_ptr, *opt_init_connect; static char *default_character_set_name; +static char *character_set_filesystem_name; static char *my_bind_addr_str; static char *default_collation_name; static char mysql_data_home_buff[2]; @@ -469,14 +470,12 @@ MY_BITMAP temp_pool; CHARSET_INFO *system_charset_info, *files_charset_info ; CHARSET_INFO *national_charset_info, *table_alias_charset; +CHARSET_INFO *character_set_filesystem; -SHOW_COMP_OPTION have_berkeley_db, have_innodb, have_isam, have_ndbcluster, - have_example_db, have_archive_db, have_csv_db; -SHOW_COMP_OPTION have_federated_db; +SHOW_COMP_OPTION have_isam; SHOW_COMP_OPTION have_raid, have_openssl, have_symlink, have_query_cache; SHOW_COMP_OPTION have_geometry, have_rtree_keys; SHOW_COMP_OPTION have_crypt, have_compress; -SHOW_COMP_OPTION have_blackhole_db; /* Thread specific variables */ @@ -1522,7 +1521,7 @@ static void network_init(void) #endif /*!EMBEDDED_LIBRARY*/ -void yyerror(const char *s) +void MYSQLerror(const char *s) { THD *thd=current_thd; char *yytext= (char*) thd->lex->tok_start; @@ -2385,9 +2384,7 @@ static int my_message_sql(uint error, const char *str, myf MyFlags) { NET *net= &thd->net; net->report_error= 1; -#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/ query_cache_abort(net); -#endif if (!net->last_error[0]) // Return only first message { strmake(net->last_error, str, sizeof(net->last_error)-1); @@ -2677,6 +2674,12 @@ static int init_common_variables(const char *conf_file_name, int argc, global_system_variables.character_set_client= default_charset_info; global_system_variables.collation_connection= default_charset_info; + if (!(character_set_filesystem= + get_charset_by_csname(character_set_filesystem_name, + MY_CS_PRIMARY, MYF(MY_WME)))) + return 1; + global_system_variables.character_set_filesystem= character_set_filesystem; + sys_init_connect.value_length= 0; if ((sys_init_connect.value= opt_init_connect)) sys_init_connect.value_length= strlen(opt_init_connect); @@ -4563,6 +4566,7 @@ enum options_mysqld OPT_GROUP_CONCAT_MAX_LEN, OPT_DEFAULT_COLLATION, OPT_CHARACTER_SET_CLIENT_HANDSHAKE, + OPT_CHARACTER_SET_FILESYSTEM, OPT_INIT_CONNECT, OPT_INIT_SLAVE, OPT_SECURE_AUTH, @@ -4571,6 +4575,7 @@ enum options_mysqld OPT_DATETIME_FORMAT, OPT_LOG_QUERIES_NOT_USING_INDEXES, OPT_DEFAULT_TIME_ZONE, + OPT_SYSDATE_IS_NOW, OPT_OPTIMIZER_SEARCH_DEPTH, OPT_OPTIMIZER_PRUNE_LEVEL, OPT_UPDATABLE_VIEWS_WITH_LIMIT, @@ -4599,8 +4604,6 @@ struct my_option my_long_options[] = (gptr*) &abort_slave_event_count, (gptr*) &abort_slave_event_count, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif /* HAVE_REPLICATION */ - {"ansi", 'a', "Use ANSI SQL syntax instead of MySQL syntax. This mode will also set transaction isolation level 'serializable'.", 0, 0, 0, - GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"allow-suspicious-udfs", OPT_ALLOW_SUSPICIOUS_UDFS, "Allows use of UDFs consisting of only one symbol xxx() " "without corresponding xxx_init() or xxx_deinit(). That also means " @@ -4608,6 +4611,8 @@ struct my_option my_long_options[] = "from libc.so", (gptr*) &opt_allow_suspicious_udfs, (gptr*) &opt_allow_suspicious_udfs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"ansi", 'a', "Use ANSI SQL syntax instead of MySQL syntax. This mode will also set transaction isolation level 'serializable'.", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"auto-increment-increment", OPT_AUTO_INCREMENT, "Auto-increment columns are incremented by this", (gptr*) &global_system_variables.auto_increment_increment, @@ -4671,6 +4676,11 @@ Disable with --skip-bdb (will save memory).", (gptr*) &opt_character_set_client_handshake, (gptr*) &opt_character_set_client_handshake, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"character-set-filesystem", OPT_CHARACTER_SET_FILESYSTEM, + "Set the filesystem character set.", + (gptr*) &character_set_filesystem_name, + (gptr*) &character_set_filesystem_name, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, {"character-set-server", 'C', "Set the default character set.", (gptr*) &default_character_set_name, (gptr*) &default_character_set_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, @@ -4856,16 +4866,16 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite, "Enable SHOW INNODB STATUS output in the innodb_status.<pid> file", (gptr*) &innobase_create_status_file, (gptr*) &innobase_create_status_file, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"innodb_table_locks", OPT_INNODB_TABLE_LOCKS, - "Enable InnoDB locking in LOCK TABLES", - (gptr*) &global_system_variables.innodb_table_locks, - (gptr*) &global_system_variables.innodb_table_locks, - 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, {"innodb_support_xa", OPT_INNODB_SUPPORT_XA, "Enable InnoDB support for the XA two-phase commit", (gptr*) &global_system_variables.innodb_support_xa, (gptr*) &global_system_variables.innodb_support_xa, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + {"innodb_table_locks", OPT_INNODB_TABLE_LOCKS, + "Enable InnoDB locking in LOCK TABLES", + (gptr*) &global_system_variables.innodb_table_locks, + (gptr*) &global_system_variables.innodb_table_locks, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, #endif /* End HAVE_INNOBASE_DB */ {"isam", OPT_ISAM, "Obsolete. ISAM storage engine is no longer supported.", (gptr*) &opt_isam, (gptr*) &opt_isam, 0, GET_BOOL, NO_ARG, 0, 0, 0, @@ -4891,6 +4901,16 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite, "File that holds the names for last binary log files.", (gptr*) &opt_binlog_index_name, (gptr*) &opt_binlog_index_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + /* + This option starts with "log-bin" to emphasize that it is specific of + binary logging. + */ + {"log-bin-trust-function-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS, + "If equal to 0 (the default), then when --log-bin is used, creation of " + "a stored function is allowed only to users having the SUPER privilege and" + " only if this function may not break binary logging.", + (gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifndef TO_BE_REMOVED_IN_5_1_OR_6_0 /* In 5.0.6 we introduced the below option, then in 5.0.16 we renamed it to @@ -4903,16 +4923,6 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite, (gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif - /* - This option starts with "log-bin" to emphasize that it is specific of - binary logging. - */ - {"log-bin-trust-function-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS, - "If equal to 0 (the default), then when --log-bin is used, creation of " - "a function is allowed only to users having the SUPER privilege and only " - "if this function may not break binary logging.", - (gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0, - GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"log-error", OPT_ERROR_LOG_FILE, "Error log file.", (gptr*) &log_error_file_ptr, (gptr*) &log_error_file_ptr, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, @@ -5155,12 +5165,6 @@ thread is in the relay logs.", {"replicate-rewrite-db", OPT_REPLICATE_REWRITE_DB, "Updates to a database with a different name than the original. Example: replicate-rewrite-db=master_db_name->slave_db_name.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"replicate-wild-do-table", OPT_REPLICATE_WILD_DO_TABLE, - "Tells the slave thread to restrict replication to the tables that match the specified wildcard pattern. To specify more than one table, use the directive multiple times, once for each table. This will work for cross-database updates. Example: replicate-wild-do-table=foo%.bar% will replicate only updates to tables in all databases that start with foo and whose table names start with bar.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"replicate-wild-ignore-table", OPT_REPLICATE_WILD_IGNORE_TABLE, - "Tells the slave thread to not replicate to the tables that match the given wildcard pattern. To specify more than one table to ignore, use the directive multiple times, once for each table. This will work for cross-database updates. Example: replicate-wild-ignore-table=foo%.bar% will not do updates to tables in databases that start with foo and whose table names start with bar.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #ifdef HAVE_REPLICATION {"replicate-same-server-id", OPT_REPLICATE_SAME_SERVER_ID, "In replication, if set to 1, do not skip events having our server id. \ @@ -5170,6 +5174,12 @@ Can't be set to 1 if --log-slave-updates is used.", (gptr*) &replicate_same_server_id, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif + {"replicate-wild-do-table", OPT_REPLICATE_WILD_DO_TABLE, + "Tells the slave thread to restrict replication to the tables that match the specified wildcard pattern. To specify more than one table, use the directive multiple times, once for each table. This will work for cross-database updates. Example: replicate-wild-do-table=foo%.bar% will replicate only updates to tables in all databases that start with foo and whose table names start with bar.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-wild-ignore-table", OPT_REPLICATE_WILD_IGNORE_TABLE, + "Tells the slave thread to not replicate to the tables that match the given wildcard pattern. To specify more than one table to ignore, use the directive multiple times, once for each table. This will work for cross-database updates. Example: replicate-wild-ignore-table=foo%.bar% will not do updates to tables in databases that start with foo and whose table names start with bar.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, // In replication, we may need to tell the other servers how to connect {"report-host", OPT_REPORT_HOST, "Hostname or IP of the slave to be reported to to the master during slave registration. Will appear in the output of SHOW SLAVE HOSTS. Leave unset if you do not want the slave to register itself with the master. Note that it is not sufficient for the master to simply read the IP of the slave off the socket once the slave connects. Due to NAT and other routing issues, that IP may not be valid for connecting to the slave from the master or other hosts.", @@ -5302,8 +5312,12 @@ log and this option does nothing anymore.", {"symbolic-links", 's', "Enable symbolic link support.", (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, IF_PURIFY(0,1), 0, 0, 0, 0, 0}, + {"sysdate-is-now", OPT_SYSDATE_IS_NOW, + "Non-default option to alias SYSDATE() to NOW() to make it safe-replicable. Since 5.0, SYSDATE() returns a `dynamic' value different for different invocations, even within the same statement.", + (gptr*) &global_system_variables.sysdate_is_now, + 0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0}, {"tc-heuristic-recover", OPT_TC_HEURISTIC_RECOVER, - "Decision to use in heuristic recover process. Possible values are COMMIT or ROLLBACK", + "Decision to use in heuristic recover process. Possible values are COMMIT or ROLLBACK.", (gptr*) &opt_tc_heuristic_recover, (gptr*) &opt_tc_heuristic_recover, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"temp-pool", OPT_TEMP_POOL, @@ -5403,6 +5417,11 @@ log and this option does nothing anymore.", "What size queue (in rows) should be allocated for handling INSERT DELAYED. If the queue becomes full, any client that does INSERT DELAYED will wait until there is room in the queue again.", (gptr*) &delayed_queue_size, (gptr*) &delayed_queue_size, 0, GET_ULONG, REQUIRED_ARG, DELAYED_QUEUE_SIZE, 1, ~0L, 0, 1, 0}, + {"div_precision_increment", OPT_DIV_PRECINCREMENT, + "Precision of the result of '/' operator will be increased on that value.", + (gptr*) &global_system_variables.div_precincrement, + (gptr*) &max_system_variables.div_precincrement, 0, GET_ULONG, + REQUIRED_ARG, 4, 0, DECIMAL_MAX_SCALE, 0, 0, 0}, {"expire_logs_days", OPT_EXPIRE_LOGS_DAYS, "If non-zero, binary logs will be purged after expire_logs_days " "days; possible purges happen at startup and at binary log rotation.", @@ -5458,6 +5477,10 @@ log and this option does nothing anymore.", (gptr*) &innobase_buffer_pool_size, (gptr*) &innobase_buffer_pool_size, 0, GET_LL, REQUIRED_ARG, 8*1024*1024L, 1024*1024L, LONGLONG_MAX, 0, 1024*1024L, 0}, + {"innodb_commit_concurrency", OPT_INNODB_THREAD_CONCURRENCY, + "Helps in performance tuning in heavily concurrent environments.", + (gptr*) &srv_commit_concurrency, (gptr*) &srv_commit_concurrency, + 0, GET_LONG, REQUIRED_ARG, 0, 0, 1000, 0, 1, 0}, {"innodb_concurrency_tickets", OPT_INNODB_CONCURRENCY_TICKETS, "Number of times a thread is allowed to enter InnoDB within the same \ SQL query after it has once got the ticket", @@ -5509,10 +5532,6 @@ log and this option does nothing anymore.", " will disable the thread throttling.", (gptr*) &srv_thread_concurrency, (gptr*) &srv_thread_concurrency, 0, GET_LONG, REQUIRED_ARG, 0, 0, 1000, 0, 1, 0}, - {"innodb_commit_concurrency", OPT_INNODB_THREAD_CONCURRENCY, - "Helps in performance tuning in heavily concurrent environments.", - (gptr*) &srv_commit_concurrency, (gptr*) &srv_commit_concurrency, - 0, GET_LONG, REQUIRED_ARG, 0, 0, 1000, 0, 1, 0}, {"innodb_thread_sleep_delay", OPT_INNODB_THREAD_SLEEP_DELAY, "Time of innodb thread sleeping before joining InnoDB queue (usec). Value 0" " disable a sleep", @@ -5633,6 +5652,11 @@ The minimum value for this variable is 4096.", (gptr*) &global_system_variables.max_sort_length, (gptr*) &max_system_variables.max_sort_length, 0, GET_ULONG, REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, + {"max_sp_recursion_depth", OPT_MAX_SP_RECURSION_DEPTH, + "Maximum stored procedure recursion depth. (discussed with docs).", + (gptr*) &global_system_variables.max_sp_recursion_depth, + (gptr*) &max_system_variables.max_sp_recursion_depth, 0, GET_ULONG, + OPT_ARG, 0, 0, 255, 0, 1, 0 }, {"max_tmp_tables", OPT_MAX_TMP_TABLES, "Maximum number of temporary tables a client can keep open at a time.", (gptr*) &global_system_variables.max_tmp_tables, @@ -5788,21 +5812,11 @@ The minimum value for this variable is 4096.", (gptr*) &max_system_variables.read_rnd_buff_size, 0, GET_ULONG, REQUIRED_ARG, 256*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, - {"div_precision_increment", OPT_DIV_PRECINCREMENT, - "Precision of the result of '/' operator will be increased on that value.", - (gptr*) &global_system_variables.div_precincrement, - (gptr*) &max_system_variables.div_precincrement, 0, GET_ULONG, - REQUIRED_ARG, 4, 0, DECIMAL_MAX_SCALE, 0, 0, 0}, {"record_buffer", OPT_RECORD_BUFFER, "Alias for read_buffer_size", (gptr*) &global_system_variables.read_buff_size, (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, - {"max_sp_recursion_depth", OPT_MAX_SP_RECURSION_DEPTH, - "Maximum stored procedure recursion depth. (discussed with docs).", - (gptr*) &global_system_variables.max_sp_recursion_depth, - (gptr*) &max_system_variables.max_sp_recursion_depth, 0, GET_ULONG, - OPT_ARG, 0, 0, 255, 0, 1, 0 }, #ifdef HAVE_REPLICATION {"relay_log_purge", OPT_RELAY_LOG_PURGE, "0 = do not purge relay logs. 1 = purge them as soon as they are no more needed.", @@ -6260,6 +6274,7 @@ static void mysql_init_variables(void) files_charset_info= &my_charset_utf8_general_ci; national_charset_info= &my_charset_utf8_general_ci; table_alias_charset= &my_charset_bin; + character_set_filesystem= &my_charset_bin; opt_date_time_formats[0]= opt_date_time_formats[1]= opt_date_time_formats[2]= 0; @@ -6320,6 +6335,7 @@ static void mysql_init_variables(void) default_character_set_name= (char*) MYSQL_DEFAULT_CHARSET_NAME; default_collation_name= (char*) MYSQL_DEFAULT_COLLATION_NAME; sys_charset_system.value= (char*) system_charset_info->csname; + character_set_filesystem_name= (char*) "binary"; /* Set default values for some option variables */ @@ -7378,6 +7394,30 @@ static void create_pid_file() /***************************************************************************** + Instantiate have_xyx for missing storage engines +*****************************************************************************/ +#undef have_berkeley_db +#undef have_innodb +#undef have_ndbcluster +#undef have_example_db +#undef have_archive_db +#undef have_csv_db +#undef have_federated_db +#undef have_partition_db +#undef have_blackhole_db + +SHOW_COMP_OPTION have_berkeley_db= SHOW_OPTION_NO; +SHOW_COMP_OPTION have_innodb= SHOW_OPTION_NO; +SHOW_COMP_OPTION have_ndbcluster= SHOW_OPTION_NO; +SHOW_COMP_OPTION have_example_db= SHOW_OPTION_NO; +SHOW_COMP_OPTION have_archive_db= SHOW_OPTION_NO; +SHOW_COMP_OPTION have_csv_db= SHOW_OPTION_NO; +SHOW_COMP_OPTION have_federated_db= SHOW_OPTION_NO; +SHOW_COMP_OPTION have_partition_db= SHOW_OPTION_NO; +SHOW_COMP_OPTION have_blackhole_db= SHOW_OPTION_NO; + + +/***************************************************************************** Instantiate templates *****************************************************************************/ diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 7326c0395e3..c80bb8bad9a 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -194,30 +194,130 @@ my_bool net_realloc(NET *net, ulong length) DBUG_RETURN(0); } - /* Remove unwanted characters from connection */ + +/* + Check if there is any data to be read from the socket + + SYNOPSIS + net_data_is_ready() + sd socket descriptor + + DESCRIPTION + Check if there is any data to be read from the socket. + + RETURN VALUES + 0 No data to read + 1 Data or EOF to read + -1 Don't know if data is ready or not +*/ + +static int net_data_is_ready(my_socket sd) +{ +#ifdef HAVE_POLL + struct pollfd ufds; + int res; + + ufds.fd= sd; + ufds.events= POLLIN | POLLPRI; + if (!(res= poll(&ufds, 1, 0))) + return 0; + if (res < 0 || !(ufds.revents & (POLLIN | POLLPRI))) + return 0; + return 1; +#else + fd_set sfds; + struct timeval tv; + int res; + +#ifndef __WIN__ + /* Windows uses an _array_ of 64 fd's as default, so it's safe */ + if (sd >= FD_SETSIZE) + return -1; +#define NET_DATA_IS_READY_CAN_RETURN_MINUS_ONE +#endif + + FD_ZERO(&sfds); + FD_SET(sd, &sfds); + + tv.tv_sec= tv.tv_usec= 0; + + if ((res= select(sd+1, &sfds, NULL, NULL, &tv)) < 0) + return 0; + else + return test(res ? FD_ISSET(sd, &sfds) : 0); +#endif +} + + +/* + Remove unwanted characters from connection + and check if disconnected + + SYNOPSIS + net_clear() + net NET handler + + DESCRIPTION + Read from socket until there is nothing more to read. Discard + what is read. + + If there is anything when to read 'net_clear' is called this + normally indicates an error in the protocol. + + When connection is properly closed (for TCP it means with + a FIN packet), then select() considers a socket "ready to read", + in the sense that there's EOF to read, but read() returns 0. + +*/ void net_clear(NET *net) { + int count, ready; DBUG_ENTER("net_clear"); -#if !defined(EXTRA_DEBUG) && !defined(EMBEDDED_LIBRARY) +#if !defined(EMBEDDED_LIBRARY) + while((ready= net_data_is_ready(net->vio->sd)) > 0) + { + /* The socket is ready */ + if ((count= vio_read(net->vio, (char*) (net->buff), + (uint32) net->max_packet)) > 0) + { + DBUG_PRINT("info",("skipped %d bytes from file: %s", + count, vio_description(net->vio))); +#ifdef EXTRA_DEBUG + fprintf(stderr,"skipped %d bytes from file: %s\n", + count, vio_description(net->vio)); +#endif + } + else + { + DBUG_PRINT("info",("socket ready but only EOF to read - disconnected")); + net->error= 2; + break; + } + } +#ifdef NET_DATA_IS_READY_CAN_RETURN_MINUS_ONE + /* 'net_data_is_ready' returned "don't know" */ + if (ready == -1) { - int count; /* One may get 'unused' warn */ + /* Read unblocking to clear net */ my_bool old_mode; if (!vio_blocking(net->vio, FALSE, &old_mode)) { - while ((count = vio_read(net->vio, (char*) (net->buff), - (uint32) net->max_packet)) > 0) + while ((count= vio_read(net->vio, (char*) (net->buff), + (uint32) net->max_packet)) > 0) DBUG_PRINT("info",("skipped %d bytes from file: %s", count, vio_description(net->vio))); vio_blocking(net->vio, TRUE, &old_mode); } } -#endif /* EXTRA_DEBUG */ +#endif +#endif net->pkt_nr=net->compress_pkt_nr=0; /* Ready for new command */ net->write_pos=net->buff; DBUG_VOID_RETURN; } + /* Flush write_buffer if not empty. */ my_bool net_flush(NET *net) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 1b712700b18..634c9db18a8 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1495,6 +1495,8 @@ public: { return (void*) alloc_root(mem_root, (uint) size); } static void operator delete(void *ptr,size_t size) { TRASH(ptr, size); } static void operator delete(void *ptr, MEM_ROOT *mem_root) { /* Never called */ } + virtual ~TABLE_READ_PLAN() {} /* Remove gcc warning */ + }; class TRP_ROR_INTERSECT; @@ -1518,6 +1520,7 @@ public: TRP_RANGE(SEL_ARG *key_arg, uint idx_arg) : key(key_arg), key_idx(idx_arg) {} + virtual ~TRP_RANGE() {} /* Remove gcc warning */ QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc) @@ -1539,6 +1542,8 @@ public: class TRP_ROR_INTERSECT : public TABLE_READ_PLAN { public: + TRP_ROR_INTERSECT() {} /* Remove gcc warning */ + virtual ~TRP_ROR_INTERSECT() {} /* Remove gcc warning */ QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc); @@ -1560,6 +1565,8 @@ public: class TRP_ROR_UNION : public TABLE_READ_PLAN { public: + TRP_ROR_UNION() {} /* Remove gcc warning */ + virtual ~TRP_ROR_UNION() {} /* Remove gcc warning */ QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc); TABLE_READ_PLAN **first_ror; /* array of ptrs to plans for merged scans */ @@ -1576,6 +1583,8 @@ public: class TRP_INDEX_MERGE : public TABLE_READ_PLAN { public: + TRP_INDEX_MERGE() {} /* Remove gcc warning */ + virtual ~TRP_INDEX_MERGE() {} /* Remove gcc warning */ QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc); TRP_RANGE **range_scans; /* array of ptrs to plans of merged scans */ @@ -1625,6 +1634,7 @@ public: if (key_infix_len) memcpy(this->key_infix, key_infix_arg, key_infix_len); } + virtual ~TRP_GROUP_MIN_MAX() {} /* Remove gcc warning */ QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc); @@ -3594,9 +3604,18 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) /* Here when simple cond */ if (cond->const_item()) { - if (cond->val_int()) - DBUG_RETURN(new SEL_TREE(SEL_TREE::ALWAYS)); - DBUG_RETURN(new SEL_TREE(SEL_TREE::IMPOSSIBLE)); + /* + During the cond->val_int() evaluation we can come across a subselect + item which may allocate memory on the thd->mem_root and assumes + all the memory allocated has the same life span as the subselect + item itself. So we have to restore the thread's mem_root here. + */ + MEM_ROOT *tmp_root= param->mem_root; + param->thd->mem_root= param->old_root; + tree= cond->val_int() ? new(tmp_root) SEL_TREE(SEL_TREE::ALWAYS) : + new(tmp_root) SEL_TREE(SEL_TREE::IMPOSSIBLE); + param->thd->mem_root= tmp_root; + DBUG_RETURN(tree); } table_map ref_tables= 0; diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 041b770ac0b..415465b0cd1 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -533,7 +533,7 @@ parse_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str) read escaped string from ptr to eol in already allocated str SYNOPSIS - parse_escaped_string() + read_escaped_string() ptr - pointer on string beginning eol - pointer on character after end of string str - target string @@ -604,7 +604,7 @@ read_escaped_string(char *ptr, char *eol, LEX_STRING *str) # - pointer on symbol after string */ -static char * +char * parse_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str) { char *eol= strchr(ptr, '\n'); @@ -622,7 +622,7 @@ parse_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str) parse '' delimited escaped string SYNOPSIS - parse_escaped_string() + parse_quoted_escaped_string() ptr - pointer on string beginning end - pointer on symbol after parsed string end (still owned by buffer and can be accessed @@ -746,7 +746,6 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root, char *eol; LEX_STRING *str; List<LEX_STRING> *list; - ulonglong *num; DBUG_ENTER("File_parser::parse"); while (ptr < end && found < required) diff --git a/sql/parse_file.h b/sql/parse_file.h index afa88da2ead..33871588e11 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -49,6 +49,8 @@ struct File_option class Unknown_key_hook { public: + Unknown_key_hook() {} /* Remove gcc warning */ + virtual ~Unknown_key_hook() {} /* Remove gcc warning */ virtual bool process_unknown_string(char *&unknown_key, gptr base, MEM_ROOT *mem_root, char *end)= 0; }; @@ -59,6 +61,7 @@ public: class File_parser_dummy_hook: public Unknown_key_hook { public: + File_parser_dummy_hook() {} /* Remove gcc warning */ virtual bool process_unknown_string(char *&unknown_key, gptr base, MEM_ROOT *mem_root, char *end); }; @@ -69,6 +72,9 @@ bool get_file_options_ulllist(char *&ptr, char *end, char *line, gptr base, File_option *parameter, MEM_ROOT *mem_root); +char * +parse_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str); + class File_parser; File_parser *sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root, bool bad_format_errors); diff --git a/sql/protocol.cc b/sql/protocol.cc index 15f7049ec2f..650bd8fc58f 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -29,6 +29,7 @@ static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; static void write_eof_packet(THD *thd, NET *net); +void net_send_error_packet(THD *thd, uint sql_errno, const char *err); #ifndef EMBEDDED_LIBRARY bool Protocol::net_store_data(const char *from, uint length) @@ -56,10 +57,6 @@ bool Protocol_prep::net_store_data(const char *from, uint length) void net_send_error(THD *thd, uint sql_errno, const char *err) { -#ifndef EMBEDDED_LIBRARY - uint length; - char buff[MYSQL_ERRMSG_SIZE+2], *pos; -#endif NET *net= &thd->net; bool generate_warning= thd->killed != THD::KILL_CONNECTION; DBUG_ENTER("net_send_error"); @@ -106,42 +103,8 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, sql_errno, err); } -#ifdef EMBEDDED_LIBRARY - net->last_errno= sql_errno; - strmake(net->last_error, err, sizeof(net->last_error)-1); - strmov(net->sqlstate, mysql_errno_to_sqlstate(sql_errno)); -#else - - if (net->vio == 0) - { - if (thd->bootstrap) - { - /* In bootstrap it's ok to print on stderr */ - fprintf(stderr,"ERROR: %d %s\n",sql_errno,err); - } - DBUG_VOID_RETURN; - } + net_send_error_packet(thd, sql_errno, err); - if (net->return_errno) - { // new client code; Add errno before message - int2store(buff,sql_errno); - pos= buff+2; - if (thd->client_capabilities & CLIENT_PROTOCOL_41) - { - /* The first # is to make the protocol backward compatible */ - buff[2]= '#'; - pos= strmov(buff+3, mysql_errno_to_sqlstate(sql_errno)); - } - length= (uint) (strmake(pos, err, MYSQL_ERRMSG_SIZE-1) - buff); - err=buff; - } - else - { - length=(uint) strlen(err); - set_if_smaller(length,MYSQL_ERRMSG_SIZE-1); - } - VOID(net_write_command(net,(uchar) 255, "", 0, (char*) err,length)); -#endif /* EMBEDDED_LIBRARY*/ thd->is_fatal_error=0; // Error message is given thd->net.report_error= 0; @@ -430,6 +393,47 @@ bool send_old_password_request(THD *thd) return my_net_write(net, eof_buff, 1) || net_flush(net); } + +void net_send_error_packet(THD *thd, uint sql_errno, const char *err) +{ + NET *net= &thd->net; + uint length; + char buff[MYSQL_ERRMSG_SIZE+2], *pos; + + DBUG_ENTER("send_error_packet"); + + if (net->vio == 0) + { + if (thd->bootstrap) + { + /* In bootstrap it's ok to print on stderr */ + fprintf(stderr,"ERROR: %d %s\n",sql_errno,err); + } + DBUG_VOID_RETURN; + } + + if (net->return_errno) + { // new client code; Add errno before message + int2store(buff,sql_errno); + pos= buff+2; + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + { + /* The first # is to make the protocol backward compatible */ + buff[2]= '#'; + pos= strmov(buff+3, mysql_errno_to_sqlstate(sql_errno)); + } + length= (uint) (strmake(pos, err, MYSQL_ERRMSG_SIZE-1) - buff); + err=buff; + } + else + { + length=(uint) strlen(err); + set_if_smaller(length,MYSQL_ERRMSG_SIZE-1); + } + VOID(net_write_command(net,(uchar) 255, "", 0, (char*) err,length)); + DBUG_VOID_RETURN; +} + #endif /* EMBEDDED_LIBRARY */ /* diff --git a/sql/protocol.h b/sql/protocol.h index 8d9da5774b2..85c22724b74 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -91,6 +91,12 @@ public: virtual bool store_date(TIME *time)=0; virtual bool store_time(TIME *time)=0; virtual bool store(Field *field)=0; +#ifdef EMBEDDED_LIBRARY + int begin_dataset(); + virtual void remove_last_row() {} +#else + void remove_last_row() {} +#endif }; @@ -117,6 +123,9 @@ public: virtual bool store(float nr, uint32 decimals, String *buffer); virtual bool store(double from, uint32 decimals, String *buffer); virtual bool store(Field *field); +#ifdef EMBEDDED_LIBRARY + void remove_last_row(); +#endif }; diff --git a/sql/set_var.cc b/sql/set_var.cc index d945e0842e4..7be79ab59f0 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -148,6 +148,7 @@ sys_var_character_set_database sys_character_set_database("character_set_databas 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_character_set_filesystem sys_character_set_filesystem("character_set_filesystem"); sys_var_thd_ulong sys_completion_type("completion_type", &SV::completion_type, check_completion_type, @@ -580,6 +581,7 @@ sys_var *sys_variables[]= &sys_character_set_client, &sys_character_set_connection, &sys_character_set_results, + &sys_character_set_filesystem, &sys_charset_system, &sys_collation_connection, &sys_collation_database, @@ -771,6 +773,7 @@ struct show_var_st init_vars[]= { {sys_character_set_client.name,(char*) &sys_character_set_client, SHOW_SYS}, {sys_character_set_connection.name,(char*) &sys_character_set_connection,SHOW_SYS}, {sys_character_set_database.name, (char*) &sys_character_set_database,SHOW_SYS}, + {sys_character_set_filesystem.name,(char*) &sys_character_set_filesystem, SHOW_SYS}, {sys_character_set_results.name,(char*) &sys_character_set_results, SHOW_SYS}, {sys_character_set_server.name, (char*) &sys_character_set_server,SHOW_SYS}, {sys_charset_system.name, (char*) &sys_charset_system, SHOW_SYS}, @@ -835,6 +838,7 @@ struct show_var_st init_vars[]= { {sys_innodb_fast_shutdown.name,(char*) &sys_innodb_fast_shutdown, SHOW_SYS}, {"innodb_file_io_threads", (char*) &innobase_file_io_threads, SHOW_LONG }, {"innodb_file_per_table", (char*) &innobase_file_per_table, SHOW_MY_BOOL}, + {sys_innodb_flush_log_at_trx_commit.name, (char*) &sys_innodb_flush_log_at_trx_commit, SHOW_SYS}, {"innodb_flush_method", (char*) &innobase_unix_file_flush_method, SHOW_CHAR_PTR}, {"innodb_force_recovery", (char*) &innobase_force_recovery, SHOW_LONG }, {"innodb_lock_wait_timeout", (char*) &innobase_lock_wait_timeout, SHOW_LONG }, @@ -854,7 +858,6 @@ struct show_var_st init_vars[]= { {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS}, {sys_innodb_thread_concurrency.name, (char*) &sys_innodb_thread_concurrency, SHOW_SYS}, {sys_innodb_thread_sleep_delay.name, (char*) &sys_innodb_thread_sleep_delay, SHOW_SYS}, - {sys_innodb_flush_log_at_trx_commit.name, (char*) &sys_innodb_flush_log_at_trx_commit, SHOW_SYS}, #endif {sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS}, {sys_join_buffer_size.name, (char*) &sys_join_buffer_size, SHOW_SYS}, @@ -1656,7 +1659,12 @@ bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names) else { ulonglong tmp= var->value->val_int(); - if (tmp >= enum_names->count) + /* + For when the enum is made to contain 64 elements, as 1ULL<<64 is + undefined, we guard with a "count<64" test. + */ + if (unlikely((tmp >= ((ULL(1)) << enum_names->count)) && + (enum_names->count < 64))) { llstr(tmp, buff); goto err; @@ -2077,6 +2085,32 @@ void sys_var_character_set_client::set_default(THD *thd, enum_var_type type) CHARSET_INFO ** +sys_var_character_set_filesystem::ci_ptr(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + return &global_system_variables.character_set_filesystem; + else + return &thd->variables.character_set_filesystem; +} + + +extern CHARSET_INFO *character_set_filesystem; + +void +sys_var_character_set_filesystem::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + global_system_variables.character_set_filesystem= character_set_filesystem; + else + { + thd->variables.character_set_filesystem= (global_system_variables. + character_set_filesystem); + thd->update_charset(); + } +} + + +CHARSET_INFO ** sys_var_character_set_results::ci_ptr(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) diff --git a/sql/set_var.h b/sql/set_var.h index 18c3353e8ff..046281ec7c5 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -540,6 +540,15 @@ public: virtual CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type)= 0; }; +class sys_var_character_set_filesystem :public sys_var_character_set +{ +public: + sys_var_character_set_filesystem(const char *name_arg) : + sys_var_character_set(name_arg) {} + void set_default(THD *thd, enum_var_type type); + CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type); +}; + class sys_var_character_set_client :public sys_var_character_set { public: diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 1315de6528a..37487c245a9 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -2978,23 +2978,23 @@ ER_UDF_EXISTS swe "Funktionen '%-.64s' finns redan" ukr "æÕÎËÃ¦Ñ '%-.64s' ×ÖÅ ¦ÓÎÕ¤" ER_CANT_OPEN_LIBRARY - cze "Nemohu otev-Bøít sdílenou knihovnu '%-.64s' (errno: %d %s)" - dan "Kan ikke åbne delt bibliotek '%-.64s' (errno: %d %s)" - nla "Kan shared library '%-.64s' niet openen (Errcode: %d %s)" - eng "Can't open shared library '%-.64s' (errno: %d %-.64s)" - jps "shared library '%-.64s' ‚ðŠJ‚Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d %s)", - est "Ei suuda avada jagatud teeki '%-.64s' (veakood: %d %-.64s)" - fre "Impossible d'ouvrir la bibliothèque partagée '%-.64s' (errno: %d %s)" - ger "Kann Shared Library '%-.64s' nicht öffnen (Fehler: %d %-.64s)" - greek "Äåí åßíáé äõíáôÞ ç áíÜãíùóç ôçò shared library '%-.64s' (êùäéêüò ëÜèïõò: %d %s)" - hun "A(z) '%-.64s' megosztott konyvtar nem hasznalhato (hibakod: %d %s)" - ita "Impossibile aprire la libreria condivisa '%-.64s' (errno: %d %s)" - jpn "shared library '%-.64s' ¤ò³«¤¯»ö¤¬¤Ç¤¤Þ¤»¤ó (errno: %d %s)" - kor "'%-.64s' °øÀ¯ ¶óÀ̹ö·¯¸®¸¦ ¿¼ö ¾ø½À´Ï´Ù.(¿¡·¯¹øÈ£: %d %s)" - nor "Can't open shared library '%-.64s' (errno: %d %s)" - norwegian-ny "Can't open shared library '%-.64s' (errno: %d %s)" - pol "Can't open shared library '%-.64s' (errno: %d %s)" - por "Não pode abrir biblioteca compartilhada '%-.64s' (erro no. '%d' - '%-.64s')" + cze "Nemohu otev-Bøít sdílenou knihovnu '%-.64s' (errno: %d %-.128s)" + dan "Kan ikke åbne delt bibliotek '%-.64s' (errno: %d %-.128s)" + nla "Kan shared library '%-.64s' niet openen (Errcode: %d %-.128s)" + eng "Can't open shared library '%-.64s' (errno: %d %-.128s)" + jps "shared library '%-.64s' ‚ðŠJ‚Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d %-.128s)", + est "Ei suuda avada jagatud teeki '%-.64s' (veakood: %d %-.128s)" + fre "Impossible d'ouvrir la bibliothèque partagée '%-.64s' (errno: %d %-.128s)" + ger "Kann Shared Library '%-.64s' nicht öffnen (Fehler: %d %-.128s)" + greek "Äåí åßíáé äõíáôÞ ç áíÜãíùóç ôçò shared library '%-.64s' (êùäéêüò ëÜèïõò: %d %-.128s)" + hun "A(z) '%-.64s' megosztott konyvtar nem hasznalhato (hibakod: %d %-.128s)" + ita "Impossibile aprire la libreria condivisa '%-.64s' (errno: %d %-.128s)" + jpn "shared library '%-.64s' ¤ò³«¤¯»ö¤¬¤Ç¤¤Þ¤»¤ó (errno: %d %-.128s)" + kor "'%-.64s' °øÀ¯ ¶óÀ̹ö·¯¸®¸¦ ¿¼ö ¾ø½À´Ï´Ù.(¿¡·¯¹øÈ£: %d %-.128s)" + nor "Can't open shared library '%-.64s' (errno: %d %-.128s)" + norwegian-ny "Can't open shared library '%-.64s' (errno: %d %-.128s)" + pol "Can't open shared library '%-.64s' (errno: %d %-.128s)" + por "Não pode abrir biblioteca compartilhada '%-.64s' (erro no. '%d' - '%-.128s')" rum "Nu pot deschide libraria shared '%-.64s' (Eroare: %d %-.64s)" rus "îÅ×ÏÚÍÏÖÎÏ ÏÔËÒÙÔØ ÄÉÎÁÍÉÞÅÓËÕÀ ÂÉÂÌÉÏÔÅËÕ '%-.64s' (ÏÛÉÂËÁ: %d %-.64s)" serbian "Ne mogu da otvorim share-ovanu biblioteku '%-.64s' (errno: %d %-.64s)" @@ -3003,27 +3003,27 @@ ER_CANT_OPEN_LIBRARY swe "Kan inte öppna det dynamiska biblioteket '%-.64s' (Felkod: %d %s)" ukr "îÅ ÍÏÖÕ ×¦ÄËÒÉÔÉ ÒÏÚĦÌÀ×ÁÎÕ Â¦Â̦ÏÔÅËÕ '%-.64s' (ÐÏÍÉÌËÁ: %d %-.64s)" ER_CANT_FIND_DL_ENTRY - cze "Nemohu naj-Bít funkci '%-.64s' v knihovnì" - dan "Kan ikke finde funktionen '%-.64s' i bibliotek" - nla "Kan functie '%-.64s' niet in library vinden" - eng "Can't find function '%-.64s' in library" - jps "function '%-.64s' ‚ðƒ‰ƒCƒuƒ‰ƒŠ[’†‚ÉŒ©•t‚¯‚鎖‚ª‚Å‚«‚Ü‚¹‚ñ", - est "Ei leia funktsiooni '%-.64s' antud teegis" - fre "Impossible de trouver la fonction '%-.64s' dans la bibliothèque" - ger "Kann Funktion '%-.64s' in der Library nicht finden" - greek "Äåí åßíáé äõíáôÞ ç áíåýñåóç ôçò óõíÜñôçóçò '%-.64s' óôçí âéâëéïèÞêç" - hun "A(z) '%-.64s' fuggveny nem talalhato a konyvtarban" - ita "Impossibile trovare la funzione '%-.64s' nella libreria" - jpn "function '%-.64s' ¤ò¥é¥¤¥Ö¥é¥ê¡¼Ãæ¤Ë¸«ÉÕ¤±¤ë»ö¤¬¤Ç¤¤Þ¤»¤ó" - kor "¶óÀ̹ö·¯¸®¿¡¼ '%-.64s' ÇÔ¼ö¸¦ ãÀ» ¼ö ¾ø½À´Ï´Ù." - por "Não pode encontrar a função '%-.64s' na biblioteca" - rum "Nu pot gasi functia '%-.64s' in libraria" - rus "îÅ×ÏÚÍÏÖÎÏ ÏÔÙÓËÁÔØ ÆÕÎËÃÉÀ '%-.64s' × ÂÉÂÌÉÏÔÅËÅ" - serbian "Ne mogu da pronadjem funkciju '%-.64s' u biblioteci" - slo "Nemô¾em nájs» funkciu '%-.64s' v kni¾nici" - spa "No puedo encontrar función '%-.64s' en libraria" - swe "Hittar inte funktionen '%-.64s' in det dynamiska biblioteket" - ukr "îÅ ÍÏÖÕ ÚÎÁÊÔÉ ÆÕÎËæÀ '%-.64s' Õ Â¦Â̦ÏÔÅæ" + cze "Nemohu naj-Bít funkci '%-.128s' v knihovnì" + dan "Kan ikke finde funktionen '%-.128s' i bibliotek" + nla "Kan functie '%-.128s' niet in library vinden" + eng "Can't find function '%-.128s' in library" + jps "function '%-.128s' ‚ðƒ‰ƒCƒuƒ‰ƒŠ[’†‚ÉŒ©•t‚¯‚鎖‚ª‚Å‚«‚Ü‚¹‚ñ", + est "Ei leia funktsiooni '%-.128s' antud teegis" + fre "Impossible de trouver la fonction '%-.128s' dans la bibliothèque" + ger "Kann Funktion '%-.128s' in der Library nicht finden" + greek "Äåí åßíáé äõíáôÞ ç áíåýñåóç ôçò óõíÜñôçóçò '%-.128s' óôçí âéâëéïèÞêç" + hun "A(z) '%-.128s' fuggveny nem talalhato a konyvtarban" + ita "Impossibile trovare la funzione '%-.128s' nella libreria" + jpn "function '%-.128s' ¤ò¥é¥¤¥Ö¥é¥ê¡¼Ãæ¤Ë¸«ÉÕ¤±¤ë»ö¤¬¤Ç¤¤Þ¤»¤ó" + kor "¶óÀ̹ö·¯¸®¿¡¼ '%-.128s' ÇÔ¼ö¸¦ ãÀ» ¼ö ¾ø½À´Ï´Ù." + por "Não pode encontrar a função '%-.128s' na biblioteca" + rum "Nu pot gasi functia '%-.128s' in libraria" + rus "îÅ×ÏÚÍÏÖÎÏ ÏÔÙÓËÁÔØ ÆÕÎËÃÉÀ '%-.128s' × ÂÉÂÌÉÏÔÅËÅ" + serbian "Ne mogu da pronadjem funkciju '%-.128s' u biblioteci" + slo "Nemô¾em nájs» funkciu '%-.128s' v kni¾nici" + spa "No puedo encontrar función '%-.128s' en libraria" + swe "Hittar inte funktionen '%-.128s' in det dynamiska biblioteket" + ukr "îÅ ÍÏÖÕ ÚÎÁÊÔÉ ÆÕÎËæÀ '%-.128s' Õ Â¦Â̦ÏÔÅæ" ER_FUNCTION_NOT_DEFINED cze "Funkce '%-.64s' nen-Bí definována" dan "Funktionen '%-.64s' er ikke defineret" @@ -5607,5 +5607,7 @@ ER_SP_PROC_TABLE_CORRUPT eng "Failed to load routine %s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)" ER_SP_WRONG_NAME 42000 eng "Incorrect routine name '%-.64s'" +ER_TABLE_NEEDS_UPGRADE + eng "Table upgrade required. Please do \"REPAIR TABLE `%-.32s`\" to fix it!" ER_SP_NO_AGGREGATE 42000 eng "AGGREGATE is not supported for stored functions" diff --git a/sql/slave.cc b/sql/slave.cc index 3795cbaf7c0..fa7ccc4427d 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1622,7 +1622,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, save_vio = thd->net.vio; thd->net.vio = 0; /* Rebuild the index file from the copied data file (with REPAIR) */ - error=file->repair(thd,&check_opt) != 0; + error=file->ha_repair(thd,&check_opt) != 0; thd->net.vio = save_vio; if (error) my_error(ER_INDEX_REBUILD, MYF(0), tables.table->s->table_name); diff --git a/sql/sp.cc b/sql/sp.cc index 93dfd68cef3..cfcf011032d 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -21,6 +21,8 @@ #include "sp_cache.h" #include "sql_trigger.h" +#include <my_user.h> + static bool create_string(THD *thd, String *buf, int sp_type, @@ -28,7 +30,9 @@ create_string(THD *thd, String *buf, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, - st_sp_chistics *chistics); + st_sp_chistics *chistics, + const LEX_STRING *definer_user, + const LEX_STRING *definer_host); static int db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong sql_mode, const char *params, const char *returns, @@ -265,7 +269,7 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) static int db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) { - extern int yyparse(void *thd); + extern int MYSQLparse(void *thd); TABLE *table; const char *params, *returns, *body; int ret; @@ -406,6 +410,15 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong old_sql_mode= thd->variables.sql_mode; ha_rows old_select_limit= thd->variables.select_limit; sp_rcontext *old_spcont= thd->spcont; + + char definer_user_name_holder[USERNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder, + USERNAME_LENGTH); + + char definer_host_name_holder[HOSTNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder, + HOSTNAME_LENGTH); + int ret; thd->variables.sql_mode= sql_mode; @@ -414,14 +427,25 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, thd->lex= &newlex; newlex.current_select= NULL; + parse_user(definer, strlen(definer), + definer_user_name.str, &definer_user_name.length, + definer_host_name.str, &definer_host_name.length); + defstr.set_charset(system_charset_info); + + /* + We have to add DEFINER clause and provide proper routine characterstics in + routine definition statement that we build here to be able to use this + definition for SHOW CREATE PROCEDURE later. + */ + if (!create_string(thd, &defstr, type, name, params, strlen(params), returns, strlen(returns), body, strlen(body), - &chistics)) + &chistics, &definer_user_name, &definer_host_name)) { ret= SP_INTERNAL_ERROR; goto end; @@ -435,7 +459,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); thd->spcont= 0; - if (yyparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) + if (MYSQLparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) { sp_head *sp= newlex.sphead; @@ -449,7 +473,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) goto end; *sphp= newlex.sphead; - (*sphp)->set_definer((char*) definer, (uint) strlen(definer)); + (*sphp)->set_definer(&definer_user_name, &definer_host_name); (*sphp)->set_info(created, modified, &chistics, sql_mode); (*sphp)->optimize(); } @@ -500,8 +524,10 @@ db_create_routine(THD *thd, int type, sp_head *sp) else { restore_record(table, s->default_values); // Get default values for fields - strxmov(definer, thd->security_ctx->priv_user, "@", - thd->security_ctx->priv_host, NullS); + + /* NOTE: all needed privilege checks have been already done. */ + strxmov(definer, thd->lex->definer->user.str, "@", + thd->lex->definer->host.str, NullS); if (table->s->fields != MYSQL_PROC_FIELD_COUNT) { @@ -596,8 +622,17 @@ db_create_routine(THD *thd, int type, sp_head *sp) else if (mysql_bin_log.is_open()) { thd->clear_error(); + + String log_query; + log_query.set_charset(system_charset_info); + log_query.append(STRING_WITH_LEN("CREATE ")); + append_definer(thd, &log_query, &thd->lex->definer->user, + &thd->lex->definer->host); + log_query.append(thd->lex->stmt_definition_begin); + /* Such a statement can always go directly to binlog, no trans cache */ - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0, + FALSE); mysql_bin_log.write(&qinfo); } @@ -1016,6 +1051,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error) { TABLE_LIST *routine; bool result= 0; + bool sp_object_found; DBUG_ENTER("sp_exists_routine"); for (routine= routines; routine; routine= routine->next_global) { @@ -1028,10 +1064,12 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error) lex_name.str= thd->strmake(routine->table_name, lex_name.length); name= new sp_name(lex_db, lex_name); name->init_qname(thd); - if (sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, - &thd->sp_proc_cache, FALSE) != NULL || - sp_find_routine(thd, TYPE_ENUM_FUNCTION, name, - &thd->sp_func_cache, FALSE) != NULL) + sp_object_found= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, + &thd->sp_proc_cache, FALSE) != NULL || + sp_find_routine(thd, TYPE_ENUM_FUNCTION, name, + &thd->sp_func_cache, FALSE) != NULL; + mysql_reset_errors(thd, TRUE); + if (sp_object_found) { if (any) DBUG_RETURN(1); @@ -1724,14 +1762,18 @@ create_string(THD *thd, String *buf, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, - st_sp_chistics *chistics) + st_sp_chistics *chistics, + const LEX_STRING *definer_user, + const LEX_STRING *definer_host) { /* Make some room to begin with */ if (buf->alloc(100 + name->m_qname.length + paramslen + returnslen + bodylen + - chistics->comment.length)) + chistics->comment.length + 10 /* length of " DEFINER= "*/ + + USER_HOST_BUFF_SIZE)) return FALSE; buf->append(STRING_WITH_LEN("CREATE ")); + append_definer(thd, buf, definer_user, definer_host); if (type == TYPE_ENUM_FUNCTION) buf->append(STRING_WITH_LEN("FUNCTION ")); else diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 27dc0103335..bba9479c8f3 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -80,8 +80,8 @@ sp_map_item_type(enum enum_field_types type) /* Return a string representation of the Item value. - NOTE: this is a legacy-compatible implementation. It fails if the value - contains non-ordinary symbols, which should be escaped. + NOTE: If the item has a string result type, the string is escaped + according to its character set. SYNOPSIS item a pointer to the Item @@ -119,9 +119,9 @@ sp_get_item_value(Item *item, String *str) buf.append('_'); buf.append(result->charset()->csname); - buf.append('\''); - buf.append(*result); - buf.append('\''); + if (result->charset()->escape_with_backslash_is_dangerous) + buf.append(' '); + append_query_string(result->charset(), result, &buf); str->copy(buf); return str; @@ -736,13 +736,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) Statements that have is_update_query(stmt) == FALSE (e.g. SELECTs) are not written into binary log. Instead we catch function calls the statement makes and write it into binary log separately (see #3). - - We actually can easily write SELECT statements into the binary log in the - right order (we don't have issues with const tables being unlocked early - because SELECTs that use FUNCTIONs unlock all tables at once) We don't do - it because replication slave thread currently can't execute SELECT - statements. Fixing this is on the TODO. - + 2. PROCEDURE calls CALL statements are not written into binary log. Instead @@ -763,7 +757,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) function execution (grep for start_union_events and stop_union_events) If the answers are No and Yes, we write the function call into the binary - log as "DO spfunc(<param1value>, <param2value>, ...)" + log as "SELECT spfunc(<param1value>, <param2value>, ...)". 4. Miscellaneous issues. @@ -1310,7 +1304,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, char buf[256]; String bufstr(buf, sizeof(buf), &my_charset_bin); bufstr.length(0); - bufstr.append(STRING_WITH_LEN("DO ")); + bufstr.append(STRING_WITH_LEN("SELECT ")); append_identifier(thd, &bufstr, m_name.str, m_name.length); bufstr.append('('); for (uint i=0; i < argcount; i++) @@ -1609,6 +1603,8 @@ sp_head::reset_lex(THD *thd) sublex->trg_table_fields.empty(); sublex->sp_lex_in_use= FALSE; + sublex->in_comment= oldlex->in_comment; + /* Reset type info. */ sublex->charset= NULL; @@ -1814,19 +1810,27 @@ sp_head::set_info(longlong created, longlong modified, void sp_head::set_definer(const char *definer, uint definerlen) { - uint user_name_len; - char user_name_str[USERNAME_LENGTH + 1]; - uint host_name_len; - char host_name_str[HOSTNAME_LENGTH + 1]; + char user_name_holder[USERNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_LENGTH); - parse_user(definer, definerlen, user_name_str, &user_name_len, - host_name_str, &host_name_len); + char host_name_holder[HOSTNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT host_name(host_name_holder, HOSTNAME_LENGTH); - m_definer_user.str= strmake_root(mem_root, user_name_str, user_name_len); - m_definer_user.length= user_name_len; + parse_user(definer, definerlen, user_name.str, &user_name.length, + host_name.str, &host_name.length); - m_definer_host.str= strmake_root(mem_root, host_name_str, host_name_len); - m_definer_host.length= host_name_len; + set_definer(&user_name, &host_name); +} + + +void +sp_head::set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name) +{ + m_definer_user.str= strmake_root(mem_root, user_name->str, user_name->length); + m_definer_user.length= user_name->length; + + m_definer_host.str= strmake_root(mem_root, host_name->str, host_name->length); + m_definer_host.length= host_name->length; } @@ -3167,24 +3171,9 @@ sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup) sp->m_definer_host.str, sp->m_db.str)) { -#ifdef NOT_YET_REPLICATION_SAFE - /* - Until we don't properly replicate information about stored routine - definer with stored routine creation statement all stored routines - on slave are created under ''@'' definer. Therefore we won't be able - to run any routine which was replicated from master on slave server - if we emit error here. This will cause big problems for users - who use slave for fail-over. So until we fully implement WL#2897 - "Complete definer support in the stored routines" we run suid - stored routines for which we were unable to find definer under - invoker security context. - */ my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str, sp->m_definer_host.str); return TRUE; -#else - return FALSE; -#endif } *backup= thd->security_ctx; thd->security_ctx= &sp->m_security_ctx; diff --git a/sql/sp_head.h b/sql/sp_head.h index a4dd68ee4a3..64d9167fb17 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -302,6 +302,7 @@ public: st_sp_chistics *chistics, ulong sql_mode); void set_definer(const char *definer, uint definerlen); + void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name); void reset_thd_mem_root(THD *thd); diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index a8bd8cd2aa0..f69053a7c88 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -122,30 +122,38 @@ sp_pcontext::pop_context() } uint -sp_pcontext::diff_handlers(sp_pcontext *ctx) +sp_pcontext::diff_handlers(sp_pcontext *ctx, bool exclusive) { uint n= 0; sp_pcontext *pctx= this; + sp_pcontext *last_ctx= NULL; while (pctx && pctx != ctx) { n+= pctx->m_handlers; + last_ctx= pctx; pctx= pctx->parent_context(); } if (pctx) - return n; + return (exclusive && last_ctx ? n - last_ctx->m_handlers : n); return 0; // Didn't find ctx } uint -sp_pcontext::diff_cursors(sp_pcontext *ctx) +sp_pcontext::diff_cursors(sp_pcontext *ctx, bool exclusive) { + uint n= 0; sp_pcontext *pctx= this; + sp_pcontext *last_ctx= NULL; while (pctx && pctx != ctx) + { + n+= pctx->m_cursor.elements; + last_ctx= pctx; pctx= pctx->parent_context(); + } if (pctx) - return ctx->current_cursors() - pctx->current_cursors(); + return (exclusive && last_ctx ? n - last_ctx->m_cursor.elements : n); return 0; // Didn't find ctx } diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index d1cd7b964c2..872c7c1d505 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -119,11 +119,15 @@ class sp_pcontext : public Sql_alloc return m_parent; } + /* + Number of handlers/cursors to pop between this context and 'ctx'. + If 'exclusive' is true, don't count the last block we are leaving; + this is used for LEAVE where we will jump to the cpop/hpop instructions. + */ uint - diff_handlers(sp_pcontext *ctx); - + diff_handlers(sp_pcontext *ctx, bool exclusive); uint - diff_cursors(sp_pcontext *ctx); + diff_cursors(sp_pcontext *ctx, bool exclusive); // diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index c3c05228eef..e7393902e72 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -268,6 +268,7 @@ class Select_fetch_into_spvars: public select_result_interceptor List<struct sp_pvar> *spvar_list; uint field_count; public: + Select_fetch_into_spvars() {} /* Remove gcc warning */ uint get_field_count() { return field_count; } void set_spvar_list(List<struct sp_pvar> *vars) { spvar_list= vars; } diff --git a/sql/spatial.h b/sql/spatial.h index 527dc750bdc..a6f74a1ada0 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -165,6 +165,8 @@ struct Geometry_buffer; class Geometry { public: + Geometry() {} /* Remove gcc warning */ + virtual ~Geometry() {} /* Remove gcc warning */ static void *operator new(size_t size, void *buffer) { return buffer; @@ -302,6 +304,8 @@ protected: class Gis_point: public Geometry { public: + Gis_point() {} /* Remove gcc warning */ + virtual ~Gis_point() {} /* Remove gcc warning */ uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); @@ -349,6 +353,8 @@ public: class Gis_line_string: public Geometry { public: + Gis_line_string() {} /* Remove gcc warning */ + virtual ~Gis_line_string() {} /* Remove gcc warning */ uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); @@ -375,6 +381,8 @@ public: class Gis_polygon: public Geometry { public: + Gis_polygon() {} /* Remove gcc warning */ + virtual ~Gis_polygon() {} /* Remove gcc warning */ uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); @@ -401,6 +409,8 @@ public: class Gis_multi_point: public Geometry { public: + Gis_multi_point() {} /* Remove gcc warning */ + virtual ~Gis_multi_point() {} /* Remove gcc warning */ uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); @@ -423,6 +433,8 @@ public: class Gis_multi_line_string: public Geometry { public: + Gis_multi_line_string() {} /* Remove gcc warning */ + virtual ~Gis_multi_line_string() {} /* Remove gcc warning */ uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); @@ -447,6 +459,8 @@ public: class Gis_multi_polygon: public Geometry { public: + Gis_multi_polygon() {} /* Remove gcc warning */ + virtual ~Gis_multi_polygon() {} /* Remove gcc warning */ uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); @@ -471,6 +485,8 @@ public: class Gis_geometry_collection: public Geometry { public: + Gis_geometry_collection() {} /* Remove gcc warning */ + virtual ~Gis_geometry_collection() {} /* Remove gcc warning */ uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index c67ce383398..d66a631dbcc 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -148,7 +148,9 @@ my_bool acl_init(bool dont_read_acl_tables) acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0, (hash_get_key) acl_entry_get_key, - (hash_free_key) free, system_charset_info); + (hash_free_key) free, + /* Use the case sensitive "binary" charset */ + &my_charset_bin); if (dont_read_acl_tables) { DBUG_RETURN(0); /* purecov: tested */ @@ -2256,7 +2258,10 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, { if (exact) { - if (compare_hostname(&grant_name->host, host, ip)) + if ((host && + !my_strcasecmp(system_charset_info, host, + grant_name->host.hostname)) || + (ip && !strcmp(ip, grant_name->host.hostname))) return grant_name; } else @@ -3532,6 +3537,13 @@ end: RETURN 0 ok 1 Error: User did not have the requested privileges + + NOTE + This functions assumes that either number of tables to be inspected + by it is limited explicitly (i.e. is is not UINT_MAX) or table list + used and thd->lex->query_tables_own_last value correspond to each + other (the latter should be either 0 or point to next_global member + of one of elements of this table list). ****************************************************************************/ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 0e4198a5114..af9246c673a 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -27,6 +27,8 @@ #pragma implementation // gcc: Class implementation #endif +#define MYSQL_LEX 1 + #include "mysql_priv.h" #include "procedure.h" #include "sql_analyse.h" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9d1720cc527..2b5a3d1f38d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -368,6 +368,15 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, DESCRIPTION Marks all tables in the list which were used by current substatement (they are marked by its query_id) as free for reuse. + + NOTE + The reason we reset query_id is that it's not enough to just test + if table->query_id != thd->query_id to know if a table is in use. + + For example + SELECT f1_that_uses_t1() FROM t1; + In f1_that_uses_t1() we will see one instance of t1 where query_id is + set to query_id of original query. */ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) @@ -678,11 +687,11 @@ void close_temporary_tables(THD *thd) */ TABLE_LIST *find_table_in_list(TABLE_LIST *table, - uint offset, + st_table_list *TABLE_LIST::*link, const char *db_name, const char *table_name) { - for (; table; table= *(TABLE_LIST **) ((char*) table + offset)) + for (; table; table= table->*link ) { if ((table->table == 0 || table->table->s->tmp_table == NO_TMP_TABLE) && strcmp(table->db, db_name) == 0 && @@ -1982,22 +1991,11 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) statement for which table list for prelocking is already built, let us cache routines and try to build such table list. - NOTE: We can't delay prelocking until we will met some sub-statement - which really uses tables, since this will imply that we have to restore - its table list to be able execute it in some other context. - And current views implementation assumes that view tables are added to - global table list only once during PS preparing/first SP execution. - Also locking at earlier stage is probably faster altough may decrease - concurrency a bit. - NOTE: We will mark statement as requiring prelocking only if we will have non empty table list. But this does not guarantee that in prelocked mode we will have some locked tables, because queries which use only derived/information schema tables and views possible. Thus "counter" may be still zero for prelocked statement... - - NOTE: The above notes may be out of date. Please wait for psergey to - document new prelocked behavior. */ if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && @@ -2083,48 +2081,23 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) if (refresh) // Refresh in progress { - /* close all 'old' tables used by this thread */ - pthread_mutex_lock(&LOCK_open); - // if query_id is not reset, we will get an error - // re-opening a temp table - thd->version=refresh_version; - TABLE **prev_table= &thd->open_tables; - bool found=0; - for (TABLE_LIST *tmp= *start; tmp; tmp= tmp->next_global) - { - /* Close normal (not temporary) changed tables */ - if (tmp->table && ! tmp->table->s->tmp_table) - { - if (tmp->table->s->version != refresh_version || - ! tmp->table->db_stat) - { - VOID(hash_delete(&open_cache,(byte*) tmp->table)); - tmp->table=0; - found=1; - } - else - { - *prev_table= tmp->table; // Relink open list - prev_table= &tmp->table->next; - } - } - } - *prev_table=0; - pthread_mutex_unlock(&LOCK_open); - if (found) - VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh /* - Let us prepare for recalculation of set of prelocked tables. - First we pretend that we have finished calculation which we - were doing currently. Then we restore list of tables to be - opened and set of used routines to the state in which they were - before first open_tables() call for this statement (i.e. before - we have calculated current set of tables for prelocking). + We have met name-locked or old version of table. Now we have + to close all tables which are not up to date. We also have to + throw away set of prelocked tables (and thus close tables from + this set that were open by now) since it possible that one of + tables which determined its content was changed. + + Instead of implementing complex/non-robust logic mentioned + above we simply close and then reopen all tables. + + In order to prepare for recalculation of set of prelocked tables + we pretend that we have finished calculation which we were doing + currently. */ if (query_tables_last_own) thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - thd->lex->chop_off_not_own_tables(); - sp_remove_not_own_routines(thd->lex); + close_tables_for_reopen(thd, start); goto restart; } result= -1; // Fatal error @@ -2335,7 +2308,7 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) break; if (!need_reopen) DBUG_RETURN(-1); - close_tables_for_reopen(thd, tables); + close_tables_for_reopen(thd, &tables); } DBUG_RETURN(0); } @@ -2372,7 +2345,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) break; if (!need_reopen) DBUG_RETURN(-1); - close_tables_for_reopen(thd, tables); + close_tables_for_reopen(thd, &tables); } if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || (thd->fill_derived_tables() && @@ -2600,18 +2573,24 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) SYNOPSIS close_tables_for_reopen() - thd Thread context - tables List of tables which we were trying to open and lock + thd in Thread context + tables in/out List of tables which we were trying to open and lock */ -void close_tables_for_reopen(THD *thd, TABLE_LIST *tables) +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) { + /* + If table list consists only from tables from prelocking set, table list + for new attempt should be empty, so we have to update list's root pointer. + */ + if (thd->lex->first_not_own_table() == *tables) + *tables= 0; thd->lex->chop_off_not_own_tables(); sp_remove_not_own_routines(thd->lex); - for (TABLE_LIST *tmp= tables; tmp; tmp= tmp->next_global) - if (tmp->table && !tmp->table->s->tmp_table) - tmp->table= 0; + for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global) + tmp->table= 0; + mark_used_tables_as_free_for_reuse(thd, thd->temporary_tables); close_thread_tables(thd); } @@ -3624,10 +3603,19 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, { Field_iterator_table_ref it_1, it_2; Natural_join_column *nj_col_1, *nj_col_2; - const char *field_name_1; Query_arena *arena, backup; - bool add_columns= TRUE; bool result= TRUE; + bool first_outer_loop= TRUE; + /* + Leaf table references to which new natural join columns are added + if the leaves are != NULL. + */ + TABLE_LIST *leaf_1= (table_ref_1->nested_join && + !table_ref_1->is_natural_join) ? + NULL : table_ref_1; + TABLE_LIST *leaf_2= (table_ref_2->nested_join && + !table_ref_2->is_natural_join) ? + NULL : table_ref_2; DBUG_ENTER("mark_common_columns"); DBUG_PRINT("info", ("operand_1: %s operand_2: %s", @@ -3636,35 +3624,14 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, *found_using_fields= 0; arena= thd->activate_stmt_arena_if_needed(&backup); - /* - TABLE_LIST::join_columns could be allocated by the previous call to - store_natural_using_join_columns() for the lower level of nested tables. - */ - if (!table_ref_1->join_columns) - { - if (!(table_ref_1->join_columns= new List<Natural_join_column>)) - goto err; - table_ref_1->is_join_columns_complete= FALSE; - } - if (!table_ref_2->join_columns) - { - if (!(table_ref_2->join_columns= new List<Natural_join_column>)) - goto err; - table_ref_2->is_join_columns_complete= FALSE; - } - for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next()) { - bool is_created_1; bool found= FALSE; - if (!(nj_col_1= it_1.get_or_create_column_ref(&is_created_1))) + const char *field_name_1; + if (!(nj_col_1= it_1.get_or_create_column_ref(leaf_1))) goto err; field_name_1= nj_col_1->name(); - /* If nj_col_1 was just created add it to the list of join columns. */ - if (is_created_1) - table_ref_1->join_columns->push_back(nj_col_1); - /* Find a field with the same name in table_ref_2. @@ -3675,17 +3642,12 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, nj_col_2= NULL; for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next()) { - bool is_created_2; Natural_join_column *cur_nj_col_2; const char *cur_field_name_2; - if (!(cur_nj_col_2= it_2.get_or_create_column_ref(&is_created_2))) + if (!(cur_nj_col_2= it_2.get_or_create_column_ref(leaf_2))) goto err; cur_field_name_2= cur_nj_col_2->name(); - /* If nj_col_1 was just created add it to the list of join columns. */ - if (add_columns && is_created_2) - table_ref_2->join_columns->push_back(cur_nj_col_2); - /* Compare the two columns and check for duplicate common fields. A common field is duplicate either if it was already found in @@ -3704,9 +3666,15 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, found= TRUE; } } - /* Force it_2.set() to use table_ref_2->join_columns. */ - table_ref_2->is_join_columns_complete= TRUE; - add_columns= FALSE; + if (first_outer_loop && leaf_2) + { + /* + Make sure that the next inner loop "knows" that all columns + are materialized already. + */ + leaf_2->is_join_columns_complete= TRUE; + first_outer_loop= FALSE; + } if (!found) continue; // No matching field @@ -3790,7 +3758,8 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, ++(*found_using_fields); } } - table_ref_1->is_join_columns_complete= TRUE; + if (leaf_1) + leaf_1->is_join_columns_complete= TRUE; /* Everything is OK. @@ -3853,7 +3822,6 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, { Field_iterator_table_ref it_1, it_2; Natural_join_column *nj_col_1, *nj_col_2; - bool is_created; Query_arena *arena, backup; bool result= TRUE; List<Natural_join_column> *non_join_columns; @@ -4647,16 +4615,15 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (tables->is_natural_join) { - bool is_created; TABLE *field_table; /* In this case we are sure that the column ref will not be created because it was already created and stored with the natural join. */ Natural_join_column *nj_col; - if (!(nj_col= field_iterator.get_or_create_column_ref(&is_created))) + if (!(nj_col= field_iterator.get_natural_column_ref())) DBUG_RETURN(TRUE); - DBUG_ASSERT(nj_col->table_field && !is_created); + DBUG_ASSERT(nj_col->table_field); field_table= nj_col->table_ref->table; if (field_table) { diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 69a0d6cd05d..29d314d3c44 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -69,6 +69,7 @@ class Query_cache; struct Query_cache_block_table { + Query_cache_block_table() {} /* Remove gcc warning */ TABLE_COUNTER_TYPE n; // numbr in table (from 0) Query_cache_block_table *next, *prev; Query_cache_table *parent; @@ -78,6 +79,7 @@ struct Query_cache_block_table struct Query_cache_block { + Query_cache_block() {} /* Remove gcc warning */ enum block_type {FREE, QUERY, RESULT, RES_CONT, RES_BEG, RES_INCOMPLETE, TABLE, INCOMPLETE}; @@ -143,6 +145,7 @@ struct Query_cache_query struct Query_cache_table { + Query_cache_table() {} /* Remove gcc warning */ char *tbl; uint32 key_len; uint8 table_type; @@ -171,6 +174,7 @@ struct Query_cache_table struct Query_cache_result { + Query_cache_result() {} /* Remove gcc warning */ Query_cache_block *query; inline gptr data() @@ -197,6 +201,7 @@ extern "C" void query_cache_invalidate_by_MyISAM_filename(const char* filename); struct Query_cache_memory_bin { + Query_cache_memory_bin() {} /* Remove gcc warning */ #ifndef DBUG_OFF ulong size; #endif @@ -215,6 +220,7 @@ struct Query_cache_memory_bin struct Query_cache_memory_bin_step { + Query_cache_memory_bin_step() {} /* Remove gcc warning */ ulong size; ulong increment; uint idx; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b3709235d8b..59391a333c3 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -648,6 +648,9 @@ void THD::update_charset() charset_is_collation_connection= !String::needs_conversion(0,charset(),variables.collation_connection, ¬_used); + charset_is_character_set_filesystem= + !String::needs_conversion(0, charset(), + variables.character_set_filesystem, ¬_used); } @@ -947,6 +950,7 @@ bool select_send::send_data(List<Item> &items) DBUG_RETURN(0); if (!thd->net.report_error) DBUG_RETURN(protocol->write()); + protocol->remove_last_row(); DBUG_RETURN(1); } @@ -1978,10 +1982,8 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, cuted_fields= 0; transaction.savepoints= 0; -#ifndef EMBEDDED_LIBRARY /* Surpress OK packets in case if we will execute statements */ net.no_send_ok= TRUE; -#endif } diff --git a/sql/sql_class.h b/sql/sql_class.h index c025b4f0774..6189470b88d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -69,7 +69,8 @@ class TC_LOG class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging { - public: +public: + TC_LOG_DUMMY() {} /* Remove gcc warning */ int open(const char *opt_name) { return 0; } void close() { } int log(THD *thd, my_xid xid) { return 1; } @@ -569,6 +570,7 @@ struct system_variables my_bool old_passwords; /* Only charset part of these variables is sensible */ + CHARSET_INFO *character_set_filesystem; CHARSET_INFO *character_set_client; CHARSET_INFO *character_set_results; @@ -583,6 +585,7 @@ struct system_variables DATE_TIME_FORMAT *date_format; DATE_TIME_FORMAT *datetime_format; DATE_TIME_FORMAT *time_format; + my_bool sysdate_is_now; }; @@ -929,6 +932,7 @@ void xid_cache_delete(XID_STATE *xid_state); class Security_context { public: + Security_context() {} /* Remove gcc warning */ /* host - host of the client user - user of the client, set to NULL until the user has been read from @@ -1101,13 +1105,16 @@ 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; - String query_rest; + struct st_mysql_data *cur_data; + struct st_mysql_data *first_data; + struct st_mysql_data **data_tail; + void clear_data_list(); + struct st_mysql_data *alloc_new_dataset(); #endif NET net; // client connection descriptor MEM_ROOT warn_root; // For warnings and errors @@ -1341,6 +1348,7 @@ public: bool query_error, bootstrap, cleanup_done; bool tmp_table_used; bool charset_is_system_charset, charset_is_collation_connection; + bool charset_is_character_set_filesystem; bool enable_slow_log; /* enable slow log for current statement */ bool no_trans_update, abort_on_warning; bool got_warning; /* Set on call to push_warning() */ @@ -1657,6 +1665,11 @@ public: */ virtual void cleanup(); void set_thd(THD *thd_arg) { thd= thd_arg; } +#ifdef EMBEDDED_LIBRARY + virtual void begin_dataset() {} +#else + void begin_dataset() {} +#endif }; @@ -1669,6 +1682,7 @@ public: class select_result_interceptor: public select_result { public: + select_result_interceptor() {} /* Remove gcc warning */ uint field_count(List<Item> &fields) const { return 0; } bool send_fields(List<Item> &fields, uint flag) { return FALSE; } }; @@ -1956,6 +1970,7 @@ class Table_ident :public Sql_alloc class user_var_entry { public: + user_var_entry() {} /* Remove gcc warning */ LEX_STRING name; char *value; ulong length; diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 89c160cd70a..33ad27b9d14 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -603,6 +603,7 @@ void Materialized_cursor::fetch(ulong num_rows) THD *thd= table->in_use; int res= 0; + result->begin_dataset(); for (fetch_limit+= num_rows; fetch_count < fetch_limit; fetch_count++) { if ((res= table->file->rnd_next(table->record[0]))) diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 357d2d5a21f..4caa0076c60 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1098,17 +1098,21 @@ err: bool mysql_change_db(THD *thd, const char *name, bool no_access_check) { int length, db_length; - char *dbname=my_strdup((char*) name,MYF(MY_WME)); + char *dbname= thd->slave_thread ? (char *) name : + my_strdup((char *) name, MYF(MY_WME)); char path[FN_REFLEN]; HA_CREATE_INFO create; bool system_db= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; Security_context *sctx= thd->security_ctx; + LINT_INIT(db_access); #endif DBUG_ENTER("mysql_change_db"); DBUG_PRINT("enter",("name: '%s'",name)); + LINT_INIT(db_length); + /* dbname can only be NULL if malloc failed */ if (!dbname || !(db_length= strlen(dbname))) { @@ -1118,7 +1122,8 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) system_db= 1; goto end; } - x_free(dbname); /* purecov: inspected */ + if (!(thd->slave_thread)) + x_free(dbname); /* purecov: inspected */ my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ @@ -1126,7 +1131,8 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) if (check_db_name(dbname)) { my_error(ER_WRONG_DB_NAME, MYF(0), dbname); - x_free(dbname); + if (!(thd->slave_thread)) + my_free(dbname, MYF(0)); DBUG_RETURN(1); } DBUG_PRINT("info",("Use database: %s", dbname)); @@ -1156,7 +1162,8 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) dbname); mysql_log.write(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR), sctx->priv_user, sctx->priv_host, dbname); - my_free(dbname,MYF(0)); + if (!(thd->slave_thread)) + my_free(dbname,MYF(0)); DBUG_RETURN(1); } } @@ -1168,7 +1175,8 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) if (my_access(path,F_OK)) { my_error(ER_BAD_DB_ERROR, MYF(0), dbname); - my_free(dbname,MYF(0)); + if (!(thd->slave_thread)) + my_free(dbname,MYF(0)); DBUG_RETURN(1); } end: @@ -1177,7 +1185,7 @@ end: if (dbname && dbname[0] == 0) { if (!(thd->slave_thread)) - x_free(dbname); + my_free(dbname, MYF(0)); thd->db= NULL; thd->db_length= 0; } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d8a8f28b92b..56dbd423b69 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -813,6 +813,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) strmov(path, table->s->path); *table_ptr= table->next; // Unlink table from list close_temporary(table,0); + if (thd->slave_thread) + --slave_open_temp_tables; *fn_ext(path)=0; // Remove the .frm extension ha_create_table(path, &create_info,1); // We don't need to call invalidate() because this table is not in cache diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 191a6e0a1fd..19811efbb12 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -108,6 +108,7 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, { MYSQL_ERROR *err= 0; DBUG_ENTER("push_warning"); + DBUG_PRINT("enter", ("code: %d, msg: %s", code, msg)); if (level == MYSQL_ERROR::WARN_LEVEL_NOTE && !(thd->options & OPTION_SQL_NOTES)) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index eb74144c1ea..b6ff0ecc216 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -258,6 +258,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->security_ctx->master_access & SUPER_ACL)); bool transactional_table, joins_freed= FALSE; + bool changed; uint value_count; ulong counter = 1; ulonglong id; @@ -544,32 +545,30 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, else if (table->next_number_field && info.copied) id=table->next_number_field->val_int(); // Return auto_increment value - /* - Invalidate the table in the query cache if something changed. - For the transactional algorithm to work the invalidation must be - before binlog writing and ha_autocommit_or_rollback - */ - if (info.copied || info.deleted || info.updated) - { - query_cache_invalidate3(thd, table_list, 1); - } - transactional_table= table->file->has_transactions(); - if ((info.copied || info.deleted || info.updated) && - (error <= 0 || !transactional_table)) + if ((changed= (info.copied || info.deleted || info.updated))) { - if (mysql_bin_log.is_open()) + /* + Invalidate the table in the query cache if something changed. + For the transactional algorithm to work the invalidation must be + before binlog writing and ha_autocommit_or_rollback + */ + query_cache_invalidate3(thd, table_list, 1); + if (error <= 0 || !transactional_table) { - if (error <= 0) - thd->clear_error(); - Query_log_event qinfo(thd, thd->query, thd->query_length, - transactional_table, FALSE); - if (mysql_bin_log.write(&qinfo) && transactional_table) - error=1; + if (mysql_bin_log.is_open()) + { + if (error <= 0) + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, + transactional_table, FALSE); + if (mysql_bin_log.write(&qinfo) && transactional_table) + error=1; + } + if (!transactional_table) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } - if (!transactional_table) - thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } if (transactional_table) error=ha_autocommit_or_rollback(thd,error); @@ -577,6 +576,16 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, if (thd->lock) { mysql_unlock_tables(thd, thd->lock); + /* + Invalidate the table in the query cache if something changed + after unlocking when changes become fisible. + TODO: this is workaround. right way will be move invalidating in + the unlock procedure. + */ + if (lock_type == TL_WRITE_CONCURRENT_INSERT && changed) + { + query_cache_invalidate3(thd, table_list, 1); + } thd->lock=0; } } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 1ed8887a878..2b31abd6a50 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -17,6 +17,7 @@ /* A lexical scanner on a temporary buffer with a yacc interface */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "item_create.h" #include <m_ctype.h> @@ -223,7 +224,7 @@ static int find_keyword(LEX *lex, uint len, bool function) SYNOPSIS is_keyword() - name checked name + name checked name (must not be empty) len length of checked name RETURN VALUES @@ -233,6 +234,7 @@ static int find_keyword(LEX *lex, uint len, bool function) bool is_keyword(const char *name, uint len) { + DBUG_ASSERT(len != 0); return get_hash_symbol(name,len,0)!=0; } @@ -505,14 +507,14 @@ static inline uint int_token(const char *str,uint length) } /* - yylex remember the following states from the following yylex() + MYSQLlex remember the following states from the following MYSQLlex() - MY_LEX_EOQ Found end of query - MY_LEX_OPERATOR_OR_IDENT Last state was an ident, text or number (which can't be followed by a signed number) */ -int yylex(void *arg, void *yythd) +int MYSQLlex(void *arg, void *yythd) { reg1 uchar c; int tokval, result_state; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7b2ea359fb2..8512c075f69 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -37,8 +37,12 @@ class sp_pcontext; #define LEX_YYSTYPE void * #else #include "lex_symbol.h" +#if MYSQL_LEX #include "sql_yacc.h" #define LEX_YYSTYPE YYSTYPE * +#else +#define LEX_YYSTYPE void * +#endif #endif /* @@ -712,7 +716,7 @@ typedef struct st_lex uchar *buf; /* The beginning of string, used by SPs */ uchar *ptr,*tok_start,*tok_end,*end_of_query; - /* The values of tok_start/tok_end as they were one call of yylex before */ + /* The values of tok_start/tok_end as they were one call of MYSQLlex before */ uchar *tok_start_prev, *tok_end_prev; char *length,*dec,*change,*name; @@ -909,12 +913,16 @@ typedef struct st_lex SQL_LIST trg_table_fields; /* - trigger_definition_begin points to the beginning of the word "TRIGGER" in - CREATE TRIGGER statement. This is used to add possibly omitted DEFINER - clause to the trigger definition statement before dumping it to the - binlog. + stmt_definition_begin is intended to point to the next word after + DEFINER-clause in the following statements: + - CREATE TRIGGER (points to "TRIGGER"); + - CREATE PROCEDURE (points to "PROCEDURE"); + - CREATE FUNCTION (points to "FUNCTION" or "AGGREGATE"); + + This pointer is required to add possibly omitted DEFINER-clause to the + DDL-statement before dumping it to the binlog. */ - const char *trigger_definition_begin; + const char *stmt_definition_begin; /* If non-0 then indicates that query requires prelocking and points to @@ -1057,7 +1065,7 @@ extern void lex_init(void); extern void lex_free(void); extern void lex_start(THD *thd, uchar *buf,uint length); extern void lex_end(LEX *lex); -extern int yylex(void *arg, void *yythd); +extern int MYSQLlex(void *arg, void *yythd); extern pthread_key(LEX*,THR_LEX); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 978cab704c0..f9d04fc873e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "sql_repl.h" #include "repl_failsafe.h" @@ -1724,13 +1725,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, net->no_send_error= 0; /* Multiple queries exits, execute them individually - in embedded server - just store them to be executed later */ -#ifndef EMBEDDED_LIBRARY if (thd->lock || thd->open_tables || thd->derived_tables || thd->prelocked_mode) close_thread_tables(thd); -#endif ulong length= (ulong)(packet_end-packet); log_slow_statement(thd); @@ -1748,25 +1746,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_time(); /* Reset the query start time. */ /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); -#ifndef EMBEDDED_LIBRARY mysql_parse(thd, packet, length); -#else - /* - 'packet' can point inside the query_rest's buffer - so we have to do memmove here - */ - if (thd->query_rest.length() > length) - { - memmove(thd->query_rest.c_ptr(), packet, length); - thd->query_rest.length(length); - } - else - thd->query_rest.copy(packet, length, thd->query_rest.charset()); - - thd->server_status&= ~ (SERVER_QUERY_NO_INDEX_USED | - SERVER_QUERY_NO_GOOD_INDEX_USED); - break; -#endif /*EMBEDDED_LIBRARY*/ } if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -3334,6 +3314,19 @@ end_with_restore_list: select_lex->context.table_list= select_lex->context.first_name_resolution_table= second_table; res= handle_select(thd, lex, result, OPTION_SETUP_TABLES_DONE); + /* + Invalidate the table in the query cache if something changed + after unlocking when changes become visible. + TODO: this is workaround. right way will be move invalidating in + the unlock procedure. + */ + if (first_table->lock_type == TL_WRITE_CONCURRENT_INSERT && + thd->lock) + { + mysql_unlock_tables(thd, thd->lock); + query_cache_invalidate3(thd, first_table, 1); + thd->lock=0; + } delete result; } /* revert changes for SP */ @@ -4167,6 +4160,90 @@ end_with_restore_list: #endif /* + If the definer is not specified, this means that CREATE-statement missed + DEFINER-clause. DEFINER-clause can be missed in two cases: + + - The user submitted a statement w/o the clause. This is a normal + case, we should assign CURRENT_USER as definer. + + - Our slave received an updated from the master, that does not + replicate definer for stored rountines. We should also assign + CURRENT_USER as definer here, but also we should mark this routine + as NON-SUID. This is essential for the sake of backward + compatibility. + + The problem is the slave thread is running under "special" user (@), + that actually does not exist. In the older versions we do not fail + execution of a stored routine if its definer does not exist and + continue the execution under the authorization of the invoker + (BUG#13198). And now if we try to switch to slave-current-user (@), + we will fail. + + Actually, this leads to the inconsistent state of master and + slave (different definers, different SUID behaviour), but it seems, + this is the best we can do. + */ + + if (!lex->definer) + { + bool res= FALSE; + Query_arena original_arena; + Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); + + if (!(lex->definer= create_default_definer(thd))) + res= TRUE; + + if (ps_arena) + thd->restore_active_arena(ps_arena, &original_arena); + + if (res) + { + /* Error has been already reported. */ + delete lex->sphead; + lex->sphead= 0; + goto error; + } + + if (thd->slave_thread) + lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; + } + + /* + If the specified definer differs from the current user, we should check + that the current user has SUPER privilege (in order to create a stored + routine under another user one must have SUPER privilege). + */ + + else if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || + my_strcasecmp(system_charset_info, + lex->definer->host.str, + thd->security_ctx->priv_host)) + { + if (check_global_access(thd, SUPER_ACL)) + { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + delete lex->sphead; + lex->sphead= 0; + goto error; + } + } + + /* Check that the specified definer exists. Emit a warning if not. */ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (!is_acl_user(lex->definer->host.str, + lex->definer->user.str)) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_SUCH_USER, + ER(ER_NO_SUCH_USER), + lex->definer->user.str, + lex->definer->host.str); + } +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + + /* We need to copy name and db in order to use them for check_routine_access which is called after lex->sphead has been deleted. @@ -4180,7 +4257,7 @@ end_with_restore_list: /* We must cleanup the unit and the lex here because sp_grant_privileges calls (indirectly) db_find_routine, - which in turn may call yyparse with THD::lex. + which in turn may call MYSQLparse with THD::lex. TODO: fix db_find_routine to use a temporary lex. */ lex->unit.cleanup(); @@ -4273,10 +4350,8 @@ end_with_restore_list: goto error; } -#ifndef EMBEDDED_LIBRARY my_bool nsok= thd->net.no_send_ok; thd->net.no_send_ok= TRUE; -#endif if (sp->m_flags & sp_head::MULTI_RESULTS) { if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS)) @@ -4286,9 +4361,7 @@ end_with_restore_list: back */ my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str); -#ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; -#endif goto error; } /* @@ -4305,18 +4378,14 @@ end_with_restore_list: sp->m_db.str, sp->m_name.str, TRUE, 0) || sp_change_security_context(thd, sp, &save_ctx)) { -#ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; -#endif goto error; } if (save_ctx && check_routine_access(thd, EXECUTE_ACL, sp->m_db.str, sp->m_name.str, TRUE, 0)) { -#ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; -#endif sp_restore_security_context(thd, save_ctx); goto error; } @@ -4348,9 +4417,7 @@ end_with_restore_list: sp_restore_security_context(thd, save_ctx); #endif -#ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; -#endif thd->server_status&= ~bits_to_be_cleared; if (!res) @@ -4846,7 +4913,9 @@ end_with_restore_list: res= mysql_xa_recover(thd); break; default: +#ifndef EMBEDDED_LIBRARY DBUG_ASSERT(0); /* Impossible */ +#endif send_ok(thd); break; } @@ -5603,7 +5672,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_func_cache); - if (!yyparse((void *)thd) && ! thd->is_fatal_error) + if (!MYSQLparse((void *)thd) && ! thd->is_fatal_error) { #ifndef NO_EMBEDDED_ACCESS_CHECKS if (mqh_used && thd->user_connect && @@ -5683,7 +5752,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) DBUG_ENTER("mysql_test_parse_for_slave"); mysql_init_query(thd, (uchar*) inBuf, length); - if (!yyparse((void*) thd) && ! thd->is_fatal_error && + if (!MYSQLparse((void*) thd) && ! thd->is_fatal_error && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ thd->end_statement(); @@ -6008,10 +6077,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, /* table_list.next points to the last inserted TABLE_LIST->next_local' element + We don't use the offsetof() macro here to avoid warnings from gcc */ - previous_table_ref= (TABLE_LIST*) (table_list.next - - offsetof(TABLE_LIST, next_local)); - DBUG_ASSERT(previous_table_ref); + previous_table_ref= (TABLE_LIST*) ((char*) table_list.next - + ((char*) &(ptr->next_local) - + (char*) ptr)); /* Set next_name_resolution_table of the previous table reference to point to the current table reference. In effect the list @@ -7196,47 +7266,54 @@ Item *negate_expression(THD *thd, Item *expr) /* Set the specified definer to the default value, which is the current user in - the thread. Also check that the current user satisfies to the definers - requirements. + the thread. SYNOPSIS get_default_definer() thd [in] thread handler definer [out] definer - - RETURN - error status, that is: - - FALSE -- on success; - - TRUE -- on error (current user can not be a definer). */ -bool get_default_definer(THD *thd, LEX_USER *definer) +void get_default_definer(THD *thd, LEX_USER *definer) { - /* Check that current user has non-empty host name. */ - const Security_context *sctx= thd->security_ctx; - if (sctx->priv_host[0] == 0) - { - my_error(ER_MALFORMED_DEFINER, MYF(0)); - return TRUE; - } - - /* Fill in. */ - definer->user.str= (char *) sctx->priv_user; definer->user.length= strlen(definer->user.str); definer->host.str= (char *) sctx->priv_host; definer->host.length= strlen(definer->host.str); +} - return FALSE; + +/* + Create default definer for the specified THD. + + SYNOPSIS + create_default_definer() + thd [in] thread handler + + RETURN + On success, return a valid pointer to the created and initialized + LEX_USER, which contains definer information. + On error, return 0. +*/ + +LEX_USER *create_default_definer(THD *thd) +{ + LEX_USER *definer; + + if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) + return 0; + + get_default_definer(thd, definer); + + return definer; } /* - Create definer with the given user and host names. Also check that the user - and host names satisfy definers requirements. + Create definer with the given user and host names. SYNOPSIS create_definer() @@ -7246,7 +7323,7 @@ bool get_default_definer(THD *thd, LEX_USER *definer) RETURN On success, return a valid pointer to the created and initialized - LEX_STRING, which contains definer information. + LEX_USER, which contains definer information. On error, return 0. */ @@ -7254,14 +7331,6 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) { LEX_USER *definer; - /* Check that specified host name is valid. */ - - if (host_name->length == 0) - { - my_error(ER_MALFORMED_DEFINER, MYF(0)); - return 0; - } - /* Create and initialize. */ if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index fbc767688b8..0f4ae04962b 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -92,6 +92,12 @@ public: virtual bool send_fields(List<Item> &list, uint flags); virtual bool send_data(List<Item> &items); virtual bool send_eof(); +#ifdef EMBEDDED_LIBRARY + void begin_dataset() + { + protocol.begin_dataset(); + } +#endif }; /****************************************************************************** @@ -524,9 +530,10 @@ void set_param_time(Item_param *param, uchar **pos, ulong len) void set_param_datetime(Item_param *param, uchar **pos, ulong len) { - MYSQL_TIME *to= (MYSQL_TIME*)*pos; + MYSQL_TIME tm= *((MYSQL_TIME*)*pos); + tm.neg= 0; - param->set_time(to, MYSQL_TIMESTAMP_DATETIME, + param->set_time(&tm, MYSQL_TIMESTAMP_DATETIME, MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } @@ -1135,7 +1142,7 @@ static int mysql_test_update(Prepared_statement *stmt, break; if (!need_reopen) goto error; - close_tables_for_reopen(thd, table_list); + close_tables_for_reopen(thd, &table_list); } /* @@ -2750,7 +2757,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lex->safe_to_cache_query= FALSE; lex->stmt_prepare_mode= TRUE; - error= yyparse((void *)thd) || thd->is_fatal_error || + error= MYSQLparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(this); /* While doing context analysis of the query (in check_prepared_statement) diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 80fcb973028..74951029de9 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -19,6 +19,7 @@ */ #include "mysql_priv.h" +#include "sql_trigger.h" static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, @@ -176,8 +177,26 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) if (table_type == DB_TYPE_UNKNOWN) my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); else - rc= mysql_rename_table(table_type, ren_table->db, old_alias, - new_table->db, new_alias); + { + if (!(rc= mysql_rename_table(table_type, ren_table->db, old_alias, + new_table->db, new_alias))) + { + if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db, + old_alias, + new_table->db, + new_alias))) + { + /* + We've succeeded in renaming table's .frm and in updating + corresponding handler data, but have failed to update table's + triggers appropriately. So let us revert operations on .frm + and handler's data and report about failure to rename table. + */ + (void) mysql_rename_table(table_type, new_table->db, new_alias, + ren_table->db, old_alias); + } + } + } break; } case FRMTYPE_VIEW: diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9160c023576..6e530b58d74 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -364,22 +364,8 @@ JOIN::prepare(Item ***rref_pointer_array, select_lex->having_fix_field= 0; if (having_fix_rc || thd->net.report_error) DBUG_RETURN(-1); /* purecov: inspected */ - if (having->with_sum_func) - having->split_sum_func2(thd, ref_pointer_array, all_fields, - &having, TRUE); thd->lex->allow_sum_func= save_allow_sum_func; } - if (select_lex->inner_sum_func_list) - { - Item_sum *end=select_lex->inner_sum_func_list; - Item_sum *item_sum= end; - do - { - item_sum= item_sum->next; - item_sum->split_sum_func2(thd, ref_pointer_array, - all_fields, item_sum->ref_by, FALSE); - } while (item_sum != end); - } if (!thd->lex->view_prepare_mode) { @@ -397,6 +383,21 @@ JOIN::prepare(Item ***rref_pointer_array, } } + if (having && having->with_sum_func) + having->split_sum_func2(thd, ref_pointer_array, all_fields, + &having, TRUE); + if (select_lex->inner_sum_func_list) + { + Item_sum *end=select_lex->inner_sum_func_list; + Item_sum *item_sum= end; + do + { + item_sum= item_sum->next; + item_sum->split_sum_func2(thd, ref_pointer_array, + all_fields, item_sum->ref_by, FALSE); + } while (item_sum != end); + } + if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ DBUG_RETURN(-1); @@ -7065,7 +7066,10 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, if (!cond) cond= new Item_cond_and(eq_list); else + { + DBUG_ASSERT(cond->type() == Item::COND_ITEM); ((Item_cond *) cond)->add_at_head(&eq_list); + } cond->quick_fix_field(); cond->update_used_tables(); @@ -7150,6 +7154,11 @@ static COND* substitute_for_best_equal_field(COND *cond, while ((item_equal= it++)) { cond= eliminate_item_equal(cond, cond_equal->upper_levels, item_equal); + // This occurs when eliminate_item_equal() founds that cond is + // always false and substitues it with Item_int 0. + // Due to this, value of item_equal will be 0, so just return it. + if (cond->type() != Item::COND_ITEM) + break; } } } @@ -12746,11 +12755,12 @@ calc_group_buffer(JOIN *join,ORDER *group) Field *field= group_item->get_tmp_table_field(); if (field) { - if (field->type() == FIELD_TYPE_BLOB) + enum_field_types type; + if ((type= field->type()) == FIELD_TYPE_BLOB) key_length+=MAX_BLOB_WIDTH; // Can't be used as a key - else if (field->type() == MYSQL_TYPE_VARCHAR) + else if (type == MYSQL_TYPE_VARCHAR || type == MYSQL_TYPE_VAR_STRING) key_length+= field->field_length + HA_KEY_BLOB_LENGTH; - else if (field->type() == FIELD_TYPE_BIT) + else if (type == FIELD_TYPE_BIT) { /* Bit is usually stored as a longlong key for group fields */ key_length+= 8; // Big enough diff --git a/sql/sql_select.h b/sql/sql_select.h index 9046398faaf..01ed8048e4a 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -104,6 +104,7 @@ typedef int (*Read_record_func)(struct st_join_table *tab); Next_select_func setup_end_select_func(JOIN *join); typedef struct st_join_table { + st_join_table() {} /* Remove gcc warning */ TABLE *table; KEYUSE *keyuse; /* pointer to first used key */ SQL_SELECT *select; @@ -174,7 +175,9 @@ typedef struct st_rollup class JOIN :public Sql_alloc { - public: + JOIN(const JOIN &rhs); /* not implemented */ + JOIN& operator=(const JOIN &rhs); /* not implemented */ +public: JOIN_TAB *join_tab,**best_ref; JOIN_TAB **map2table; // mapping between table indexes and JOIN_TABs JOIN_TAB *join_tab_save; // saved join_tab for subquery reexecution @@ -286,13 +289,6 @@ class JOIN :public Sql_alloc init(thd_arg, fields_arg, select_options_arg, result_arg); } - JOIN(JOIN &join) - :fields_list(join.fields_list) - { - init(join.thd, join.fields_list, join.select_options, - join.result); - } - void init(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg, select_result *result_arg) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 1b854a005ce..6350fceae74 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -328,7 +328,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, table_list.table_name= file->name; table_list.table_name_length= strlen(file->name); table_list.grant.privilege=col_access; - if (check_grant(thd, TABLE_ACLS, &table_list, 1, UINT_MAX, 1)) + if (check_grant(thd, TABLE_ACLS, &table_list, 1, 1, 1)) continue; } #endif @@ -365,12 +365,15 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) { if (!table_list->view || thd->net.last_errno != ER_VIEW_INVALID) DBUG_RETURN(TRUE); + /* Clear all messages with 'error' level status and issue a warning with 'warning' level status in case of invalid view and last error is ER_VIEW_INVALID */ mysql_reset_errors(thd, true); + thd->clear_error(); + push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN, ER_VIEW_INVALID, ER(ER_VIEW_INVALID), @@ -645,6 +648,18 @@ static const char *require_quotes(const char *name, uint name_length) } +/* + Quote the given identifier if needed and append it to the target string. + If the given identifier is empty, it will be quoted. + + SYNOPSIS + append_identifier() + thd thread handler + packet target string + name the identifier to be appended + name_length length of the appending identifier +*/ + void append_identifier(THD *thd, String *packet, const char *name, uint length) { @@ -698,8 +713,11 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) length length of name IMPLEMENTATION - If name is a keyword or includes a special character, then force - quoting. + Force quoting in the following cases: + - name is empty (for one, it is possible when we use this function for + quoting user and host names for DEFINER clause); + - name is a keyword; + - name includes a special character; Otherwise identifier is quoted only if the option OPTION_QUOTE_SHOW_CREATE is set. @@ -710,7 +728,8 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) int get_quote_char_for_identifier(THD *thd, const char *name, uint length) { - if (!is_keyword(name,length) && + if (length && + !is_keyword(name,length) && !require_quotes(name, length) && !(thd->options & OPTION_QUOTE_SHOW_CREATE)) return EOF; @@ -3845,7 +3864,8 @@ bool get_schema_tables_result(JOIN *join) TABLE_LIST *table_list= tab->table->pos_in_table_list; if (table_list->schema_table && thd->fill_derived_tables()) { - bool is_subselect= (&lex->unit != lex->current_select->master_unit()); + bool is_subselect= (&lex->unit != lex->current_select->master_unit() && + lex->current_select->master_unit()->item); /* The schema table is already processed and the statement is not a subselect. diff --git a/sql/sql_string.cc b/sql/sql_string.cc index fd7bca7ec21..79228be8a76 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -819,8 +819,18 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, from++; wc= '?'; } + else if (cnvres > MY_CS_TOOSMALL) + { + /* + A correct multibyte sequence detected + But it doesn't have Unicode mapping. + */ + error_count++; + from+= (-cnvres); + wc= '?'; + } else - break; // Impossible char. + break; // Not enough characters outp: if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b1a24543a6b..4975bbaaaf1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -688,6 +688,12 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, { CHARSET_INFO *save_cs; + /* + Initialize length from its original value (number of characters), + which was set in the parser. This is necessary if we're + executing a prepared statement for the second time. + */ + sql_field->length= sql_field->char_length; if (!sql_field->charset) sql_field->charset= create_info->default_table_charset; /* @@ -872,7 +878,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->charset= (dup_field->charset ? dup_field->charset : create_info->default_table_charset); - sql_field->length= dup_field->chars_length; + sql_field->length= dup_field->char_length; sql_field->pack_length= dup_field->pack_length; sql_field->key_length= dup_field->key_length; sql_field->create_length_to_internal_length(); @@ -1299,7 +1305,9 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, } if (length > file->max_key_part_length() && key->type != Key::FULLTEXT) { - length=file->max_key_part_length(); + length= file->max_key_part_length(); + /* Align key length to multibyte char boundary */ + length-= length % sql_field->charset->mbmaxlen; if (key->type == Key::MULTIPLE) { /* not a critical problem */ @@ -2328,7 +2336,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, open_for_modify= 0; } - if (table->table->s->crashed && operator_func == &handler::check) + if (table->table->s->crashed && operator_func == &handler::ha_check) { protocol->prepare_for_resend(); protocol->store(table_name, system_charset_info); @@ -2340,6 +2348,21 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, goto err; } + if (operator_func == &handler::ha_repair) + { + if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) || + (table->table->file->ha_check_for_upgrade(check_opt) == + HA_ADMIN_NEEDS_ALTER)) + { + close_thread_tables(thd); + tmp_disable_binlog(thd); // binlogging is done by caller if wanted + result_code= mysql_recreate_table(thd, table, 0); + reenable_binlog(thd); + goto send_result; + } + + } + result_code = (table->table->file->*operator_func)(thd, check_opt); send_result: @@ -2466,6 +2489,19 @@ send_result_message: break; } + case HA_ADMIN_NEEDS_UPGRADE: + case HA_ADMIN_NEEDS_ALTER: + { + char buf[ERRMSGSIZE]; + uint length; + + protocol->store(STRING_WITH_LEN("error"), system_charset_info); + length=my_snprintf(buf, ERRMSGSIZE, ER(ER_TABLE_NEEDS_UPGRADE), table->table_name); + protocol->store(buf, length, system_charset_info); + fatal_error=1; + break; + } + default: // Probably HA_ADMIN_INTERNAL_ERROR { char buf[ERRMSGSIZE+20]; @@ -2535,7 +2571,7 @@ bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) test(check_opt->sql_flags & TT_USEFRM), HA_OPEN_FOR_REPAIR, &prepare_for_repair, - &handler::repair, 0)); + &handler::ha_repair, 0)); } @@ -2847,7 +2883,7 @@ bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt) DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, "check", lock_type, 0, HA_OPEN_FOR_REPAIR, 0, 0, - &handler::check, &view_checksum)); + &handler::ha_check, &view_checksum)); } @@ -3146,6 +3182,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, uint db_create_options, used_fields; enum db_type old_db_type,new_db_type; bool need_copy_table; + bool no_table_reopen= FALSE; DBUG_ENTER("mysql_alter_table"); thd->proc_info="init"; @@ -3259,6 +3296,13 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, close_cached_table(thd, table); if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias)) error= -1; + else if (Table_triggers_list::change_table_name(thd, db, table_name, + new_db, new_alias)) + { + VOID(mysql_rename_table(old_db_type, new_db, new_alias, db, + table_name)); + error= -1; + } } VOID(pthread_mutex_unlock(&LOCK_open)); } @@ -3788,9 +3832,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Win32 and InnoDB can't drop a table that is in use, so we must close the original table at before doing the rename */ - table_name=thd->strdup(table_name); // must be saved close_cached_table(thd, table); table=0; // Marker that table is closed + no_table_reopen= TRUE; } #if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2)) else @@ -3807,7 +3851,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(quick_rm_table(new_db_type,new_db,tmp_name)); } else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db, - new_alias)) + new_alias) || + (new_name != table_name || new_db != db) && // we also do rename + Table_triggers_list::change_table_name(thd, db, table_name, + new_db, new_alias)) + { // Try to get everything back error=1; VOID(quick_rm_table(new_db_type,new_db,new_alias)); @@ -3825,7 +3873,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(pthread_mutex_unlock(&LOCK_open)); goto err; } - if (thd->lock || new_name != table_name) // True if WIN32 + if (thd->lock || new_name != table_name || no_table_reopen) // True if WIN32 { /* Not table locking or alter table with rename @@ -4218,7 +4266,8 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) for (uint i= 0; i < t->s->fields; i++ ) { Field *f= t->field[i]; - if (f->type() == FIELD_TYPE_BLOB) + if ((f->type() == FIELD_TYPE_BLOB) || + (f->type() == MYSQL_TYPE_VARCHAR)) { String tmp; f->val_str(&tmp); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 53743314782..f943b014118 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -15,6 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "sp_head.h" #include "sql_trigger.h" @@ -65,7 +66,6 @@ File_option sql_modes_parameters= */ static const int TRG_NUM_REQUIRED_PARAMETERS= 4; -static const int TRG_MAX_VERSIONS= 3; /* Structure representing contents of .TRN file which are used to support @@ -87,7 +87,7 @@ static File_option trigname_file_parameters[]= { {(char *) STRING_WITH_LEN("trigger_table")}, offsetof(struct st_trigname, trigger_table), - FILE_OPTIONS_ESTRING + FILE_OPTIONS_ESTRING }, { { 0, 0 }, 0, FILE_OPTIONS_STRING } }; @@ -109,10 +109,6 @@ const LEX_STRING trg_event_type_names[]= static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig); -bool handle_old_incorrect_sql_modes(char *&unknown_key, gptr base, - MEM_ROOT *mem_root, - char *end, gptr hook_data); - class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook { private: @@ -125,6 +121,20 @@ public: MEM_ROOT *mem_root, char *end); }; +class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook +{ +public: + Handle_old_incorrect_trigger_table_hook(char *file_path, + LEX_STRING *trigger_table_arg) + :path(file_path), trigger_table_value(trigger_table_arg) + {}; + virtual bool process_unknown_string(char *&unknown_key, gptr base, + MEM_ROOT *mem_root, char *end); +private: + char *path; + LEX_STRING *trigger_table_value; +}; + /* Create or drop trigger for table. @@ -265,8 +275,18 @@ end: log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */ log_query.append(STRING_WITH_LEN("CREATE ")); - append_definer(thd, &log_query, &definer_user, &definer_host); - log_query.append(thd->lex->trigger_definition_begin); + + if (definer_user.str && definer_host.str) + { + /* + Append definer-clause if the trigger is SUID (a usual trigger in + new MySQL versions). + */ + + append_definer(thd, &log_query, &definer_user, &definer_host); + } + + log_query.append(thd->lex->stmt_definition_begin); } /* Such a statement can always go directly to binlog, no trans cache. */ @@ -290,17 +310,30 @@ end: LEX) tables - table list containing one open table for which the trigger is created. - definer_user - [out] after a call it points to 0-terminated string, - which contains user name part of the actual trigger - definer. The caller is responsible to provide memory for + definer_user - [out] after a call it points to 0-terminated string or + contains the NULL-string: + - 0-terminated is returned if the trigger is SUID. The + string contains user name part of the actual trigger + definer. + - NULL-string is returned if the trigger is non-SUID. + Anyway, the caller is responsible to provide memory for storing LEX_STRING object. - definer_host - [out] after a call it points to 0-terminated string, - which contains host name part of the actual trigger - definer. The caller is responsible to provide memory for + definer_host - [out] after a call it points to 0-terminated string or + contains the NULL-string: + - 0-terminated string is returned if the trigger is + SUID. The string contains host name part of the + actual trigger definer. + - NULL-string is returned if the trigger is non-SUID. + Anyway, the caller is responsible to provide memory for storing LEX_STRING object. NOTE - Assumes that trigger name is fully qualified. + - Assumes that trigger name is fully qualified. + - NULL-string means the following LEX_STRING instance: + { str = 0; length = 0 }. + - In other words, definer_user and definer_host should contain + simultaneously NULL-strings (non-SUID/old trigger) or valid strings + (SUID/new trigger). RETURN VALUE False - success @@ -337,12 +370,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, return 1; } - /* - Definer attribute of the Lex instance is always set in sql_yacc.yy when - trigger is created. - */ + if (!lex->definer) + { + /* + DEFINER-clause is missing. - DBUG_ASSERT(lex->definer); + If we are in slave thread, this means that we received CREATE TRIGGER + from the master, that does not support definer in triggers. So, we + should mark this trigger as non-SUID. Note that this does not happen + when we parse triggers' definitions during opening .TRG file. + LEX::definer is ignored in that case. + + Otherwise, we should use CURRENT_USER() as definer. + + NOTE: when CREATE TRIGGER statement is allowed to be executed in PS/SP, + it will be required to create the definer below in persistent MEM_ROOT + of PS/SP. + */ + + if (!thd->slave_thread) + { + if (!(lex->definer= create_default_definer(thd))) + return 1; + } + } /* If the specified definer differs from the current user, we should check @@ -350,10 +401,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, under another user one must have SUPER privilege). */ - if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || - my_strcasecmp(system_charset_info, - lex->definer->host.str, - thd->security_ctx->priv_host)) + if (lex->definer && + (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || + my_strcasecmp(system_charset_info, + lex->definer->host.str, + thd->security_ctx->priv_host))) { if (check_global_access(thd, SUPER_ACL)) { @@ -447,8 +499,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, *trg_sql_mode= thd->variables.sql_mode; #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!is_acl_user(lex->definer->host.str, - lex->definer->user.str)) + if (lex->definer && !is_acl_user(lex->definer->host.str, + lex->definer->user.str)) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, @@ -459,16 +511,33 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, } #endif /* NO_EMBEDDED_ACCESS_CHECKS */ - *definer_user= lex->definer->user; - *definer_host= lex->definer->host; + if (lex->definer) + { + /* SUID trigger. */ + + *definer_user= lex->definer->user; + *definer_host= lex->definer->host; + + trg_definer->str= trg_definer_holder; + trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@", + definer_host->str, NullS) - trg_definer->str; + } + else + { + /* non-SUID trigger. */ + + definer_user->str= 0; + definer_user->length= 0; + + definer_host->str= 0; + definer_host->length= 0; - trg_definer->str= trg_definer_holder; - trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@", - definer_host->str, NullS) - trg_definer->str; + trg_definer->str= (char*) ""; + trg_definer->length= 0; + } if (!sql_create_definition_file(&dir, &file, &triggers_file_type, - (gptr)this, triggers_file_parameters, - TRG_MAX_VERSIONS)) + (gptr)this, triggers_file_parameters, 0)) return 0; err_with_cleanup: @@ -492,7 +561,8 @@ err_with_cleanup: True - error */ -static bool rm_trigger_file(char *path, char *db, char *table_name) +static bool rm_trigger_file(char *path, const char *db, + const char *table_name) { strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", table_name, triggers_file_ext, NullS); @@ -516,7 +586,8 @@ static bool rm_trigger_file(char *path, char *db, char *table_name) True - error */ -static bool rm_trigname_file(char *path, char *db, char *trigger_name) +static bool rm_trigname_file(char *path, const char *db, + const char *trigger_name) { strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", trigger_name, trigname_file_ext, NullS); @@ -526,6 +597,38 @@ static bool rm_trigname_file(char *path, char *db, char *trigger_name) /* + Helper function that saves .TRG file for Table_triggers_list object. + + SYNOPSIS + save_trigger_file() + triggers Table_triggers_list object for which file should be saved + db Name of database for subject table + table_name Name of subject table + + RETURN VALUE + FALSE Success + TRUE Error +*/ + +static bool save_trigger_file(Table_triggers_list *triggers, const char *db, + const char *table_name) +{ + char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; + LEX_STRING dir, file; + + strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", db, "/", NullS); + dir.length= unpack_filename(dir_buff, dir_buff); + dir.str= dir_buff; + file.length= strxnmov(file_buff, FN_REFLEN, table_name, triggers_file_ext, + NullS) - file_buff; + file.str= file_buff; + + return sql_create_definition_file(&dir, &file, &triggers_file_type, + (gptr)triggers, triggers_file_parameters, 0); +} + + +/* Drop trigger for table. SYNOPSIS @@ -578,20 +681,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) } else { - char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; - LEX_STRING dir, file; - - strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", tables->db, - "/", NullS); - dir.length= unpack_filename(dir_buff, dir_buff); - dir.str= dir_buff; - file.length= strxnmov(file_buff, FN_REFLEN, tables->table_name, - triggers_file_ext, NullS) - file_buff; - file.str= file_buff; - - if (sql_create_definition_file(&dir, &file, &triggers_file_type, - (gptr)this, triggers_file_parameters, - TRG_MAX_VERSIONS)) + if (save_trigger_file(this, tables->db, tables->table_name)) return 1; } @@ -831,12 +921,12 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (!names_only && triggers->prepare_record1_accessors(table)) DBUG_RETURN(1); - char *trg_name_buff; List_iterator_fast<ulonglong> itm(triggers->definition_modes_list); List_iterator_fast<LEX_STRING> it_definer(triggers->definers_list); LEX *old_lex= thd->lex, lex; sp_rcontext *save_spcont= thd->spcont; ulong save_sql_mode= thd->variables.sql_mode; + LEX_STRING *on_table_name; thd->lex= &lex; @@ -853,7 +943,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); thd->spcont= 0; - if (yyparse((void *)thd) || thd->is_fatal_error) + if (MYSQLparse((void *)thd) || thd->is_fatal_error) { /* Free lex associated resources. @@ -902,6 +992,21 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, &table->mem_root)) goto err_with_lex_cleanup; + if (!(on_table_name= (LEX_STRING*) alloc_root(&table->mem_root, + sizeof(LEX_STRING)))) + goto err_with_lex_cleanup; + *on_table_name= lex.ident; + if (triggers->on_table_names_list.push_back(on_table_name, &table->mem_root)) + goto err_with_lex_cleanup; + + /* + Let us check that we correctly update trigger definitions when we + rename tables with triggers. + */ + DBUG_ASSERT(!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) && + !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, + table_name)); + if (names_only) { lex_end(&lex); @@ -1035,6 +1140,9 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) LEX_STRING path; File_parser *parser; struct st_trigname trigname; + Handle_old_incorrect_trigger_table_hook trigger_table_hook( + path_buff, &trigname.trigger_table); + DBUG_ENTER("add_table_for_trigger"); strxnmov(path_buff, FN_REFLEN, mysql_data_home, "/", trig->m_db.str, "/", @@ -1060,14 +1168,14 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) if (parser->parse((gptr)&trigname, thd->mem_root, trigname_file_parameters, 1, - &file_parser_dummy_hook)) + &trigger_table_hook)) DBUG_RETURN(0); /* We need to reset statement table list to be PS/SP friendly. */ lex->query_tables= 0; lex->query_tables_last= &lex->query_tables; DBUG_RETURN(sp_add_to_query_tables(thd, lex, trig->m_db.str, - trigname.trigger_table.str, TL_WRITE)); + trigname.trigger_table.str, TL_IGNORE)); } @@ -1137,6 +1245,225 @@ end: } +/* + Update .TRG file after renaming triggers' subject table + (change name of table in triggers' definitions). + + SYNOPSIS + change_table_name_in_triggers() + thd Thread context + db_name Database of subject table + old_table_name Old subject table's name + new_table_name New subject table's name + + RETURN VALUE + FALSE Success + TRUE Failure +*/ + +bool +Table_triggers_list::change_table_name_in_triggers(THD *thd, + const char *db_name, + LEX_STRING *old_table_name, + LEX_STRING *new_table_name) +{ + char path_buff[FN_REFLEN]; + LEX_STRING *def, *on_table_name, new_def; + ulonglong *sql_mode; + ulong save_sql_mode= thd->variables.sql_mode; + List_iterator_fast<LEX_STRING> it_def(definitions_list); + List_iterator_fast<LEX_STRING> it_on_table_name(on_table_names_list); + List_iterator_fast<ulonglong> it_mode(definition_modes_list); + uint on_q_table_name_len, before_on_len; + String buff; + + DBUG_ASSERT(definitions_list.elements == on_table_names_list.elements && + definitions_list.elements == definition_modes_list.elements); + + while ((def= it_def++)) + { + on_table_name= it_on_table_name++; + thd->variables.sql_mode= *(it_mode++); + + /* Construct CREATE TRIGGER statement with new table name. */ + buff.length(0); + before_on_len= on_table_name->str - def->str; + buff.append(def->str, before_on_len); + buff.append(STRING_WITH_LEN("ON ")); + append_identifier(thd, &buff, new_table_name->str, new_table_name->length); + buff.append(STRING_WITH_LEN(" ")); + on_q_table_name_len= buff.length() - before_on_len; + buff.append(on_table_name->str + on_table_name->length, + def->length - (before_on_len + on_table_name->length)); + /* + It is OK to allocate some memory on table's MEM_ROOT since this + table instance will be thrown out at the end of rename anyway. + */ + new_def.str= memdup_root(&table->mem_root, buff.ptr(), buff.length()); + new_def.length= buff.length(); + on_table_name->str= new_def.str + before_on_len; + on_table_name->length= on_q_table_name_len; + *def= new_def; + } + + thd->variables.sql_mode= save_sql_mode; + + if (thd->is_fatal_error) + return TRUE; /* OOM */ + + if (save_trigger_file(this, db_name, new_table_name->str)) + return TRUE; + if (rm_trigger_file(path_buff, db_name, old_table_name->str)) + { + (void) rm_trigger_file(path_buff, db_name, new_table_name->str); + return TRUE; + } + return FALSE; +} + + +/* + Iterate though Table_triggers_list::names_list list and update .TRN files + after renaming triggers' subject table. + + SYNOPSIS + change_table_name_in_trignames() + db_name Database of subject table + new_table_name New subject table's name + stopper Pointer to Table_triggers_list::names_list at + which we should stop updating. + + RETURN VALUE + 0 Success + non-0 Failure, pointer to Table_triggers_list::names_list element + for which update failed. +*/ + +LEX_STRING* +Table_triggers_list::change_table_name_in_trignames(const char *db_name, + LEX_STRING *new_table_name, + LEX_STRING *stopper) +{ + char dir_buff[FN_REFLEN], trigname_buff[FN_REFLEN]; + struct st_trigname trigname; + LEX_STRING dir, trigname_file; + LEX_STRING *trigger; + List_iterator_fast<LEX_STRING> it_name(names_list); + + strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", db_name, "/", NullS); + dir.length= unpack_filename(dir_buff, dir_buff); + dir.str= dir_buff; + + while ((trigger= it_name++) != stopper) + { + trigname_file.length= strxnmov(trigname_buff, FN_REFLEN, trigger->str, + trigname_file_ext, NullS) - trigname_buff; + trigname_file.str= trigname_buff; + + trigname.trigger_table= *new_table_name; + + if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type, + (gptr)&trigname, trigname_file_parameters, 0)) + return trigger; + } + + return 0; +} + + +/* + Update .TRG and .TRN files after renaming triggers' subject table. + + SYNOPSIS + change_table_name() + thd Thread context + db Old database of subject table + old_table Old name of subject table + new_db New database for subject table + new_table New name of subject table + + NOTE + This method tries to leave trigger related files in consistent state, + i.e. it either will complete successfully, or will fail leaving files + in their initial state. + Also this method assumes that subject table is not renamed to itself. + + RETURN VALUE + FALSE Success + TRUE Error +*/ + +bool Table_triggers_list::change_table_name(THD *thd, const char *db, + const char *old_table, + const char *new_db, + const char *new_table) +{ + TABLE table; + bool result= 0; + LEX_STRING *err_trigname; + DBUG_ENTER("change_table_name"); + + bzero(&table, sizeof(table)); + init_alloc_root(&table.mem_root, 8192, 0); + + safe_mutex_assert_owner(&LOCK_open); + + DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) || + my_strcasecmp(table_alias_charset, old_table, new_table)); + + if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE)) + { + result= 1; + goto end; + } + if (table.triggers) + { + LEX_STRING_WITH_INIT old_table_name(old_table, strlen(old_table)); + LEX_STRING_WITH_INIT new_table_name(new_table, strlen(new_table)); + /* + Since triggers should be in the same schema as their subject tables + moving table with them between two schemas raises too many questions. + (E.g. what should happen if in new schema we already have trigger + with same name ?). + */ + if (my_strcasecmp(table_alias_charset, db, new_db)) + { + my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0)); + result= 1; + goto end; + } + if (table.triggers->change_table_name_in_triggers(thd, db, + &old_table_name, + &new_table_name)) + { + result= 1; + goto end; + } + if ((err_trigname= table.triggers->change_table_name_in_trignames( + db, &new_table_name, 0))) + { + /* + If we were unable to update one of .TRN files properly we will + revert all changes that we have done and report about error. + We assume that we will be able to undo our changes without errors + (we can't do much if there will be an error anyway). + */ + (void) table.triggers->change_table_name_in_trignames(db, + &old_table_name, + err_trigname); + (void) table.triggers->change_table_name_in_triggers(thd, db, + &new_table_name, + &old_table_name); + result= 1; + goto end; + } + } +end: + delete table.triggers; + free_root(&table.mem_root, MYF(0)); + DBUG_RETURN(result); +} + bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, trg_action_time_type time_type, @@ -1228,7 +1555,7 @@ Handle_old_incorrect_sql_modes_hook::process_unknown_string(char *&unknown_key, MEM_ROOT *mem_root, char *end) { - DBUG_ENTER("handle_old_incorrect_sql_modes"); + DBUG_ENTER("Handle_old_incorrect_sql_modes_hook::process_unknown_string"); DBUG_PRINT("info", ("unknown key:%60s", unknown_key)); if (unknown_key + INVALID_SQL_MODES_LENGTH + 1 < end && @@ -1257,3 +1584,44 @@ Handle_old_incorrect_sql_modes_hook::process_unknown_string(char *&unknown_key, } DBUG_RETURN(FALSE); } + +/* + Trigger BUG#15921 compatibility hook. For details see + Handle_old_incorrect_sql_modes_hook::process_unknown_string(). +*/ + +#define INVALID_TRIGGER_TABLE_LENGTH 15 + +bool +Handle_old_incorrect_trigger_table_hook:: +process_unknown_string(char *&unknown_key, gptr base, MEM_ROOT *mem_root, + char *end) +{ + DBUG_ENTER("Handle_old_incorrect_trigger_table_hook::process_unknown_string"); + DBUG_PRINT("info", ("unknown key:%60s", unknown_key)); + + if (unknown_key + INVALID_TRIGGER_TABLE_LENGTH + 1 < end && + unknown_key[INVALID_TRIGGER_TABLE_LENGTH] == '=' && + !memcmp(unknown_key, STRING_WITH_LEN("trigger_table"))) + { + char *ptr= unknown_key + INVALID_TRIGGER_TABLE_LENGTH + 1; + + DBUG_PRINT("info", ("trigger_table affected by BUG#15921 detected")); + push_warning_printf(current_thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_OLD_FILE_FORMAT, + ER(ER_OLD_FILE_FORMAT), + (char *)path, "TRIGGER"); + + if (!(ptr= parse_escaped_string(ptr, end, mem_root, trigger_table_value))) + { + my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), "trigger_table", + unknown_key); + DBUG_RETURN(TRUE); + } + + /* Set parsing pointer to the last symbol of string (\n). */ + unknown_key= ptr-1; + } + DBUG_RETURN(FALSE); +} diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 8992de63b37..b67c22e0588 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -47,6 +47,11 @@ class Table_triggers_list: public Sql_alloc */ List<LEX_STRING> names_list; /* + List of "ON table_name" parts in trigger definitions, used for + updating trigger definitions during RENAME TABLE. + */ + List<LEX_STRING> on_table_names_list; + /* Key representing triggers for this table in set of all stored routines used by statement. TODO: We won't need this member once triggers namespace will be @@ -97,7 +102,10 @@ public: static bool check_n_load(THD *thd, const char *db, const char *table_name, TABLE *table, bool names_only); static bool drop_all_triggers(THD *thd, char *db, char *table_name); - + static bool change_table_name(THD *thd, const char *db, + const char *old_table, + const char *new_db, + const char *new_table); bool has_delete_triggers() { return (bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] || @@ -117,6 +125,13 @@ public: private: bool prepare_record1_accessors(TABLE *table); + LEX_STRING* change_table_name_in_trignames(const char *db_name, + LEX_STRING *new_table_name, + LEX_STRING *stopper); + bool change_table_name_in_triggers(THD *thd, + const char *db_name, + LEX_STRING *old_table_name, + LEX_STRING *new_table_name); }; extern const LEX_STRING trg_action_time_type_names[]; diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 40e5a9a00cf..6269c0a2eb3 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -308,6 +308,10 @@ static void del_udf(udf_func *udf) void free_udf(udf_func *udf) { DBUG_ENTER("free_udf"); + + if (!initialized) + DBUG_VOID_RETURN; + rw_wrlock(&THR_LOCK_udf); if (!--udf->usage_count) { @@ -332,6 +336,9 @@ udf_func *find_udf(const char *name,uint length,bool mark_used) udf_func *udf=0; DBUG_ENTER("find_udf"); + if (!initialized) + DBUG_RETURN(NULL); + /* TODO: This should be changed to reader locks someday! */ if (mark_used) rw_wrlock(&THR_LOCK_udf); /* Called during fix_fields */ @@ -432,6 +439,7 @@ int mysql_create_function(THD *thd,udf_func *udf) } if (!(dl = find_udf_dl(udf->dl))) { + DBUG_PRINT("info", ("Calling dlopen, udf->dl: %s", udf->dl)); if (!(dl = dlopen(udf->dl, RTLD_NOW))) { DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)", diff --git a/sql/sql_update.cc b/sql/sql_update.cc index d319167b61d..bfdd986f576 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -158,7 +158,7 @@ int mysql_update(THD *thd, break; if (!need_reopen) DBUG_RETURN(1); - close_tables_for_reopen(thd, table_list); + close_tables_for_reopen(thd, &table_list); } if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || @@ -823,7 +823,7 @@ reopen_tables: for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global) tbl->cleanup_items(); - close_tables_for_reopen(thd, table_list); + close_tables_for_reopen(thd, &table_list); goto reopen_tables; } @@ -1303,7 +1303,7 @@ bool multi_update::send_data(List<Item> ¬_used_values) memcpy((char*) tmp_table->field[0]->ptr, (char*) table->file->ref, table->file->ref_length); /* Write row, ignoring duplicated updates to a row */ - if (error= tmp_table->file->write_row(tmp_table->record[0])) + if ((error= tmp_table->file->write_row(tmp_table->record[0]))) { if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE && diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 4f62a80cfd4..39d1ae5c9fb 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -15,6 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "sql_select.h" #include "parse_file.h" @@ -208,6 +209,26 @@ bool mysql_create_view(THD *thd, if (mode != VIEW_CREATE_NEW) sp_cache_invalidate(); + if (!lex->definer) + { + /* + DEFINER-clause is missing; we have to create default definer in + persistent arena to be PS/SP friendly. + */ + + Query_arena original_arena; + Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); + + if (!(lex->definer= create_default_definer(thd))) + res= TRUE; + + if (ps_arena) + thd->restore_active_arena(ps_arena, &original_arena); + + if (res) + goto err; + } + #ifndef NO_EMBEDDED_ACCESS_CHECKS /* check definer of view: @@ -815,8 +836,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER), table->db, table->table_name); - if (get_default_definer(thd, &table->definer)) - goto err; + get_default_definer(thd, &table->definer); } /* @@ -870,7 +890,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); CHARSET_INFO *save_cs= thd->variables.character_set_client; thd->variables.character_set_client= system_charset_info; - res= yyparse((void *)thd); + res= MYSQLparse((void *)thd); thd->variables.character_set_client= save_cs; thd->variables.sql_mode= save_mode; } @@ -918,6 +938,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) tbl->skip_temporary= 1; tbl->belong_to_view= top_view; tbl->referencing_view= table; + tbl->prelocking_placeholder= table->prelocking_placeholder; /* First we fill want_privilege with SELECT_ACL (this is needed for the tables which belongs to view subqueries and temporary table views, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4f4ec5e92de..ddc267eb970 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -628,6 +628,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token UNTIL_SYM %token UPDATE_SYM %token UPDATE_SYM +%token UPGRADE_SYM %token USAGE %token USER %token USE_FRM @@ -684,7 +685,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal NCHAR_STRING opt_component key_cache_name - sp_opt_label BIN_NUM label_ident + sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem %type <lex_str_ptr> opt_table_alias @@ -777,7 +778,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <symbol> FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword keyword_sp -%type <lex_user> user grant_user get_definer +%type <lex_user> user grant_user %type <charset> opt_collate @@ -832,8 +833,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec definer view_replace_or_algorithm view_replace view_algorithm_opt - view_algorithm view_or_trigger_tail view_suid view_tail view_list_opt - view_list view_select view_check_option trigger_tail + view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail + view_suid view_tail view_list_opt view_list view_select + view_check_option trigger_tail sp_tail END_OF_INPUT %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -1188,81 +1190,13 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE udf_func_type FUNCTION_SYM sp_name - { - LEX *lex=Lex; - lex->spname= $4; - lex->udf.type= $2; - } - create_function_tail - {} - | CREATE PROCEDURE sp_name - { - LEX *lex= Lex; - sp_head *sp; - - if (lex->sphead) - { - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); - YYABORT; - } - /* Order is important here: new - reset - init */ - sp= new sp_head(); - sp->reset_thd_mem_root(YYTHD); - sp->init(lex); - - sp->m_type= TYPE_ENUM_PROCEDURE; - lex->sphead= sp; - /* - * We have to turn of CLIENT_MULTI_QUERIES while parsing a - * stored procedure, otherwise yylex will chop it into pieces - * at each ';'. - */ - sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); - } - '(' - { - LEX *lex= Lex; - - lex->sphead->m_param_begin= lex->tok_start+1; - } - sp_pdparam_list - ')' - { - LEX *lex= Lex; - - lex->sphead->m_param_end= lex->tok_start; - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - } - sp_c_chistics - { - LEX *lex= Lex; - - lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->tok_start; - } - sp_proc_stmt - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - - if (sp->check_backpatch(YYTHD)) - YYABORT; - sp->init_strings(YYTHD, lex, $3); - lex->sql_command= SQLCOM_CREATE_PROCEDURE; - /* Restore flag if it was cleared above */ - if (sp->m_old_cmq) - YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; - sp->restore_thd_mem_root(YYTHD); - } | CREATE { Lex->create_view_mode= VIEW_CREATE_NEW; Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; Lex->create_view_suid= TRUE; } - view_or_trigger + view_or_trigger_or_sp {} | CREATE USER clear_privileges grant_list { @@ -2079,10 +2013,10 @@ sp_proc_stmt: uint ip= sp->instructions(); uint n; - n= ctx->diff_handlers(lab->ctx); + n= ctx->diff_handlers(lab->ctx, TRUE); /* Exclusive the dest. */ if (n) sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); - n= ctx->diff_cursors(lab->ctx); + n= ctx->diff_cursors(lab->ctx, TRUE); /* Exclusive the dest. */ if (n) sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); i= new sp_instr_jump(ip, ctx); @@ -2108,10 +2042,10 @@ sp_proc_stmt: uint ip= sp->instructions(); uint n; - n= ctx->diff_handlers(lab->ctx); + n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */ if (n) sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); - n= ctx->diff_cursors(lab->ctx); + n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */ if (n) sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ @@ -3846,7 +3780,8 @@ mi_check_type: | FAST_SYM { Lex->check_opt.flags|= T_FAST; } | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } - | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; }; + | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } + | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; }; optimize: OPTIMIZE opt_no_write_to_binlog table_or_tables @@ -4736,9 +4671,19 @@ simple_expr: | SUBSTRING_INDEX '(' expr ',' expr ',' expr ')' { $$= new Item_func_substr_index($3,$5,$7); } | SYSDATE optional_braces - { $$= new Item_func_sysdate_local(); Lex->safe_to_cache_query=0;} + { + if (global_system_variables.sysdate_is_now == 0) + $$= new Item_func_sysdate_local(); + else $$= new Item_func_now_local(); + Lex->safe_to_cache_query=0; + } | SYSDATE '(' expr ')' - { $$= new Item_func_sysdate_local($3); Lex->safe_to_cache_query=0;} + { + if (global_system_variables.sysdate_is_now == 0) + $$= new Item_func_sysdate_local($3); + else $$= new Item_func_now_local($3); + Lex->safe_to_cache_query=0; + } | TIME_SYM '(' expr ')' { $$= new Item_time_typecast($3); } | TIMESTAMP '(' expr ')' @@ -5488,9 +5433,8 @@ select_derived2: { LEX *lex= Lex; lex->derived_tables|= DERIVED_SUBQUERY; - if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN && - lex->sql_command <= (int)SQLCOM_HA_READ) || - lex->sql_command == (int)SQLCOM_KILL) + if (lex->sql_command == (int)SQLCOM_HA_READ || + lex->sql_command == (int)SQLCOM_KILL) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; @@ -5947,7 +5891,7 @@ select_var_ident: ; into: - INTO OUTFILE TEXT_STRING_sys + INTO OUTFILE TEXT_STRING_filesystem { LEX *lex= Lex; lex->uncacheable(UNCACHEABLE_SIDEEFFECT); @@ -5956,7 +5900,7 @@ into: YYABORT; } opt_field_term opt_line_term - | INTO DUMPFILE TEXT_STRING_sys + | INTO DUMPFILE TEXT_STRING_filesystem { LEX *lex=Lex; if (!lex->describe) @@ -6124,7 +6068,19 @@ replace: ; insert_lock_option: - /* empty */ { $$= TL_WRITE_CONCURRENT_INSERT; } + /* empty */ + { +#ifdef HAVE_QUERY_CACHE + /* + If it is SP we do not allow insert optimisation whan result of + insert visible only after the table unlocking but everyone can + read table. + */ + $$= (Lex->sphead ? TL_WRITE :TL_WRITE_CONCURRENT_INSERT); +#else + $$= TL_WRITE_CONCURRENT_INSERT; +#endif + } | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } | DELAYED_SYM { $$= TL_WRITE_DELAYED; } | HIGH_PRIORITY { $$= TL_WRITE; } @@ -6889,18 +6845,18 @@ purge_option: /* kill threads */ kill: - KILL_SYM kill_option expr + KILL_SYM { Lex->sql_command= SQLCOM_KILL; } kill_option expr { LEX *lex=Lex; lex->value_list.empty(); - lex->value_list.push_front($3); - lex->sql_command= SQLCOM_KILL; + lex->value_list.push_front($4); }; kill_option: /* empty */ { Lex->type= 0; } | CONNECTION_SYM { Lex->type= 0; } - | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; }; + | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; } + ; /* change database */ @@ -6913,7 +6869,7 @@ use: USE_SYM ident /* import, export of files */ -load: LOAD DATA_SYM +load: LOAD DATA_SYM { LEX *lex=Lex; if (lex->sphead) @@ -6940,7 +6896,7 @@ load: LOAD DATA_SYM }; load_data: - load_data_lock opt_local INFILE TEXT_STRING_sys + load_data_lock opt_local INFILE TEXT_STRING_filesystem { LEX *lex=Lex; lex->sql_command= SQLCOM_LOAD; @@ -6981,7 +6937,16 @@ opt_local: load_data_lock: /* empty */ { $$= YYTHD->update_lock_default; } - | CONCURRENT { $$= TL_WRITE_CONCURRENT_INSERT ; } + | CONCURRENT + { +#ifdef HAVE_QUERY_CACHE + /* + Ignore this option in SP to avoid problem with query cache + */ + if (Lex->sphead != 0) +#endif + $$= TL_WRITE_CONCURRENT_INSERT; + } | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }; @@ -7469,6 +7434,18 @@ TEXT_STRING_literal: ; +TEXT_STRING_filesystem: + TEXT_STRING + { + THD *thd= YYTHD; + if (thd->charset_is_character_set_filesystem) + $$= $1; + else + thd->convert_string(&$$, thd->variables.character_set_filesystem, + $1.str, $1.length, thd->charset()); + } + ; + ident: IDENT_sys { $$=$1; } | keyword @@ -8925,9 +8902,8 @@ subselect_start: '(' SELECT_SYM { LEX *lex=Lex; - if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN && - lex->sql_command <= (int)SQLCOM_HA_READ) || - lex->sql_command == (int)SQLCOM_KILL) + if (lex->sql_command == (int)SQLCOM_HA_READ || + lex->sql_command == (int)SQLCOM_KILL) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; @@ -8945,45 +8921,61 @@ subselect_end: lex->nest_level--; }; +/************************************************************************** + + CREATE VIEW | TRIGGER | PROCEDURE statements. + +**************************************************************************/ + +view_or_trigger_or_sp: + definer view_or_trigger_or_sp_tail + {} + | view_replace_or_algorithm definer view_tail + {} + ; + +view_or_trigger_or_sp_tail: + view_tail + {} + | trigger_tail + {} + | sp_tail + {} + ; + +/************************************************************************** + + DEFINER clause support. + +**************************************************************************/ + definer: - get_definer + /* empty */ { - THD *thd= YYTHD; - - if (! (thd->lex->definer= create_definer(thd, &$1->user, &$1->host))) - YYABORT; + /* + We have to distinguish missing DEFINER-clause from case when + CURRENT_USER specified as definer explicitly in order to properly + handle CREATE TRIGGER statements which come to replication thread + from older master servers (i.e. to create non-suid trigger in this + case). + */ + YYTHD->lex->definer= 0; } - ; - -get_definer: - opt_current_definer + | DEFINER_SYM EQ CURRENT_USER optional_braces { - THD *thd= YYTHD; - - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - YYABORT; - - if (get_default_definer(thd, $$)) - YYABORT; + if (! (YYTHD->lex->definer= create_default_definer(YYTHD))) + YYABORT; } | DEFINER_SYM EQ ident_or_text '@' ident_or_text { - if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user)))) - YYABORT; - - $$->user= $3; - $$->host= $5; + if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5))) + YYABORT; } ; -opt_current_definer: - /* empty */ - | DEFINER_SYM EQ CURRENT_USER optional_braces - ; - /************************************************************************** - CREATE VIEW statement options. + CREATE VIEW statement parts. **************************************************************************/ @@ -9017,20 +9009,6 @@ view_algorithm_opt: {} ; -view_or_trigger: - definer view_or_trigger_tail - {} - | view_replace_or_algorithm definer view_tail - {} - ; - -view_or_trigger_tail: - view_tail - {} - | trigger_tail - {} - ; - view_suid: /* empty */ { Lex->create_view_suid= TRUE; } @@ -9112,8 +9090,8 @@ view_check_option: **************************************************************************/ trigger_tail: - TRIGGER_SYM remember_name sp_name trg_action_time trg_event - ON table_ident FOR_SYM EACH_SYM ROW_SYM + TRIGGER_SYM remember_name sp_name trg_action_time trg_event + ON remember_name table_ident FOR_SYM remember_name EACH_SYM ROW_SYM { LEX *lex= Lex; sp_head *sp; @@ -9129,8 +9107,10 @@ trigger_tail: sp->reset_thd_mem_root(YYTHD); sp->init(lex); - lex->trigger_definition_begin= $2; - + lex->stmt_definition_begin= $2; + lex->ident.str= $7; + lex->ident.length= $10 - $7; + sp->m_type= TYPE_ENUM_TRIGGER; lex->sphead= sp; lex->spname= $3; @@ -9167,16 +9147,93 @@ trigger_tail: We have to do it after parsing trigger body, because some of sp_proc_stmt alternatives are not saving/restoring LEX, so lex->query_tables can be wiped out. - - QQ: What are other consequences of this? - - QQ: Could we loosen lock type in certain cases ? */ - if (!lex->select_lex.add_table_to_list(YYTHD, $7, + if (!lex->select_lex.add_table_to_list(YYTHD, $8, (LEX_STRING*) 0, TL_OPTION_UPDATING, - TL_WRITE)) + TL_IGNORE)) + YYABORT; + } + ; + +/************************************************************************** + + CREATE FUNCTION | PROCEDURE statements parts. + +**************************************************************************/ + +sp_tail: + udf_func_type remember_name FUNCTION_SYM sp_name + { + LEX *lex=Lex; + lex->udf.type= $1; + lex->stmt_definition_begin= $2; + lex->spname= $4; + } + create_function_tail + {} + | PROCEDURE remember_name sp_name + { + LEX *lex= Lex; + sp_head *sp; + + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); YYABORT; + } + + lex->stmt_definition_begin= $2; + + /* Order is important here: new - reset - init */ + sp= new sp_head(); + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + + sp->m_type= TYPE_ENUM_PROCEDURE; + lex->sphead= sp; + /* + * We have to turn of CLIENT_MULTI_QUERIES while parsing a + * stored procedure, otherwise yylex will chop it into pieces + * at each ';'. + */ + sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + } + '(' + { + LEX *lex= Lex; + + lex->sphead->m_param_begin= lex->tok_start+1; + } + sp_pdparam_list + ')' + { + LEX *lex= Lex; + + lex->sphead->m_param_end= lex->tok_start; + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_c_chistics + { + LEX *lex= Lex; + + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->m_body_begin= lex->tok_start; + } + sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + if (sp->check_backpatch(YYTHD)) + YYABORT; + sp->init_strings(YYTHD, lex, $3); + lex->sql_command= SQLCOM_CREATE_PROCEDURE; + /* Restore flag if it was cleared above */ + if (sp->m_old_cmq) + YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + sp->restore_thd_mem_root(YYTHD); } ; diff --git a/sql/table.cc b/sql/table.cc index 9ae5348b5c6..8e23bea2540 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1597,7 +1597,7 @@ bool check_db_name(char *name) while (*name) { #if defined(USE_MB) && defined(USE_MB_IDENT) - last_char_is_space= my_isspace(default_charset_info, *name); + last_char_is_space= my_isspace(system_charset_info, *name); if (use_mb(system_charset_info)) { int len=my_ismbchar(system_charset_info, name, @@ -1643,7 +1643,7 @@ bool check_table_name(const char *name, uint length) while (name != end) { #if defined(USE_MB) && defined(USE_MB_IDENT) - last_char_is_space= my_isspace(default_charset_info, *name); + last_char_is_space= my_isspace(system_charset_info, *name); if (use_mb(system_charset_info)) { int len=my_ismbchar(system_charset_info, name, end); @@ -1674,7 +1674,7 @@ bool check_column_name(const char *name) while (*name) { #if defined(USE_MB) && defined(USE_MB_IDENT) - last_char_is_space= my_isspace(default_charset_info, *name); + last_char_is_space= my_isspace(system_charset_info, *name); if (use_mb(system_charset_info)) { int len=my_ismbchar(system_charset_info, name, @@ -2595,8 +2595,15 @@ const char *Natural_join_column::db_name() if (view_field) return table_ref->view_db.str; + /* + Test that TABLE_LIST::db is the same as st_table_share::db to + ensure consistency. An exception are I_S schema tables, which + are inconsistent in this respect. + */ DBUG_ASSERT(!strcmp(table_ref->db, - table_ref->table->s->db)); + table_ref->table->s->db) || + (table_ref->schema_table && + table_ref->table->s->db[0] == 0)); return table_ref->db; } @@ -2798,7 +2805,15 @@ const char *Field_iterator_table_ref::db_name() else if (table_ref->is_natural_join) return natural_join_it.column_ref()->db_name(); - DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db)); + /* + Test that TABLE_LIST::db is the same as st_table_share::db to + ensure consistency. An exception are I_S schema tables, which + are inconsistent in this respect. + */ + DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db) || + (table_ref->schema_table && + table_ref->table->s->db[0] == 0)); + return table_ref->db; } @@ -2819,11 +2834,31 @@ GRANT_INFO *Field_iterator_table_ref::grant() SYNOPSIS Field_iterator_table_ref::get_or_create_column_ref() - is_created [out] set to TRUE if the column was created, - FALSE if we return an already created colum + parent_table_ref the parent table reference over which the + iterator is iterating DESCRIPTION - TODO + Create a new natural join column for the current field of the + iterator if no such column was created, or return an already + created natural join column. The former happens for base tables or + views, and the latter for natural/using joins. If a new field is + created, then the field is added to 'parent_table_ref' if it is + given, or to the original table referene of the field if + parent_table_ref == NULL. + + NOTES + This method is designed so that when a Field_iterator_table_ref + walks through the fields of a table reference, all its fields + are created and stored as follows: + - If the table reference being iterated is a stored table, view or + natural/using join, store all natural join columns in a list + attached to that table reference. + - If the table reference being iterated is a nested join that is + not natural/using join, then do not materialize its result + fields. This is OK because for such table references + Field_iterator_table_ref iterates over the fields of the nested + table references (recursively). In this way we avoid the storage + of unnecessay copies of result columns of nested joins. RETURN # Pointer to a column of a natural join (or its operand) @@ -2831,22 +2866,28 @@ GRANT_INFO *Field_iterator_table_ref::grant() */ Natural_join_column * -Field_iterator_table_ref::get_or_create_column_ref(bool *is_created) +Field_iterator_table_ref::get_or_create_column_ref(TABLE_LIST *parent_table_ref) { Natural_join_column *nj_col; + bool is_created= TRUE; + uint field_count; + TABLE_LIST *add_table_ref= parent_table_ref ? + parent_table_ref : table_ref; - *is_created= TRUE; if (field_it == &table_field_it) { /* The field belongs to a stored table. */ Field *field= table_field_it.field(); nj_col= new Natural_join_column(field, table_ref); + field_count= table_ref->table->s->fields; } else if (field_it == &view_field_it) { /* The field belongs to a merge view or information schema table. */ Field_translator *translated_field= view_field_it.field_translator(); nj_col= new Natural_join_column(translated_field, table_ref); + field_count= table_ref->field_translation_end - + table_ref->field_translation; } else { @@ -2855,12 +2896,43 @@ Field_iterator_table_ref::get_or_create_column_ref(bool *is_created) already created via one of the two constructor calls above. In this case we just return the already created column reference. */ - *is_created= FALSE; + DBUG_ASSERT(table_ref->is_join_columns_complete); + is_created= FALSE; nj_col= natural_join_it.column_ref(); DBUG_ASSERT(nj_col); } DBUG_ASSERT(!nj_col->table_field || nj_col->table_ref->table == nj_col->table_field->table); + + /* + If the natural join column was just created add it to the list of + natural join columns of either 'parent_table_ref' or to the table + reference that directly contains the original field. + */ + if (is_created) + { + /* Make sure not all columns were materialized. */ + DBUG_ASSERT(!add_table_ref->is_join_columns_complete); + if (!add_table_ref->join_columns) + { + /* Create a list of natural join columns on demand. */ + if (!(add_table_ref->join_columns= new List<Natural_join_column>)) + return NULL; + add_table_ref->is_join_columns_complete= FALSE; + } + add_table_ref->join_columns->push_back(nj_col); + /* + If new fields are added to their original table reference, mark if + all fields were added. We do it here as the caller has no easy way + of knowing when to do it. + If the fields are being added to parent_table_ref, then the caller + must take care to mark when all fields are created/added. + */ + if (!parent_table_ref && + add_table_ref->join_columns->elements == field_count) + add_table_ref->is_join_columns_complete= TRUE; + } + return nj_col; } diff --git a/sql/table.h b/sql/table.h index 78a942ef301..9d6e4764de3 100644 --- a/sql/table.h +++ b/sql/table.h @@ -187,6 +187,8 @@ typedef struct st_table_share /* Information for one open table */ struct st_table { + st_table() {} /* Remove gcc warning */ + TABLE_SHARE *s; handler *file; #ifdef NOT_YET @@ -444,6 +446,7 @@ public: typedef struct st_table_list { + st_table_list() {} /* Remove gcc warning */ /* List of tables local to a subquery (used by SQL_LIST). Considers views as leaves (unlike 'next_leaf' below). Created at parse time @@ -675,6 +678,7 @@ class Item; class Field_iterator: public Sql_alloc { public: + Field_iterator() {} /* Remove gcc warning */ virtual ~Field_iterator() {} virtual void set(TABLE_LIST *)= 0; virtual void next()= 0; @@ -783,7 +787,7 @@ public: GRANT_INFO *grant(); Item *create_item(THD *thd) { return field_it->create_item(thd); } Field *field() { return field_it->field(); } - Natural_join_column *get_or_create_column_ref(bool *is_created); + Natural_join_column *get_or_create_column_ref(TABLE_LIST *parent_table_ref); Natural_join_column *get_natural_column_ref(); }; diff --git a/sql/tztime.cc b/sql/tztime.cc index 3a9d9a60aed..b86c9a44561 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -961,6 +961,7 @@ static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1); class Time_zone_system : public Time_zone { public: + Time_zone_system() {} /* Remove gcc warning */ virtual my_time_t TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const; virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; @@ -1054,6 +1055,7 @@ Time_zone_system::get_name() const class Time_zone_utc : public Time_zone { public: + Time_zone_utc() {} /* Remove gcc warning */ virtual my_time_t TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const; virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; diff --git a/sql/tztime.h b/sql/tztime.h index a168fe4fb73..23460a8e739 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -30,6 +30,7 @@ class Time_zone: public Sql_alloc { public: + Time_zone() {} /* Remove gcc warning */ /* Converts local time in broken down TIME representation to my_time_t (UTC seconds since Epoch) represenation. diff --git a/sql/udf_example.cc b/sql/udf_example.cc index 35833e63fab..73e49aef178 100644 --- a/sql/udf_example.cc +++ b/sql/udf_example.cc @@ -113,6 +113,8 @@ */ #ifdef STANDARD +/* STANDARD is defined, don't use any mysql functions */ +#include <stdlib.h> #include <stdio.h> #include <string.h> #ifdef __WIN__ @@ -125,10 +127,10 @@ typedef long long longlong; #else #include <my_global.h> #include <my_sys.h> +#include <m_string.h> // To get strmov() #endif #include <mysql.h> -#include <m_ctype.h> -#include <m_string.h> // To get strmov() +#include <ctype.h> static pthread_mutex_t LOCK_hostname; @@ -290,8 +292,8 @@ char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, for (n = ntrans + 1, n_end = ntrans + sizeof(ntrans)-2; word != w_end && n < n_end; word++ ) - if ( my_isalpha ( &my_charset_latin1, *word )) - *n++ = my_toupper ( &my_charset_latin1, *word ); + if ( isalpha ( *word )) + *n++ = toupper ( *word ); if ( n == ntrans + 1 ) /* return empty string if 0 bytes */ { @@ -495,7 +497,7 @@ char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, } } } - *length= (ulong) (result - org_result); + *length= (ulong) (max(0, result - org_result - 1)); return org_result; } @@ -519,7 +521,7 @@ my_bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { if (!args->arg_count) { - strcpy(message,"myfunc_double must have at least on argument"); + strcpy(message,"myfunc_double must have at least one argument"); return 1; } /* |