diff options
author | Alexander Nozdrin <alik@sun.com> | 2009-11-05 15:08:37 +0300 |
---|---|---|
committer | Alexander Nozdrin <alik@sun.com> | 2009-11-05 15:08:37 +0300 |
commit | d476bbb0becb27d7d3cb1fc5cc0ac4d401622893 (patch) | |
tree | 64caab6e7d4a7167ee56b4f7d6572bb07c30750a /sql | |
parent | 1a8f08b006616b0bb2a9e9e13969ebe406e9e3f2 (diff) | |
parent | 16b603a8b0c2bba16cb66b1769f21c0185435d33 (diff) | |
download | mariadb-git-d476bbb0becb27d7d3cb1fc5cc0ac4d401622893.tar.gz |
Auto-merge from mysql-next-mr.
Diffstat (limited to 'sql')
96 files changed, 7329 insertions, 3015 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 5c3a1d3a8fe..15ee0d588c4 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -111,8 +111,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ sql_plugin.h authors.h event_parse_data.h \ event_data_objects.h event_scheduler.h \ sql_partition.h partition_info.h partition_element.h \ - contributors.h sql_servers.h sql_signal.h \ - rpl_handler.h replication.h + contributors.h sql_servers.h sql_signal.h records.h \ + sql_prepare.h rpl_handler.h replication.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index dba32cac6b2..271ac73bd30 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -1225,7 +1225,9 @@ Event_timed::get_create_event(THD *thd, String *buf) expression)) DBUG_RETURN(EVEX_MICROSECOND_UNSUP); - buf->append(STRING_WITH_LEN("CREATE EVENT ")); + buf->append(STRING_WITH_LEN("CREATE ")); + append_definer(thd, buf, &definer_user, &definer_host); + buf->append(STRING_WITH_LEN("EVENT ")); append_identifier(thd, buf, name.str, name.length); if (expression) diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index daaa6be0520..ea20270b457 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -607,7 +607,12 @@ Event_scheduler::stop() LOCK_DATA(); DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str)); if (state != RUNNING) + { + /* Synchronously wait until the scheduler stops. */ + while (state != INITIALIZED) + COND_STATE_WAIT(thd, NULL, "Waiting for the scheduler to stop"); goto end; + } /* Guarantee we don't catch spurious signals */ do { diff --git a/sql/events.cc b/sql/events.cc index 34da0e185b7..af36ede39ec 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -757,7 +757,7 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol) field_list.push_back( new Item_empty_string("Database Collation", MY_CS_NAME_SIZE)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); diff --git a/sql/field.cc b/sql/field.cc index 0cd01685744..95957a37cfe 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1016,6 +1016,36 @@ Item_result Field::result_merge_type(enum_field_types field_type) Static help functions *****************************************************************************/ +/** + Output a warning for erroneous conversion of strings to numerical + values. For use with ER_TRUNCATED_WRONG_VALUE[_FOR_FIELD] + + @param thd THD object + @param str pointer to string that failed to be converted + @param length length of string + @param cs charset for string + @param typestr string describing type converted to + @param error error value to output + @param field_name (for *_FOR_FIELD) name of field + @param row_num (for *_FOR_FIELD) row number + */ +static void push_numerical_conversion_warning(THD* thd, const char* str, + uint length, CHARSET_INFO* cs, + const char* typestr, int error, + const char* field_name="UNKNOWN", + ulong row_num=0) +{ + char buf[max(max(DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE, + LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE), + DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE)]; + + String tmp(buf, sizeof(buf), cs); + tmp.copy(str, length, cs); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + error, ER(error), typestr, tmp.c_ptr(), + field_name, row_num); +} + /** Check whether a field type can be partially indexed by a key. @@ -1527,7 +1557,12 @@ void Field::make_field(Send_field *field) if (orig_table && orig_table->s->db.str && *orig_table->s->db.str) { field->db_name= orig_table->s->db.str; - field->org_table_name= orig_table->s->table_name.str; + if (orig_table->pos_in_table_list && + orig_table->pos_in_table_list->schema_table) + field->org_table_name= (orig_table->pos_in_table_list-> + schema_table->table_name); + else + field->org_table_name= orig_table->s->table_name.str; } else field->org_table_name= field->db_name= ""; @@ -1770,7 +1805,7 @@ bool Field::optimize_range(uint idx, uint part) } -Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type __attribute__((unused))) { Field *tmp; @@ -1791,7 +1826,7 @@ Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table, } -Field *Field::new_key_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit) { @@ -1808,7 +1843,7 @@ Field *Field::new_key_field(MEM_ROOT *root, struct st_table *new_table, /* This is used to generate a field in TABLE from TABLE_SHARE */ -Field *Field::clone(MEM_ROOT *root, struct st_table *new_table) +Field *Field::clone(MEM_ROOT *root, TABLE *new_table) { Field *tmp; if ((tmp= (Field*) memdup_root(root,(char*) this,size_of()))) @@ -6352,6 +6387,7 @@ check_string_copy_error(Field_str *field, return FALSE; convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, @@ -6986,7 +7022,7 @@ uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg) } -Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field_string::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type) { Field *field; @@ -7097,22 +7133,46 @@ int Field_varstring::store(longlong nr, bool unsigned_val) double Field_varstring::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int not_used; - char *end_not_used; + int error; + char *end; + double result; + CHARSET_INFO* cs= charset(); + uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - return my_strntod(field_charset, (char*) ptr+length_bytes, length, - &end_not_used, ¬_used); + result= my_strntod(cs, (char*)ptr+length_bytes, length, &end, &error); + + if (!table->in_use->no_errors && + (error || (length != (uint)(end - (char*)ptr+length_bytes) && + !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length)))) + { + push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes, + length, cs,"DOUBLE", + ER_TRUNCATED_WRONG_VALUE); + } + return result; } longlong Field_varstring::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int not_used; - char *end_not_used; + int error; + char *end; + CHARSET_INFO *cs= charset(); + uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - return my_strntoll(field_charset, (char*) ptr+length_bytes, length, 10, - &end_not_used, ¬_used); + longlong result= my_strntoll(cs, (char*) ptr+length_bytes, length, 10, + &end, &error); + + if (!table->in_use->no_errors && + (error || (length != (uint)(end - (char*)ptr+length_bytes) && + !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length)))) + { + push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes, + length, cs, "INTEGER", + ER_TRUNCATED_WRONG_VALUE); + } + return result; } String *Field_varstring::val_str(String *val_buffer __attribute__((unused)), @@ -7128,9 +7188,17 @@ String *Field_varstring::val_str(String *val_buffer __attribute__((unused)), my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; + CHARSET_INFO *cs= charset(); uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr+length_bytes, length, - charset(), decimal_value); + int error= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr+length_bytes, length, + cs, decimal_value); + + if (!table->in_use->no_errors && error) + { + push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes, + length, cs, "DECIMAL", + ER_TRUNCATED_WRONG_VALUE); + } return decimal_value; } @@ -7532,7 +7600,7 @@ int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, } -Field *Field_varstring::new_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field_varstring::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type) { Field_varstring *res= (Field_varstring*) Field::new_field(root, new_table, @@ -7544,7 +7612,7 @@ Field *Field_varstring::new_field(MEM_ROOT *root, struct st_table *new_table, Field *Field_varstring::new_key_field(MEM_ROOT *root, - struct st_table *new_table, + TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit) { @@ -8674,7 +8742,7 @@ void Field_enum::sql_type(String &res) const } -Field *Field_enum::new_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field_enum::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type) { Field_enum *res= (Field_enum*) Field::new_field(root, new_table, keep_type); @@ -9015,7 +9083,7 @@ Field_bit::do_last_null_byte() const Field *Field_bit::new_key_field(MEM_ROOT *root, - struct st_table *new_table, + TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit) { @@ -9855,8 +9923,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, break; case MYSQL_TYPE_DATE: /* Old date type. */ - if (protocol_version != PROTOCOL_VERSION-1) - sql_type= MYSQL_TYPE_NEWDATE; + sql_type= MYSQL_TYPE_NEWDATE; /* fall trough */ case MYSQL_TYPE_NEWDATE: length= 10; diff --git a/sql/field.h b/sql/field.h index 8bf641a8649..9ad00e0dab3 100644 --- a/sql/field.h +++ b/sql/field.h @@ -63,8 +63,8 @@ public: Note that you can use table->in_use as replacement for current_thd member only inside of val_*() and store() members (e.g. you can't use it in cons) */ - struct st_table *table; // Pointer for table - struct st_table *orig_table; // Pointer to original table + TABLE *table; // Pointer for table + TABLE *orig_table; // Pointer to original table const char **table_name, *field_name; LEX_STRING comment; /* Field is part of the following keys */ @@ -304,12 +304,12 @@ public: */ virtual bool can_be_compared_as_longlong() const { return FALSE; } virtual void free() {} - virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table, + virtual Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); - virtual Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, + virtual Field *new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit); - Field *clone(MEM_ROOT *mem_root, struct st_table *new_table); + Field *clone(MEM_ROOT *mem_root, TABLE *new_table); inline void move_field(uchar *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg) { ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg; @@ -1516,7 +1516,7 @@ public: enum_field_types real_type() const { return MYSQL_TYPE_STRING; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } - Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); + Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); virtual uint get_key_image(uchar *buff,uint length, imagetype type); private: int do_save_field_metadata(uchar *first_byte); @@ -1603,8 +1603,8 @@ public: enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } - Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); - Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, + Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit); uint is_equal(Create_field *new_field); @@ -1843,7 +1843,7 @@ public: { flags|=ENUM_FLAG; } - Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); + Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); enum_field_types type() const { return MYSQL_TYPE_STRING; } enum Item_result cmp_type () const { return INT_RESULT; } enum Item_result cast_to_int_type () const { return INT_RESULT; } @@ -1980,7 +1980,7 @@ public: uint param_data, bool low_byte_first); virtual void set_default(); - Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit); void set_bit_ptr(uchar *bit_ptr_arg, uchar bit_ofs_arg) @@ -2089,7 +2089,7 @@ public: A class for sending info to the client */ -class Send_field { +class Send_field :public Sql_alloc { public: const char *db_name; const char *table_name,*org_table_name; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 51cfd280a87..f567b8ac563 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -355,8 +355,8 @@ Thd_ndb::Thd_ndb() m_error_code= 0; query_state&= NDB_QUERY_NORMAL; options= 0; - (void) hash_init(&open_tables, &my_charset_bin, 5, 0, 0, - (hash_get_key)thd_ndb_share_get_key, 0, 0); + (void) my_hash_init(&open_tables, &my_charset_bin, 5, 0, 0, + (my_hash_get_key)thd_ndb_share_get_key, 0, 0); } Thd_ndb::~Thd_ndb() @@ -380,7 +380,7 @@ Thd_ndb::~Thd_ndb() ndb= NULL; } changed_tables.empty(); - hash_free(&open_tables); + my_hash_free(&open_tables); } void @@ -4587,9 +4587,9 @@ int ha_ndbcluster::init_handler_for_statement(THD *thd, Thd_ndb *thd_ndb) const void *key= m_table; HASH_SEARCH_STATE state; THD_NDB_SHARE *thd_ndb_share= - (THD_NDB_SHARE*)hash_first(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state); + (THD_NDB_SHARE*)my_hash_first(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state); while (thd_ndb_share && thd_ndb_share->key != key) - thd_ndb_share= (THD_NDB_SHARE*)hash_next(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state); + thd_ndb_share= (THD_NDB_SHARE*)my_hash_next(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state); if (thd_ndb_share == 0) { thd_ndb_share= (THD_NDB_SHARE *) alloc_root(&thd->transaction.mem_root, @@ -5523,8 +5523,8 @@ int ha_ndbcluster::create(const char *name, */ { uint length= (uint) strlen(name); - if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables, - (uchar*) name, length))) + if ((share= (NDB_SHARE*) my_hash_search(&ndbcluster_open_tables, + (uchar*) name, length))) handle_trailing_share(share); } /* @@ -7118,18 +7118,18 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, NdbDictionary::Object::UserTable) != 0) ERR_RETURN(dict->getNdbError()); - if (hash_init(&ndb_tables, system_charset_info,list.count,0,0, - (hash_get_key)tables_get_key,0,0)) + if (my_hash_init(&ndb_tables, system_charset_info,list.count,0,0, + (my_hash_get_key)tables_get_key,0,0)) { DBUG_PRINT("error", ("Failed to init HASH ndb_tables")); DBUG_RETURN(-1); } - if (hash_init(&ok_tables, system_charset_info,32,0,0, - (hash_get_key)tables_get_key,0,0)) + if (my_hash_init(&ok_tables, system_charset_info,32,0,0, + (my_hash_get_key)tables_get_key,0,0)) { DBUG_PRINT("error", ("Failed to init HASH ok_tables")); - hash_free(&ndb_tables); + my_hash_free(&ndb_tables); DBUG_RETURN(-1); } @@ -7170,7 +7170,8 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, { bool file_on_disk= FALSE; DBUG_PRINT("info", ("%s", file_name->str)); - if (hash_search(&ndb_tables, (uchar*) file_name->str, file_name->length)) + if (my_hash_search(&ndb_tables, (uchar*) file_name->str, + file_name->length)) { build_table_filename(name, sizeof(name) - 1, db, file_name->str, reg_ext, 0); @@ -7205,10 +7206,10 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, if (file_on_disk) { // Ignore this ndb table - uchar *record= hash_search(&ndb_tables, (uchar*) file_name->str, - file_name->length); + uchar *record= my_hash_search(&ndb_tables, (uchar*) file_name->str, + file_name->length); DBUG_ASSERT(record); - hash_delete(&ndb_tables, record); + my_hash_delete(&ndb_tables, record); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TABLE_EXISTS_ERROR, "Local table %s.%s shadows ndb table", @@ -7242,7 +7243,7 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, build_table_filename(name, sizeof(name) - 1, db, "", "", 0); for (i= 0; i < ok_tables.records; i++) { - file_name_str= (char*)hash_element(&ok_tables, i); + file_name_str= (char*)my_hash_element(&ok_tables, i); end= end1 + tablename_to_filename(file_name_str, end1, sizeof(name) - (end1 - name)); pthread_mutex_lock(&LOCK_open); @@ -7258,8 +7259,9 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, List<char> create_list; for (i= 0 ; i < ndb_tables.records ; i++) { - file_name_str= (char*) hash_element(&ndb_tables, i); - if (!hash_search(&ok_tables, (uchar*) file_name_str, strlen(file_name_str))) + file_name_str= (char*) my_hash_element(&ndb_tables, i); + if (!my_hash_search(&ok_tables, (uchar*) file_name_str, + strlen(file_name_str))) { build_table_filename(name, sizeof(name) - 1, db, file_name_str, reg_ext, 0); @@ -7313,8 +7315,8 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, pthread_mutex_unlock(&LOCK_open); - hash_free(&ok_tables); - hash_free(&ndb_tables); + my_hash_free(&ok_tables); + my_hash_free(&ndb_tables); // Delete schema file from files if (!strcmp(db, NDB_REP_DB)) @@ -7478,8 +7480,8 @@ static int ndbcluster_init(void *p) goto ndbcluster_init_error; } - (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0, - (hash_get_key) ndbcluster_get_key,0,0); + (void) my_hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0, + (my_hash_get_key) ndbcluster_get_key,0,0); #ifdef HAVE_NDB_BINLOG /* start the ndb injector thread */ if (ndbcluster_binlog_start()) @@ -7492,7 +7494,7 @@ static int ndbcluster_init(void *p) if (pthread_create(&tmp, &connection_attrib, ndb_util_thread_func, 0)) { DBUG_PRINT("error", ("Could not create ndb utility thread")); - hash_free(&ndbcluster_open_tables); + my_hash_free(&ndbcluster_open_tables); pthread_mutex_destroy(&ndbcluster_mutex); pthread_mutex_destroy(&LOCK_ndb_util_thread); pthread_cond_destroy(&COND_ndb_util_thread); @@ -7509,7 +7511,7 @@ static int ndbcluster_init(void *p) if (!ndb_util_thread_running) { DBUG_PRINT("error", ("ndb utility thread exited prematurely")); - hash_free(&ndbcluster_open_tables); + my_hash_free(&ndbcluster_open_tables); pthread_mutex_destroy(&ndbcluster_mutex); pthread_mutex_destroy(&LOCK_ndb_util_thread); pthread_cond_destroy(&COND_ndb_util_thread); @@ -7560,7 +7562,7 @@ static int ndbcluster_end(handlerton *hton, ha_panic_function type) while (ndbcluster_open_tables.records) { NDB_SHARE *share= - (NDB_SHARE*) hash_element(&ndbcluster_open_tables, 0); + (NDB_SHARE*) my_hash_element(&ndbcluster_open_tables, 0); #ifndef DBUG_OFF fprintf(stderr, "NDB: table share %s with use_count %d not freed\n", share->key, share->use_count); @@ -7570,7 +7572,7 @@ static int ndbcluster_end(handlerton *hton, ha_panic_function type) pthread_mutex_unlock(&ndbcluster_mutex); } #endif - hash_free(&ndbcluster_open_tables); + my_hash_free(&ndbcluster_open_tables); if (g_ndb) { @@ -7903,9 +7905,9 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname, dbname, tabname, "", 0); DBUG_PRINT("enter", ("name: %s", name)); pthread_mutex_lock(&ndbcluster_mutex); - if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables, - (uchar*) name, - strlen(name)))) + if (!(share=(NDB_SHARE*) my_hash_search(&ndbcluster_open_tables, + (uchar*) name, + strlen(name)))) { pthread_mutex_unlock(&ndbcluster_mutex); DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables", name)); @@ -8165,7 +8167,7 @@ static void print_ndbcluster_open_tables() fprintf(DBUG_FILE, ">ndbcluster_open_tables\n"); for (uint i= 0; i < ndbcluster_open_tables.records; i++) print_share("", - (NDB_SHARE*)hash_element(&ndbcluster_open_tables, i)); + (NDB_SHARE*)my_hash_element(&ndbcluster_open_tables, i)); fprintf(DBUG_FILE, "<ndbcluster_open_tables\n"); DBUG_UNLOCK_FILE; } @@ -8276,7 +8278,7 @@ int handle_trailing_share(NDB_SHARE *share) at the cost of a possible mem leak, by "renaming" the share - First remove from hash */ - hash_delete(&ndbcluster_open_tables, (uchar*) share); + my_hash_delete(&ndbcluster_open_tables, (uchar*) share); /* now give it a new name, just a running number @@ -8309,12 +8311,12 @@ static int rename_share(NDB_SHARE *share, const char *new_key) uint new_length= (uint) strlen(new_key); DBUG_PRINT("rename_share", ("old_key: %s old__length: %d", share->key, share->key_length)); - if ((tmp= (NDB_SHARE*) hash_search(&ndbcluster_open_tables, - (uchar*) new_key, new_length))) + if ((tmp= (NDB_SHARE*) my_hash_search(&ndbcluster_open_tables, + (uchar*) new_key, new_length))) handle_trailing_share(tmp); /* remove the share from hash */ - hash_delete(&ndbcluster_open_tables, (uchar*) share); + my_hash_delete(&ndbcluster_open_tables, (uchar*) share); dbug_print_open_tables(); /* save old stuff if insert should fail */ @@ -8415,9 +8417,9 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table, if (!have_lock) pthread_mutex_lock(&ndbcluster_mutex); - if (!(share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables, - (uchar*) key, - length))) + if (!(share= (NDB_SHARE*) my_hash_search(&ndbcluster_open_tables, + (uchar*) key, + length))) { if (!create_if_not_exists) { @@ -8493,7 +8495,7 @@ void ndbcluster_real_free_share(NDB_SHARE **share) DBUG_ENTER("ndbcluster_real_free_share"); dbug_print_share("ndbcluster_real_free_share:", *share); - hash_delete(&ndbcluster_open_tables, (uchar*) *share); + my_hash_delete(&ndbcluster_open_tables, (uchar*) *share); thr_lock_delete(&(*share)->lock); pthread_mutex_destroy(&(*share)->mutex); @@ -9397,7 +9399,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) } for (i= 0, open_count= 0; i < record_count; i++) { - share= (NDB_SHARE *)hash_element(&ndbcluster_open_tables, i); + share= (NDB_SHARE *)my_hash_element(&ndbcluster_open_tables, i); #ifdef HAVE_NDB_BINLOG if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0)) <= 0) @@ -9783,7 +9785,7 @@ void ha_ndbcluster::set_auto_partitions(partition_info *part_info) int ha_ndbcluster::set_range_data(void *tab_ref, partition_info *part_info) { NDBTAB *tab= (NDBTAB*)tab_ref; - int32 *range_data= (int32*)my_malloc(part_info->no_parts*sizeof(int32), + int32 *range_data= (int32*)my_malloc(part_info->num_parts*sizeof(int32), MYF(0)); uint i; int error= 0; @@ -9792,17 +9794,17 @@ int ha_ndbcluster::set_range_data(void *tab_ref, partition_info *part_info) if (!range_data) { - mem_alloc_error(part_info->no_parts*sizeof(int32)); + mem_alloc_error(part_info->num_parts*sizeof(int32)); DBUG_RETURN(1); } - for (i= 0; i < part_info->no_parts; i++) + for (i= 0; i < part_info->num_parts; i++) { longlong range_val= part_info->range_int_array[i]; if (unsigned_flag) range_val-= 0x8000000000000000ULL; if (range_val < INT_MIN32 || range_val >= INT_MAX32) { - if ((i != part_info->no_parts - 1) || + if ((i != part_info->num_parts - 1) || (range_val != LONGLONG_MAX)) { my_error(ER_LIMITED_PART_RANGE, MYF(0), "NDB"); @@ -9813,7 +9815,7 @@ int ha_ndbcluster::set_range_data(void *tab_ref, partition_info *part_info) } range_data[i]= (int32)range_val; } - tab->setRangeListData(range_data, sizeof(int32)*part_info->no_parts); + tab->setRangeListData(range_data, sizeof(int32)*part_info->num_parts); error: my_free((char*)range_data, MYF(0)); DBUG_RETURN(error); @@ -9822,7 +9824,7 @@ error: int ha_ndbcluster::set_list_data(void *tab_ref, partition_info *part_info) { NDBTAB *tab= (NDBTAB*)tab_ref; - int32 *list_data= (int32*)my_malloc(part_info->no_list_values * 2 + int32 *list_data= (int32*)my_malloc(part_info->num_list_values * 2 * sizeof(int32), MYF(0)); uint32 *part_id, i; int error= 0; @@ -9831,10 +9833,10 @@ int ha_ndbcluster::set_list_data(void *tab_ref, partition_info *part_info) if (!list_data) { - mem_alloc_error(part_info->no_list_values*2*sizeof(int32)); + mem_alloc_error(part_info->num_list_values*2*sizeof(int32)); DBUG_RETURN(1); } - for (i= 0; i < part_info->no_list_values; i++) + for (i= 0; i < part_info->num_list_values; i++) { LIST_PART_ENTRY *list_entry= &part_info->list_array[i]; longlong list_val= list_entry->list_value; @@ -9850,7 +9852,7 @@ int ha_ndbcluster::set_list_data(void *tab_ref, partition_info *part_info) part_id= (uint32*)&list_data[2*i+1]; *part_id= list_entry->partition_id; } - tab->setRangeListData(list_data, 2*sizeof(int32)*part_info->no_list_values); + tab->setRangeListData(list_data, 2*sizeof(int32)*part_info->num_list_values); error: my_free((char*)list_data, MYF(0)); DBUG_RETURN(error); @@ -9972,11 +9974,11 @@ uint ha_ndbcluster::set_up_partition_info(partition_info *part_info, ng= 0; ts_names[fd_index]= part_elem->tablespace_name; frag_data[fd_index++]= ng; - } while (++j < part_info->no_subparts); + } while (++j < part_info->num_subparts); } first= FALSE; - } while (++i < part_info->no_parts); - tab->setDefaultNoPartitionsFlag(part_info->use_default_no_partitions); + } while (++i < part_info->num_parts); + tab->setDefaultNoPartitionsFlag(part_info->use_default_num_partitions); tab->setLinearFlag(part_info->linear_hash_ind); { ha_rows max_rows= table_share->max_rows; @@ -10370,7 +10372,7 @@ ndberror2: } -bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts) +bool ha_ndbcluster::get_no_parts(const char *name, uint *num_parts) { Ndb *ndb; NDBDICT *dict; @@ -10392,7 +10394,7 @@ bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts) Ndb_table_guard ndbtab_g(dict= ndb->getDictionary(), m_tabname); if (!ndbtab_g.get_table()) ERR_BREAK(dict->getNdbError(), err); - *no_parts= ndbtab_g.get_table()->getFragmentCount(); + *num_parts= ndbtab_g.get_table()->getFragmentCount(); DBUG_RETURN(FALSE); } @@ -10449,7 +10451,8 @@ static int ndbcluster_fill_files_table(handlerton *hton, continue; ERR_RETURN(ndberr); } - + table->field[IS_FILES_TABLE_CATALOG]->store(STRING_WITH_LEN("def"), + system_charset_info); table->field[IS_FILES_FILE_NAME]->set_notnull(); table->field[IS_FILES_FILE_NAME]->store(elt.name, strlen(elt.name), system_charset_info); diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 64bce078319..8bd94abbe27 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -740,9 +740,9 @@ static NDB_SHARE *ndbcluster_check_ndb_apply_status_share() { pthread_mutex_lock(&ndbcluster_mutex); - void *share= hash_search(&ndbcluster_open_tables, - (uchar*) NDB_APPLY_TABLE_FILE, - sizeof(NDB_APPLY_TABLE_FILE) - 1); + void *share= my_hash_search(&ndbcluster_open_tables, + (uchar*) NDB_APPLY_TABLE_FILE, + sizeof(NDB_APPLY_TABLE_FILE) - 1); DBUG_PRINT("info",("ndbcluster_check_ndb_apply_status_share %s 0x%lx", NDB_APPLY_TABLE_FILE, (long) share)); pthread_mutex_unlock(&ndbcluster_mutex); @@ -758,9 +758,9 @@ static NDB_SHARE *ndbcluster_check_ndb_schema_share() { pthread_mutex_lock(&ndbcluster_mutex); - void *share= hash_search(&ndbcluster_open_tables, - (uchar*) NDB_SCHEMA_TABLE_FILE, - sizeof(NDB_SCHEMA_TABLE_FILE) - 1); + void *share= my_hash_search(&ndbcluster_open_tables, + (uchar*) NDB_SCHEMA_TABLE_FILE, + sizeof(NDB_SCHEMA_TABLE_FILE) - 1); DBUG_PRINT("info",("ndbcluster_check_ndb_schema_share %s 0x%lx", NDB_SCHEMA_TABLE_FILE, (long) share)); pthread_mutex_unlock(&ndbcluster_mutex); @@ -2184,8 +2184,8 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, { pthread_mutex_lock(&ndbcluster_mutex); NDB_SCHEMA_OBJECT *ndb_schema_object= - (NDB_SCHEMA_OBJECT*) hash_search(&ndb_schema_objects, - (uchar*) key, strlen(key)); + (NDB_SCHEMA_OBJECT*) my_hash_search(&ndb_schema_objects, + (uchar*) key, strlen(key)); if (ndb_schema_object) { pthread_mutex_lock(&ndb_schema_object->mutex); @@ -2571,8 +2571,8 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key, pthread_mutex_lock(&ndbcluster_mutex); /* Handle any trailing share */ - NDB_SHARE *share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables, - (uchar*) key, key_len); + NDB_SHARE *share= (NDB_SHARE*) my_hash_search(&ndbcluster_open_tables, + (uchar*) key, key_len); if (share && share_may_exist) { @@ -3558,9 +3558,9 @@ static NDB_SCHEMA_OBJECT *ndb_get_schema_object(const char *key, if (!have_lock) pthread_mutex_lock(&ndbcluster_mutex); while (!(ndb_schema_object= - (NDB_SCHEMA_OBJECT*) hash_search(&ndb_schema_objects, - (uchar*) key, - length))) + (NDB_SCHEMA_OBJECT*) my_hash_search(&ndb_schema_objects, + (uchar*) key, + length))) { if (!create_if_not_exists) { @@ -3609,7 +3609,7 @@ static void ndb_free_schema_object(NDB_SCHEMA_OBJECT **ndb_schema_object, if (!--(*ndb_schema_object)->use_count) { DBUG_PRINT("info", ("use_count: %d", (*ndb_schema_object)->use_count)); - hash_delete(&ndb_schema_objects, (uchar*) *ndb_schema_object); + my_hash_delete(&ndb_schema_objects, (uchar*) *ndb_schema_object); pthread_mutex_destroy(&(*ndb_schema_object)->mutex); my_free((uchar*) *ndb_schema_object, MYF(0)); *ndb_schema_object= 0; @@ -3715,8 +3715,8 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) } /* init hash for schema object distribution */ - (void) hash_init(&ndb_schema_objects, system_charset_info, 32, 0, 0, - (hash_get_key)ndb_schema_objects_get_key, 0, 0); + (void) my_hash_init(&ndb_schema_objects, system_charset_info, 32, 0, 0, + (my_hash_get_key)ndb_schema_objects_get_key, 0, 0); /* Expose global reference to our ndb object. @@ -4367,7 +4367,7 @@ err: i_ndb= 0; } - hash_free(&ndb_schema_objects); + my_hash_free(&ndb_schema_objects); net_end(&thd->net); thd->cleanup(); diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 5ebe5c2faea..7e5eccb2374 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1,4 +1,4 @@ -/* Copyright 2005-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright 2005-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -245,7 +245,7 @@ void ha_partition::init_handler_variables() /* this allows blackhole to work properly */ - m_no_locks= 0; + m_num_locks= 0; #ifdef DONT_HAVE_TO_BE_INITALIZED m_start_key.flag= 0; @@ -580,8 +580,8 @@ int ha_partition::drop_partitions(const char *path) { List_iterator<partition_element> part_it(m_part_info->partitions); char part_name_buff[FN_REFLEN]; - uint no_parts= m_part_info->partitions.elements; - uint no_subparts= m_part_info->no_subparts; + uint num_parts= m_part_info->partitions.elements; + uint num_subparts= m_part_info->num_subparts; uint i= 0; uint name_variant; int ret_error; @@ -611,7 +611,7 @@ int ha_partition::drop_partitions(const char *path) do { partition_element *sub_elem= sub_it++; - part= i * no_subparts + j; + part= i * num_subparts + j; create_subpartition_name(part_name_buff, path, part_elem->partition_name, sub_elem->partition_name, name_variant); @@ -621,7 +621,7 @@ int ha_partition::drop_partitions(const char *path) error= ret_error; if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos)) error= 1; - } while (++j < no_subparts); + } while (++j < num_subparts); } else { @@ -640,7 +640,7 @@ int ha_partition::drop_partitions(const char *path) else part_elem->part_state= PART_IS_DROPPED; } - } while (++i < no_parts); + } while (++i < num_parts); VOID(sync_ddl_log()); DBUG_RETURN(error); } @@ -671,9 +671,9 @@ int ha_partition::rename_partitions(const char *path) List_iterator<partition_element> temp_it(m_part_info->temp_partitions); char part_name_buff[FN_REFLEN]; char norm_name_buff[FN_REFLEN]; - uint no_parts= m_part_info->partitions.elements; + uint num_parts= m_part_info->partitions.elements; uint part_count= 0; - uint no_subparts= m_part_info->no_subparts; + uint num_subparts= m_part_info->num_subparts; uint i= 0; uint j= 0; int error= 0; @@ -722,7 +722,7 @@ int ha_partition::rename_partitions(const char *path) error= 1; else sub_elem->log_entry= NULL; /* Indicate success */ - } while (++j < no_subparts); + } while (++j < num_subparts); } else { @@ -778,7 +778,7 @@ int ha_partition::rename_partitions(const char *path) do { sub_elem= sub_it++; - part= i * no_subparts + j; + part= i * num_subparts + j; create_subpartition_name(norm_name_buff, path, part_elem->partition_name, sub_elem->partition_name, @@ -807,7 +807,7 @@ int ha_partition::rename_partitions(const char *path) error= 1; else sub_elem->log_entry= NULL; - } while (++j < no_subparts); + } while (++j < num_subparts); } else { @@ -839,7 +839,7 @@ int ha_partition::rename_partitions(const char *path) part_elem->log_entry= NULL; } } - } while (++i < no_parts); + } while (++i < num_parts); VOID(sync_ddl_log()); DBUG_RETURN(error); } @@ -1093,8 +1093,8 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flag) { List_iterator<partition_element> part_it(m_part_info->partitions); - uint no_parts= m_part_info->no_parts; - uint no_subparts= m_part_info->no_subparts; + uint num_parts= m_part_info->num_parts; + uint num_subparts= m_part_info->num_subparts; uint i= 0; int error; DBUG_ENTER("ha_partition::handle_opt_partitions"); @@ -1118,7 +1118,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, do { sub_elem= subpart_it++; - part= i * no_subparts + j; + part= i * num_subparts + j; DBUG_PRINT("info", ("Optimize subpartition %u (%s)", part, sub_elem->partition_name)); #ifdef NOT_USED @@ -1148,7 +1148,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, } while (part_elem= part_it++); DBUG_RETURN(error); } - } while (++j < no_subparts); + } while (++j < num_subparts); } else { @@ -1183,7 +1183,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, } part_elem->part_state= PART_NORMAL; } - } while (++i < no_parts); + } while (++i < num_parts); DBUG_RETURN(FALSE); } @@ -1387,10 +1387,10 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, List_iterator<partition_element> part_it(m_part_info->partitions); List_iterator <partition_element> t_it(m_part_info->temp_partitions); char part_name_buff[FN_REFLEN]; - uint no_parts= m_part_info->partitions.elements; - uint no_subparts= m_part_info->no_subparts; + uint num_parts= m_part_info->partitions.elements; + uint num_subparts= m_part_info->num_subparts; uint i= 0; - uint no_remain_partitions, part_count, orig_count; + uint num_remain_partitions, part_count, orig_count; handler **new_file_array; int error= 1; bool first; @@ -1406,7 +1406,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, part_name_buff))); m_reorged_parts= 0; if (!m_part_info->is_sub_partitioned()) - no_subparts= 1; + num_subparts= 1; /* Step 1: @@ -1415,7 +1415,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, */ if (temp_partitions) { - m_reorged_parts= temp_partitions * no_subparts; + m_reorged_parts= temp_partitions * num_subparts; } else { @@ -1425,9 +1425,9 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, if (part_elem->part_state == PART_CHANGED || part_elem->part_state == PART_REORGED_DROPPED) { - m_reorged_parts+= no_subparts; + m_reorged_parts+= num_subparts; } - } while (++i < no_parts); + } while (++i < num_parts); } if (m_reorged_parts && !(m_reorged_file= (handler**)sql_calloc(sizeof(handler*)* @@ -1442,10 +1442,10 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, Calculate number of partitions after change and allocate space for their handler references. */ - no_remain_partitions= 0; + num_remain_partitions= 0; if (temp_partitions) { - no_remain_partitions= no_parts * no_subparts; + num_remain_partitions= num_parts * num_subparts; } else { @@ -1458,17 +1458,17 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, part_elem->part_state == PART_TO_BE_ADDED || part_elem->part_state == PART_CHANGED) { - no_remain_partitions+= no_subparts; + num_remain_partitions+= num_subparts; } - } while (++i < no_parts); + } while (++i < num_parts); } if (!(new_file_array= (handler**)sql_calloc(sizeof(handler*)* - (2*(no_remain_partitions + 1))))) + (2*(num_remain_partitions + 1))))) { - mem_alloc_error(sizeof(handler*)*2*(no_remain_partitions+1)); + mem_alloc_error(sizeof(handler*)*2*(num_remain_partitions+1)); DBUG_RETURN(ER_OUTOFMEMORY); } - m_added_file= &new_file_array[no_remain_partitions + 1]; + m_added_file= &new_file_array[num_remain_partitions + 1]; /* Step 3: @@ -1487,9 +1487,9 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, part_elem->part_state == PART_REORGED_DROPPED) { memcpy((void*)&m_reorged_file[part_count], - (void*)&m_file[i*no_subparts], - sizeof(handler*)*no_subparts); - part_count+= no_subparts; + (void*)&m_file[i*num_subparts], + sizeof(handler*)*num_subparts); + part_count+= num_subparts; } else if (first && temp_partitions && part_elem->part_state == PART_TO_BE_ADDED) @@ -1504,11 +1504,11 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, ones used to be. */ first= FALSE; - DBUG_ASSERT(((i*no_subparts) + m_reorged_parts) <= m_file_tot_parts); - memcpy((void*)m_reorged_file, &m_file[i*no_subparts], + DBUG_ASSERT(((i*num_subparts) + m_reorged_parts) <= m_file_tot_parts); + memcpy((void*)m_reorged_file, &m_file[i*num_subparts], sizeof(handler*)*m_reorged_parts); } - } while (++i < no_parts); + } while (++i < num_parts); } /* @@ -1526,11 +1526,11 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, partition_element *part_elem= part_it++; if (part_elem->part_state == PART_NORMAL) { - DBUG_ASSERT(orig_count + no_subparts <= m_file_tot_parts); + DBUG_ASSERT(orig_count + num_subparts <= m_file_tot_parts); memcpy((void*)&new_file_array[part_count], (void*)&m_file[orig_count], - sizeof(handler*)*no_subparts); - part_count+= no_subparts; - orig_count+= no_subparts; + sizeof(handler*)*num_subparts); + part_count+= num_subparts; + orig_count+= num_subparts; } else if (part_elem->part_state == PART_CHANGED || part_elem->part_state == PART_TO_BE_ADDED) @@ -1546,16 +1546,16 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, mem_alloc_error(sizeof(handler)); DBUG_RETURN(ER_OUTOFMEMORY); } - } while (++j < no_subparts); + } while (++j < num_subparts); if (part_elem->part_state == PART_CHANGED) - orig_count+= no_subparts; + orig_count+= num_subparts; else if (temp_partitions && first) { - orig_count+= (no_subparts * temp_partitions); + orig_count+= (num_subparts * temp_partitions); first= FALSE; } } - } while (++i < no_parts); + } while (++i < num_parts); first= FALSE; /* Step 5: @@ -1592,7 +1592,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, part_elem->partition_name, sub_elem->partition_name, name_variant); - part= i * no_subparts + j; + part= i * num_subparts + j; DBUG_PRINT("info", ("Add subpartition %s", part_name_buff)); if ((error= prepare_new_partition(table, create_info, new_file_array[part], @@ -1603,7 +1603,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, DBUG_RETURN(error); } m_added_file[part_count++]= new_file_array[part]; - } while (++j < no_subparts); + } while (++j < num_subparts); } else { @@ -1622,7 +1622,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, m_added_file[part_count++]= new_file_array[i]; } } - } while (++i < no_parts); + } while (++i < num_parts); /* Step 6: @@ -1639,7 +1639,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, part_elem->part_state= PART_IS_CHANGED; else if (part_elem->part_state == PART_REORGED_DROPPED) part_elem->part_state= PART_TO_BE_DROPPED; - } while (++i < no_parts); + } while (++i < num_parts); for (i= 0; i < temp_partitions; i++) { partition_element *part_elem= t_it++; @@ -1680,9 +1680,9 @@ int ha_partition::copy_partitions(ulonglong * const copied, if (m_part_info->linear_hash_ind) { if (m_part_info->part_type == HASH_PARTITION) - set_linear_hash_mask(m_part_info, m_part_info->no_parts); + set_linear_hash_mask(m_part_info, m_part_info->num_parts); else - set_linear_hash_mask(m_part_info, m_part_info->no_subparts); + set_linear_hash_mask(m_part_info, m_part_info->num_subparts); } while (reorg_part < m_reorged_parts) @@ -1961,7 +1961,7 @@ partition_element *ha_partition::find_partition_element(uint part_id) uint curr_part_id= 0; List_iterator_fast <partition_element> part_it(m_part_info->partitions); - for (i= 0; i < m_part_info->no_parts; i++) + for (i= 0; i < m_part_info->num_parts; i++) { partition_element *part_elem; part_elem= part_it++; @@ -1969,7 +1969,7 @@ partition_element *ha_partition::find_partition_element(uint part_id) { uint j; List_iterator_fast <partition_element> sub_it(part_elem->subpartitions); - for (j= 0; j < m_part_info->no_subparts; j++) + for (j= 0; j < m_part_info->num_subparts; j++) { part_elem= sub_it++; if (part_id == curr_part_id++) @@ -2090,7 +2090,7 @@ bool ha_partition::create_handler_file(const char *name) { partition_element *part_elem, *subpart_elem; uint i, j, part_name_len, subpart_name_len; - uint tot_partition_words, tot_name_len, no_parts; + uint tot_partition_words, tot_name_len, num_parts; uint tot_parts= 0; uint tot_len_words, tot_len_byte, chksum, tot_name_words; char *name_buffer_ptr; @@ -2103,11 +2103,11 @@ bool ha_partition::create_handler_file(const char *name) List_iterator_fast <partition_element> part_it(m_part_info->partitions); DBUG_ENTER("create_handler_file"); - no_parts= m_part_info->partitions.elements; - DBUG_PRINT("info", ("table name = %s, no_parts = %u", name, - no_parts)); + num_parts= m_part_info->partitions.elements; + DBUG_PRINT("info", ("table name = %s, num_parts = %u", name, + num_parts)); tot_name_len= 0; - for (i= 0; i < no_parts; i++) + for (i= 0; i < num_parts; i++) { part_elem= part_it++; if (part_elem->part_state != PART_NORMAL && @@ -2125,7 +2125,7 @@ bool ha_partition::create_handler_file(const char *name) else { List_iterator_fast <partition_element> sub_it(part_elem->subpartitions); - for (j= 0; j < m_part_info->no_subparts; j++) + for (j= 0; j < m_part_info->num_subparts; j++) { subpart_elem= sub_it++; tablename_to_filename(subpart_elem->partition_name, @@ -2159,7 +2159,7 @@ bool ha_partition::create_handler_file(const char *name) engine_array= (file_buffer + 12); name_buffer_ptr= (char*) (file_buffer + ((4 + tot_partition_words) * 4)); part_it.rewind(); - for (i= 0; i < no_parts; i++) + for (i= 0; i < num_parts; i++) { part_elem= part_it++; if (part_elem->part_state != PART_NORMAL && @@ -2177,7 +2177,7 @@ bool ha_partition::create_handler_file(const char *name) else { List_iterator_fast <partition_element> sub_it(part_elem->subpartitions); - for (j= 0; j < m_part_info->no_subparts; j++) + for (j= 0; j < m_part_info->num_subparts; j++) { subpart_elem= sub_it++; tablename_to_filename(part_elem->partition_name, part_name, @@ -2313,7 +2313,7 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root) } m_file_tot_parts= m_tot_parts; bzero((char*) m_file, alloc_len); - DBUG_ASSERT(m_part_info->no_parts > 0); + DBUG_ASSERT(m_part_info->num_parts > 0); i= 0; part_count= 0; @@ -2326,7 +2326,7 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root) part_elem= part_it++; if (m_is_sub_partitioned) { - for (j= 0; j < m_part_info->no_subparts; j++) + for (j= 0; j < m_part_info->num_subparts; j++) { if (!(m_file[part_count++]= get_new_handler(table_share, mem_root, part_elem->engine_type))) @@ -2343,7 +2343,7 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root) DBUG_PRINT("info", ("engine_type: %u", (uint) ha_legacy_type(part_elem->engine_type))); } - } while (++i < m_part_info->no_parts); + } while (++i < m_part_info->num_parts); if (part_elem->engine_type == myisam_hton) { DBUG_PRINT("info", ("MyISAM")); @@ -2452,6 +2452,21 @@ err1: /**************************************************************************** MODULE open/close object ****************************************************************************/ + + +/** + A destructor for partition-specific TABLE_SHARE data. +*/ + +void ha_data_partition_destroy(void *ha_data) +{ + if (ha_data) + { + HA_DATA_PARTITION *ha_data_partition= (HA_DATA_PARTITION*) ha_data; + pthread_mutex_destroy(&ha_data_partition->mutex); + } +} + /* Open handler object @@ -2546,7 +2561,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) if ((error= (*file)->ha_open(table, (const char*) name_buff, mode, test_if_locked))) goto err_handler; - m_no_locks+= (*file)->lock_count(); + m_num_locks+= (*file)->lock_count(); name_buffer_ptr+= strlen(name_buffer_ptr) + 1; set_if_bigger(ref_length, ((*file)->ref_length)); /* @@ -2608,6 +2623,8 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) } DBUG_PRINT("info", ("table_share->ha_data 0x%p", ha_data)); bzero(ha_data, sizeof(HA_DATA_PARTITION)); + table_share->ha_data_destroy= ha_data_partition_destroy; + pthread_mutex_init(&ha_data->mutex, MY_MUTEX_INIT_FAST); } if (is_not_tmp_table) pthread_mutex_unlock(&table_share->mutex); @@ -2893,8 +2910,8 @@ int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type) uint ha_partition::lock_count() const { DBUG_ENTER("ha_partition::lock_count"); - DBUG_PRINT("info", ("m_no_locks %d", m_no_locks)); - DBUG_RETURN(m_no_locks); + DBUG_PRINT("info", ("m_num_locks %d", m_num_locks)); + DBUG_RETURN(m_num_locks); } @@ -3305,13 +3322,13 @@ int ha_partition::delete_all_rows() /* ALTER TABLE t TRUNCATE PARTITION ... */ List_iterator<partition_element> part_it(m_part_info->partitions); int saved_error= 0; - uint no_parts= m_part_info->no_parts; - uint no_subparts= m_part_info->no_subparts; + uint num_parts= m_part_info->num_parts; + uint num_subparts= m_part_info->num_subparts; uint i= 0; - uint no_parts_set= alter_info->partition_names.elements; - uint no_parts_found= set_part_state(alter_info, m_part_info, - PART_ADMIN); - if (no_parts_set != no_parts_found && + uint num_parts_set= alter_info->partition_names.elements; + uint num_parts_found= set_part_state(alter_info, m_part_info, + PART_ADMIN); + if (num_parts_set != num_parts_found && (!(alter_info->flags & ALTER_ALL_PARTITION))) DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); @@ -3334,7 +3351,7 @@ int ha_partition::delete_all_rows() do { sub_elem= subpart_it++; - part= i * no_subparts + j; + part= i * num_subparts + j; bitmap_set_bit(&m_part_info->used_partitions, part); if (!saved_error) { @@ -3348,7 +3365,7 @@ int ha_partition::delete_all_rows() error != HA_ERR_WRONG_COMMAND) saved_error= error; } - } while (++j < no_subparts); + } while (++j < num_subparts); } else { @@ -3368,7 +3385,7 @@ int ha_partition::delete_all_rows() } part_elem->part_state= PART_NORMAL; } - } while (++i < no_parts); + } while (++i < num_parts); DBUG_RETURN(saved_error); } truncate= TRUE; diff --git a/sql/ha_partition.h b/sql/ha_partition.h index c62318daf84..d4579d013fd 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1,7 +1,7 @@ #ifndef HA_PARTITION_INCLUDED #define HA_PARTITION_INCLUDED -/* Copyright 2005-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright 2005-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,7 +22,8 @@ enum partition_keywords { - PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR + PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR, + PKW_COLUMNS }; /* @@ -48,6 +49,7 @@ typedef struct st_ha_data_partition { ulonglong next_auto_inc_val; /**< first non reserved value */ bool auto_inc_initialized; + pthread_mutex_t mutex; } HA_DATA_PARTITION; #define PARTITION_BYTES_IN_POS 2 @@ -114,7 +116,7 @@ private: uint m_reorged_parts; // Number of reorganised parts uint m_tot_parts; // Total number of partitions; - uint m_no_locks; // For engines like ha_blackhole, which needs no locks + uint m_num_locks; // For engines like ha_blackhole, which needs no locks uint m_last_part; // Last file that we update,write,read int m_lock_type; // Remembers type of last // external_lock @@ -246,10 +248,10 @@ public: size_t pack_frm_len); virtual int drop_partitions(const char *path); virtual int rename_partitions(const char *path); - bool get_no_parts(const char *name, uint *no_parts) + bool get_no_parts(const char *name, uint *num_parts) { DBUG_ENTER("ha_partition::get_no_parts"); - *no_parts= m_tot_parts; + *num_parts= m_tot_parts; DBUG_RETURN(0); } virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share); diff --git a/sql/handler.cc b/sql/handler.cc index bc8c627a6ae..cccedc20f48 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1570,7 +1570,7 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin, } // recovery mode if (info->commit_list ? - hash_search(info->commit_list, (uchar *)&x, sizeof(x)) != 0 : + my_hash_search(info->commit_list, (uchar *)&x, sizeof(x)) != 0 : tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT) { #ifndef DBUG_OFF @@ -1681,12 +1681,12 @@ bool mysql_xa_recover(THD *thd) field_list.push_back(new Item_int("bqual_length", 0, MY_INT32_NUM_DECIMAL_DIGITS)); field_list.push_back(new Item_empty_string("data",XIDDATASIZE)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); pthread_mutex_lock(&LOCK_xid_cache); - while ((xs= (XID_STATE*)hash_element(&xid_cache, i++))) + while ((xs= (XID_STATE*) my_hash_element(&xid_cache, i++))) { if (xs->xa_state==XA_PREPARED) { @@ -3009,9 +3009,9 @@ static bool update_frm_version(TABLE *table) if ((result= my_pwrite(file,(uchar*) version,4,51L,MYF_RW))) goto err; - for (entry=(TABLE*) hash_first(&open_cache,(uchar*) key,key_length, &state); + for (entry=(TABLE*) my_hash_first(&open_cache,(uchar*) key,key_length, &state); entry; - entry= (TABLE*) hash_next(&open_cache,(uchar*) key,key_length, &state)) + entry= (TABLE*) my_hash_next(&open_cache,(uchar*) key,key_length, &state)) entry->s->mysql_version= MYSQL_VERSION_ID; } err: @@ -4467,7 +4467,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) field_list.push_back(new Item_empty_string("Name",FN_REFLEN)); field_list.push_back(new Item_empty_string("Status",10)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) return TRUE; diff --git a/sql/handler.h b/sql/handler.h index 632c262b59a..ffac0472f2a 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -515,9 +515,8 @@ class st_alter_tablespace : public Sql_alloc /* The handler for a table type. Will be included in the TABLE structure */ -struct st_table; -typedef struct st_table TABLE; -typedef struct st_table_share TABLE_SHARE; +struct TABLE; +struct TABLE_SHARE; struct st_foreign_key_info; typedef struct st_foreign_key_info FOREIGN_KEY_INFO; typedef bool (stat_print_fn)(THD *thd, const char *type, uint type_len, @@ -592,6 +591,7 @@ struct handler_iterator { void *buffer; }; +class handler; /* handlerton is a singleton structure - one instance per storage engine - to provide access to storage engine functionality that works on the @@ -1095,8 +1095,8 @@ class handler :public Sql_alloc public: typedef ulonglong Table_flags; protected: - struct st_table_share *table_share; /* The table definition */ - struct st_table *table; /* The current open table */ + TABLE_SHARE *table_share; /* The table definition */ + TABLE *table; /* The current open table */ Table_flags cached_table_flags; /* Set on init() and open() */ ha_rows estimation_rows_to_insert; @@ -1172,7 +1172,7 @@ public: virtual ~handler(void) { DBUG_ASSERT(locked == FALSE); - /* TODO: DBUG_ASSERT(inited == NONE); */ + DBUG_ASSERT(inited == NONE); } virtual handler *clone(MEM_ROOT *mem_root); /** This is called after create to allow us to set up cached variables */ diff --git a/sql/hash_filo.h b/sql/hash_filo.h index ab13d338695..5d17b880b4d 100644 --- a/sql/hash_filo.h +++ b/sql/hash_filo.h @@ -38,8 +38,8 @@ class hash_filo_element class hash_filo { const uint size, key_offset, key_length; - const hash_get_key get_key; - hash_free_key free_element; + const my_hash_get_key get_key; + my_hash_free_key free_element; bool init; CHARSET_INFO *hash_charset; @@ -49,7 +49,7 @@ public: HASH cache; hash_filo(uint size_arg, uint key_offset_arg , uint key_length_arg, - hash_get_key get_key_arg, hash_free_key free_element_arg, + my_hash_get_key get_key_arg, my_hash_free_key free_element_arg, CHARSET_INFO *hash_charset_arg) :size(size_arg), key_offset(key_offset_arg), key_length(key_length_arg), get_key(get_key_arg), free_element(free_element_arg),init(0), @@ -63,7 +63,7 @@ public: if (init) { if (cache.array.buffer) /* Avoid problems with thread library */ - (void) hash_free(&cache); + (void) my_hash_free(&cache); pthread_mutex_destroy(&lock); } } @@ -76,8 +76,8 @@ public: } if (!locked) (void) pthread_mutex_lock(&lock); - (void) hash_free(&cache); - (void) hash_init(&cache,hash_charset,size,key_offset, + (void) my_hash_free(&cache); + (void) my_hash_init(&cache,hash_charset,size,key_offset, key_length, get_key, free_element,0); if (!locked) (void) pthread_mutex_unlock(&lock); @@ -87,7 +87,7 @@ public: hash_filo_element *search(uchar* key, size_t length) { hash_filo_element *entry=(hash_filo_element*) - hash_search(&cache,(uchar*) key,length); + my_hash_search(&cache,(uchar*) key,length); if (entry) { // Found; link it first if (entry != first_link) @@ -113,7 +113,7 @@ public: { hash_filo_element *tmp=last_link; last_link=last_link->prev_used; - hash_delete(&cache,(uchar*) tmp); + my_hash_delete(&cache,(uchar*) tmp); } if (my_hash_insert(&cache,(uchar*) entry)) { diff --git a/sql/hostname.cc b/sql/hostname.cc index c8cf46383a9..45b10d16ce2 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -64,7 +64,7 @@ bool hostname_cache_init() uint offset= (uint) ((char*) (&tmp.ip) - (char*) &tmp); if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset, sizeof(struct in_addr),NULL, - (hash_free_key) free, + (my_hash_free_key) free, &my_charset_bin))) return 1; hostname_cache->clear(); diff --git a/sql/item.cc b/sql/item.cc index aca5370529c..dc0adfdc29e 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2586,7 +2586,8 @@ Item_param::Item_param(uint pos_in_query_arg) : param_type(MYSQL_TYPE_VARCHAR), pos_in_query(pos_in_query_arg), set_param_func(default_set_param_func), - limit_clause_param(FALSE) + limit_clause_param(FALSE), + m_out_param_info(NULL) { name= (char*) "?"; /* @@ -2668,6 +2669,17 @@ void Item_param::set_decimal(const char *str, ulong length) DBUG_VOID_RETURN; } +void Item_param::set_decimal(const my_decimal *dv) +{ + state= DECIMAL_VALUE; + + my_decimal2decimal(dv, &decimal_value); + + decimals= (uint8) decimal_value.frac; + unsigned_flag= !decimal_value.sign(); + max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, + decimals, unsigned_flag); +} /** Set parameter value from MYSQL_TIME value. @@ -3290,6 +3302,158 @@ Item_param::set_param_type_and_swap_value(Item_param *src) str_value_ptr.swap(src->str_value_ptr); } + +/** + This operation is intended to store some item value in Item_param to be + used later. + + @param thd thread context + @param ctx stored procedure runtime context + @param it a pointer to an item in the tree + + @return Error status + @retval TRUE on error + @retval FALSE on success +*/ + +bool +Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it) +{ + Item *value= *it; + + if (value->is_null()) + { + set_null(); + return FALSE; + } + + null_value= FALSE; + + switch (value->result_type()) { + case STRING_RESULT: + { + char str_buffer[STRING_BUFFER_USUAL_SIZE]; + String sv_buffer(str_buffer, sizeof(str_buffer), &my_charset_bin); + String *sv= value->val_str(&sv_buffer); + + if (!sv) + return TRUE; + + set_str(sv->c_ptr_safe(), sv->length()); + str_value_ptr.set(str_value.ptr(), + str_value.length(), + str_value.charset()); + collation.set(str_value.charset(), DERIVATION_COERCIBLE); + decimals= 0; + param_type= MYSQL_TYPE_STRING; + + break; + } + + case REAL_RESULT: + set_double(value->val_real()); + param_type= MYSQL_TYPE_DOUBLE; + break; + + case INT_RESULT: + set_int(value->val_int(), value->max_length); + param_type= MYSQL_TYPE_LONG; + break; + + case DECIMAL_RESULT: + { + my_decimal dv_buf; + my_decimal *dv= value->val_decimal(&dv_buf); + + if (!dv) + return TRUE; + + set_decimal(dv); + param_type= MYSQL_TYPE_NEWDECIMAL; + + break; + } + + default: + /* That can not happen. */ + + DBUG_ASSERT(TRUE); // Abort in debug mode. + + set_null(); // Set to NULL in release mode. + return FALSE; + } + + item_result_type= value->result_type(); + item_type= value->type(); + return FALSE; +} + + +/** + Setter of Item_param::m_out_param_info. + + m_out_param_info is used to store information about store routine + OUT-parameters, such as stored routine name, database, stored routine + variable name. It is supposed to be set in sp_head::execute() after + Item_param::set_value() is called. +*/ + +void +Item_param::set_out_param_info(Send_field *info) +{ + m_out_param_info= info; +} + + +/** + Getter of Item_param::m_out_param_info. + + m_out_param_info is used to store information about store routine + OUT-parameters, such as stored routine name, database, stored routine + variable name. It is supposed to be retrieved in + Protocol_binary::send_out_parameters() during creation of OUT-parameter + result set. +*/ + +const Send_field * +Item_param::get_out_param_info() const +{ + return m_out_param_info; +} + + +/** + Fill meta-data information for the corresponding column in a result set. + If this is an OUT-parameter of a stored procedure, preserve meta-data of + stored-routine variable. + + @param field container for meta-data to be filled +*/ + +void Item_param::make_field(Send_field *field) +{ + Item::make_field(field); + + if (!m_out_param_info) + return; + + /* + This is an OUT-parameter of stored procedure. We should use + OUT-parameter info to fill out the names. + */ + + field->db_name= m_out_param_info->db_name; + field->table_name= m_out_param_info->table_name; + field->org_table_name= m_out_param_info->org_table_name; + field->col_name= m_out_param_info->col_name; + field->org_col_name= m_out_param_info->org_col_name; + field->length= m_out_param_info->length; + field->charsetnr= m_out_param_info->charsetnr; + field->flags= m_out_param_info->flags; + field->decimals= m_out_param_info->decimals; + field->type= m_out_param_info->type; +} + /**************************************************************************** Item_copy ****************************************************************************/ @@ -3521,7 +3685,7 @@ void Item_copy_decimal::copy() /* - Functions to convert item to field (for send_fields) + Functions to convert item to field (for send_result_set_metadata) */ /* ARGSUSED */ diff --git a/sql/item.h b/sql/item.h index b44e84f4b15..bb7ef73a5e2 100644 --- a/sql/item.h +++ b/sql/item.h @@ -453,6 +453,11 @@ public: TRUE if error has occured. */ virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0; + + virtual void set_out_param_info(Send_field *info) {} + + virtual const Send_field *get_out_param_info() const + { return NULL; } }; @@ -1563,7 +1568,8 @@ public: /* Item represents one placeholder ('?') of prepared statement */ -class Item_param :public Item +class Item_param :public Item, + private Settable_routine_parameter { char cnvbuf[MAX_FIELD_WIDTH]; String cnvstr; @@ -1651,6 +1657,7 @@ public: void set_int(longlong i, uint32 max_length_arg); void set_double(double i); void set_decimal(const char *str, ulong length); + void set_decimal(const my_decimal *dv); bool set_str(const char *str, ulong length); bool set_longdata(const char *str, ulong length); void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg); @@ -1700,6 +1707,25 @@ public: /** Item is a argument to a limit clause. */ bool limit_clause_param; void set_param_type_and_swap_value(Item_param *from); + +private: + virtual inline Settable_routine_parameter * + get_settable_routine_parameter() + { + return this; + } + + virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it); + + virtual void set_out_param_info(Send_field *info); + +public: + virtual const Send_field *get_out_param_info() const; + + virtual void make_field(Send_field *field); + +private: + Send_field *m_out_param_info; }; @@ -2057,7 +2083,7 @@ public: /** Item_empty_string -- is a utility class to put an item into List<Item> - which is then used in protocol.send_fields() when sending SHOW output to + which is then used in protocol.send_result_set_metadata() when sending SHOW output to the client. */ diff --git a/sql/item_create.cc b/sql/item_create.cc index 2f80d16b928..070ab5263dd 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -2052,6 +2052,18 @@ protected: virtual ~Create_func_to_days() {} }; +class Create_func_to_seconds : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_to_seconds s_singleton; + +protected: + Create_func_to_seconds() {} + virtual ~Create_func_to_seconds() {} +}; + #ifdef HAVE_SPATIAL class Create_func_touches : public Create_func_arg2 @@ -4505,6 +4517,15 @@ Create_func_to_days::create(THD *thd, Item *arg1) } +Create_func_to_seconds Create_func_to_seconds::s_singleton; + +Item* +Create_func_to_seconds::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_to_seconds(arg1); +} + + #ifdef HAVE_SPATIAL Create_func_touches Create_func_touches::s_singleton; @@ -4942,6 +4963,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)}, { { C_STRING_WITH_LEN("TOUCHES") }, GEOM_BUILDER(Create_func_touches)}, { { C_STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)}, + { { C_STRING_WITH_LEN("TO_SECONDS") }, BUILDER(Create_func_to_seconds)}, { { C_STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)}, { { C_STRING_WITH_LEN("UNCOMPRESS") }, BUILDER(Create_func_uncompress)}, { { C_STRING_WITH_LEN("UNCOMPRESSED_LENGTH") }, BUILDER(Create_func_uncompressed_length)}, @@ -4985,14 +5007,14 @@ int item_create_init() DBUG_ENTER("item_create_init"); - if (hash_init(& native_functions_hash, - system_charset_info, - array_elements(func_array), - 0, - 0, - (hash_get_key) get_native_fct_hash_key, - NULL, /* Nothing to free */ - MYF(0))) + if (my_hash_init(& native_functions_hash, + system_charset_info, + array_elements(func_array), + 0, + 0, + (my_hash_get_key) get_native_fct_hash_key, + NULL, /* Nothing to free */ + MYF(0))) DBUG_RETURN(1); for (func= func_array; func->builder != NULL; func++) @@ -5004,7 +5026,7 @@ int item_create_init() #ifndef DBUG_OFF for (uint i=0 ; i < native_functions_hash.records ; i++) { - func= (Native_func_registry*) hash_element(& native_functions_hash, i); + func= (Native_func_registry*) my_hash_element(& native_functions_hash, i); DBUG_PRINT("info", ("native function: %s length: %u", func->name.str, (uint) func->name.length)); } @@ -5022,7 +5044,7 @@ int item_create_init() void item_create_cleanup() { DBUG_ENTER("item_create_cleanup"); - hash_free(& native_functions_hash); + my_hash_free(& native_functions_hash); DBUG_VOID_RETURN; } @@ -5033,9 +5055,9 @@ find_native_function_builder(THD *thd, LEX_STRING name) Create_func *builder= NULL; /* Thread safe */ - func= (Native_func_registry*) hash_search(& native_functions_hash, - (uchar*) name.str, - name.length); + func= (Native_func_registry*) my_hash_search(& native_functions_hash, + (uchar*) name.str, + name.length); if (func) { diff --git a/sql/item_func.cc b/sql/item_func.cc index c0d5fc6480d..f4db3ef03e7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3283,7 +3283,7 @@ public: { if (key) { - hash_delete(&hash_user_locks,(uchar*) this); + my_hash_delete(&hash_user_locks,(uchar*) this); my_free(key, MYF(0)); } pthread_cond_destroy(&cond); @@ -3307,8 +3307,8 @@ static bool item_user_lock_inited= 0; void item_user_lock_init(void) { pthread_mutex_init(&LOCK_user_locks,MY_MUTEX_INIT_SLOW); - hash_init(&hash_user_locks,system_charset_info, - 16,0,0,(hash_get_key) ull_get_key,NULL,0); + my_hash_init(&hash_user_locks,system_charset_info, + 16,0,0,(my_hash_get_key) ull_get_key,NULL,0); item_user_lock_inited= 1; } @@ -3317,7 +3317,7 @@ void item_user_lock_free(void) if (item_user_lock_inited) { item_user_lock_inited= 0; - hash_free(&hash_user_locks); + my_hash_free(&hash_user_locks); pthread_mutex_destroy(&LOCK_user_locks); } } @@ -3384,9 +3384,9 @@ void debug_sync_point(const char* lock_name, uint lock_timeout) this case, we will not be waiting, but rather, just waste CPU and memory on the whole deal */ - if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks, - (uchar*) lock_name, - lock_name_len)))) + if (!(ull= ((User_level_lock*) my_hash_search(&hash_user_locks, + (uchar*) lock_name, + lock_name_len)))) { pthread_mutex_unlock(&LOCK_user_locks); return; @@ -3487,9 +3487,9 @@ longlong Item_func_get_lock::val_int() thd->ull=0; } - if (!(ull= ((User_level_lock *) hash_search(&hash_user_locks, - (uchar*) res->ptr(), - (size_t) res->length())))) + if (!(ull= ((User_level_lock *) my_hash_search(&hash_user_locks, + (uchar*) res->ptr(), + (size_t) res->length())))) { ull= new User_level_lock((uchar*) res->ptr(), (size_t) res->length(), thd->thread_id); @@ -3591,9 +3591,9 @@ longlong Item_func_release_lock::val_int() result=0; pthread_mutex_lock(&LOCK_user_locks); - if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks, - (const uchar*) res->ptr(), - (size_t) res->length())))) + if (!(ull= ((User_level_lock*) my_hash_search(&hash_user_locks, + (const uchar*) res->ptr(), + (size_t) res->length())))) { null_value=1; } @@ -3773,12 +3773,12 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name, { user_var_entry *entry; - if (!(entry = (user_var_entry*) hash_search(hash, (uchar*) name.str, - name.length)) && + if (!(entry = (user_var_entry*) my_hash_search(hash, (uchar*) name.str, + name.length)) && create_if_not_exists) { uint size=ALIGN_SIZE(sizeof(user_var_entry))+name.length+1+extra_size; - if (!hash_inited(hash)) + if (!my_hash_inited(hash)) return 0; if (!(entry = (user_var_entry*) my_malloc(size,MYF(MY_WME)))) return 0; @@ -5707,8 +5707,8 @@ longlong Item_func_is_free_lock::val_int() } pthread_mutex_lock(&LOCK_user_locks); - ull= (User_level_lock *) hash_search(&hash_user_locks, (uchar*) res->ptr(), - (size_t) res->length()); + ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(), + (size_t) res->length()); pthread_mutex_unlock(&LOCK_user_locks); if (!ull || !ull->locked) return 1; @@ -5726,8 +5726,8 @@ longlong Item_func_is_used_lock::val_int() return 0; pthread_mutex_lock(&LOCK_user_locks); - ull= (User_level_lock *) hash_search(&hash_user_locks, (uchar*) res->ptr(), - (size_t) res->length()); + ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(), + (size_t) res->length()); pthread_mutex_unlock(&LOCK_user_locks); if (!ull || !ull->locked) return 0; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index da651cec70c..d16710f6660 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1863,7 +1863,8 @@ void subselect_uniquesubquery_engine::fix_length_and_dec(Item_cache **row) DBUG_ASSERT(0); } -int init_read_record_seq(JOIN_TAB *tab); +int read_first_record_seq(JOIN_TAB *tab); +int rr_sequential(READ_RECORD *info); int join_read_always_key_or_null(JOIN_TAB *tab); int join_read_next_same_or_null(READ_RECORD *info); @@ -1945,7 +1946,8 @@ int subselect_single_select_engine::exec() /* Change the access method to full table scan */ tab->save_read_first_record= tab->read_first_record; tab->save_read_record= tab->read_record.read_record; - tab->read_first_record= init_read_record_seq; + tab->read_record.read_record= rr_sequential; + tab->read_first_record= read_first_record_seq; tab->read_record.record= tab->table->record[0]; tab->read_record.thd= join->thd; tab->read_record.ref_length= tab->table->file->ref_length; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index c8576722c69..273e996a6a0 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -564,6 +564,11 @@ Item *Item_sum::set_arg(uint i, THD *thd, Item *new_val) int Item_sum::set_aggregator(Aggregator::Aggregator_type aggregator) { + if (aggr) + { + DBUG_ASSERT(aggregator == aggr->Aggrtype()); + return FALSE; + } switch (aggregator) { case Aggregator::DISTINCT_AGGREGATOR: @@ -736,12 +741,12 @@ bool Aggregator_distinct::setup(THD *thd) if (list.push_back(item)) return TRUE; // End of memory if (item->const_item() && item->is_null()) - always_null=1; + always_null= true; } if (always_null) return FALSE; - count_field_types(select_lex,tmp_table_param,list,0); - tmp_table_param->force_copy_fields= item_sum->force_copy_fields; + count_field_types(select_lex, tmp_table_param, list, 0); + tmp_table_param->force_copy_fields= item_sum->has_force_copy_fields(); DBUG_ASSERT(table == 0); /* Make create_tmp_table() convert BIT columns to BIGINT. @@ -844,10 +849,10 @@ bool Aggregator_distinct::setup(THD *thd) List<Create_field> field_list; Create_field field_def; /* field definition */ Item *arg; - DBUG_ENTER("Item_sum_distinct::setup"); + DBUG_ENTER("Aggregator_distinct::setup"); /* It's legal to call setup() more than once when in a subquery */ if (tree) - return FALSE; + DBUG_RETURN(FALSE); /* Virtual table and the tree are created anew on each re-execution of @@ -855,23 +860,23 @@ bool Aggregator_distinct::setup(THD *thd) mem_root. */ if (field_list.push_back(&field_def)) - return TRUE; + DBUG_RETURN(TRUE); item_sum->null_value= item_sum->maybe_null= 1; item_sum->quick_group= 0; DBUG_ASSERT(item_sum->get_arg(0)->fixed); - arg = item_sum->get_arg(0); + arg= item_sum->get_arg(0); if (arg->const_item()) { (void) arg->val_int(); if (arg->null_value) - always_null=1; + always_null= true; } if (always_null) - return FALSE; + DBUG_RETURN(FALSE); enum enum_field_types field_type; @@ -884,7 +889,7 @@ bool Aggregator_distinct::setup(THD *thd) arg->unsigned_flag); if (! (table= create_virtual_tmp_table(thd, field_list))) - return TRUE; + DBUG_RETURN(TRUE); /* XXX: check that the case of CHAR(0) works OK */ tree_key_length= table->s->reclength - table->s->null_bytes; diff --git a/sql/item_sum.h b/sql/item_sum.h index 62addf4e033..6d7418204fc 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -302,13 +302,14 @@ class st_select_lex; class Item_sum :public Item_result_field { -public: +protected: /** Aggregator class instance. Not set initially. Allocated only after it is determined if the incoming data are already distinct. */ Aggregator *aggr; +private: /** Used in making ROLLUP. Set for the ROLLUP copies of the original Item_sum and passed to create_tmp_field() to cause it to work @@ -324,6 +325,11 @@ public: */ bool with_distinct; +public: + + bool has_force_copy_fields() const { return force_copy_fields; } + bool has_with_distinct() const { return with_distinct; } + enum Sumfunctype { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC, AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC, @@ -447,12 +453,12 @@ public: may be initialized to 0 by clear() and to NULL by no_rows_in_result(). */ - void no_rows_in_result() + virtual void no_rows_in_result() { if (!aggr) - set_aggregator(with_distinct ? - Aggregator::DISTINCT_AGGREGATOR : - Aggregator::SIMPLE_AGGREGATOR); + set_aggregator(with_distinct ? + Aggregator::DISTINCT_AGGREGATOR : + Aggregator::SIMPLE_AGGREGATOR); reset(); } virtual void make_unique() { force_copy_fields= TRUE; } @@ -511,11 +517,12 @@ public: */ int set_aggregator(Aggregator::Aggregator_type aggregator); + virtual void clear()= 0; virtual bool add()= 0; - virtual bool setup(THD *thd) {return 0;} + virtual bool setup(THD *thd) { return false; } - void cleanup (); + virtual void cleanup(); }; @@ -533,9 +540,6 @@ class Unique; class Aggregator_distinct : public Aggregator { friend class Item_sum_sum; - friend class Item_sum_count; - friend class Item_sum_avg; -protected: /* flag to prevent consecutive runs of endup(). Normally in endup there are @@ -567,7 +571,7 @@ protected: uint32 *field_lengths; /* - used in conjunction with 'table' to support the access to Field classes + Used in conjunction with 'table' to support the access to Field classes for COUNT(DISTINCT). Needed by copy_fields()/copy_funcs(). */ TMP_TABLE_PARAM *tmp_table_param; @@ -637,7 +641,6 @@ public: class Item_sum_num :public Item_sum { - friend class Aggregator_distinct; protected: /* val_xxx() functions may be called several times during the execution of a @@ -692,14 +695,14 @@ protected: void fix_length_and_dec(); public: - Item_sum_sum(Item *item_par, bool distinct= FALSE) :Item_sum_num(item_par) + Item_sum_sum(Item *item_par, bool distinct) :Item_sum_num(item_par) { set_distinct(distinct); } Item_sum_sum(THD *thd, Item_sum_sum *item); enum Sumfunctype sum_func () const { - return with_distinct ? SUM_DISTINCT_FUNC : SUM_FUNC; + return has_with_distinct() ? SUM_DISTINCT_FUNC : SUM_FUNC; } void clear(); bool add(); @@ -713,7 +716,7 @@ public: void no_rows_in_result() {} const char *func_name() const { - return with_distinct ? "sum(distinct " : "sum("; + return has_with_distinct() ? "sum(distinct " : "sum("; } Item *copy_or_same(THD* thd); }; @@ -752,7 +755,7 @@ class Item_sum_count :public Item_sum_int {} enum Sumfunctype sum_func () const { - return with_distinct ? COUNT_DISTINCT_FUNC : COUNT_FUNC; + return has_with_distinct() ? COUNT_DISTINCT_FUNC : COUNT_FUNC; } void no_rows_in_result() { count=0; } void make_const(longlong count_arg) @@ -765,7 +768,7 @@ class Item_sum_count :public Item_sum_int void update_field(); const char *func_name() const { - return with_distinct ? "count(distinct " : "count("; + return has_with_distinct() ? "count(distinct " : "count("; } Item *copy_or_same(THD* thd); }; @@ -806,7 +809,7 @@ public: uint prec_increment; uint f_precision, f_scale, dec_bin_size; - Item_sum_avg(Item *item_par, bool distinct= FALSE) + Item_sum_avg(Item *item_par, bool distinct) :Item_sum_sum(item_par, distinct), count(0) {} Item_sum_avg(THD *thd, Item_sum_avg *item) @@ -816,7 +819,7 @@ public: void fix_length_and_dec(); enum Sumfunctype sum_func () const { - return with_distinct ? AVG_DISTINCT_FUNC : AVG_FUNC; + return has_with_distinct() ? AVG_DISTINCT_FUNC : AVG_FUNC; } void clear(); bool add(); @@ -832,7 +835,7 @@ public: void no_rows_in_result() {} const char *func_name() const { - return with_distinct ? "avg(distinct " : "avg("; + return has_with_distinct() ? "avg(distinct " : "avg("; } Item *copy_or_same(THD* thd); Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index eefc8429f5e..84ddc88487d 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -936,6 +936,48 @@ longlong Item_func_to_days::val_int() } +longlong Item_func_to_seconds::val_int_endpoint(bool left_endp, + bool *incl_endp) +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + longlong seconds; + longlong days; + int dummy; /* unused */ + if (get_arg0_date(<ime, TIME_FUZZY_DATE)) + { + /* got NULL, leave the incl_endp intact */ + return LONGLONG_MIN; + } + seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second; + seconds= ltime.neg ? -seconds : seconds; + days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day); + seconds+= days * 24L * 3600L; + /* Set to NULL if invalid date, but keep the value */ + null_value= check_date(<ime, + (ltime.year || ltime.month || ltime.day), + (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE), + &dummy); + /* + Even if the evaluation return NULL, seconds is useful for pruning + */ + return seconds; +} + +longlong Item_func_to_seconds::val_int() +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + longlong seconds; + longlong days; + if (get_arg0_date(<ime, TIME_NO_ZERO_DATE)) + return 0; + seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second; + seconds=ltime.neg ? -seconds : seconds; + days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day); + return seconds + days * 24L * 3600L; +} + /* Get information about this Item tree monotonicity @@ -962,6 +1004,17 @@ enum_monotonicity_info Item_func_to_days::get_monotonicity_info() const return NON_MONOTONIC; } +enum_monotonicity_info Item_func_to_seconds::get_monotonicity_info() const +{ + if (args[0]->type() == Item::FIELD_ITEM) + { + if (args[0]->field_type() == MYSQL_TYPE_DATE || + args[0]->field_type() == MYSQL_TYPE_DATETIME) + return MONOTONIC_STRICT_INCREASING_NOT_NULL; + } + return NON_MONOTONIC; +} + longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) { diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 7f1b2ed3a53..cdd74c8c601 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -76,6 +76,24 @@ public: }; +class Item_func_to_seconds :public Item_int_func +{ +public: + Item_func_to_seconds(Item *a) :Item_int_func(a) {} + longlong val_int(); + const char *func_name() const { return "to_seconds"; } + void fix_length_and_dec() + { + decimals=0; + max_length=6*MY_CHARSET_BIN_MB_MAXLEN; + maybe_null=1; + } + enum_monotonicity_info get_monotonicity_info() const; + longlong val_int_endpoint(bool left_endp, bool *incl_endp); + bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} +}; + + class Item_func_dayofmonth :public Item_int_func { public: @@ -874,6 +892,7 @@ public: { decimals=0; max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + maybe_null= 1; } longlong val_int(); }; @@ -1028,6 +1047,11 @@ public: Item_func_last_day(Item *a) :Item_date(a) {} const char *func_name() const { return "last_day"; } bool get_date(MYSQL_TIME *res, uint fuzzy_date); + void fix_length_and_dec() + { + Item_date::fix_length_and_dec(); + maybe_null= 1; + } }; #endif /* ITEM_TIMEFUNC_INCLUDED */ diff --git a/sql/lock.cc b/sql/lock.cc index 93d8b868688..c0cda1dbf03 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -1029,11 +1029,11 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use) if (check_in_use) { /* Only insert the table if we haven't insert it already */ - for (table=(TABLE*) hash_first(&open_cache, (uchar*)key, - key_length, &state); + for (table=(TABLE*) my_hash_first(&open_cache, (uchar*)key, + key_length, &state); table ; - table = (TABLE*) hash_next(&open_cache,(uchar*) key, - key_length, &state)) + table = (TABLE*) my_hash_next(&open_cache,(uchar*) key, + key_length, &state)) { if (table->in_use == thd) { @@ -1060,7 +1060,7 @@ void unlock_table_name(THD *thd, TABLE_LIST *table_list) { if (table_list->table) { - hash_delete(&open_cache, (uchar*) table_list->table); + my_hash_delete(&open_cache, (uchar*) table_list->table); broadcast_refresh(); } } @@ -1235,11 +1235,11 @@ is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key, HASH_SEARCH_STATE state; TABLE *table; - for (table= (TABLE*) hash_first(&open_cache, key, - key_length, &state); + for (table= (TABLE*) my_hash_first(&open_cache, key, + key_length, &state); table ; - table= (TABLE*) hash_next(&open_cache, key, - key_length, &state)) + table= (TABLE*) my_hash_next(&open_cache, key, + key_length, &state)) { if (table->in_use == thd && table->open_placeholder == 1 && diff --git a/sql/log.cc b/sql/log.cc index 4f5230b82cc..5450e19172b 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -110,9 +110,16 @@ sql_print_message_func sql_print_message_handlers[3] = }; +/** + Create the name of the default general log file + + @param[IN] buff Location for building new string. + @param[IN] log_ext The extension for the file (e.g .log) + @returns Pointer to a new string containing the name +*/ char *make_default_log_name(char *buff,const char* log_ext) { - strmake(buff, pidfile_name, FN_REFLEN-5); + strmake(buff, default_logfile_name, FN_REFLEN-5); return fn_format(buff, buff, mysql_data_home, log_ext, MYF(MY_UNPACK_FILENAME|MY_REPLACE_EXT)); } @@ -5493,8 +5500,8 @@ int TC_LOG_MMAP::recover() goto err1; } - if (hash_init(&xids, &my_charset_bin, tc_log_page_size/3, 0, - sizeof(my_xid), 0, 0, MYF(0))) + if (my_hash_init(&xids, &my_charset_bin, tc_log_page_size/3, 0, + sizeof(my_xid), 0, 0, MYF(0))) goto err1; for ( ; p < end_p ; p++) @@ -5507,12 +5514,12 @@ int TC_LOG_MMAP::recover() if (ha_recover(&xids)) goto err2; - hash_free(&xids); + my_hash_free(&xids); bzero(data, (size_t)file_length); return 0; err2: - hash_free(&xids); + my_hash_free(&xids); err1: sql_print_error("Crash recovery failed. Either correct the problem " "(if it's, for example, out of memory error) and restart, " @@ -5696,8 +5703,8 @@ int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle) MEM_ROOT mem_root; if (! fdle->is_valid() || - hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0, - sizeof(my_xid), 0, 0, MYF(0))) + my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0, + sizeof(my_xid), 0, 0, MYF(0))) goto err1; init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE); @@ -5722,12 +5729,12 @@ int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle) goto err2; free_root(&mem_root, MYF(0)); - hash_free(&xids); + my_hash_free(&xids); return 0; err2: free_root(&mem_root, MYF(0)); - hash_free(&xids); + my_hash_free(&xids); err1: sql_print_error("Crash recovery failed. Either correct the problem " "(if it's, for example, out of memory error) and restart, " diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index c9909298b63..b539e004481 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -305,7 +305,7 @@ protected: #define TABLE_OPEN_CACHE_DEFAULT 400 #define TABLE_DEF_CACHE_DEFAULT 400 /** - We must have room for at least 256 table definitions in the table + We must have room for at least 400 table definitions in the table cache, since otherwise there is no chance prepared statements that use these many tables can work. Prepared statements use table definition cache ids (table_map_id) @@ -672,7 +672,6 @@ enum enum_parsing_place IN_ON }; -struct st_table; #define thd_proc_info(thd, msg) set_thd_proc_info(thd, msg, __func__, __FILE__, __LINE__) class THD; @@ -910,6 +909,7 @@ bool general_log_write(THD *thd, enum enum_server_command command, #include "tztime.h" #ifdef MYSQL_SERVER #include "sql_servers.h" +#include "records.h" #include "opt_range.h" #ifdef HAVE_QUERY_CACHE @@ -917,7 +917,7 @@ struct Query_cache_query_flags { unsigned int client_long_flag:1; unsigned int client_protocol_41:1; - unsigned int result_in_binary_protocol:1; + unsigned int protocol_type:2; unsigned int more_results_exists:1; unsigned int in_trans:1; unsigned int autocommit:1; @@ -936,6 +936,8 @@ struct Query_cache_query_flags }; #define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags) #include "sql_cache.h" +#define query_cache_abort(A) query_cache.abort(A) +#define query_cache_end_of_result(A) query_cache.end_of_result(A) #define query_cache_store_query(A, B) query_cache.store_query(A, B) #define query_cache_destroy() query_cache.destroy() #define query_cache_result_size_limit(A) query_cache.result_size_limit(A) @@ -1057,9 +1059,11 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, bool *write_to_binlog); #ifndef NO_EMBEDDED_ACCESS_CHECKS bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv, - bool no_grant, bool no_errors, bool schema_db); -bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables, - uint number, bool no_errors); + bool no_grant, bool no_errors, bool schema_db); +bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, + bool any_combination_of_privileges_will_do, + uint number, + bool no_errors); #else inline bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv, bool no_grant, bool no_errors, @@ -1069,8 +1073,10 @@ inline bool check_access(THD *thd, ulong access, const char *db, *save_priv= GLOBAL_ACLS; return false; } -inline bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables, - uint number, bool no_errors) +inline bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, + bool no_errors, + bool any_combination_of_privileges_will_do, + uint number) { return false; } #endif /*NO_EMBEDDED_ACCESS_CHECKS*/ @@ -1163,6 +1169,8 @@ int prepare_create_field(Create_field *sql_field, uint *blob_columns, int *timestamps, int *timestamps_with_niladic, longlong table_flags); +CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, + HA_CREATE_INFO *create_info); bool mysql_create_table(THD *thd,const char *db, const char *table_name, HA_CREATE_INFO *create_info, Alter_info *alter_info, @@ -1334,7 +1342,6 @@ void remove_status_vars(SHOW_VAR *list); void init_status_vars(); void free_status_vars(); void reset_status_vars(); - /* information schema */ extern LEX_STRING INFORMATION_SCHEMA_NAME; /* log tables */ @@ -1361,19 +1368,6 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table); #define is_schema_db(X) \ !my_strcasecmp(system_charset_info, INFORMATION_SCHEMA_NAME.str, (X)) -/* sql_prepare.cc */ - -void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length); -void mysqld_stmt_execute(THD *thd, char *packet, uint packet_length); -void mysqld_stmt_close(THD *thd, char *packet); -void mysql_sql_stmt_prepare(THD *thd); -void mysql_sql_stmt_execute(THD *thd); -void mysql_sql_stmt_close(THD *thd); -void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length); -void mysqld_stmt_reset(THD *thd, char *packet); -void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); -void reinit_stmt_before_use(THD *thd, LEX *lex); - /* sql_handler.cc */ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen); bool mysql_ha_close(THD *thd, TABLE_LIST *tables); @@ -1552,6 +1546,11 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, handlerton *old_db_type, bool *partition_changed, uint *fast_alter_partition); +char *generate_partition_syntax(partition_info *part_info, + uint *buf_length, bool use_sql_alloc, + bool show_partition_options, + HA_CREATE_INFO *create_info, + Alter_info *alter_info); #endif /* bits for last argument to remove_table_from_cache() */ @@ -1873,6 +1872,7 @@ extern MYSQL_PLUGIN_IMPORT uint reg_ext_length; #ifdef MYSQL_SERVER extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN]; extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file; +extern char default_logfile_name[FN_REFLEN]; extern char log_error_file[FN_REFLEN], *opt_tc_log_file; extern ulonglong log_10_int[20]; extern ulonglong keybuff_size; @@ -1973,14 +1973,13 @@ extern FILE *bootstrap_file; extern int bootstrap_error; extern FILE *stderror_file; extern pthread_key(MEM_ROOT**,THR_MALLOC); -extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_lock_db, +extern pthread_mutex_t LOCK_mysql_create_db, LOCK_open, LOCK_lock_db, LOCK_mapped_file,LOCK_user_locks, LOCK_status, LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone, LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock, LOCK_global_system_variables, LOCK_user_conn, - LOCK_prepared_stmt_count, LOCK_error_messages, - LOCK_bytes_sent, LOCK_bytes_received, LOCK_connection_count; + LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count; extern MYSQL_PLUGIN_IMPORT pthread_mutex_t LOCK_thread_count; #ifdef HAVE_OPENSSL extern pthread_mutex_t LOCK_des_key_file; @@ -2024,7 +2023,7 @@ extern uint sql_command_flags[]; extern TYPELIB log_output_typelib; /* optional things, have_* variables */ -extern SHOW_COMP_OPTION have_community_features; +extern SHOW_COMP_OPTION have_profiling; extern handlerton *partition_hton; extern handlerton *myisam_hton; @@ -2175,12 +2174,6 @@ longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, int test_if_number(char *str,int *res,bool allow_wildcards); void change_byte(uchar *,uint,char,char); -void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form, - SQL_SELECT *select, int use_record_cache, - bool print_errors, bool disable_rr_cache); -void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, - bool print_error, uint idx); -void end_read_record(READ_RECORD *info); ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder, uint s_length, SQL_SELECT *select, ha_rows max_rows, bool sort_positions, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 608934d6864..a004f6304f1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -582,6 +582,7 @@ const char *log_output_str= "FILE"; time_t server_start_time, flush_status_time; char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], system_time_zone[30]; +char default_logfile_name[FN_REFLEN]; char *default_tz_name; char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN]; char mysql_real_data_home[FN_REFLEN], @@ -647,17 +648,17 @@ MY_LOCALE *my_default_lc_time_names; SHOW_COMP_OPTION have_ssl, have_symlink, have_dlopen, have_query_cache; SHOW_COMP_OPTION have_geometry, have_rtree_keys; SHOW_COMP_OPTION have_crypt, have_compress; -SHOW_COMP_OPTION have_community_features; +SHOW_COMP_OPTION have_profiling; /* Thread specific variables */ pthread_key(MEM_ROOT**,THR_MALLOC); pthread_key(THD*, THR_THD); -pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, +pthread_mutex_t LOCK_mysql_create_db, LOCK_open, LOCK_thread_count, LOCK_mapped_file, LOCK_status, LOCK_global_read_lock, LOCK_error_log, LOCK_uuid_generator, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, - LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received, + LOCK_crypt, LOCK_global_system_variables, LOCK_user_conn, LOCK_slave_list, LOCK_active_mi, LOCK_connection_count, LOCK_error_messages; @@ -1006,7 +1007,7 @@ static void close_connections(void) if (tmp->vio_ok()) { if (global_system_variables.log_warnings) - sql_print_warning(ER(ER_FORCING_CLOSE),my_progname, + sql_print_warning(ER_DEFAULT(ER_FORCING_CLOSE),my_progname, tmp->thread_id, (tmp->main_security_ctx.user ? tmp->main_security_ctx.user : "")); @@ -1422,7 +1423,6 @@ static void clean_up_mutexes() { (void) pthread_mutex_destroy(&LOCK_mysql_create_db); (void) pthread_mutex_destroy(&LOCK_lock_db); - (void) pthread_mutex_destroy(&LOCK_Acl); (void) rwlock_destroy(&LOCK_grant); (void) pthread_mutex_destroy(&LOCK_open); (void) pthread_mutex_destroy(&LOCK_thread_count); @@ -1434,8 +1434,6 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_delayed_create); (void) pthread_mutex_destroy(&LOCK_manager); (void) pthread_mutex_destroy(&LOCK_crypt); - (void) pthread_mutex_destroy(&LOCK_bytes_sent); - (void) pthread_mutex_destroy(&LOCK_bytes_received); (void) pthread_mutex_destroy(&LOCK_user_conn); (void) pthread_mutex_destroy(&LOCK_connection_count); Events::destroy_mutexes(); @@ -3081,7 +3079,6 @@ SHOW_VAR com_status_vars[]= { {"show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS}, {"show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS}, {"show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS}, - {"show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS}, {"show_contributors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS}, {"show_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS}, {"show_create_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_EVENT]), SHOW_LONG_STATUS}, @@ -3199,10 +3196,13 @@ static int init_common_variables(const char *conf_file_name, int argc, strmake(glob_hostname, STRING_WITH_LEN("localhost")); sql_print_warning("gethostname failed, using '%s' as hostname", glob_hostname); - strmake(pidfile_name, STRING_WITH_LEN("mysql")); + strmake(default_logfile_name, STRING_WITH_LEN("mysql")); } else - strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5); + strmake(default_logfile_name, glob_hostname, + sizeof(default_logfile_name)-5); + + strmake(pidfile_name, default_logfile_name, sizeof(pidfile_name)-5); strmov(fn_ext(pidfile_name),".pid"); // Add proper extension /* @@ -3488,7 +3488,7 @@ static int init_common_variables(const char *conf_file_name, int argc, if (opt_slow_log && opt_slow_logname && !(log_output_options & LOG_FILE) && !(log_output_options & LOG_NONE)) sql_print_warning("Although a path was specified for the " - "--log_slow_queries option, log tables are used. " + "--log-slow-queries option, log tables are used. " "To enable logging to files use the --log-output=file option."); s= opt_logname ? opt_logname : make_default_log_name(buff, ".log"); @@ -3571,7 +3571,6 @@ static int init_thread_environment() { (void) pthread_mutex_init(&LOCK_mysql_create_db,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_lock_db,MY_MUTEX_INIT_SLOW); - (void) pthread_mutex_init(&LOCK_Acl,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_open, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_mapped_file,MY_MUTEX_INIT_SLOW); @@ -3582,8 +3581,6 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_delayed_create,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_manager,MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_crypt,MY_MUTEX_INIT_FAST); - (void) pthread_mutex_init(&LOCK_bytes_sent,MY_MUTEX_INIT_FAST); - (void) pthread_mutex_init(&LOCK_bytes_received,MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_user_conn, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_global_system_variables, MY_MUTEX_INIT_FAST); @@ -4171,8 +4168,7 @@ static void create_shutdown_thread() #ifdef __WIN__ hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name); pthread_t hThread; - if (pthread_create(&hThread,&connection_attrib, - handle_connections_sockets_thread, 0)) + if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0)) sql_print_warning("Can't create thread to handle shutdown requests"); // On "Stop Service" we have to do regular shutdown @@ -4951,9 +4947,6 @@ static void create_new_thread(THD *thd) NET *net=&thd->net; DBUG_ENTER("create_new_thread"); - if (protocol_version > 9) - net->return_errno=1; - /* Don't allow too many connections. We roughly check here that we allow only (max_connections + 1) connections. @@ -5871,10 +5864,6 @@ struct my_option my_long_options[] = "Set the default storage engine (table type) for tables.", (uchar**)&default_storage_engine_str, (uchar**)&default_storage_engine_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"default-table-type", OPT_STORAGE_ENGINE, - "(deprecated) Use --default-storage-engine.", - (uchar**)&default_storage_engine_str, (uchar**)&default_storage_engine_str, - 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"default-time-zone", OPT_DEFAULT_TIME_ZONE, "Set the default time zone.", (uchar**) &default_tz_name, (uchar**) &default_tz_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, @@ -6063,14 +6052,14 @@ Disable with --skip-super-large-pages.", (uchar**) &opt_log_slow_slave_statements, (uchar**) &opt_log_slow_slave_statements, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"log_slow_queries", OPT_SLOW_QUERY_LOG, - "Log slow queries to a table or log file. Defaults logging to table " - "mysql.slow_log or hostname-slow.log if --log-output=file is used. " - "Must be enabled to activate other slow log options. " - "(deprecated option, use --slow_query_log/--slow_query_log_file instead)", + {"log-slow-queries", OPT_SLOW_QUERY_LOG, + "Log slow queries to a table or log file. Defaults logging to table " + "mysql.slow_log or hostname-slow.log if --log-output=file is used. " + "Must be enabled to activate other slow log options. " + "Deprecated option, use --slow-query-log/--slow-query-log-file instead.", (uchar**) &opt_slow_logname, (uchar**) &opt_slow_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"slow_query_log_file", OPT_SLOW_QUERY_LOG_FILE, + {"slow-query-log-file", OPT_SLOW_QUERY_LOG_FILE, "Log slow queries to given log file. Defaults logging to hostname-slow.log. Must be enabled to activate other slow log options.", (uchar**) &opt_slow_logname, (uchar**) &opt_slow_logname, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -6308,7 +6297,7 @@ master-ssl", "Maximum time in seconds to wait for the port to become free. " "(Default: no wait)", (uchar**) &mysqld_port_timeout, (uchar**) &mysqld_port_timeout, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) {"profiling_history_size", OPT_PROFILING, "Limit of query profiling memory", (uchar**) &global_system_variables.profiling_history_size, (uchar**) &max_system_variables.profiling_history_size, @@ -6791,7 +6780,7 @@ The minimum value for this variable is 4096.", (uchar**) &max_write_lock_count, (uchar**) &max_write_lock_count, 0, GET_ULONG, REQUIRED_ARG, ULONG_MAX, 1, ULONG_MAX, 0, 1, 0}, {"min_examined_row_limit", OPT_MIN_EXAMINED_ROW_LIMIT, - "Don't log queries which examine less than min_examined_row_limit rows to file.", + "Don't write queries to slow log that examine fewer than min_examined_row_limit rows.", (uchar**) &global_system_variables.min_examined_row_limit, (uchar**) &max_system_variables.min_examined_row_limit, 0, GET_ULONG, REQUIRED_ARG, 0, 0, ULONG_MAX, 0, 1L, 0}, @@ -7136,7 +7125,7 @@ static int show_starttime(THD *thd, SHOW_VAR *var, char *buff) return 0; } -#ifdef COMMUNITY_SERVER +#ifdef ENABLED_PROFILING static int show_flushstatustime(THD *thd, SHOW_VAR *var, char *buff) { var->type= SHOW_LONG; @@ -7631,7 +7620,7 @@ SHOW_VAR status_vars[]= { {"Threads_created", (char*) &thread_created, SHOW_LONG_NOFLUSH}, {"Threads_running", (char*) &thread_running, SHOW_INT}, {"Uptime", (char*) &show_starttime, SHOW_FUNC}, -#ifdef COMMUNITY_SERVER +#ifdef ENABLED_PROFILING {"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_FUNC}, #endif {NullS, NullS, SHOW_LONG} @@ -7863,10 +7852,10 @@ static int mysql_init_variables(void) "d:t:i:o,/tmp/mysqld.trace"); #endif opt_error_log= IF_WIN(1,0); -#ifdef COMMUNITY_SERVER - have_community_features = SHOW_OPTION_YES; +#ifdef ENABLED_PROFILING + have_profiling = SHOW_OPTION_YES; #else - have_community_features = SHOW_OPTION_NO; + have_profiling = SHOW_OPTION_NO; #endif global_system_variables.ndb_index_stat_enable=FALSE; max_system_variables.ndb_index_stat_enable=TRUE; @@ -7984,7 +7973,7 @@ mysqld_get_one_option(int optid, default_collation_name= 0; break; case 'l': - WARN_DEPRECATED(NULL, "7.0", "--log", "'--general_log'/'--general_log_file'"); + WARN_DEPRECATED(NULL, "7.0", "--log", "'--general-log'/'--general-log-file'"); opt_log=1; break; case 'h': @@ -8158,7 +8147,8 @@ mysqld_get_one_option(int optid, } #endif /* HAVE_REPLICATION */ case (int) OPT_SLOW_QUERY_LOG: - WARN_DEPRECATED(NULL, "7.0", "--log_slow_queries", "'--slow_query_log'/'--slow_query_log_file'"); + WARN_DEPRECATED(NULL, "7.0", "--log-slow-queries", + "'--slow-query-log'/'--slow-query-log-file'"); opt_slow_log= 1; break; #ifdef WITH_CSV_STORAGE_ENGINE @@ -8986,9 +8976,7 @@ void refresh_status(THD *thd) /* Reset the counters of all key caches (default and named). */ process_key_caches(reset_key_cache_counters); -#ifdef COMMUNITY_SERVER flush_status_time= time((time_t*) 0); -#endif pthread_mutex_unlock(&LOCK_status); /* diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 7ff26c50afc..5cf3597c638 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -91,12 +91,11 @@ void sql_print_error(const char *format,...); */ extern uint test_flags; extern ulong bytes_sent, bytes_received, net_big_packet_count; -extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; #ifndef MYSQL_INSTANCE_MANAGER #ifdef HAVE_QUERY_CACHE #define USE_QUERY_CACHE -extern void query_cache_init_query(NET *net); -extern void query_cache_insert(NET *net, const char *packet, ulong length); +extern void query_cache_insert(const char *packet, ulong length, + unsigned pkt_nr); #endif // HAVE_QUERY_CACHE #define update_statistics(A) A #endif /* MYSQL_INSTANCE_MANGER */ @@ -125,18 +124,14 @@ my_bool my_net_init(NET *net, Vio* vio) MYF(MY_WME)))) DBUG_RETURN(1); net->buff_end=net->buff+net->max_packet; - net->error=0; net->return_errno=0; net->return_status=0; + net->error=0; net->return_status=0; net->pkt_nr=net->compress_pkt_nr=0; net->write_pos=net->read_pos = net->buff; net->last_error[0]=0; net->compress=0; net->reading_or_writing=0; net->where_b = net->remain_in_buf=0; net->last_errno=0; -#ifdef USE_QUERY_CACHE - query_cache_init_query(net); -#else - net->query_cache_query= 0; -#endif + net->unused= 0; if (vio != 0) /* If real connection */ { @@ -586,7 +581,7 @@ net_real_write(NET *net,const uchar *packet, size_t len) DBUG_ENTER("net_real_write"); #if defined(MYSQL_SERVER) && defined(USE_QUERY_CACHE) - query_cache_insert(net, (char*) packet, len); + query_cache_insert((char*) packet, len, net->pkt_nr); #endif if (net->error == 2) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e1c7d4b38aa..ce86fa535fe 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -438,35 +438,55 @@ public: return 0; } - /* returns a number of keypart values appended to the key buffer */ - int store_min_key(KEY_PART *key, uchar **range_key, uint *range_key_flag) + /* + Returns a number of keypart values appended to the key buffer + for min key and max key. This function is used by both Range + Analysis and Partition pruning. For partition pruning we have + to ensure that we don't store also subpartition fields. Thus + we have to stop at the last partition part and not step into + the subpartition fields. For Range Analysis we set last_part + to MAX_KEY which we should never reach. + */ + int store_min_key(KEY_PART *key, + uchar **range_key, + uint *range_key_flag, + uint last_part) { SEL_ARG *key_tree= first(); uint res= key_tree->store_min(key[key_tree->part].store_length, range_key, *range_key_flag); *range_key_flag|= key_tree->min_flag; if (key_tree->next_key_part && + key_tree->part != last_part && key_tree->next_key_part->part == key_tree->part+1 && !(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN)) && key_tree->next_key_part->type == SEL_ARG::KEY_RANGE) - res+= key_tree->next_key_part->store_min_key(key, range_key, - range_key_flag); + res+= key_tree->next_key_part->store_min_key(key, + range_key, + range_key_flag, + last_part); return res; } /* returns a number of keypart values appended to the key buffer */ - int store_max_key(KEY_PART *key, uchar **range_key, uint *range_key_flag) + int store_max_key(KEY_PART *key, + uchar **range_key, + uint *range_key_flag, + uint last_part) { SEL_ARG *key_tree= last(); uint res=key_tree->store_max(key[key_tree->part].store_length, range_key, *range_key_flag); (*range_key_flag)|= key_tree->max_flag; if (key_tree->next_key_part && - key_tree->next_key_part->part == key_tree->part+1 && - !(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX)) && - key_tree->next_key_part->type == SEL_ARG::KEY_RANGE) - res+= key_tree->next_key_part->store_max_key(key, range_key, - range_key_flag); + key_tree->part != last_part && + key_tree->next_key_part->part == key_tree->part+1 && + !(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX)) && + key_tree->next_key_part->type == SEL_ARG::KEY_RANGE) + res+= key_tree->next_key_part->store_max_key(key, + range_key, + range_key_flag, + last_part); return res; } @@ -634,6 +654,14 @@ public: using_real_indexes==TRUE */ uint real_keynr[MAX_KEY]; + + /* + Used to store 'current key tuples', in both range analysis and + partitioning (list) analysis + */ + uchar min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH], + max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; + /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */ uint alloced_sel_args; }; @@ -645,8 +673,6 @@ public: longlong baseflag; uint max_key_part, range_count; - uchar min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH], - max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; bool quick; // Don't calulate possible keys uint fields_bitmap_size; @@ -2604,6 +2630,8 @@ typedef struct st_part_prune_param /* Same as above for subpartitioning */ my_bool *is_subpart_keypart; + my_bool ignore_part_fields; /* Ignore rest of partioning fields */ + /*************************************************************** Following fields form find_used_partitions() recursion context: **************************************************************/ @@ -2617,8 +2645,13 @@ typedef struct st_part_prune_param /* Iterator to be used to obtain the "current" set of used partitions */ PARTITION_ITERATOR part_iter; - /* Initialized bitmap of no_subparts size */ + /* Initialized bitmap of num_subparts size */ MY_BITMAP subparts_bitmap; + + uchar *cur_min_key; + uchar *cur_max_key; + + uint cur_min_flag, cur_max_flag; } PART_PRUNE_PARAM; static bool create_partition_index_description(PART_PRUNE_PARAM *prune_par); @@ -2736,6 +2769,11 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) prune_param.arg_stack_end= prune_param.arg_stack; prune_param.cur_part_fields= 0; prune_param.cur_subpart_fields= 0; + + prune_param.cur_min_key= prune_param.range_param.min_key; + prune_param.cur_max_key= prune_param.range_param.max_key; + prune_param.cur_min_flag= prune_param.cur_max_flag= 0; + init_all_partitions_iterator(part_info, &prune_param.part_iter); if (!tree->keys[0] || (-1 == (res= find_used_partitions(&prune_param, tree->keys[0])))) @@ -2873,8 +2911,8 @@ static void mark_full_partition_used_no_parts(partition_info* part_info, static void mark_full_partition_used_with_parts(partition_info *part_info, uint32 part_id) { - uint32 start= part_id * part_info->no_subparts; - uint32 end= start + part_info->no_subparts; + uint32 start= part_id * part_info->num_subparts; + uint32 end= start + part_info->num_subparts; DBUG_ENTER("mark_full_partition_used_with_parts"); for (; start != end; start++) @@ -2972,6 +3010,11 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge) ppar->arg_stack_end= ppar->arg_stack; ppar->cur_part_fields= 0; ppar->cur_subpart_fields= 0; + + ppar->cur_min_key= ppar->range_param.min_key; + ppar->cur_max_key= ppar->range_param.max_key; + ppar->cur_min_flag= ppar->cur_max_flag= 0; + init_all_partitions_iterator(ppar->part_info, &ppar->part_iter); SEL_ARG *key_tree= (*ptree)->keys[0]; if (!key_tree || (-1 == (res |= find_used_partitions(ppar, key_tree)))) @@ -3095,9 +3138,14 @@ static int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) { int res, left_res=0, right_res=0; - int partno= (int)key_tree->part; - bool pushed= FALSE; + int key_tree_part= (int)key_tree->part; bool set_full_part_if_bad_ret= FALSE; + bool ignore_part_fields= ppar->ignore_part_fields; + bool did_set_ignore_part_fields= FALSE; + RANGE_OPT_PARAM *range_par= &(ppar->range_param); + + if (check_stack_overrun(range_par->thd, 3*STACK_MIN_SIZE, NULL)) + return -1; if (key_tree->left != &null_element) { @@ -3105,56 +3153,177 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) return -1; } + /* Push SEL_ARG's to stack to enable looking backwards as well */ + ppar->cur_part_fields+= ppar->is_part_keypart[key_tree_part]; + ppar->cur_subpart_fields+= ppar->is_subpart_keypart[key_tree_part]; + *(ppar->arg_stack_end++)= key_tree; + if (key_tree->type == SEL_ARG::KEY_RANGE) { - if (partno == 0 && (NULL != ppar->part_info->get_part_iter_for_interval)) + if (ppar->part_info->get_part_iter_for_interval && + key_tree->part <= ppar->last_part_partno) { - /* - Partitioning is done by RANGE|INTERVAL(monotonic_expr(fieldX)), and - we got "const1 CMP fieldX CMP const2" interval <-- psergey-todo: change + if (ignore_part_fields) + { + /* + We come here when a condition on the first partitioning + fields led to evaluating the partitioning condition + (due to finding a condition of the type a < const or + b > const). Thus we must ignore the rest of the + partitioning fields but we still want to analyse the + subpartitioning fields. + */ + if (key_tree->next_key_part) + res= find_used_partitions(ppar, key_tree->next_key_part); + else + res= -1; + goto pop_and_go_right; + } + /* Collect left and right bound, their lengths and flags */ + uchar *min_key= ppar->cur_min_key; + uchar *max_key= ppar->cur_max_key; + uchar *tmp_min_key= min_key; + uchar *tmp_max_key= max_key; + key_tree->store_min(ppar->key[key_tree->part].store_length, + &tmp_min_key, ppar->cur_min_flag); + key_tree->store_max(ppar->key[key_tree->part].store_length, + &tmp_max_key, ppar->cur_max_flag); + uint flag; + if (key_tree->next_key_part && + key_tree->next_key_part->part == key_tree->part+1 && + key_tree->next_key_part->part <= ppar->last_part_partno && + key_tree->next_key_part->type == SEL_ARG::KEY_RANGE) + { + /* + There are more key parts for partition pruning to handle + This mainly happens when the condition is an equality + condition. + */ + if ((tmp_min_key - min_key) == (tmp_max_key - max_key) && + (memcmp(min_key, max_key, (uint)(tmp_max_key - max_key)) == 0) && + !key_tree->min_flag && !key_tree->max_flag) + { + /* Set 'parameters' */ + ppar->cur_min_key= tmp_min_key; + ppar->cur_max_key= tmp_max_key; + uint save_min_flag= ppar->cur_min_flag; + uint save_max_flag= ppar->cur_max_flag; + + ppar->cur_min_flag|= key_tree->min_flag; + ppar->cur_max_flag|= key_tree->max_flag; + + res= find_used_partitions(ppar, key_tree->next_key_part); + + /* Restore 'parameters' back */ + ppar->cur_min_key= min_key; + ppar->cur_max_key= max_key; + + ppar->cur_min_flag= save_min_flag; + ppar->cur_max_flag= save_max_flag; + goto pop_and_go_right; + } + /* We have arrived at the last field in the partition pruning */ + uint tmp_min_flag= key_tree->min_flag, + tmp_max_flag= key_tree->max_flag; + if (!tmp_min_flag) + key_tree->next_key_part->store_min_key(ppar->key, + &tmp_min_key, + &tmp_min_flag, + ppar->last_part_partno); + if (!tmp_max_flag) + key_tree->next_key_part->store_max_key(ppar->key, + &tmp_max_key, + &tmp_max_flag, + ppar->last_part_partno); + flag= tmp_min_flag | tmp_max_flag; + } + else + flag= key_tree->min_flag | key_tree->max_flag; + + if (tmp_min_key != range_par->min_key) + flag&= ~NO_MIN_RANGE; + else + flag|= NO_MIN_RANGE; + if (tmp_max_key != range_par->max_key) + flag&= ~NO_MAX_RANGE; + else + flag|= NO_MAX_RANGE; + + /* + We need to call the interval mapper if we have a condition which + makes sense to prune on. In the example of COLUMNS on a and + b it makes sense if we have a condition on a, or conditions on + both a and b. If we only have conditions on b it might make sense + but this is a harder case we will solve later. For the harder case + this clause then turns into use of all partitions and thus we + simply set res= -1 as if the mapper had returned that. + TODO: What to do here is defined in WL#4065. */ - DBUG_EXECUTE("info", dbug_print_segment_range(key_tree, - ppar->range_param. - key_parts);); - res= ppar->part_info-> - get_part_iter_for_interval(ppar->part_info, - FALSE, - key_tree->min_value, - key_tree->max_value, - key_tree->min_flag | key_tree->max_flag, - &ppar->part_iter); - if (!res) - goto go_right; /* res==0 --> no satisfying partitions */ + if (ppar->arg_stack[0]->part == 0) + { + uint32 i; + uint32 store_length_array[MAX_KEY]; + uint32 num_keys= ppar->part_fields; + + for (i= 0; i < num_keys; i++) + store_length_array[i]= ppar->key[i].store_length; + res= ppar->part_info-> + get_part_iter_for_interval(ppar->part_info, + FALSE, + store_length_array, + range_par->min_key, + range_par->max_key, + tmp_min_key - range_par->min_key, + tmp_max_key - range_par->max_key, + flag, + &ppar->part_iter); + if (!res) + goto pop_and_go_right; /* res==0 --> no satisfying partitions */ + } + else + res= -1; + if (res == -1) { - //get a full range iterator + /* get a full range iterator */ init_all_partitions_iterator(ppar->part_info, &ppar->part_iter); } /* Save our intent to mark full partition as used if we will not be able to obtain further limits on subpartitions */ + if (key_tree_part < ppar->last_part_partno) + { + /* + We need to ignore the rest of the partitioning fields in all + evaluations after this + */ + did_set_ignore_part_fields= TRUE; + ppar->ignore_part_fields= TRUE; + } set_full_part_if_bad_ret= TRUE; goto process_next_key_part; } - if (partno == ppar->last_subpart_partno && + if (key_tree_part == ppar->last_subpart_partno && (NULL != ppar->part_info->get_subpart_iter_for_interval)) { PARTITION_ITERATOR subpart_iter; DBUG_EXECUTE("info", dbug_print_segment_range(key_tree, - ppar->range_param. - key_parts);); + range_par->key_parts);); res= ppar->part_info-> get_subpart_iter_for_interval(ppar->part_info, TRUE, + NULL, /* Currently not used here */ key_tree->min_value, key_tree->max_value, - key_tree->min_flag | key_tree->max_flag, + 0, 0, /* Those are ignored here */ + key_tree->min_flag | + key_tree->max_flag, &subpart_iter); DBUG_ASSERT(res); /* We can't get "no satisfying subpartitions" */ if (res == -1) - return -1; /* all subpartitions satisfy */ + goto pop_and_go_right; /* all subpartitions satisfy */ uint32 subpart_id; bitmap_clear_all(&ppar->subparts_bitmap); @@ -3167,23 +3336,19 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) != NOT_A_PARTITION_ID) { - for (uint i= 0; i < ppar->part_info->no_subparts; i++) + for (uint i= 0; i < ppar->part_info->num_subparts; i++) if (bitmap_is_set(&ppar->subparts_bitmap, i)) bitmap_set_bit(&ppar->part_info->used_partitions, - part_id * ppar->part_info->no_subparts + i); + part_id * ppar->part_info->num_subparts + i); } - goto go_right; + goto pop_and_go_right; } if (key_tree->is_singlepoint()) { - pushed= TRUE; - ppar->cur_part_fields+= ppar->is_part_keypart[partno]; - ppar->cur_subpart_fields+= ppar->is_subpart_keypart[partno]; - *(ppar->arg_stack_end++) = key_tree; - - if (partno == ppar->last_part_partno && - ppar->cur_part_fields == ppar->part_fields) + if (key_tree_part == ppar->last_part_partno && + ppar->cur_part_fields == ppar->part_fields && + ppar->part_info->get_part_iter_for_interval == NULL) { /* Ok, we've got "fieldN<=>constN"-type SEL_ARGs for all partitioning @@ -3212,7 +3377,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) goto process_next_key_part; } - if (partno == ppar->last_subpart_partno && + if (key_tree_part == ppar->last_subpart_partno && ppar->cur_subpart_fields == ppar->subpart_fields) { /* @@ -3236,7 +3401,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) NOT_A_PARTITION_ID) { bitmap_set_bit(&part_info->used_partitions, - part_id * part_info->no_subparts + subpart_id); + part_id * part_info->num_subparts + subpart_id); } res= 1; /* Some partitions were marked as used */ goto pop_and_go_right; @@ -3249,8 +3414,11 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) we're processing subpartititoning's key parts, this means we'll not be able to infer any suitable condition, so bail out. */ - if (partno >= ppar->last_part_partno) - return -1; + if (key_tree_part >= ppar->last_part_partno) + { + res= -1; + goto pop_and_go_right; + } } } @@ -3259,7 +3427,17 @@ process_next_key_part: res= find_used_partitions(ppar, key_tree->next_key_part); else res= -1; - + + if (did_set_ignore_part_fields) + { + /* + We have returned from processing all key trees linked to our next + key part. We are ready to be moving down (using right pointers) and + this tree is a new evaluation requiring its own decision on whether + to ignore partitioning fields. + */ + ppar->ignore_part_fields= FALSE; + } if (set_full_part_if_bad_ret) { if (res == -1) @@ -3282,18 +3460,14 @@ process_next_key_part: init_all_partitions_iterator(ppar->part_info, &ppar->part_iter); } - if (pushed) - { pop_and_go_right: - /* Pop this key part info off the "stack" */ - ppar->arg_stack_end--; - ppar->cur_part_fields-= ppar->is_part_keypart[partno]; - ppar->cur_subpart_fields-= ppar->is_subpart_keypart[partno]; - } + /* Pop this key part info off the "stack" */ + ppar->arg_stack_end--; + ppar->cur_part_fields-= ppar->is_part_keypart[key_tree_part]; + ppar->cur_subpart_fields-= ppar->is_subpart_keypart[key_tree_part]; if (res == -1) return -1; -go_right: if (key_tree->right != &null_element) { if (-1 == (right_res= find_used_partitions(ppar,key_tree->right))) @@ -3375,13 +3549,14 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) uint used_part_fields, used_subpart_fields; used_part_fields= fields_ok_for_partition_index(part_info->part_field_array) ? - part_info->no_part_fields : 0; + part_info->num_part_fields : 0; used_subpart_fields= fields_ok_for_partition_index(part_info->subpart_field_array)? - part_info->no_subpart_fields : 0; + part_info->num_subpart_fields : 0; uint total_parts= used_part_fields + used_subpart_fields; + ppar->ignore_part_fields= FALSE; ppar->part_fields= used_part_fields; ppar->last_part_partno= (int)used_part_fields - 1; @@ -3416,10 +3591,10 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) if (ppar->subpart_fields) { my_bitmap_map *buf; - uint32 bufsize= bitmap_buffer_size(ppar->part_info->no_subparts); + uint32 bufsize= bitmap_buffer_size(ppar->part_info->num_subparts); if (!(buf= (my_bitmap_map*) alloc_root(alloc, bufsize))) return TRUE; - bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->no_subparts, + bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->num_subparts, FALSE); } range_par->key_parts= key_part; @@ -3430,12 +3605,8 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) { key_part->key= 0; key_part->part= part; - key_part->store_length= key_part->length= (uint16) (*field)->key_length(); - if ((*field)->real_maybe_null()) - key_part->store_length+= HA_KEY_NULL_LENGTH; - if ((*field)->type() == MYSQL_TYPE_BLOB || - (*field)->real_type() == MYSQL_TYPE_VARCHAR) - key_part->store_length+= HA_KEY_BLOB_LENGTH; + key_part->length= (uint16)(*field)->key_length(); + key_part->store_length= (uint16)get_partition_field_store_length(*field); DBUG_PRINT("info", ("part %u length %u store_length %u", part, key_part->length, key_part->store_length)); @@ -7553,12 +7724,16 @@ check_quick_keys(PARAM *param, uint idx, SEL_ARG *key_tree, tmp_max_flag=key_tree->max_flag; if (!tmp_min_flag) tmp_min_keypart+= - key_tree->next_key_part->store_min_key(param->key[idx], &tmp_min_key, - &tmp_min_flag); + key_tree->next_key_part->store_min_key(param->key[idx], + &tmp_min_key, + &tmp_min_flag, + MAX_KEY); if (!tmp_max_flag) tmp_max_keypart+= - key_tree->next_key_part->store_max_key(param->key[idx], &tmp_max_key, - &tmp_max_flag); + key_tree->next_key_part->store_max_key(param->key[idx], + &tmp_max_key, + &tmp_max_flag, + MAX_KEY); min_key_length= (uint) (tmp_min_key - param->min_key); max_key_length= (uint) (tmp_max_key - param->max_key); } @@ -7828,11 +8003,15 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, { uint tmp_min_flag=key_tree->min_flag,tmp_max_flag=key_tree->max_flag; if (!tmp_min_flag) - min_part+= key_tree->next_key_part->store_min_key(key, &tmp_min_key, - &tmp_min_flag); + min_part+= key_tree->next_key_part->store_min_key(key, + &tmp_min_key, + &tmp_min_flag, + MAX_KEY); if (!tmp_max_flag) - max_part+= key_tree->next_key_part->store_max_key(key, &tmp_max_key, - &tmp_max_flag); + max_part+= key_tree->next_key_part->store_max_key(key, + &tmp_max_key, + &tmp_max_flag, + MAX_KEY); flag=tmp_min_flag | tmp_max_flag; } } diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 9d546b24c7c..13b10ac2e8f 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -356,9 +356,9 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) const_result= 0; break; } - item_sum->set_aggregator (item_sum->with_distinct ? - Aggregator::DISTINCT_AGGREGATOR : - Aggregator::SIMPLE_AGGREGATOR); + item_sum->set_aggregator(item_sum->has_with_distinct() ? + Aggregator::DISTINCT_AGGREGATOR : + Aggregator::SIMPLE_AGGREGATOR); if (!count) { /* If count == 0, then we know that is_exact_count == TRUE. */ @@ -447,9 +447,9 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) const_result= 0; break; } - item_sum->set_aggregator (item_sum->with_distinct ? - Aggregator::DISTINCT_AGGREGATOR : - Aggregator::SIMPLE_AGGREGATOR); + item_sum->set_aggregator(item_sum->has_with_distinct() ? + Aggregator::DISTINCT_AGGREGATOR : + Aggregator::SIMPLE_AGGREGATOR); if (!count) { /* If count != 1, then we know that is_exact_count == TRUE. */ diff --git a/sql/partition_element.h b/sql/partition_element.h index 5575eb93bfb..73f85ac275f 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -1,7 +1,7 @@ #ifndef PARTITION_ELEMENT_INCLUDED #define PARTITION_ELEMENT_INCLUDED -/* Copyright (C) 2006 MySQL AB +/* Copyright 2005-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,6 +40,35 @@ enum partition_state { }; /* + This struct is used to keep track of column expressions as part + of the COLUMNS concept in conjunction with RANGE and LIST partitioning. + The value can be either of MINVALUE, MAXVALUE and an expression that + must be constant and evaluate to the same type as the column it + represents. + + The data in this fixed in two steps. The parser will only fill in whether + it is a max_value or provide an expression. Filling in + column_value, part_info, partition_id, null_value is done by the + function fix_column_value_function. However the item tree needs + fixed also before writing it into the frm file (in add_column_list_values). + To distinguish between those two variants, fixed= 1 after the + fixing in add_column_list_values and fixed= 2 otherwise. This is + since the fixing in add_column_list_values isn't a complete fixing. +*/ + +typedef struct p_column_list_val +{ + void* column_value; + Item* item_expression; + partition_info *part_info; + uint partition_id; + bool max_value; + bool null_value; + char fixed; +} part_column_list_val; + + +/* This struct is used to contain the value of an element in the VALUES IN struct. It needs to keep knowledge of whether it is a signed/unsigned value and whether it is @@ -49,8 +78,10 @@ enum partition_state { typedef struct p_elem_val { longlong value; + uint added_items; bool null_value; bool unsigned_flag; + part_column_list_val *col_val_array; } part_elem_value; struct st_ddl_log_memory_entry; @@ -72,8 +103,9 @@ public: enum partition_state part_state; uint16 nodegroup_id; bool has_null_value; - bool signed_flag;/* Indicate whether this partition uses signed constants */ - bool max_value; /* Indicate whether this partition uses MAXVALUE */ + /* signed_flag and max_value only relevant for subpartitions */ + bool signed_flag; + bool max_value; partition_element() : part_max_rows(0), part_min_rows(0), range_value(0), diff --git a/sql/partition_info.cc b/sql/partition_info.cc index ba9ea0e876e..cbb4d2331c9 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2006 MySQL AB +/* Copyright (C) 2006-2008 MySQL AB, Sun Microsystems Inc. 2008-2009 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -75,7 +75,7 @@ partition_info *partition_info::get_clone() SYNOPSIS create_default_partition_names() part_no Partition number for subparts - no_parts Number of partitions + num_parts Number of partitions start_no Starting partition number subpart Is it subpartitions @@ -91,10 +91,10 @@ partition_info *partition_info::get_clone() #define MAX_PART_NAME_SIZE 8 char *partition_info::create_default_partition_names(uint part_no, - uint no_parts_arg, + uint num_parts_arg, uint start_no) { - char *ptr= (char*) sql_calloc(no_parts_arg*MAX_PART_NAME_SIZE); + char *ptr= (char*) sql_calloc(num_parts_arg*MAX_PART_NAME_SIZE); char *move_ptr= ptr; uint i= 0; DBUG_ENTER("create_default_partition_names"); @@ -105,11 +105,11 @@ char *partition_info::create_default_partition_names(uint part_no, { my_sprintf(move_ptr, (move_ptr,"p%u", (start_no + i))); move_ptr+=MAX_PART_NAME_SIZE; - } while (++i < no_parts_arg); + } while (++i < num_parts_arg); } else { - mem_alloc_error(no_parts_arg*MAX_PART_NAME_SIZE); + mem_alloc_error(num_parts_arg*MAX_PART_NAME_SIZE); } DBUG_RETURN(ptr); } @@ -189,19 +189,19 @@ bool partition_info::set_up_default_partitions(handler *file, goto end; } - if ((no_parts == 0) && - ((no_parts= file->get_default_no_partitions(info)) == 0)) + if ((num_parts == 0) && + ((num_parts= file->get_default_no_partitions(info)) == 0)) { my_error(ER_PARTITION_NOT_DEFINED_ERROR, MYF(0), "partitions"); goto end; } - if (unlikely(no_parts > MAX_PARTITIONS)) + if (unlikely(num_parts > MAX_PARTITIONS)) { my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); goto end; } - if (unlikely((!(default_name= create_default_partition_names(0, no_parts, + if (unlikely((!(default_name= create_default_partition_names(0, num_parts, start_no))))) goto end; i= 0; @@ -220,7 +220,7 @@ bool partition_info::set_up_default_partitions(handler *file, mem_alloc_error(sizeof(partition_element)); goto end; } - } while (++i < no_parts); + } while (++i < num_parts); result= FALSE; end: DBUG_RETURN(result); @@ -259,9 +259,9 @@ bool partition_info::set_up_default_subpartitions(handler *file, List_iterator<partition_element> part_it(partitions); DBUG_ENTER("partition_info::set_up_default_subpartitions"); - if (no_subparts == 0) - no_subparts= file->get_default_no_partitions(info); - if (unlikely((no_parts * no_subparts) > MAX_PARTITIONS)) + if (num_subparts == 0) + num_subparts= file->get_default_no_partitions(info); + if (unlikely((num_parts * num_subparts) > MAX_PARTITIONS)) { my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); goto end; @@ -288,8 +288,8 @@ bool partition_info::set_up_default_subpartitions(handler *file, mem_alloc_error(sizeof(partition_element)); goto end; } - } while (++j < no_subparts); - } while (++i < no_parts); + } while (++j < num_subparts); + } while (++i < num_parts); result= FALSE; end: DBUG_RETURN(result); @@ -334,6 +334,49 @@ bool partition_info::set_up_defaults_for_partitioning(handler *file, /* + Support routine for check_partition_info + + SYNOPSIS + has_unique_fields + no parameters + + RETURN VALUE + Erroneus field name Error, there are two fields with same name + NULL Ok, no field defined twice + + DESCRIPTION + Check that the user haven't defined the same field twice in + key or column list partitioning. +*/ +char* partition_info::has_unique_fields() +{ + char *field_name_outer, *field_name_inner; + List_iterator<char> it_outer(part_field_list); + uint num_fields= part_field_list.elements; + uint i,j; + DBUG_ENTER("partition_info::has_unique_fields"); + + for (i= 0; i < num_fields; i++) + { + field_name_outer= it_outer++; + List_iterator<char> it_inner(part_field_list); + for (j= 0; j < num_fields; j++) + { + field_name_inner= it_inner++; + if (i >= j) + continue; + if (!(my_strcasecmp(system_charset_info, + field_name_outer, + field_name_inner))) + { + DBUG_RETURN(field_name_outer); + } + } + } + DBUG_RETURN(NULL); +} + +/* A support function to check if a partition element's name is unique SYNOPSIS @@ -518,12 +561,12 @@ bool partition_info::check_engine_mix(handlerton *engine_type, { handlerton *old_engine_type= engine_type; bool first= TRUE; - uint no_parts= partitions.elements; + uint num_parts= partitions.elements; DBUG_ENTER("partition_info::check_engine_mix"); DBUG_PRINT("info", ("in: engine_type = %s, table_engine_set = %u", ha_resolve_storage_engine_name(engine_type), table_engine_set)); - if (no_parts) + if (num_parts) { List_iterator<partition_element> part_it(partitions); uint i= 0; @@ -536,7 +579,7 @@ bool partition_info::check_engine_mix(handlerton *engine_type, if (is_sub_partitioned() && part_elem->subpartitions.elements) { - uint no_subparts= part_elem->subpartitions.elements; + uint num_subparts= part_elem->subpartitions.elements; uint j= 0; List_iterator<partition_element> sub_it(part_elem->subpartitions); do @@ -548,7 +591,7 @@ bool partition_info::check_engine_mix(handlerton *engine_type, if (check_engine_condition(sub_elem, table_engine_set, &engine_type, &first)) goto error; - } while (++j < no_subparts); + } while (++j < num_subparts); /* ensure that the partition also has correct engine */ if (check_engine_condition(part_elem, table_engine_set, &engine_type, &first)) @@ -557,7 +600,7 @@ bool partition_info::check_engine_mix(handlerton *engine_type, else if (check_engine_condition(part_elem, table_engine_set, &engine_type, &first)) goto error; - } while (++i < no_parts); + } while (++i < num_parts); } DBUG_PRINT("info", ("engine_type = %s", ha_resolve_storage_engine_name(engine_type))); @@ -589,6 +632,7 @@ error: SYNOPSIS check_range_constants() + thd Thread object RETURN VALUE TRUE An error occurred during creation of range constants @@ -601,76 +645,112 @@ error: called for RANGE PARTITIONed tables. */ -bool partition_info::check_range_constants() +bool partition_info::check_range_constants(THD *thd) { partition_element* part_def; - longlong current_largest; - longlong part_range_value; bool first= TRUE; uint i; List_iterator<partition_element> it(partitions); - bool result= TRUE; - bool signed_flag= !part_expr->unsigned_flag; + int result= TRUE; DBUG_ENTER("partition_info::check_range_constants"); - DBUG_PRINT("enter", ("INT_RESULT with %d parts", no_parts)); - - LINT_INIT(current_largest); + DBUG_PRINT("enter", ("RANGE with %d parts, column_list = %u", num_parts, + column_list)); - part_result_type= INT_RESULT; - range_int_array= (longlong*)sql_alloc(no_parts * sizeof(longlong)); - if (unlikely(range_int_array == NULL)) + if (column_list) { - mem_alloc_error(no_parts * sizeof(longlong)); - goto end; - } - i= 0; - do - { - part_def= it++; - if ((i != (no_parts - 1)) || !defined_max_value) + part_column_list_val *loc_range_col_array; + part_column_list_val *current_largest_col_val; + uint num_column_values= part_field_list.elements; + uint size_entries= sizeof(part_column_list_val) * num_column_values; + range_col_array= (part_column_list_val*)sql_calloc(num_parts * + size_entries); + LINT_INIT(current_largest_col_val); + if (unlikely(range_col_array == NULL)) { - part_range_value= part_def->range_value; - if (!signed_flag) - part_range_value-= 0x8000000000000000ULL; + mem_alloc_error(num_parts * size_entries); + goto end; } - else - part_range_value= LONGLONG_MAX; - if (first) + loc_range_col_array= range_col_array; + i= 0; + do { - current_largest= part_range_value; - range_int_array[0]= part_range_value; + part_def= it++; + { + List_iterator<part_elem_value> list_val_it(part_def->list_val_list); + part_elem_value *range_val= list_val_it++; + part_column_list_val *col_val= range_val->col_val_array; + + if (fix_column_value_functions(thd, range_val, i)) + goto end; + memcpy(loc_range_col_array, (const void*)col_val, size_entries); + loc_range_col_array+= num_column_values; + if (!first) + { + if (compare_column_values((const void*)current_largest_col_val, + (const void*)col_val) >= 0) + goto range_not_increasing_error; + } + current_largest_col_val= col_val; + } first= FALSE; + } while (++i < num_parts); + } + else + { + longlong current_largest; + longlong part_range_value; + bool signed_flag= !part_expr->unsigned_flag; + + LINT_INIT(current_largest); + + part_result_type= INT_RESULT; + range_int_array= (longlong*)sql_alloc(num_parts * sizeof(longlong)); + if (unlikely(range_int_array == NULL)) + { + mem_alloc_error(num_parts * sizeof(longlong)); + goto end; } - else + i= 0; + do { - if (likely(current_largest < part_range_value)) + part_def= it++; + if ((i != (num_parts - 1)) || !defined_max_value) { - current_largest= part_range_value; - range_int_array[i]= part_range_value; - } - else if (defined_max_value && - current_largest == part_range_value && - part_range_value == LONGLONG_MAX && - i == (no_parts - 1)) - { - range_int_array[i]= part_range_value; + part_range_value= part_def->range_value; + if (!signed_flag) + part_range_value-= 0x8000000000000000ULL; } else + part_range_value= LONGLONG_MAX; + + if (!first) { - my_error(ER_RANGE_NOT_INCREASING_ERROR, MYF(0)); - goto end; + if (unlikely(current_largest > part_range_value) || + (unlikely(current_largest == part_range_value) && + (part_range_value < LONGLONG_MAX || + i != (num_parts - 1) || + !defined_max_value))) + goto range_not_increasing_error; } - } - } while (++i < no_parts); + range_int_array[i]= part_range_value; + current_largest= part_range_value; + first= FALSE; + } while (++i < num_parts); + } result= FALSE; end: DBUG_RETURN(result); + +range_not_increasing_error: + my_error(ER_RANGE_NOT_INCREASING_ERROR, MYF(0)); + goto end; } /* Support routines for check_list_constants used by qsort to sort the - constant list expressions. One routine for unsigned and one for signed. + constant list expressions. One routine for integers and one for + column lists. SYNOPSIS list_part_cmp() @@ -695,6 +775,54 @@ int partition_info::list_part_cmp(const void* a, const void* b) return 0; } + /* + Compare two lists of column values in RANGE/LIST partitioning + SYNOPSIS + compare_column_values() + first First column list argument + second Second column list argument + RETURN VALUES + 0 Equal + -1 First argument is smaller + +1 First argument is larger +*/ + +int partition_info::compare_column_values(const void *first_arg, + const void *second_arg) +{ + const part_column_list_val *first= (part_column_list_val*)first_arg; + const part_column_list_val *second= (part_column_list_val*)second_arg; + partition_info *part_info= first->part_info; + Field **field; + + for (field= part_info->part_field_array; *field; + field++, first++, second++) + { + if (first->max_value || second->max_value) + { + if (first->max_value && second->max_value) + continue; + if (second->max_value) + return -1; + else + return +1; + } + if (first->null_value || second->null_value) + { + if (first->null_value && second->null_value) + continue; + if (second->null_value) + return +1; + else + return -1; + } + int res= (*field)->cmp((const uchar*)first->column_value, + (const uchar*)second->column_value); + if (res) + return res; + } + return 0; +} /* This routine allocates an array for all list constants to achieve a fast @@ -704,6 +832,7 @@ int partition_info::list_part_cmp(const void* a, const void* b) SYNOPSIS check_list_constants() + thd Thread object RETURN VALUE TRUE An error occurred during creation of list constants @@ -716,20 +845,23 @@ int partition_info::list_part_cmp(const void* a, const void* b) called for LIST PARTITIONed tables. */ -bool partition_info::check_list_constants() +bool partition_info::check_list_constants(THD *thd) { - uint i; + uint i, size_entries, num_column_values; uint list_index= 0; part_elem_value *list_value; bool result= TRUE; - longlong curr_value, prev_value, type_add, calc_value; + longlong type_add, calc_value; + void *curr_value, *prev_value; partition_element* part_def; bool found_null= FALSE; + int (*compare_func)(const void *, const void*); + void *ptr; List_iterator<partition_element> list_func_it(partitions); DBUG_ENTER("partition_info::check_list_constants"); part_result_type= INT_RESULT; - no_list_values= 0; + num_list_values= 0; /* We begin by calculating the number of list values that have been defined in the first step. @@ -762,51 +894,86 @@ bool partition_info::check_list_constants() } List_iterator<part_elem_value> list_val_it1(part_def->list_val_list); while (list_val_it1++) - no_list_values++; - } while (++i < no_parts); + num_list_values++; + } while (++i < num_parts); list_func_it.rewind(); - list_array= (LIST_PART_ENTRY*)sql_alloc((no_list_values+1) * - sizeof(LIST_PART_ENTRY)); - if (unlikely(list_array == NULL)) + num_column_values= part_field_list.elements; + size_entries= column_list ? + (num_column_values * sizeof(part_column_list_val)) : + sizeof(LIST_PART_ENTRY); + ptr= sql_calloc((num_list_values+1) * size_entries); + if (unlikely(ptr == NULL)) { - mem_alloc_error(no_list_values * sizeof(LIST_PART_ENTRY)); + mem_alloc_error(num_list_values * size_entries); goto end; } - - i= 0; - /* - Fix to be able to reuse signed sort functions also for unsigned - partition functions. - */ - type_add= (longlong)(part_expr->unsigned_flag ? + if (column_list) + { + part_column_list_val *loc_list_col_array; + loc_list_col_array= (part_column_list_val*)ptr; + list_col_array= (part_column_list_val*)ptr; + compare_func= compare_column_values; + i= 0; + do + { + part_def= list_func_it++; + List_iterator<part_elem_value> list_val_it2(part_def->list_val_list); + while ((list_value= list_val_it2++)) + { + part_column_list_val *col_val= list_value->col_val_array; + if (unlikely(fix_column_value_functions(thd, list_value, i))) + { + DBUG_RETURN(TRUE); + } + memcpy(loc_list_col_array, (const void*)col_val, size_entries); + loc_list_col_array+= num_column_values; + } + } while (++i < num_parts); + } + else + { + compare_func= list_part_cmp; + list_array= (LIST_PART_ENTRY*)ptr; + i= 0; + /* + Fix to be able to reuse signed sort functions also for unsigned + partition functions. + */ + type_add= (longlong)(part_expr->unsigned_flag ? 0x8000000000000000ULL : 0ULL); - do - { - part_def= list_func_it++; - List_iterator<part_elem_value> list_val_it2(part_def->list_val_list); - while ((list_value= list_val_it2++)) + do { - calc_value= list_value->value - type_add; - list_array[list_index].list_value= calc_value; - list_array[list_index++].partition_id= i; - } - } while (++i < no_parts); - - if (fixed && no_list_values) + part_def= list_func_it++; + List_iterator<part_elem_value> list_val_it2(part_def->list_val_list); + while ((list_value= list_val_it2++)) + { + calc_value= list_value->value - type_add; + list_array[list_index].list_value= calc_value; + list_array[list_index++].partition_id= i; + } + } while (++i < num_parts); + } + DBUG_ASSERT(fixed); + if (num_list_values) { bool first= TRUE; - my_qsort((void*)list_array, no_list_values, sizeof(LIST_PART_ENTRY), - &list_part_cmp); - + /* + list_array and list_col_array are unions, so this works for both + variants of LIST partitioning. + */ + my_qsort((void*)list_array, num_list_values, size_entries, + compare_func); + i= 0; LINT_INIT(prev_value); do { - DBUG_ASSERT(i < no_list_values); - curr_value= list_array[i].list_value; - if (likely(first || prev_value != curr_value)) + DBUG_ASSERT(i < num_list_values); + curr_value= column_list ? (void*)&list_col_array[num_column_values * i] : + (void*)&list_array[i]; + if (likely(first || compare_func(curr_value, prev_value))) { prev_value= curr_value; first= FALSE; @@ -816,7 +983,7 @@ bool partition_info::check_list_constants() my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0)); goto end; } - } while (++i < no_list_values); + } while (++i < num_list_values); } result= FALSE; end: @@ -829,10 +996,11 @@ end: SYNOPSIS check_partition_info() + thd Thread object + eng_type Return value for used engine in partitions file A reference to a handler of the table info Create info - engine_type Return value for used engine in partitions - check_partition_function Should we check the partition function + add_or_reorg_part Is it ALTER TABLE ADD/REORGANIZE command RETURN VALUE TRUE Error, something went wrong @@ -848,7 +1016,7 @@ end: bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, handler *file, HA_CREATE_INFO *info, - bool check_partition_function) + bool add_or_reorg_part) { handlerton *table_engine= default_engine_type; uint i, tot_partitions; @@ -859,11 +1027,11 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, DBUG_PRINT("info", ("default table_engine = %s", ha_resolve_storage_engine_name(table_engine))); - if (check_partition_function) + if (!add_or_reorg_part) { int err= 0; - if (part_type != HASH_PARTITION || !list_of_part_fields) + if (!list_of_part_fields) { DBUG_ASSERT(part_expr); err= part_expr->walk(&Item::check_partition_func_processor, 0, @@ -877,9 +1045,12 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); goto end; } + if (thd->lex->sql_command == SQLCOM_CREATE_TABLE && + fix_parser_data(thd)) + goto end; } if (unlikely(!is_sub_partitioned() && - !(use_default_subpartitions && use_default_no_subpartitions))) + !(use_default_subpartitions && use_default_num_subpartitions))) { my_error(ER_SUBPARTITION_ERROR, MYF(0)); goto end; @@ -937,6 +1108,12 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } } + if (part_field_list.elements > 0 && + (same_name= has_unique_fields())) + { + my_error(ER_SAME_NAME_PARTITION_FIELD, MYF(0), same_name); + goto end; + } if ((same_name= has_unique_names())) { my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name); @@ -945,8 +1122,8 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, i= 0; { List_iterator<partition_element> part_it(partitions); - uint no_parts_not_set= 0; - uint prev_no_subparts_not_set= no_subparts + 1; + uint num_parts_not_set= 0; + uint prev_num_subparts_not_set= num_subparts + 1; do { partition_element *part_elem= part_it++; @@ -968,7 +1145,7 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, { if (part_elem->engine_type == NULL) { - no_parts_not_set++; + num_parts_not_set++; part_elem->engine_type= default_engine_type; } if (check_table_name(part_elem->partition_name, @@ -983,7 +1160,7 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, else { uint j= 0; - uint no_subparts_not_set= 0; + uint num_subparts_not_set= 0; List_iterator<partition_element> sub_it(part_elem->subpartitions); partition_element *sub_elem; do @@ -1002,44 +1179,45 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, else { sub_elem->engine_type= default_engine_type; - no_subparts_not_set++; + num_subparts_not_set++; } } DBUG_PRINT("info", ("part = %d sub = %d engine = %s", i, j, ha_resolve_storage_engine_name(sub_elem->engine_type))); - } while (++j < no_subparts); + } while (++j < num_subparts); - if (prev_no_subparts_not_set == (no_subparts + 1) && - (no_subparts_not_set == 0 || no_subparts_not_set == no_subparts)) - prev_no_subparts_not_set= no_subparts_not_set; + if (prev_num_subparts_not_set == (num_subparts + 1) && + (num_subparts_not_set == 0 || + num_subparts_not_set == num_subparts)) + prev_num_subparts_not_set= num_subparts_not_set; if (!table_engine_set && - prev_no_subparts_not_set != no_subparts_not_set) + prev_num_subparts_not_set != num_subparts_not_set) { - DBUG_PRINT("info", ("no_subparts_not_set = %u no_subparts = %u", - no_subparts_not_set, no_subparts)); + DBUG_PRINT("info", ("num_subparts_not_set = %u num_subparts = %u", + num_subparts_not_set, num_subparts)); my_error(ER_MIX_HANDLER_ERROR, MYF(0)); goto end; } if (part_elem->engine_type == NULL) { - if (no_subparts_not_set == 0) + if (num_subparts_not_set == 0) part_elem->engine_type= sub_elem->engine_type; else { - no_parts_not_set++; + num_parts_not_set++; part_elem->engine_type= default_engine_type; } } } - } while (++i < no_parts); + } while (++i < num_parts); if (!table_engine_set && - no_parts_not_set != 0 && - no_parts_not_set != no_parts) + num_parts_not_set != 0 && + num_parts_not_set != num_parts) { - DBUG_PRINT("info", ("no_parts_not_set = %u no_parts = %u", - no_parts_not_set, no_subparts)); + DBUG_PRINT("info", ("num_parts_not_set = %u num_parts = %u", + num_parts_not_set, num_subparts)); my_error(ER_MIX_HANDLER_ERROR, MYF(0)); goto end; } @@ -1062,10 +1240,12 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, list constants. */ - if (fixed) + if (add_or_reorg_part) { - if (unlikely((part_type == RANGE_PARTITION && check_range_constants()) || - (part_type == LIST_PARTITION && check_list_constants()))) + if (unlikely((part_type == RANGE_PARTITION && + check_range_constants(thd)) || + (part_type == LIST_PARTITION && + check_list_constants(thd)))) goto end; } result= FALSE; @@ -1096,20 +1276,101 @@ void partition_info::print_no_partition_found(TABLE *table) if (check_single_table_access(current_thd, SELECT_ACL, &table_list, TRUE)) + { my_message(ER_NO_PARTITION_FOR_GIVEN_VALUE, ER(ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT), MYF(0)); + } else { - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); - if (part_expr->null_value) - buf_ptr= (char*)"NULL"; + if (column_list) + buf_ptr= (char*)"from column_list"; else - longlong2str(err_value, buf, - part_expr->unsigned_flag ? 10 : -10); + { + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + if (part_expr->null_value) + buf_ptr= (char*)"NULL"; + else + longlong2str(err_value, buf, + part_expr->unsigned_flag ? 10 : -10); + dbug_tmp_restore_column_map(table->read_set, old_map); + } my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, MYF(0), buf_ptr); - dbug_tmp_restore_column_map(table->read_set, old_map); } } + + +/* + Set fields related to partition expression + SYNOPSIS + set_part_expr() + start_token Start of partition function string + item_ptr Pointer to item tree + end_token End of partition function string + is_subpart Subpartition indicator + RETURN VALUES + TRUE Memory allocation error + FALSE Success +*/ + +bool partition_info::set_part_expr(char *start_token, Item *item_ptr, + char *end_token, bool is_subpart) +{ + uint expr_len= end_token - start_token; + char *func_string= (char*) sql_memdup(start_token, expr_len); + + if (!func_string) + { + mem_alloc_error(expr_len); + return TRUE; + } + if (is_subpart) + { + list_of_subpart_fields= FALSE; + subpart_expr= item_ptr; + subpart_func_string= func_string; + subpart_func_len= expr_len; + } + else + { + list_of_part_fields= FALSE; + part_expr= item_ptr; + part_func_string= func_string; + part_func_len= expr_len; + } + return FALSE; +} + + +/* + Check that partition fields and subpartition fields are not too long + + SYNOPSIS + check_partition_field_length() + + RETURN VALUES + TRUE Total length was too big + FALSE Length is ok +*/ + +bool partition_info::check_partition_field_length() +{ + uint store_length= 0; + uint i; + DBUG_ENTER("partition_info::check_partition_field_length"); + + for (i= 0; i < num_part_fields; i++) + store_length+= get_partition_field_store_length(part_field_array[i]); + if (store_length > MAX_KEY_LENGTH) + DBUG_RETURN(TRUE); + store_length= 0; + for (i= 0; i < num_subpart_fields; i++) + store_length+= get_partition_field_store_length(subpart_field_array[i]); + if (store_length > MAX_KEY_LENGTH) + DBUG_RETURN(TRUE); + DBUG_RETURN(FALSE); +} + + /* Set up buffers and arrays for fields requiring preparation SYNOPSIS @@ -1221,46 +1482,6 @@ bool partition_info::set_up_charset_field_preps() } subpart_charset_field_array[i]= NULL; } - if (tot_fields) - { - uint k; - size= tot_fields*sizeof(char**); - if (!(char_ptrs= (uchar**)sql_calloc(size))) - goto error; - full_part_field_buffers= char_ptrs; - if (!(char_ptrs= (uchar**)sql_calloc(size))) - goto error; - restore_full_part_field_ptrs= char_ptrs; - size= (tot_fields + 1) * sizeof(char**); - if (!(char_ptrs= (uchar**)sql_calloc(size))) - goto error; - full_part_charset_field_array= (Field**)char_ptrs; - for (i= 0; i < tot_part_fields; i++) - { - full_part_charset_field_array[i]= part_charset_field_array[i]; - full_part_field_buffers[i]= part_field_buffers[i]; - } - k= tot_part_fields; - for (i= 0; i < tot_subpart_fields; i++) - { - uint j; - bool found= FALSE; - field= subpart_charset_field_array[i]; - - for (j= 0; j < tot_part_fields; j++) - { - if (field == part_charset_field_array[i]) - found= TRUE; - } - if (!found) - { - full_part_charset_field_array[k]= subpart_charset_field_array[i]; - full_part_field_buffers[k]= subpart_field_buffers[i]; - k++; - } - } - full_part_charset_field_array[k]= NULL; - } DBUG_RETURN(FALSE); error: mem_alloc_error(size); @@ -1321,5 +1542,631 @@ id_err: return 1; } +/* + Create a new column value in current list with maxvalue + Called from parser + + SYNOPSIS + add_max_value() + RETURN + TRUE Error + FALSE Success +*/ + +int partition_info::add_max_value() +{ + DBUG_ENTER("partition_info::add_max_value"); + + part_column_list_val *col_val; + if (!(col_val= add_column_value())) + { + DBUG_RETURN(TRUE); + } + col_val->max_value= TRUE; + DBUG_RETURN(FALSE); +} + +/* + Create a new column value in current list + Called from parser + + SYNOPSIS + add_column_value() + RETURN + >0 A part_column_list_val object which have been + inserted into its list + 0 Memory allocation failure +*/ + +part_column_list_val *partition_info::add_column_value() +{ + uint max_val= num_columns ? num_columns : MAX_REF_PARTS; + DBUG_ENTER("add_column_value"); + DBUG_PRINT("enter", ("num_columns = %u, curr_list_object %u, max_val = %u", + num_columns, curr_list_object, max_val)); + if (curr_list_object < max_val) + { + curr_list_val->added_items++; + DBUG_RETURN(&curr_list_val->col_val_array[curr_list_object++]); + } + if (!num_columns && part_type == LIST_PARTITION) + { + /* + We're trying to add more than MAX_REF_PARTS, this can happen + in ALTER TABLE using List partitions where the first partition + uses VALUES IN (1,2,3...,17) where the number of fields in + the list is more than MAX_REF_PARTS, in this case we know + that the number of columns must be 1 and we thus reorganize + into the structure used for 1 column. After this we call + ourselves recursively which should always succeed. + */ + if (!reorganize_into_single_field_col_val()) + { + DBUG_RETURN(add_column_value()); + } + DBUG_RETURN(NULL); + } + if (column_list) + { + my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0)); + } + else + { + if (part_type == RANGE_PARTITION) + my_error(ER_TOO_MANY_VALUES_ERROR, MYF(0), "RANGE"); + else + my_error(ER_TOO_MANY_VALUES_ERROR, MYF(0), "LIST"); + } + DBUG_RETURN(NULL); +} + + +/* + Initialise part_elem_value object at setting of a new object + (Helper functions to functions called by parser) + + SYNOPSIS + init_col_val + col_val Column value object to be initialised + item Item object representing column value + + RETURN VALUES + TRUE Failure + FALSE Success +*/ +void partition_info::init_col_val(part_column_list_val *col_val, Item *item) +{ + DBUG_ENTER("partition_info::init_col_val"); + + col_val->item_expression= item; + col_val->null_value= item->null_value; + if (item->result_type() == INT_RESULT) + { + /* + This could be both column_list partitioning and function + partitioning, but it doesn't hurt to set the function + partitioning flags about unsignedness. + */ + curr_list_val->value= item->val_int(); + curr_list_val->unsigned_flag= TRUE; + if (!item->unsigned_flag && + curr_list_val->value < 0) + curr_list_val->unsigned_flag= FALSE; + if (!curr_list_val->unsigned_flag) + curr_part_elem->signed_flag= TRUE; + } + col_val->part_info= NULL; + DBUG_VOID_RETURN; +} +/* + Add a column value in VALUES LESS THAN or VALUES IN + (Called from parser) + + SYNOPSIS + add_column_list_value() + lex Parser's lex object + thd Thread object + item Item object representing column value + + RETURN VALUES + TRUE Failure + FALSE Success +*/ +bool partition_info::add_column_list_value(THD *thd, Item *item) +{ + part_column_list_val *col_val; + Name_resolution_context *context= &thd->lex->current_select->context; + TABLE_LIST *save_list= context->table_list; + const char *save_where= thd->where; + DBUG_ENTER("partition_info::add_column_list_value"); + + if (part_type == LIST_PARTITION && + num_columns == 1U) + { + if (init_column_part()) + { + DBUG_RETURN(TRUE); + } + } + + context->table_list= 0; + if (column_list) + thd->where= "field list"; + else + thd->where= "partition function"; + + if (item->walk(&Item::check_partition_func_processor, 0, + NULL)) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + DBUG_RETURN(TRUE); + } + if (item->fix_fields(thd, (Item**)0) || + ((context->table_list= save_list), FALSE) || + (!item->const_item())) + { + context->table_list= save_list; + thd->where= save_where; + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + DBUG_RETURN(TRUE); + } + thd->where= save_where; + + if (!(col_val= add_column_value())) + { + DBUG_RETURN(TRUE); + } + init_col_val(col_val, item); + DBUG_RETURN(FALSE); +} + +/* + Initialise part_info object for receiving a set of column values + for a partition, called when parser reaches VALUES LESS THAN or + VALUES IN. + + SYNOPSIS + init_column_part() + lex Parser's lex object + + RETURN VALUES + TRUE Failure + FALSE Success +*/ +bool partition_info::init_column_part() +{ + partition_element *p_elem= curr_part_elem; + part_column_list_val *col_val_array; + part_elem_value *list_val; + uint loc_num_columns; + DBUG_ENTER("partition_info::init_column_part"); + + if (!(list_val= + (part_elem_value*)sql_calloc(sizeof(part_elem_value))) || + p_elem->list_val_list.push_back(list_val)) + { + mem_alloc_error(sizeof(part_elem_value)); + DBUG_RETURN(TRUE); + } + if (num_columns) + loc_num_columns= num_columns; + else + loc_num_columns= MAX_REF_PARTS; + if (!(col_val_array= + (part_column_list_val*)sql_calloc(loc_num_columns * + sizeof(part_column_list_val)))) + { + mem_alloc_error(loc_num_columns * sizeof(part_elem_value)); + DBUG_RETURN(TRUE); + } + list_val->col_val_array= col_val_array; + list_val->added_items= 0; + curr_list_val= list_val; + curr_list_object= 0; + DBUG_RETURN(FALSE); +} + +/* + In the case of ALTER TABLE ADD/REORGANIZE PARTITION for LIST + partitions we can specify list values as: + VALUES IN (v1, v2,,,, v17) if we're using the first partitioning + variant with a function or a column list partitioned table with + one partition field. In this case the parser knows not the + number of columns start with and allocates MAX_REF_PARTS in the + array. If we try to allocate something beyond MAX_REF_PARTS we + will call this function to reorganize into a structure with + num_columns = 1. Also when the parser knows that we used LIST + partitioning and we used a VALUES IN like above where number of + values was smaller than MAX_REF_PARTS or equal, then we will + reorganize after discovering this in the parser. + + SYNOPSIS + reorganize_into_single_field_col_val() + + RETURN VALUES + TRUE Failure + FALSE Success +*/ +int partition_info::reorganize_into_single_field_col_val() +{ + part_column_list_val *col_val, *new_col_val; + part_elem_value *val= curr_list_val; + uint loc_num_columns= num_columns; + uint i; + DBUG_ENTER("partition_info::reorganize_into_single_field_col_val"); + + num_columns= 1; + val->added_items= 1U; + col_val= &val->col_val_array[0]; + init_col_val(col_val, col_val->item_expression); + for (i= 1; i < loc_num_columns; i++) + { + col_val= &val->col_val_array[i]; + DBUG_ASSERT(part_type == LIST_PARTITION); + if (init_column_part()) + { + DBUG_RETURN(TRUE); + } + if (!(new_col_val= add_column_value())) + { + DBUG_RETURN(TRUE); + } + memcpy(new_col_val, col_val, sizeof(*col_val)); + init_col_val(new_col_val, col_val->item_expression); + } + curr_list_val= val; + DBUG_RETURN(FALSE); +} + +/* + This function handles the case of function-based partitioning. + It fixes some data structures created in the parser and puts + them in the format required by the rest of the partitioning + code. + + SYNOPSIS + fix_func_partition() + thd Thread object + col_val Array of one value + part_elem The partition instance + part_id Id of partition instance + + RETURN VALUES + TRUE Failure + FALSE Success +*/ +int partition_info::fix_func_partition(THD *thd, + part_elem_value *val, + partition_element *part_elem, + uint part_id) +{ + part_column_list_val *col_val= val->col_val_array; + DBUG_ENTER("partition_info::fix_func_partition"); + + if (col_val->fixed) + { + DBUG_RETURN(FALSE); + } + if (val->added_items != 1) + { + my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } + if (col_val->max_value) + { + /* The parser ensures we're not LIST partitioned here */ + DBUG_ASSERT(part_type == RANGE_PARTITION); + if (defined_max_value) + { + my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } + if (part_id == (num_parts - 1)) + { + defined_max_value= TRUE; + part_elem->max_value= TRUE; + part_elem->range_value= LONGLONG_MAX; + } + else + { + my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } + } + else + { + Item *item_expr= col_val->item_expression; + if ((val->null_value= item_expr->null_value)) + { + if (part_elem->has_null_value) + { + my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } + part_elem->has_null_value= TRUE; + } + else if (item_expr->result_type() != INT_RESULT) + { + my_error(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } + if (part_type == RANGE_PARTITION) + { + if (part_elem->has_null_value) + { + my_error(ER_NULL_IN_VALUES_LESS_THAN, MYF(0)); + DBUG_RETURN(TRUE); + } + part_elem->range_value= val->value; + } + } + col_val->fixed= 2; + DBUG_RETURN(FALSE); +} + +/* + Get column item with a proper character set according to the field + + SYNOPSIS + get_column_item() + item Item object to start with + field Field for which the item will be compared to + + RETURN VALUES + NULL Error + item Returned item +*/ + +Item* partition_info::get_column_item(Item *item, Field *field) +{ + if (field->result_type() == STRING_RESULT && + item->collation.collation != field->charset()) + { + if (!(item= convert_charset_partition_constant(item, + field->charset()))) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + return NULL; + } + } + return item; +} + +/* + Evaluate VALUES functions for column list values + SYNOPSIS + fix_column_value_functions() + thd Thread object + col_val List of column values + part_id Partition id we are fixing + + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Fix column VALUES and store in memory array adapted to the data type +*/ + +bool partition_info::fix_column_value_functions(THD *thd, + part_elem_value *val, + uint part_id) +{ + uint num_columns= part_field_list.elements; + bool result= FALSE; + uint i; + part_column_list_val *col_val= val->col_val_array; + DBUG_ENTER("partition_info::fix_column_value_functions"); + + if (col_val->fixed > 1) + { + DBUG_RETURN(FALSE); + } + for (i= 0; i < num_columns; col_val++, i++) + { + Item *column_item= col_val->item_expression; + Field *field= part_field_array[i]; + col_val->part_info= this; + col_val->partition_id= part_id; + if (col_val->max_value) + col_val->column_value= NULL; + else + { + col_val->column_value= NULL; + if (!col_val->null_value) + { + uchar *val_ptr; + uint len= field->pack_length(); + ulong save_sql_mode; + bool save_got_warning; + + if (!(column_item= get_column_item(column_item, + field))) + { + result= TRUE; + goto end; + } + save_sql_mode= thd->variables.sql_mode; + thd->variables.sql_mode= 0; + save_got_warning= thd->got_warning; + thd->got_warning= 0; + if (column_item->save_in_field(field, TRUE) || + thd->got_warning) + { + my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0)); + result= TRUE; + goto end; + } + thd->got_warning= save_got_warning; + thd->variables.sql_mode= save_sql_mode; + if (!(val_ptr= (uchar*) sql_calloc(len))) + { + mem_alloc_error(len); + result= TRUE; + goto end; + } + col_val->column_value= val_ptr; + memcpy(val_ptr, field->ptr, len); + } + } + col_val->fixed= 2; + } +end: + DBUG_RETURN(result); +} + +/* + The parser generates generic data structures, we need to set them up + as the rest of the code expects to find them. This is in reality part + of the syntax check of the parser code. + + It is necessary to call this function in the case of a CREATE TABLE + statement, in this case we do it early in the check_partition_info + function. + + It is necessary to call this function for ALTER TABLE where we + assign a completely new partition structure, in this case we do it + in prep_alter_part_table after discovering that the partition + structure is entirely redefined. + + It's necessary to call this method also for ALTER TABLE ADD/REORGANIZE + of partitions, in this we call it in prep_alter_part_table after + making some initial checks but before going deep to check the partition + info, we also assign the column_list variable before calling this function + here. + + Finally we also call it immediately after returning from parsing the + partitioning text found in the frm file. + + This function mainly fixes the VALUES parts, these are handled differently + whether or not we use column list partitioning. Since the parser doesn't + know which we are using we need to set-up the old data structures after + the parser is complete when we know if what type of partitioning the + base table is using. + + For column lists we will handle this in the fix_column_value_function. + For column lists it is sufficient to verify that the number of columns + and number of elements are in synch with each other. So only partitioning + using functions need to be set-up to their data structures. + + SYNOPSIS + fix_parser_data() + thd Thread object + + RETURN VALUES + TRUE Failure + FALSE Success +*/ + +int partition_info::fix_parser_data(THD *thd) +{ + List_iterator<partition_element> it(partitions); + partition_element *part_elem; + uint num_elements; + uint i= 0, j, k; + DBUG_ENTER("partition_info::fix_parser_data"); + + if (!(part_type == RANGE_PARTITION || + part_type == LIST_PARTITION)) + { + /* Nothing to do for HASH/KEY partitioning */ + DBUG_RETURN(FALSE); + } + do + { + part_elem= it++; + List_iterator<part_elem_value> list_val_it(part_elem->list_val_list); + j= 0; + num_elements= part_elem->list_val_list.elements; + DBUG_ASSERT(part_type == RANGE_PARTITION ? + num_elements == 1U : TRUE); + do + { + part_elem_value *val= list_val_it++; + if (column_list) + { + if (val->added_items != num_columns) + { + my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } + for (k= 0; k < num_columns; k++) + { + part_column_list_val *col_val= &val->col_val_array[k]; + if (col_val->null_value && part_type == RANGE_PARTITION) + { + my_error(ER_NULL_IN_VALUES_LESS_THAN, MYF(0)); + DBUG_RETURN(TRUE); + } + } + } + else + { + if (fix_func_partition(thd, val, part_elem, i)) + { + DBUG_RETURN(TRUE); + } + if (val->null_value) + { + /* + Null values aren't required in the value part, they are kept per + partition instance, only LIST partitions have NULL values. + */ + list_val_it.remove(); + } + } + } while (++j < num_elements); + } while (++i < num_parts); + DBUG_RETURN(FALSE); +} + +void partition_info::print_debug(const char *str, uint *value) +{ + DBUG_ENTER("print_debug"); + if (value) + DBUG_PRINT("info", ("parser: %s, val = %u", str, *value)); + else + DBUG_PRINT("info", ("parser: %s", str)); + DBUG_VOID_RETURN; +} +#else /* WITH_PARTITION_STORAGE_ENGINE */ + /* + For builds without partitioning we need to define these functions + since we they are called from the parser. The parser cannot + remove code parts using ifdef, but the code parts cannot be called + so we simply need to add empty functions to make the linker happy. + */ +part_column_list_val *partition_info::add_column_value() +{ + return NULL; +} + +bool partition_info::set_part_expr(char *start_token, Item *item_ptr, + char *end_token, bool is_subpart) +{ + (void)start_token; + (void)item_ptr; + (void)end_token; + (void)is_subpart; + return FALSE; +} + +int partition_info::reorganize_into_single_field_col_val() +{ + return 0; +} + +bool partition_info::init_column_part() +{ + return FALSE; +} + +bool partition_info::add_column_list_value(Item *item) +{ + return FALSE; +} +int partition_info::add_max_value() +{ + return 0; +} #endif /* WITH_PARTITION_STORAGE_ENGINE */ diff --git a/sql/partition_info.h b/sql/partition_info.h index b5a6c4a0961..0ac8dec4945 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -1,7 +1,7 @@ #ifndef PARTITION_INFO_INCLUDED #define PARTITION_INFO_INCLUDED -/* Copyright 2006-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright 2006-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -67,10 +67,9 @@ public: /* When we have various string fields we might need some preparation before and clean-up after calling the get_part_id_func's. We need - one such method for get_partition_id and one for - get_part_partition_id and one for get_subpartition_id. + one such method for get_part_partition_id and one for + get_subpartition_id. */ - get_part_id_func get_partition_id_charset; get_part_id_func get_part_partition_id_charset; get_subpart_id_func get_subpartition_id_charset; @@ -84,7 +83,6 @@ public: without duplicates, NULL-terminated. */ Field **full_part_field_array; - Field **full_part_charset_field_array; /* Set of all fields used in partition and subpartition expression. Required for testing of partition fields in write_set when @@ -100,10 +98,8 @@ public: */ uchar **part_field_buffers; uchar **subpart_field_buffers; - uchar **full_part_field_buffers; uchar **restore_part_field_ptrs; uchar **restore_subpart_field_ptrs; - uchar **restore_full_part_field_ptrs; Item *part_expr; Item *subpart_expr; @@ -127,6 +123,8 @@ public: union { longlong *range_int_array; LIST_PART_ENTRY *list_array; + part_column_list_val *range_col_array; + part_column_list_val *list_col_array; }; /******************************************** @@ -157,6 +155,10 @@ public: partition_element *curr_part_elem; partition_element *current_partition; + part_elem_value *curr_list_val; + uint curr_list_object; + uint num_columns; + /* These key_map's are used for Partitioning to enable quick decisions on whether we can derive more information about which partition to @@ -175,17 +177,17 @@ public: uint part_func_len; uint subpart_func_len; - uint no_parts; - uint no_subparts; + uint num_parts; + uint num_subparts; uint count_curr_subparts; uint part_error_code; - uint no_list_values; + uint num_list_values; - uint no_part_fields; - uint no_subpart_fields; - uint no_full_part_fields; + uint num_part_fields; + uint num_subpart_fields; + uint num_full_part_fields; uint has_null_part_id; /* @@ -196,9 +198,9 @@ public: uint16 linear_hash_mask; bool use_default_partitions; - bool use_default_no_partitions; + bool use_default_num_partitions; bool use_default_subpartitions; - bool use_default_no_subpartitions; + bool use_default_num_subpartitions; bool default_partitions_setup; bool defined_max_value; bool list_of_part_fields; @@ -208,7 +210,7 @@ public: bool is_auto_partitioned; bool from_openfrm; bool has_null_value; - + bool column_list; partition_info() : get_partition_id(NULL), get_part_partition_id(NULL), @@ -217,11 +219,8 @@ public: part_charset_field_array(NULL), subpart_charset_field_array(NULL), full_part_field_array(NULL), - full_part_charset_field_array(NULL), part_field_buffers(NULL), subpart_field_buffers(NULL), - full_part_field_buffers(NULL), restore_part_field_ptrs(NULL), restore_subpart_field_ptrs(NULL), - restore_full_part_field_ptrs(NULL), part_expr(NULL), subpart_expr(NULL), item_free_list(NULL), first_log_entry(NULL), exec_log_entry(NULL), frm_log_entry(NULL), list_array(NULL), err_value(0), @@ -229,22 +228,23 @@ public: part_func_string(NULL), subpart_func_string(NULL), part_state(NULL), curr_part_elem(NULL), current_partition(NULL), + curr_list_object(0), num_columns(0), default_engine_type(NULL), part_result_type(INT_RESULT), part_type(NOT_A_PARTITION), subpart_type(NOT_A_PARTITION), part_info_len(0), part_state_len(0), part_func_len(0), subpart_func_len(0), - no_parts(0), no_subparts(0), + num_parts(0), num_subparts(0), count_curr_subparts(0), part_error_code(0), - no_list_values(0), no_part_fields(0), no_subpart_fields(0), - no_full_part_fields(0), has_null_part_id(0), linear_hash_mask(0), - use_default_partitions(TRUE), use_default_no_partitions(TRUE), - use_default_subpartitions(TRUE), use_default_no_subpartitions(TRUE), + num_list_values(0), num_part_fields(0), num_subpart_fields(0), + num_full_part_fields(0), has_null_part_id(0), linear_hash_mask(0), + use_default_partitions(TRUE), use_default_num_partitions(TRUE), + use_default_subpartitions(TRUE), use_default_num_subpartitions(TRUE), default_partitions_setup(FALSE), defined_max_value(FALSE), list_of_part_fields(FALSE), list_of_subpart_fields(FALSE), linear_hash_ind(FALSE), fixed(FALSE), is_auto_partitioned(FALSE), from_openfrm(FALSE), - has_null_value(FALSE) + has_null_value(FALSE), column_list(FALSE) { all_fields_in_PF.clear_all(); all_fields_in_PPF.clear_all(); @@ -267,27 +267,47 @@ public: /* Returns the total number of partitions on the leaf level */ uint get_tot_partitions() { - return no_parts * (is_sub_partitioned() ? no_subparts : 1); + return num_parts * (is_sub_partitioned() ? num_subparts : 1); } bool set_up_defaults_for_partitioning(handler *file, HA_CREATE_INFO *info, uint start_no); + char *has_unique_fields(); char *has_unique_names(); bool check_engine_mix(handlerton *engine_type, bool default_engine); - bool check_range_constants(); - bool check_list_constants(); + bool check_range_constants(THD *thd); + bool check_list_constants(THD *thd); bool check_partition_info(THD *thd, handlerton **eng_type, handler *file, HA_CREATE_INFO *info, bool check_partition_function); void print_no_partition_found(TABLE *table); + void print_debug(const char *str, uint*); + Item* get_column_item(Item *item, Field *field); + int fix_func_partition(THD *thd, + part_elem_value *val, + partition_element *part_elem, + uint part_id); + bool fix_column_value_functions(THD *thd, + part_elem_value *val, + uint part_id); + int fix_parser_data(THD *thd); + int add_max_value(); + void init_col_val(part_column_list_val *col_val, Item *item); + int reorganize_into_single_field_col_val(); + part_column_list_val *add_column_value(); + bool set_part_expr(char *start_token, Item *item_ptr, + char *end_token, bool is_subpart); + static int compare_column_values(const void *a, const void *b); bool set_up_charset_field_preps(); + bool check_partition_field_length(); + bool init_column_part(); + bool add_column_list_value(THD *thd, Item *item); private: static int list_part_cmp(const void* a, const void* b); - static int list_part_cmp_unsigned(const void* a, const void* b); bool set_up_default_partitions(handler *file, HA_CREATE_INFO *info, uint start_no); bool set_up_default_subpartitions(handler *file, HA_CREATE_INFO *info); - char *create_default_partition_names(uint part_no, uint no_parts, + char *create_default_partition_names(uint part_no, uint num_parts, uint start_no); char *create_subpartition_name(uint subpart_no, const char *part_name); bool has_unique_name(partition_element *element); @@ -313,7 +333,7 @@ void init_all_partitions_iterator(partition_info *part_info, PARTITION_ITERATOR *part_iter) { part_iter->part_nums.start= part_iter->part_nums.cur= 0; - part_iter->part_nums.end= part_info->no_parts; + part_iter->part_nums.end= part_info->num_parts; part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE; part_iter->get_next= get_next_partition_id_range; } diff --git a/sql/procedure.h b/sql/procedure.h index c6f50493876..25b30ac593d 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -26,7 +26,7 @@ #define PROC_NO_SORT 1 /**< Bits in flags */ #define PROC_GROUP 2 /**< proc must have group */ -/* Procedure items used by procedures to store values for send_fields */ +/* Procedure items used by procedures to store values for send_result_set_metadata */ class Item_proc :public Item { diff --git a/sql/protocol.cc b/sql/protocol.cc index fcfa14310bb..5990f0f001a 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -28,6 +28,7 @@ #include <stdarg.h> static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; +/* Declared non-static only because of the embedded library. */ bool net_send_error_packet(THD *, uint, const char *, const char *); /* Declared non-static only because of the embedded library. */ bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *); @@ -141,6 +142,7 @@ bool Protocol::net_store_data(const uchar *from, size_t length, bool net_send_error(THD *thd, uint sql_errno, const char *err, const char* sqlstate) { + bool error; DBUG_ENTER("net_send_error"); DBUG_ASSERT(!thd->spcont); @@ -148,7 +150,6 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err, DBUG_ASSERT(err); DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err)); - bool error; if (sqlstate == NULL) sqlstate= mysql_errno_to_sqlstate(sql_errno); @@ -397,32 +398,24 @@ bool net_send_error_packet(THD *thd, uint sql_errno, const char *err, DBUG_RETURN(FALSE); } - 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= (uchar*) strmov((char*) buff+3, sqlstate); - } - converted_err_len= convert_error_message((char*)converted_err, - sizeof(converted_err), - thd->variables.character_set_results, - err, strlen(err), - system_charset_info, &error); - length= (uint) (strmake((char*) pos, (char*)converted_err, MYSQL_ERRMSG_SIZE) - - (char*) buff); - err= (char*) buff; - } - else + int2store(buff,sql_errno); + pos= buff+2; + if (thd->client_capabilities & CLIENT_PROTOCOL_41) { - length=(uint) strlen(err); - set_if_smaller(length,MYSQL_ERRMSG_SIZE-1); + /* The first # is to make the protocol backward compatible */ + buff[2]= '#'; + pos= (uchar*) strmov((char*) buff+3, sqlstate); } + converted_err_len= convert_error_message((char*)converted_err, + sizeof(converted_err), + thd->variables.character_set_results, + err, strlen(err), + system_charset_info, &error); + length= (uint) (strmake((char*) pos, (char*)converted_err, MYSQL_ERRMSG_SIZE) - + (char*) buff); + err= (char*) buff; DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err, - length)); + length)); } #endif /* EMBEDDED_LIBRARY */ @@ -486,6 +479,12 @@ static uchar *net_store_length_fast(uchar *packet, uint length) packet is "buffered" in the diagnostics area and sent to the client in the end of statement. + @note This method defines a template, but delegates actual + sending of data to virtual Protocol::send_{ok,eof,error}. This + allows for implementation of protocols that "intercept" ok/eof/error + messages, and store them in memory, etc, instead of sending to + the client. + @pre The diagnostics area is assigned or disabled. It can not be empty -- we assume that every SQL statement or COM_* command generates OK, ERROR, or EOF status. @@ -500,47 +499,94 @@ static uchar *net_store_length_fast(uchar *packet, uint length) Diagnostics_area::is_sent is set for debugging purposes only. */ -void net_end_statement(THD *thd) +void Protocol::end_statement() { + DBUG_ENTER("Protocol::end_statement"); DBUG_ASSERT(! thd->stmt_da->is_sent); + bool error= FALSE; /* Can not be true, but do not take chances in production. */ if (thd->stmt_da->is_sent) - return; - - bool error= FALSE; + DBUG_VOID_RETURN; switch (thd->stmt_da->status()) { case Diagnostics_area::DA_ERROR: /* The query failed, send error to log and abort bootstrap. */ - error= net_send_error(thd, - thd->stmt_da->sql_errno(), - thd->stmt_da->message(), - thd->stmt_da->get_sqlstate()); + error= send_error(thd->stmt_da->sql_errno(), + thd->stmt_da->message(), + thd->stmt_da->get_sqlstate()); break; case Diagnostics_area::DA_EOF: - error= net_send_eof(thd, - thd->stmt_da->server_status(), - thd->stmt_da->statement_warn_count()); + error= send_eof(thd->stmt_da->server_status(), + thd->stmt_da->statement_warn_count()); break; case Diagnostics_area::DA_OK: - error= net_send_ok(thd, - thd->stmt_da->server_status(), - thd->stmt_da->statement_warn_count(), - thd->stmt_da->affected_rows(), - thd->stmt_da->last_insert_id(), - thd->stmt_da->message()); + error= send_ok(thd->stmt_da->server_status(), + thd->stmt_da->statement_warn_count(), + thd->stmt_da->affected_rows(), + thd->stmt_da->last_insert_id(), + thd->stmt_da->message()); break; case Diagnostics_area::DA_DISABLED: break; case Diagnostics_area::DA_EMPTY: default: DBUG_ASSERT(0); - error= net_send_ok(thd, thd->server_status, 0, 0, 0, NULL); + error= send_ok(thd->server_status, 0, 0, 0, NULL); break; } if (!error) thd->stmt_da->is_sent= TRUE; + DBUG_VOID_RETURN; +} + + +/** + A default implementation of "OK" packet response to the client. + + Currently this implementation is re-used by both network-oriented + protocols -- the binary and text one. They do not differ + in their OK packet format, which allows for a significant simplification + on client side. +*/ + +bool Protocol::send_ok(uint server_status, uint statement_warn_count, + ulonglong affected_rows, ulonglong last_insert_id, + const char *message) +{ + DBUG_ENTER("Protocol::send_ok"); + + DBUG_RETURN(net_send_ok(thd, server_status, statement_warn_count, + affected_rows, last_insert_id, message)); +} + + +/** + A default implementation of "EOF" packet response to the client. + + Binary and text protocol do not differ in their EOF packet format. +*/ + +bool Protocol::send_eof(uint server_status, uint statement_warn_count) +{ + DBUG_ENTER("Protocol::send_eof"); + + DBUG_RETURN(net_send_eof(thd, server_status, statement_warn_count)); +} + + +/** + A default implementation of "ERROR" packet response to the client. + + Binary and text protocol do not differ in ERROR packet format. +*/ + +bool Protocol::send_error(uint sql_errno, const char *err_msg, + const char *sql_state) +{ + DBUG_ENTER("Protocol::send_error"); + + DBUG_RETURN(net_send_error_packet(thd, sql_errno, err_msg, sql_state)); } @@ -597,9 +643,10 @@ void Protocol::init(THD *thd_arg) for the error. */ -void Protocol::end_partial_result_set(THD *thd) +void Protocol::end_partial_result_set(THD *thd_arg) { - net_send_eof(thd, thd->server_status, 0 /* no warnings, we're inside SP */); + net_send_eof(thd_arg, thd_arg->server_status, + 0 /* no warnings, we're inside SP */); } @@ -632,16 +679,16 @@ bool Protocol::flush() 1 Error (Note that in this case the error is not sent to the client) */ -bool Protocol::send_fields(List<Item> *list, uint flags) +bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) { List_iterator_fast<Item> it(*list); Item *item; - uchar buff[80]; + uchar buff[MAX_FIELD_WIDTH]; String tmp((char*) buff,sizeof(buff),&my_charset_bin); Protocol_text prot(thd); String *local_packet= prot.storage_packet(); CHARSET_INFO *thd_charset= thd->variables.character_set_results; - DBUG_ENTER("send_fields"); + DBUG_ENTER("send_result_set_metadata"); if (flags & SEND_NUM_ROWS) { // Packet with number of elements @@ -786,7 +833,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags) write_eof_packet(thd, &thd->net, thd->server_status, thd->warning_info->statement_warn_count()); } - DBUG_RETURN(prepare_for_send(list)); + DBUG_RETURN(prepare_for_send(list->elements)); err: my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), @@ -805,6 +852,47 @@ bool Protocol::write() /** + Send one result set row. + + @param row_items a collection of column values for that row + + @return Error status. + @retval TRUE Error. + @retval FALSE Success. +*/ + +bool Protocol::send_result_set_row(List<Item> *row_items) +{ + char buffer[MAX_FIELD_WIDTH]; + String str_buffer(buffer, sizeof (buffer), &my_charset_bin); + List_iterator_fast<Item> it(*row_items); + + DBUG_ENTER("Protocol::send_result_set_row"); + + for (Item *item= it++; item; item= it++) + { + if (item->send(this, &str_buffer)) + { + // If we're out of memory, reclaim some, to help us recover. + this->free(); + DBUG_RETURN(TRUE); + } + /* Item::send() may generate an error. If so, abort the loop. */ + if (thd->is_error()) + DBUG_RETURN(TRUE); + + /* + Reset str_buffer to its original state, as it may have been altered in + Item::send(). + */ + str_buffer.set(buffer, sizeof(buffer), &my_charset_bin); + } + + DBUG_RETURN(FALSE); +} + + +/** Send \\0 end terminated string. @param from NullS or \\0 terminated string @@ -850,7 +938,6 @@ bool Protocol::store(I_List<i_string>* str_list) return store((char*) tmp.ptr(), len, tmp.charset()); } - /**************************************************************************** Functions to handle the simple (default) protocol where everything is This protocol is the one that is used by default between the MySQL server @@ -1129,6 +1216,53 @@ bool Protocol_text::store_time(MYSQL_TIME *tm) return net_store_data((uchar*) buff, length); } +/** + Assign OUT-parameters to user variables. + + @param sp_params List of PS/SP parameters (both input and output). + + @return Error status. + @retval FALSE Success. + @retval TRUE Error. +*/ + +bool Protocol_text::send_out_parameters(List<Item_param> *sp_params) +{ + DBUG_ASSERT(sp_params->elements == + thd->lex->prepared_stmt_params.elements); + + List_iterator_fast<Item_param> item_param_it(*sp_params); + List_iterator_fast<LEX_STRING> user_var_name_it(thd->lex->prepared_stmt_params); + + while (true) + { + Item_param *item_param= item_param_it++; + LEX_STRING *user_var_name= user_var_name_it++; + + if (!item_param || !user_var_name) + break; + + if (!item_param->get_out_param_info()) + continue; // It's an IN-parameter. + + Item_func_set_user_var *suv= + new Item_func_set_user_var(*user_var_name, item_param); + /* + Item_func_set_user_var is not fixed after construction, call + fix_fields(). + */ + if (suv->fix_fields(thd, NULL)) + return TRUE; + + if (suv->check(FALSE)) + return TRUE; + + if (suv->update()) + return TRUE; + } + + return FALSE; +} /**************************************************************************** Functions to handle the binary protocol used with prepared statements @@ -1149,14 +1283,13 @@ bool Protocol_text::store_time(MYSQL_TIME *tm) [..]..[[length]data] data ****************************************************************************/ -bool Protocol_binary::prepare_for_send(List<Item> *item_list) +bool Protocol_binary::prepare_for_send(uint num_columns) { - Protocol::prepare_for_send(item_list); + Protocol::prepare_for_send(num_columns); bit_fields= (field_count+9)/8; - if (packet->alloc(bit_fields+1)) - return 1; + return packet->alloc(bit_fields+1); + /* prepare_for_resend will be called after this one */ - return 0; } @@ -1344,3 +1477,80 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm) buff[0]=(char) length; // Length is stored first return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC); } + +/** + Send a result set with OUT-parameter values by means of PS-protocol. + + @param sp_params List of PS/SP parameters (both input and output). + + @return Error status. + @retval FALSE Success. + @retval TRUE Error. +*/ + +bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params) +{ + if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS)) + { + /* The client does not support OUT-parameters. */ + return FALSE; + } + + List<Item> out_param_lst; + + { + List_iterator_fast<Item_param> item_param_it(*sp_params); + + while (true) + { + Item_param *item_param= item_param_it++; + + if (!item_param) + break; + + if (!item_param->get_out_param_info()) + continue; // It's an IN-parameter. + + if (out_param_lst.push_back(item_param)) + return TRUE; + } + } + + if (!out_param_lst.elements) + return FALSE; + + /* + We have to set SERVER_PS_OUT_PARAMS in THD::server_status, because it + is used in send_result_set_metadata(). + */ + + thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS; + + /* Send meta-data. */ + if (send_result_set_metadata(&out_param_lst, SEND_NUM_ROWS | SEND_EOF)) + return TRUE; + + /* Send data. */ + + prepare_for_resend(); + + if (send_result_set_row(&out_param_lst)) + return TRUE; + + if (write()) + return TRUE; + + /* Restore THD::server_status. */ + thd->server_status&= ~SERVER_PS_OUT_PARAMS; + + /* + Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet + for sure. + */ + thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; + + /* Send EOF-packet. */ + net_send_eof(thd, thd->server_status, 0); + + return FALSE; +} diff --git a/sql/protocol.h b/sql/protocol.h index 2857ee2e3ca..142f7919d6f 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -24,6 +24,7 @@ class i_string; class THD; +class Item_param; typedef struct st_mysql_field MYSQL_FIELD; typedef struct st_mysql_rows MYSQL_ROWS; @@ -50,6 +51,16 @@ protected: CHARSET_INFO *fromcs, CHARSET_INFO *tocs); bool store_string_aux(const char *from, size_t length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs); + + virtual bool send_ok(uint server_status, uint statement_warn_count, + ulonglong affected_rows, ulonglong last_insert_id, + const char *message); + + virtual bool send_eof(uint server_status, uint statement_warn_count); + + virtual bool send_error(uint sql_errno, const char *err_msg, + const char *sql_state); + public: Protocol() {} Protocol(THD *thd_arg) { init(thd_arg); } @@ -57,7 +68,8 @@ public: void init(THD* thd_arg); enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 }; - virtual bool send_fields(List<Item> *list, uint flags); + virtual bool send_result_set_metadata(List<Item> *list, uint flags); + bool send_result_set_row(List<Item> *row_items); bool store(I_List<i_string> *str_list); bool store(const char *from, CHARSET_INFO *cs); @@ -75,9 +87,9 @@ public: inline bool store(String *str) { return store((char*) str->ptr(), str->length(), str->charset()); } - virtual bool prepare_for_send(List<Item> *item_list) + virtual bool prepare_for_send(uint num_columns) { - field_count=item_list->elements; + field_count= num_columns; return 0; } virtual bool flush(); @@ -99,6 +111,8 @@ public: virtual bool store_date(MYSQL_TIME *time)=0; virtual bool store_time(MYSQL_TIME *time)=0; virtual bool store(Field *field)=0; + + virtual bool send_out_parameters(List<Item_param> *sp_params)=0; #ifdef EMBEDDED_LIBRARY int begin_dataset(); virtual void remove_last_row() {} @@ -107,13 +121,15 @@ public: #endif enum enum_protocol_type { - PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1 /* - before adding here or change the values, consider that it is cast to a - bit in sql_cache.cc. + Before adding a new type, please make sure + there is enough storage for it in Query_cache_query_flags. */ + PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1, PROTOCOL_LOCAL= 2 }; virtual enum enum_protocol_type type()= 0; + + void end_statement(); }; @@ -140,6 +156,8 @@ public: virtual bool store(float nr, uint32 decimals, String *buffer); virtual bool store(double from, uint32 decimals, String *buffer); virtual bool store(Field *field); + + virtual bool send_out_parameters(List<Item_param> *sp_params); #ifdef EMBEDDED_LIBRARY void remove_last_row(); #endif @@ -154,7 +172,7 @@ private: public: Protocol_binary() {} Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {} - virtual bool prepare_for_send(List<Item> *item_list); + virtual bool prepare_for_send(uint num_columns); virtual void prepare_for_resend(); #ifdef EMBEDDED_LIBRARY virtual bool write(); @@ -175,13 +193,15 @@ public: virtual bool store(float nr, uint32 decimals, String *buffer); virtual bool store(double from, uint32 decimals, String *buffer); virtual bool store(Field *field); + + virtual bool send_out_parameters(List<Item_param> *sp_params); + virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; }; }; void send_warning(THD *thd, uint sql_errno, const char *err=0); bool net_send_error(THD *thd, uint sql_errno, const char *err, const char* sqlstate); -void net_end_statement(THD *thd); bool send_old_password_request(THD *thd); uchar *net_store_data(uchar *to,const uchar *from, size_t length); uchar *net_store_data(uchar *to,int32 from); diff --git a/sql/records.cc b/sql/records.cc index 9e040de3fda..9b5ea40478e 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -13,6 +13,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef USE_PRAGMA_INTERFACE +#pragma implementation /* gcc class implementation */ +#endif /** @file @@ -21,8 +24,10 @@ Functions for easy reading of records, possible through a cache */ +#include "records.h" #include "mysql_priv.h" + static int rr_quick(READ_RECORD *info); int rr_sequential(READ_RECORD *info); static int rr_from_tempfile(READ_RECORD *info); diff --git a/sql/records.h b/sql/records.h new file mode 100644 index 00000000000..9207a05f826 --- /dev/null +++ b/sql/records.h @@ -0,0 +1,75 @@ +#ifndef SQL_RECORDS_H +#define SQL_RECORDS_H +/* Copyright (C) 2008 Sun/MySQL + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif +#include <my_global.h> /* for uint typedefs */ + +struct st_join_table; +class handler; +struct TABLE; +class THD; +class SQL_SELECT; + +/** + A context for reading through a single table using a chosen access method: + index read, scan, etc, use of cache, etc. + + Use by: + READ_RECORD read_record; + init_read_record(&read_record, ...); + while (read_record.read_record()) + { + ... + } + end_read_record(); +*/ + +struct READ_RECORD +{ + typedef int (*Read_func)(READ_RECORD*); + typedef int (*Setup_func)(struct st_join_table*); + + TABLE *table; /* Head-form */ + handler *file; + TABLE **forms; /* head and ref forms */ + Read_func read_record; + THD *thd; + SQL_SELECT *select; + uint cache_records; + uint ref_length,struct_length,reclength,rec_cache_size,error_offset; + uint index; + uchar *ref_pos; /* pointer to form->refpos */ + uchar *record; + uchar *rec_buf; /* to read field values after filesort */ + uchar *cache,*cache_pos,*cache_end,*read_positions; + struct st_io_cache *io_cache; + bool print_error, ignore_not_found_rows; + +public: + READ_RECORD() {} +}; + +void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form, + SQL_SELECT *select, int use_record_cache, + bool print_errors, bool disable_rr_cache); +void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, + bool print_error, uint idx); +void end_read_record(READ_RECORD *info); + +#endif /* SQL_RECORDS_H */ diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 8963532e192..c6c7f185080 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -146,10 +146,10 @@ void unregister_slave(THD* thd, bool only_mine, bool need_mutex) pthread_mutex_lock(&LOCK_slave_list); SLAVE_INFO* old_si; - if ((old_si = (SLAVE_INFO*)hash_search(&slave_list, - (uchar*)&thd->server_id, 4)) && + if ((old_si = (SLAVE_INFO*)my_hash_search(&slave_list, + (uchar*)&thd->server_id, 4)) && (!only_mine || old_si->thd == thd)) - hash_delete(&slave_list, (uchar*)old_si); + my_hash_delete(&slave_list, (uchar*)old_si); if (need_mutex) pthread_mutex_unlock(&LOCK_slave_list); @@ -221,17 +221,18 @@ extern "C" void slave_info_free(void *s) void init_slave_list() { - hash_init(&slave_list, system_charset_info, SLAVE_LIST_CHUNK, 0, 0, - (hash_get_key) slave_list_key, (hash_free_key) slave_info_free, 0); + my_hash_init(&slave_list, system_charset_info, SLAVE_LIST_CHUNK, 0, 0, + (my_hash_get_key) slave_list_key, + (my_hash_free_key) slave_info_free, 0); pthread_mutex_init(&LOCK_slave_list, MY_MUTEX_INIT_FAST); } void end_slave_list() { /* No protection by a mutex needed as we are only called at shutdown */ - if (hash_inited(&slave_list)) + if (my_hash_inited(&slave_list)) { - hash_free(&slave_list); + my_hash_free(&slave_list); pthread_mutex_destroy(&LOCK_slave_list); } } @@ -470,7 +471,7 @@ bool show_new_master(THD* thd) field_list.push_back(new Item_empty_string("Log_name", 20)); field_list.push_back(new Item_return_int("Log_pos", 10, MYSQL_TYPE_LONGLONG)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); protocol->prepare_for_resend(); @@ -547,8 +548,8 @@ HOSTS"; uint32 log_server_id; SLAVE_INFO* si, *old_si; log_server_id = atoi(row[0]); - if ((old_si= (SLAVE_INFO*)hash_search(&slave_list, - (uchar*)&log_server_id,4))) + if ((old_si= (SLAVE_INFO*)my_hash_search(&slave_list, + (uchar*)&log_server_id,4))) si = old_si; else { @@ -676,7 +677,7 @@ bool show_slave_hosts(THD* thd) field_list.push_back(new Item_return_int("Master_id", 10, MYSQL_TYPE_LONG)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -684,7 +685,7 @@ bool show_slave_hosts(THD* thd) for (uint i = 0; i < slave_list.records; ++i) { - SLAVE_INFO* si = (SLAVE_INFO*) hash_element(&slave_list, i); + SLAVE_INFO* si = (SLAVE_INFO*) my_hash_element(&slave_list, i); protocol->prepare_for_resend(); protocol->store((uint32) si->server_id); protocol->store(si->host, &my_charset_bin); diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc index 68272c58bb1..392d8baf50e 100644 --- a/sql/rpl_filter.cc +++ b/sql/rpl_filter.cc @@ -32,9 +32,9 @@ Rpl_filter::Rpl_filter() : Rpl_filter::~Rpl_filter() { if (do_table_inited) - hash_free(&do_table); + my_hash_free(&do_table); if (ignore_table_inited) - hash_free(&ignore_table); + my_hash_free(&ignore_table); if (wild_do_table_inited) free_string_array(&wild_do_table); if (wild_ignore_table_inited) @@ -103,12 +103,12 @@ Rpl_filter::tables_ok(const char* db, TABLE_LIST* tables) len= (uint) (strmov(end, tables->table_name) - hash_key); if (do_table_inited) // if there are any do's { - if (hash_search(&do_table, (uchar*) hash_key, len)) + if (my_hash_search(&do_table, (uchar*) hash_key, len)) DBUG_RETURN(1); } if (ignore_table_inited) // if there are any ignores { - if (hash_search(&ignore_table, (uchar*) hash_key, len)) + if (my_hash_search(&ignore_table, (uchar*) hash_key, len)) DBUG_RETURN(0); } if (wild_do_table_inited && @@ -387,7 +387,7 @@ void free_table_ent(void* a) void Rpl_filter::init_table_rule_hash(HASH* h, bool* h_inited) { - hash_init(h, system_charset_info,TABLE_RULE_HASH_SIZE,0,0, + my_hash_init(h, system_charset_info,TABLE_RULE_HASH_SIZE,0,0, get_table_key, free_table_ent, 0); *h_inited = 1; } @@ -458,7 +458,7 @@ Rpl_filter::table_rule_ent_hash_to_str(String* s, HASH* h, bool inited) { for (uint i= 0; i < h->records; i++) { - TABLE_RULE_ENT* e= (TABLE_RULE_ENT*) hash_element(h, i); + TABLE_RULE_ENT* e= (TABLE_RULE_ENT*) my_hash_element(h, i); if (s->length()) s->append(','); s->append(e->db,e->key_len); diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc index da7aade5b99..b8b82bc2f98 100644 --- a/sql/rpl_handler.cc +++ b/sql/rpl_handler.cc @@ -44,7 +44,7 @@ int get_user_var_int(const char *name, { my_bool null_val; user_var_entry *entry= - (user_var_entry*) hash_search(¤t_thd->user_vars, + (user_var_entry*) my_hash_search(¤t_thd->user_vars, (uchar*) name, strlen(name)); if (!entry) return 1; @@ -59,7 +59,7 @@ int get_user_var_real(const char *name, { my_bool null_val; user_var_entry *entry= - (user_var_entry*) hash_search(¤t_thd->user_vars, + (user_var_entry*) my_hash_search(¤t_thd->user_vars, (uchar*) name, strlen(name)); if (!entry) return 1; @@ -75,7 +75,7 @@ int get_user_var_str(const char *name, char *value, String str; my_bool null_val; user_var_entry *entry= - (user_var_entry*) hash_search(¤t_thd->user_vars, + (user_var_entry*) my_hash_search(¤t_thd->user_vars, (uchar*) name, strlen(name)); if (!entry) return 1; diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h index 4ece092c5b8..a0c71fee099 100644 --- a/sql/rpl_injector.h +++ b/sql/rpl_injector.h @@ -25,9 +25,8 @@ /* Forward declarations */ class handler; class MYSQL_BIN_LOG; -struct st_table; +struct TABLE; -typedef st_table TABLE; /* Injector to inject rows into the MySQL server. diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc index a004c354263..8b7fea3b40e 100644 --- a/sql/rpl_tblmap.cc +++ b/sql/rpl_tblmap.cc @@ -34,10 +34,10 @@ table_mapping::table_mapping() No "free_element" function for entries passed here, as the entries are allocated in a MEM_ROOT (freed as a whole in the destructor), they cannot be freed one by one. - Note that below we don't test if hash_init() succeeded. This constructor - is called at startup only. + Note that below we don't test if my_hash_init() succeeded. This + constructor is called at startup only. */ - (void) hash_init(&m_table_ids,&my_charset_bin,TABLE_ID_HASH_SIZE, + (void) my_hash_init(&m_table_ids,&my_charset_bin,TABLE_ID_HASH_SIZE, offsetof(entry,table_id),sizeof(ulong), 0,0,0); /* We don't preallocate any block, this is consistent with m_free=0 above */ @@ -49,7 +49,7 @@ table_mapping::~table_mapping() #ifdef MYSQL_CLIENT clear_tables(); #endif - hash_free(&m_table_ids); + my_hash_free(&m_table_ids); free_root(&m_mem_root, MYF(0)); } @@ -115,7 +115,7 @@ int table_mapping::set_table(ulong table_id, TABLE* table) #ifdef MYSQL_CLIENT free_table_map_log_event(e->table); #endif - hash_delete(&m_table_ids,(uchar *)e); + my_hash_delete(&m_table_ids,(uchar *)e); } e->table_id= table_id; e->table= table; @@ -132,7 +132,7 @@ int table_mapping::remove_table(ulong table_id) entry *e= find_entry(table_id); if (e) { - hash_delete(&m_table_ids,(uchar *)e); + my_hash_delete(&m_table_ids,(uchar *)e); /* we add this entry to the chain of free (free for use) entries */ e->next= m_free; m_free= e; @@ -150,7 +150,7 @@ void table_mapping::clear_tables() DBUG_ENTER("table_mapping::clear_tables()"); for (uint i= 0; i < m_table_ids.records; i++) { - entry *e= (entry *)hash_element(&m_table_ids, i); + entry *e= (entry *)my_hash_element(&m_table_ids, i); #ifdef MYSQL_CLIENT free_table_map_log_event(e->table); #endif diff --git a/sql/rpl_tblmap.h b/sql/rpl_tblmap.h index 3b5b10be580..a6ec8bcbc9b 100644 --- a/sql/rpl_tblmap.h +++ b/sql/rpl_tblmap.h @@ -18,8 +18,7 @@ /* Forward declarations */ #ifndef MYSQL_CLIENT -struct st_table; -typedef st_table TABLE; +struct TABLE; #else class Table_map_log_event; typedef Table_map_log_event TABLE; @@ -91,9 +90,9 @@ private: entry *find_entry(ulong table_id) { - return (entry *)hash_search(&m_table_ids, - (uchar*)&table_id, - sizeof(table_id)); + return (entry *) my_hash_search(&m_table_ids, + (uchar*)&table_id, + sizeof(table_id)); } int expand(); diff --git a/sql/set_var.cc b/sql/set_var.cc index 29ed552bfb0..36597658077 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -125,8 +125,10 @@ static void fix_net_read_timeout(THD *thd, enum_var_type type); static void fix_net_write_timeout(THD *thd, enum_var_type type); static void fix_net_retry_count(THD *thd, enum_var_type type); static void fix_max_join_size(THD *thd, enum_var_type type); +#ifdef HAVE_QUERY_CACHE static void fix_query_cache_size(THD *thd, enum_var_type type); static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type); +#endif static void fix_myisam_max_sort_file_size(THD *thd, enum_var_type type); static void fix_max_binlog_size(THD *thd, enum_var_type type); static void fix_max_relay_log_size(THD *thd, enum_var_type type); @@ -493,9 +495,6 @@ static sys_var_thd_ulong sys_div_precincrement(&vars, "div_precision_increment", &SV::div_precincrement); static sys_var_long_ptr sys_rpl_recovery_rank(&vars, "rpl_recovery_rank", &rpl_recovery_rank); -static sys_var_long_ptr sys_query_cache_size(&vars, "query_cache_size", - &query_cache_size, - fix_query_cache_size); static sys_var_thd_ulong sys_range_alloc_block_size(&vars, "range_alloc_block_size", &SV::range_alloc_block_size); @@ -557,14 +556,20 @@ sys_var_enum_const sys_thread_handling(&vars, "thread_handling", NULL); #ifdef HAVE_QUERY_CACHE +static sys_var_long_ptr sys_query_cache_size(&vars, "query_cache_size", + &query_cache_size, + fix_query_cache_size); static sys_var_long_ptr sys_query_cache_limit(&vars, "query_cache_limit", - &query_cache.query_cache_limit); -static sys_var_long_ptr sys_query_cache_min_res_unit(&vars, "query_cache_min_res_unit", - &query_cache_min_res_unit, - fix_query_cache_min_res_unit); + &query_cache.query_cache_limit); +static sys_var_long_ptr + sys_query_cache_min_res_unit(&vars, "query_cache_min_res_unit", + &query_cache_min_res_unit, + fix_query_cache_min_res_unit); +static int check_query_cache_type(THD *thd, set_var *var); static sys_var_thd_enum sys_query_cache_type(&vars, "query_cache_type", &SV::query_cache_type, - &query_cache_type_typelib); + &query_cache_type_typelib, NULL, + check_query_cache_type); static sys_var_thd_bool sys_query_cache_wlock_invalidate(&vars, "query_cache_wlock_invalidate", &SV::query_cache_wlock_invalidate); @@ -768,7 +773,7 @@ static sys_var_thd_bit sys_unique_checks(&vars, "unique_checks", 0, OPTION_RELAXED_UNIQUE_CHECKS, 1, sys_var::SESSION_VARIABLE_IN_BINLOG); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) static sys_var_thd_bit sys_profiling(&vars, "profiling", NULL, set_option_bit, ulonglong(OPTION_PROFILING)); @@ -873,9 +878,9 @@ static sys_var_have_plugin sys_have_ndbcluster(&vars, "have_ndbcluster", C_STRIN static sys_var_have_variable sys_have_openssl(&vars, "have_openssl", &have_ssl); static sys_var_have_variable sys_have_ssl(&vars, "have_ssl", &have_ssl); static sys_var_have_plugin sys_have_partition_db(&vars, "have_partitioning", C_STRING_WITH_LEN("partition"), MYSQL_STORAGE_ENGINE_PLUGIN); +static sys_var_have_variable sys_have_profiling(&vars, "have_profiling", &have_profiling); static sys_var_have_variable sys_have_query_cache(&vars, "have_query_cache", &have_query_cache); -static sys_var_have_variable sys_have_community_features(&vars, "have_community_features", &have_community_features); static sys_var_have_variable sys_have_rtree_keys(&vars, "have_rtree_keys", &have_rtree_keys); static sys_var_have_variable sys_have_symlink(&vars, "have_symlink", &have_symlink); /* Global read-only variable describing server license */ @@ -1131,10 +1136,9 @@ static void fix_net_retry_count(THD *thd __attribute__((unused)), {} #endif /* HAVE_REPLICATION */ - +#ifdef HAVE_QUERY_CACHE static void fix_query_cache_size(THD *thd, enum_var_type type) { -#ifdef HAVE_QUERY_CACHE ulong new_cache_size= query_cache.resize(query_cache_size); /* @@ -1148,11 +1152,35 @@ static void fix_query_cache_size(THD *thd, enum_var_type type) query_cache_size, new_cache_size); query_cache_size= new_cache_size; -#endif } -#ifdef HAVE_QUERY_CACHE +/** + Trigger before query_cache_type variable is updated. + @param thd Thread handler + @param var Pointer to the new variable status + + @return Status code + @retval 1 Failure + @retval 0 Success +*/ + +static int check_query_cache_type(THD *thd, set_var *var) +{ + /* + Don't allow changes of the query_cache_type if the query cache + is disabled. + */ + if (query_cache.is_disabled()) + { + my_error(ER_QUERY_CACHE_DISABLED,MYF(0)); + return 1; + } + + return 0; +} + + static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type) { query_cache_min_res_unit= @@ -1824,7 +1852,7 @@ bool sys_var::check_enum(THD *thd, set_var *var, const TYPELIB *enum_names) if (!(res=var->value->val_str(&str)) || ((long) (var->save_result.ulong_value= (ulong) find_type(enum_names, res->ptr(), - res->length(),1)-1)) < 0) + res->length(), FALSE) - 1)) < 0) { if (res) { @@ -2573,9 +2601,20 @@ bool update_sys_var_str_path(THD *thd, sys_var_str *var_str, { MYSQL_QUERY_LOG *file_log; char buff[FN_REFLEN]; - char *res= 0, *old_value=(char *)(var ? var->value->str_value.ptr() : 0); + char *res= 0, *old_value= 0; bool result= 0; - uint str_length= (var ? var->value->str_value.length() : 0); + uint str_length= 0; + + if (var) + { + String str(buff, sizeof(buff), system_charset_info), *newval; + + newval= var->value->val_str(&str); + old_value= newval->c_ptr_safe(); + str_length= strlen(old_value); + } + + switch (log_type) { case QUERY_LOG_SLOW: @@ -3353,7 +3392,7 @@ int mysql_add_sys_var_chain(sys_var *first, struct my_option *long_options) error: for (; first != var; first= first->next) - hash_delete(&system_variable_hash, (uchar*) first); + my_hash_delete(&system_variable_hash, (uchar*) first); return 1; } @@ -3377,7 +3416,7 @@ int mysql_del_sys_var_chain(sys_var *first) /* A write lock should be held on LOCK_system_variables_hash */ for (sys_var *var= first; var; var= var->next) - result|= hash_delete(&system_variable_hash, (uchar*) var); + result|= my_hash_delete(&system_variable_hash, (uchar*) var); return result; } @@ -3414,7 +3453,7 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted) for (i= 0; i < count; i++) { - sys_var *var= (sys_var*) hash_element(&system_variable_hash, i); + sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i); show->name= var->name; show->value= (char*) var; show->type= SHOW_SYS; @@ -3451,8 +3490,8 @@ int set_var_init() for (sys_var *var=vars.first; var; var= var->next, count++) ; - if (hash_init(&system_variable_hash, system_charset_info, count, 0, - 0, (hash_get_key) get_sys_var_length, 0, HASH_UNIQUE)) + if (my_hash_init(&system_variable_hash, system_charset_info, count, 0, + 0, (my_hash_get_key) get_sys_var_length, 0, HASH_UNIQUE)) goto error; vars.last->next= NULL; @@ -3477,7 +3516,7 @@ error: void set_var_free() { - hash_free(&system_variable_hash); + my_hash_free(&system_variable_hash); } @@ -3503,7 +3542,7 @@ sys_var *intern_find_sys_var(const char *str, uint length, bool no_error) This function is only called from the sql_plugin.cc. A lock on LOCK_system_variable_hash should be held */ - var= (sys_var*) hash_search(&system_variable_hash, + var= (sys_var*) my_hash_search(&system_variable_hash, (uchar*) str, length ? length : strlen(str)); if (!(var || no_error)) my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str); @@ -3590,6 +3629,16 @@ bool not_all_support_one_shot(List<set_var_base> *var_list) Functions to handle SET mysql_internal_variable=const_expr *****************************************************************************/ +/** + Verify that the supplied value is correct. + + @param thd Thread handler + + @return status code + @retval -1 Failure + @retval 0 Success +*/ + int set_var::check(THD *thd) { if (var->is_readonly()) @@ -4017,7 +4066,8 @@ ulong fix_sql_mode(ulong sql_mode) if (sql_mode & MODE_TRADITIONAL) sql_mode|= (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES | MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_ERROR_FOR_DIVISION_BY_ZERO | MODE_NO_AUTO_CREATE_USER); + MODE_ERROR_FOR_DIVISION_BY_ZERO | MODE_NO_AUTO_CREATE_USER | + MODE_NO_ENGINE_SUBSTITUTION); return sql_mode; } diff --git a/sql/set_var.h b/sql/set_var.h index a83de6425ae..c08097521d2 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -543,10 +543,16 @@ public: { chain_sys_var(chain); } bool check(THD *thd, set_var *var) { - int ret= 0; - if (check_func) - ret= (*check_func)(thd, var); - return ret ? ret : check_enum(thd, var, enum_names); + /* + check_enum fails if the character representation supplied was wrong + or that the integer value was wrong or missing. + */ + if (check_enum(thd, var, enum_names)) + return TRUE; + else if ((check_func && (*check_func)(thd, var))) + return TRUE; + else + return FALSE; } bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 2b2d60c0657..4260efdeb56 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6240,4 +6240,23 @@ ER_UNKNOWN_LOCALE ER_SLAVE_IGNORE_SERVER_IDS eng "The requested server id %d clashes with the slave startup option --replicate-same-server-id" - +ER_QUERY_CACHE_DISABLED + eng "Query cache is disabled; restart the server with query_cache_type=1 to enable it" +ER_SAME_NAME_PARTITION_FIELD + eng "Duplicate partition field name '%-.192s'" +ER_PARTITION_COLUMN_LIST_ERROR + eng "Inconsistency in usage of column lists for partitioning" +ER_WRONG_TYPE_COLUMN_VALUE_ERROR + eng "Partition column values of incorrect type" +ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR + eng "Too many fields in '%-.192s'" +ER_MAXVALUE_IN_VALUES_IN + eng "Cannot use MAXVALUE as value in VALUES IN" +ER_TOO_MANY_VALUES_ERROR + eng "Cannot have more than one value for this type of %-.64s partitioning" +ER_ROW_SINGLE_PARTITION_FIELD_ERROR + eng "Row expressions in VALUES IN only allowed for multi-field column partitioning" +ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD + eng "Field '%-.192s' is of a not allowed type for this type of partitioning" +ER_PARTITION_FIELDS_TOO_LONG + eng "The total length of the partitioning fields is too large" diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 317087854c1..034f987e0e7 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6241,3 +6241,21 @@ ER_UNKNOWN_LOCALE ER_SLAVE_IGNORE_SERVER_IDS eng "The requested server id %d clashes with the slave startup option --replicate-same-server-id" +ER_SAME_NAME_PARTITION_FIELD + eng "Duplicate partition field name '%-.192s'" +ER_PARTITION_COLUMN_LIST_ERROR + eng "Inconsistency in usage of column lists for partitioning" +ER_WRONG_TYPE_COLUMN_VALUE_ERROR + eng "Partition column values of incorrect type" +ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR + eng "Too many fields in '%-.192s'" +ER_MAXVALUE_IN_VALUES_IN + eng "Cannot use MAXVALUE as value in VALUES IN" +ER_TOO_MANY_VALUES_ERROR + eng "Cannot have more than one value for this type of %-.64s partitioning" +ER_ROW_SINGLE_PARTITION_FIELD_ERROR + eng "Row expressions in VALUES IN only allowed for multi-field column partitioning" +ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD + eng "Field '%-.192s' is of a not allowed type for this type of partitioning" +ER_PARTITION_FIELDS_TOO_LONG + eng "The total length of the partitioning fields is too large" diff --git a/sql/slave.cc b/sql/slave.cc index 899e6d0152c..7700417d61b 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1873,7 +1873,7 @@ bool show_master_info(THD* thd, Master_info* mi) field_list.push_back(new Item_return_int("Master_Server_Id", sizeof(ulong), MYSQL_TYPE_LONG)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); diff --git a/sql/sp.cc b/sql/sp.cc index 5898e553320..9d5efe227dc 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1500,11 +1500,11 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, const LEX_STRING *key, TABLE_LIST *belong_to_view) { - hash_init_opt(&lex->sroutines, system_charset_info, - Query_tables_list::START_SROUTINES_HASH_SIZE, - 0, 0, sp_sroutine_key, 0, 0); + my_hash_init_opt(&lex->sroutines, system_charset_info, + Query_tables_list::START_SROUTINES_HASH_SIZE, + 0, 0, sp_sroutine_key, 0, 0); - if (!hash_search(&lex->sroutines, (uchar *)key->str, key->length)) + if (!my_hash_search(&lex->sroutines, (uchar *)key->str, key->length)) { Sroutine_hash_entry *rn= (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) + @@ -1569,7 +1569,7 @@ void sp_remove_not_own_routines(LEX *lex) but we want to be more future-proof. */ next_rt= not_own_rt->next; - hash_delete(&lex->sroutines, (uchar *)not_own_rt); + my_hash_delete(&lex->sroutines, (uchar *)not_own_rt); } *(Sroutine_hash_entry **)lex->sroutines_list_own_last= NULL; @@ -1598,8 +1598,8 @@ void sp_update_sp_used_routines(HASH *dst, HASH *src) { for (uint i=0 ; i < src->records ; i++) { - Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i); - if (!hash_search(dst, (uchar *)rt->key.str, rt->key.length)) + Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i); + if (!my_hash_search(dst, (uchar *)rt->key.str, rt->key.length)) my_hash_insert(dst, (uchar *)rt); } } @@ -1625,7 +1625,7 @@ sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src, { for (uint i=0 ; i < src->records ; i++) { - Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i); + Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i); (void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view); } } diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 64898915b7e..d9a23d2be4e 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -44,7 +44,8 @@ public: inline sp_head *lookup(char *name, uint namelen) { - return (sp_head *)hash_search(&m_hashtable, (const uchar *)name, namelen); + return (sp_head *) my_hash_search(&m_hashtable, (const uchar *)name, + namelen); } #ifdef NOT_USED @@ -255,15 +256,15 @@ sp_cache::sp_cache() sp_cache::~sp_cache() { - hash_free(&m_hashtable); + my_hash_free(&m_hashtable); } void sp_cache::init() { - hash_init(&m_hashtable, system_charset_info, 0, 0, 0, - hash_get_key_for_sp_head, hash_free_sp_head, 0); + my_hash_init(&m_hashtable, system_charset_info, 0, 0, 0, + hash_get_key_for_sp_head, hash_free_sp_head, 0); version= 0; } @@ -271,5 +272,5 @@ sp_cache::init() void sp_cache::cleanup() { - hash_free(&m_hashtable); + my_hash_free(&m_hashtable); } diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a95b27ef9cd..eb9ee6eeebf 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -14,6 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" +#include "sql_prepare.h" #include "probes_mysql.h" #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation @@ -178,7 +179,6 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_SHOW_RELAYLOG_EVENTS: case SQLCOM_SHOW_CHARSETS: case SQLCOM_SHOW_COLLATIONS: - case SQLCOM_SHOW_COLUMN_TYPES: case SQLCOM_SHOW_CONTRIBUTORS: case SQLCOM_SHOW_CREATE: case SQLCOM_SHOW_CREATE_DB: @@ -532,8 +532,9 @@ sp_head::sp_head() m_backpatch.empty(); m_cont_backpatch.empty(); m_lex.empty(); - hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0); - hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0); + my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0); + my_hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, + 0, 0); m_body_utf8.str= NULL; m_body_utf8.length= 0; @@ -782,8 +783,8 @@ sp_head::destroy() m_thd->lex= lex; } - hash_free(&m_sptabs); - hash_free(&m_sroutines); + my_hash_free(&m_sptabs); + my_hash_free(&m_sroutines); DBUG_VOID_RETURN; } @@ -1208,7 +1209,7 @@ sp_head::execute(THD *thd) */ thd->spcont->callers_arena= &backup_arena; -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) /* Discard the initial part of executing routines. */ thd->profiling.discard_current_query(); #endif @@ -1217,7 +1218,7 @@ sp_head::execute(THD *thd) sp_instr *i; uint hip; // Handler ip -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) /* Treat each "instr" of a routine as discrete unit that could be profiled. Profiling only records information for segments of code that set the @@ -1230,7 +1231,7 @@ sp_head::execute(THD *thd) i = get_instr(ip); // Returns NULL when we're done. if (i == NULL) { -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.discard_current_query(); #endif break; @@ -1313,7 +1314,7 @@ sp_head::execute(THD *thd) } } while (!err_status && !thd->killed && !thd->is_fatal_error); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); thd->profiling.start_new_query("tail end of routine"); #endif @@ -1958,15 +1959,19 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) } } - /* - Okay, got values for all arguments. Close tables that might be used by - arguments evaluation. If arguments evaluation required prelocking mode, + /* + Okay, got values for all arguments. Close tables that might be used by + arguments evaluation. If arguments evaluation required prelocking mode, we'll leave it here. */ if (!thd->in_sub_stmt) { thd->lex->unit.cleanup(); - close_thread_tables(thd); + + thd_proc_info(thd, "closing tables"); + close_thread_tables(thd); + thd_proc_info(thd, 0); + thd->rollback_item_tree_changes(); } @@ -2039,6 +2044,16 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) err_status= TRUE; break; } + + Send_field *out_param_info= new (thd->mem_root) Send_field(); + nctx->get_item(i)->make_field(out_param_info); + out_param_info->db_name= m_db.str; + out_param_info->table_name= m_name.str; + out_param_info->org_table_name= m_name.str; + out_param_info->col_name= spvar->name.str; + out_param_info->org_col_name= spvar->name.str; + + srp->set_out_param_info(out_param_info); } } @@ -2363,7 +2378,8 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) bzero((char*) &tables,sizeof(tables)); tables.db= (char*) "mysql"; tables.table_name= tables.alias= (char*) "proc"; - *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1, TRUE) || + *full_access= (!check_table_access(thd, SELECT_ACL, &tables, FALSE, + 1, TRUE) || (!strcmp(sp->m_definer_user.str, thd->security_ctx->priv_user) && !strcmp(sp->m_definer_host.str, @@ -2446,7 +2462,7 @@ sp_head::show_create_routine(THD *thd, int type) fields.push_back(new Item_empty_string("Database Collation", MY_CS_NAME_SIZE)); - if (protocol->send_fields(&fields, + if (protocol->send_result_set_metadata(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { DBUG_RETURN(TRUE); @@ -2632,8 +2648,8 @@ sp_head::show_routine_code(THD *thd) field_list.push_back(new Item_uint("Pos", 9)); // 1024 is for not to confuse old clients field_list.push_back(new Item_empty_string("Instruction", - max(buffer.length(), 1024))); - if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + max(buffer.length(), 1024))); + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); @@ -2809,7 +2825,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables) Check whenever we have access to tables for this statement and open and lock them before executing instructions core function. */ - if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE) + if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE) || open_and_lock_tables(thd, tables)) result= -1; else @@ -2845,7 +2861,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) query= thd->query; query_length= thd->query_length; -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) /* This s-p instr is profilable and will be captured. */ thd->profiling.set_query_source(m_query.str, m_query.length); #endif @@ -2865,7 +2881,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this); if (thd->stmt_da->is_eof()) - net_end_statement(thd); + thd->protocol->end_statement(); query_cache_end_of_result(thd); @@ -3821,7 +3837,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) for (uint i= 0 ; i < m_sptabs.records ; i++) { - tab= (SP_TABLE *)hash_element(&m_sptabs, i); + tab= (SP_TABLE*) my_hash_element(&m_sptabs, i); tab->query_lock_count= 0; } @@ -3855,8 +3871,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) (and therefore should not be prelocked). Otherwise we will erroneously treat table with same name but with different alias as non-temporary. */ - if ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname, tlen)) || - ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname, + if ((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname, tlen)) || + ((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname, tlen - alen - 1)) && tab->temp)) { @@ -3941,7 +3957,7 @@ sp_head::add_used_tables_to_table_list(THD *thd, { char *tab_buff, *key_buff; TABLE_LIST *table; - SP_TABLE *stab= (SP_TABLE *)hash_element(&m_sptabs, i); + SP_TABLE *stab= (SP_TABLE*) my_hash_element(&m_sptabs, i); if (stab->temp) continue; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index dcda3c038ce..f5553b38f60 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -261,8 +261,8 @@ my_bool acl_init(bool dont_read_acl_tables) DBUG_ENTER("acl_init"); acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0, - (hash_get_key) acl_entry_get_key, - (hash_free_key) free, + (my_hash_get_key) acl_entry_get_key, + (my_hash_free_key) free, lower_case_file_system ? system_charset_info : &my_charset_bin); if (dont_read_acl_tables) @@ -638,7 +638,7 @@ void acl_free(bool end) delete_dynamic(&acl_users); delete_dynamic(&acl_dbs); delete_dynamic(&acl_wild_hosts); - hash_free(&acl_check_hosts); + my_hash_free(&acl_check_hosts); if (!end) acl_cache->clear(1); /* purecov: inspected */ else @@ -714,7 +714,7 @@ my_bool acl_reload(THD *thd) old_acl_dbs=acl_dbs; old_mem=mem; delete_dynamic(&acl_wild_hosts); - hash_free(&acl_check_hosts); + my_hash_free(&acl_check_hosts); if ((return_val= acl_load(thd, tables))) { // Error. Revert to old list @@ -1429,8 +1429,8 @@ static void init_check_host(void) DBUG_ENTER("init_check_host"); VOID(my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip), acl_users.elements,1)); - VOID(hash_init(&acl_check_hosts,system_charset_info,acl_users.elements,0,0, - (hash_get_key) check_get_key,0,0)); + VOID(my_hash_init(&acl_check_hosts,system_charset_info,acl_users.elements,0,0, + (my_hash_get_key) check_get_key,0,0)); if (!allow_all_hosts) { for (uint i=0 ; i < acl_users.elements ; i++) @@ -1452,8 +1452,9 @@ static void init_check_host(void) if (j == acl_wild_hosts.elements) // If new (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host); } - else if (!hash_search(&acl_check_hosts,(uchar*) acl_user->host.hostname, - strlen(acl_user->host.hostname))) + else if (!my_hash_search(&acl_check_hosts,(uchar*) + acl_user->host.hostname, + strlen(acl_user->host.hostname))) { if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user)) { // End of memory @@ -1480,7 +1481,7 @@ static void init_check_host(void) void rebuild_check_host(void) { delete_dynamic(&acl_wild_hosts); - hash_free(&acl_check_hosts); + my_hash_free(&acl_check_hosts); init_check_host(); } @@ -1493,8 +1494,8 @@ bool acl_check_host(const char *host, const char *ip) return 0; VOID(pthread_mutex_lock(&acl_cache->lock)); - if ((host && hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) || - (ip && hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip)))) + if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) || + (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip)))) { VOID(pthread_mutex_unlock(&acl_cache->lock)); return 0; // Found host @@ -1879,7 +1880,7 @@ static bool test_if_create_new_users(THD *thd) sctx->priv_user, tl.db, 0); if (!(db_access & INSERT_ACL)) { - if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1)) + if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE)) create_new_users=0; } } @@ -2298,8 +2299,8 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, const char *t, ulong p, ulong c) :GRANT_NAME(h,d,u,t,p), cols(c) { - (void) hash_init2(&hash_columns,4,system_charset_info, - 0,0,0, (hash_get_key) get_key_column,0,0); + (void) my_hash_init2(&hash_columns,4,system_charset_info, + 0,0,0, (my_hash_get_key) get_key_column,0,0); } @@ -2339,15 +2340,15 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) if (!db || !tname) { /* Wrong table row; Ignore it */ - hash_clear(&hash_columns); /* allow for destruction */ + my_hash_clear(&hash_columns); /* allow for destruction */ cols= 0; return; } cols= (ulong) form->field[7]->val_int(); cols = fix_rights_for_column(cols); - (void) hash_init2(&hash_columns,4,system_charset_info, - 0,0,0, (hash_get_key) get_key_column,0,0); + (void) my_hash_init2(&hash_columns,4,system_charset_info, + 0,0,0, (my_hash_get_key) get_key_column,0,0); if (cols) { uint key_prefix_len; @@ -2399,7 +2400,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) GRANT_TABLE::~GRANT_TABLE() { - hash_free(&hash_columns); + my_hash_free(&hash_columns); } @@ -2413,7 +2414,7 @@ static uchar* get_grant_table(GRANT_NAME *buff, size_t *length, void free_grant_table(GRANT_TABLE *grant_table) { - hash_free(&grant_table->hash_columns); + my_hash_free(&grant_table->hash_columns); } @@ -2431,11 +2432,11 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, HASH_SEARCH_STATE state; len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1; - for (grant_name= (GRANT_NAME*) hash_first(name_hash, (uchar*) helping, - len, &state); + for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping, + len, &state); grant_name ; - grant_name= (GRANT_NAME*) hash_next(name_hash,(uchar*) helping, - len, &state)) + grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping, + len, &state)) { if (exact) { @@ -2479,7 +2480,8 @@ table_hash_search(const char *host, const char *ip, const char *db, inline GRANT_COLUMN * column_hash_search(GRANT_TABLE *t, const char *cname, uint length) { - return (GRANT_COLUMN*) hash_search(&t->hash_columns, (uchar*) cname,length); + return (GRANT_COLUMN*) my_hash_search(&t->hash_columns, + (uchar*) cname, length); } @@ -2659,7 +2661,7 @@ static int replace_column_table(GRANT_TABLE *g_t, goto end; /* purecov: deadcode */ } if (grant_column) - hash_delete(&g_t->hash_columns,(uchar*) grant_column); + my_hash_delete(&g_t->hash_columns,(uchar*) grant_column); } } } while (!table->file->index_next(table->record[0]) && @@ -2785,7 +2787,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, } else { - hash_delete(&column_priv_hash,(uchar*) grant_table); + my_hash_delete(&column_priv_hash,(uchar*) grant_table); } DBUG_RETURN(0); @@ -2906,7 +2908,8 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, } else { - hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*) grant_name); + my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*) + grant_name); } DBUG_RETURN(0); @@ -3147,8 +3150,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, column_priv= 0; for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++) { - grant_column= (GRANT_COLUMN*) hash_element(&grant_table->hash_columns, - idx); + grant_column= (GRANT_COLUMN*) + my_hash_element(&grant_table->hash_columns, idx); grant_column->rights&= ~rights; // Fix other columns column_priv|= grant_column->rights; } @@ -3479,9 +3482,9 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, void grant_free(void) { DBUG_ENTER("grant_free"); - hash_free(&column_priv_hash); - hash_free(&proc_priv_hash); - hash_free(&func_priv_hash); + my_hash_free(&column_priv_hash); + my_hash_free(&proc_priv_hash); + my_hash_free(&func_priv_hash); free_root(&memex,MYF(0)); DBUG_VOID_RETURN; } @@ -3538,12 +3541,12 @@ static my_bool grant_load_procs_priv(TABLE *p_table) MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); DBUG_ENTER("grant_load_procs_priv"); - (void) hash_init(&proc_priv_hash,system_charset_info, - 0,0,0, (hash_get_key) get_grant_table, - 0,0); - (void) hash_init(&func_priv_hash,system_charset_info, - 0,0,0, (hash_get_key) get_grant_table, - 0,0); + (void) my_hash_init(&proc_priv_hash,system_charset_info, + 0,0,0, (my_hash_get_key) get_grant_table, + 0,0); + (void) my_hash_init(&func_priv_hash,system_charset_info, + 0,0,0, (my_hash_get_key) get_grant_table, + 0,0); p_table->file->ha_index_init(0, 1); p_table->use_all_columns(); @@ -3639,9 +3642,9 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables) thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; - (void) hash_init(&column_priv_hash,system_charset_info, - 0,0,0, (hash_get_key) get_grant_table, - (hash_free_key) free_grant_table,0); + (void) my_hash_init(&column_priv_hash,system_charset_info, + 0,0,0, (my_hash_get_key) get_grant_table, + (my_hash_free_key) free_grant_table,0); t_table = tables[0].table; c_table = tables[1].table; @@ -3744,8 +3747,8 @@ static my_bool grant_reload_procs_priv(THD *thd) } else { - hash_free(&old_proc_priv_hash); - hash_free(&old_func_priv_hash); + my_hash_free(&old_proc_priv_hash); + my_hash_free(&old_func_priv_hash); } rw_unlock(&LOCK_grant); @@ -3814,7 +3817,7 @@ my_bool grant_reload(THD *thd) } else { - hash_free(&old_column_priv_hash); + my_hash_free(&old_column_priv_hash); free_root(&old_mem,MYF(0)); } rw_unlock(&LOCK_grant); @@ -3835,40 +3838,52 @@ end: DBUG_RETURN(return_val); } -/**************************************************************************** - Check table level grants - SYNOPSIS - bool check_grant() - thd Thread handler - want_access Bits of privileges user needs to have - tables List of tables to check. The user should have 'want_access' - to all tables in list. - show_table <> 0 if we are in show table. In this case it's enough to have - any privilege for the table - number Check at most this number of tables. - no_errors If 0 then we write an error. The error is sent directly to - the client +/** + @brief Check table level grants - RETURN - 0 ok - 1 Error: User did not have the requested privileges + @param thd Thread handler + @param want_access Bits of privileges user needs to have. + @param tables List of tables to check. The user should have + 'want_access' to all tables in list. + @param any_combination_will_do TRUE if it's enough to have any privilege for + any combination of the table columns. + @param number Check at most this number of tables. + @param no_errors TRUE if no error should be sent directly to the client. - NOTE - This functions assumes that either number of tables to be inspected + If table->grant.want_privilege != 0 then the requested privileges where + in the set of COL_ACLS but access was not granted on the table level. As + a consequence an extra check of column privileges is required. + + Specifically if this function returns FALSE the user has some kind of + privilege on a combination of columns in each table. + + This function is usually preceeded by check_access which establish the + User-, Db- and Host access rights. + + @see check_access + @see check_table_access + + @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). -****************************************************************************/ + + @return Access status + @retval FALSE Access granted; But column privileges might need to be + checked. + @retval TRUE The user did not have the requested privileges on any of the + tables. + +*/ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, - uint show_table, uint number, bool no_errors) + bool any_combination_will_do, uint number, bool no_errors) { TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table(); Security_context *sctx= thd->security_ctx; uint i; - ulong orig_want_access= want_access; DBUG_ENTER("check_grant"); DBUG_ASSERT(number > 0); @@ -3886,7 +3901,10 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, i < number && table != first_not_own_table; table= table->next_global, i++) { - /* Remove SHOW_VIEW_ACL, because it will be checked during making view */ + /* + Save a copy of the privileges without the SHOW_VIEW_ACL attribute. + It will be checked during making view. + */ table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); } @@ -3899,7 +3917,6 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, sctx = test(table->security_ctx) ? table->security_ctx : thd->security_ctx; - want_access= orig_want_access; want_access&= ~sctx->master_access; if (!want_access) continue; // ok @@ -3929,8 +3946,13 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, want_access &= ~table->grant.privilege; goto err; // No grants } - if (show_table) - continue; // We have some priv on this + + /* + For SHOW COLUMNS, SHOW INDEX it is enough to have some + privileges on any column combination on the table. + */ + if (any_combination_will_do) + continue; table->grant.grant_table=grant_table; // Remember for column test table->grant.version=grant_version; @@ -3948,7 +3970,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, } } rw_unlock(&LOCK_grant); - DBUG_RETURN(0); + DBUG_RETURN(FALSE); err: rw_unlock(&LOCK_grant); @@ -3962,7 +3984,97 @@ err: sctx->host_or_ip, table ? table->get_table_name() : "unknown"); } - DBUG_RETURN(1); + DBUG_RETURN(TRUE); +} + + +/** + Check if all tables in the table list has any of the requested table level + privileges matching the current user. + + @param thd A pointer to the thread context. + @param required_access Set of privileges to compare against. + @param tables[in,out] A list of tables to be checked. + + @note If the table grant hash contains any grant table, this table will be + attached to the corresponding TABLE_LIST object in 'tables'. + + @return + @retval TRUE There is a privilege on the table level granted to the + current user. + @retval FALSE There are no privileges on the table level granted to the + current user. +*/ + +bool has_any_table_level_privileges(THD *thd, ulong required_access, + TABLE_LIST *tables) +{ + + Security_context *sctx; + GRANT_TABLE *grant_table; + TABLE_LIST *table; + + /* For each table in tables */ + for (table= tables; table; table= table->next_global) + { + /* + If this table is a VIEW, then it will supply its own security context. + This is because VIEWs can have a DEFINER or an INVOKER security role. + */ + sctx= table->security_ctx ? table->security_ctx : thd->security_ctx; + + /* + Get privileges from table_priv and column_priv tables by searching + the cache. + */ + rw_rdlock(&LOCK_grant); + grant_table= table_hash_search(sctx->host, sctx->ip, + table->db, sctx->priv_user, + table->table_name,0); + rw_unlock(&LOCK_grant); + + /* Stop if there are no grants for the current user */ + if (!grant_table) + return FALSE; + + /* + Save a pointer to the found grant_table in the table object. + This pointer can later be used to verify other access requirements + without having to look up the grant table in the hash. + */ + table->grant.grant_table= grant_table; + table->grant.version= grant_version; + table->grant.privilege|= grant_table->privs; + /* + Save all privileges which might be subject to column privileges + but not which aren't yet granted by table level ACLs. + This is can later be used for column privilege checks. + */ + table->grant.want_privilege= ((required_access & COL_ACLS) + & ~table->grant.privilege); + + /* + If the requested privileges share any intersection with the current + table privileges we have found at least one common privilege on the + table level. + */ + if (grant_table->privs & required_access) + continue; /* Check next table */ + + /* + There are no table level privileges which satisfies any of the + requested privileges. There might still be column privileges which + does though. + */ + return FALSE; + } + + /* + All tables in TABLE_LIST satisfy the requirement of having any + privilege on the table level. + */ + + return TRUE; } @@ -4209,7 +4321,7 @@ static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash) for (uint idx= 0; idx < hash->records; ++idx) { - GRANT_NAME *item= (GRANT_NAME*) hash_element(hash, idx); + GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx); if (strcmp(item->user, sctx->priv_user) == 0 && strcmp(item->db, db) == 0 && @@ -4242,8 +4354,9 @@ bool check_grant_db(THD *thd,const char *db) for (uint idx=0 ; idx < column_priv_hash.records ; idx++) { - GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, - idx); + GRANT_TABLE *grant_table= (GRANT_TABLE*) + my_hash_element(&column_priv_hash, + idx); if (len < grant_table->key_length && !memcmp(grant_table->hash_key,helping,len) && compare_hostname(&grant_table->host, sctx->host, sctx->ip)) @@ -4466,13 +4579,13 @@ static const char *command_array[]= "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT", "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE", - "CREATE USER", "EVENT", "TRIGGER" + "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE" }; static uint command_lengths[]= { 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9, - 14, 13, 11, 5, 7 + 14, 13, 11, 5, 7, 17 }; @@ -4527,7 +4640,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) strxmov(buff,"Grants for ",lex_user->user.str,"@", lex_user->host.str,NullS); field_list.push_back(field); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -4715,8 +4828,8 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) for (index=0 ; index < column_priv_hash.records ; index++) { const char *user, *host; - GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, - index); + GRANT_TABLE *grant_table= (GRANT_TABLE*) + my_hash_element(&column_priv_hash, index); if (!(user=grant_table->user)) user= ""; @@ -4769,7 +4882,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) col_index++) { GRANT_COLUMN *grant_column = (GRANT_COLUMN*) - hash_element(&grant_table->hash_columns,col_index); + my_hash_element(&grant_table->hash_columns,col_index); if (grant_column->rights & j) { if (!found_col) @@ -4859,7 +4972,7 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, for (index=0 ; index < hash->records ; index++) { const char *user, *host; - GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index); + GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index); if (!(user=grant_proc->user)) user= ""; @@ -5372,13 +5485,13 @@ static int handle_grant_struct(uint struct_no, bool drop, break; case 2: - grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx); + grant_name= (GRANT_NAME*) my_hash_element(&column_priv_hash, idx); user= grant_name->user; host= grant_name->host.hostname; break; case 3: - grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx); + grant_name= (GRANT_NAME*) my_hash_element(&proc_priv_hash, idx); user= grant_name->user; host= grant_name->host.hostname; break; @@ -5411,11 +5524,11 @@ static int handle_grant_struct(uint struct_no, bool drop, break; case 2: - hash_delete(&column_priv_hash, (uchar*) grant_name); + my_hash_delete(&column_priv_hash, (uchar*) grant_name); break; case 3: - hash_delete(&proc_priv_hash, (uchar*) grant_name); + my_hash_delete(&proc_priv_hash, (uchar*) grant_name); break; } elements--; @@ -5929,8 +6042,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; ) { const char *user,*host; - GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash, - counter); + GRANT_TABLE *grant_table= + (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter); if (!(user=grant_table->user)) user= ""; if (!(host=grant_table->host.hostname)) @@ -5976,7 +6089,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) for (counter= 0, revoked= 0 ; counter < hash->records ; ) { const char *user,*host; - GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter); + GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); if (!(user=grant_proc->user)) user= ""; if (!(host=grant_proc->host.hostname)) @@ -6124,7 +6237,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, { for (counter= 0, revoked= 0 ; counter < hash->records ; ) { - GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter); + GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); if (!my_strcasecmp(system_charset_info, grant_proc->db, sp_db) && !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name)) { @@ -6334,6 +6447,7 @@ static bool update_schema_privilege(THD *thd, TABLE *table, char *buff, CHARSET_INFO *cs= system_charset_info; restore_record(table, s->default_values); table->field[0]->store(buff, (uint) strlen(buff), cs); + table->field[1]->store(STRING_WITH_LEN("def"), cs); if (db) table->field[i++]->store(db, (uint) strlen(db), cs); if (t_name) @@ -6514,7 +6628,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) for (index=0 ; index < column_priv_hash.records ; index++) { const char *user, *host, *is_grantable= "YES"; - GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, + GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, index); if (!(user=grant_table->user)) user= ""; @@ -6597,7 +6711,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) for (index=0 ; index < column_priv_hash.records ; index++) { const char *user, *host, *is_grantable= "YES"; - GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, + GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, index); if (!(user=grant_table->user)) user= ""; @@ -6632,7 +6746,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) col_index++) { GRANT_COLUMN *grant_column = (GRANT_COLUMN*) - hash_element(&grant_table->hash_columns,col_index); + my_hash_element(&grant_table->hash_columns,col_index); if ((grant_column->rights & j) && (table_access & j)) { if (update_schema_privilege(thd, table, buff, grant_table->db, diff --git a/sql/sql_acl.h b/sql/sql_acl.h index c0622bd747c..5c1f34b0b34 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -46,6 +46,7 @@ #define CREATE_USER_ACL (1L << 25) #define EVENT_ACL (1L << 26) #define TRIGGER_ACL (1L << 27) +#define CREATE_TABLESPACE_ACL (1L << 28) /* don't forget to update 1. static struct show_privileges_st sys_privileges[] @@ -54,7 +55,6 @@ 4. acl_init() or whatever - to define behaviour for old privilege tables 5. sql_yacc.yy - for GRANT/REVOKE to work */ -#define EXTRA_ACL (1L << 29) #define NO_ACCESS (1L << 30) #define DB_ACLS \ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ @@ -82,11 +82,17 @@ REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \ CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \ EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \ - ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL) + ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL | \ + CREATE_TABLESPACE_ACL) #define DEFAULT_CREATE_PROC_ACLS \ (ALTER_PROC_ACL | EXECUTE_ACL) +#define SHOW_CREATE_TABLE_ACLS \ +(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | \ + CREATE_ACL | DROP_ACL | ALTER_ACL | INDEX_ACL | \ + TRIGGER_ACL | REFERENCES_ACL | GRANT_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL) + /* Defines to change the above bits to how things are stored in tables This is needed as the 'host' and 'db' table is missing a few privileges @@ -241,7 +247,7 @@ my_bool grant_init(); void grant_free(void); my_bool grant_reload(THD *thd); bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, - uint show_command, uint number, bool dont_print_error); + bool any_combination_will_do, uint number, bool no_errors); bool check_grant_column (THD *thd, GRANT_INFO *grant, const char *db_name, const char *table_name, const char *name, uint length, Security_context *sctx); @@ -272,8 +278,12 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, bool check_routine_level_acl(THD *thd, const char *db, const char *name, bool is_proc); bool is_acl_user(const char *host, const char *user); +bool has_any_table_level_privileges(THD *thd, ulong required_access, + TABLE_LIST *tables); + #ifdef NO_EMBEDDED_ACCESS_CHECKS #define check_grant(A,B,C,D,E,F) 0 #define check_grant_db(A,B) 0 +#define has_any_table_level_privileges(A,B,C) 0 #endif #endif /* SQL_ACL_INCLUDED */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 087ce469d65..072c7c2aede 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -22,6 +22,7 @@ #include "sp_head.h" #include "sp.h" #include "sql_trigger.h" +#include "sql_prepare.h" #include <m_ctype.h> #include <my_dir.h> #include <hash.h> @@ -127,9 +128,9 @@ extern "C" uchar *table_cache_key(const uchar *record, size_t *length, bool table_cache_init(void) { - return hash_init(&open_cache, &my_charset_bin, table_cache_size+16, - 0, 0, table_cache_key, - (hash_free_key) free_cache_entry, 0) != 0; + return my_hash_init(&open_cache, &my_charset_bin, table_cache_size+16, + 0, 0, table_cache_key, + (my_hash_free_key) free_cache_entry, 0) != 0; } void table_cache_free(void) @@ -139,7 +140,7 @@ void table_cache_free(void) { close_cached_tables(NULL, NULL, FALSE, FALSE, FALSE); if (!open_cache.records) // Safety first - hash_free(&open_cache); + my_hash_free(&open_cache); } DBUG_VOID_RETURN; } @@ -174,7 +175,7 @@ static void check_unused(void) } for (idx=0 ; idx < open_cache.records ; idx++) { - TABLE *entry=(TABLE*) hash_element(&open_cache,idx); + TABLE *entry=(TABLE*) my_hash_element(&open_cache,idx); if (!entry->in_use) count--; if (entry->file) @@ -287,9 +288,9 @@ bool table_def_init(void) oldest_unused_share= &end_of_unused_share; end_of_unused_share.prev= &oldest_unused_share; - return hash_init(&table_def_cache, &my_charset_bin, table_def_size, - 0, 0, table_def_key, - (hash_free_key) table_def_free_entry, 0) != 0; + return my_hash_init(&table_def_cache, &my_charset_bin, table_def_size, + 0, 0, table_def_key, + (my_hash_free_key) table_def_free_entry, 0) != 0; } @@ -300,7 +301,7 @@ void table_def_free(void) { table_def_inited= 0; pthread_mutex_destroy(&LOCK_table_share); - hash_free(&table_def_cache); + my_hash_free(&table_def_cache); } DBUG_VOID_RETURN; } @@ -346,8 +347,8 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, *error= 0; /* Read table definition from cache */ - if ((share= (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key, - key_length))) + if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, + key_length))) goto found; if (!(share= alloc_table_share(table_list, key, key_length))) @@ -384,7 +385,7 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, if (open_table_def(thd, share, db_flags)) { *error= share->error; - (void) hash_delete(&table_def_cache, (uchar*) share); + (void) my_hash_delete(&table_def_cache, (uchar*) share); DBUG_RETURN(0); } share->ref_count++; // Mark in use @@ -436,7 +437,7 @@ found: oldest_unused_share->next) { pthread_mutex_lock(&oldest_unused_share->mutex); - VOID(hash_delete(&table_def_cache, (uchar*) oldest_unused_share)); + VOID(my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share)); } DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u", @@ -591,7 +592,7 @@ void release_table_share(TABLE_SHARE *share, enum release_type type) if (to_be_deleted) { DBUG_PRINT("info", ("Deleting share")); - hash_delete(&table_def_cache, (uchar*) share); + my_hash_delete(&table_def_cache, (uchar*) share); DBUG_VOID_RETURN; } pthread_mutex_unlock(&share->mutex); @@ -622,7 +623,8 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name) table_list.db= (char*) db; table_list.table_name= (char*) table_name; key_length= create_table_def_key((THD*) 0, key, &table_list, 0); - return (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key, key_length); + return (TABLE_SHARE*) my_hash_search(&table_def_cache, + (uchar*) key, key_length); } @@ -719,7 +721,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) for (uint idx=0 ; result == 0 && idx < open_cache.records; idx++) { OPEN_TABLE_LIST *table; - TABLE *entry=(TABLE*) hash_element(&open_cache,idx); + TABLE *entry=(TABLE*) my_hash_element(&open_cache,idx); TABLE_SHARE *share= entry->s; if (db && my_strcasecmp(system_charset_info, db, share->db.str)) @@ -732,7 +734,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) table_list.table_name= share->table_name.str; table_list.grant.privilege=0; - if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list, 1, TRUE)) + if (check_table_access(thd,SELECT_ACL,&table_list, TRUE, 1, TRUE)) continue; /* need to check if we haven't already listed it */ for (table= open_list ; table ; table=table->next) @@ -868,17 +870,17 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, while (unused_tables) { #ifdef EXTRA_DEBUG - if (hash_delete(&open_cache,(uchar*) unused_tables)) + if (my_hash_delete(&open_cache,(uchar*) unused_tables)) printf("Warning: Couldn't delete open table from hash\n"); #else - VOID(hash_delete(&open_cache,(uchar*) unused_tables)); + VOID(my_hash_delete(&open_cache,(uchar*) unused_tables)); #endif } /* Free table shares */ while (oldest_unused_share->next) { pthread_mutex_lock(&oldest_unused_share->mutex); - VOID(hash_delete(&table_def_cache, (uchar*) oldest_unused_share)); + VOID(my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share)); } DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu", refresh_version)); @@ -923,7 +925,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, */ for (uint idx=0 ; idx < open_cache.records ; idx++) { - TABLE *table=(TABLE*) hash_element(&open_cache,idx); + TABLE *table=(TABLE*) my_hash_element(&open_cache,idx); if (table->in_use) table->in_use->some_tables_deleted= 1; } @@ -968,7 +970,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, found=0; for (uint idx=0 ; idx < open_cache.records ; idx++) { - TABLE *table=(TABLE*) hash_element(&open_cache,idx); + TABLE *table=(TABLE*) my_hash_element(&open_cache,idx); /* Avoid a self-deadlock. */ if (table->in_use == thd) continue; @@ -1050,7 +1052,7 @@ bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh, for (idx= 0; idx < table_def_cache.records; idx++) { - TABLE_SHARE *share= (TABLE_SHARE *) hash_element(&table_def_cache, idx); + TABLE_SHARE *share= (TABLE_SHARE *) my_hash_element(&table_def_cache, idx); /* Ignore if table is not open or does not have a connect_string */ if (!share->connect_string.length || !share->ref_count) @@ -1206,7 +1208,7 @@ static void close_open_tables(THD *thd) /* Free tables to hold down open files */ while (open_cache.records > table_cache_size && unused_tables) - VOID(hash_delete(&open_cache,(uchar*) unused_tables)); /* purecov: tested */ + VOID(my_hash_delete(&open_cache,(uchar*) unused_tables)); /* purecov: tested */ check_unused(); if (found_old_table) { @@ -1392,7 +1394,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) if (table->needs_reopen_or_name_lock() || thd->version != refresh_version || !table->db_stat) { - VOID(hash_delete(&open_cache,(uchar*) table)); + VOID(my_hash_delete(&open_cache,(uchar*) table)); found_old_table=1; } else @@ -2095,7 +2097,7 @@ void unlink_open_table(THD *thd, TABLE *find, bool unlock) /* Remove table from open_tables list. */ *prev= list->next; /* Close table. */ - VOID(hash_delete(&open_cache,(uchar*) list)); // Close table + VOID(my_hash_delete(&open_cache,(uchar*) list)); // Close table } else { @@ -2401,7 +2403,7 @@ bool lock_table_name_if_not_cached(THD *thd, const char *db, key_length= (uint)(strmov(strmov(key, db) + 1, table_name) - key) + 1; VOID(pthread_mutex_lock(&LOCK_open)); - if (hash_search(&open_cache, (uchar *)key, key_length)) + if (my_hash_search(&open_cache, (uchar *)key, key_length)) { VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_PRINT("info", ("Table is cached, name-lock is not obtained")); @@ -2756,11 +2758,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, an implicit "pending locks queue" - see wait_for_locked_table_names for details. */ - for (table= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length, - &state); + for (table= (TABLE*) my_hash_first(&open_cache, (uchar*) key, key_length, + &state); table && table->in_use ; - table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length, - &state)) + table= (TABLE*) my_hash_next(&open_cache, (uchar*) key, key_length, + &state)) { DBUG_PRINT("tcache", ("in_use table: '%s'.'%s' 0x%lx", table->s->db.str, table->s->table_name.str, (long) table)); @@ -2872,7 +2874,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, DBUG_PRINT("tcache", ("opening new table")); /* Free cache if too big */ while (open_cache.records > table_cache_size && unused_tables) - VOID(hash_delete(&open_cache,(uchar*) unused_tables)); /* purecov: tested */ + VOID(my_hash_delete(&open_cache,(uchar*) unused_tables)); /* purecov: tested */ if (table_list->create) { @@ -2930,7 +2932,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, Set 1 as a flag here */ if (error < 0) - table_list->view= (st_lex*)1; + table_list->view= (LEX*)1; my_free((uchar*)table, MYF(0)); VOID(pthread_mutex_unlock(&LOCK_open)); @@ -3330,7 +3332,7 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) */ if (table->child_l || table->parent) detach_merge_children(table, TRUE); - VOID(hash_delete(&open_cache,(uchar*) table)); + VOID(my_hash_delete(&open_cache,(uchar*) table)); error=1; } else @@ -3359,7 +3361,7 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) { while (err_tables) { - VOID(hash_delete(&open_cache, (uchar*) err_tables)); + VOID(my_hash_delete(&open_cache, (uchar*) err_tables)); err_tables= err_tables->next; } } @@ -3511,11 +3513,11 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) DBUG_PRINT("loop", ("table_name: %s", table->alias)); HASH_SEARCH_STATE state; - for (TABLE *search= (TABLE*) hash_first(&open_cache, (uchar*) key, - key_length, &state); + for (TABLE *search= (TABLE*) my_hash_first(&open_cache, (uchar*) key, + key_length, &state); search ; - search= (TABLE*) hash_next(&open_cache, (uchar*) key, - key_length, &state)) + search= (TABLE*) my_hash_next(&open_cache, (uchar*) key, + key_length, &state)) { DBUG_PRINT("info", ("share: 0x%lx " "open_placeholder: %d locked_by_name: %d " @@ -3641,7 +3643,7 @@ TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name) else { /* We already have a name lock, remove copy */ - VOID(hash_delete(&open_cache,(uchar*) table)); + VOID(my_hash_delete(&open_cache,(uchar*) table)); } } else @@ -4520,9 +4522,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ for (tables= *start; tables ;tables= tables->next_global) { - DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx", - tables->db, tables->table_name, (long) tables)); - safe_to_ignore_table= FALSE; /* @@ -4559,6 +4558,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) } DBUG_RETURN(-1); } + DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx", + tables->db, tables->table_name, (long) tables)); (*counter)++; /* @@ -4615,7 +4616,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) Let us free memory used by 'sroutines' hash here since we never call destructor for this LEX. */ - hash_free(&tables->view->sroutines); + my_hash_free(&tables->view->sroutines); goto process_view_routines; } @@ -5883,8 +5884,8 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, field_ptr= table->field + cached_field_index; else if (table->s->name_hash.records) { - field_ptr= (Field**) hash_search(&table->s->name_hash, (uchar*) name, - length); + field_ptr= (Field**) my_hash_search(&table->s->name_hash, (uchar*) name, + length); if (field_ptr) { /* @@ -6011,7 +6012,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, table_name && table_name[0] && (my_strcasecmp(table_alias_charset, table_list->alias, table_name) || (db_name && db_name[0] && table_list->db && table_list->db[0] && - strcmp(db_name, table_list->db)))) + (table_list->schema_table ? + my_strcasecmp(system_charset_info, db_name, table_list->db) : + strcmp(db_name, table_list->db))))) DBUG_RETURN(0); *actual_table= NULL; @@ -6130,8 +6133,8 @@ Field *find_field_in_table_sef(TABLE *table, const char *name) Field **field_ptr; if (table->s->name_hash.records) { - field_ptr= (Field**)hash_search(&table->s->name_hash,(uchar*) name, - strlen(name)); + field_ptr= (Field**)my_hash_search(&table->s->name_hash,(uchar*) name, + strlen(name)); if (field_ptr) { /* @@ -8373,7 +8376,7 @@ void remove_db_from_cache(const char *db) { for (uint idx=0 ; idx < open_cache.records ; idx++) { - TABLE *table=(TABLE*) hash_element(&open_cache,idx); + TABLE *table=(TABLE*) my_hash_element(&open_cache,idx); if (!strcmp(table->s->db.str, db)) { table->s->version= 0L; /* Free when thread is ready */ @@ -8382,7 +8385,7 @@ void remove_db_from_cache(const char *db) } } while (unused_tables && !unused_tables->s->version) - VOID(hash_delete(&open_cache,(uchar*) unused_tables)); + VOID(my_hash_delete(&open_cache,(uchar*) unused_tables)); } @@ -8398,7 +8401,7 @@ void flush_tables() { (void) pthread_mutex_lock(&LOCK_open); while (unused_tables) - hash_delete(&open_cache,(uchar*) unused_tables); + my_hash_delete(&open_cache,(uchar*) unused_tables); (void) pthread_mutex_unlock(&LOCK_open); } @@ -8435,11 +8438,11 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, HASH_SEARCH_STATE state; result= signalled= 0; - for (table= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length, - &state); + for (table= (TABLE*) my_hash_first(&open_cache, (uchar*) key, key_length, + &state); table; - table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length, - &state)) + table= (TABLE*) my_hash_next(&open_cache, (uchar*) key, key_length, + &state)) { THD *in_use; DBUG_PRINT("tcache", ("found table: '%s'.'%s' 0x%lx", table->s->db.str, @@ -8506,12 +8509,12 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, } } while (unused_tables && !unused_tables->s->version) - VOID(hash_delete(&open_cache,(uchar*) unused_tables)); + VOID(my_hash_delete(&open_cache,(uchar*) unused_tables)); DBUG_PRINT("info", ("Removing table from table_def_cache")); /* Remove table from table definition cache if it's not in use */ - if ((share= (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key, - key_length))) + if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, + key_length))) { DBUG_PRINT("info", ("share version: %lu ref_count: %u", share->version, share->ref_count)); @@ -8519,7 +8522,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, if (share->ref_count == 0) { pthread_mutex_lock(&share->mutex); - VOID(hash_delete(&table_def_cache, (uchar*) share)); + VOID(my_hash_delete(&table_def_cache, (uchar*) share)); } } @@ -8767,11 +8770,11 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table key_length=(uint) (strmov(strmov(key,lpt->db)+1,lpt->table_name)-key)+1; VOID(pthread_mutex_lock(&LOCK_open)); HASH_SEARCH_STATE state; - for (table= (TABLE*) hash_first(&open_cache,(uchar*) key,key_length, - &state) ; + for (table= (TABLE*) my_hash_first(&open_cache,(uchar*) key,key_length, + &state) ; table; - table= (TABLE*) hash_next(&open_cache,(uchar*) key,key_length, - &state)) + table= (TABLE*) my_hash_next(&open_cache,(uchar*) key,key_length, + &state)) { THD *in_use= table->in_use; table->s->version= 0L; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 049c541d284..6f550f92987 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -286,6 +286,7 @@ functions: if (and only if) this query has a registered result set writer (thd->net.query_cache_query). 4. Query_cache::invalidate + Query_cache::invalidate_locked_for_write - Called from various places to invalidate query cache based on data- base, table and myisam file name. During an on going invalidation the query cache is temporarily disabled. @@ -789,19 +790,20 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length, Note on double-check locking (DCL) usage. Below, in query_cache_insert(), query_cache_abort() and - query_cache_end_of_result() we use what is called double-check - locking (DCL) for NET::query_cache_query. I.e. we test it first - without a lock, and, if positive, test again under the lock. + Query_cache::end_of_result() we use what is called double-check + locking (DCL) for Query_cache_tls::first_query_block. + I.e. we test it first without a lock, and, if positive, test again + under the lock. - This means that if we see 'NET::query_cache_query == 0' without a + This means that if we see 'first_query_block == 0' without a lock we will skip the operation. But this is safe here: when we started to cache a query, we called Query_cache::store_query(), and - NET::query_cache_query was set to non-zero in this thread (and the + 'first_query_block' was set to non-zero in this thread (and the thread always sees results of its memory operations, mutex or not). - If later we see 'NET::query_cache_query == 0' without locking a + If later we see 'first_query_block == 0' without locking a mutex, that may only mean that some other thread have reset it by invalidating the query. Skipping the operation in this case is the - right thing to do, as NET::query_cache_query won't get non-zero for + right thing to do, as first_query_block won't get non-zero for this query again. See also comments in Query_cache::store_query() and @@ -810,56 +812,71 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length, NOTE, however, that double-check locking is not applicable in 'invalidate' functions, as we may erroneously skip invalidation, because the thread doing invalidation may never see non-zero - NET::query_cache_query. + 'first_query_block'. */ -void query_cache_init_query(NET *net) +/** + libmysql convenience wrapper to insert data into query cache. +*/ +void query_cache_insert(const char *packet, ulong length, + unsigned pkt_nr) { + THD *thd= current_thd; + /* - It is safe to initialize 'NET::query_cache_query' without a lock - here, because before it will be accessed from different threads it - will be set in this thread under a lock, and access from the same - thread is always safe. + Current_thd can be NULL when a new connection is immediately ended + due to "Too many connections". thd->store_globals() has not been + called at this time and hence my_pthread_setspecific_ptr(THR_THD, + this) has not been called for this thread. */ - net->query_cache_query= 0; + + if (!thd) + return; + + query_cache.insert(&thd->query_cache_tls, + packet, length, + pkt_nr); } -/* +/** Insert the packet into the query cache. */ -void query_cache_insert(NET *net, const char *packet, ulong length) +void +Query_cache::insert(Query_cache_tls *query_cache_tls, + const char *packet, ulong length, + unsigned pkt_nr) { - DBUG_ENTER("query_cache_insert"); + DBUG_ENTER("Query_cache::insert"); /* See the comment on double-check locking usage above. */ - if (net->query_cache_query == 0) + if (is_disabled() || query_cache_tls->first_query_block == NULL) DBUG_VOID_RETURN; DBUG_EXECUTE_IF("wait_in_query_cache_insert", debug_wait_for_kill("wait_in_query_cache_insert"); ); - if (query_cache.try_lock()) + + if (try_lock()) DBUG_VOID_RETURN; - Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query; - if (!query_block) + Query_cache_block *query_block = query_cache_tls->first_query_block; + if (query_block == NULL) { /* We lost the writer and the currently processed query has been invalidated; there is nothing left to do. */ - query_cache.unlock(); + unlock(); DBUG_VOID_RETURN; } - BLOCK_LOCK_WR(query_block); Query_cache_query *header= query_block->query(); Query_cache_block *result= header->result(); - DUMP(&query_cache); + DUMP(this); DBUG_PRINT("qcache", ("insert packet %lu bytes long",length)); /* @@ -867,8 +884,8 @@ void query_cache_insert(NET *net, const char *packet, ulong length) still need structure_guard_mutex to free the query, and therefore unlock it later in this function. */ - if (!query_cache.append_result_data(&result, length, (uchar*) packet, - query_block)) + if (!append_result_data(&result, length, (uchar*) packet, + query_block)) { DBUG_PRINT("warning", ("Can't append data")); header->result(result); @@ -877,60 +894,63 @@ void query_cache_insert(NET *net, const char *packet, ulong length) query_cache.free_query(query_block); query_cache.refused++; // append_result_data no success => we need unlock - query_cache.unlock(); + unlock(); DBUG_VOID_RETURN; } header->result(result); - header->last_pkt_nr= net->pkt_nr; + header->last_pkt_nr= pkt_nr; BLOCK_UNLOCK_WR(query_block); - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); + DBUG_EXECUTE("check_querycache",check_integrity(0);); DBUG_VOID_RETURN; } -void query_cache_abort(NET *net) +void +Query_cache::abort(Query_cache_tls *query_cache_tls) { DBUG_ENTER("query_cache_abort"); THD *thd= current_thd; /* See the comment on double-check locking usage above. */ - if (net->query_cache_query == 0) + if (is_disabled() || query_cache_tls->first_query_block == NULL) DBUG_VOID_RETURN; - if (query_cache.try_lock()) + if (try_lock()) DBUG_VOID_RETURN; /* While we were waiting another thread might have changed the status of the writer. Make sure the writer still exists before continue. */ - Query_cache_block *query_block= ((Query_cache_block*) - net->query_cache_query); + Query_cache_block *query_block= query_cache_tls->first_query_block; if (query_block) { thd_proc_info(thd, "storing result in query cache"); - DUMP(&query_cache); + DUMP(this); BLOCK_LOCK_WR(query_block); // The following call will remove the lock on query_block - query_cache.free_query(query_block); - net->query_cache_query= 0; - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + free_query(query_block); + query_cache_tls->first_query_block= NULL; + DBUG_EXECUTE("check_querycache", check_integrity(1);); } - query_cache.unlock(); + unlock(); + DBUG_VOID_RETURN; } -void query_cache_end_of_result(THD *thd) +void Query_cache::end_of_result(THD *thd) { Query_cache_block *query_block; - DBUG_ENTER("query_cache_end_of_result"); + Query_cache_tls *query_cache_tls= &thd->query_cache_tls; + ulonglong limit_found_rows= thd->limit_found_rows; + DBUG_ENTER("Query_cache::end_of_result"); /* See the comment on double-check locking usage above. */ - if (thd->net.query_cache_query == 0) + if (query_cache_tls->first_query_block == NULL) DBUG_VOID_RETURN; /* Ensure that only complete results are cached. */ @@ -938,19 +958,19 @@ void query_cache_end_of_result(THD *thd) if (thd->killed) { - query_cache_abort(&thd->net); + query_cache_abort(&thd->query_cache_tls); DBUG_VOID_RETURN; } #ifdef EMBEDDED_LIBRARY - query_cache_insert(&thd->net, (char*)thd, - emb_count_querycache_size(thd)); + insert(query_cache_tls, (char*)thd, + emb_count_querycache_size(thd), 0); #endif - if (query_cache.try_lock()) + if (try_lock()) DBUG_VOID_RETURN; - query_block= ((Query_cache_block*) thd->net.query_cache_query); + query_block= query_cache_tls->first_query_block; if (query_block) { /* @@ -959,7 +979,7 @@ void query_cache_end_of_result(THD *thd) block, the writer should be dropped. */ thd_proc_info(thd, "storing result in query cache"); - DUMP(&query_cache); + DUMP(this); BLOCK_LOCK_WR(query_block); Query_cache_query *header= query_block->query(); Query_cache_block *last_result_block; @@ -976,8 +996,8 @@ void query_cache_end_of_result(THD *thd) and removed from QC. */ DBUG_ASSERT(0); - query_cache.free_query(query_block); - query_cache.unlock(); + free_query(query_block); + unlock(); DBUG_VOID_RETURN; } last_result_block= header->result()->prev; @@ -986,17 +1006,17 @@ void query_cache_end_of_result(THD *thd) if (last_result_block->length >= query_cache.min_allocation_unit + len) query_cache.split_block(last_result_block,len); - header->found_rows(current_thd->limit_found_rows); + header->found_rows(limit_found_rows); header->result()->type= Query_cache_block::RESULT; /* Drop the writer. */ header->writer(0); - thd->net.query_cache_query= 0; + query_cache_tls->first_query_block= NULL; BLOCK_UNLOCK_WR(query_block); - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); - + DBUG_EXECUTE("check_querycache", check_integrity(1);); } - query_cache.unlock(); + + unlock(); DBUG_VOID_RETURN; } @@ -1036,7 +1056,7 @@ Query_cache::Query_cache(ulong query_cache_limit_arg, min_result_data_size(ALIGN_SIZE(min_result_data_size_arg)), def_query_hash_size(ALIGN_SIZE(def_query_hash_size_arg)), def_table_hash_size(ALIGN_SIZE(def_table_hash_size_arg)), - initialized(0) + initialized(0), m_query_cache_is_disabled(FALSE) { ulong min_needed= (ALIGN_SIZE(sizeof(Query_cache_block)) + ALIGN_SIZE(sizeof(Query_cache_block_table)) + @@ -1074,7 +1094,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg) Drop the writer; this will cancel any attempts to store the processed statement associated with this writer. */ - query->writer()->query_cache_query= 0; + query->writer()->first_query_block= NULL; query->writer(0); refused++; } @@ -1136,7 +1156,9 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) protocol (COM_EXECUTE) cannot be served to statements asking for results in the text protocol (COM_QUERY) and vice-versa. */ - flags.result_in_binary_protocol= (unsigned int) thd->protocol->type(); + flags.protocol_type= (unsigned int) thd->protocol->type(); + /* PROTOCOL_LOCAL results are not cached. */ + DBUG_ASSERT(flags.protocol_type != (unsigned int) Protocol::PROTOCOL_LOCAL); flags.more_results_exists= test(thd->server_status & SERVER_MORE_RESULTS_EXISTS); flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS); @@ -1165,7 +1187,7 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \ def_week_frmt: %lu, in_trans: %d, autocommit: %d", (int)flags.client_long_flag, (int)flags.client_protocol_41, - (int)flags.result_in_binary_protocol, + (int)flags.protocol_type, (int)flags.more_results_exists, flags.pkt_nr, flags.character_set_client_num, @@ -1230,7 +1252,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", /* Check if another thread is processing the same query? */ Query_cache_block *competitor = (Query_cache_block *) - hash_search(&queries, (uchar*) thd->query, tot_length); + my_hash_search(&queries, (uchar*) thd->query, tot_length); DBUG_PRINT("qcache", ("competitor 0x%lx", (ulong) competitor)); if (competitor == 0) { @@ -1259,7 +1281,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", { refused++; DBUG_PRINT("warning", ("tables list including failed")); - hash_delete(&queries, (uchar *) query_block); + my_hash_delete(&queries, (uchar *) query_block); header->unlock_n_destroy(); free_memory_block(query_block); unlock(); @@ -1268,8 +1290,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", double_linked_list_simple_include(query_block, &queries_blocks); inserts++; queries_in_cache++; - net->query_cache_query= (uchar*) query_block; - header->writer(net); + thd->query_cache_tls.first_query_block= query_block; + header->writer(&thd->query_cache_tls); header->tables_type(tables_type); unlock(); @@ -1325,7 +1347,10 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) { ulonglong engine_data; Query_cache_query *query; - Query_cache_block *first_result_block, *result_block; +#ifndef EMBEDDED_LIBRARY + Query_cache_block *first_result_block; +#endif + Query_cache_block *result_block; Query_cache_block_table *block_table, *block_table_end; ulong tot_length; Query_cache_query_flags flags; @@ -1338,8 +1363,8 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) See also a note on double-check locking usage above. */ - if (thd->locked_tables || thd->variables.query_cache_type == 0 || - query_cache_size == 0) + if (is_disabled() || thd->locked_tables || + thd->variables.query_cache_type == 0 || query_cache_size == 0) goto err; if (!thd->lex->safe_to_cache_query) @@ -1396,12 +1421,6 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) if (query_cache_size == 0) goto err_unlock; - /* - Check that we haven't forgot to reset the query cache variables; - make sure there are no attached query cache writer to this thread. - */ - DBUG_ASSERT(thd->net.query_cache_query == 0); - Query_cache_block *query_block; tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE; @@ -1423,7 +1442,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG); flags.client_protocol_41= test(thd->client_capabilities & CLIENT_PROTOCOL_41); - flags.result_in_binary_protocol= (unsigned int)thd->protocol->type(); + flags.protocol_type= (unsigned int) thd->protocol->type(); flags.more_results_exists= test(thd->server_status & SERVER_MORE_RESULTS_EXISTS); flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS); @@ -1450,7 +1469,7 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \ def_week_frmt: %lu, in_trans: %d, autocommit: %d", (int)flags.client_long_flag, (int)flags.client_protocol_41, - (int)flags.result_in_binary_protocol, + (int)flags.protocol_type, (int)flags.more_results_exists, flags.pkt_nr, flags.character_set_client_num, @@ -1467,8 +1486,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", (int)flags.autocommit)); memcpy((uchar *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)), (uchar*) &flags, QUERY_CACHE_FLAGS_SIZE); - query_block = (Query_cache_block *) hash_search(&queries, (uchar*) sql, - tot_length); + query_block = (Query_cache_block *) my_hash_search(&queries, (uchar*) sql, + tot_length); /* Quick abort on unlocked data */ if (query_block == 0 || query_block->query()->result() == 0 || @@ -1483,7 +1502,10 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", BLOCK_LOCK_RD(query_block); query = query_block->query(); - result_block= first_result_block= query->result(); + result_block= query->result(); +#ifndef EMBEDDED_LIBRARY + first_result_block= result_block; +#endif if (result_block == 0 || result_block->type != Query_cache_block::RESULT) { @@ -1545,7 +1567,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", table_list.db = table->db(); table_list.alias= table_list.table_name= table->table(); #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_table_access(thd,SELECT_ACL,&table_list, 1, TRUE)) + if (check_table_access(thd,SELECT_ACL,&table_list, FALSE, 1,TRUE)) { DBUG_PRINT("qcache", ("probably no SELECT access to %s.%s => return to normal processing", @@ -1648,6 +1670,8 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, my_bool using_transactions) { DBUG_ENTER("Query_cache::invalidate (table list)"); + if (is_disabled()) + DBUG_VOID_RETURN; using_transactions= using_transactions && (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); @@ -1678,6 +1702,9 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) { DBUG_ENTER("Query_cache::invalidate (changed table list)"); + if (is_disabled()) + DBUG_VOID_RETURN; + THD *thd= current_thd; for (; tables_used; tables_used= tables_used->next) { @@ -1703,8 +1730,11 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) */ void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used) { - THD *thd= current_thd; DBUG_ENTER("Query_cache::invalidate_locked_for_write"); + if (is_disabled()) + DBUG_VOID_RETURN; + + THD *thd= current_thd; for (; tables_used; tables_used= tables_used->next_local) { thd_proc_info(thd, "invalidating query cache entries (table)"); @@ -1725,7 +1755,9 @@ void Query_cache::invalidate(THD *thd, TABLE *table, my_bool using_transactions) { DBUG_ENTER("Query_cache::invalidate (table)"); - + if (is_disabled()) + DBUG_VOID_RETURN; + using_transactions= using_transactions && (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); if (using_transactions && @@ -1742,6 +1774,8 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, my_bool using_transactions) { DBUG_ENTER("Query_cache::invalidate (key)"); + if (is_disabled()) + DBUG_VOID_RETURN; using_transactions= using_transactions && (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); @@ -1760,9 +1794,12 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, void Query_cache::invalidate(char *db) { - bool restart= FALSE; + DBUG_ENTER("Query_cache::invalidate (db)"); + if (is_disabled()) + DBUG_VOID_RETURN; + bool restart= FALSE; /* Lock the query cache and queue all invalidation attempts to avoid the risk of a race between invalidation, cache inserts and flushes. @@ -1847,6 +1884,9 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename) void Query_cache::flush() { DBUG_ENTER("Query_cache::flush"); + if (is_disabled()) + DBUG_VOID_RETURN; + DBUG_EXECUTE_IF("wait_in_query_cache_flush1", debug_wait_for_kill("wait_in_query_cache_flush1");); @@ -1878,6 +1918,9 @@ void Query_cache::pack(ulong join_limit, uint iteration_limit) { DBUG_ENTER("Query_cache::pack"); + if (is_disabled()) + DBUG_VOID_RETURN; + /* If the entire qc is being invalidated we can bail out early instead of waiting for the lock. @@ -1935,6 +1978,15 @@ void Query_cache::init() pthread_cond_init(&COND_cache_status_changed, NULL); m_cache_lock_status= Query_cache::UNLOCKED; initialized = 1; + /* + If we explicitly turn off query cache from the command line query cache will + be disabled for the reminder of the server life time. This is because we + want to avoid locking the QC specific mutex if query cache isn't going to + be used. + */ + if (global_system_variables.query_cache_type == 0) + query_cache.disable_query_cache(); + DBUG_VOID_RETURN; } @@ -2073,8 +2125,8 @@ ulong Query_cache::init_cache() DUMP(this); - VOID(hash_init(&queries, &my_charset_bin, def_query_hash_size, 0, 0, - query_cache_query_get_key, 0, 0)); + VOID(my_hash_init(&queries, &my_charset_bin, def_query_hash_size, 0, 0, + query_cache_query_get_key, 0, 0)); #ifndef FN_NO_CASE_SENCE /* If lower_case_table_names!=0 then db and table names are already @@ -2084,8 +2136,8 @@ ulong Query_cache::init_cache() lower_case_table_names == 0 then we should distinguish my_table and MY_TABLE cases and so again can use binary collation. */ - VOID(hash_init(&tables, &my_charset_bin, def_table_hash_size, 0, 0, - query_cache_table_get_key, 0, 0)); + VOID(my_hash_init(&tables, &my_charset_bin, def_table_hash_size, 0, 0, + query_cache_table_get_key, 0, 0)); #else /* On windows, OS/2, MacOS X with HFS+ or any other case insensitive @@ -2095,10 +2147,10 @@ ulong Query_cache::init_cache() file system) and so should use case insensitive collation for comparison. */ - VOID(hash_init(&tables, - lower_case_table_names ? &my_charset_bin : - files_charset_info, - def_table_hash_size, 0, 0,query_cache_table_get_key, 0, 0)); + VOID(my_hash_init(&tables, + lower_case_table_names ? &my_charset_bin : + files_charset_info, + def_table_hash_size, 0, 0,query_cache_table_get_key, 0, 0)); #endif queries_in_cache = 0; @@ -2148,8 +2200,8 @@ void Query_cache::free_cache() my_free((uchar*) cache, MYF(MY_ALLOW_ZERO_PTR)); make_disabled(); - hash_free(&queries); - hash_free(&tables); + my_hash_free(&queries); + my_hash_free(&tables); DBUG_VOID_RETURN; } @@ -2261,7 +2313,7 @@ void Query_cache::free_query_internal(Query_cache_block *query_block) if (query->writer() != 0) { /* Tell MySQL that this query should not be cached anymore */ - query->writer()->query_cache_query= 0; + query->writer()->first_query_block= NULL; query->writer(0); } double_linked_list_exclude(query_block, &queries_blocks); @@ -2324,7 +2376,7 @@ void Query_cache::free_query(Query_cache_block *query_block) (ulong) query_block, query_block->query()->length() )); - hash_delete(&queries,(uchar *) query_block); + my_hash_delete(&queries,(uchar *) query_block); free_query_internal(query_block); DBUG_VOID_RETURN; @@ -2653,7 +2705,7 @@ void Query_cache::invalidate_table_internal(THD *thd, uchar *key, uint32 key_length) { Query_cache_block *table_block= - (Query_cache_block*)hash_search(&tables, key, key_length); + (Query_cache_block*)my_hash_search(&tables, key, key_length); if (table_block) { Query_cache_block_table *list_root= table_block->table(0); @@ -2851,7 +2903,7 @@ Query_cache::insert_table(uint key_len, char *key, THD *thd= current_thd; Query_cache_block *table_block= - (Query_cache_block *)hash_search(&tables, (uchar*) key, key_len); + (Query_cache_block *) my_hash_search(&tables, (uchar*) key, key_len); if (table_block && table_block->table()->engine_data() != engine_data) @@ -2967,7 +3019,7 @@ void Query_cache::unlink_table(Query_cache_block_table *node) Query_cache_block *table_block= neighbour->block(); double_linked_list_exclude(table_block, &tables_blocks); - hash_delete(&tables,(uchar *) table_block); + my_hash_delete(&tables,(uchar *) table_block); free_memory_block(table_block); } DBUG_VOID_RETURN; @@ -3474,7 +3526,8 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used, */ TABLE_COUNTER_TYPE -Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex, +Query_cache::is_cacheable(THD *thd, size_t query_len, const char *query, + LEX *lex, TABLE_LIST *tables_used, uint8 *tables_type) { TABLE_COUNTER_TYPE table_count; @@ -3651,7 +3704,7 @@ my_bool Query_cache::move_by_type(uchar **border, uchar *key; size_t key_length; key=query_cache_table_get_key((uchar*) block, &key_length, 0); - hash_first(&tables, (uchar*) key, key_length, &record_idx); + my_hash_first(&tables, (uchar*) key, key_length, &record_idx); block->destroy(); new_block->init(len); @@ -3685,7 +3738,7 @@ my_bool Query_cache::move_by_type(uchar **border, /* Fix pointer to table name */ new_block->table()->table(new_block->table()->db() + tablename_offset); /* Fix hash to point at moved block */ - hash_replace(&tables, &record_idx, (uchar*) new_block); + my_hash_replace(&tables, &record_idx, (uchar*) new_block); DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx", len, (ulong) new_block, (ulong) *border)); @@ -3711,7 +3764,7 @@ my_bool Query_cache::move_by_type(uchar **border, uchar *key; size_t key_length; key=query_cache_query_get_key((uchar*) block, &key_length, 0); - hash_first(&queries, (uchar*) key, key_length, &record_idx); + my_hash_first(&queries, (uchar*) key, key_length, &record_idx); // Move table of used tables memmove((char*) new_block->table(0), (char*) block->table(0), ALIGN_SIZE(n_tables*sizeof(Query_cache_block_table))); @@ -3773,13 +3826,13 @@ my_bool Query_cache::move_by_type(uchar **border, If someone is writing to this block, inform the writer that the block has been moved. */ - NET *net = new_block->query()->writer(); - if (net != 0) + Query_cache_tls *query_cache_tls= new_block->query()->writer(); + if (query_cache_tls != NULL) { - net->query_cache_query= (uchar*) new_block; + query_cache_tls->first_query_block= new_block; } /* Fix hash to point at moved block */ - hash_replace(&queries, &record_idx, (uchar*) new_block); + my_hash_replace(&queries, &record_idx, (uchar*) new_block); DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx", len, (ulong) new_block, (ulong) *border)); break; @@ -4190,13 +4243,13 @@ my_bool Query_cache::check_integrity(bool locked) if (!locked) lock_and_suspend(); - if (hash_check(&queries)) + if (my_hash_check(&queries)) { DBUG_PRINT("error", ("queries hash is damaged")); result = 1; } - if (hash_check(&tables)) + if (my_hash_check(&tables)) { DBUG_PRINT("error", ("tables hash is damaged")); result = 1; @@ -4363,7 +4416,7 @@ my_bool Query_cache::check_integrity(bool locked) (ulong) block, (uint) block->type)); size_t length; uchar *key = query_cache_query_get_key((uchar*) block, &length, 0); - uchar* val = hash_search(&queries, key, length); + uchar* val = my_hash_search(&queries, key, length); if (((uchar*)block) != val) { DBUG_PRINT("error", ("block 0x%lx found in queries hash like 0x%lx", @@ -4398,7 +4451,7 @@ my_bool Query_cache::check_integrity(bool locked) (ulong) block, (uint) block->type)); size_t length; uchar *key = query_cache_table_get_key((uchar*) block, &length, 0); - uchar* val = hash_search(&tables, key, length); + uchar* val = my_hash_search(&tables, key, length); if (((uchar*)block) != val) { DBUG_PRINT("error", ("block 0x%lx found in tables hash like 0x%lx", @@ -4638,3 +4691,4 @@ err2: #endif /* DBUG_OFF */ #endif /*HAVE_QUERY_CACHE*/ + diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 777ddd39280..6b187ebd6c2 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -64,6 +64,8 @@ struct Query_cache_table; struct Query_cache_query; struct Query_cache_result; class Query_cache; +struct Query_cache_tls; +struct LEX; /** This class represents a node in the linked chain of queries @@ -137,7 +139,7 @@ struct Query_cache_query ulonglong limit_found_rows; rw_lock_t lock; Query_cache_block *res; - NET *wri; + Query_cache_tls *wri; ulong len; uint8 tbls_type; unsigned int last_pkt_nr; @@ -149,8 +151,8 @@ struct Query_cache_query inline void found_rows(ulonglong rows) { limit_found_rows= rows; } inline Query_cache_block *result() { return res; } inline void result(Query_cache_block *p) { res= p; } - inline NET *writer() { return wri; } - inline void writer(NET *p) { wri= p; } + inline Query_cache_tls *writer() { return wri; } + inline void writer(Query_cache_tls *p) { wri= p; } inline uint8 tables_type() { return tbls_type; } inline void tables_type(uint8 type) { tbls_type= type; } inline ulong length() { return len; } @@ -279,8 +281,11 @@ private: enum Cache_lock_status { UNLOCKED, LOCKED_NO_WAIT, LOCKED }; Cache_lock_status m_cache_lock_status; + bool m_query_cache_is_disabled; + void free_query_internal(Query_cache_block *point); void invalidate_table_internal(THD *thd, uchar *key, uint32 key_length); + void disable_query_cache(void) { m_query_cache_is_disabled= TRUE; } protected: /* @@ -407,7 +412,8 @@ protected: If query is cacheable return number tables in query (query without tables not cached) */ - TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query, + TABLE_COUNTER_TYPE is_cacheable(THD *thd, size_t query_len, + const char *query, LEX *lex, TABLE_LIST *tables_used, uint8 *tables_type); TABLE_COUNTER_TYPE process_and_count_tables(THD *thd, @@ -423,6 +429,8 @@ protected: uint def_query_hash_size = QUERY_CACHE_DEF_QUERY_HASH_SIZE, uint def_table_hash_size = QUERY_CACHE_DEF_TABLE_HASH_SIZE); + bool is_disabled(void) { return m_query_cache_is_disabled; } + /* initialize cache (mutex) */ void init(); /* resize query cache (return real query size, 0 if disabled) */ @@ -462,10 +470,13 @@ protected: void destroy(); - friend void query_cache_init_query(NET *net); - friend void query_cache_insert(NET *net, const char *packet, ulong length); - friend void query_cache_end_of_result(THD *thd); - friend void query_cache_abort(NET *net); + void insert(Query_cache_tls *query_cache_tls, + const char *packet, + ulong length, + unsigned pkt_nr); + + void end_of_result(THD *thd); + void abort(Query_cache_tls *query_cache_tls); /* The following functions are only used when debugging @@ -493,9 +504,4 @@ protected: extern Query_cache query_cache; extern TYPELIB query_cache_type_typelib; -void query_cache_init_query(NET *net); -void query_cache_insert(NET *net, const char *packet, ulong length); -void query_cache_end_of_result(THD *thd); -void query_cache_abort(NET *net); - #endif diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 1958bbe271c..c4e1399baae 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -91,7 +91,9 @@ extern "C" void free_user_var(user_var_entry *entry) bool Key_part_spec::operator==(const Key_part_spec& other) const { - return length == other.length && !strcmp(field_name, other.field_name); + return length == other.length && + !my_strcasecmp(system_charset_info, field_name.str, + other.field_name.str); } /** @@ -258,7 +260,7 @@ const char *set_thd_proc_info(THD *thd, const char *info, const char *old_info= thd->proc_info; DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, (info != NULL) ? info : "(null)")); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.status_change(info, calling_function, calling_file, calling_line); #endif thd->proc_info= info; @@ -409,7 +411,7 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length, /** - Implementation of Drop_table_error_handler::handle_error(). + Implementation of Drop_table_error_handler::handle_condition(). The reason in having this implementation is to silence technical low-level warnings during DROP TABLE operation. Currently we don't want to expose the following warnings during DROP TABLE: @@ -487,7 +489,7 @@ THD::THD() killed= NOT_KILLED; col_access=0; is_slave_error= thread_specific_used= FALSE; - hash_clear(&handler_tables_hash); + my_hash_clear(&handler_tables_hash); tmp_table=0; used_tables=0; cuted_fields= 0L; @@ -520,9 +522,6 @@ THD::THD() net.vio=0; #endif client_capabilities= 0; // minimalistic client -#ifdef HAVE_QUERY_CACHE - query_cache_init_query(&net); // If error on boot -#endif ull=0; system_thread= NON_SYSTEM_THREAD; cleanup_done= abort_on_warning= no_warnings_for_error= 0; @@ -543,13 +542,13 @@ THD::THD() *scramble= '\0'; init(); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) profiling.set_thd(this); #endif user_connect=(USER_CONN *)0; - hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, - (hash_get_key) get_var_key, - (hash_free_key) free_user_var, 0); + my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, + (my_hash_get_key) get_var_key, + (my_hash_free_key) free_user_var, 0); sp_proc_cache= NULL; sp_func_cache= NULL; @@ -815,7 +814,7 @@ THD::raise_condition_no_handler(uint sql_errno, MYSQL_ERROR *cond= NULL; DBUG_ENTER("THD::raise_condition_no_handler"); - query_cache_abort(& net); + query_cache_abort(&query_cache_tls); /* FIXME: broken special case */ if (no_warnings_for_error && (level == MYSQL_ERROR::WARN_LEVEL_ERROR)) @@ -969,9 +968,9 @@ void THD::change_user(void) cleanup_done= 0; init(); stmt_map.reset(); - hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, - (hash_get_key) get_var_key, - (hash_free_key) free_user_var, 0); + my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, + (my_hash_get_key) get_var_key, + (my_hash_free_key) free_user_var, 0); sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); } @@ -1008,7 +1007,7 @@ void THD::cleanup(void) mysql_ha_cleanup(this); delete_dynamic(&user_var_events); - hash_free(&user_vars); + my_hash_free(&user_vars); close_temporary_tables(this); my_free((char*) variables.time_format, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) variables.date_format, MYF(MY_ALLOW_ZERO_PTR)); @@ -1526,8 +1525,8 @@ int THD::send_explain_fields(select_result *result) } item->maybe_null= 1; field_list.push_back(new Item_empty_string("Extra", 255, cs)); - return (result->send_fields(field_list, - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)); + return (result->send_result_set_metadata(field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)); } #ifdef SIGNAL_WITH_VIO_CLOSE @@ -1655,10 +1654,10 @@ bool sql_exchange::escaped_given(void) } -bool select_send::send_fields(List<Item> &list, uint flags) +bool select_send::send_result_set_metadata(List<Item> &list, uint flags) { bool res; - if (!(res= thd->protocol->send_fields(&list, flags))) + if (!(res= thd->protocol->send_result_set_metadata(&list, flags))) is_result_set_started= 1; return res; } @@ -1699,10 +1698,13 @@ void select_send::cleanup() bool select_send::send_data(List<Item> &items) { + Protocol *protocol= thd->protocol; + DBUG_ENTER("select_send::send_data"); + if (unit->offset_limit_cnt) { // using limit offset,count unit->offset_limit_cnt--; - return 0; + DBUG_RETURN(FALSE); } /* @@ -1712,36 +1714,18 @@ bool select_send::send_data(List<Item> &items) */ ha_release_temporary_latches(thd); - List_iterator_fast<Item> li(items); - Protocol *protocol= thd->protocol; - char buff[MAX_FIELD_WIDTH]; - String buffer(buff, sizeof(buff), &my_charset_bin); - DBUG_ENTER("select_send::send_data"); - protocol->prepare_for_resend(); - Item *item; - while ((item=li++)) - { - if (item->send(protocol, &buffer)) - { - protocol->free(); // Free used buffer - my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - break; - } - /* - Reset buffer to its original state, as it may have been altered in - Item::send(). - */ - buffer.set(buff, sizeof(buff), &my_charset_bin); - } - thd->sent_row_count++; - if (thd->is_error()) + if (protocol->send_result_set_row(&items)) { protocol->remove_last_row(); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } + + thd->sent_row_count++; + if (thd->vio_ok()) DBUG_RETURN(protocol->write()); + DBUG_RETURN(0); } @@ -2639,12 +2623,12 @@ Statement_map::Statement_map() : START_STMT_HASH_SIZE = 16, START_NAME_HASH_SIZE = 16 }; - hash_init(&st_hash, &my_charset_bin, START_STMT_HASH_SIZE, 0, 0, - get_statement_id_as_hash_key, - delete_statement_as_hash_key, MYF(0)); - hash_init(&names_hash, system_charset_info, START_NAME_HASH_SIZE, 0, 0, - (hash_get_key) get_stmt_name_hash_key, - NULL,MYF(0)); + my_hash_init(&st_hash, &my_charset_bin, START_STMT_HASH_SIZE, 0, 0, + get_statement_id_as_hash_key, + delete_statement_as_hash_key, MYF(0)); + my_hash_init(&names_hash, system_charset_info, START_NAME_HASH_SIZE, 0, 0, + (my_hash_get_key) get_stmt_name_hash_key, + NULL,MYF(0)); } @@ -2709,9 +2693,9 @@ int Statement_map::insert(THD *thd, Statement *statement) err_max: if (statement->name.str) - hash_delete(&names_hash, (uchar*) statement); + my_hash_delete(&names_hash, (uchar*) statement); err_names_hash: - hash_delete(&st_hash, (uchar*) statement); + my_hash_delete(&st_hash, (uchar*) statement); err_st_hash: return 1; } @@ -2732,9 +2716,9 @@ void Statement_map::erase(Statement *statement) if (statement == last_found_statement) last_found_statement= 0; if (statement->name.str) - hash_delete(&names_hash, (uchar *) statement); + my_hash_delete(&names_hash, (uchar *) statement); - hash_delete(&st_hash, (uchar *) statement); + my_hash_delete(&st_hash, (uchar *) statement); pthread_mutex_lock(&LOCK_prepared_stmt_count); DBUG_ASSERT(prepared_stmt_count > 0); prepared_stmt_count--; @@ -2764,8 +2748,8 @@ Statement_map::~Statement_map() prepared_stmt_count-= st_hash.records; pthread_mutex_unlock(&LOCK_prepared_stmt_count); - hash_free(&names_hash); - hash_free(&st_hash); + my_hash_free(&names_hash); + my_hash_free(&st_hash); } bool select_dumpvar::send_data(List<Item> &items) @@ -3302,15 +3286,15 @@ void xid_free_hash(void *ptr) bool xid_cache_init() { pthread_mutex_init(&LOCK_xid_cache, MY_MUTEX_INIT_FAST); - return hash_init(&xid_cache, &my_charset_bin, 100, 0, 0, - xid_get_hash_key, xid_free_hash, 0) != 0; + return my_hash_init(&xid_cache, &my_charset_bin, 100, 0, 0, + xid_get_hash_key, xid_free_hash, 0) != 0; } void xid_cache_free() { - if (hash_inited(&xid_cache)) + if (my_hash_inited(&xid_cache)) { - hash_free(&xid_cache); + my_hash_free(&xid_cache); pthread_mutex_destroy(&LOCK_xid_cache); } } @@ -3318,7 +3302,8 @@ void xid_cache_free() XID_STATE *xid_cache_search(XID *xid) { pthread_mutex_lock(&LOCK_xid_cache); - XID_STATE *res=(XID_STATE *)hash_search(&xid_cache, xid->key(), xid->key_length()); + XID_STATE *res=(XID_STATE *)my_hash_search(&xid_cache, xid->key(), + xid->key_length()); pthread_mutex_unlock(&LOCK_xid_cache); return res; } @@ -3329,7 +3314,7 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state) XID_STATE *xs; my_bool res; pthread_mutex_lock(&LOCK_xid_cache); - if (hash_search(&xid_cache, xid->key(), xid->key_length())) + if (my_hash_search(&xid_cache, xid->key(), xid->key_length())) res=0; else if (!(xs=(XID_STATE *)my_malloc(sizeof(*xs), MYF(MY_WME)))) res=1; @@ -3348,8 +3333,8 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state) bool xid_cache_insert(XID_STATE *xid_state) { pthread_mutex_lock(&LOCK_xid_cache); - DBUG_ASSERT(hash_search(&xid_cache, xid_state->xid.key(), - xid_state->xid.key_length())==0); + DBUG_ASSERT(my_hash_search(&xid_cache, xid_state->xid.key(), + xid_state->xid.key_length())==0); my_bool res=my_hash_insert(&xid_cache, (uchar*)xid_state); pthread_mutex_unlock(&LOCK_xid_cache); return res; @@ -3359,7 +3344,7 @@ bool xid_cache_insert(XID_STATE *xid_state) void xid_cache_delete(XID_STATE *xid_state) { pthread_mutex_lock(&LOCK_xid_cache); - hash_delete(&xid_cache, (uchar *)xid_state); + my_hash_delete(&xid_cache, (uchar *)xid_state); pthread_mutex_unlock(&LOCK_xid_cache); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 3f40195ba5b..1a5cceaca90 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -26,52 +26,8 @@ #include "log.h" #include "rpl_tblmap.h" -/** - An interface that is used to take an action when - the locking module notices that a table version has changed - since the last execution. "Table" here may refer to any kind of - table -- a base table, a temporary table, a view or an - information schema table. - - When we open and lock tables for execution of a prepared - statement, we must verify that they did not change - since statement prepare. If some table did change, the statement - parse tree *may* be no longer valid, e.g. in case it contains - optimizations that depend on table metadata. - - This class provides an interface (a method) that is - invoked when such a situation takes place. - The implementation of the method simply reports an error, but - the exact details depend on the nature of the SQL statement. - - At most 1 instance of this class is active at a time, in which - case THD::m_reprepare_observer is not NULL. - - @sa check_and_update_table_version() for details of the - version tracking algorithm - - @sa Open_tables_state::m_reprepare_observer for the life cycle - of metadata observers. -*/ - -class Reprepare_observer -{ -public: - Reprepare_observer() {} - /** - Check if a change of metadata is OK. In future - the signature of this method may be extended to accept the old - and the new versions, but since currently the check is very - simple, we only need the THD to report an error. - */ - bool report_error(THD *thd); - bool is_invalidated() const { return m_invalidated; } - void reset_reprepare_observer() { m_invalidated= FALSE; } -private: - bool m_invalidated; -}; - +class Reprepare_observer; class Relay_log_info; class Query_log_event; @@ -149,9 +105,14 @@ typedef struct st_copy_info { class Key_part_spec :public Sql_alloc { public: - const char *field_name; + LEX_STRING field_name; uint length; - Key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {} + Key_part_spec(const LEX_STRING &name, uint len) + : field_name(name), length(len) + {} + Key_part_spec(const char *name, const size_t name_len, uint len) + : length(len) + { field_name.str= (char *)name; field_name.length= name_len; } bool operator==(const Key_part_spec& other) const; /** Construct a copy of this Key_part_spec. field_name is copied @@ -204,15 +165,24 @@ public: enum Keytype type; KEY_CREATE_INFO key_create_info; List<Key_part_spec> columns; - const char *name; + LEX_STRING name; bool generated; - Key(enum Keytype type_par, const char *name_arg, + Key(enum Keytype type_par, const LEX_STRING &name_arg, KEY_CREATE_INFO *key_info_arg, bool generated_arg, List<Key_part_spec> &cols) :type(type_par), key_create_info(*key_info_arg), columns(cols), name(name_arg), generated(generated_arg) {} + Key(enum Keytype type_par, const char *name_arg, size_t name_len_arg, + KEY_CREATE_INFO *key_info_arg, bool generated_arg, + List<Key_part_spec> &cols) + :type(type_par), key_create_info(*key_info_arg), columns(cols), + generated(generated_arg) + { + name.str= (char *)name_arg; + name.length= name_len_arg; + } Key(const Key &rhs, MEM_ROOT *mem_root); virtual ~Key() {} /* Equality comparison of keys (ignoring name) */ @@ -237,7 +207,7 @@ public: Table_ident *ref_table; List<Key_part_spec> ref_columns; uint delete_opt, update_opt, match_opt; - Foreign_key(const char *name_arg, List<Key_part_spec> &cols, + Foreign_key(const LEX_STRING &name_arg, List<Key_part_spec> &cols, Table_ident *table, List<Key_part_spec> &ref_cols, uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg) :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols), @@ -270,6 +240,27 @@ public: LEX_COLUMN (const String& x,const uint& y ): column (x),rights (y) {} }; +/** + Query_cache_tls -- query cache thread local data. +*/ + +struct Query_cache_block; + +struct Query_cache_tls +{ + /* + 'first_query_block' should be accessed only via query cache + functions and methods to maintain proper locking. + */ + Query_cache_block *first_query_block; + void set_first_query_block(Query_cache_block *first_query_block_arg) + { + first_query_block= first_query_block_arg; + } + + Query_cache_tls() :first_query_block(NULL) {} +}; + /* SIGNAL / RESIGNAL / GET DIAGNOSTICS */ /** @@ -743,8 +734,8 @@ public: Statement *find_by_name(LEX_STRING *name) { Statement *stmt; - stmt= (Statement*)hash_search(&names_hash, (uchar*)name->str, - name->length); + stmt= (Statement*)my_hash_search(&names_hash, (uchar*)name->str, + name->length); return stmt; } @@ -753,7 +744,7 @@ public: if (last_found_statement == 0 || id != last_found_statement->id) { Statement *stmt; - stmt= (Statement *) hash_search(&st_hash, (uchar *) &id, sizeof(id)); + stmt= (Statement *) my_hash_search(&st_hash, (uchar *) &id, sizeof(id)); if (stmt && stmt->name.str) return NULL; last_found_statement= stmt; @@ -1232,6 +1223,9 @@ public: */ struct st_mysql_stmt *current_stmt; #endif +#ifdef HAVE_QUERY_CACHE + Query_cache_tls query_cache_tls; +#endif NET net; // client connection descriptor Protocol *protocol; // Current protocol Protocol_text protocol_text; // Normal protocol @@ -1650,7 +1644,7 @@ public: CHARSET_INFO *db_charset; Warning_info *warning_info; Diagnostics_area *stmt_da; -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) PROFILING profiling; #endif @@ -2414,7 +2408,7 @@ public: */ virtual uint field_count(List<Item> &fields) const { return fields.elements; } - virtual bool send_fields(List<Item> &list, uint flags)=0; + virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0; virtual bool send_data(List<Item> &items)=0; virtual bool initialize_tables (JOIN *join=0) { return 0; } virtual void send_error(uint errcode,const char *err); @@ -2459,7 +2453,7 @@ 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; } + bool send_result_set_metadata(List<Item> &fields, uint flag) { return FALSE; } }; @@ -2472,7 +2466,7 @@ class select_send :public select_result { bool is_result_set_started; public: select_send() :is_result_set_started(FALSE) {} - bool send_fields(List<Item> &list, uint flags); + bool send_result_set_metadata(List<Item> &list, uint flags); bool send_data(List<Item> &items); bool send_eof(); virtual bool check_simple_select() const { return FALSE; } diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index bbb75984b5d..959209df412 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -61,7 +61,7 @@ static int get_or_create_user_conn(THD *thd, const char *user, user_len= strlen(user); temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1; (void) pthread_mutex_lock(&LOCK_user_conn); - if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, + if (!(uc = (struct user_conn *) my_hash_search(&hash_user_connections, (uchar*) temp_user, temp_len))) { /* First connection for user; Create a user connection object */ @@ -151,7 +151,15 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc) end: if (error) + { uc->connections--; // no need for decrease_user_connections() here + /* + The thread may returned back to the pool and assigned to a user + that doesn't have a limit. Ensure the user is not using resources + of someone else. + */ + thd->user_connect= NULL; + } (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_RETURN(error); } @@ -183,7 +191,7 @@ void decrease_user_connections(USER_CONN *uc) if (!--uc->connections && !mqh_used) { /* Last connection for user; Delete it */ - (void) hash_delete(&hash_user_connections,(uchar*) uc); + (void) my_hash_delete(&hash_user_connections,(uchar*) uc); } (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_VOID_RETURN; @@ -462,7 +470,10 @@ check_user(THD *thd, enum enum_server_command command, { /* mysql_change_db() has pushed the error message. */ if (thd->user_connect) + { decrease_user_connections(thd->user_connect); + thd->user_connect= 0; + } DBUG_RETURN(1); } } @@ -486,6 +497,18 @@ check_user(THD *thd, enum enum_server_command command, thd->main_security_ctx.user, thd->main_security_ctx.host_or_ip, passwd_len ? ER(ER_YES) : ER(ER_NO)); + /* + log access denied messages to the error log when log-warnings = 2 + so that the overhead of the general query log is not required to track + failed connections + */ + if (global_system_variables.log_warnings > 1) + { + sql_print_warning(ER(ER_ACCESS_DENIED_ERROR), + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); + } DBUG_RETURN(1); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ } @@ -513,10 +536,10 @@ extern "C" void free_user(struct user_conn *uc) void init_max_user_conn(void) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - (void) hash_init(&hash_user_connections,system_charset_info,max_connections, - 0,0, - (hash_get_key) get_key_conn, (hash_free_key) free_user, - 0); + (void) + my_hash_init(&hash_user_connections,system_charset_info,max_connections, + 0,0, (my_hash_get_key) get_key_conn, + (my_hash_free_key) free_user, 0); #endif } @@ -524,7 +547,7 @@ void init_max_user_conn(void) void free_max_user_conn(void) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - hash_free(&hash_user_connections); + my_hash_free(&hash_user_connections); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ } @@ -542,8 +565,9 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0) memcpy(temp_user,lu->user.str,lu->user.length); memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length); temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0; - if ((uc = (struct user_conn *) hash_search(&hash_user_connections, - (uchar*) temp_user, temp_len))) + if ((uc = (struct user_conn *) my_hash_search(&hash_user_connections, + (uchar*) temp_user, + temp_len))) { uc->questions=0; get_mqh(temp_user,&temp_user[lu->user.length+1],uc); @@ -556,8 +580,8 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0) /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */ for (uint idx=0;idx < hash_user_connections.records; idx++) { - USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections, - idx); + USER_CONN *uc=(struct user_conn *) + my_hash_element(&hash_user_connections, idx); if (get_them) get_mqh(uc->user,uc->host,uc); uc->questions=0; @@ -945,7 +969,7 @@ static bool login_connection(THD *thd) my_net_set_write_timeout(net, connect_timeout); error= check_connection(thd); - net_end_statement(thd); + thd->protocol->end_statement(); if (error) { // Wrong permissions @@ -975,7 +999,15 @@ static void end_connection(THD *thd) NET *net= &thd->net; plugin_thdvar_cleanup(thd); if (thd->user_connect) + { decrease_user_connections(thd->user_connect); + /* + The thread may returned back to the pool and assigned to a user + that doesn't have a limit. Ensure the user is not using resources + of someone else. + */ + thd->user_connect= NULL; + } if (thd->killed || (net->error && net->vio != 0)) { diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 553342f155b..6d07f753086 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -90,7 +90,7 @@ class Materialized_cursor: public Server_side_cursor public: Materialized_cursor(select_result *result, TABLE *table); - int fill_item_list(THD *thd, List<Item> &send_fields); + int fill_item_list(THD *thd, List<Item> &send_result_set_metadata); virtual bool is_open() const { return table != 0; } virtual int open(JOIN *join __attribute__((unused))); virtual void fetch(ulong num_rows); @@ -115,7 +115,7 @@ public: Materialized_cursor *materialized_cursor; Select_materialize(select_result *result_arg) :result(result_arg), materialized_cursor(0) {} - virtual bool send_fields(List<Item> &list, uint flags); + virtual bool send_result_set_metadata(List<Item> &list, uint flags); }; @@ -376,12 +376,12 @@ Sensitive_cursor::open(JOIN *join_arg) join->change_result(result); /* Send fields description to the client; server_status is sent - in 'EOF' packet, which follows send_fields(). - We don't simply use SEND_EOF flag of send_fields because we also + in 'EOF' packet, which follows send_result_set_metadata(). + We don't simply use SEND_EOF flag of send_result_set_metadata because we also want to flush the network buffer, which is done only in a standalone send_eof(). */ - result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS); + result->send_result_set_metadata(*join->fields, Protocol::SEND_NUM_ROWS); thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; result->send_eof(); thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; @@ -566,14 +566,14 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg, Preserve the original metadata that would be sent to the client. @param thd Thread identifier. - @param send_fields List of fields that would be sent. + @param send_result_set_metadata List of fields that would be sent. */ -int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields) +int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_result_set_metadata) { Query_arena backup_arena; int rc; - List_iterator_fast<Item> it_org(send_fields); + List_iterator_fast<Item> it_org(send_result_set_metadata); List_iterator_fast<Item> it_dst(item_list); Item *item_org; Item *item_dst; @@ -583,7 +583,7 @@ int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields) if ((rc= table->fill_item_list(&item_list))) goto end; - DBUG_ASSERT(send_fields.elements == item_list.elements); + DBUG_ASSERT(send_result_set_metadata.elements == item_list.elements); /* Unless we preserve the original metadata, it will be lost, @@ -623,17 +623,17 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused))) { /* Now send the result set metadata to the client. We need to - do it here, as in Select_materialize::send_fields the items - for column types are not yet created (send_fields requires + do it here, as in Select_materialize::send_result_set_metadata the items + for column types are not yet created (send_result_set_metadata requires a list of items). The new types may differ from the original ones sent at prepare if some of them were altered by MySQL HEAP tables mechanism -- used when create_tmp_field_from_item may alter the original column type. - We can't simply supply SEND_EOF flag to send_fields, because - send_fields doesn't flush the network buffer. + We can't simply supply SEND_EOF flag to send_result_set_metadata, because + send_result_set_metadata doesn't flush the network buffer. */ - rc= result->send_fields(item_list, Protocol::SEND_NUM_ROWS); + rc= result->send_result_set_metadata(item_list, Protocol::SEND_NUM_ROWS); thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; result->send_eof(); thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; @@ -717,7 +717,7 @@ Materialized_cursor::~Materialized_cursor() Select_materialize ****************************************************************************/ -bool Select_materialize::send_fields(List<Item> &list, uint flags) +bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags) { DBUG_ASSERT(table == 0); if (create_result_table(unit->thd, unit->get_unit_column_types(), diff --git a/sql/sql_db.cc b/sql/sql_db.cc index c19bfba9fc1..5d1a9ac9398 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -105,8 +105,8 @@ static my_bool lock_db_insert(const char *dbname, uint length) safe_mutex_assert_owner(&LOCK_lock_db); - if (!(opt= (my_dblock_t*) hash_search(&lock_db_cache, - (uchar*) dbname, length))) + if (!(opt= (my_dblock_t*) my_hash_search(&lock_db_cache, + (uchar*) dbname, length))) { /* Db is not in the hash, insert it */ char *tmp_name; @@ -139,9 +139,9 @@ void lock_db_delete(const char *name, uint length) { my_dblock_t *opt; safe_mutex_assert_owner(&LOCK_lock_db); - if ((opt= (my_dblock_t *)hash_search(&lock_db_cache, - (const uchar*) name, length))) - hash_delete(&lock_db_cache, (uchar*) opt); + if ((opt= (my_dblock_t *)my_hash_search(&lock_db_cache, + (const uchar*) name, length))) + my_hash_delete(&lock_db_cache, (uchar*) opt); } @@ -221,14 +221,14 @@ bool my_database_names_init(void) if (!dboptions_init) { dboptions_init= 1; - error= hash_init(&dboptions, lower_case_table_names ? - &my_charset_bin : system_charset_info, - 32, 0, 0, (hash_get_key) dboptions_get_key, - free_dbopt,0) || - hash_init(&lock_db_cache, lower_case_table_names ? - &my_charset_bin : system_charset_info, - 32, 0, 0, (hash_get_key) lock_db_get_key, - lock_db_free_element,0); + error= my_hash_init(&dboptions, lower_case_table_names ? + &my_charset_bin : system_charset_info, + 32, 0, 0, (my_hash_get_key) dboptions_get_key, + free_dbopt,0) || + my_hash_init(&lock_db_cache, lower_case_table_names ? + &my_charset_bin : system_charset_info, + 32, 0, 0, (my_hash_get_key) lock_db_get_key, + lock_db_free_element,0); } return error; @@ -245,9 +245,9 @@ void my_database_names_free(void) if (dboptions_init) { dboptions_init= 0; - hash_free(&dboptions); + my_hash_free(&dboptions); (void) rwlock_destroy(&LOCK_dboptions); - hash_free(&lock_db_cache); + my_hash_free(&lock_db_cache); } } @@ -259,11 +259,11 @@ void my_database_names_free(void) void my_dbopt_cleanup(void) { rw_wrlock(&LOCK_dboptions); - hash_free(&dboptions); - hash_init(&dboptions, lower_case_table_names ? - &my_charset_bin : system_charset_info, - 32, 0, 0, (hash_get_key) dboptions_get_key, - free_dbopt,0); + my_hash_free(&dboptions); + my_hash_init(&dboptions, lower_case_table_names ? + &my_charset_bin : system_charset_info, + 32, 0, 0, (my_hash_get_key) dboptions_get_key, + free_dbopt,0); rw_unlock(&LOCK_dboptions); } @@ -289,7 +289,7 @@ static my_bool get_dbopt(const char *dbname, HA_CREATE_INFO *create) length= (uint) strlen(dbname); rw_rdlock(&LOCK_dboptions); - if ((opt= (my_dbopt_t*) hash_search(&dboptions, (uchar*) dbname, length))) + if ((opt= (my_dbopt_t*) my_hash_search(&dboptions, (uchar*) dbname, length))) { create->default_table_charset= opt->charset; error= 0; @@ -321,7 +321,8 @@ static my_bool put_dbopt(const char *dbname, HA_CREATE_INFO *create) length= (uint) strlen(dbname); rw_wrlock(&LOCK_dboptions); - if (!(opt= (my_dbopt_t*) hash_search(&dboptions, (uchar*) dbname, length))) + if (!(opt= (my_dbopt_t*) my_hash_search(&dboptions, (uchar*) dbname, + length))) { /* Options are not in the hash, insert them */ char *tmp_name; @@ -361,9 +362,9 @@ void del_dbopt(const char *path) { my_dbopt_t *opt; rw_wrlock(&LOCK_dboptions); - if ((opt= (my_dbopt_t *)hash_search(&dboptions, (const uchar*) path, - strlen(path)))) - hash_delete(&dboptions, (uchar*) opt); + if ((opt= (my_dbopt_t *)my_hash_search(&dboptions, (const uchar*) path, + strlen(path)))) + my_hash_delete(&dboptions, (uchar*) opt); rw_unlock(&LOCK_dboptions); } @@ -1027,7 +1028,7 @@ exit: SELECT DATABASE() in the future). For this we free() thd->db and set it to 0. */ - if (thd->db && !strcmp(thd->db, db)) + if (thd->db && !strcmp(thd->db, db) && error == 0) mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); start_waiting_global_read_lock(thd); @@ -1719,8 +1720,8 @@ lock_databases(THD *thd, const char *db1, uint length1, { pthread_mutex_lock(&LOCK_lock_db); while (!thd->killed && - (hash_search(&lock_db_cache,(uchar*) db1, length1) || - hash_search(&lock_db_cache,(uchar*) db2, length2))) + (my_hash_search(&lock_db_cache,(uchar*) db1, length1) || + my_hash_search(&lock_db_cache,(uchar*) db2, length2))) { wait_for_condition(thd, &LOCK_lock_db, &COND_refresh); pthread_mutex_lock(&LOCK_lock_db); diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 78b30619999..eeefdb99eed 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -673,7 +673,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show) field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG)); field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE)); - if (thd->protocol->send_fields(&field_list, + if (thd->protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 3bbf4b78d07..ab3f2797405 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -207,21 +207,21 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) DBUG_RETURN(TRUE); } - if (! hash_inited(&thd->handler_tables_hash)) + if (! my_hash_inited(&thd->handler_tables_hash)) { /* HASH entries are of type TABLE_LIST. */ - if (hash_init(&thd->handler_tables_hash, &my_charset_latin1, - HANDLER_TABLES_HASH_SIZE, 0, 0, - (hash_get_key) mysql_ha_hash_get_key, - (hash_free_key) mysql_ha_hash_free, 0)) + if (my_hash_init(&thd->handler_tables_hash, &my_charset_latin1, + HANDLER_TABLES_HASH_SIZE, 0, 0, + (my_hash_get_key) mysql_ha_hash_get_key, + (my_hash_free_key) mysql_ha_hash_free, 0)) goto err; } else if (! reopen) /* Otherwise we have 'tables' already. */ { - if (hash_search(&thd->handler_tables_hash, (uchar*) tables->alias, - strlen(tables->alias) + 1)) + if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias, + strlen(tables->alias) + 1)) { DBUG_PRINT("info",("duplicate '%s'", tables->alias)); my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias); @@ -367,12 +367,12 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) DBUG_PRINT("enter",("'%s'.'%s' as '%s'", tables->db, tables->table_name, tables->alias)); - if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash, - (uchar*) tables->alias, - strlen(tables->alias) + 1))) + if ((hash_tables= (TABLE_LIST*) my_hash_search(&thd->handler_tables_hash, + (uchar*) tables->alias, + strlen(tables->alias) + 1))) { mysql_ha_close_table(thd, hash_tables, FALSE); - hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); + my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); } else { @@ -436,9 +436,9 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, it++; retry: - if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash, - (uchar*) tables->alias, - strlen(tables->alias) + 1))) + if ((hash_tables= (TABLE_LIST*) my_hash_search(&thd->handler_tables_hash, + (uchar*) tables->alias, + strlen(tables->alias) + 1))) { table= hash_tables->table; DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: 0x%lx", @@ -460,7 +460,7 @@ retry: hash_tables->db, hash_tables->table_name, hash_tables->alias, table)); } - + table->pos_in_table_list= tables; #if MYSQL_VERSION_ID < 40100 if (*tables->db && strcmp(table->table_cache_key, tables->db)) { @@ -545,7 +545,7 @@ retry: tables->db, tables->alias, &it, 0)) goto err; - protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); + protocol->send_result_set_metadata(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); /* In ::external_lock InnoDB resets the fields which tell it that @@ -667,18 +667,11 @@ retry: continue; if (num_rows >= offset_limit_cnt) { - Item *item; protocol->prepare_for_resend(); - it.rewind(); - while ((item=it++)) - { - if (item->send(thd->protocol, &buffer)) - { - protocol->free(); // Free used - my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - goto err; - } - } + + if (protocol->send_result_set_row(&list)) + goto err; + protocol->write(); } num_rows++; @@ -716,7 +709,7 @@ static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables) /* search for all handlers with matching table names */ for (uint i= 0; i < thd->handler_tables_hash.records; i++) { - hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i); + hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); for (tables= first; tables; tables= tables->next_local) { if ((! *tables->db || @@ -760,7 +753,7 @@ void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked) next= hash_tables->next_local; if (hash_tables->table) mysql_ha_close_table(thd, hash_tables, is_locked); - hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); + my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); hash_tables= next; } @@ -786,7 +779,7 @@ void mysql_ha_flush(THD *thd) for (uint i= 0; i < thd->handler_tables_hash.records; i++) { - hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i); + hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock()) mysql_ha_close_table(thd, hash_tables, TRUE); } @@ -810,12 +803,12 @@ void mysql_ha_cleanup(THD *thd) for (uint i= 0; i < thd->handler_tables_hash.records; i++) { - hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i); + hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); if (hash_tables->table) mysql_ha_close_table(thd, hash_tables, FALSE); - } + } - hash_free(&thd->handler_tables_hash); + my_hash_free(&thd->handler_tables_hash); DBUG_VOID_RETURN; } diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 2818aa5082c..003741a7ddc 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -431,7 +431,7 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3) field_list.push_back(new Item_empty_string("description",1000)); field_list.push_back(new Item_empty_string("example",1000)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); @@ -463,7 +463,7 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3) +- -+ RETURN VALUES - result of protocol->send_fields + result of protocol->send_result_set_metadata */ int send_header_2(Protocol *protocol, bool for_category) @@ -474,7 +474,7 @@ int send_header_2(Protocol *protocol, bool for_category) field_list.push_back(new Item_empty_string("source_category_name",64)); field_list.push_back(new Item_empty_string("name",64)); field_list.push_back(new Item_empty_string("is_it_category",1)); - DBUG_RETURN(protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + DBUG_RETURN(protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 1ef4d5827ec..f2f95dfb553 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -107,8 +107,8 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); 1 Error */ -bool check_view_single_update(List<Item> &fields, TABLE_LIST *view, - table_map *map) +bool check_view_single_update(List<Item> &fields, List<Item> *values, + TABLE_LIST *view, table_map *map) { /* it is join view => we need to find the table for update */ List_iterator_fast<Item> it(fields); @@ -119,6 +119,17 @@ bool check_view_single_update(List<Item> &fields, TABLE_LIST *view, while ((item= it++)) tables|= item->used_tables(); + if (values) + { + it.init(*values); + while ((item= it++)) + tables|= item->used_tables(); + } + + /* Convert to real table bits */ + tables&= ~PSEUDO_TABLE_BITS; + + /* Check found map against provided map */ if (*map) { @@ -165,7 +176,9 @@ error: static int check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, List<Item> &values, - bool check_unique, table_map *map) + bool check_unique, + bool fields_and_values_from_different_maps, + table_map *map) { TABLE *table= table_list->table; @@ -238,7 +251,10 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (table_list->effective_algorithm == VIEW_ALGORITHM_MERGE) { - if (check_view_single_update(fields, table_list, map)) + if (check_view_single_update(fields, + fields_and_values_from_different_maps ? + (List<Item>*) 0 : &values, + table_list, map)) return -1; table= table_list->table; } @@ -298,7 +314,8 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, */ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, - List<Item> &update_fields, table_map *map) + List<Item> &update_fields, + List<Item> &update_values, table_map *map) { TABLE *table= insert_table_list->table; my_bool timestamp_mark= 0; @@ -318,7 +335,8 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, return -1; if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE && - check_view_single_update(update_fields, insert_table_list, map)) + check_view_single_update(update_fields, &update_values, + insert_table_list, map)) return -1; if (table->timestamp_field) @@ -1242,9 +1260,9 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, table_list->next_local= 0; context->resolve_in_table_list_only(table_list); - res= check_insert_fields(thd, context->table_list, fields, *values, - !insert_into_view, &map) || - setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0); + res= (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0) || + check_insert_fields(thd, context->table_list, fields, *values, + !insert_into_view, 0, &map)); if (!res && check_fields) { @@ -1257,18 +1275,19 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, thd->abort_on_warning= saved_abort_on_warning; } + if (!res) + res= setup_fields(thd, 0, update_values, MARK_COLUMNS_READ, 0, 0); + if (!res && duplic == DUP_UPDATE) { select_lex->no_wrap_view_item= TRUE; - res= check_update_fields(thd, context->table_list, update_fields, &map); + res= check_update_fields(thd, context->table_list, update_fields, + update_values, &map); select_lex->no_wrap_view_item= FALSE; } /* Restore the current context. */ ctx_state.restore_state(context, table_list); - - if (!res) - res= setup_fields(thd, 0, update_values, MARK_COLUMNS_READ, 0, 0); } if (res) @@ -2729,6 +2748,12 @@ bool Delayed_insert::handle_inserts(void) thread_safe_increment(delayed_insert_writes,&LOCK_delayed_status); pthread_mutex_lock(&mutex); + /* + Reset the table->auto_increment_field_not_null as it is valid for + only one row. + */ + table->auto_increment_field_not_null= FALSE; + delete row; /* Let READ clients do something once in a while @@ -2931,9 +2956,9 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) we are fixing fields from insert list. */ lex->current_select= &lex->select_lex; - res= check_insert_fields(thd, table_list, *fields, values, - !insert_into_view, &map) || - setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, 0); + res= (setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, 0) || + check_insert_fields(thd, table_list, *fields, values, + !insert_into_view, 1, &map)); if (!res && fields->elements) { @@ -2960,7 +2985,8 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) lex->select_lex.no_wrap_view_item= TRUE; res= res || check_update_fields(thd, context->table_list, - *info.update_fields, &map); + *info.update_fields, *info.update_values, + &map); lex->select_lex.no_wrap_view_item= FALSE; /* When we are not using GROUP BY and there are no ungrouped aggregate functions @@ -3837,6 +3863,7 @@ void select_create::abort() { table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); + table->auto_increment_field_not_null= FALSE; if (!create_info->table_existed) drop_open_table(thd, table, create_table->db, create_table->table_name); table=0; // Safety diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 2adbc44eb12..f6dd1fae90a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -31,7 +31,14 @@ sys_var *trg_new_row_fake_var= (sys_var*) 0x01; +/** + LEX_STRING constant for null-string to be used in parser and other places. +*/ +const LEX_STRING null_lex_str= {NULL, 0}; +const LEX_STRING empty_lex_str= { (char*) "", 0 }; + /* Longest standard keyword name */ + #define TOCK_NAME_LENGTH 24 /* @@ -134,6 +141,7 @@ Lex_input_stream::Lex_input_stream(THD *thd, found_semicolon(NULL), ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)), stmt_prepare_mode(FALSE), + multi_statements(TRUE), in_comment(NO_COMMENT), m_underscore_cs(NULL) { @@ -1497,7 +1505,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) keys_onoff(rhs.keys_onoff), tablespace_op(rhs.tablespace_op), partition_names(rhs.partition_names, mem_root), - no_parts(rhs.no_parts), + num_parts(rhs.num_parts), change_level(rhs.change_level), datetime_field(rhs.datetime_field), error_if_not_empty(rhs.error_if_not_empty) @@ -2091,7 +2099,7 @@ void st_select_lex::print_limit(THD *thd, to implement the clean up. */ -void st_lex::cleanup_lex_after_parse_error(THD *thd) +void LEX::cleanup_lex_after_parse_error(THD *thd) { /* Delete sphead for the side effect of restoring of the original @@ -2150,7 +2158,7 @@ void Query_tables_list::reset_query_tables_list(bool init) We delay real initialization of hash (and therefore related memory allocation) until first insertion into this hash. */ - hash_clear(&sroutines); + my_hash_clear(&sroutines); } else if (sroutines.records) { @@ -2173,7 +2181,7 @@ void Query_tables_list::reset_query_tables_list(bool init) void Query_tables_list::destroy_query_tables_list() { - hash_free(&sroutines); + my_hash_free(&sroutines); } @@ -2181,7 +2189,7 @@ void Query_tables_list::destroy_query_tables_list() Initialize LEX object. SYNOPSIS - st_lex::st_lex() + LEX::LEX() NOTE LEX object initialized with this constructor can be used as part of @@ -2191,7 +2199,7 @@ void Query_tables_list::destroy_query_tables_list() for this. */ -st_lex::st_lex() +LEX::LEX() :result(0), sql_command(SQLCOM_END), option_type(OPT_DEFAULT), is_lex_started(0) { @@ -2208,7 +2216,7 @@ st_lex::st_lex() Check whether the merging algorithm can be used on this VIEW SYNOPSIS - st_lex::can_be_merged() + LEX::can_be_merged() DESCRIPTION We can apply merge algorithm if it is single SELECT view with @@ -2222,7 +2230,7 @@ st_lex::st_lex() TRUE - merge algorithm can be used */ -bool st_lex::can_be_merged() +bool LEX::can_be_merged() { // TODO: do not forget implement case when select_lex.table_list.elements==0 @@ -2259,19 +2267,19 @@ bool st_lex::can_be_merged() check if command can use VIEW with MERGE algorithm (for top VIEWs) SYNOPSIS - st_lex::can_use_merged() + LEX::can_use_merged() DESCRIPTION Only listed here commands can use merge algorithm in top level SELECT_LEX (for subqueries will be used merge algorithm if - st_lex::can_not_use_merged() is not TRUE). + LEX::can_not_use_merged() is not TRUE). RETURN FALSE - command can't use merged VIEWs TRUE - VIEWs with MERGE algorithms can be used */ -bool st_lex::can_use_merged() +bool LEX::can_use_merged() { switch (sql_command) { @@ -2296,18 +2304,18 @@ bool st_lex::can_use_merged() Check if command can't use merged views in any part of command SYNOPSIS - st_lex::can_not_use_merged() + LEX::can_not_use_merged() DESCRIPTION Temporary table algorithm will be used on all SELECT levels for queries - listed here (see also st_lex::can_use_merged()). + listed here (see also LEX::can_use_merged()). RETURN FALSE - command can't use merged VIEWs TRUE - VIEWs with MERGE algorithms can be used */ -bool st_lex::can_not_use_merged() +bool LEX::can_not_use_merged() { switch (sql_command) { @@ -2336,7 +2344,7 @@ bool st_lex::can_not_use_merged() FALSE no, we need data */ -bool st_lex::only_view_structure() +bool LEX::only_view_structure() { switch (sql_command) { case SQLCOM_SHOW_CREATE: @@ -2365,7 +2373,7 @@ bool st_lex::only_view_structure() */ -bool st_lex::need_correct_ident() +bool LEX::need_correct_ident() { switch(sql_command) { @@ -2395,7 +2403,7 @@ bool st_lex::need_correct_ident() VIEW_CHECK_CASCADED CHECK OPTION CASCADED */ -uint8 st_lex::get_effective_with_check(TABLE_LIST *view) +uint8 LEX::get_effective_with_check(TABLE_LIST *view) { if (view->select_lex->master_unit() == &unit && which_check_option_applicable()) @@ -2424,7 +2432,7 @@ uint8 st_lex::get_effective_with_check(TABLE_LIST *view) */ bool -st_lex::copy_db_to(char **p_db, size_t *p_db_length) const +LEX::copy_db_to(char **p_db, size_t *p_db_length) const { if (sphead) { @@ -2501,7 +2509,7 @@ void st_select_lex_unit::set_limit(st_select_lex *sl) clause. */ -void st_lex::set_trg_event_type_for_tables() +void LEX::set_trg_event_type_for_tables() { uint8 new_trg_event_map= 0; @@ -2644,7 +2652,7 @@ void st_lex::set_trg_event_type_for_tables() In this case link_to_local is set. */ -TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local) +TABLE_LIST *LEX::unlink_first_table(bool *link_to_local) { TABLE_LIST *first; if ((first= query_tables)) @@ -2684,7 +2692,7 @@ TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local) table list SYNOPSYS - st_lex::first_lists_tables_same() + LEX::first_lists_tables_same() NOTES In many cases (for example, usual INSERT/DELETE/...) the first table of @@ -2695,7 +2703,7 @@ TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local) the global list first. */ -void st_lex::first_lists_tables_same() +void LEX::first_lists_tables_same() { TABLE_LIST *first_table= (TABLE_LIST*) select_lex.table_list.first; if (query_tables != first_table && first_table != 0) @@ -2731,7 +2739,7 @@ void st_lex::first_lists_tables_same() global list */ -void st_lex::link_first_table_back(TABLE_LIST *first, +void LEX::link_first_table_back(TABLE_LIST *first, bool link_to_local) { if (first) @@ -2758,7 +2766,7 @@ void st_lex::link_first_table_back(TABLE_LIST *first, cleanup lex for case when we open table by table for processing SYNOPSIS - st_lex::cleanup_after_one_table_open() + LEX::cleanup_after_one_table_open() NOTE This method is mostly responsible for cleaning up of selects lists and @@ -2766,7 +2774,7 @@ void st_lex::link_first_table_back(TABLE_LIST *first, to call Query_tables_list::reset_query_tables_list(FALSE). */ -void st_lex::cleanup_after_one_table_open() +void LEX::cleanup_after_one_table_open() { /* thd->lex->derived_tables & additional units may be set if we open @@ -2801,7 +2809,7 @@ void st_lex::cleanup_after_one_table_open() backup Pointer to Query_tables_list instance to be used for backup */ -void st_lex::reset_n_backup_query_tables_list(Query_tables_list *backup) +void LEX::reset_n_backup_query_tables_list(Query_tables_list *backup) { backup->set_query_tables_list(this); /* @@ -2820,7 +2828,7 @@ void st_lex::reset_n_backup_query_tables_list(Query_tables_list *backup) backup Pointer to Query_tables_list instance used for backup */ -void st_lex::restore_backup_query_tables_list(Query_tables_list *backup) +void LEX::restore_backup_query_tables_list(Query_tables_list *backup) { this->destroy_query_tables_list(); this->set_query_tables_list(backup); @@ -2831,14 +2839,14 @@ void st_lex::restore_backup_query_tables_list(Query_tables_list *backup) Checks for usage of routines and/or tables in a parsed statement SYNOPSIS - st_lex:table_or_sp_used() + LEX:table_or_sp_used() RETURN FALSE No routines and tables used TRUE Either or both routines and tables are used. */ -bool st_lex::table_or_sp_used() +bool LEX::table_or_sp_used() { DBUG_ENTER("table_or_sp_used"); @@ -2999,7 +3007,7 @@ bool st_select_lex::add_index_hint (THD *thd, char *str, uint length) @retval FALSE No, not a management partition command */ -bool st_lex::is_partition_management() const +bool LEX::is_partition_management() const { return (sql_command == SQLCOM_ALTER_TABLE && (alter_info.flags == ALTER_ADD_PARTITION || diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 05a4d1d78bf..4bf8cd41aee 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -97,7 +97,7 @@ enum enum_sql_command { SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI, SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO, SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, - SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, + SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL, @@ -400,7 +400,7 @@ public: Base class for st_select_lex (SELECT_LEX) & st_select_lex_unit (SELECT_LEX_UNIT) */ -struct st_lex; +struct LEX; class st_select_lex; class st_select_lex_unit; class st_select_lex_node { @@ -470,7 +470,7 @@ public: virtual void set_lock_for_tables(thr_lock_type lock_type) {} friend class st_select_lex_unit; - friend bool mysql_new_select(struct st_lex *lex, bool move_down); + friend bool mysql_new_select(LEX *lex, bool move_down); friend bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, uint flags); private: @@ -590,7 +590,7 @@ public: /* Saved values of the WHERE and HAVING clauses*/ Item::cond_result cond_value, having_value; /* point on lex in which it was created, used in view subquery detection */ - st_lex *parent_lex; + LEX *parent_lex; enum olap_type olap; /* FROM clause - points to the beginning of the TABLE_LIST::next_local list. */ SQL_LIST table_list; @@ -899,7 +899,7 @@ public: enum enum_enable_or_disable keys_onoff; enum tablespace_op_type tablespace_op; List<char> partition_names; - uint no_parts; + uint num_parts; enum_alter_table_change_level change_level; Create_field *datetime_field; bool error_if_not_empty; @@ -909,7 +909,7 @@ public: flags(0), keys_onoff(LEAVE_AS_IS), tablespace_op(NO_TABLESPACE_OP), - no_parts(0), + num_parts(0), change_level(ALTER_TABLE_METADATA_ONLY), datetime_field(NULL), error_if_not_empty(FALSE) @@ -924,7 +924,7 @@ public: flags= 0; keys_onoff= LEAVE_AS_IS; tablespace_op= NO_TABLESPACE_OP; - no_parts= 0; + num_parts= 0; partition_names.empty(); change_level= ALTER_TABLE_METADATA_ONLY; datetime_field= 0; @@ -956,6 +956,9 @@ extern sys_var *trg_new_row_fake_var; enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE, XA_SUSPEND, XA_FOR_MIGRATE}; +extern const LEX_STRING null_lex_str; +extern const LEX_STRING empty_lex_str; + /* Class representing list of all tables used by statement. @@ -964,7 +967,7 @@ enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE, stored functions/triggers to this list in order to pre-open and lock them. - Also used by st_lex::reset_n_backup/restore_backup_query_tables_list() + Also used by LEX::reset_n_backup/restore_backup_query_tables_list() methods to save and restore this information. */ @@ -1492,9 +1495,13 @@ public: /** TRUE if we're parsing a prepared statement: in this mode - we should allow placeholders and disallow multi-statements. + we should allow placeholders. */ bool stmt_prepare_mode; + /** + TRUE if we should allow multi-statements. + */ + bool multi_statements; /** State of the lexical analyser for comments. */ enum_comment_state in_comment; @@ -1553,7 +1560,7 @@ protected: Constructor. @param lex the LEX structure that represents parts of this statement. */ - Sql_statement(struct st_lex *lex) + Sql_statement(LEX *lex) : m_lex(lex) {} @@ -1578,12 +1585,12 @@ protected: with the minimum set of attributes, instead of a LEX structure that contains the collection of every possible attribute. */ - struct st_lex *m_lex; + LEX *m_lex; }; /* The state of the lex parsing. This is saved in the THD struct */ -typedef struct st_lex : public Query_tables_list +struct LEX: public Query_tables_list { SELECT_LEX_UNIT unit; /* most upper unit */ SELECT_LEX select_lex; /* first SELECT_LEX */ @@ -1833,9 +1840,9 @@ typedef struct st_lex : public Query_tables_list */ bool protect_against_global_read_lock; - st_lex(); + LEX(); - virtual ~st_lex() + virtual ~LEX() { destroy_query_tables_list(); plugin_unlock_list(NULL, (plugin_ref *)plugins.buffer, plugins.elements); @@ -1877,7 +1884,7 @@ typedef struct st_lex : public Query_tables_list Is this update command where 'WHITH CHECK OPTION' clause is important SYNOPSIS - st_lex::which_check_option_applicable() + LEX::which_check_option_applicable() RETURN TRUE have to take 'WHITH CHECK OPTION' clause into account @@ -1949,7 +1956,7 @@ typedef struct st_lex : public Query_tables_list } return FALSE; } -} LEX; +}; /** @@ -2040,7 +2047,7 @@ public: }; -struct st_lex_local: public st_lex +struct st_lex_local: public LEX { static void *operator new(size_t size) throw() { diff --git a/sql/sql_list.cc b/sql/sql_list.cc index 49b649133d0..a256212471d 100644 --- a/sql/sql_list.cc +++ b/sql/sql_list.cc @@ -18,7 +18,7 @@ #pragma implementation // gcc: Class implementation #endif -#include "mysql_priv.h" +#include "sql_list.h" list_node end_of_list; diff --git a/sql/sql_list.h b/sql/sql_list.h index 22df77afeb3..74f4cc0ec0d 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -15,11 +15,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "my_global.h" +#include "my_sys.h" +#include "m_string.h" /* for TRASH */ + #ifdef USE_PRAGMA_INTERFACE #pragma interface /* gcc class implementation */ #endif +void *sql_alloc(size_t); + /* mysql standard class memory allocator */ class Sql_alloc diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc index e6b12d0e38b..6b027c62b81 100644 --- a/sql/sql_locale.cc +++ b/sql/sql_locale.cc @@ -1733,6 +1733,24 @@ static TYPELIB my_locale_typelib_day_names_sr_RS = { array_elements(my_locale_day_names_sr_RS)-1, "", my_locale_day_names_sr_RS, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sr_RS = { array_elements(my_locale_ab_day_names_sr_RS)-1, "", my_locale_ab_day_names_sr_RS, NULL }; +MY_LOCALE my_locale_sr_YU /* Deprecated, use sr_RS instead */ +( + 48, + "sr_YU", + "Serbian - Yugoslavia", + FALSE, + &my_locale_typelib_month_names_sr_RS, + &my_locale_typelib_ab_month_names_sr_RS, + &my_locale_typelib_day_names_sr_RS, + &my_locale_typelib_ab_day_names_sr_RS, + 9, + 10, + '.', /* decimal point sr_RS */ + '\0', /* thousands_sep sr_RS */ + "\x80", /* grouping sr_RS */ + &global_errmsgs[sr_RS] +); + MY_LOCALE my_locale_sr_RS ( 48, @@ -3347,6 +3365,13 @@ MY_LOCALE *my_locales[]= }; +MY_LOCALE *my_locales_deprecated[]= +{ + &my_locale_sr_YU, + NULL +}; + + MY_LOCALE *my_locale_by_number(uint number) { MY_LOCALE *locale; @@ -3359,22 +3384,56 @@ MY_LOCALE *my_locale_by_number(uint number) } -MY_LOCALE *my_locale_by_name(const char *name) +static MY_LOCALE* +my_locale_by_name(MY_LOCALE** locales, const char *name) { MY_LOCALE **locale; - for (locale= my_locales; *locale != NULL; locale++) + for (locale= locales; *locale != NULL; locale++) { if (!my_strcasecmp(&my_charset_latin1, (*locale)->name, name)) - { - // Check that locale is on its correct position in the array - DBUG_ASSERT((*locale) == my_locales[(*locale)->number]); return *locale; - } } return NULL; } +MY_LOCALE *my_locale_by_name(const char *name) +{ + MY_LOCALE *locale; + + if ((locale= my_locale_by_name(my_locales, name))) + { + // Check that locale is on its correct position in the array + DBUG_ASSERT(locale == my_locales[locale->number]); + return locale; + } + else if ((locale= my_locale_by_name(my_locales_deprecated, name))) + { + THD *thd= current_thd; + /* + Replace the deprecated locale to the corresponding + 'fresh' locale with the same ID. + */ + locale= my_locales[locale->number]; + if (thd) + { + // Send a warning to the client + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), + name, locale->name); + } + else + { + // Send a warning to mysqld error log + sql_print_warning("The syntax '%s' is deprecated and will be removed. " + "Please use %s instead.", + name, locale->name); + } + } + return locale; +} + + void cleanup_errmsgs() { for (MY_LOCALE_ERRMSGS *msgs= global_errmsgs; msgs->language; msgs++) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d677414070f..b99ff1c99c5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -28,6 +28,7 @@ #include "sp_cache.h" #include "events.h" #include "sql_trigger.h" +#include "sql_prepare.h" #include "probes_mysql.h" /** @@ -46,7 +47,6 @@ "FUNCTION" : "PROCEDURE") static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); -static bool check_show_create_table_access(THD *thd, TABLE_LIST *table); const char *any_db="*any*"; // Special symbol for check_access @@ -301,7 +301,6 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_SLAVE_HOSTS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_BINLOG_EVENTS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_COLUMN_TYPES]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_STORAGE_ENGINES]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_AUTHORS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CONTRIBUTORS]= CF_STATUS_COMMAND; @@ -376,7 +375,7 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, Vio* save_vio; ulong save_client_capabilities; -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.start_new_query(); thd->profiling.set_query_source(init_command_var->value, init_command_var->value_length); @@ -404,7 +403,7 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, thd->client_capabilities= save_client_capabilities; thd->net.vio= save_vio; -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); #endif } @@ -454,7 +453,7 @@ static void handle_bootstrap_impl(THD *thd) /* purecov: begin tested */ if (net_realloc(&(thd->net), 2 * thd->net.max_packet)) { - net_end_statement(thd); + thd->protocol->end_statement(); bootstrap_error= 1; break; } @@ -480,7 +479,7 @@ static void handle_bootstrap_impl(THD *thd) QUERY_CACHE_FLAGS_SIZE); thd->set_query(query, length); DBUG_PRINT("query",("%-.4096s",thd->query)); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.start_new_query(); thd->profiling.set_query_source(thd->query, length); #endif @@ -495,9 +494,9 @@ static void handle_bootstrap_impl(THD *thd) close_thread_tables(thd); // Free tables bootstrap_error= thd->is_error(); - net_end_statement(thd); + thd->protocol->end_statement(); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); #endif @@ -590,7 +589,7 @@ static bool check_merge_table_access(THD *thd, char *db, tlist->db= db; /* purecov: inspected */ } error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, - table_list, UINT_MAX, FALSE); + table_list, FALSE, UINT_MAX, FALSE); } return error; } @@ -807,7 +806,7 @@ bool do_command(THD *thd) net_new_transaction(net); packet_length= my_net_read(net); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.start_new_query(); #endif if (packet_length == packet_error) @@ -820,7 +819,7 @@ bool do_command(THD *thd) /* The error must be set. */ DBUG_ASSERT(thd->is_error()); - net_end_statement(thd); + thd->protocol->end_statement(); if (net->error != 3) { @@ -867,7 +866,7 @@ bool do_command(THD *thd) return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1)); out: -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); #endif DBUG_RETURN(return_value); @@ -1224,7 +1223,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, general_log_write(thd, command, thd->query, thd->query_length); DBUG_PRINT("query",("%-.4096s",thd->query)); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.set_query_source(thd->query, thd->query_length); #endif @@ -1237,7 +1236,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { char *beginning_of_next_stmt= (char*) end_of_stmt; - net_end_statement(thd); + thd->protocol->end_statement(); query_cache_end_of_result(thd); /* Multiple queries exits, execute them individually @@ -1259,7 +1258,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, MYSQL_QUERY_DONE(thd->is_error()); } -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); thd->profiling.start_new_query("continuing"); thd->profiling.set_query_source(beginning_of_next_stmt, length); @@ -1335,7 +1334,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege, 0, 0, test(table_list.schema_table))) break; - if (check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0)) + if (check_grant(thd, SELECT_ACL, &table_list, TRUE, UINT_MAX, FALSE)) break; /* init structures for VIEW processing */ table_list.select_lex= &(thd->lex->select_lex); @@ -1631,7 +1630,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->transaction.stmt.reset(); - net_end_statement(thd); + thd->protocol->end_statement(); query_cache_end_of_result(thd); thd->proc_info= "closing tables"; @@ -1800,7 +1799,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, Mark this current profiling record to be discarded. We don't wish to have SHOW commands show up in profiling. */ -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.discard_current_query(); #endif break; @@ -2215,14 +2214,16 @@ mysql_execute_command(THD *thd) #endif case SQLCOM_SHOW_STATUS_PROC: case SQLCOM_SHOW_STATUS_FUNC: - if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))) + if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE))) res= execute_sqlcom_select(thd, all_tables); break; case SQLCOM_SHOW_STATUS: { system_status_var old_status_var= thd->status_var; thd->initial_status_var= &old_status_var; - if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))) + if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE))) res= execute_sqlcom_select(thd, all_tables); /* Don't log SHOW STATUS commands to slow query log */ thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | @@ -2252,18 +2253,22 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_STORAGE_ENGINES: case SQLCOM_SHOW_PROFILE: case SQLCOM_SELECT: + { thd->status_var.last_query_cost= 0.0; + + /* + lex->exchange != NULL implies SELECT .. INTO OUTFILE and this + requires FILE_ACL access. + */ + ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL : + SELECT_ACL; + if (all_tables) - { res= check_table_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : - SELECT_ACL, - all_tables, UINT_MAX, FALSE); - } + privileges_requested, + all_tables, FALSE, UINT_MAX, FALSE); else - res= check_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, - any_db, 0, 0, 0, 0); + res= check_access(thd, privileges_requested, any_db, 0, 0, 0, UINT_MAX); if (res) break; @@ -2274,7 +2279,8 @@ mysql_execute_command(THD *thd) res= execute_sqlcom_select(thd, all_tables); break; - case SQLCOM_PREPARE: + } +case SQLCOM_PREPARE: { mysql_sql_stmt_prepare(thd); break; @@ -2290,8 +2296,8 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_DO: - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || - open_and_lock_tables(thd, all_tables)) + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || open_and_lock_tables(thd, all_tables)) goto error; res= mysql_do(thd, *lex->insert_list); @@ -2355,7 +2361,7 @@ mysql_execute_command(THD *thd) } case SQLCOM_SHOW_PROFILES: { -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.discard_current_query(); res= thd->profiling.show_profiles(); if (res) @@ -2401,8 +2407,8 @@ mysql_execute_command(THD *thd) case SQLCOM_BACKUP_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || - check_global_access(thd, FILE_ACL)) + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_backup_table(thd, first_table); @@ -2413,8 +2419,8 @@ mysql_execute_command(THD *thd) case SQLCOM_RESTORE_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, INSERT_ACL, all_tables, UINT_MAX, FALSE) || - check_global_access(thd, FILE_ACL)) + if (check_table_access(thd, INSERT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_restore_table(thd, first_table); @@ -2513,7 +2519,7 @@ mysql_execute_command(THD *thd) test(first_table->schema_table))) goto error; /* purecov: inspected */ /* Check that the first table has CREATE privilege */ - if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0)) + if (check_grant(thd, CREATE_ACL, all_tables, FALSE, 1, FALSE)) goto error; pthread_mutex_lock(&LOCK_active_mi); @@ -2883,7 +2889,7 @@ end_with_restore_list: (TABLE_LIST *) create_info.merge_list.first)) goto error; /* purecov: inspected */ - if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0)) + if (check_grant(thd, priv_needed, all_tables, FALSE, UINT_MAX, FALSE)) goto error; if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) { // Rename of table @@ -2892,8 +2898,8 @@ end_with_restore_list: tmp_table.table_name= lex->name.str; tmp_table.db=select_lex->db; tmp_table.grant.privilege=priv; - if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0, - UINT_MAX, 0)) + if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE, + UINT_MAX, FALSE)) goto error; } @@ -2947,10 +2953,11 @@ end_with_restore_list: */ old_list= table[0]; new_list= table->next_local[0]; - if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, 0, 1, 0) || + if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) || (!test_all_bits(table->next_local->grant.privilege, INSERT_ACL | CREATE_ACL) && - check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0))) + check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, FALSE, 1, + FALSE))) goto error; } @@ -2981,11 +2988,75 @@ end_with_restore_list: goto error; #else { - /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ + /* + Access check: + SHOW CREATE TABLE require any privileges on the table level (ie + effecting all columns in the table). + SHOW CREATE VIEW require the SHOW_VIEW and SELECT ACLs on the table + level. + NOTE: SHOW_VIEW ACL is checked when the view is created. + */ + if (lex->only_view) + { + if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE)) + { + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), + "SHOW", thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, first_table->alias); + goto error; + } + + /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ first_table->skip_temporary= 1; - if (check_show_create_table_access(thd, first_table)) - goto error; + } + else + { + ulong save_priv; + + /* + If it is an INFORMATION_SCHEMA table, SELECT_ACL privilege is the + only privilege allowed. For any other privilege check_access() + reports an error. That's how internal implementation protects + INFORMATION_SCHEMA from updates. + + For ordinary tables any privilege from the SHOW_CREATE_TABLE_ACLS + set is sufficient. + */ + + ulong check_privs= test(first_table->schema_table) ? + SELECT_ACL : SHOW_CREATE_TABLE_ACLS; + + if (check_access(thd, check_privs, first_table->db, + &save_priv, FALSE, FALSE, + test(first_table->schema_table))) + goto error; + + /* + save_priv contains any privileges actually granted by check_access + (i.e. save_priv contains global (user- and database-level) + privileges). + + The fact that check_access() returned FALSE does not mean that + access is granted. We need to check if save_priv contains any + table-specific privilege. If not, we need to check table-level + privileges. + + If there are no global privileges and no table-level privileges, + access is denied. + */ + + if (!(save_priv & (SHOW_CREATE_TABLE_ACLS)) && + !has_any_table_level_privileges(thd, SHOW_CREATE_TABLE_ACLS, first_table)) + { + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), + "SHOW", thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, first_table->alias); + goto error; + } + } + + /* Access is granted. Execute the command. */ res= mysqld_show_create(thd, first_table); break; } @@ -2993,8 +3064,8 @@ end_with_restore_list: case SQLCOM_CHECKSUM: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, - UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, all_tables, + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ res = mysql_checksum_table(thd, first_table, &lex->check_opt); break; @@ -3003,7 +3074,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_repair_table(thd, first_table, &lex->check_opt); @@ -3022,8 +3093,8 @@ end_with_restore_list: case SQLCOM_CHECK: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, - UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, all_tables, + TRUE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_check_table(thd, first_table, &lex->check_opt); @@ -3035,7 +3106,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_analyze_table(thd, first_table, &lex->check_opt); @@ -3056,7 +3127,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? @@ -3429,7 +3500,7 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (!lex->drop_temporary) { - if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE)) + if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) goto error; @@ -3474,9 +3545,6 @@ end_with_restore_list: case SQLCOM_SHOW_PRIVILEGES: res= mysqld_show_privileges(thd); break; - case SQLCOM_SHOW_COLUMN_TYPES: - res= mysqld_show_column_types(thd); - break; case SQLCOM_SHOW_ENGINE_LOGS: #ifdef DONT_ALLOW_SHOW_COMMANDS my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), @@ -3537,8 +3605,8 @@ end_with_restore_list: if (lex->autocommit && end_active_trans(thd)) goto error; - if ((check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || - open_and_lock_tables(thd, all_tables))) + if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || open_and_lock_tables(thd, all_tables))) goto error; if (lex->one_shot_set && not_all_support_one_shot(lex_var_list)) { @@ -3592,7 +3660,7 @@ end_with_restore_list: if (end_active_trans(thd)) goto error; if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; if (lex->protect_against_global_read_lock && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) @@ -3988,7 +4056,7 @@ end_with_restore_list: else { if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL), - all_tables, 0, UINT_MAX, 0)) + all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* Conditionally writes to binlog */ res= mysql_table_grant(thd, all_tables, lex->users_list, @@ -4097,7 +4165,7 @@ end_with_restore_list: #endif case SQLCOM_HA_OPEN: DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; res= mysql_ha_open(thd, first_table, 0); break; @@ -4383,7 +4451,8 @@ create_sp_error: This will cache all SP and SF and open and lock all tables required for execution. */ - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE) || open_and_lock_tables(thd, all_tables)) goto error; @@ -4662,10 +4731,10 @@ create_sp_error: } break; } -#ifndef DBUG_OFF case SQLCOM_SHOW_PROC_CODE: case SQLCOM_SHOW_FUNC_CODE: { +#ifndef DBUG_OFF sp_head *sp; if (lex->sql_command == SQLCOM_SHOW_PROC_CODE) @@ -4682,8 +4751,12 @@ create_sp_error: goto error; } break; - } +#else + my_error(ER_FEATURE_DISABLED, MYF(0), + "SHOW PROCEDURE|FUNCTION CODE", "--with-debug"); + goto error; #endif // ifndef DBUG_OFF + } case SQLCOM_SHOW_CREATE_TRIGGER: { if (lex->spname->m_name.length > NAME_LEN) @@ -4711,8 +4784,8 @@ create_sp_error: } case SQLCOM_DROP_VIEW: { - if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE) || - end_active_trans(thd)) + if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || end_active_trans(thd)) goto error; /* Conditionally writes to binlog. */ res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); @@ -4922,7 +4995,7 @@ create_sp_error: res= mysql_xa_recover(thd); break; case SQLCOM_ALTER_TABLESPACE: - if (check_access(thd, ALTER_ACL, thd->db, 0, 1, 0, thd->db ? is_schema_db(thd->db) : 0)) + if (check_global_access(thd, CREATE_TABLESPACE_ACL)) break; if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info))) my_ok(thd); @@ -5161,7 +5234,7 @@ bool check_single_table_access(THD *thd, ulong privilege, (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && !(all_tables->view && all_tables->effective_algorithm == VIEW_ALGORITHM_TMPTABLE) && - check_grant(thd, privilege, all_tables, 0, 1, no_errors)) + check_grant(thd, privilege, all_tables, FALSE, 1, no_errors)) goto deny; thd->security_ctx= backup_ctx; @@ -5206,7 +5279,8 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) subselects_tables= subselects_tables->next_global; } if (subselects_tables && - (check_table_access(thd, SELECT_ACL, subselects_tables, UINT_MAX, FALSE))) + (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE, + UINT_MAX, FALSE))) return 1; } return 0; @@ -5214,46 +5288,54 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) /** - Get the user (global) and database privileges for all used tables. - - @param save_priv In this we store global and db level grants for the - table. Note that we don't store db level grants if the - global grants is enough to satisfy the request and the - global grants contains a SELECT grant. - - @note - The idea of EXTRA_ACL is that one will be granted access to the table if - one has the asked privilege on any column combination of the table; For - example to be able to check a table one needs to have SELECT privilege on - any column of the table. - - @retval - 0 ok - @retval - 1 If we can't get the privileges and we don't use table/column - grants. + @brief Compare requested privileges with the privileges acquired from the + User- and Db-tables. + @param thd Thread handler + @param want_access The requested access privileges. + @param db A pointer to the Db name. + @param[out] save_priv A pointer to the granted privileges will be stored. + @param dont_check_global_grants True if no global grants are checked. + @param no_error True if no errors should be sent to the client. + @param schema_db True if the db specified belongs to the meta data tables. + + 'save_priv' is used to save the User-table (global) and Db-table grants for + the supplied db name. Note that we don't store db level grants if the global + grants is enough to satisfy the request AND the global grants contains a + SELECT grant. + + A meta data table (from INFORMATION_SCHEMA) can always be accessed with + a SELECT_ACL. + + @see check_grant + + @return Status of denial of access by exclusive ACLs. + @retval FALSE Access can't exclusively be denied by Db- and User-table + access unless Column- and Table-grants are checked too. + @retval TRUE Access denied. */ + bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool dont_check_global_grants, bool no_errors, bool schema_db) { Security_context *sctx= thd->security_ctx; ulong db_access; + /* GRANT command: In case of database level grant the database name may be a pattern, in case of table|column level grant the database name can not be a pattern. We use 'dont_check_global_grants' as a flag to determine - if it's database level grant command + if it's database level grant command (see SQLCOM_GRANT case, mysql_execute_command() function) and set db_is_pattern according to 'dont_check_global_grants' value. */ - bool db_is_pattern= (test(want_access & GRANT_ACL) && - dont_check_global_grants); + bool db_is_pattern= ((want_access & GRANT_ACL) && dont_check_global_grants); ulong dummy; DBUG_ENTER("check_access"); DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu", db ? db : "", want_access, sctx->master_access)); + if (save_priv) *save_priv=0; else @@ -5271,8 +5353,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (schema_db) { - if ((!(sctx->master_access & FILE_ACL) && (want_access & FILE_ACL)) || - (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) + /* + We don't allow any simple privileges but SELECT_ACL or CREATE_VIEW_ACL + on the information_schema database. + */ + want_access &= ~SELECT_ACL; + if (want_access & DB_ACLS) { if (!no_errors) { @@ -5280,10 +5366,15 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, db_name); } + /* + Access denied; + [out] *save_privileges= 0 + */ DBUG_RETURN(TRUE); } else { + /* Access granted */ *save_priv= SELECT_ACL; DBUG_RETURN(FALSE); } @@ -5291,20 +5382,27 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if ((sctx->master_access & want_access) == want_access) { + /* get access for current db */ + db_access= sctx->db_access; /* - If we don't have a global SELECT privilege, we have to get the database - specific access rights to be able to handle queries of type + 1. If we don't have a global SELECT privilege, we have to get the + database specific access rights to be able to handle queries of type UPDATE t1 SET a=1 WHERE b > 0 + 2. Change db access if it isn't current db which is being addressed */ - db_access= sctx->db_access; if (!(sctx->master_access & SELECT_ACL) && (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))) db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); + + /* + The effective privileges are the union of the global privileges + and the the intersection of db- and host-privileges. + */ *save_priv=sctx->master_access | db_access; DBUG_RETURN(FALSE); } - if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) || + if (((want_access & ~sctx->master_access) & ~DB_ACLS) || (! db && dont_check_global_grants)) { // We can never grant this DBUG_PRINT("error",("No possible access")); @@ -5319,33 +5417,66 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, } if (db == any_db) - DBUG_RETURN(FALSE); // Allow select on anything + { + /* + Access granted; Allow select on *any* db. + [out] *save_privileges= 0 + */ + DBUG_RETURN(FALSE); + } if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))) db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); else db_access= sctx->db_access; - DBUG_PRINT("info",("db_access: %lu", db_access)); - /* Remove SHOW attribute and access rights we already have */ - want_access &= ~(sctx->master_access | EXTRA_ACL); DBUG_PRINT("info",("db_access: %lu want_access: %lu", db_access, want_access)); - db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access); - if (db_access == want_access || + /* + Save the union of User-table and the intersection between Db-table and + Host-table privileges. + */ + db_access= (db_access | sctx->master_access); + *save_priv= db_access; + + /* + We need to investigate column- and table access if all requested privileges + belongs to the bit set of . + */ + bool need_table_or_column_check= + (want_access & (TABLE_ACLS | PROC_ACLS | db_access)) == want_access; + + /* + Grant access if the requested access is in the intersection of + host- and db-privileges (as retrieved from the acl cache), + also grant access if all the requested privileges are in the union of + TABLES_ACLS and PROC_ACLS; see check_grant. + */ + if ( (db_access & want_access) == want_access || (!dont_check_global_grants && - !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS)))) - DBUG_RETURN(FALSE); /* Ok */ + need_table_or_column_check)) + { + /* + Ok; but need to check table- and column privileges. + [out] *save_privileges is (User-priv | (Db-priv & Host-priv)) + */ + DBUG_RETURN(FALSE); + } + /* + Access is denied; + [out] *save_privileges is (User-priv | (Db-priv & Host-priv)) + */ DBUG_PRINT("error",("Access denied")); if (!no_errors) my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, (db ? db : (thd->db ? thd->db : - "unknown"))); /* purecov: tested */ - DBUG_RETURN(TRUE); /* purecov: tested */ + "unknown"))); + DBUG_RETURN(TRUE); + } @@ -5391,14 +5522,20 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) DBUG_ASSERT(dst_table); - if (check_access(thd, SELECT_ACL | EXTRA_ACL, - dst_table->db, - &dst_table->grant.privilege, - FALSE, FALSE, + if (check_access(thd, SELECT_ACL, dst_table->db, + &dst_table->grant.privilege, FALSE, FALSE, test(dst_table->schema_table))) - return FALSE; + return TRUE; /* Access denied */ + + /* + Check_grant will grant access if there is any column privileges on + all of the tables thanks to the fourth parameter (bool show_table). + */ + if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE)) + return TRUE; /* Access denied */ - return (check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE)); + /* Access granted */ + return FALSE; } default: break; @@ -5408,30 +5545,46 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) } -/** - Check the privilege for all used tables. - @param thd Thread context - @param want_access Privileges requested - @param tables List of tables to be checked - @param number Check at most this number of tables. - @param no_errors FALSE/TRUE - report/don't report error to - the client (using my_error() call). +/** + @brief Check if the requested privileges exists in either User-, Host- or + Db-tables. + @param thd Thread context + @param want_access Privileges requested + @param tables List of tables to be compared against + @param no_errors Don't report error to the client (using my_error() call). + @param any_combination_of_privileges_will_do TRUE if any privileges on any + column combination is enough. + @param number Only the first 'number' tables in the linked list are + relevant. + + The suppled table list contains cached privileges. This functions calls the + help functions check_access and check_grant to verify the first three steps + in the privileges check queue: + 1. Global privileges + 2. OR (db privileges AND host privileges) + 3. OR table privileges + 4. OR column privileges (not checked by this function!) + 5. OR routine privileges (not checked by this function!) + + @see check_access + @see check_grant + + @note This functions assumes that 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). - @note - Table privileges are cached in the table list for GRANT checking. - This functions assumes that 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). - - @retval FALSE OK - @retval TRUE Access denied + @return + @retval FALSE OK + @retval TRUE Access denied; But column or routine privileges might need to + be checked also. */ bool -check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, - uint number, bool no_errors) +check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, + bool any_combination_of_privileges_will_do, + uint number, bool no_errors) { TABLE_LIST *org_tables= tables; TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); @@ -5442,22 +5595,31 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, the given table list refers to the list for prelocking (contains tables of other queries). For simple queries first_not_own_table is 0. */ - for (; i < number && tables != first_not_own_table; + for (; i < number && tables != first_not_own_table && tables; tables= tables->next_global, i++) { + ulong want_access= requirements; if (tables->security_ctx) sctx= tables->security_ctx; else sctx= backup_ctx; - if (tables->schema_table && - (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) + /* + Always allow SELECT on schema tables. This is done by removing the + required SELECT_ACL privilege in the want_access parameter. + Disallow any other DDL or DML operation on any schema table. + */ + if (tables->schema_table) { - if (!no_errors) - my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - sctx->priv_user, sctx->priv_host, - INFORMATION_SCHEMA_NAME.str); - return TRUE; + want_access &= ~SELECT_ACL; + if (want_access & DB_ACLS) + { + if (!no_errors) + my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), + sctx->priv_user, sctx->priv_host, + INFORMATION_SCHEMA_NAME.str); + goto deny; + } } /* Register access for view underlying table. @@ -5469,33 +5631,34 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, { if (check_show_access(thd, tables)) goto deny; - continue; } + DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0, + tables->view != 0)); if (tables->is_anonymous_derived_table() || - (tables->table && (int)tables->table->s->tmp_table)) + (tables->table && tables->table->s && + (int)tables->table->s->tmp_table)) continue; thd->security_ctx= sctx; - if ((sctx->master_access & want_access) == - (want_access & ~EXTRA_ACL) && - thd->db) + if ((sctx->master_access & want_access) == want_access && + thd->db) tables->grant.privilege= want_access; else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0) { if (check_access(thd, want_access, tables->get_db_name(), - &tables->grant.privilege, 0, no_errors, + &tables->grant.privilege, 0, no_errors, test(tables->schema_table))) goto deny; // Access denied } else if (check_access(thd, want_access, tables->get_db_name(), - &tables->grant.privilege, 0, no_errors, - test(tables->schema_table))) + &tables->grant.privilege, 0, no_errors, 0)) goto deny; } thd->security_ctx= backup_ctx; - return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, - test(want_access & EXTRA_ACL), number, no_errors); + return check_grant(thd,requirements,org_tables, + any_combination_of_privileges_will_do, + number, no_errors); deny: thd->security_ctx= backup_ctx; return TRUE; @@ -5583,7 +5746,7 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) if (!check_access(thd, access, table->db, &table->grant.privilege, 0, 1, test(table->schema_table)) && - !check_grant(thd, access, table, 0, 1, 1)) + !check_grant(thd, access, table, FALSE, 1, TRUE)) DBUG_RETURN(0); } } @@ -6049,7 +6212,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); - query_cache_abort(&thd->net); + query_cache_abort(&thd->query_cache_tls); } if (thd->lex->sphead) { @@ -6133,8 +6296,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, if (type_modifier & PRI_KEY_FLAG) { Key *key; - lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); - key= new Key(Key::PRIMARY, NullS, + lex->col_list.push_back(new Key_part_spec(*field_name, 0)); + key= new Key(Key::PRIMARY, null_lex_str, &default_key_create_info, 0, lex->col_list); lex->alter_info.key_list.push_back(key); @@ -6143,8 +6306,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG)) { Key *key; - lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); - key= new Key(Key::UNIQUE, NullS, + lex->col_list.push_back(new Key_part_spec(*field_name, 0)); + key= new Key(Key::UNIQUE, null_lex_str, &default_key_create_info, 0, lex->col_list); lex->alter_info.key_list.push_back(key); @@ -6344,7 +6507,19 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, if (!ptr->derived && !my_strcasecmp(system_charset_info, ptr->db, INFORMATION_SCHEMA_NAME.str)) { - ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name); + ST_SCHEMA_TABLE *schema_table; + if (ptr->updating && + /* Special cases which are processed by commands itself */ + lex->sql_command != SQLCOM_CHECK && + lex->sql_command != SQLCOM_CHECKSUM) + { + my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), + thd->security_ctx->priv_user, + thd->security_ctx->priv_host, + INFORMATION_SCHEMA_NAME.str); + DBUG_RETURN(0); + } + schema_table= find_schema_table(thd, ptr->table_name); if (!schema_table || (schema_table->hidden && ((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 || @@ -7277,11 +7452,11 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) else if ((check_access(thd, UPDATE_ACL, table->db, &table->grant.privilege, 0, 1, test(table->schema_table)) || - check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) && + check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) && (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0, test(table->schema_table)) || - check_grant(thd, SELECT_ACL, table, 0, 1, 0))) + check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE))) DBUG_RETURN(TRUE); table->table_in_first_from_clause= 1; @@ -7299,7 +7474,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) if (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0, test(table->schema_table)) || - check_grant(thd, SELECT_ACL, table, 0, 1, 0)) + check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); } } @@ -7339,7 +7514,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) /* sql_yacc guarantees that tables and aux_tables are not zero */ DBUG_ASSERT(aux_tables != 0); - if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); /* @@ -7348,7 +7523,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) call check_table_access() safely. */ thd->lex->query_tables_own_last= 0; - if (check_table_access(thd, DELETE_ACL, aux_tables, UINT_MAX, FALSE)) + if (check_table_access(thd, DELETE_ACL, aux_tables, FALSE, UINT_MAX, FALSE)) { thd->lex->query_tables_own_last= save_query_tables_own_last; DBUG_RETURN(TRUE); @@ -7502,25 +7677,6 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) /** - @brief Check privileges for SHOW CREATE TABLE statement. - - @param thd Thread context - @param table Target table - - @retval TRUE Failure - @retval FALSE Success -*/ - -static bool check_show_create_table_access(THD *thd, TABLE_LIST *table) -{ - return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db, - &table->grant.privilege, 0, 0, - test(table->schema_table)) || - check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0); -} - - -/** CREATE TABLE query pre-check. @param thd Thread handler @@ -7559,7 +7715,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, lex->create_info.merge_list.first)) goto err; if (want_priv != CREATE_TMP_ACL && - check_grant(thd, want_priv, create_table, 0, 1, 0)) + check_grant(thd, want_priv, create_table, FALSE, 1, FALSE)) goto err; if (select_lex->item_list.elements) @@ -7587,12 +7743,13 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, } } #endif - if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) + if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE, + UINT_MAX, FALSE)) goto err; } else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) { - if (check_show_create_table_access(thd, tables)) + if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) goto err; } error= FALSE; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index eb711001ef6..978628aed41 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -1,4 +1,4 @@ -/* Copyright 2005-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright 2005-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,16 +18,29 @@ to partitioning introduced in MySQL version 5.1. It contains functionality used by all handlers that support partitioning, such as the partitioning handler itself and the NDB handler. + (Much of the code in this file has been split into partition_info.cc and + the header files partition_info.h + partition_element.h + sql_partition.h) - The first version was written by Mikael Ronstrom. + The first version was written by Mikael Ronstrom 2004-2006. + Various parts of the optimizer code was written by Sergey Petrunia. + Code have been maintained by Mattias Jonsson. + The second version was written by Mikael Ronstrom 2006-2007 with some + final fixes for partition pruning in 2008-2009 with assistance from Sergey + Petrunia and Mattias Jonsson. - This version supports RANGE partitioning, LIST partitioning, HASH + The first version supports RANGE partitioning, LIST partitioning, HASH partitioning and composite partitioning (hereafter called subpartitioning) where each RANGE/LIST partitioning is HASH partitioned. The hash function can either be supplied by the user or by only a list of fields (also called KEY partitioning), where the MySQL server will use an internal hash function. There are quite a few defaults that can be used as well. + + The second version introduces a new variant of RANGE and LIST partitioning + which is often referred to as column lists in the code variables. This + enables a user to specify a set of columns and their concatenated value + as the partition value. By comparing the concatenation of these values + the proper partition can be choosen. */ /* Some general useful functions */ @@ -54,9 +67,11 @@ const LEX_STRING partition_keywords[]= { C_STRING_WITH_LEN("LIST") }, { C_STRING_WITH_LEN("KEY") }, { C_STRING_WITH_LEN("MAXVALUE") }, - { C_STRING_WITH_LEN("LINEAR ") } + { C_STRING_WITH_LEN("LINEAR ") }, + { C_STRING_WITH_LEN(" COLUMNS") } }; static const char *part_str= "PARTITION"; +static const char *subpart_str= "SUBPARTITION"; static const char *sub_str= "SUB"; static const char *by_str= "BY"; static const char *space_str= " "; @@ -65,26 +80,23 @@ static const char *end_paren_str= ")"; static const char *begin_paren_str= "("; static const char *comma_str= ","; -static int get_part_id_charset_func_all(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -static int get_part_id_charset_func_part(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -static int get_part_id_charset_func_subpart(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -static int get_part_part_id_charset_func(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -static int get_subpart_id_charset_func(partition_info *part_info, - uint32 *part_id); +int get_partition_id_list_col(partition_info *part_info, + uint32 *part_id, + longlong *func_value); int get_partition_id_list(partition_info *part_info, uint32 *part_id, longlong *func_value); +int get_partition_id_range_col(partition_info *part_info, + uint32 *part_id, + longlong *func_value); int get_partition_id_range(partition_info *part_info, uint32 *part_id, longlong *func_value); +static int get_part_id_charset_func_part(partition_info *part_info, + uint32 *part_id, + longlong *func_value); +static int get_part_id_charset_func_subpart(partition_info *part_info, + uint32 *part_id); int get_partition_id_hash_nosub(partition_info *part_info, uint32 *part_id, longlong *func_value); @@ -97,30 +109,9 @@ int get_partition_id_linear_hash_nosub(partition_info *part_info, int get_partition_id_linear_key_nosub(partition_info *part_info, uint32 *part_id, longlong *func_value); -int get_partition_id_range_sub_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_range_sub_key(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_range_sub_linear_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_range_sub_linear_key(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_list_sub_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_list_sub_key(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_list_sub_linear_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_list_sub_linear_key(partition_info *part_info, - uint32 *part_id, - longlong *func_value); +int get_partition_id_with_sub(partition_info *part_info, + uint32 *part_id, + longlong *func_value); int get_partition_id_hash_sub(partition_info *part_info, uint32 *part_id); int get_partition_id_key_sub(partition_info *part_info, @@ -138,17 +129,64 @@ uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter); uint32 get_next_partition_id_list(PARTITION_ITERATOR* part_iter); int get_part_iter_for_interval_via_mapping(partition_info *part_info, bool is_subpart, + uint32 *store_length_array, uchar *min_value, uchar *max_value, + uint min_len, uint max_len, uint flags, PARTITION_ITERATOR *part_iter); +int get_part_iter_for_interval_cols_via_map(partition_info *part_info, + bool is_subpart, + uint32 *store_length_array, + uchar *min_value, uchar *max_value, + uint min_len, uint max_len, + uint flags, + PARTITION_ITERATOR *part_iter); int get_part_iter_for_interval_via_walking(partition_info *part_info, bool is_subpart, + uint32 *store_length_array, uchar *min_value, uchar *max_value, + uint min_len, uint max_len, uint flags, PARTITION_ITERATOR *part_iter); +static int cmp_rec_and_tuple(part_column_list_val *val, uint32 nvals_in_rec); +static int cmp_rec_and_tuple_prune(part_column_list_val *val, + uint32 n_vals_in_rec, + bool tail_is_min); #ifdef WITH_PARTITION_STORAGE_ENGINE /* + Convert constants in VALUES definition to the character set the + corresponding field uses. + + SYNOPSIS + convert_charset_partition_constant() + item Item to convert + cs Character set to convert to + + RETURN VALUE + NULL Error + item New converted item +*/ + +Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs) +{ + THD *thd= current_thd; + Name_resolution_context *context= &thd->lex->current_select->context; + TABLE_LIST *save_list= context->table_list; + const char *save_where= thd->where; + + item= item->safe_charset_converter(cs); + context->table_list= NULL; + thd->where= "convert character set partition constant"; + if (!item || item->fix_fields(thd, (Item**)NULL)) + item= NULL; + thd->where= save_where; + context->table_list= save_list; + return item; +} + + +/* A support function to check if a name is in a list of strings SYNOPSIS @@ -165,7 +203,7 @@ bool is_name_in_list(char *name, List<char> list_names) { List_iterator<char> names_it(list_names); - uint no_names= list_names.elements; + uint num_names= list_names.elements; uint i= 0; do @@ -173,7 +211,7 @@ bool is_name_in_list(char *name, char *list_name= names_it++; if (!(my_strcasecmp(system_charset_info, name, list_name))) return TRUE; - } while (++i < no_names); + } while (++i < num_names); return FALSE; } @@ -200,26 +238,26 @@ bool partition_default_handling(TABLE *table, partition_info *part_info, { DBUG_ENTER("partition_default_handling"); - if (part_info->use_default_no_partitions) + if (part_info->use_default_num_partitions) { if (!is_create_table_ind && - table->file->get_no_parts(normalized_path, &part_info->no_parts)) + table->file->get_no_parts(normalized_path, &part_info->num_parts)) { DBUG_RETURN(TRUE); } } else if (part_info->is_sub_partitioned() && - part_info->use_default_no_subpartitions) + part_info->use_default_num_subpartitions) { - uint no_parts; + uint num_parts; if (!is_create_table_ind && - (table->file->get_no_parts(normalized_path, &no_parts))) + (table->file->get_no_parts(normalized_path, &num_parts))) { DBUG_RETURN(TRUE); } - DBUG_ASSERT(part_info->no_parts > 0); - part_info->no_subparts= no_parts / part_info->no_parts; - DBUG_ASSERT((no_parts % part_info->no_parts) == 0); + DBUG_ASSERT(part_info->num_parts > 0); + part_info->num_subparts= num_parts / part_info->num_parts; + DBUG_ASSERT((num_parts % part_info->num_parts) == 0); } part_info->set_up_defaults_for_partitioning(table->file, (ulonglong)0, (uint)0); @@ -252,8 +290,8 @@ bool check_reorganise_list(partition_info *new_part_info, List<char> list_part_names) { uint new_count, old_count; - uint no_new_parts= new_part_info->partitions.elements; - uint no_old_parts= old_part_info->partitions.elements; + uint num_new_parts= new_part_info->partitions.elements; + uint num_old_parts= old_part_info->partitions.elements; List_iterator<partition_element> new_parts_it(new_part_info->partitions); bool same_part_info= (new_part_info == old_part_info); DBUG_ENTER("check_reorganise_list"); @@ -276,8 +314,8 @@ bool check_reorganise_list(partition_info *new_part_info, if (!is_name_in_list(old_name, list_part_names)) DBUG_RETURN(TRUE); } - } while (old_count < no_old_parts); - } while (new_count < no_new_parts); + } while (old_count < num_old_parts); + } while (new_count < num_new_parts); DBUG_RETURN(FALSE); } @@ -452,9 +490,10 @@ static bool set_up_field_array(TABLE *table, bool is_sub_part) { Field **ptr, *field, **field_array; - uint no_fields= 0; + uint num_fields= 0; uint size_field_array; uint i= 0; + uint inx; partition_info *part_info= table->part_info; int result= FALSE; DBUG_ENTER("set_up_field_array"); @@ -463,9 +502,19 @@ static bool set_up_field_array(TABLE *table, while ((field= *(ptr++))) { if (field->flags & GET_FIXED_FIELDS_FLAG) - no_fields++; + num_fields++; + } + if (num_fields > MAX_REF_PARTS) + { + char *ptr; + if (is_sub_part) + ptr= (char*)"subpartition function"; + else + ptr= (char*)"partition function"; + my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), ptr); + DBUG_RETURN(TRUE); } - if (no_fields == 0) + if (num_fields == 0) { /* We are using hidden key as partitioning field @@ -473,8 +522,8 @@ static bool set_up_field_array(TABLE *table, DBUG_ASSERT(!is_sub_part); DBUG_RETURN(result); } - size_field_array= (no_fields+1)*sizeof(Field*); - field_array= (Field**)sql_alloc(size_field_array); + size_field_array= (num_fields+1)*sizeof(Field*); + field_array= (Field**)sql_calloc(size_field_array); if (unlikely(!field_array)) { mem_alloc_error(size_field_array); @@ -489,7 +538,32 @@ static bool set_up_field_array(TABLE *table, field->flags|= FIELD_IN_PART_FUNC_FLAG; if (likely(!result)) { - field_array[i++]= field; + if (!is_sub_part && part_info->column_list) + { + List_iterator<char> it(part_info->part_field_list); + char *field_name; + + DBUG_ASSERT(num_fields == part_info->part_field_list.elements); + inx= 0; + do + { + field_name= it++; + if (!my_strcasecmp(system_charset_info, + field_name, + field->field_name)) + break; + } while (++inx < num_fields); + if (inx == num_fields) + { + mem_alloc_error(1); + result= TRUE; + continue; + } + } + else + inx= i; + field_array[inx]= field; + i++; /* We check that the fields are proper. It is required for each @@ -507,16 +581,16 @@ static bool set_up_field_array(TABLE *table, } } } - field_array[no_fields]= 0; + field_array[num_fields]= 0; if (!is_sub_part) { part_info->part_field_array= field_array; - part_info->no_part_fields= no_fields; + part_info->num_part_fields= num_fields; } else { part_info->subpart_field_array= field_array; - part_info->no_subpart_fields= no_fields; + part_info->num_subpart_fields= num_fields; } DBUG_RETURN(result); } @@ -555,36 +629,36 @@ static bool create_full_part_field_array(THD *thd, TABLE *table, if (!part_info->is_sub_partitioned()) { part_info->full_part_field_array= part_info->part_field_array; - part_info->no_full_part_fields= part_info->no_part_fields; + part_info->num_full_part_fields= part_info->num_part_fields; } else { Field *field, **field_array; - uint no_part_fields=0, size_field_array; + uint num_part_fields=0, size_field_array; ptr= table->field; while ((field= *(ptr++))) { if (field->flags & FIELD_IN_PART_FUNC_FLAG) - no_part_fields++; + num_part_fields++; } - size_field_array= (no_part_fields+1)*sizeof(Field*); - field_array= (Field**)sql_alloc(size_field_array); + size_field_array= (num_part_fields+1)*sizeof(Field*); + field_array= (Field**)sql_calloc(size_field_array); if (unlikely(!field_array)) { mem_alloc_error(size_field_array); result= TRUE; goto end; } - no_part_fields= 0; + num_part_fields= 0; ptr= table->field; while ((field= *(ptr++))) { if (field->flags & FIELD_IN_PART_FUNC_FLAG) - field_array[no_part_fields++]= field; + field_array[num_part_fields++]= field; } - field_array[no_part_fields]=0; + field_array[num_part_fields]=0; part_info->full_part_field_array= field_array; - part_info->no_full_part_fields= no_part_fields; + part_info->num_full_part_fields= num_part_fields; } /* @@ -780,16 +854,16 @@ static bool handle_list_of_fields(List_iterator<char> it, goto end; } } - if (is_list_empty) + if (is_list_empty && part_info->part_type == HASH_PARTITION) { uint primary_key= table->s->primary_key; if (primary_key != MAX_KEY) { - uint no_key_parts= table->key_info[primary_key].key_parts, i; + uint num_key_parts= table->key_info[primary_key].key_parts, i; /* In the case of an empty list we use primary key as partition key. */ - for (i= 0; i < no_key_parts; i++) + for (i= 0; i < num_key_parts; i++) { Field *field= table->key_info[primary_key].key_part[i].field; field->flags|= GET_FIXED_FIELDS_FLAG; @@ -853,7 +927,7 @@ int check_signed_flag(partition_info *part_info) error= ER_PARTITION_CONST_DOMAIN_ERROR; break; } - } while (++i < part_info->no_parts); + } while (++i < part_info->num_parts); } return error; } @@ -872,7 +946,6 @@ int check_signed_flag(partition_info *part_info) table The table object part_info Reference to partitioning data structure is_sub_part Is the table subpartitioned as well - is_field_to_be_setup Flag if we are to set-up field arrays RETURN VALUE TRUE An error occurred, something was wrong with the @@ -895,8 +968,8 @@ int check_signed_flag(partition_info *part_info) on the field object. */ -bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, - bool is_sub_part, bool is_field_to_be_setup) +static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, + bool is_sub_part) { partition_info *part_info= table->part_info; uint dir_length, home_dir_length; @@ -911,13 +984,6 @@ bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, bool save_use_only_table_context; DBUG_ENTER("fix_fields_part_func"); - if (part_info->fixed) - { - if (!(is_sub_part || (error= check_signed_flag(part_info)))) - result= FALSE; - goto end; - } - /* Set-up the TABLE_LIST object to be a list with a single table Set the object to zero to create NULL pointers and set alias @@ -989,8 +1055,7 @@ bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, if (unlikely(error)) { DBUG_PRINT("info", ("Field in partition function not part of table")); - if (is_field_to_be_setup) - clear_field_flag(table); + clear_field_flag(table); goto end; } thd->where= save_where; @@ -1002,11 +1067,7 @@ bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, } if ((!is_sub_part) && (error= check_signed_flag(part_info))) goto end; - result= FALSE; - if (is_field_to_be_setup) - result= set_up_field_array(table, is_sub_part); - if (!is_sub_part) - part_info->fixed= TRUE; + result= set_up_field_array(table, is_sub_part); end: table->get_fields_in_item_tree= FALSE; table->map= 0; //Restore old value @@ -1185,9 +1246,9 @@ void check_range_capable_PF(TABLE *table) static bool set_up_partition_bitmap(THD *thd, partition_info *part_info) { uint32 *bitmap_buf; - uint bitmap_bits= part_info->no_subparts? - (part_info->no_subparts* part_info->no_parts): - part_info->no_parts; + uint bitmap_bits= part_info->num_subparts? + (part_info->num_subparts* part_info->num_parts): + part_info->num_parts; uint bitmap_bytes= bitmap_buffer_size(bitmap_bits); DBUG_ENTER("set_up_partition_bitmap"); @@ -1287,64 +1348,47 @@ static void set_up_partition_func_pointers(partition_info *part_info) if (part_info->is_sub_partitioned()) { + part_info->get_partition_id= get_partition_id_with_sub; if (part_info->part_type == RANGE_PARTITION) { - part_info->get_part_partition_id= get_partition_id_range; + if (part_info->column_list) + part_info->get_part_partition_id= get_partition_id_range_col; + else + part_info->get_part_partition_id= get_partition_id_range; if (part_info->list_of_subpart_fields) { if (part_info->linear_hash_ind) - { - part_info->get_partition_id= get_partition_id_range_sub_linear_key; part_info->get_subpartition_id= get_partition_id_linear_key_sub; - } else - { - part_info->get_partition_id= get_partition_id_range_sub_key; part_info->get_subpartition_id= get_partition_id_key_sub; - } } else { if (part_info->linear_hash_ind) - { - part_info->get_partition_id= get_partition_id_range_sub_linear_hash; part_info->get_subpartition_id= get_partition_id_linear_hash_sub; - } else - { - part_info->get_partition_id= get_partition_id_range_sub_hash; part_info->get_subpartition_id= get_partition_id_hash_sub; - } } } else /* LIST Partitioning */ { - part_info->get_part_partition_id= get_partition_id_list; + if (part_info->column_list) + part_info->get_part_partition_id= get_partition_id_list_col; + else + part_info->get_part_partition_id= get_partition_id_list; if (part_info->list_of_subpart_fields) { if (part_info->linear_hash_ind) - { - part_info->get_partition_id= get_partition_id_list_sub_linear_key; part_info->get_subpartition_id= get_partition_id_linear_key_sub; - } else - { - part_info->get_partition_id= get_partition_id_list_sub_key; part_info->get_subpartition_id= get_partition_id_key_sub; - } } else { if (part_info->linear_hash_ind) - { - part_info->get_partition_id= get_partition_id_list_sub_linear_hash; part_info->get_subpartition_id= get_partition_id_linear_hash_sub; - } else - { - part_info->get_partition_id= get_partition_id_list_sub_hash; part_info->get_subpartition_id= get_partition_id_hash_sub; - } } } } @@ -1353,9 +1397,19 @@ static void set_up_partition_func_pointers(partition_info *part_info) part_info->get_part_partition_id= NULL; part_info->get_subpartition_id= NULL; if (part_info->part_type == RANGE_PARTITION) - part_info->get_partition_id= get_partition_id_range; + { + if (part_info->column_list) + part_info->get_partition_id= get_partition_id_range_col; + else + part_info->get_partition_id= get_partition_id_range; + } else if (part_info->part_type == LIST_PARTITION) - part_info->get_partition_id= get_partition_id_list; + { + if (part_info->column_list) + part_info->get_partition_id= get_partition_id_list_col; + else + part_info->get_partition_id= get_partition_id_list; + } else /* HASH partitioning */ { if (part_info->list_of_part_fields) @@ -1374,32 +1428,43 @@ static void set_up_partition_func_pointers(partition_info *part_info) } } } - if (part_info->full_part_charset_field_array) - { - DBUG_ASSERT(part_info->get_partition_id); - part_info->get_partition_id_charset= part_info->get_partition_id; - if (part_info->part_charset_field_array && - part_info->subpart_charset_field_array) - part_info->get_partition_id= get_part_id_charset_func_all; - else if (part_info->part_charset_field_array) - part_info->get_partition_id= get_part_id_charset_func_part; - else - part_info->get_partition_id= get_part_id_charset_func_subpart; - } - if (part_info->part_charset_field_array && - part_info->is_sub_partitioned()) + /* + We need special functions to handle character sets since they require copy + of field pointers and restore afterwards. For subpartitioned tables we do + the copy and restore individually on the part and subpart parts. For non- + subpartitioned tables we use the same functions as used for the parts part + of subpartioning. + Thus for subpartitioned tables the get_partition_id is always + get_partition_id_with_sub, even when character sets exists. + */ + if (part_info->part_charset_field_array) { - DBUG_ASSERT(part_info->get_part_partition_id); - part_info->get_part_partition_id_charset= + if (part_info->is_sub_partitioned()) + { + DBUG_ASSERT(part_info->get_part_partition_id); + if (!part_info->column_list) + { + part_info->get_part_partition_id_charset= part_info->get_part_partition_id; - part_info->get_part_partition_id= get_part_part_id_charset_func; + part_info->get_part_partition_id= get_part_id_charset_func_part; + } + } + else + { + DBUG_ASSERT(part_info->get_partition_id); + if (!part_info->column_list) + { + part_info->get_part_partition_id_charset= part_info->get_partition_id; + part_info->get_part_partition_id= get_part_id_charset_func_part; + } + } } if (part_info->subpart_charset_field_array) { DBUG_ASSERT(part_info->get_subpartition_id); part_info->get_subpartition_id_charset= part_info->get_subpartition_id; - part_info->get_subpartition_id= get_subpart_id_charset_func; + part_info->get_subpartition_id= get_part_id_charset_func_subpart; } DBUG_VOID_RETURN; } @@ -1407,22 +1472,22 @@ static void set_up_partition_func_pointers(partition_info *part_info) /* For linear hashing we need a mask which is on the form 2**n - 1 where - 2**n >= no_parts. Thus if no_parts is 6 then mask is 2**3 - 1 = 8 - 1 = 7. + 2**n >= num_parts. Thus if num_parts is 6 then mask is 2**3 - 1 = 8 - 1 = 7. SYNOPSIS set_linear_hash_mask() part_info Reference to partitioning data structure - no_parts Number of parts in linear hash partitioning + num_parts Number of parts in linear hash partitioning RETURN VALUE NONE */ -void set_linear_hash_mask(partition_info *part_info, uint no_parts) +void set_linear_hash_mask(partition_info *part_info, uint num_parts) { uint mask; - for (mask= 1; mask < no_parts; mask<<=1) + for (mask= 1; mask < num_parts; mask<<=1) ; part_info->linear_hash_mask= mask - 1; } @@ -1436,7 +1501,7 @@ void set_linear_hash_mask(partition_info *part_info, uint no_parts) get_part_id_from_linear_hash() hash_value Hash value calculated by HASH function or KEY function mask Mask calculated previously by set_linear_hash_mask - no_parts Number of partitions in HASH partitioned part + num_parts Number of partitions in HASH partitioned part RETURN VALUE part_id The calculated partition identity (starting at 0) @@ -1449,11 +1514,11 @@ void set_linear_hash_mask(partition_info *part_info, uint no_parts) */ static uint32 get_part_id_from_linear_hash(longlong hash_value, uint mask, - uint no_parts) + uint num_parts) { uint32 part_id= (uint32)(hash_value & mask); - if (part_id >= no_parts) + if (part_id >= num_parts) { uint new_mask= ((mask + 1) >> 1) - 1; part_id= (uint32)(hash_value & new_mask); @@ -1597,7 +1662,7 @@ bool fix_partition_func(THD *thd, TABLE *table, function is correct. */ if (part_info->linear_hash_ind) - set_linear_hash_mask(part_info, part_info->no_subparts); + set_linear_hash_mask(part_info, part_info->num_subparts); if (part_info->list_of_subpart_fields) { List_iterator<char> it(part_info->subpart_field_list); @@ -1607,12 +1672,12 @@ bool fix_partition_func(THD *thd, TABLE *table, else { if (unlikely(fix_fields_part_func(thd, part_info->subpart_expr, - table, TRUE, TRUE))) + table, TRUE))) goto end; if (unlikely(part_info->subpart_expr->result_type() != INT_RESULT)) { - my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), - "SUBPARTITION"); + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0), + subpart_str); goto end; } } @@ -1625,7 +1690,7 @@ bool fix_partition_func(THD *thd, TABLE *table, if (part_info->part_type == HASH_PARTITION) { if (part_info->linear_hash_ind) - set_linear_hash_mask(part_info, part_info->no_parts); + set_linear_hash_mask(part_info, part_info->num_parts); if (part_info->list_of_part_fields) { List_iterator<char> it(part_info->part_field_list); @@ -1635,32 +1700,43 @@ bool fix_partition_func(THD *thd, TABLE *table, else { if (unlikely(fix_fields_part_func(thd, part_info->part_expr, - table, FALSE, TRUE))) + table, FALSE))) goto end; if (unlikely(part_info->part_expr->result_type() != INT_RESULT)) { - my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str); + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0), part_str); goto end; } part_info->part_result_type= INT_RESULT; } + part_info->fixed= TRUE; } else { const char *error_str; - if (unlikely(fix_fields_part_func(thd, part_info->part_expr, - table, FALSE, TRUE))) - goto end; + if (part_info->column_list) + { + List_iterator<char> it(part_info->part_field_list); + if (unlikely(handle_list_of_fields(it, table, part_info, FALSE))) + goto end; + } + else + { + if (unlikely(fix_fields_part_func(thd, part_info->part_expr, + table, FALSE))) + goto end; + } + part_info->fixed= TRUE; if (part_info->part_type == RANGE_PARTITION) { error_str= partition_keywords[PKW_RANGE].str; - if (unlikely(part_info->check_range_constants())) + if (unlikely(part_info->check_range_constants(thd))) goto end; } else if (part_info->part_type == LIST_PARTITION) { error_str= partition_keywords[PKW_LIST].str; - if (unlikely(part_info->check_list_constants())) + if (unlikely(part_info->check_list_constants(thd))) goto end; } else @@ -1669,12 +1745,13 @@ bool fix_partition_func(THD *thd, TABLE *table, my_error(ER_INCONSISTENT_PARTITION_INFO_ERROR, MYF(0)); goto end; } - if (unlikely(part_info->no_parts < 1)) + if (unlikely(part_info->num_parts < 1)) { my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_str); goto end; } - if (unlikely(part_info->part_expr->result_type() != INT_RESULT)) + if (unlikely(!part_info->column_list && + part_info->part_expr->result_type() != INT_RESULT)) { my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str); goto end; @@ -1682,7 +1759,8 @@ bool fix_partition_func(THD *thd, TABLE *table, } if (((part_info->part_type != HASH_PARTITION || part_info->list_of_part_fields == FALSE) && - check_part_func_fields(part_info->part_field_array, TRUE)) || + (!part_info->column_list && + check_part_func_fields(part_info->part_field_array, TRUE))) || (part_info->list_of_part_fields == FALSE && part_info->is_sub_partitioned() && check_part_func_fields(part_info->subpart_field_array, TRUE))) @@ -1705,6 +1783,11 @@ bool fix_partition_func(THD *thd, TABLE *table, my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); goto end; } + if (unlikely(part_info->check_partition_field_length())) + { + my_error(ER_PARTITION_FIELDS_TOO_LONG, MYF(0)); + goto end; + } check_range_capable_PF(table); set_up_partition_key_maps(table, part_info); set_up_partition_func_pointers(part_info); @@ -1727,9 +1810,9 @@ end: static int add_write(File fptr, const char *buf, uint len) { - uint len_written= my_write(fptr, (const uchar*)buf, len, MYF(0)); + uint ret_code= my_write(fptr, (const uchar*)buf, len, MYF(MY_FNABP)); - if (likely(len == len_written)) + if (likely(ret_code == 0)) return 0; else return 1; @@ -1778,14 +1861,8 @@ static int add_begin_parenthesis(File fptr) static int add_part_key_word(File fptr, const char *key_string) { int err= add_string(fptr, key_string); - err+= add_space(fptr); - return err + add_begin_parenthesis(fptr); -} - -static int add_hash(File fptr) -{ - return add_part_key_word(fptr, partition_keywords[PKW_HASH].str); + return err; } static int add_partition(File fptr) @@ -1816,16 +1893,16 @@ static int add_subpartition_by(File fptr) return err + add_partition_by(fptr); } -static int add_key_partition(File fptr, List<char> field_list) +static int add_part_field_list(File fptr, List<char> field_list) { - uint i, no_fields; - int err; + uint i, num_fields; + int err= 0; List_iterator<char> part_it(field_list); - err= add_part_key_word(fptr, partition_keywords[PKW_KEY].str); - no_fields= field_list.elements; + num_fields= field_list.elements; i= 0; - while (i < no_fields) + err+= add_begin_parenthesis(fptr); + while (i < num_fields) { const char *field_str= part_it++; String field_string("", 0, system_charset_info); @@ -1836,10 +1913,11 @@ static int add_key_partition(File fptr, List<char> field_list) strlen(field_str)); thd->options= save_options; err+= add_string_object(fptr, &field_string); - if (i != (no_fields-1)) + if (i != (num_fields-1)) err+= add_comma(fptr); i++; } + err+= add_end_parenthesis(fptr); return err; } @@ -1949,37 +2027,269 @@ static int add_partition_options(File fptr, partition_element *p_elem) return err + add_engine(fptr,p_elem->engine_type); } -static int add_partition_values(File fptr, partition_info *part_info, partition_element *p_elem) + +/* + Check partition fields for result type and if they need + to check the character set. + + SYNOPSIS + check_part_field() + sql_type Type provided by user + field_name Name of field, used for error handling + result_type Out value: Result type of field + need_cs_check Out value: Do we need character set check + + RETURN VALUES + TRUE Error + FALSE Ok +*/ + +static int check_part_field(enum_field_types sql_type, + const char *field_name, + Item_result *result_type, + bool *need_cs_check) +{ + if (sql_type >= MYSQL_TYPE_TINY_BLOB && + sql_type <= MYSQL_TYPE_BLOB) + { + my_error(ER_BLOB_FIELD_IN_PART_FUNC_ERROR, MYF(0)); + return TRUE; + } + switch (sql_type) + { + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + *result_type= INT_RESULT; + *need_cs_check= FALSE; + return FALSE; + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + *result_type= STRING_RESULT; + *need_cs_check= TRUE; + return FALSE; + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + *result_type= STRING_RESULT; + *need_cs_check= TRUE; + return FALSE; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + goto error; + default: + goto error; + } +error: + my_error(ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD, MYF(0), + field_name); + return TRUE; +} + + +/* + Find the given field's Create_field object using name of field + + SYNOPSIS + get_sql_field() + field_name Field name + alter_info Info from ALTER TABLE/CREATE TABLE + + RETURN VALUE + sql_field Object filled in by parser about field + NULL No field found +*/ + +static Create_field* get_sql_field(char *field_name, + Alter_info *alter_info) +{ + List_iterator<Create_field> it(alter_info->create_list); + Create_field *sql_field; + DBUG_ENTER("get_sql_field"); + + while ((sql_field= it++)) + { + if (!(my_strcasecmp(system_charset_info, + sql_field->field_name, + field_name))) + { + DBUG_RETURN(sql_field); + } + } + DBUG_RETURN(NULL); +} + + +static int add_column_list_values(File fptr, partition_info *part_info, + part_elem_value *list_value, + HA_CREATE_INFO *create_info, + Alter_info *alter_info) +{ + int err= 0; + uint i; + List_iterator<char> it(part_info->part_field_list); + uint num_elements= part_info->part_field_list.elements; + bool use_parenthesis= (part_info->part_type == LIST_PARTITION && + part_info->num_columns > 1U); + + if (use_parenthesis) + err+= add_begin_parenthesis(fptr); + for (i= 0; i < num_elements; i++) + { + part_column_list_val *col_val= &list_value->col_val_array[i]; + char *field_name= it++; + if (col_val->max_value) + err+= add_string(fptr, partition_keywords[PKW_MAXVALUE].str); + else if (col_val->null_value) + err+= add_string(fptr, "NULL"); + else + { + char buffer[MAX_KEY_LENGTH]; + String str(buffer, sizeof(buffer), &my_charset_bin); + Item *item_expr= col_val->item_expression; + if (item_expr->null_value) + err+= add_string(fptr, "NULL"); + else + { + String *res; + CHARSET_INFO *field_cs; + bool need_cs_check= FALSE; + Item_result result_type= STRING_RESULT; + + /* + This function is called at a very early stage, even before + we have prepared the sql_field objects. Thus we have to + find the proper sql_field object and get the character set + from that object. + */ + if (create_info) + { + Create_field *sql_field; + + if (!(sql_field= get_sql_field(field_name, + alter_info))) + { + my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0)); + return 1; + } + if (check_part_field(sql_field->sql_type, + sql_field->field_name, + &result_type, + &need_cs_check)) + return 1; + if (need_cs_check) + field_cs= get_sql_field_charset(sql_field, create_info); + else + field_cs= NULL; + } + else + { + Field *field= part_info->part_field_array[i]; + result_type= field->result_type(); + if (check_part_field(field->real_type(), + field->field_name, + &result_type, + &need_cs_check)) + return 1; + DBUG_ASSERT(result_type == field->result_type()); + if (need_cs_check) + field_cs= field->charset(); + else + field_cs= NULL; + } + if (result_type != item_expr->result_type()) + { + my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0)); + return 1; + } + if (field_cs && field_cs != item_expr->collation.collation) + { + if (!(item_expr= convert_charset_partition_constant(item_expr, + field_cs))) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + return 1; + } + } + { + String val_conv; + val_conv.set_charset(system_charset_info); + res= item_expr->val_str(&str); + if (get_cs_converted_part_value_from_string(current_thd, + item_expr, res, + &val_conv, field_cs, + (bool)(alter_info != NULL))) + return 1; + err+= add_string_object(fptr, &val_conv); + } + } + } + if (i != (num_elements - 1)) + err+= add_string(fptr, comma_str); + } + if (use_parenthesis) + err+= add_end_parenthesis(fptr); + return err; +} + +static int add_partition_values(File fptr, partition_info *part_info, + partition_element *p_elem, + HA_CREATE_INFO *create_info, + Alter_info *alter_info) { int err= 0; if (part_info->part_type == RANGE_PARTITION) { err+= add_string(fptr, " VALUES LESS THAN "); - if (!p_elem->max_value) + if (part_info->column_list) { + List_iterator<part_elem_value> list_val_it(p_elem->list_val_list); + part_elem_value *list_value= list_val_it++; err+= add_begin_parenthesis(fptr); - if (p_elem->signed_flag) - err+= add_int(fptr, p_elem->range_value); - else - err+= add_uint(fptr, p_elem->range_value); + err+= add_column_list_values(fptr, part_info, list_value, + create_info, alter_info); err+= add_end_parenthesis(fptr); } else - err+= add_string(fptr, partition_keywords[PKW_MAXVALUE].str); + { + if (!p_elem->max_value) + { + err+= add_begin_parenthesis(fptr); + if (p_elem->signed_flag) + err+= add_int(fptr, p_elem->range_value); + else + err+= add_uint(fptr, p_elem->range_value); + err+= add_end_parenthesis(fptr); + } + else + err+= add_string(fptr, partition_keywords[PKW_MAXVALUE].str); + } } else if (part_info->part_type == LIST_PARTITION) { uint i; List_iterator<part_elem_value> list_val_it(p_elem->list_val_list); err+= add_string(fptr, " VALUES IN "); - uint no_items= p_elem->list_val_list.elements; + uint num_items= p_elem->list_val_list.elements; err+= add_begin_parenthesis(fptr); if (p_elem->has_null_value) { err+= add_string(fptr, "NULL"); - if (no_items == 0) + if (num_items == 0) { err+= add_end_parenthesis(fptr); goto end; @@ -1991,13 +2301,19 @@ static int add_partition_values(File fptr, partition_info *part_info, partition_ { part_elem_value *list_value= list_val_it++; - if (!list_value->unsigned_flag) - err+= add_int(fptr, list_value->value); + if (part_info->column_list) + err+= add_column_list_values(fptr, part_info, list_value, + create_info, alter_info); else - err+= add_uint(fptr, list_value->value); - if (i != (no_items-1)) + { + if (!list_value->unsigned_flag) + err+= add_int(fptr, list_value->value); + else + err+= add_uint(fptr, list_value->value); + } + if (i != (num_items-1)) err+= add_comma(fptr); - } while (++i < no_items); + } while (++i < num_items); err+= add_end_parenthesis(fptr); } end: @@ -2016,6 +2332,8 @@ end: use_sql_alloc Allocate buffer from sql_alloc if true otherwise use my_malloc show_partition_options Should we display partition options + create_info Info generated by parser + alter_info Info generated by parser RETURN VALUES NULL error @@ -2044,9 +2362,11 @@ end: char *generate_partition_syntax(partition_info *part_info, uint *buf_length, bool use_sql_alloc, - bool show_partition_options) + bool show_partition_options, + HA_CREATE_INFO *create_info, + Alter_info *alter_info) { - uint i,j, tot_no_parts, no_subparts; + uint i,j, tot_num_parts, num_subparts; partition_element *part_elem; ulonglong buffer_length; char path[FN_REFLEN]; @@ -2077,9 +2397,12 @@ char *generate_partition_syntax(partition_info *part_info, if (part_info->linear_hash_ind) err+= add_string(fptr, partition_keywords[PKW_LINEAR].str); if (part_info->list_of_part_fields) - err+= add_key_partition(fptr, part_info->part_field_list); + { + err+= add_part_key_word(fptr, partition_keywords[PKW_KEY].str); + err+= add_part_field_list(fptr, part_info->part_field_list); + } else - err+= add_hash(fptr); + err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str); break; default: DBUG_ASSERT(0); @@ -2089,15 +2412,23 @@ char *generate_partition_syntax(partition_info *part_info, DBUG_RETURN(NULL); } if (part_info->part_expr) + { + err+= add_begin_parenthesis(fptr); err+= add_string_len(fptr, part_info->part_func_string, part_info->part_func_len); - err+= add_end_parenthesis(fptr); - if ((!part_info->use_default_no_partitions) && + err+= add_end_parenthesis(fptr); + } + else if (part_info->column_list) + { + err+= add_string(fptr, partition_keywords[PKW_COLUMNS].str); + err+= add_part_field_list(fptr, part_info->part_field_list); + } + if ((!part_info->use_default_num_partitions) && part_info->use_default_partitions) { err+= add_string(fptr, "\n"); err+= add_string(fptr, "PARTITIONS "); - err+= add_int(fptr, part_info->no_parts); + err+= add_int(fptr, part_info->num_parts); } if (part_info->is_sub_partitioned()) { @@ -2107,23 +2438,29 @@ char *generate_partition_syntax(partition_info *part_info, if (part_info->linear_hash_ind) err+= add_string(fptr, partition_keywords[PKW_LINEAR].str); if (part_info->list_of_subpart_fields) - err+= add_key_partition(fptr, part_info->subpart_field_list); + { + add_part_key_word(fptr, partition_keywords[PKW_KEY].str); + add_part_field_list(fptr, part_info->subpart_field_list); + } else - err+= add_hash(fptr); + err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str); if (part_info->subpart_expr) + { + err+= add_begin_parenthesis(fptr); err+= add_string_len(fptr, part_info->subpart_func_string, part_info->subpart_func_len); - err+= add_end_parenthesis(fptr); - if ((!part_info->use_default_no_subpartitions) && + err+= add_end_parenthesis(fptr); + } + if ((!part_info->use_default_num_subpartitions) && part_info->use_default_subpartitions) { err+= add_string(fptr, "\n"); err+= add_string(fptr, "SUBPARTITIONS "); - err+= add_int(fptr, part_info->no_subparts); + err+= add_int(fptr, part_info->num_subparts); } } - tot_no_parts= part_info->partitions.elements; - no_subparts= part_info->no_subparts; + tot_num_parts= part_info->partitions.elements; + num_subparts= part_info->num_subparts; if (!part_info->use_default_partitions) { @@ -2146,7 +2483,8 @@ char *generate_partition_syntax(partition_info *part_info, first= FALSE; err+= add_partition(fptr); err+= add_name_string(fptr, part_elem->partition_name); - err+= add_partition_values(fptr, part_info, part_elem); + err+= add_partition_values(fptr, part_info, part_elem, + create_info, alter_info); if (!part_info->is_sub_partitioned() || part_info->use_default_subpartitions) { @@ -2167,7 +2505,7 @@ char *generate_partition_syntax(partition_info *part_info, err+= add_name_string(fptr, part_elem->partition_name); if (show_partition_options) err+= add_partition_options(fptr, part_elem); - if (j != (no_subparts-1)) + if (j != (num_subparts-1)) { err+= add_comma(fptr); err+= add_string(fptr, "\n"); @@ -2176,12 +2514,12 @@ char *generate_partition_syntax(partition_info *part_info, } else err+= add_end_parenthesis(fptr); - } while (++j < no_subparts); + } while (++j < num_subparts); } } - if (i == (tot_no_parts-1)) + if (i == (tot_num_parts-1)) err+= add_end_parenthesis(fptr); - } while (++i < tot_no_parts); + } while (++i < tot_num_parts); } if (err) goto close_file; @@ -2328,14 +2666,14 @@ static uint32 calculate_key_value(Field **field_array) get_part_id_for_sub() loc_part_id Local partition id sub_part_id Subpartition id - no_subparts Number of subparts + num_subparts Number of subparts */ inline static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id, - uint no_subparts) + uint num_subparts) { - return (uint32)((loc_part_id * no_subparts) + sub_part_id); + return (uint32)((loc_part_id * num_subparts) + sub_part_id); } @@ -2344,7 +2682,7 @@ static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id, SYNOPSIS get_part_id_hash() - no_parts Number of hash partitions + num_parts Number of hash partitions part_expr Item tree of hash function out:part_id The returned partition id out:func_value Value of hash function @@ -2354,7 +2692,7 @@ static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id, FALSE Success */ -static int get_part_id_hash(uint no_parts, +static int get_part_id_hash(uint num_parts, Item *part_expr, uint32 *part_id, longlong *func_value) @@ -2365,7 +2703,7 @@ static int get_part_id_hash(uint no_parts, if (part_val_int(part_expr, func_value)) DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); - int_hash_id= *func_value % no_parts; + int_hash_id= *func_value % num_parts; *part_id= int_hash_id < 0 ? (uint32) -int_hash_id : (uint32) int_hash_id; DBUG_RETURN(FALSE); @@ -2379,7 +2717,7 @@ static int get_part_id_hash(uint no_parts, get_part_id_linear_hash() part_info A reference to the partition_info struct where all the desired information is given - no_parts Number of hash partitions + num_parts Number of hash partitions part_expr Item tree of hash function out:part_id The returned partition id out:func_value Value of hash function @@ -2390,7 +2728,7 @@ static int get_part_id_hash(uint no_parts, */ static int get_part_id_linear_hash(partition_info *part_info, - uint no_parts, + uint num_parts, Item *part_expr, uint32 *part_id, longlong *func_value) @@ -2402,7 +2740,7 @@ static int get_part_id_linear_hash(partition_info *part_info, *part_id= get_part_id_from_linear_hash(*func_value, part_info->linear_hash_mask, - no_parts); + num_parts); DBUG_RETURN(FALSE); } @@ -2413,7 +2751,7 @@ static int get_part_id_linear_hash(partition_info *part_info, SYNOPSIS get_part_id_key() field_array Array of fields for PARTTION KEY - no_parts Number of KEY partitions + num_parts Number of KEY partitions RETURN VALUE Calculated partition id @@ -2421,12 +2759,12 @@ static int get_part_id_linear_hash(partition_info *part_info, inline static uint32 get_part_id_key(Field **field_array, - uint no_parts, + uint num_parts, longlong *func_value) { DBUG_ENTER("get_part_id_key"); *func_value= calculate_key_value(field_array); - DBUG_RETURN((uint32) (*func_value % no_parts)); + DBUG_RETURN((uint32) (*func_value % num_parts)); } @@ -2438,7 +2776,7 @@ static uint32 get_part_id_key(Field **field_array, part_info A reference to the partition_info struct where all the desired information is given field_array Array of fields for PARTTION KEY - no_parts Number of KEY partitions + num_parts Number of KEY partitions RETURN VALUE Calculated partition id @@ -2447,15 +2785,15 @@ static uint32 get_part_id_key(Field **field_array, inline static uint32 get_part_id_linear_key(partition_info *part_info, Field **field_array, - uint no_parts, + uint num_parts, longlong *func_value) { - DBUG_ENTER("get_partition_id_linear_key"); + DBUG_ENTER("get_part_id_linear_key"); *func_value= calculate_key_value(field_array); DBUG_RETURN(get_part_id_from_linear_hash(*func_value, part_info->linear_hash_mask, - no_parts)); + num_parts)); } /* @@ -2489,7 +2827,8 @@ static void copy_to_part_field_buffers(Field **ptr, if (!field->maybe_null() || !field->is_null()) { CHARSET_INFO *cs= ((Field_str*)field)->charset(); - uint len= field->pack_length(); + uint max_len= field->pack_length(); + uint data_len= field->data_length(); uchar *field_buf= *field_bufs; /* We only use the field buffer for VARCHAR and CHAR strings @@ -2501,17 +2840,17 @@ static void copy_to_part_field_buffers(Field **ptr, if (field->type() == MYSQL_TYPE_VARCHAR) { uint len_bytes= ((Field_varstring*)field)->length_bytes; - my_strnxfrm(cs, field_buf + len_bytes, (len - len_bytes), - field->ptr + len_bytes, field->field_length); + my_strnxfrm(cs, field_buf + len_bytes, max_len, + field->ptr + len_bytes, data_len); if (len_bytes == 1) - *field_buf= (uchar) field->field_length; + *field_buf= (uchar) data_len; else - int2store(field_buf, field->field_length); + int2store(field_buf, data_len); } else { - my_strnxfrm(cs, field_buf, len, - field->ptr, field->field_length); + my_strnxfrm(cs, field_buf, max_len, + field->ptr, max_len); } field->ptr= field_buf; } @@ -2541,6 +2880,44 @@ static void restore_part_field_pointers(Field **ptr, uchar **restore_ptr) return; } +/* + This function is used to calculate the partition id where all partition + fields have been prepared to point to a record where the partition field + values are bound. + + SYNOPSIS + get_partition_id() + part_info A reference to the partition_info struct where all the + desired information is given + out:part_id The partition id is returned through this pointer + out:func_value Value of partition function (longlong) + + RETURN VALUE + part_id Partition id of partition that would contain + row with given values of PF-fields + HA_ERR_NO_PARTITION_FOUND The fields of the partition function didn't + fit into any partition and thus the values of + the PF-fields are not allowed. + + DESCRIPTION + A routine used from write_row, update_row and delete_row from any + handler supporting partitioning. It is also a support routine for + get_partition_set used to find the set of partitions needed to scan + for a certain index scan or full table scan. + + It is actually 9 different variants of this function which are called + through a function pointer. + + get_partition_id_list + get_partition_id_list_col + get_partition_id_range + get_partition_id_range_col + get_partition_id_hash_nosub + get_partition_id_key_nosub + get_partition_id_linear_hash_nosub + get_partition_id_linear_key_nosub + get_partition_id_with_sub +*/ /* This function is used to calculate the main partition to use in the case of @@ -2562,67 +2939,26 @@ static void restore_part_field_pointers(Field **ptr, uchar **restore_ptr) DESCRIPTION - It is actually 6 different variants of this function which are called + It is actually 8 different variants of this function which are called through a function pointer. get_partition_id_list + get_partition_id_list_col get_partition_id_range + get_partition_id_range_col get_partition_id_hash_nosub get_partition_id_key_nosub get_partition_id_linear_hash_nosub get_partition_id_linear_key_nosub */ -static int get_part_id_charset_func_subpart(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - int res; - copy_to_part_field_buffers(part_info->subpart_charset_field_array, - part_info->subpart_field_buffers, - part_info->restore_subpart_field_ptrs); - res= part_info->get_partition_id_charset(part_info, part_id, func_value); - restore_part_field_pointers(part_info->subpart_charset_field_array, - part_info->restore_subpart_field_ptrs); - return res; -} - - static int get_part_id_charset_func_part(partition_info *part_info, uint32 *part_id, longlong *func_value) { int res; - copy_to_part_field_buffers(part_info->part_charset_field_array, - part_info->part_field_buffers, - part_info->restore_part_field_ptrs); - res= part_info->get_partition_id_charset(part_info, part_id, func_value); - restore_part_field_pointers(part_info->part_charset_field_array, - part_info->restore_part_field_ptrs); - return res; -} - - -static int get_part_id_charset_func_all(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - int res; - copy_to_part_field_buffers(part_info->full_part_field_array, - part_info->full_part_field_buffers, - part_info->restore_full_part_field_ptrs); - res= part_info->get_partition_id_charset(part_info, part_id, func_value); - restore_part_field_pointers(part_info->full_part_field_array, - part_info->restore_full_part_field_ptrs); - return res; -} - + DBUG_ENTER("get_part_id_charset_func_part"); -static int get_part_part_id_charset_func(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - int res; copy_to_part_field_buffers(part_info->part_charset_field_array, part_info->part_field_buffers, part_info->restore_part_field_ptrs); @@ -2630,21 +2966,58 @@ static int get_part_part_id_charset_func(partition_info *part_info, part_id, func_value); restore_part_field_pointers(part_info->part_charset_field_array, part_info->restore_part_field_ptrs); - return res; + DBUG_RETURN(res); } -static int get_subpart_id_charset_func(partition_info *part_info, - uint32 *part_id) +static int get_part_id_charset_func_subpart(partition_info *part_info, + uint32 *part_id) { int res; + DBUG_ENTER("get_part_id_charset_func_subpart"); + copy_to_part_field_buffers(part_info->subpart_charset_field_array, part_info->subpart_field_buffers, part_info->restore_subpart_field_ptrs); res= part_info->get_subpartition_id_charset(part_info, part_id); restore_part_field_pointers(part_info->subpart_charset_field_array, part_info->restore_subpart_field_ptrs); - return res; + DBUG_RETURN(res); +} + +int get_partition_id_list_col(partition_info *part_info, + uint32 *part_id, + longlong *func_value) +{ + part_column_list_val *list_col_array= part_info->list_col_array; + uint num_columns= part_info->part_field_list.elements; + int list_index, cmp; + int min_list_index= 0; + int max_list_index= part_info->num_list_values - 1; + DBUG_ENTER("get_partition_id_list_col"); + + while (max_list_index >= min_list_index) + { + list_index= (max_list_index + min_list_index) >> 1; + cmp= cmp_rec_and_tuple(list_col_array + list_index*num_columns, + num_columns); + if (cmp > 0) + min_list_index= list_index + 1; + else if (cmp < 0) + { + if (!list_index) + goto notfound; + max_list_index= list_index - 1; + } + else + { + *part_id= (uint32)list_col_array[list_index].partition_id; + DBUG_RETURN(0); + } + } +notfound: + *part_id= 0; + DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); } @@ -2655,7 +3028,7 @@ int get_partition_id_list(partition_info *part_info, LIST_PART_ENTRY *list_array= part_info->list_array; int list_index; int min_list_index= 0; - int max_list_index= part_info->no_list_values - 1; + int max_list_index= part_info->num_list_values - 1; longlong part_func_value; int error= part_val_int(part_info->part_expr, &part_func_value); longlong list_value; @@ -2725,7 +3098,7 @@ notfound: index idx. The function returns first number idx, such that list_array[idx].list_value is NOT contained within the passed interval. - If all array elements are contained, part_info->no_list_values is + If all array elements are contained, part_info->num_list_values is returned. NOTE @@ -2739,6 +3112,44 @@ notfound: The edge of corresponding sub-array of part_info->list_array */ +uint32 get_partition_id_cols_list_for_endpoint(partition_info *part_info, + bool left_endpoint, + bool include_endpoint, + uint32 nparts) +{ + part_column_list_val *list_col_array= part_info->list_col_array; + uint num_columns= part_info->part_field_list.elements; + int list_index, cmp; + uint min_list_index= 0; + uint max_list_index= part_info->num_list_values - 1; + bool tailf= !(left_endpoint ^ include_endpoint); + DBUG_ENTER("get_partition_id_cols_list_for_endpoint"); + + do + { + list_index= (max_list_index + min_list_index) >> 1; + cmp= cmp_rec_and_tuple_prune(list_col_array + list_index*num_columns, + nparts, tailf); + if (cmp > 0) + min_list_index= list_index + 1; + else if (cmp < 0) + { + if (!list_index) + goto notfound; + max_list_index= list_index - 1; + } + else + { + DBUG_RETURN(list_index + test(!tailf)); + } + } while (max_list_index >= min_list_index); + if (cmp > 0) + list_index++; +notfound: + DBUG_RETURN(list_index); +} + + uint32 get_list_array_idx_for_endpoint_charset(partition_info *part_info, bool left_endpoint, bool include_endpoint) @@ -2760,7 +3171,7 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info, { LIST_PART_ENTRY *list_array= part_info->list_array; uint list_index; - uint min_list_index= 0, max_list_index= part_info->no_list_values - 1; + uint min_list_index= 0, max_list_index= part_info->num_list_values - 1; longlong list_value; /* Get the partitioning function value for the endpoint */ longlong part_func_value= @@ -2790,7 +3201,7 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info, if (unsigned_flag) part_func_value-= 0x8000000000000000ULL; - DBUG_ASSERT(part_info->no_list_values); + DBUG_ASSERT(part_info->num_list_values); do { list_index= (max_list_index + min_list_index) >> 1; @@ -2815,12 +3226,49 @@ notfound: } +int get_partition_id_range_col(partition_info *part_info, + uint32 *part_id, + longlong *func_value) +{ + part_column_list_val *range_col_array= part_info->range_col_array; + uint num_columns= part_info->part_field_list.elements; + uint max_partition= part_info->num_parts - 1; + uint min_part_id= 0; + uint max_part_id= max_partition; + uint loc_part_id; + DBUG_ENTER("get_partition_id_range_col"); + + while (max_part_id > min_part_id) + { + loc_part_id= (max_part_id + min_part_id + 1) >> 1; + if (cmp_rec_and_tuple(range_col_array + loc_part_id*num_columns, + num_columns) >= 0) + min_part_id= loc_part_id + 1; + else + max_part_id= loc_part_id - 1; + } + loc_part_id= max_part_id; + if (loc_part_id != max_partition) + if (cmp_rec_and_tuple(range_col_array + loc_part_id*num_columns, + num_columns) >= 0) + loc_part_id++; + *part_id= (uint32)loc_part_id; + if (loc_part_id == max_partition && + (cmp_rec_and_tuple(range_col_array + loc_part_id*num_columns, + num_columns) >= 0)) + DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); + + DBUG_PRINT("exit",("partition: %d", *part_id)); + DBUG_RETURN(0); +} + + int get_partition_id_range(partition_info *part_info, uint32 *part_id, longlong *func_value) { longlong *range_array= part_info->range_int_array; - uint max_partition= part_info->no_parts - 1; + uint max_partition= part_info->num_parts - 1; uint min_part_id= 0; uint max_part_id= max_partition; uint loc_part_id; @@ -2897,7 +3345,7 @@ int get_partition_id_range(partition_info *part_info, represented by range_int_array[idx] has EMPTY intersection with the passed interval. If the interval represented by the last array element has non-empty - intersection with the passed interval, part_info->no_parts is + intersection with the passed interval, part_info->num_parts is returned. RETURN @@ -2925,7 +3373,7 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info, bool include_endpoint) { longlong *range_array= part_info->range_int_array; - uint max_partition= part_info->no_parts - 1; + uint max_partition= part_info->num_parts - 1; uint min_part_id= 0, max_part_id= max_partition, loc_part_id; /* Get the partitioning function value for the endpoint */ longlong part_func_value= @@ -3008,7 +3456,7 @@ int get_partition_id_hash_nosub(partition_info *part_info, uint32 *part_id, longlong *func_value) { - return get_part_id_hash(part_info->no_parts, part_info->part_expr, + return get_part_id_hash(part_info->num_parts, part_info->part_expr, part_id, func_value); } @@ -3017,7 +3465,7 @@ int get_partition_id_linear_hash_nosub(partition_info *part_info, uint32 *part_id, longlong *func_value) { - return get_part_id_linear_hash(part_info, part_info->no_parts, + return get_part_id_linear_hash(part_info, part_info->num_parts, part_info->part_expr, part_id, func_value); } @@ -3027,232 +3475,44 @@ int get_partition_id_key_nosub(partition_info *part_info, longlong *func_value) { *part_id= get_part_id_key(part_info->part_field_array, - part_info->no_parts, func_value); + part_info->num_parts, func_value); return 0; } int get_partition_id_linear_key_nosub(partition_info *part_info, - uint32 *part_id, - longlong *func_value) + uint32 *part_id, + longlong *func_value) { *part_id= get_part_id_linear_key(part_info, part_info->part_field_array, - part_info->no_parts, func_value); + part_info->num_parts, func_value); return 0; } -int get_partition_id_range_sub_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - uint32 loc_part_id, sub_part_id; - uint no_subparts; - longlong local_func_value; - int error; - DBUG_ENTER("get_partition_id_range_sub_hash"); - LINT_INIT(loc_part_id); - LINT_INIT(sub_part_id); - - if (unlikely((error= get_partition_id_range(part_info, &loc_part_id, - func_value)))) - { - DBUG_RETURN(error); - } - no_subparts= part_info->no_subparts; - if (unlikely((error= get_part_id_hash(no_subparts, part_info->subpart_expr, - &sub_part_id, &local_func_value)))) - { - DBUG_RETURN(error); - } - - *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); - DBUG_RETURN(0); -} - - -int get_partition_id_range_sub_linear_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - uint32 loc_part_id, sub_part_id; - uint no_subparts; - longlong local_func_value; - int error; - DBUG_ENTER("get_partition_id_range_sub_linear_hash"); - LINT_INIT(loc_part_id); - LINT_INIT(sub_part_id); - - if (unlikely((error= get_partition_id_range(part_info, &loc_part_id, - func_value)))) - { - DBUG_RETURN(error); - } - no_subparts= part_info->no_subparts; - if (unlikely((error= get_part_id_linear_hash(part_info, no_subparts, - part_info->subpart_expr, - &sub_part_id, - &local_func_value)))) - { - DBUG_RETURN(error); - } - - *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); - DBUG_RETURN(0); -} - - -int get_partition_id_range_sub_key(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - uint32 loc_part_id, sub_part_id; - uint no_subparts; - longlong local_func_value; - int error; - DBUG_ENTER("get_partition_id_range_sub_key"); - LINT_INIT(loc_part_id); - - if (unlikely((error= get_partition_id_range(part_info, &loc_part_id, - func_value)))) - { - DBUG_RETURN(error); - } - no_subparts= part_info->no_subparts; - sub_part_id= get_part_id_key(part_info->subpart_field_array, - no_subparts, &local_func_value); - *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); - DBUG_RETURN(0); -} - - -int get_partition_id_range_sub_linear_key(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - uint32 loc_part_id, sub_part_id; - uint no_subparts; - longlong local_func_value; - int error; - DBUG_ENTER("get_partition_id_range_sub_linear_key"); - LINT_INIT(loc_part_id); - - if (unlikely((error= get_partition_id_range(part_info, &loc_part_id, - func_value)))) - { - DBUG_RETURN(error); - } - no_subparts= part_info->no_subparts; - sub_part_id= get_part_id_linear_key(part_info, - part_info->subpart_field_array, - no_subparts, &local_func_value); - *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); - DBUG_RETURN(0); -} - - -int get_partition_id_list_sub_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - uint32 loc_part_id, sub_part_id; - uint no_subparts; - longlong local_func_value; - int error; - DBUG_ENTER("get_partition_id_list_sub_hash"); - LINT_INIT(sub_part_id); - - if (unlikely((error= get_partition_id_list(part_info, &loc_part_id, - func_value)))) - { - DBUG_RETURN(error); - } - no_subparts= part_info->no_subparts; - if (unlikely((error= get_part_id_hash(no_subparts, part_info->subpart_expr, - &sub_part_id, &local_func_value)))) - { - DBUG_RETURN(error); - } - - *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); - DBUG_RETURN(0); -} - - -int get_partition_id_list_sub_linear_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - uint32 loc_part_id, sub_part_id; - uint no_subparts; - longlong local_func_value; - int error; - DBUG_ENTER("get_partition_id_list_sub_linear_hash"); - LINT_INIT(sub_part_id); - - if (unlikely((error= get_partition_id_list(part_info, &loc_part_id, - func_value)))) - { - DBUG_RETURN(error); - } - no_subparts= part_info->no_subparts; - if (unlikely((error= get_part_id_linear_hash(part_info, no_subparts, - part_info->subpart_expr, - &sub_part_id, - &local_func_value)))) - { - DBUG_RETURN(error); - } - - *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); - DBUG_RETURN(0); -} - - -int get_partition_id_list_sub_key(partition_info *part_info, - uint32 *part_id, - longlong *func_value) +int get_partition_id_with_sub(partition_info *part_info, + uint32 *part_id, + longlong *func_value) { uint32 loc_part_id, sub_part_id; - uint no_subparts; - longlong local_func_value; + uint num_subparts; int error; - DBUG_ENTER("get_partition_id_range_sub_key"); + DBUG_ENTER("get_partition_id_with_sub"); - if (unlikely((error= get_partition_id_list(part_info, &loc_part_id, - func_value)))) + if (unlikely((error= part_info->get_part_partition_id(part_info, + &loc_part_id, + func_value)))) { DBUG_RETURN(error); } - no_subparts= part_info->no_subparts; - sub_part_id= get_part_id_key(part_info->subpart_field_array, - no_subparts, &local_func_value); - *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); - DBUG_RETURN(0); -} - - -int get_partition_id_list_sub_linear_key(partition_info *part_info, - uint32 *part_id, - longlong *func_value) -{ - uint32 loc_part_id, sub_part_id; - uint no_subparts; - longlong local_func_value; - int error; - DBUG_ENTER("get_partition_id_list_sub_linear_key"); - - if (unlikely((error= get_partition_id_list(part_info, &loc_part_id, - func_value)))) + num_subparts= part_info->num_subparts; + if (unlikely((error= part_info->get_subpartition_id(part_info, + &sub_part_id)))) { DBUG_RETURN(error); - } - no_subparts= part_info->no_subparts; - sub_part_id= get_part_id_linear_key(part_info, - part_info->subpart_field_array, - no_subparts, &local_func_value); - *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); + } + *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, num_subparts); DBUG_RETURN(0); } @@ -3285,7 +3545,7 @@ int get_partition_id_hash_sub(partition_info *part_info, uint32 *part_id) { longlong func_value; - return get_part_id_hash(part_info->no_subparts, part_info->subpart_expr, + return get_part_id_hash(part_info->num_subparts, part_info->subpart_expr, part_id, &func_value); } @@ -3294,7 +3554,7 @@ int get_partition_id_linear_hash_sub(partition_info *part_info, uint32 *part_id) { longlong func_value; - return get_part_id_linear_hash(part_info, part_info->no_subparts, + return get_part_id_linear_hash(part_info, part_info->num_subparts, part_info->subpart_expr, part_id, &func_value); } @@ -3305,7 +3565,7 @@ int get_partition_id_key_sub(partition_info *part_info, { longlong func_value; *part_id= get_part_id_key(part_info->subpart_field_array, - part_info->no_subparts, &func_value); + part_info->num_subparts, &func_value); return FALSE; } @@ -3316,7 +3576,7 @@ int get_partition_id_linear_key_sub(partition_info *part_info, longlong func_value; *part_id= get_part_id_linear_key(part_info, part_info->subpart_field_array, - part_info->no_subparts, &func_value); + part_info->num_subparts, &func_value); return FALSE; } @@ -3615,16 +3875,16 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, const key_range *key_spec, part_id_range *part_spec) { partition_info *part_info= table->part_info; - uint no_parts= part_info->get_tot_partitions(); + uint num_parts= part_info->get_tot_partitions(); uint i, part_id; - uint sub_part= no_parts; - uint32 part_part= no_parts; + uint sub_part= num_parts; + uint32 part_part= num_parts; KEY *key_info= NULL; bool found_part_field= FALSE; DBUG_ENTER("get_partition_set"); part_spec->start_part= 0; - part_spec->end_part= no_parts - 1; + part_spec->end_part= num_parts - 1; if ((index < MAX_KEY) && key_spec->flag == (uint)HA_READ_KEY_EXACT && part_info->some_fields_in_PF.is_set(index)) @@ -3661,7 +3921,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, { if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part)) { - part_spec->start_part= no_parts; + part_spec->start_part= num_parts; DBUG_VOID_RETURN; } } @@ -3675,7 +3935,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, allowed values. Thus it is certain that the result of this scan will be empty. */ - part_spec->start_part= no_parts; + part_spec->start_part= num_parts; DBUG_VOID_RETURN; } } @@ -3713,7 +3973,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, { if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part)) { - part_spec->start_part= no_parts; + part_spec->start_part= num_parts; clear_indicator_in_key_fields(key_info); DBUG_VOID_RETURN; } @@ -3722,7 +3982,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, { if (get_part_id_from_key(table,buf,key_info,key_spec,&part_part)) { - part_spec->start_part= no_parts; + part_spec->start_part= num_parts; clear_indicator_in_key_fields(key_info); DBUG_VOID_RETURN; } @@ -3743,29 +4003,29 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, nothing or we have discovered a range of partitions with possible holes in it. We need a bitvector to further the work here. */ - if (!(part_part == no_parts && sub_part == no_parts)) + if (!(part_part == num_parts && sub_part == num_parts)) { /* We can only arrive here if we are using subpartitioning. */ - if (part_part != no_parts) + if (part_part != num_parts) { /* We know the top partition and need to scan all underlying subpartitions. This is a range without holes. */ - DBUG_ASSERT(sub_part == no_parts); - part_spec->start_part= part_part * part_info->no_subparts; - part_spec->end_part= part_spec->start_part+part_info->no_subparts - 1; + DBUG_ASSERT(sub_part == num_parts); + part_spec->start_part= part_part * part_info->num_subparts; + part_spec->end_part= part_spec->start_part+part_info->num_subparts - 1; } else { - DBUG_ASSERT(sub_part != no_parts); + DBUG_ASSERT(sub_part != num_parts); part_spec->start_part= sub_part; part_spec->end_part=sub_part+ - (part_info->no_subparts*(part_info->no_parts-1)); - for (i= 0, part_id= sub_part; i < part_info->no_parts; - i++, part_id+= part_info->no_subparts) + (part_info->num_subparts*(part_info->num_parts-1)); + for (i= 0, part_id= sub_part; i < part_info->num_parts; + i++, part_id+= part_info->num_subparts) ; //Set bit part_id in bit array } } @@ -3884,10 +4144,12 @@ bool mysql_unpack_partition(THD *thd, mem_alloc_error(sizeof(partition_info)); goto end; } - lex.part_info->part_state= part_state; - lex.part_info->part_state_len= part_state_len; + part_info= lex.part_info; + part_info->part_state= part_state; + part_info->part_state_len= part_state_len; DBUG_PRINT("info", ("Parse: %s", part_buf)); - if (parse_sql(thd, & parser_state, NULL)) + if (parse_sql(thd, & parser_state, NULL) || + part_info->fix_parser_data(thd)) { thd->free_items(); goto end; @@ -3908,7 +4170,6 @@ bool mysql_unpack_partition(THD *thd, */ DBUG_PRINT("info", ("Successful parse")); - part_info= lex.part_info; DBUG_PRINT("info", ("default engine = %s, default_db_type = %s", ha_resolve_storage_engine_name(part_info->default_engine_type), ha_resolve_storage_engine_name(default_db_type))); @@ -4029,9 +4290,9 @@ set_engine_all_partitions(partition_info *part_info, partition_element *sub_elem= sub_it++; sub_elem->engine_type= engine_type; - } while (++j < part_info->no_subparts); + } while (++j < part_info->num_subparts); } - } while (++i < part_info->no_parts); + } while (++i < part_info->num_parts); } /* SYNOPSIS @@ -4176,7 +4437,7 @@ uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info, enum partition_state part_state) { uint part_count= 0; - uint no_parts_found= 0; + uint num_parts_found= 0; List_iterator<partition_element> part_it(tab_part_info->partitions); do @@ -4191,15 +4452,15 @@ uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info, I.e mark the partition as a partition to be "changed" by analyzing/optimizing/rebuilding/checking/repairing/... */ - no_parts_found++; + num_parts_found++; part_elem->part_state= part_state; DBUG_PRINT("info", ("Setting part_state to %u for partition %s", part_state, part_elem->partition_name)); } else part_elem->part_state= PART_NORMAL; - } while (++part_count < tab_part_info->no_parts); - return no_parts_found; + } while (++part_count < tab_part_info->num_parts); + return num_parts_found; } @@ -4266,6 +4527,11 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, partition_info *tab_part_info= table->part_info; partition_info *alt_part_info= thd->work_part_info; uint flags= 0; + bool is_last_partition_reorged; + part_elem_value *tab_max_elem_val= NULL; + part_elem_value *alt_max_elem_val= NULL; + longlong tab_max_range= 0, alt_max_range= 0; + if (!tab_part_info) { my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0)); @@ -4275,13 +4541,13 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, { uint new_part_no, curr_part_no; if (tab_part_info->part_type != HASH_PARTITION || - tab_part_info->use_default_no_partitions) + tab_part_info->use_default_num_partitions) { my_error(ER_REORG_NO_PARAM_ERROR, MYF(0)); DBUG_RETURN(TRUE); } new_part_no= table->file->get_default_no_partitions(create_info); - curr_part_no= tab_part_info->no_parts; + curr_part_no= tab_part_info->num_parts; if (new_part_no == curr_part_no) { /* @@ -4299,7 +4565,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, setting the flag for no default number of partitions */ alter_info->flags|= ALTER_ADD_PARTITION; - thd->work_part_info->no_parts= new_part_no - curr_part_no; + thd->work_part_info->num_parts= new_part_no - curr_part_no; } else { @@ -4308,7 +4574,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, without setting the flag for no default number of partitions */ alter_info->flags|= ALTER_COALESCE_PARTITION; - alter_info->no_parts= curr_part_no - new_part_no; + alter_info->num_parts= curr_part_no - new_part_no; } } if (!(flags= table->file->alter_table_flags(alter_info->flags))) @@ -4320,34 +4586,73 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0); DBUG_PRINT("info", ("*fast_alter_partition: %d flags: 0x%x", *fast_alter_partition, flags)); - if (((alter_info->flags & ALTER_ADD_PARTITION) || - (alter_info->flags & ALTER_REORGANIZE_PARTITION)) && - (thd->work_part_info->part_type != tab_part_info->part_type) && - (thd->work_part_info->part_type != NOT_A_PARTITION)) + if ((alter_info->flags & ALTER_ADD_PARTITION) || + (alter_info->flags & ALTER_REORGANIZE_PARTITION)) { - if (thd->work_part_info->part_type == RANGE_PARTITION) + if (thd->work_part_info->part_type != tab_part_info->part_type) { - my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), - "RANGE", "LESS THAN"); - } - else if (thd->work_part_info->part_type == LIST_PARTITION) - { - DBUG_ASSERT(thd->work_part_info->part_type == LIST_PARTITION); - my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), - "LIST", "IN"); + if (thd->work_part_info->part_type == NOT_A_PARTITION) + { + if (tab_part_info->part_type == RANGE_PARTITION) + { + my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), "RANGE"); + DBUG_RETURN(TRUE); + } + else if (tab_part_info->part_type == LIST_PARTITION) + { + my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), "LIST"); + DBUG_RETURN(TRUE); + } + /* + Hash partitions can be altered without parser finds out about + that it is HASH partitioned. So no error here. + */ + } + else + { + if (thd->work_part_info->part_type == RANGE_PARTITION) + { + my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN"); + } + else if (thd->work_part_info->part_type == LIST_PARTITION) + { + DBUG_ASSERT(thd->work_part_info->part_type == LIST_PARTITION); + my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "LIST", "IN"); + } + else if (tab_part_info->part_type == RANGE_PARTITION) + { + my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN"); + } + else + { + DBUG_ASSERT(tab_part_info->part_type == LIST_PARTITION); + my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "LIST", "IN"); + } + DBUG_RETURN(TRUE); + } } - else if (tab_part_info->part_type == RANGE_PARTITION) + if ((tab_part_info->column_list && + alt_part_info->num_columns != tab_part_info->num_columns) || + (!tab_part_info->column_list && + (tab_part_info->part_type == RANGE_PARTITION || + tab_part_info->part_type == LIST_PARTITION) && + alt_part_info->num_columns != 1U) || + (!tab_part_info->column_list && + tab_part_info->part_type == HASH_PARTITION && + alt_part_info->num_columns != 0)) { - my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), - "RANGE", "LESS THAN"); + my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0)); + DBUG_RETURN(TRUE); } - else + alt_part_info->column_list= tab_part_info->column_list; + if (alt_part_info->fix_parser_data(thd)) { - DBUG_ASSERT(tab_part_info->part_type == LIST_PARTITION); - my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), - "LIST", "IN"); + DBUG_RETURN(TRUE); } - DBUG_RETURN(TRUE); } if (alter_info->flags & ALTER_ADD_PARTITION) { @@ -4357,9 +4662,9 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, partitioning scheme as currently set-up. Partitions are always added at the end in ADD PARTITION. */ - uint no_new_partitions= alt_part_info->no_parts; - uint no_orig_partitions= tab_part_info->no_parts; - uint check_total_partitions= no_new_partitions + no_orig_partitions; + uint num_new_partitions= alt_part_info->num_parts; + uint num_orig_partitions= tab_part_info->num_parts; + uint check_total_partitions= num_new_partitions + num_orig_partitions; uint new_total_partitions= check_total_partitions; /* We allow quite a lot of values to be supplied by defaults, however we @@ -4376,22 +4681,22 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0)); DBUG_RETURN(TRUE); } - if (no_new_partitions == 0) + if (num_new_partitions == 0) { my_error(ER_ADD_PARTITION_NO_NEW_PARTITION, MYF(0)); DBUG_RETURN(TRUE); } if (tab_part_info->is_sub_partitioned()) { - if (alt_part_info->no_subparts == 0) - alt_part_info->no_subparts= tab_part_info->no_subparts; - else if (alt_part_info->no_subparts != tab_part_info->no_subparts) + if (alt_part_info->num_subparts == 0) + alt_part_info->num_subparts= tab_part_info->num_subparts; + else if (alt_part_info->num_subparts != tab_part_info->num_subparts) { my_error(ER_ADD_PARTITION_SUBPART_ERROR, MYF(0)); DBUG_RETURN(TRUE); } check_total_partitions= new_total_partitions* - alt_part_info->no_subparts; + alt_part_info->num_subparts; } if (check_total_partitions > MAX_PARTITIONS) { @@ -4401,8 +4706,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, alt_part_info->part_type= tab_part_info->part_type; alt_part_info->subpart_type= tab_part_info->subpart_type; if (alt_part_info->set_up_defaults_for_partitioning(table->file, - ULL(0), - tab_part_info->no_parts)) + ULL(0), + tab_part_info->num_parts)) { DBUG_RETURN(TRUE); } @@ -4477,7 +4782,7 @@ that are reorganised. uint lower_2n= upper_2n >> 1; bool all_parts= TRUE; if (tab_part_info->linear_hash_ind && - no_new_partitions < upper_2n) + num_new_partitions < upper_2n) { /* An analysis of which parts needs reorganisation shows that it is @@ -4486,7 +4791,7 @@ that are reorganised. onwards it starts again from partition 0 and goes on until it reaches p(upper_2n - 1). If the last new partition reaches beyond upper_2n - 1 then the first interval will end with - p(lower_2n - 1) and start with p(no_orig_partitions - lower_2n). + p(lower_2n - 1) and start with p(num_orig_partitions - lower_2n). If lower_2n partitions are added then p0 to p(lower_2n - 1) will be reorganised which means that the two interval becomes one interval at this point. Thus only when adding less than @@ -4514,7 +4819,7 @@ that are reorganised. to TRUE. In this case we don't get into this if-part at all. */ all_parts= FALSE; - if (no_new_partitions >= lower_2n) + if (num_new_partitions >= lower_2n) { /* In this case there is only one interval since the two intervals @@ -4530,8 +4835,8 @@ that are reorganised. Also in this case there is only one interval since we are not going over a 2**n boundary */ - start_part= no_orig_partitions - lower_2n; - end_part= start_part + (no_new_partitions - 1); + start_part= num_orig_partitions - lower_2n; + end_part= start_part + (num_new_partitions - 1); } else { @@ -4540,7 +4845,7 @@ that are reorganised. new parts that would ensure that the intervals become overlapping. */ - start_part= no_orig_partitions - lower_2n; + start_part= num_orig_partitions - lower_2n; end_part= upper_2n - 1; start_sec_part= 0; end_sec_part= new_total_partitions - (upper_2n + 1); @@ -4557,7 +4862,7 @@ that are reorganised. { p_elem->part_state= PART_CHANGED; } - } while (++part_no < no_orig_partitions); + } while (++part_no < num_orig_partitions); } /* Need to concatenate the lists here to make it possible to check the @@ -4580,8 +4885,8 @@ that are reorganised. mem_alloc_error(1); DBUG_RETURN(TRUE); } - } while (++part_count < no_new_partitions); - tab_part_info->no_parts+= no_new_partitions; + } while (++part_count < num_new_partitions); + tab_part_info->num_parts+= num_new_partitions; } /* If we specify partitions explicitly we don't use defaults anymore. @@ -4596,7 +4901,7 @@ that are reorganised. DBUG_PRINT("info", ("part_info: 0x%lx", (long) tab_part_info)); tab_part_info->use_default_partitions= FALSE; } - tab_part_info->use_default_no_partitions= FALSE; + tab_part_info->use_default_num_partitions= FALSE; tab_part_info->is_auto_partitioned= FALSE; } } @@ -4610,8 +4915,8 @@ that are reorganised. command to drop the partition failed in the middle. */ uint part_count= 0; - uint no_parts_dropped= alter_info->partition_names.elements; - uint no_parts_found= 0; + uint num_parts_dropped= alter_info->partition_names.elements; + uint num_parts_found= 0; List_iterator<partition_element> part_it(tab_part_info->partitions); tab_part_info->is_auto_partitioned= FALSE; @@ -4621,7 +4926,7 @@ that are reorganised. my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP"); DBUG_RETURN(TRUE); } - if (no_parts_dropped >= tab_part_info->no_parts) + if (num_parts_dropped >= tab_part_info->num_parts) { my_error(ER_DROP_LAST_PARTITION, MYF(0)); DBUG_RETURN(TRUE); @@ -4635,11 +4940,11 @@ that are reorganised. /* Set state to indicate that the partition is to be dropped. */ - no_parts_found++; + num_parts_found++; part_elem->part_state= PART_TO_BE_DROPPED; } - } while (++part_count < tab_part_info->no_parts); - if (no_parts_found != no_parts_dropped) + } while (++part_count < tab_part_info->num_parts); + if (num_parts_found != num_parts_dropped) { my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "DROP"); DBUG_RETURN(TRUE); @@ -4649,14 +4954,14 @@ that are reorganised. my_error(ER_ROW_IS_REFERENCED, MYF(0)); DBUG_RETURN(TRUE); } - tab_part_info->no_parts-= no_parts_dropped; + tab_part_info->num_parts-= num_parts_dropped; } else if (alter_info->flags & ALTER_REBUILD_PARTITION) { - uint no_parts_found; - uint no_parts_opt= alter_info->partition_names.elements; - no_parts_found= set_part_state(alter_info, tab_part_info, PART_CHANGED); - if (no_parts_found != no_parts_opt && + uint num_parts_found; + uint num_parts_opt= alter_info->partition_names.elements; + num_parts_found= set_part_state(alter_info, tab_part_info, PART_CHANGED); + if (num_parts_found != num_parts_opt && (!(alter_info->flags & ALTER_ALL_PARTITION))) { my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REBUILD"); @@ -4670,20 +4975,20 @@ that are reorganised. } else if (alter_info->flags & ALTER_COALESCE_PARTITION) { - uint no_parts_coalesced= alter_info->no_parts; - uint no_parts_remain= tab_part_info->no_parts - no_parts_coalesced; + uint num_parts_coalesced= alter_info->num_parts; + uint num_parts_remain= tab_part_info->num_parts - num_parts_coalesced; List_iterator<partition_element> part_it(tab_part_info->partitions); if (tab_part_info->part_type != HASH_PARTITION) { my_error(ER_COALESCE_ONLY_ON_HASH_PARTITION, MYF(0)); DBUG_RETURN(TRUE); } - if (no_parts_coalesced == 0) + if (num_parts_coalesced == 0) { my_error(ER_COALESCE_PARTITION_NO_PARTITION, MYF(0)); DBUG_RETURN(TRUE); } - if (no_parts_coalesced >= tab_part_info->no_parts) + if (num_parts_coalesced >= tab_part_info->num_parts) { my_error(ER_DROP_LAST_PARTITION, MYF(0)); DBUG_RETURN(TRUE); @@ -4731,21 +5036,21 @@ state of p1. uint upper_2n= tab_part_info->linear_hash_mask + 1; uint lower_2n= upper_2n >> 1; all_parts= FALSE; - if (no_parts_coalesced >= lower_2n) + if (num_parts_coalesced >= lower_2n) { all_parts= TRUE; } - else if (no_parts_remain >= lower_2n) + else if (num_parts_remain >= lower_2n) { - end_part= tab_part_info->no_parts - (lower_2n + 1); - start_part= no_parts_remain - lower_2n; + end_part= tab_part_info->num_parts - (lower_2n + 1); + start_part= num_parts_remain - lower_2n; } else { start_part= 0; - end_part= tab_part_info->no_parts - (lower_2n + 1); + end_part= tab_part_info->num_parts - (lower_2n + 1); end_sec_part= (lower_2n >> 1) - 1; - start_sec_part= end_sec_part - (lower_2n - (no_parts_remain + 1)); + start_sec_part= end_sec_part - (lower_2n - (num_parts_remain + 1)); } } do @@ -4756,19 +5061,19 @@ state of p1. (part_count >= start_part && part_count <= end_part) || (part_count >= start_sec_part && part_count <= end_sec_part))) p_elem->part_state= PART_CHANGED; - if (++part_count > no_parts_remain) + if (++part_count > num_parts_remain) { if (*fast_alter_partition) p_elem->part_state= PART_REORGED_DROPPED; else part_it.remove(); } - } while (part_count < tab_part_info->no_parts); - tab_part_info->no_parts= no_parts_remain; + } while (part_count < tab_part_info->num_parts); + tab_part_info->num_parts= num_parts_remain; } if (!(alter_info->flags & ALTER_TABLE_REORG)) { - tab_part_info->use_default_no_partitions= FALSE; + tab_part_info->use_default_num_partitions= FALSE; tab_part_info->is_auto_partitioned= FALSE; } } @@ -4785,33 +5090,32 @@ state of p1. range as those changed from. This command can be used on RANGE and LIST partitions. */ - uint no_parts_reorged= alter_info->partition_names.elements; - uint no_parts_new= thd->work_part_info->partitions.elements; - partition_info *alt_part_info= thd->work_part_info; + uint num_parts_reorged= alter_info->partition_names.elements; + uint num_parts_new= thd->work_part_info->partitions.elements; uint check_total_partitions; tab_part_info->is_auto_partitioned= FALSE; - if (no_parts_reorged > tab_part_info->no_parts) + if (num_parts_reorged > tab_part_info->num_parts) { my_error(ER_REORG_PARTITION_NOT_EXIST, MYF(0)); DBUG_RETURN(TRUE); } if (!(tab_part_info->part_type == RANGE_PARTITION || tab_part_info->part_type == LIST_PARTITION) && - (no_parts_new != no_parts_reorged)) + (num_parts_new != num_parts_reorged)) { my_error(ER_REORG_HASH_ONLY_ON_SAME_NO, MYF(0)); DBUG_RETURN(TRUE); } if (tab_part_info->is_sub_partitioned() && - alt_part_info->no_subparts && - alt_part_info->no_subparts != tab_part_info->no_subparts) + alt_part_info->num_subparts && + alt_part_info->num_subparts != tab_part_info->num_subparts) { my_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR, MYF(0)); DBUG_RETURN(TRUE); } - check_total_partitions= tab_part_info->no_parts + no_parts_new; - check_total_partitions-= no_parts_reorged; + check_total_partitions= tab_part_info->num_parts + num_parts_new; + check_total_partitions-= num_parts_reorged; if (check_total_partitions > MAX_PARTITIONS) { my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); @@ -4819,7 +5123,7 @@ state of p1. } alt_part_info->part_type= tab_part_info->part_type; alt_part_info->subpart_type= tab_part_info->subpart_type; - alt_part_info->no_subparts= tab_part_info->no_subparts; + alt_part_info->num_subparts= tab_part_info->num_subparts; DBUG_ASSERT(!alt_part_info->use_default_partitions); if (alt_part_info->set_up_defaults_for_partitioning(table->file, ULL(0), @@ -4867,9 +5171,7 @@ the generated partition syntax in a correct manner. uint part_count= 0; bool found_first= FALSE; bool found_last= FALSE; - bool is_last_partition_reorged; uint drop_count= 0; - longlong tab_max_range= 0, alt_max_range= 0; do { partition_element *part_elem= tab_it++; @@ -4879,7 +5181,13 @@ the generated partition syntax in a correct manner. { is_last_partition_reorged= TRUE; drop_count++; - tab_max_range= part_elem->range_value; + if (tab_part_info->column_list) + { + List_iterator<part_elem_value> p(part_elem->list_val_list); + tab_max_elem_val= p++; + } + else + tab_max_range= part_elem->range_value; if (*fast_alter_partition && tab_part_info->temp_partitions.push_back(part_elem)) { @@ -4891,20 +5199,28 @@ the generated partition syntax in a correct manner. if (!found_first) { uint alt_part_count= 0; - found_first= TRUE; + partition_element *alt_part_elem; List_iterator<partition_element> alt_it(alt_part_info->partitions); + found_first= TRUE; do { - partition_element *alt_part_elem= alt_it++; - alt_max_range= alt_part_elem->range_value; + alt_part_elem= alt_it++; + if (tab_part_info->column_list) + { + List_iterator<part_elem_value> p(alt_part_elem->list_val_list); + alt_max_elem_val= p++; + } + else + alt_max_range= alt_part_elem->range_value; + if (*fast_alter_partition) alt_part_elem->part_state= PART_TO_BE_ADDED; if (alt_part_count == 0) tab_it.replace(alt_part_elem); else tab_it.after(alt_part_elem); - } while (++alt_part_count < no_parts_new); + } while (++alt_part_count < num_parts_new); } else if (found_last) { @@ -4919,32 +5235,13 @@ the generated partition syntax in a correct manner. if (found_first) found_last= TRUE; } - } while (++part_count < tab_part_info->no_parts); - if (drop_count != no_parts_reorged) + } while (++part_count < tab_part_info->num_parts); + if (drop_count != num_parts_reorged) { my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REORGANIZE"); DBUG_RETURN(TRUE); } - if (tab_part_info->part_type == RANGE_PARTITION && - ((is_last_partition_reorged && - alt_max_range < tab_max_range) || - (!is_last_partition_reorged && - alt_max_range != tab_max_range))) - { - /* - For range partitioning the total resulting range before and - after the change must be the same except in one case. This is - when the last partition is reorganised, in this case it is - acceptable to increase the total range. - The reason is that it is not allowed to have "holes" in the - middle of the ranges and thus we should not allow to reorganise - to create "holes". Also we should not allow using REORGANIZE - to drop data. - */ - my_error(ER_REORG_OUTSIDE_RANGE, MYF(0)); - DBUG_RETURN(TRUE); - } - tab_part_info->no_parts= check_total_partitions; + tab_part_info->num_parts= check_total_partitions; } } else @@ -4960,11 +5257,43 @@ the generated partition syntax in a correct manner. !alt_part_info->use_default_subpartitions) { tab_part_info->use_default_subpartitions= FALSE; - tab_part_info->use_default_no_subpartitions= FALSE; + tab_part_info->use_default_num_subpartitions= FALSE; } if (tab_part_info->check_partition_info(thd, (handlerton**)NULL, - table->file, ULL(0), FALSE)) + table->file, ULL(0), TRUE)) + { + DBUG_RETURN(TRUE); + } + /* + The check below needs to be performed after check_partition_info + since this function "fixes" the item trees of the new partitions + to reorganize into + */ + if (alter_info->flags == ALTER_REORGANIZE_PARTITION && + tab_part_info->part_type == RANGE_PARTITION && + ((is_last_partition_reorged && + (tab_part_info->column_list ? + (tab_part_info->compare_column_values( + alt_max_elem_val->col_val_array, + tab_max_elem_val->col_val_array) < 0) : + alt_max_range < tab_max_range)) || + (!is_last_partition_reorged && + (tab_part_info->column_list ? + (tab_part_info->compare_column_values( + alt_max_elem_val->col_val_array, + tab_max_elem_val->col_val_array) != 0) : + alt_max_range != tab_max_range)))) { + /* + For range partitioning the total resulting range before and + after the change must be the same except in one case. This is + when the last partition is reorganised, in this case it is + acceptable to increase the total range. + The reason is that it is not allowed to have "holes" in the + middle of the ranges and thus we should not allow to reorganise + to create "holes". + */ + my_error(ER_REORG_OUTSIDE_RANGE, MYF(0)); DBUG_RETURN(TRUE); } } @@ -5080,6 +5409,10 @@ the generated partition syntax in a correct manner. { DBUG_PRINT("info", ("partition changed")); *partition_changed= TRUE; + if (thd->work_part_info->fix_parser_data(thd)) + { + DBUG_RETURN(TRUE); + } } /* Set up partition default_engine_type either from the create_info @@ -5240,8 +5573,8 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) part_it.remove(); remove_count++; } - } while (++i < part_info->no_parts); - part_info->no_parts-= remove_count; + } while (++i < part_info->num_parts); + part_info->num_parts-= remove_count; DBUG_RETURN(FALSE); } @@ -5363,7 +5696,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, char normal_path[FN_REFLEN]; List_iterator<partition_element> part_it(part_info->partitions); uint temp_partitions= part_info->temp_partitions.elements; - uint no_elements= part_info->partitions.elements; + uint num_elements= part_info->partitions.elements; uint i= 0; DBUG_ENTER("write_log_changed_partitions"); @@ -5376,7 +5709,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, if (part_info->is_sub_partitioned()) { List_iterator<partition_element> sub_it(part_elem->subpartitions); - uint no_subparts= part_info->no_subparts; + uint num_subparts= part_info->num_subparts; uint j= 0; do { @@ -5405,7 +5738,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, *next_entry= log_entry->entry_pos; sub_elem->log_entry= log_entry; insert_part_info_log_entry_list(part_info, log_entry); - } while (++j < no_subparts); + } while (++j < num_subparts); } else { @@ -5433,7 +5766,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, insert_part_info_log_entry_list(part_info, log_entry); } } - } while (++i < no_elements); + } while (++i < num_elements); DBUG_RETURN(FALSE); } @@ -5459,14 +5792,14 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, char tmp_path[FN_LEN]; List_iterator<partition_element> part_it(part_info->partitions); List_iterator<partition_element> temp_it(part_info->temp_partitions); - uint no_temp_partitions= part_info->temp_partitions.elements; - uint no_elements= part_info->partitions.elements; + uint num_temp_partitions= part_info->temp_partitions.elements; + uint num_elements= part_info->partitions.elements; DBUG_ENTER("write_log_dropped_partitions"); ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION; if (temp_list) - no_elements= no_temp_partitions; - while (no_elements--) + num_elements= num_temp_partitions; + while (num_elements--) { partition_element *part_elem; if (temp_list) @@ -5480,14 +5813,14 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, uint name_variant; if (part_elem->part_state == PART_CHANGED || (part_elem->part_state == PART_TO_BE_ADDED && - no_temp_partitions)) + num_temp_partitions)) name_variant= TEMP_PART_NAME; else name_variant= NORMAL_PART_NAME; if (part_info->is_sub_partitioned()) { List_iterator<partition_element> sub_it(part_elem->subpartitions); - uint no_subparts= part_info->no_subparts; + uint num_subparts= part_info->num_subparts; uint j= 0; do { @@ -5507,7 +5840,7 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, *next_entry= log_entry->entry_pos; sub_elem->log_entry= log_entry; insert_part_info_log_entry_list(part_info, log_entry); - } while (++j < no_subparts); + } while (++j < num_subparts); } else { @@ -6605,16 +6938,19 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str) IMPLEMENTATION There are two available interval analyzer functions: (1) get_part_iter_for_interval_via_mapping - (2) get_part_iter_for_interval_via_walking + (2) get_part_iter_for_interval_cols_via_map + (3) get_part_iter_for_interval_via_walking They both have limited applicability: (1) is applicable for "PARTITION BY <RANGE|LIST>(func(t.field))", where func is a monotonic function. - - (2) is applicable for + + (2) is applicable for "PARTITION BY <RANGE|LIST> COLUMNS (field_list) + + (3) is applicable for "[SUB]PARTITION BY <any-partitioning-type>(any_func(t.integer_field))" - If both are applicable, (1) is preferred over (2). + If both (1) and (3) are applicable, (1) is preferred over (3). This function sets part_info::get_part_iter_for_interval according to this criteria, and also sets some auxilary fields that the function @@ -6634,10 +6970,19 @@ static void set_up_range_analysis_info(partition_info *part_info) switch (part_info->part_type) { case RANGE_PARTITION: case LIST_PARTITION: - if (part_info->part_expr->get_monotonicity_info() != NON_MONOTONIC) + if (!part_info->column_list) + { + if (part_info->part_expr->get_monotonicity_info() != NON_MONOTONIC) + { + part_info->get_part_iter_for_interval= + get_part_iter_for_interval_via_mapping; + goto setup_subparts; + } + } + else { part_info->get_part_iter_for_interval= - get_part_iter_for_interval_via_mapping; + get_part_iter_for_interval_cols_via_map; goto setup_subparts; } default: @@ -6648,7 +6993,7 @@ static void set_up_range_analysis_info(partition_info *part_info) Check if get_part_iter_for_interval_via_walking() can be used for partitioning */ - if (part_info->no_part_fields == 1) + if (part_info->num_part_fields == 1) { Field *field= part_info->part_field_array[0]; switch (field->type()) { @@ -6670,7 +7015,7 @@ setup_subparts: Check if get_part_iter_for_interval_via_walking() can be used for subpartitioning */ - if (part_info->no_subpart_fields == 1) + if (part_info->num_subpart_fields == 1) { Field *field= part_info->subpart_field_array[0]; switch (field->type()) { @@ -6688,9 +7033,118 @@ setup_subparts: } +/* + This function takes a memory of packed fields in opt-range format + and stores it in record format. To avoid having to worry about how + the length of fields are calculated in opt-range format we send + an array of lengths used for each field in store_length_array. + + SYNOPSIS + store_tuple_to_record() + pfield Field array + store_length_array Array of field lengths + value Memory where fields are stored + value_end End of memory + + RETURN VALUE + nparts Number of fields assigned +*/ +uint32 store_tuple_to_record(Field **pfield, + uint32 *store_length_array, + uchar *value, + uchar *value_end) +{ + /* This function is inspired by store_key_image_rec. */ + uint32 nparts= 0; + uchar *loc_value; + while (value < value_end) + { + loc_value= value; + if ((*pfield)->real_maybe_null()) + { + if (*loc_value) + (*pfield)->set_null(); + else + (*pfield)->set_notnull(); + loc_value++; + } + uint len= (*pfield)->pack_length(); + (*pfield)->set_key_image(loc_value, len); + value+= *store_length_array; + store_length_array++; + nparts++; + pfield++; + } + return nparts; +} + +/* + RANGE(columns) partitioning: compare value bound and probe tuple. + + The value bound always is a full tuple (but may include the MAXVALUE + special value). + + The probe tuple may be a prefix of partitioning tuple. The tail_is_min + parameter specifies whether the suffix components should be assumed to + hold MAXVALUE +*/ + +static int cmp_rec_and_tuple(part_column_list_val *val, uint32 nvals_in_rec) +{ + partition_info *part_info= val->part_info; + Field **field= part_info->part_field_array; + Field **fields_end= field + nvals_in_rec; + int res; + + for (; field != fields_end; field++, val++) + { + if (val->max_value) + return -1; + if ((*field)->is_null()) + { + if (val->null_value) + continue; + return -1; + } + if (val->null_value) + return +1; + res= (*field)->cmp((const uchar*)val->column_value); + if (res) + return res; + } + return 0; +} + + +static int cmp_rec_and_tuple_prune(part_column_list_val *val, + uint32 n_vals_in_rec, + bool tail_is_min) +{ + int cmp; + Field **field; + partition_info *part_info; + if ((cmp= cmp_rec_and_tuple(val, n_vals_in_rec))) + return cmp; + part_info= val->part_info; + field= part_info->part_field_array + n_vals_in_rec; + for (; *field; field++, val++) + { + if (tail_is_min) + return -1; + if (!tail_is_min && !val->max_value) + return +1; + } + return 0; +} + + typedef uint32 (*get_endpoint_func)(partition_info*, bool left_endpoint, bool include_endpoint); +typedef uint32 (*get_col_endpoint_func)(partition_info*, bool left_endpoint, + bool include_endpoint, + uint32 num_parts); + /* Partitioning Interval Analysis: Initialize the iterator for "mapping" case @@ -6726,18 +7180,145 @@ typedef uint32 (*get_endpoint_func)(partition_info*, bool left_endpoint, -1 - All partitions would match (iterator not initialized) */ +uint32 get_partition_id_cols_range_for_endpoint(partition_info *part_info, + bool left_endpoint, + bool include_endpoint, + uint32 nparts) +{ + uint max_partition= part_info->num_parts - 1; + uint min_part_id= 0, max_part_id= max_partition, loc_part_id; + part_column_list_val *range_col_array= part_info->range_col_array; + uint num_columns= part_info->part_field_list.elements; + bool tailf= !(left_endpoint ^ include_endpoint); + DBUG_ENTER("get_partition_id_cols_range_for_endpoint"); + + /* Get the partitioning function value for the endpoint */ + while (max_part_id > min_part_id) + { + loc_part_id= (max_part_id + min_part_id + 1) >> 1; + if (cmp_rec_and_tuple_prune(range_col_array + loc_part_id*num_columns, + nparts, tailf) >= 0) + min_part_id= loc_part_id + 1; + else + max_part_id= loc_part_id - 1; + } + loc_part_id= max_part_id; + if (loc_part_id < max_partition && + cmp_rec_and_tuple_prune(range_col_array + (loc_part_id+1)*num_columns, + nparts, tailf) >= 0 + ) + { + loc_part_id++; + } + if (left_endpoint) + { + if (cmp_rec_and_tuple_prune(range_col_array + loc_part_id*num_columns, + nparts, tailf) >= 0) + loc_part_id++; + } + else + { + if (loc_part_id < max_partition) + { + int res= cmp_rec_and_tuple_prune(range_col_array + + loc_part_id * num_columns, + nparts, tailf); + if (!res) + loc_part_id += test(include_endpoint); + else if (res > 0) + loc_part_id++; + } + loc_part_id++; + } + DBUG_RETURN(loc_part_id); +} + + +int get_part_iter_for_interval_cols_via_map(partition_info *part_info, + bool is_subpart, + uint32 *store_length_array, + uchar *min_value, uchar *max_value, + uint min_len, uint max_len, + uint flags, + PARTITION_ITERATOR *part_iter) +{ + uint32 nparts; + get_col_endpoint_func get_col_endpoint; + DBUG_ENTER("get_part_iter_for_interval_cols_via_map"); + + if (part_info->part_type == RANGE_PARTITION) + { + get_col_endpoint= get_partition_id_cols_range_for_endpoint; + part_iter->get_next= get_next_partition_id_range; + } + else if (part_info->part_type == LIST_PARTITION) + { + get_col_endpoint= get_partition_id_cols_list_for_endpoint; + part_iter->get_next= get_next_partition_id_list; + part_iter->part_info= part_info; + DBUG_ASSERT(part_info->num_list_values); + } + else + assert(0); + + if (flags & NO_MIN_RANGE) + part_iter->part_nums.start= part_iter->part_nums.cur= 0; + else + { + // Copy from min_value to record + nparts= store_tuple_to_record(part_info->part_field_array, + store_length_array, + min_value, + min_value + min_len); + part_iter->part_nums.start= part_iter->part_nums.cur= + get_col_endpoint(part_info, TRUE, !(flags & NEAR_MIN), + nparts); + } + if (flags & NO_MAX_RANGE) + { + if (part_info->part_type == RANGE_PARTITION) + part_iter->part_nums.end= part_info->num_parts; + else /* LIST_PARTITION */ + { + DBUG_ASSERT(part_info->part_type == LIST_PARTITION); + part_iter->part_nums.end= part_info->num_list_values; + } + } + else + { + // Copy from max_value to record + nparts= store_tuple_to_record(part_info->part_field_array, + store_length_array, + max_value, + max_value + max_len); + part_iter->part_nums.end= get_col_endpoint(part_info, FALSE, + !(flags & NEAR_MAX), + nparts); + } + if (part_iter->part_nums.start == part_iter->part_nums.end) + DBUG_RETURN(0); + DBUG_RETURN(1); +} + + int get_part_iter_for_interval_via_mapping(partition_info *part_info, bool is_subpart, + uint32 *store_length_array, /* ignored */ uchar *min_value, uchar *max_value, + uint min_len, uint max_len, /* ignored */ uint flags, PARTITION_ITERATOR *part_iter) { - DBUG_ASSERT(!is_subpart); Field *field= part_info->part_field_array[0]; uint32 max_endpoint_val; get_endpoint_func get_endpoint; bool can_match_multiple_values; /* is not '=' */ uint field_len= field->pack_length_in_rec(); + DBUG_ENTER("get_part_iter_for_interval_via_mapping"); + DBUG_ASSERT(!is_subpart); + (void) store_length_array; + (void)min_len; + (void)max_len; part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE; if (part_info->part_type == RANGE_PARTITION) @@ -6746,7 +7327,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, get_endpoint= get_partition_id_range_for_endpoint_charset; else get_endpoint= get_partition_id_range_for_endpoint; - max_endpoint_val= part_info->no_parts; + max_endpoint_val= part_info->num_parts; part_iter->get_next= get_next_partition_id_range; } else if (part_info->part_type == LIST_PARTITION) @@ -6756,7 +7337,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, get_endpoint= get_list_array_idx_for_endpoint_charset; else get_endpoint= get_list_array_idx_for_endpoint; - max_endpoint_val= part_info->no_list_values; + max_endpoint_val= part_info->num_list_values; part_iter->get_next= get_next_partition_id_list; part_iter->part_info= part_info; if (max_endpoint_val == 0) @@ -6769,7 +7350,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, part_iter->part_nums.start= part_iter->part_nums.end= 0; part_iter->part_nums.cur= 0; part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE; - return -1; + DBUG_RETURN(-1); } } else @@ -6801,11 +7382,11 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, { part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE; part_iter->part_nums.start= part_iter->part_nums.cur= 0; - if (*max_value && !(flags & NO_MAX_RANGE)) + if (!(flags & NO_MAX_RANGE) && *max_value) { /* The right bound is X <= NULL, i.e. it is a "X IS NULL" interval */ part_iter->part_nums.end= 0; - return 1; + DBUG_RETURN(1); } } else @@ -6829,11 +7410,11 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, part_iter->part_nums.cur= part_iter->part_nums.start= 0; part_iter->part_nums.end= 0; part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE; - return 1; + DBUG_RETURN(1); } part_iter->part_nums.cur= part_iter->part_nums.start; if (part_iter->part_nums.start == max_endpoint_val) - return 0; /* No partitions */ + DBUG_RETURN(0); /* No partitions */ } } @@ -6847,9 +7428,9 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, part_iter->part_nums.end= get_endpoint(part_info, 0, include_endp); if (part_iter->part_nums.start >= part_iter->part_nums.end && !part_iter->ret_null_part) - return 0; /* No partitions */ + DBUG_RETURN(0); /* No partitions */ } - return 1; /* Ok, iterator initialized */ + DBUG_RETURN(1); /* Ok, iterator initialized */ } @@ -6897,25 +7478,32 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, */ int get_part_iter_for_interval_via_walking(partition_info *part_info, - bool is_subpart, - uchar *min_value, uchar *max_value, - uint flags, - PARTITION_ITERATOR *part_iter) + bool is_subpart, + uint32 *store_length_array, /* ignored */ + uchar *min_value, uchar *max_value, + uint min_len, uint max_len, /* ignored */ + uint flags, + PARTITION_ITERATOR *part_iter) { Field *field; uint total_parts; partition_iter_func get_next_func; + DBUG_ENTER("get_part_iter_for_interval_via_walking"); + (void)store_length_array; + (void)min_len; + (void)max_len; + part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE; if (is_subpart) { field= part_info->subpart_field_array[0]; - total_parts= part_info->no_subparts; + total_parts= part_info->num_subparts; get_next_func= get_next_subpartition_via_walking; } else { field= part_info->part_field_array[0]; - total_parts= part_info->no_parts; + total_parts= part_info->num_parts; get_next_func= get_next_partition_via_walking; } @@ -6935,7 +7523,7 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info, if (!part_info->get_subpartition_id(part_info, &part_id)) { init_single_partition_iterator(part_id, part_iter); - return 1; /* Ok, iterator initialized */ + DBUG_RETURN(1); /* Ok, iterator initialized */ } } else @@ -6948,10 +7536,10 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info, if (!res) { init_single_partition_iterator(part_id, part_iter); - return 1; /* Ok, iterator initialized */ + DBUG_RETURN(1); /* Ok, iterator initialized */ } } - return 0; /* No partitions match */ + DBUG_RETURN(0); /* No partitions match */ } if ((field->real_maybe_null() && @@ -6959,7 +7547,7 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info, (!(flags & NO_MAX_RANGE) && *max_value))) || // X <? NULL (flags & (NO_MIN_RANGE | NO_MAX_RANGE))) // -inf at any bound { - return -1; /* Can't handle this interval, have to use all partitions */ + DBUG_RETURN(-1); /* Can't handle this interval, have to use all partitions */ } /* Get integers for left and right interval bound */ @@ -6978,7 +7566,7 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info, an empty interval by "wrapping around" a + 4G-1 + 1 = a. */ if ((ulonglong)b - (ulonglong)a == ~0ULL) - return -1; + DBUG_RETURN(-1); a += test(flags & NEAR_MIN); b += test(!(flags & NEAR_MAX)); @@ -7001,13 +7589,13 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info, - there are not many values to enumerate. */ if ((n_values > 2*total_parts) && n_values > MAX_RANGE_TO_WALK) - return -1; + DBUG_RETURN(-1); part_iter->field_vals.start= part_iter->field_vals.cur= a; part_iter->field_vals.end= b; part_iter->part_info= part_info; part_iter->get_next= get_next_func; - return 1; + DBUG_RETURN(1); } @@ -7055,8 +7643,9 @@ uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter) DESCRIPTION This implementation of PARTITION_ITERATOR::get_next() is special for - LIST partitioning: it enumerates partition ids in - part_info->list_array[i] where i runs over [min_idx, max_idx] interval. + LIST partitioning: it enumerates partition ids in + part_info->list_array[i] (list_col_array[i] for COLUMNS LIST + partitioning) where i runs over [min_idx, max_idx] interval. The function conforms to partition_iter_func type. RETURN @@ -7078,8 +7667,13 @@ uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter) return NOT_A_PARTITION_ID; } else - return part_iter->part_info->list_array[part_iter-> - part_nums.cur++].partition_id; + { + partition_info *part_info= part_iter->part_info; + uint32 num_part= part_iter->part_nums.cur++; + return part_info->column_list ? + part_info->list_col_array[num_part].partition_id : + part_info->list_array[num_part].partition_id; + } } @@ -7220,5 +7814,17 @@ void create_subpartition_name(char *out, const char *in1, strxmov(out, in1, "#P#", transl_part_name, "#SP#", transl_subpart_name, "#REN#", NullS); } + +uint get_partition_field_store_length(Field *field) +{ + uint store_length; + + store_length= field->key_length(); + if (field->real_maybe_null()) + store_length+= HA_KEY_NULL_LENGTH; + if (field->real_type() == MYSQL_TYPE_VARCHAR) + store_length+= HA_KEY_BLOB_LENGTH; + return store_length; +} #endif diff --git a/sql/sql_partition.h b/sql/sql_partition.h index 0c47340016c..6e1bf8b5728 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -1,7 +1,7 @@ #ifndef SQL_PARTITION_INCLUDED #define SQL_PARTITION_INCLUDED -/* Copyright (C) 2006 MySQL AB +/* Copyright 2005-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -68,15 +68,19 @@ int get_part_for_delete(const uchar *buf, const uchar *rec0, void prune_partition_set(const TABLE *table, part_id_range *part_spec); bool check_partition_info(partition_info *part_info,handlerton **eng_type, TABLE *table, handler *file, HA_CREATE_INFO *info); -void set_linear_hash_mask(partition_info *part_info, uint no_parts); +void set_linear_hash_mask(partition_info *part_info, uint num_parts); bool fix_partition_func(THD *thd, TABLE *table, bool create_table_ind); -char *generate_partition_syntax(partition_info *part_info, - uint *buf_length, bool use_sql_alloc, - bool show_partition_options); bool partition_key_modified(TABLE *table, const MY_BITMAP *fields); void get_partition_set(const TABLE *table, uchar *buf, const uint index, const key_range *key_spec, part_id_range *part_spec); +uint get_partition_field_store_length(Field *field); +int get_cs_converted_part_value_from_string(THD *thd, + Item *item, + String *input_str, + String *output_str, + CHARSET_INFO *cs, + bool use_hex); void get_full_part_id_from_key(const TABLE *table, uchar *buf, KEY *key_info, const key_range *key_spec, @@ -99,6 +103,7 @@ bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, bool check_part_func_fields(Field **ptr, bool ok_with_charsets); bool field_is_partition_charset(Field *field); +Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs); /* A "Get next" function for partition iterator. @@ -175,13 +180,16 @@ typedef struct st_partition_iter SYNOPSIS get_partitions_in_range_iter() - part_info Partitioning info - is_subpart - min_val Left edge, field value in opt_range_key format. - max_val Right edge, field value in opt_range_key format. - flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE, - NO_MAX_RANGE. - part_iter Iterator structure to be initialized + part_info Partitioning info + is_subpart + store_length_array Length of fields packed in opt_range_key format + min_val Left edge, field value in opt_range_key format + max_val Right edge, field value in opt_range_key format + min_len Length of minimum value + max_len Length of maximum value + flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE, + NO_MAX_RANGE + part_iter Iterator structure to be initialized DESCRIPTION Functions with this signature are used to perform "Partitioning Interval @@ -194,8 +202,9 @@ typedef struct st_partition_iter The set of partitions is returned by initializing an iterator in *part_iter NOTES - There are currently two functions of this type: + There are currently three functions of this type: - get_part_iter_for_interval_via_walking + - get_part_iter_for_interval_cols_via_map - get_part_iter_for_interval_via_mapping RETURN @@ -206,7 +215,9 @@ typedef struct st_partition_iter typedef int (*get_partitions_in_range_iter)(partition_info *part_info, bool is_subpart, + uint32 *store_length_array, uchar *min_val, uchar *max_val, + uint min_len, uint max_len, uint flags, PARTITION_ITERATOR *part_iter); diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 6c48d98fb48..2167b28776a 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -578,14 +578,15 @@ static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int ty for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) { struct st_plugin_int *plugin= (st_plugin_int *) - hash_search(&plugin_hash[i], (const uchar *)name->str, name->length); + my_hash_search(&plugin_hash[i], (const uchar *)name->str, name->length); if (plugin) DBUG_RETURN(plugin); } } else DBUG_RETURN((st_plugin_int *) - hash_search(&plugin_hash[type], (const uchar *)name->str, name->length)); + my_hash_search(&plugin_hash[type], (const uchar *)name->str, + name->length)); DBUG_RETURN(0); } @@ -853,7 +854,7 @@ static void plugin_del(struct st_plugin_int *plugin) safe_mutex_assert_owner(&LOCK_plugin); /* Free allocated strings before deleting the plugin. */ plugin_vars_free_values(plugin->system_vars); - hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin); + my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin); if (plugin->plugin_dl) plugin_dl_del(&plugin->plugin_dl->dl); plugin->state= PLUGIN_IS_FREED; @@ -1128,8 +1129,8 @@ int plugin_init(int *argc, char **argv, int flags) init_alloc_root(&plugin_mem_root, 4096, 4096); init_alloc_root(&tmp_root, 4096, 4096); - if (hash_init(&bookmark_hash, &my_charset_bin, 16, 0, 0, - get_bookmark_hash_key, NULL, HASH_UNIQUE)) + if (my_hash_init(&bookmark_hash, &my_charset_bin, 16, 0, 0, + get_bookmark_hash_key, NULL, HASH_UNIQUE)) goto err; @@ -1143,8 +1144,8 @@ int plugin_init(int *argc, char **argv, int flags) for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) { - if (hash_init(&plugin_hash[i], system_charset_info, 16, 0, 0, - get_plugin_hash_key, NULL, HASH_UNIQUE)) + if (my_hash_init(&plugin_hash[i], system_charset_info, 16, 0, 0, + get_plugin_hash_key, NULL, HASH_UNIQUE)) goto err; } @@ -1622,7 +1623,7 @@ void plugin_shutdown(void) /* Dispose of the memory */ for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) - hash_free(&plugin_hash[i]); + my_hash_free(&plugin_hash[i]); delete_dynamic(&plugin_array); count= plugin_dl_array.elements; @@ -1634,7 +1635,7 @@ void plugin_shutdown(void) my_afree(dl); delete_dynamic(&plugin_dl_array); - hash_free(&bookmark_hash); + my_hash_free(&bookmark_hash); free_root(&plugin_mem_root, MYF(0)); global_variables_dynamic_size= 0; @@ -1655,7 +1656,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl bzero(&tables, sizeof(tables)); tables.db= (char *)"mysql"; tables.table_name= tables.alias= (char *)"plugin"; - if (check_table_access(thd, INSERT_ACL, &tables, 1, FALSE)) + if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); /* need to open before acquiring LOCK_plugin or it will deadlock */ @@ -1759,12 +1760,13 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) reap_plugins(); pthread_mutex_unlock(&LOCK_plugin); + uchar user_key[MAX_KEY_LENGTH]; table->use_all_columns(); table->field[0]->store(name->str, name->length, system_charset_info); - if (! table->file->index_read_idx_map(table->record[0], 0, - (uchar *)table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + key_copy(user_key, table->record[0], table->key_info, + table->key_info->key_length); + if (! table->file->index_read_idx_map(table->record[0], 0, user_key, + HA_WHOLE_KEY, HA_READ_KEY_EXACT)) { int error; /* @@ -1822,7 +1824,7 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, HASH *hash= plugin_hash + type; for (idx= 0; idx < total; idx++) { - plugin= (struct st_plugin_int *) hash_element(hash, idx); + plugin= (struct st_plugin_int *) my_hash_element(hash, idx); plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL; } } @@ -2221,8 +2223,8 @@ static st_bookmark *find_bookmark(const char *plugin, const char *name, varname[0]= flags & PLUGIN_VAR_TYPEMASK; - result= (st_bookmark*) hash_search(&bookmark_hash, - (const uchar*) varname, length - 1); + result= (st_bookmark*) my_hash_search(&bookmark_hash, + (const uchar*) varname, length - 1); my_afree(varname); return result; @@ -2382,7 +2384,7 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock) { sys_var_pluginvar *pi; sys_var *var; - st_bookmark *v= (st_bookmark*) hash_element(&bookmark_hash,idx); + st_bookmark *v= (st_bookmark*) my_hash_element(&bookmark_hash,idx); if (v->version <= thd->variables.dynamic_variables_version || !(var= intern_find_sys_var(v->key + 1, v->name_len, true)) || @@ -2476,7 +2478,7 @@ static void cleanup_variables(THD *thd, struct system_variables *vars) rw_rdlock(&LOCK_system_variables_hash); for (idx= 0; idx < bookmark_hash.records; idx++) { - v= (st_bookmark*) hash_element(&bookmark_hash, idx); + v= (st_bookmark*) my_hash_element(&bookmark_hash, idx); if (v->version > vars->dynamic_variables_version || !(var= intern_find_sys_var(v->key + 1, v->name_len, true)) || !(pivar= var->cast_pluginvar()) || diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 75a0c538e04..e54c704589f 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -84,6 +84,7 @@ When one supplies long data for a placeholder: */ #include "mysql_priv.h" +#include "sql_prepare.h" #include "sql_select.h" // for JOIN #include "sql_cursor.h" #include "sp_head.h" @@ -106,7 +107,7 @@ class Select_fetch_protocol_binary: public select_send Protocol_binary protocol; public: Select_fetch_protocol_binary(THD *thd); - virtual bool send_fields(List<Item> &list, uint flags); + virtual bool send_result_set_metadata(List<Item> &list, uint flags); virtual bool send_data(List<Item> &items); virtual bool send_eof(); #ifdef EMBEDDED_LIBRARY @@ -163,6 +164,7 @@ public: bool execute_loop(String *expanded_query, bool open_cursor, uchar *packet_arg, uchar *packet_end_arg); + bool execute_server_runnable(Server_runnable *server_runnable); /* Destroy this statement */ void deallocate(); private: @@ -183,6 +185,78 @@ private: void swap_prepared_statement(Prepared_statement *copy); }; +/** + Execute one SQL statement in an isolated context. +*/ + +class Execute_sql_statement: public Server_runnable +{ +public: + Execute_sql_statement(LEX_STRING sql_text); + virtual bool execute_server_code(THD *thd); +private: + LEX_STRING m_sql_text; +}; + + +class Ed_connection; + +/** + Protocol_local: a helper class to intercept the result + of the data written to the network. +*/ + +class Protocol_local :public Protocol +{ +public: + Protocol_local(THD *thd, Ed_connection *ed_connection); + ~Protocol_local() { free_root(&m_rset_root, MYF(0)); } +protected: + virtual void prepare_for_resend(); + virtual bool write(); + virtual bool store_null(); + virtual bool store_tiny(longlong from); + virtual bool store_short(longlong from); + virtual bool store_long(longlong from); + virtual bool store_longlong(longlong from, bool unsigned_flag); + virtual bool store_decimal(const my_decimal *); + virtual bool store(const char *from, size_t length, CHARSET_INFO *cs); + virtual bool store(const char *from, size_t length, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs); + virtual bool store(MYSQL_TIME *time); + virtual bool store_date(MYSQL_TIME *time); + virtual bool store_time(MYSQL_TIME *time); + virtual bool store(float value, uint32 decimals, String *buffer); + virtual bool store(double value, uint32 decimals, String *buffer); + virtual bool store(Field *field); + + virtual bool send_result_set_metadata(List<Item> *list, uint flags); + virtual bool send_out_parameters(List<Item_param> *sp_params); +#ifdef EMBEDDED_LIBRARY + void remove_last_row(); +#endif + virtual enum enum_protocol_type type() { return PROTOCOL_LOCAL; }; + + virtual bool send_ok(uint server_status, uint statement_warn_count, + ulonglong affected_rows, ulonglong last_insert_id, + const char *message); + + virtual bool send_eof(uint server_status, uint statement_warn_count); + virtual bool send_error(uint sql_errno, const char *err_msg, const char* sqlstate); +private: + bool store_string(const char *str, size_t length, + CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs); + + bool store_column(const void *data, size_t length); + void opt_add_row_to_rset(); +private: + Ed_connection *m_connection; + MEM_ROOT m_rset_root; + List<Ed_row> *m_rset; + size_t m_column_count; + Ed_column *m_current_row; + Ed_column *m_current_column; +}; /****************************************************************************** Implementation @@ -260,7 +334,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) error= my_net_write(net, buff, sizeof(buff)); if (stmt->param_count && ! error) { - error= thd->protocol_text.send_fields((List<Item> *) + error= thd->protocol_text.send_result_set_metadata((List<Item> *) &stmt->lex->param_list, Protocol::SEND_EOF); } @@ -1027,9 +1101,9 @@ static bool insert_params_from_vars(Prepared_statement *stmt, { Item_param *param= *it; varname= var_it++; - entry= (user_var_entry*)hash_search(&stmt->thd->user_vars, - (uchar*) varname->str, - varname->length); + entry= (user_var_entry*)my_hash_search(&stmt->thd->user_vars, + (uchar*) varname->str, + varname->length); if (param->set_from_user_var(stmt->thd, entry) || param->convert_str_value(stmt->thd)) DBUG_RETURN(1); @@ -1074,8 +1148,8 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, Item_param *param= *it; varname= var_it++; - entry= (user_var_entry *) hash_search(&thd->user_vars, (uchar*) varname->str, - varname->length); + entry= (user_var_entry *) my_hash_search(&thd->user_vars, (uchar*) + varname->str, varname->length); /* We have to call the setup_one_conversion_function() here to set the parameter's members that might be needed further @@ -1336,7 +1410,7 @@ static int mysql_test_select(Prepared_statement *stmt, ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL; if (tables) { - if (check_table_access(thd, privilege, tables, UINT_MAX, FALSE)) + if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE)) goto error; } else if (check_access(thd, privilege, any_db,0,0,0,0)) @@ -1374,7 +1448,7 @@ static int mysql_test_select(Prepared_statement *stmt, unit->prepare call above. */ if (send_prep_stmt(stmt, lex->result->field_count(fields)) || - lex->result->send_fields(fields, Protocol::SEND_EOF) || + lex->result->send_result_set_metadata(fields, Protocol::SEND_EOF) || thd->protocol->flush()) goto error; DBUG_RETURN(2); @@ -1405,7 +1479,8 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, THD *thd= stmt->thd; DBUG_ENTER("mysql_test_do_fields"); - if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) + if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE, + UINT_MAX, FALSE)) DBUG_RETURN(TRUE); if (open_normal_and_derived_tables(thd, tables, 0)) @@ -1436,8 +1511,9 @@ static bool mysql_test_set_fields(Prepared_statement *stmt, THD *thd= stmt->thd; set_var_base *var; - if ((tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) - || open_normal_and_derived_tables(thd, tables, 0)) + if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, + UINT_MAX, FALSE)) || + open_normal_and_derived_tables(thd, tables, 0)) goto error; while ((var= it++)) @@ -1472,7 +1548,8 @@ static bool mysql_test_call_fields(Prepared_statement *stmt, THD *thd= stmt->thd; Item *item; - if ((tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) || + if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, + UINT_MAX, FALSE)) || open_normal_and_derived_tables(thd, tables, 0)) goto err; @@ -1920,30 +1997,6 @@ static bool check_prepared_statement(Prepared_statement *stmt) Note that we don't need to have cases in this list if they are marked with CF_STATUS_COMMAND in sql_command_flags */ - case SQLCOM_SHOW_PROCESSLIST: - case SQLCOM_SHOW_STORAGE_ENGINES: - case SQLCOM_SHOW_PRIVILEGES: - case SQLCOM_SHOW_COLUMN_TYPES: - case SQLCOM_SHOW_ENGINE_LOGS: - case SQLCOM_SHOW_ENGINE_STATUS: - case SQLCOM_SHOW_ENGINE_MUTEX: - case SQLCOM_SHOW_CREATE_DB: - case SQLCOM_SHOW_GRANTS: - case SQLCOM_SHOW_BINLOG_EVENTS: - case SQLCOM_SHOW_MASTER_STAT: - case SQLCOM_SHOW_SLAVE_STAT: - case SQLCOM_SHOW_CREATE_PROC: - case SQLCOM_SHOW_CREATE_FUNC: - case SQLCOM_SHOW_CREATE_EVENT: - case SQLCOM_SHOW_CREATE_TRIGGER: - case SQLCOM_SHOW_CREATE: - case SQLCOM_SHOW_PROC_CODE: - case SQLCOM_SHOW_FUNC_CODE: - case SQLCOM_SHOW_AUTHORS: - case SQLCOM_SHOW_CONTRIBUTORS: - case SQLCOM_SHOW_WARNS: - case SQLCOM_SHOW_ERRORS: - case SQLCOM_SHOW_BINLOGS: case SQLCOM_DROP_TABLE: case SQLCOM_RENAME_TABLE: case SQLCOM_ALTER_TABLE: @@ -2066,7 +2119,6 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) { Protocol *save_protocol= thd->protocol; Prepared_statement *stmt; - bool error; DBUG_ENTER("mysqld_stmt_prepare"); DBUG_PRINT("prep_query", ("%s", packet)); @@ -2091,15 +2143,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) thd->protocol= &thd->protocol_binary; - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(),QUERY_PRIOR); - - error= stmt->prepare(packet, packet_length); - - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(),WAIT_PRIOR); - - if (error) + if (stmt->prepare(packet, packet_length)) { /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); @@ -2149,9 +2193,9 @@ static const char *get_dynamic_sql_string(LEX *lex, uint *query_len) convert it for error messages to be uniform. */ if ((entry= - (user_var_entry*)hash_search(&thd->user_vars, - (uchar*)lex->prepared_stmt_code.str, - lex->prepared_stmt_code.length)) + (user_var_entry*)my_hash_search(&thd->user_vars, + (uchar*)lex->prepared_stmt_code.str, + lex->prepared_stmt_code.length)) && entry->value) { my_bool is_var_null; @@ -2459,7 +2503,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) DBUG_VOID_RETURN; } -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.set_query_source(stmt->query, stmt->query_length); #endif DBUG_PRINT("exec_query", ("%s", stmt->query)); @@ -2478,7 +2522,6 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio);); DBUG_VOID_RETURN; - } @@ -2787,19 +2830,19 @@ Select_fetch_protocol_binary::Select_fetch_protocol_binary(THD *thd_arg) :protocol(thd_arg) {} -bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags) +bool Select_fetch_protocol_binary::send_result_set_metadata(List<Item> &list, uint flags) { bool rc; Protocol *save_protocol= thd->protocol; /* - Protocol::send_fields caches the information about column types: + Protocol::send_result_set_metadata caches the information about column types: this information is later used to send data. Therefore, the same dedicated Protocol object must be used for all operations with a cursor. */ thd->protocol= &protocol; - rc= select_send::send_fields(list, flags); + rc= select_send::send_result_set_metadata(list, flags); thd->protocol= save_protocol; return rc; @@ -2848,6 +2891,70 @@ Reprepare_observer::report_error(THD *thd) } +/******************************************************************* +* Server_runnable +*******************************************************************/ + +Server_runnable::~Server_runnable() +{ +} + +/////////////////////////////////////////////////////////////////////////// + +Execute_sql_statement:: +Execute_sql_statement(LEX_STRING sql_text) + :m_sql_text(sql_text) +{} + + +/** + Parse and execute a statement. Does not prepare the query. + + Allows to execute a statement from within another statement. + The main property of the implementation is that it does not + affect the environment -- i.e. you can run many + executions without having to cleanup/reset THD in between. +*/ + +bool +Execute_sql_statement::execute_server_code(THD *thd) +{ + bool error; + + if (alloc_query(thd, m_sql_text.str, m_sql_text.length)) + return TRUE; + + Parser_state parser_state(thd, thd->query, thd->query_length); + + parser_state.m_lip.multi_statements= FALSE; + lex_start(thd); + + error= parse_sql(thd, &parser_state, NULL) || thd->is_error(); + + if (error) + goto end; + + thd->lex->set_trg_event_type_for_tables(); + + error= mysql_execute_command(thd); + + if (thd->killed_errno()) + { + if (! thd->stmt_da->is_set()) + thd->send_kill_message(); + } + + /* report error issued during command execution */ + if (error == 0 && thd->spcont == NULL) + general_log_write(thd, COM_STMT_EXECUTE, + thd->query, thd->query_length); + +end: + lex_end(thd->lex); + + return error; +} + /*************************************************************************** Prepared_statement ****************************************************************************/ @@ -2945,7 +3052,8 @@ void Prepared_statement::cleanup_stmt() DBUG_ENTER("Prepared_statement::cleanup_stmt"); DBUG_PRINT("enter",("stmt: 0x%lx", (long) this)); - DBUG_ASSERT(lex->sphead == 0); + delete lex->sphead; + lex->sphead= 0; /* The order is important */ lex->unit.cleanup(); cleanup_items(free_list); @@ -3058,6 +3166,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) Parser_state parser_state(thd, thd->query, thd->query_length); parser_state.m_lip.stmt_prepare_mode= TRUE; + parser_state.m_lip.multi_statements= FALSE; lex_start(thd); error= parse_sql(thd, & parser_state, NULL) || @@ -3302,6 +3411,45 @@ reexecute: } +bool +Prepared_statement::execute_server_runnable(Server_runnable *server_runnable) +{ + Statement stmt_backup; + bool error; + Query_arena *save_stmt_arena= thd->stmt_arena; + Item_change_list save_change_list; + thd->change_list= save_change_list; + + state= CONVENTIONAL_EXECUTION; + + if (!(lex= new (mem_root) st_lex_local)) + return TRUE; + + thd->set_n_backup_statement(this, &stmt_backup); + thd->set_n_backup_active_arena(this, &stmt_backup); + thd->stmt_arena= this; + + error= server_runnable->execute_server_code(thd); + + delete lex->sphead; + lex->sphead= 0; + /* The order is important */ + lex->unit.cleanup(); + close_thread_tables(thd); + thd->cleanup_after_query(); + + thd->restore_active_arena(this, &stmt_backup); + thd->restore_backup_statement(this, &stmt_backup); + thd->stmt_arena= save_stmt_arena; + + save_change_list= thd->change_list; + + /* Items and memory will freed in destructor */ + + return error; +} + + /** Reprepare this prepared statement. @@ -3637,6 +3785,15 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) if (state == Query_arena::PREPARED) state= Query_arena::EXECUTED; + if (this->lex->sql_command == SQLCOM_CALL) + { + if (is_sql_prepare()) + thd->protocol_text.send_out_parameters(&this->lex->param_list); + else + thd->protocol->send_out_parameters(&this->lex->param_list); + } + + /* Log COM_EXECUTE to the general log. Note, that in case of SQL prepared statements this causes two records to be output: @@ -3670,3 +3827,573 @@ void Prepared_statement::deallocate() /* Statement map calls delete stmt on erase */ thd->stmt_map.erase(this); } + + +/*************************************************************************** +* Ed_result_set +***************************************************************************/ +/** + Use operator delete to free memory of Ed_result_set. + Accessing members of a class after the class has been destroyed + is a violation of the C++ standard but is commonly used in the + server code. +*/ + +void Ed_result_set::operator delete(void *ptr, size_t size) throw () +{ + if (ptr) + { + /* + Make a stack copy, otherwise free_root() will attempt to + write to freed memory. + */ + MEM_ROOT own_root= ((Ed_result_set*) ptr)->m_mem_root; + free_root(&own_root, MYF(0)); + } +} + + +/** + Initialize an instance of Ed_result_set. + + Instances of the class, as well as all result set rows, are + always allocated in the memory root passed over as the second + argument. In the constructor, we take over ownership of the + memory root. It will be freed when the class is destroyed. + + sic: Ed_result_est is not designed to be allocated on stack. +*/ + +Ed_result_set::Ed_result_set(List<Ed_row> *rows_arg, + size_t column_count_arg, + MEM_ROOT *mem_root_arg) + :m_mem_root(*mem_root_arg), + m_column_count(column_count_arg), + m_rows(rows_arg), + m_next_rset(NULL) +{ + /* Take over responsibility for the memory */ + clear_alloc_root(mem_root_arg); +} + +/*************************************************************************** +* Ed_result_set +***************************************************************************/ + +/** + Create a new "execute direct" connection. +*/ + +Ed_connection::Ed_connection(THD *thd) + :m_warning_info(thd->query_id), + m_thd(thd), + m_rsets(0), + m_current_rset(0) +{ +} + + +/** + Free all result sets of the previous statement, if any, + and reset warnings and errors. + + Called before execution of the next query. +*/ + +void +Ed_connection::free_old_result() +{ + while (m_rsets) + { + Ed_result_set *rset= m_rsets->m_next_rset; + delete m_rsets; + m_rsets= rset; + } + m_current_rset= m_rsets; + m_diagnostics_area.reset_diagnostics_area(); + m_warning_info.clear_warning_info(m_thd->query_id); +} + + +/** + A simple wrapper that uses a helper class to execute SQL statements. +*/ + +bool +Ed_connection::execute_direct(LEX_STRING sql_text) +{ + Execute_sql_statement execute_sql_statement(sql_text); + DBUG_PRINT("ed_query", ("%s", sql_text.str)); + + return execute_direct(&execute_sql_statement); +} + + +/** + Execute a fragment of server functionality without an effect on + thd, and store results in memory. + + Conventions: + - the code fragment must finish with OK, EOF or ERROR. + - the code fragment doesn't have to close thread tables, + free memory, commit statement transaction or do any other + cleanup that is normally done in the end of dispatch_command(). + + @param server_runnable A code fragment to execute. +*/ + +bool Ed_connection::execute_direct(Server_runnable *server_runnable) +{ + bool rc= FALSE; + Protocol_local protocol_local(m_thd, this); + Prepared_statement stmt(m_thd); + Protocol *save_protocol= m_thd->protocol; + Diagnostics_area *save_diagnostics_area= m_thd->stmt_da; + Warning_info *save_warning_info= m_thd->warning_info; + + DBUG_ENTER("Ed_connection::execute_direct"); + + free_old_result(); /* Delete all data from previous execution, if any */ + + m_thd->protocol= &protocol_local; + m_thd->stmt_da= &m_diagnostics_area; + m_thd->warning_info= &m_warning_info; + + rc= stmt.execute_server_runnable(server_runnable); + m_thd->protocol->end_statement(); + + m_thd->protocol= save_protocol; + m_thd->stmt_da= save_diagnostics_area; + m_thd->warning_info= save_warning_info; + /* + Protocol_local makes use of m_current_rset to keep + track of the last result set, while adding result sets to the end. + Reset it to point to the first result set instead. + */ + m_current_rset= m_rsets; + + DBUG_RETURN(rc); +} + + +/** + A helper method that is called only during execution. + + Although Ed_connection doesn't support multi-statements, + a statement may generate many result sets. All subsequent + result sets are appended to the end. + + @pre This is called only by Protocol_local. +*/ + +void +Ed_connection::add_result_set(Ed_result_set *ed_result_set) +{ + if (m_rsets) + { + m_current_rset->m_next_rset= ed_result_set; + /* While appending, use m_current_rset as a pointer to the tail. */ + m_current_rset= ed_result_set; + } + else + m_current_rset= m_rsets= ed_result_set; +} + + +/** + Release ownership of the current result set to the client. + + Since we use a simple linked list for result sets, + this method uses a linear search of the previous result + set to exclude the released instance from the list. + + @todo Use double-linked list, when this is really used. + + XXX: This has never been tested with more than one result set! + + @pre There must be a result set. +*/ + +Ed_result_set * +Ed_connection::store_result_set() +{ + Ed_result_set *ed_result_set; + + DBUG_ASSERT(m_current_rset); + + if (m_current_rset == m_rsets) + { + /* Assign the return value */ + ed_result_set= m_current_rset; + /* Exclude the return value from the list. */ + m_current_rset= m_rsets= m_rsets->m_next_rset; + } + else + { + Ed_result_set *prev_rset= m_rsets; + /* Assign the return value. */ + ed_result_set= m_current_rset; + + /* Exclude the return value from the list */ + while (prev_rset->m_next_rset != m_current_rset) + prev_rset= ed_result_set->m_next_rset; + m_current_rset= prev_rset->m_next_rset= m_current_rset->m_next_rset; + } + ed_result_set->m_next_rset= NULL; /* safety */ + + return ed_result_set; +} + +/************************************************************************* +* Protocol_local +**************************************************************************/ + +Protocol_local::Protocol_local(THD *thd, Ed_connection *ed_connection) + :Protocol(thd), + m_connection(ed_connection), + m_rset(NULL), + m_column_count(0), + m_current_row(NULL), + m_current_column(NULL) +{ + clear_alloc_root(&m_rset_root); +} + +/** + Called between two result set rows. + + Prepare structures to fill result set rows. + Unfortunately, we can't return an error here. If memory allocation + fails, we'll have to return an error later. And so is done + in methods such as @sa store_column(). +*/ + +void Protocol_local::prepare_for_resend() +{ + DBUG_ASSERT(alloc_root_inited(&m_rset_root)); + + opt_add_row_to_rset(); + /* Start a new row. */ + m_current_row= (Ed_column *) alloc_root(&m_rset_root, + sizeof(Ed_column) * m_column_count); + m_current_column= m_current_row; +} + + +/** + In "real" protocols this is called to finish a result set row. + Unused in the local implementation. +*/ + +bool Protocol_local::write() +{ + return FALSE; +} + +/** + A helper function to add the current row to the current result + set. Called in @sa prepare_for_resend(), when a new row is started, + and in send_eof(), when the result set is finished. +*/ + +void Protocol_local::opt_add_row_to_rset() +{ + if (m_current_row) + { + /* Add the old row to the result set */ + Ed_row *ed_row= new (&m_rset_root) Ed_row(m_current_row, m_column_count); + if (ed_row) + m_rset->push_back(ed_row, &m_rset_root); + } +} + + +/** + Add a NULL column to the current row. +*/ + +bool Protocol_local::store_null() +{ + if (m_current_column == NULL) + return TRUE; /* prepare_for_resend() failed to allocate memory. */ + + bzero(m_current_column, sizeof(*m_current_column)); + ++m_current_column; + return FALSE; +} + + +/** + A helper method to add any column to the current row + in its binary form. + + Allocates memory for the data in the result set memory root. +*/ + +bool Protocol_local::store_column(const void *data, size_t length) +{ + if (m_current_column == NULL) + return TRUE; /* prepare_for_resend() failed to allocate memory. */ + /* + alloc_root() automatically aligns memory, so we don't need to + do any extra alignment if we're pointing to, say, an integer. + */ + m_current_column->str= (char*) memdup_root(&m_rset_root, + data, + length + 1 /* Safety */); + if (! m_current_column->str) + return TRUE; + m_current_column->str[length]= '\0'; /* Safety */ + m_current_column->length= length; + ++m_current_column; + return FALSE; +} + + +/** + Store a string value in a result set column, optionally + having converted it to character_set_results. +*/ + +bool +Protocol_local::store_string(const char *str, size_t length, + CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs) +{ + /* Store with conversion */ + uint error_unused; + + if (dst_cs && !my_charset_same(src_cs, dst_cs) && + src_cs != &my_charset_bin && + dst_cs != &my_charset_bin) + { + if (convert->copy(str, length, src_cs, dst_cs, &error_unused)) + return TRUE; + str= convert->ptr(); + length= convert->length(); + } + return store_column(str, length); +} + + +/** Store a tiny int as is (1 byte) in a result set column. */ + +bool Protocol_local::store_tiny(longlong value) +{ + char v= (char) value; + return store_column(&v, 1); +} + + +/** Store a short as is (2 bytes, host order) in a result set column. */ + +bool Protocol_local::store_short(longlong value) +{ + int16 v= (int16) value; + return store_column(&v, 2); +} + + +/** Store a "long" as is (4 bytes, host order) in a result set column. */ + +bool Protocol_local::store_long(longlong value) +{ + int32 v= (int32) value; + return store_column(&v, 4); +} + + +/** Store a "longlong" as is (8 bytes, host order) in a result set column. */ + +bool Protocol_local::store_longlong(longlong value, bool unsigned_flag) +{ + int64 v= (int64) value; + return store_column(&v, 8); +} + + +/** Store a decimal in string format in a result set column */ + +bool Protocol_local::store_decimal(const my_decimal *value) +{ + char buf[DECIMAL_MAX_STR_LENGTH]; + String str(buf, sizeof (buf), &my_charset_bin); + int rc; + + rc= my_decimal2string(E_DEC_FATAL_ERROR, value, 0, 0, 0, &str); + + if (rc) + return TRUE; + + return store_column(str.ptr(), str.length()); +} + + +/** Convert to cs_results and store a string. */ + +bool Protocol_local::store(const char *str, size_t length, + CHARSET_INFO *src_cs) +{ + CHARSET_INFO *dst_cs; + + dst_cs= m_connection->m_thd->variables.character_set_results; + return store_string(str, length, src_cs, dst_cs); +} + + +/** Store a string. */ + +bool Protocol_local::store(const char *str, size_t length, + CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs) +{ + return store_string(str, length, src_cs, dst_cs); +} + + +/* Store MYSQL_TIME (in binary format) */ + +bool Protocol_local::store(MYSQL_TIME *time) +{ + return store_column(time, sizeof(MYSQL_TIME)); +} + + +/** Store MYSQL_TIME (in binary format) */ + +bool Protocol_local::store_date(MYSQL_TIME *time) +{ + return store_column(time, sizeof(MYSQL_TIME)); +} + + +/** Store MYSQL_TIME (in binary format) */ + +bool Protocol_local::store_time(MYSQL_TIME *time) +{ + return store_column(time, sizeof(MYSQL_TIME)); +} + + +/* Store a floating point number, as is. */ + +bool Protocol_local::store(float value, uint32 decimals, String *buffer) +{ + return store_column(&value, sizeof(float)); +} + + +/* Store a double precision number, as is. */ + +bool Protocol_local::store(double value, uint32 decimals, String *buffer) +{ + return store_column(&value, sizeof (double)); +} + + +/* Store a Field. */ + +bool Protocol_local::store(Field *field) +{ + if (field->is_null()) + return store_null(); + return field->send_binary(this); +} + + +/** Called to start a new result set. */ + +bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint) +{ + DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root)); + + init_sql_alloc(&m_rset_root, MEM_ROOT_BLOCK_SIZE, 0); + + if (! (m_rset= new (&m_rset_root) List<Ed_row>)) + return TRUE; + + m_column_count= columns->elements; + + return FALSE; +} + + +/** + Normally this is a separate result set with OUT parameters + of stored procedures. Currently unsupported for the local + version. +*/ + +bool Protocol_local::send_out_parameters(List<Item_param> *sp_params) +{ + return FALSE; +} + + +/** Called for statements that don't have a result set, at statement end. */ + +bool +Protocol_local::send_ok(uint server_status, uint statement_warn_count, + ulonglong affected_rows, ulonglong last_insert_id, + const char *message) +{ + /* + Just make sure nothing is sent to the client, we have grabbed + the status information in the connection diagnostics area. + */ + return FALSE; +} + + +/** + Called at the end of a result set. Append a complete + result set to the list in Ed_connection. + + Don't send anything to the client, but instead finish + building of the result set at hand. +*/ + +bool Protocol_local::send_eof(uint server_status, uint statement_warn_count) +{ + Ed_result_set *ed_result_set; + + DBUG_ASSERT(m_rset); + + opt_add_row_to_rset(); + m_current_row= 0; + + ed_result_set= new (&m_rset_root) Ed_result_set(m_rset, m_column_count, + &m_rset_root); + + m_rset= NULL; + + if (! ed_result_set) + return TRUE; + + /* In case of successful allocation memory ownership was transferred. */ + DBUG_ASSERT(!alloc_root_inited(&m_rset_root)); + + /* + Link the created Ed_result_set instance into the list of connection + result sets. Never fails. + */ + m_connection->add_result_set(ed_result_set); + return FALSE; +} + + +/** Called to send an error to the client at the end of a statement. */ + +bool +Protocol_local::send_error(uint sql_errno, const char *err_msg, const char*) +{ + /* + Just make sure that nothing is sent to the client (default + implementation). + */ + return FALSE; +} + + +#ifdef EMBEDDED_LIBRARY +void Protocol_local::remove_last_row() +{ } +#endif diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h new file mode 100644 index 00000000000..11017b127b1 --- /dev/null +++ b/sql/sql_prepare.h @@ -0,0 +1,367 @@ +#ifndef SQL_PREPARE_H +#define SQL_PREPARE_H +/* Copyright (C) 1995-2008 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "sql_error.h" + +class THD; +struct LEX; + +/** + An interface that is used to take an action when + the locking module notices that a table version has changed + since the last execution. "Table" here may refer to any kind of + table -- a base table, a temporary table, a view or an + information schema table. + + When we open and lock tables for execution of a prepared + statement, we must verify that they did not change + since statement prepare. If some table did change, the statement + parse tree *may* be no longer valid, e.g. in case it contains + optimizations that depend on table metadata. + + This class provides an interface (a method) that is + invoked when such a situation takes place. + The implementation of the method simply reports an error, but + the exact details depend on the nature of the SQL statement. + + At most 1 instance of this class is active at a time, in which + case THD::m_reprepare_observer is not NULL. + + @sa check_and_update_table_version() for details of the + version tracking algorithm + + @sa Open_tables_state::m_reprepare_observer for the life cycle + of metadata observers. +*/ + +class Reprepare_observer +{ +public: + /** + Check if a change of metadata is OK. In future + the signature of this method may be extended to accept the old + and the new versions, but since currently the check is very + simple, we only need the THD to report an error. + */ + bool report_error(THD *thd); + bool is_invalidated() const { return m_invalidated; } + void reset_reprepare_observer() { m_invalidated= FALSE; } +private: + bool m_invalidated; +}; + + +void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length); +void mysqld_stmt_execute(THD *thd, char *packet, uint packet_length); +void mysqld_stmt_close(THD *thd, char *packet); +void mysql_sql_stmt_prepare(THD *thd); +void mysql_sql_stmt_execute(THD *thd); +void mysql_sql_stmt_close(THD *thd); +void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length); +void mysqld_stmt_reset(THD *thd, char *packet); +void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); +void reinit_stmt_before_use(THD *thd, LEX *lex); + +/** + Execute a fragment of server code in an isolated context, so that + it doesn't leave any effect on THD. THD must have no open tables. + The code must not leave any open tables around. + The result of execution (if any) is stored in Ed_result. +*/ + +class Server_runnable +{ +public: + virtual bool execute_server_code(THD *thd)= 0; + virtual ~Server_runnable(); +}; + + +/** + Execute direct interface. + + @todo Implement support for prelocked mode. +*/ + +class Ed_row; + +/** + Ed_result_set -- a container with result set rows. + @todo Implement support for result set metadata and + automatic type conversion. +*/ + +class Ed_result_set: public Sql_alloc +{ +public: + operator List<Ed_row>&() { return *m_rows; } + unsigned int size() const { return m_rows->elements; } + + Ed_result_set(List<Ed_row> *rows_arg, size_t column_count, + MEM_ROOT *mem_root_arg); + + /** We don't call member destructors, they all are POD types. */ + ~Ed_result_set() {} + + size_t get_field_count() const { return m_column_count; } + + static void operator delete(void *ptr, size_t size) throw (); +private: + Ed_result_set(const Ed_result_set &); /* not implemented */ + Ed_result_set &operator=(Ed_result_set &); /* not implemented */ +private: + MEM_ROOT m_mem_root; + size_t m_column_count; + List<Ed_row> *m_rows; + Ed_result_set *m_next_rset; + friend class Ed_connection; +}; + + +class Ed_connection +{ +public: + /** + Construct a new "execute direct" connection. + + The connection can be used to execute SQL statements. + If the connection failed to initialize, the error + will be returned on the attempt to execute a statement. + + @pre thd must have no open tables + while the connection is used. However, + Ed_connection works okay in LOCK TABLES mode. + Other properties of THD, such as the current warning + information, errors, etc. do not matter and are + preserved by Ed_connection. One thread may have many + Ed_connections created for it. + */ + Ed_connection(THD *thd); + + /** + Execute one SQL statement. + + Until this method is executed, no other methods of + Ed_connection can be used. Life cycle of Ed_connection is: + + Initialized -> a statement has been executed -> + look at result, move to next result -> + look at result, move to next result -> + ... + moved beyond the last result == Initialized. + + This method can be called repeatedly. Once it's invoked, + results of the previous execution are lost. + + A result of execute_direct() can be either: + + - success, no result set rows. In this case get_field_count() + returns 0. This happens after execution of INSERT, UPDATE, + DELETE, DROP and similar statements. Some other methods, such + as get_affected_rows() can be used to retrieve additional + result information. + + - success, there are some result set rows (maybe 0). E.g. + happens after SELECT. In this case get_field_count() returns + the number of columns in a result set and store_result() + can be used to retrieve a result set.. + + - an error, methods to retrieve error information can + be used. + + @return execution status + @retval FALSE success, use get_field_count() + to determine what to do next. + @retval TRUE error, use get_last_error() + to see the error number. + */ + bool execute_direct(LEX_STRING sql_text); + + /** + Same as the previous, but takes an instance of Server_runnable + instead of SQL statement text. + + @return execution status + + @retval FALSE success, use get_field_count() + if your code fragment is supposed to + return a result set + @retval TRUE failure + */ + bool execute_direct(Server_runnable *server_runnable); + + /** + Get the number of result set fields. + + This method is valid only if we have a result: + execute_direct() has been called. Otherwise + the returned value is undefined. + + @sa Documentation for C API function + mysql_field_count() + */ + ulong get_field_count() const + { + return m_current_rset ? m_current_rset->get_field_count() : 0; + } + + /** + Get the number of affected (deleted, updated) + rows for the current statement. Can be + used for statements with get_field_count() == 0. + + @sa Documentation for C API function + mysql_affected_rows(). + */ + ulonglong get_affected_rows() const + { + return m_diagnostics_area.affected_rows(); + } + + /** + Get the last insert id, if any. + + @sa Documentation for mysql_insert_id(). + */ + ulonglong get_last_insert_id() const + { + return m_diagnostics_area.last_insert_id(); + } + + /** + Get the total number of warnings for the last executed + statement. Note, that there is only one warning list even + if a statement returns multiple results. + + @sa Documentation for C API function + mysql_num_warnings(). + */ + ulong get_warn_count() const + { + return m_warning_info.warn_count(); + } + /** + Get the server warnings as a result set. + The result set has fixed metadata: + The first column is the level. + The second is a numeric code. + The third is warning text. + */ + List<MYSQL_ERROR> *get_warn_list() { return &m_warning_info.warn_list(); } + /** + The following members are only valid if execute_direct() + or move_to_next_result() returned an error. + They never fail, but if they are called when there is no + result, or no error, the result is not defined. + */ + const char *get_last_error() const { return m_diagnostics_area.message(); } + unsigned int get_last_errno() const { return m_diagnostics_area.sql_errno(); } + const char *get_last_sqlstate() const { return m_diagnostics_area.get_sqlstate(); } + + /** + Provided get_field_count() is not 0, this never fails. You don't + need to free the result set, this is done automatically when + you advance to the next result set or destroy the connection. + Not returning const because of List iterator not accepting + Should be used when you would like Ed_connection to manage + result set memory for you. + */ + Ed_result_set *use_result_set() { return m_current_rset; } + /** + Provided get_field_count() is not 0, this never fails. You + must free the returned result set. This can be called only + once after execute_direct(). + Should be used when you would like to get the results + and destroy the connection. + */ + Ed_result_set *store_result_set(); + + /** + If the query returns multiple results, this method + can be checked if there is another result beyond the next + one. + Never fails. + */ + bool has_next_result() const { return test(m_current_rset->m_next_rset); } + /** + Only valid to call if has_next_result() returned true. + Otherwise the result is undefined. + */ + bool move_to_next_result() + { + m_current_rset= m_current_rset->m_next_rset; + return test(m_current_rset); + } + + ~Ed_connection() { free_old_result(); } +private: + Diagnostics_area m_diagnostics_area; + Warning_info m_warning_info; + /** + Execute direct interface does not support multi-statements, only + multi-results. So we never have a situation when we have + a mix of result sets and OK or error packets. We either + have a single result set, a single error, or a single OK, + or we have a series of result sets, followed by an OK or error. + */ + THD *m_thd; + Ed_result_set *m_rsets; + Ed_result_set *m_current_rset; + friend class Protocol_local; +private: + void free_old_result(); + void add_result_set(Ed_result_set *ed_result_set); +private: + Ed_connection(const Ed_connection &); /* not implemented */ + Ed_connection &operator=(Ed_connection &); /* not implemented */ +}; + + +/** One result set column. */ + +struct Ed_column: public LEX_STRING +{ + /** Implementation note: destructor for this class is never called. */ +}; + + +/** One result set record. */ + +class Ed_row: public Sql_alloc +{ +public: + const Ed_column &operator[](const unsigned int column_index) const + { + return *get_column(column_index); + } + const Ed_column *get_column(const unsigned int column_index) const + { + DBUG_ASSERT(column_index < size()); + return m_column_array + column_index; + } + size_t size() const { return m_column_count; } + + Ed_row(Ed_column *column_array_arg, size_t column_count_arg) + :m_column_array(column_array_arg), + m_column_count(column_count_arg) + {} +private: + Ed_column *m_column_array; + size_t m_column_count; /* TODO: change to point to metadata */ +}; + +#endif // SQL_PREPARE_H diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index 8c9b147089f..69e5bc3cbb4 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -47,7 +47,7 @@ const char * const _unknown_func_ = "<unknown>"; int fill_query_profile_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond) { -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) return(thd->profiling.fill_statistics_info(thd, tables, cond)); #else my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILE", "enable-profiling"); @@ -129,7 +129,7 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table) } -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) #define RUSAGE_USEC(tv) ((tv).tv_sec*1000*1000 + (tv).tv_usec) #define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1))-RUSAGE_USEC((tv2))) @@ -415,7 +415,7 @@ bool PROFILING::show_profiles() MYSQL_TYPE_DOUBLE)); field_list.push_back(new Item_empty_string("Query", 40)); - if (thd->protocol->send_fields(&field_list, + if (thd->protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); diff --git a/sql/sql_profile.h b/sql/sql_profile.h index 245959e0953..bffe1cb576b 100644 --- a/sql/sql_profile.h +++ b/sql/sql_profile.h @@ -41,7 +41,7 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table); #define PROFILE_ALL (uint)(~0) -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) #include "mysql_priv.h" #ifdef HAVE_SYS_RESOURCE_H diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 5f07df64905..4e5ce08ab5d 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -377,7 +377,7 @@ static ulonglong get_heartbeat_period(THD * thd) my_bool null_value; LEX_STRING name= { C_STRING_WITH_LEN("master_heartbeat_period")}; user_var_entry *entry= - (user_var_entry*) hash_search(&thd->user_vars, (uchar*) name.str, + (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str, name.length); return entry? entry->val_int(&null_value) : 0; } @@ -1674,7 +1674,7 @@ bool mysql_show_binlog_events(THD* thd) DBUG_ENTER("mysql_show_binlog_events"); Log_event::init_show_field_list(&field_list); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -1849,7 +1849,7 @@ bool show_binlog_info(THD* thd) field_list.push_back(new Item_empty_string("Binlog_Do_DB",255)); field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); protocol->prepare_for_resend(); @@ -1895,13 +1895,13 @@ bool show_binlogs(THD* thd) if (!mysql_bin_log.is_open()) { my_message(ER_NO_BINARY_LOGGING, ER(ER_NO_BINARY_LOGGING), MYF(0)); - return 1; + DBUG_RETURN(TRUE); } field_list.push_back(new Item_empty_string("Log_name", 255)); field_list.push_back(new Item_return_int("File_size", 20, MYSQL_TYPE_LONGLONG)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7bd373207ec..6705f37c538 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1002,13 +1002,13 @@ JOIN::optimize() } if (const_tables && !thd->locked_tables && !(select_options & SELECT_NO_UNLOCK)) - mysql_unlock_some_tables(thd, table, const_tables); + mysql_unlock_some_tables(thd, all_tables, const_tables); if (!conds && outer_join) { /* Handle the case where we have an OUTER JOIN without a WHERE */ conds=new Item_int((longlong) 1,1); // Always true } - select= make_select(*table, const_table_map, + select= make_select(*all_tables, const_table_map, const_table_map, conds, 1, &error); if (error) { /* purecov: inspected */ @@ -1700,8 +1700,8 @@ JOIN::exec() (zero_result_cause?zero_result_cause:"No tables used")); else { - if (result->send_fields(*columns_list, - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + if (result->send_result_set_metadata(*columns_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { DBUG_VOID_RETURN; } @@ -2094,15 +2094,17 @@ JOIN::exec() if (curr_join->make_sum_func_list(*curr_all_fields, *curr_fields_list, 1, TRUE) || - prepare_sum_aggregators (curr_join->sum_funcs, !curr_join->join_tab || - !curr_join->join_tab->is_using_agg_loose_index_scan()) || + prepare_sum_aggregators(curr_join->sum_funcs, + !curr_join->join_tab || + !curr_join->join_tab-> + is_using_agg_loose_index_scan()) || setup_sum_funcs(curr_join->thd, curr_join->sum_funcs) || thd->is_fatal_error) DBUG_VOID_RETURN; } if (curr_join->group_list || curr_join->order) { - DBUG_PRINT("info",("Sorting for send_fields")); + DBUG_PRINT("info",("Sorting for send_result_set_metadata")); thd_proc_info(thd, "Sorting result"); /* If we have already done the group, add HAVING to sorted table */ if (curr_join->tmp_having && ! curr_join->group_list && @@ -2242,7 +2244,7 @@ JOIN::exec() { thd_proc_info(thd, "Sending data"); DBUG_PRINT("info", ("%s", thd->proc_info)); - result->send_fields((procedure ? curr_join->procedure_fields_list : + result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list : *curr_fields_list), Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); error= do_select(curr_join, curr_fields_list, NULL, procedure); @@ -2689,7 +2691,10 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, goto error; // Fatal error } else + { found_const_table_map|= s->table->map; + s->table->pos_in_table_list->optimized_away= TRUE; + } } /* loop until no more const tables are found */ @@ -2926,7 +2931,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, join->join_tab=stat; join->map2table=stat_ref; - join->table= join->all_tables=table_vector; + join->all_tables= table_vector; join->const_tables=const_count; join->found_const_table_map=found_const_table_map; @@ -3631,16 +3636,16 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, Item_func *arg0=(Item_func *)(func->arguments()[0]), *arg1=(Item_func *)(func->arguments()[1]); if (arg1->const_item() && - ((functype == Item_func::GE_FUNC && arg1->val_real() > 0) || - (functype == Item_func::GT_FUNC && arg1->val_real() >=0)) && arg0->type() == Item::FUNC_ITEM && - arg0->functype() == Item_func::FT_FUNC) + arg0->functype() == Item_func::FT_FUNC && + ((functype == Item_func::GE_FUNC && arg1->val_real() > 0) || + (functype == Item_func::GT_FUNC && arg1->val_real() >=0))) cond_func=(Item_func_match *) arg0; else if (arg0->const_item() && - ((functype == Item_func::LE_FUNC && arg0->val_real() > 0) || - (functype == Item_func::LT_FUNC && arg0->val_real() >=0)) && arg1->type() == Item::FUNC_ITEM && - arg1->functype() == Item_func::FT_FUNC) + arg1->functype() == Item_func::FT_FUNC && + ((functype == Item_func::LE_FUNC && arg0->val_real() > 0) || + (functype == Item_func::LT_FUNC && arg0->val_real() >=0))) cond_func=(Item_func_match *) arg1; } } @@ -4005,7 +4010,7 @@ is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args) join->select_lex->olap == ROLLUP_TYPE) /* Check (B3) for ROLLUP */ return false; - if (join->make_sum_func_list(join->all_fields, join->fields_list, 1)) + if (join->make_sum_func_list(join->all_fields, join->fields_list, true)) return false; for (sum_item_ptr= join->sum_funcs; *sum_item_ptr; sum_item_ptr++) @@ -4346,7 +4351,8 @@ best_access_path(JOIN *join, in ReuseRangeEstimateForRef-3. */ if (table->quick_keys.is_set(key) && - const_part & (1 << table->quick_key_parts[key]) && + (const_part & ((1 << table->quick_key_parts[key])-1)) == + ((1 << table->quick_key_parts[key])-1) && table->quick_n_ranges[key] == 1 && records > (double) table->quick_rows[key]) { @@ -5616,7 +5622,7 @@ get_best_combination(JOIN *join) { TABLE *form; *j= *join->best_positions[tablenr].table; - form=join->table[tablenr]=j->table; + form=join->all_tables[tablenr]=j->table; used_tables|= form->map; form->reginfo.join_tab=j; if (!*j->on_expr_ref) @@ -5888,7 +5894,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table) DBUG_RETURN(TRUE); /* purecov: inspected */ join_tab= parent->join_tab_reexec; - table= &parent->table_reexec[0]; parent->table_reexec[0]= tmp_table; + parent->table_reexec[0]= tmp_table; tables= 1; const_tables= 0; const_table_map= 0; @@ -6920,24 +6926,23 @@ void JOIN::cleanup(bool full) { DBUG_ENTER("JOIN::cleanup"); - if (table) + if (all_tables) { JOIN_TAB *tab,*end; /* Only a sorted table may be cached. This sorted table is always the - first non const table in join->table + first non const table in join->all_tables */ if (tables > const_tables) // Test for not-const tables { - free_io_cache(table[const_tables]); - filesort_free_buffers(table[const_tables],full); + free_io_cache(all_tables[const_tables]); + filesort_free_buffers(all_tables[const_tables],full); } if (full) { for (tab= join_tab, end= tab+tables; tab != end; tab++) tab->cleanup(); - table= 0; } else { @@ -7236,7 +7241,7 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, if (having && having->val_int() == 0) send_row=0; } - if (!(result->send_fields(fields, + if (!(result->send_result_set_metadata(fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))) { bool send_error= FALSE; @@ -7266,7 +7271,7 @@ static void clear_tables(JOIN *join) are not re-calculated. */ for (uint i=join->const_tables ; i < join->tables ; i++) - mark_as_null_row(join->table[i]); // All fields are NULL + mark_as_null_row(join->all_tables[i]); // All fields are NULL } /***************************************************************************** @@ -9208,7 +9213,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) thd->substitute_null_with_insert_id)) { #ifdef HAVE_QUERY_CACHE - query_cache_abort(&thd->net); + query_cache_abort(&thd->query_cache_tls); #endif COND *new_cond; if ((new_cond= new Item_func_eq(args[0], @@ -9758,7 +9763,7 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps) Create a temp table according to a field list. Given field pointers are changed to point at tmp_table for - send_fields. The table object is self contained: it's + send_result_set_metadata. The table object is self contained: it's allocated in its own memory root, as well as Field objects created for table columns. This function will replace Item_sum items in 'fields' list with @@ -9943,6 +9948,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, share->primary_key= MAX_KEY; // Indicate no primary key share->keys_for_keyread.init(); share->keys_in_use.init(); + if (param->schema_table) + share->db= INFORMATION_SCHEMA_NAME; /* Calculate which type of fields we will store in the temporary table */ @@ -11022,26 +11029,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) if (error == NESTED_LOOP_NO_MORE_ROWS) error= NESTED_LOOP_OK; - if (error == NESTED_LOOP_OK) - { - /* - Sic: this branch works even if rc != 0, e.g. when - send_data above returns an error. - */ - if (!table) // If sending data to client - { - /* - The following will unlock all cursors if the command wasn't an - update command - */ - join->join_free(); // Unlock all cursors - if (join->result->send_eof()) - rc= 1; // Don't send error - } - DBUG_PRINT("info",("%ld records output", (long) join->send_records)); - } - else - rc= -1; + if (table) { int tmp, new_errno= 0; @@ -11058,6 +11046,29 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) if (new_errno) table->file->print_error(new_errno,MYF(0)); } + else + { + /* + The following will unlock all cursors if the command wasn't an + update command + */ + join->join_free(); // Unlock all cursors + } + if (error == NESTED_LOOP_OK) + { + /* + Sic: this branch works even if rc != 0, e.g. when + send_data above returns an error. + */ + if (!table) // If sending data to client + { + if (join->result->send_eof()) + rc= 1; // Don't send error + } + DBUG_PRINT("info",("%ld records output", (long) join->send_records)); + } + else + rc= -1; #ifndef DBUG_OFF if (rc) { @@ -11905,10 +11916,8 @@ join_init_quick_read_record(JOIN_TAB *tab) } -int rr_sequential(READ_RECORD *info); -int init_read_record_seq(JOIN_TAB *tab) +int read_first_record_seq(JOIN_TAB *tab) { - tab->read_record.read_record= rr_sequential; if (tab->read_record.file->ha_rnd_init(1)) return 1; return (*tab->read_record.read_record)(&tab->read_record); @@ -13918,8 +13927,8 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, extra_length= ALIGN_SIZE(key_length)-key_length; } - if (hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0, - key_length, (hash_get_key) 0, 0, 0)) + if (my_hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0, + key_length, (my_hash_get_key) 0, 0, 0)) { my_free((char*) key_buffer,MYF(0)); DBUG_RETURN(1); @@ -13960,7 +13969,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, key_pos+= *field_length++; } /* Check if it exists before */ - if (hash_search(&hash, org_key_pos, key_length)) + if (my_hash_search(&hash, org_key_pos, key_length)) { /* Duplicated found ; Remove the row */ if ((error=file->ha_delete_row(record))) @@ -13971,14 +13980,14 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, key_pos+=extra_length; } my_free((char*) key_buffer,MYF(0)); - hash_free(&hash); + my_hash_free(&hash); file->extra(HA_EXTRA_NO_CACHE); (void) file->ha_rnd_end(); DBUG_RETURN(0); err: my_free((char*) key_buffer,MYF(0)); - hash_free(&hash); + my_hash_free(&hash); file->extra(HA_EXTRA_NO_CACHE); (void) file->ha_rnd_end(); if (error) @@ -15014,7 +15023,7 @@ test_if_group_changed(List<Cached_item> &list) Only FIELD_ITEM:s and FUNC_ITEM:s needs to be saved between groups. Change old item_field to use a new field with points at saved fieldvalue - This function is only called before use of send_fields. + This function is only called before use of send_result_set_metadata. @param thd THD pointer @param param temporary table parameters @@ -15254,7 +15263,7 @@ bool JOIN::alloc_func_list() Initialize 'sum_funcs' array with all Item_sum objects. @param field_list All items - @param send_fields Items in select list + @param send_result_set_metadata Items in select list @param before_group_by Set to 1 if this is called before GROUP BY handling @param recompute Set to TRUE if sum_funcs must be recomputed @@ -15264,7 +15273,7 @@ bool JOIN::alloc_func_list() 1 error */ -bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields, +bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_result_set_metadata, bool before_group_by, bool recompute) { List_iterator_fast<Item> it(field_list); @@ -15286,7 +15295,7 @@ bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields, if (before_group_by && rollup.state == ROLLUP::STATE_INITED) { rollup.state= ROLLUP::STATE_READY; - if (rollup_make_fields(field_list, send_fields, &func)) + if (rollup_make_fields(field_list, send_result_set_metadata, &func)) DBUG_RETURN(TRUE); // Should never happen } else if (rollup.state == ROLLUP::STATE_NONE) @@ -15471,10 +15480,10 @@ static bool setup_sum_funcs(THD *thd, Item_sum **func_ptr) static bool prepare_sum_aggregators(Item_sum **func_ptr, bool need_distinct) { Item_sum *func; - DBUG_ENTER("setup_sum_funcs"); + DBUG_ENTER("prepare_sum_aggregators"); while ((func= *(func_ptr++))) { - if (func->set_aggregator(need_distinct && func->with_distinct ? + if (func->set_aggregator(need_distinct && func->has_with_distinct() ? Aggregator::DISTINCT_AGGREGATOR : Aggregator::SIMPLE_AGGREGATOR)) DBUG_RETURN(TRUE); @@ -16591,18 +16600,35 @@ static void print_join(THD *thd, { /* List is reversed => we should reverse it before using */ List_iterator_fast<TABLE_LIST> ti(*tables); - TABLE_LIST **table= (TABLE_LIST **)thd->alloc(sizeof(TABLE_LIST*) * - tables->elements); - if (table == 0) + TABLE_LIST **table; + uint non_const_tables= 0; + + for (TABLE_LIST *t= ti++; t ; t= ti++) + if (!t->optimized_away) + non_const_tables++; + if (!non_const_tables) + { + str->append(STRING_WITH_LEN("dual")); + return; // all tables were optimized away + } + ti.rewind(); + + if (!(table= (TABLE_LIST **)thd->alloc(sizeof(TABLE_LIST*) * + non_const_tables))) return; // out of memory - for (TABLE_LIST **t= table + (tables->elements - 1); t >= table; t--) - *t= ti++; + TABLE_LIST *tmp, **t= table + (non_const_tables - 1); + while ((tmp= ti++)) + { + if (tmp->optimized_away) + continue; + *t--= tmp; + } DBUG_ASSERT(tables->elements >= 1); (*table)->print(thd, str, query_type); - TABLE_LIST **end= table + tables->elements; + TABLE_LIST **end= table + non_const_tables; for (TABLE_LIST **tbl= table + 1; tbl < end; tbl++) { TABLE_LIST *curr= *tbl; diff --git a/sql/sql_select.h b/sql/sql_select.h index f7dc09ddc62..853931f437a 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -137,7 +137,6 @@ enum enum_nested_loop_state typedef enum_nested_loop_state (*Next_select_func)(JOIN *, struct st_join_table *, bool); -typedef int (*Read_record_func)(struct st_join_table *tab); Next_select_func setup_end_select_func(JOIN *join); @@ -165,7 +164,7 @@ typedef struct st_join_table { */ uint packed_info; - Read_record_func read_first_record; + READ_RECORD::Setup_func read_first_record; Next_select_func next_select; READ_RECORD read_record; /* @@ -173,8 +172,8 @@ typedef struct st_join_table { if it is executed by an alternative full table scan when the left operand of the subquery predicate is evaluated to NULL. */ - Read_record_func save_read_first_record;/* to save read_first_record */ - int (*save_read_record) (READ_RECORD *);/* to save read_record.read_record */ + READ_RECORD::Setup_func save_read_first_record;/* to save read_first_record */ + READ_RECORD::Read_func save_read_record;/* to save read_record.read_record */ double worst_seeks; key_map const_keys; /**< Keys with constant part */ key_map checked_keys; /**< Keys checked in find_best */ @@ -283,7 +282,7 @@ 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 - TABLE **table,**all_tables,*sort_by_table; + TABLE **all_tables,*sort_by_table; uint tables,const_tables; uint send_group_parts; /** @@ -439,7 +438,7 @@ public: select_result *result_arg) { join_tab= join_tab_save= 0; - table= 0; + all_tables= 0; tables= 0; const_tables= 0; join_list= 0; diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 33058887952..e8fa3d984a7 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -120,8 +120,8 @@ bool servers_init(bool dont_read_servers_table) DBUG_RETURN(TRUE); /* initialise our servers cache */ - if (hash_init(&servers_cache, system_charset_info, 32, 0, 0, - (hash_get_key) servers_cache_get_key, 0, 0)) + if (my_hash_init(&servers_cache, system_charset_info, 32, 0, 0, + (my_hash_get_key) servers_cache_get_key, 0, 0)) { return_val= TRUE; /* we failed, out of memory? */ goto end; @@ -646,9 +646,10 @@ delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options) server_options->server_name_length)); - if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache, - (uchar*) server_options->server_name, - server_options->server_name_length))) + if (!(server= (FOREIGN_SERVER *) + my_hash_search(&servers_cache, + (uchar*) server_options->server_name, + server_options->server_name_length))) { DBUG_PRINT("info", ("server_name %s length %d not found!", server_options->server_name, @@ -663,7 +664,7 @@ delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options) server->server_name, server->server_name_length)); - VOID(hash_delete(&servers_cache, (uchar*) server)); + VOID(my_hash_delete(&servers_cache, (uchar*) server)); error= 0; @@ -770,7 +771,7 @@ int update_server_record_in_cache(FOREIGN_SERVER *existing, /* delete the existing server struct from the server cache */ - VOID(hash_delete(&servers_cache, (uchar*)existing)); + VOID(my_hash_delete(&servers_cache, (uchar*)existing)); /* Insert the altered server struct into the server cache @@ -965,8 +966,8 @@ int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options) rw_wrlock(&THR_LOCK_servers); /* hit the memory first */ - if (hash_search(&servers_cache, (uchar*) server_options->server_name, - server_options->server_name_length)) + if (my_hash_search(&servers_cache, (uchar*) server_options->server_name, + server_options->server_name_length)) goto end; @@ -1014,9 +1015,9 @@ int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options) rw_wrlock(&THR_LOCK_servers); - if (!(existing= (FOREIGN_SERVER *) hash_search(&servers_cache, - (uchar*) name.str, - name.length))) + if (!(existing= (FOREIGN_SERVER *) my_hash_search(&servers_cache, + (uchar*) name.str, + name.length))) goto end; altered= (FOREIGN_SERVER *)alloc_root(&mem, @@ -1195,7 +1196,7 @@ prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options, void servers_free(bool end) { DBUG_ENTER("servers_free"); - if (!hash_inited(&servers_cache)) + if (!my_hash_inited(&servers_cache)) DBUG_VOID_RETURN; if (!end) { @@ -1205,7 +1206,7 @@ void servers_free(bool end) } rwlock_destroy(&THR_LOCK_servers); free_root(&mem,MYF(0)); - hash_free(&servers_cache); + my_hash_free(&servers_cache); DBUG_VOID_RETURN; } @@ -1286,9 +1287,9 @@ FOREIGN_SERVER *get_server_by_name(MEM_ROOT *mem, const char *server_name, DBUG_PRINT("info", ("locking servers_cache")); rw_rdlock(&THR_LOCK_servers); - if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache, - (uchar*) server_name, - server_name_length))) + if (!(server= (FOREIGN_SERVER *) my_hash_search(&servers_cache, + (uchar*) server_name, + server_name_length))) { DBUG_PRINT("info", ("server_name %s length %u not found!", server_name, (unsigned) server_name_length)); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 61fda3ec184..518712a8db0 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -25,6 +25,7 @@ #include "sql_trigger.h" #include "authors.h" #include "contributors.h" +#include "sql_partition.h" #ifdef HAVE_EVENT_SCHEDULER #include "events.h" #include "event_data_objects.h" @@ -77,6 +78,12 @@ static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **), static void store_key_options(THD *thd, String *packet, TABLE *table, KEY *key_info); +static void get_cs_converted_string_value(THD *thd, + String *input_str, + String *output_str, + CHARSET_INFO *cs, + bool use_hex); + static void append_algorithm(TABLE_LIST *table, String *buff); @@ -216,7 +223,7 @@ bool mysqld_show_authors(THD *thd) field_list.push_back(new Item_empty_string("Location",40)); field_list.push_back(new Item_empty_string("Comment",80)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -250,7 +257,7 @@ bool mysqld_show_contributors(THD *thd) field_list.push_back(new Item_empty_string("Location",40)); field_list.push_back(new Item_empty_string("Comment",80)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -310,6 +317,7 @@ static struct show_privileges_st sys_privileges[]= {"Shutdown","Server Admin", "To shut down the server"}, {"Super","Server Admin","To use KILL thread, SET GLOBAL, CHANGE MASTER, etc."}, {"Trigger","Tables", "To use triggers"}, + {"Create tablespace", "Server Admin", "To create/alter/drop tablespaces"}, {"Update", "Tables", "To update existing rows"}, {"Usage","Server Admin","No privileges - allow connect only"}, {NullS, NullS, NullS} @@ -325,7 +333,7 @@ bool mysqld_show_privileges(THD *thd) field_list.push_back(new Item_empty_string("Context",15)); field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -344,94 +352,6 @@ bool mysqld_show_privileges(THD *thd) } -/*************************************************************************** - List all column types -***************************************************************************/ - -struct show_column_type_st -{ - const char *type; - uint size; - const char *min_value; - const char *max_value; - uint precision; - uint scale; - const char *nullable; - const char *auto_increment; - const char *unsigned_attr; - const char *zerofill; - const char *searchable; - const char *case_sensitivity; - const char *default_value; - const char *comment; -}; - -/* TODO: Add remaning types */ - -static struct show_column_type_st sys_column_types[]= -{ - {"tinyint", - 1, "-128", "127", 0, 0, "YES", "YES", - "NO", "YES", "YES", "NO", "NULL,0", - "A very small integer"}, - {"tinyint unsigned", - 1, "0" , "255", 0, 0, "YES", "YES", - "YES", "YES", "YES", "NO", "NULL,0", - "A very small integer"}, -}; - -bool mysqld_show_column_types(THD *thd) -{ - List<Item> field_list; - Protocol *protocol= thd->protocol; - DBUG_ENTER("mysqld_show_column_types"); - - field_list.push_back(new Item_empty_string("Type",30)); - field_list.push_back(new Item_int("Size",(longlong) 1, - MY_INT64_NUM_DECIMAL_DIGITS)); - field_list.push_back(new Item_empty_string("Min_Value",20)); - field_list.push_back(new Item_empty_string("Max_Value",20)); - field_list.push_back(new Item_return_int("Prec", 4, MYSQL_TYPE_SHORT)); - field_list.push_back(new Item_return_int("Scale", 4, MYSQL_TYPE_SHORT)); - field_list.push_back(new Item_empty_string("Nullable",4)); - field_list.push_back(new Item_empty_string("Auto_Increment",4)); - field_list.push_back(new Item_empty_string("Unsigned",4)); - field_list.push_back(new Item_empty_string("Zerofill",4)); - field_list.push_back(new Item_empty_string("Searchable",4)); - field_list.push_back(new Item_empty_string("Case_Sensitive",4)); - field_list.push_back(new Item_empty_string("Default",NAME_CHAR_LEN)); - field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN)); - - if (protocol->send_fields(&field_list, - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) - DBUG_RETURN(TRUE); - - /* TODO: Change the loop to not use 'i' */ - for (uint i=0; i < sizeof(sys_column_types)/sizeof(sys_column_types[0]); i++) - { - protocol->prepare_for_resend(); - protocol->store(sys_column_types[i].type, system_charset_info); - protocol->store((ulonglong) sys_column_types[i].size); - protocol->store(sys_column_types[i].min_value, system_charset_info); - protocol->store(sys_column_types[i].max_value, system_charset_info); - protocol->store_short((longlong) sys_column_types[i].precision); - protocol->store_short((longlong) sys_column_types[i].scale); - protocol->store(sys_column_types[i].nullable, system_charset_info); - protocol->store(sys_column_types[i].auto_increment, system_charset_info); - protocol->store(sys_column_types[i].unsigned_attr, system_charset_info); - protocol->store(sys_column_types[i].zerofill, system_charset_info); - protocol->store(sys_column_types[i].searchable, system_charset_info); - protocol->store(sys_column_types[i].case_sensitivity, system_charset_info); - protocol->store(sys_column_types[i].default_value, system_charset_info); - protocol->store(sys_column_types[i].comment, system_charset_info); - if (protocol->write()) - DBUG_RETURN(TRUE); - } - my_eof(thd); - DBUG_RETURN(FALSE); -} - - /* find_files() - find files in a given directory. @@ -560,7 +480,7 @@ find_files(THD *thd, List<LEX_STRING> *files, const char *db, table_list.table_name= uname; table_list.table_name_length= file_name_len; table_list.grant.privilege=col_access; - if (check_grant(thd, TABLE_ACLS, &table_list, 1, 1, 1)) + if (check_grant(thd, TABLE_ACLS, &table_list, TRUE, 1, TRUE)) continue; } #endif @@ -763,7 +683,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) max(buffer.length(),1024))); } - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); protocol->prepare_for_resend(); @@ -848,7 +768,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname, field_list.push_back(new Item_empty_string("Database",NAME_CHAR_LEN)); field_list.push_back(new Item_empty_string("Create Database",1024)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -916,7 +836,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) } restore_record(table, s->default_values); // Get empty record table->use_all_columns(); - if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS)) + if (thd->protocol->send_result_set_metadata(&field_list, Protocol::SEND_DEFAULTS)) DBUG_VOID_RETURN; my_eof(thd); DBUG_VOID_RETURN; @@ -1574,7 +1494,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, ((part_syntax= generate_partition_syntax(table->part_info, &part_syntax_len, FALSE, - show_table_options)))) + show_table_options, + NULL, NULL)))) { packet->append(STRING_WITH_LEN("\n/*!50100")); packet->append(part_syntax, part_syntax_len); @@ -1806,7 +1727,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) field->maybe_null=1; field_list.push_back(field=new Item_empty_string("Info",max_query_length)); field->maybe_null=1; - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_VOID_RETURN; @@ -3187,12 +3108,31 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, int error; char key[MAX_DBKEY_LENGTH]; uint key_length; + char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &tbl, sizeof(TABLE)); - table_list.table_name= table_name->str; - table_list.db= db_name->str; + if (lower_case_table_names) + { + /* + In lower_case_table_names > 0 metadata locking and table definition + cache subsystems require normalized (lowercased) database and table + names as input. + */ + strmov(db_name_buff, db_name->str); + strmov(table_name_buff, table_name->str); + my_casedn_str(files_charset_info, db_name_buff); + my_casedn_str(files_charset_info, table_name_buff); + table_list.db= db_name_buff; + table_list.table_name= table_name_buff; + } + else + { + table_list.table_name= table_name->str; + table_list.db= db_name->str; + } + key_length= create_table_def_key(thd, key, &table_list, 0); pthread_mutex_lock(&LOCK_open); share= get_table_share(thd, &table_list, key, @@ -3230,7 +3170,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, { tbl.s= share; table_list.table= &tbl; - table_list.view= (st_lex*) share->is_view; + table_list.view= (LEX*) share->is_view; res= schema_table->process_table(thd, &table_list, table, res, db_name, table_name); closefrm(&tbl, true); @@ -3408,6 +3348,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) (!lookup_field_vals.table_value.length || lookup_field_vals.wild_table_value)) { + table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info); if (schema_table_store_record(thd, table)) goto err; /* Out of space in temporary table */ continue; @@ -3519,6 +3460,7 @@ bool store_schema_shemata(THD* thd, TABLE *table, LEX_STRING *db_name, CHARSET_INFO *cs) { restore_record(table, s->default_values); + table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info); table->field[1]->store(db_name->str, db_name->length, system_charset_info); table->field[2]->store(cs->csname, strlen(cs->csname), system_charset_info); table->field[3]->store(cs->name, strlen(cs->name), system_charset_info); @@ -3609,6 +3551,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, DBUG_ENTER("get_schema_tables_record"); restore_record(table, s->default_values); + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(table_name->str, table_name->length, cs); if (res) @@ -3856,8 +3799,9 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, #ifndef NO_EMBEDDED_ACCESS_CHECKS uint col_access; - check_access(thd,SELECT_ACL | EXTRA_ACL, db_name->str, - &tables->grant.privilege, 0, 0, test(tables->schema_table)); + check_access(thd,SELECT_ACL, db_name->str, + &tables->grant.privilege, FALSE, FALSE, + test(tables->schema_table)); col_access= get_column_grant(thd, &tables->grant, db_name->str, table_name->str, field->field_name) & COL_ACLS; @@ -3875,6 +3819,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, table->field[17]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs); #endif + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(table_name->str, table_name->length, cs); table->field[3]->store(field->field_name, strlen(field->field_name), @@ -4224,6 +4169,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, table->field[3]->store(sp_name.ptr(), sp_name.length(), cs); get_field(thd->mem_root, proc_table->field[3], &tmp_string); table->field[0]->store(tmp_string.ptr(), tmp_string.length(), cs); + table->field[1]->store(STRING_WITH_LEN("def"), cs); table->field[2]->store(sp_db.ptr(), sp_db.length(), cs); get_field(thd->mem_root, proc_table->field[2], &tmp_string); table->field[4]->store(tmp_string.ptr(), tmp_string.length(), cs); @@ -4296,7 +4242,8 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) proc_tables.table_name= proc_tables.alias= (char*) "proc"; proc_tables.table_name_length= 4; proc_tables.lock_type= TL_READ; - full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1, TRUE); + full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, FALSE, + 1, TRUE); if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup))) { DBUG_RETURN(1); @@ -4366,6 +4313,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, for (uint j=0 ; j < key_info->key_parts ; j++,key_part++) { restore_record(table, s->default_values); + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(table_name->str, table_name->length, cs); table->field[3]->store((longlong) ((key_info->flags & @@ -4482,6 +4430,7 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, tmp_db_name= db_name; tmp_table_name= table_name; } + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(tmp_db_name->str, tmp_db_name->length, cs); table->field[2]->store(tmp_table_name->str, tmp_table_name->length, cs); if (!only_share) @@ -4572,6 +4521,7 @@ bool store_constraints(THD *thd, TABLE *table, LEX_STRING *db_name, { CHARSET_INFO *cs= system_charset_info; restore_record(table, s->default_values); + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(key_name, key_len, cs); table->field[3]->store(db_name->str, db_name->length, cs); @@ -4656,10 +4606,12 @@ static bool store_trigger(THD *thd, TABLE *table, LEX_STRING *db_name, LEX_STRING sql_mode_rep; restore_record(table, s->default_values); + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(trigger_name->str, trigger_name->length, cs); table->field[3]->store(trg_event_type_names[event].str, trg_event_type_names[event].length, cs); + table->field[4]->store(STRING_WITH_LEN("def"), cs); table->field[5]->store(db_name->str, db_name->length, cs); table->field[6]->store(table_name->str, table_name->length, cs); table->field[9]->store(trigger_stmt->str, trigger_stmt->length, cs); @@ -4705,7 +4657,7 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, Table_triggers_list *triggers= tables->table->triggers; int event, timing; - if (check_table_access(thd, TRIGGER_ACL, tables, 1, TRUE)) + if (check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, TRUE)) goto ret; for (event= 0; event < (int)TRG_EVENT_MAX; event++) @@ -4755,8 +4707,10 @@ void store_key_column_usage(TABLE *table, LEX_STRING *db_name, longlong idx) { CHARSET_INFO *cs= system_charset_info; + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(key_name, key_len, cs); + table->field[3]->store(STRING_WITH_LEN("def"), cs); table->field[4]->store(db_name->str, db_name->length, cs); table->field[5]->store(table_name->str, table_name->length, cs); table->field[6]->store(con_type, con_len, cs); @@ -4869,6 +4823,57 @@ static void collect_partition_expr(List<char> &field_list, String *str) } return; } + + +/* + Convert a string in a given character set to a string which can be + used for FRM file storage in which case use_hex is TRUE and we store + the character constants as hex strings in the character set encoding + their field have. In the case of SHOW CREATE TABLE and the + PARTITIONS information schema table we instead provide utf8 strings + to the user and convert to the utf8 character set. + + SYNOPSIS + get_cs_converted_part_value_from_string() + item Item from which constant comes + input_str String as provided by val_str after + conversion to character set + output_str Out value: The string created + cs Character set string is encoded in + NULL for INT_RESULT's here + use_hex TRUE => hex string created + FALSE => utf8 constant string created + + RETURN VALUES + TRUE Error + FALSE Ok +*/ + +int get_cs_converted_part_value_from_string(THD *thd, + Item *item, + String *input_str, + String *output_str, + CHARSET_INFO *cs, + bool use_hex) +{ + if (item->result_type() == INT_RESULT) + { + longlong value= item->val_int(); + output_str->set(value, system_charset_info); + return FALSE; + } + if (!input_str) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + return TRUE; + } + get_cs_converted_string_value(thd, + input_str, + output_str, + cs, + use_hex); + return FALSE; +} #endif @@ -4882,6 +4887,7 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table, PARTITION_INFO stat_info; MYSQL_TIME time; file->get_dynamic_partition_info(&stat_info, part_id); + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[12]->store((longlong) stat_info.records, TRUE); table->field[13]->store((longlong) stat_info.mean_rec_length, TRUE); table->field[14]->store((longlong) stat_info.data_file_length, TRUE); @@ -4949,6 +4955,49 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table, return; } +static int +get_partition_column_description(THD *thd, + partition_info *part_info, + part_elem_value *list_value, + String &tmp_str) +{ + uint num_elements= part_info->part_field_list.elements; + uint i; + DBUG_ENTER("get_partition_column_description"); + + for (i= 0; i < num_elements; i++) + { + part_column_list_val *col_val= &list_value->col_val_array[i]; + if (col_val->max_value) + tmp_str.append(partition_keywords[PKW_MAXVALUE].str); + else if (col_val->null_value) + tmp_str.append("NULL"); + else + { + char buffer[MAX_KEY_LENGTH]; + String str(buffer, sizeof(buffer), &my_charset_bin); + String val_conv; + Item *item= col_val->item_expression; + + if (!(item= part_info->get_column_item(item, + part_info->part_field_array[i]))) + { + DBUG_RETURN(1); + } + String *res= item->val_str(&str); + if (get_cs_converted_part_value_from_string(thd, item, res, &val_conv, + part_info->part_field_array[i]->charset(), + FALSE)) + { + DBUG_RETURN(1); + } + tmp_str.append(val_conv); + } + if (i != num_elements - 1) + tmp_str.append(","); + } + DBUG_RETURN(0); +} static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, @@ -4984,6 +5033,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, uint part_pos= 0, part_id= 0; restore_record(table, s->default_values); + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(table_name->str, table_name->length, cs); @@ -4991,12 +5041,18 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, /* Partition method*/ switch (part_info->part_type) { case RANGE_PARTITION: - table->field[7]->store(partition_keywords[PKW_RANGE].str, - partition_keywords[PKW_RANGE].length, cs); - break; case LIST_PARTITION: - table->field[7]->store(partition_keywords[PKW_LIST].str, - partition_keywords[PKW_LIST].length, cs); + tmp_res.length(0); + if (part_info->part_type == RANGE_PARTITION) + tmp_res.append(partition_keywords[PKW_RANGE].str, + partition_keywords[PKW_RANGE].length); + else + tmp_res.append(partition_keywords[PKW_LIST].str, + partition_keywords[PKW_LIST].length); + if (part_info->column_list) + tmp_res.append(partition_keywords[PKW_COLUMNS].str, + partition_keywords[PKW_COLUMNS].length); + table->field[7]->store(tmp_res.ptr(), tmp_res.length(), cs); break; case HASH_PARTITION: tmp_res.length(0); @@ -5074,36 +5130,70 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, /* Partition description */ if (part_info->part_type == RANGE_PARTITION) { - if (part_elem->range_value != LONGLONG_MAX) - table->field[11]->store((longlong) part_elem->range_value, FALSE); + if (part_info->column_list) + { + List_iterator<part_elem_value> list_val_it(part_elem->list_val_list); + part_elem_value *list_value= list_val_it++; + tmp_str.length(0); + if (get_partition_column_description(thd, + part_info, + list_value, + tmp_str)) + { + DBUG_RETURN(1); + } + table->field[11]->store(tmp_str.ptr(), tmp_str.length(), cs); + } else - table->field[11]->store(partition_keywords[PKW_MAXVALUE].str, + { + if (part_elem->range_value != LONGLONG_MAX) + table->field[11]->store((longlong) part_elem->range_value, FALSE); + else + table->field[11]->store(partition_keywords[PKW_MAXVALUE].str, partition_keywords[PKW_MAXVALUE].length, cs); + } table->field[11]->set_notnull(); } else if (part_info->part_type == LIST_PARTITION) { List_iterator<part_elem_value> list_val_it(part_elem->list_val_list); part_elem_value *list_value; - uint no_items= part_elem->list_val_list.elements; + uint num_items= part_elem->list_val_list.elements; tmp_str.length(0); tmp_res.length(0); if (part_elem->has_null_value) { tmp_str.append("NULL"); - if (no_items > 0) + if (num_items > 0) tmp_str.append(","); } while ((list_value= list_val_it++)) { - if (!list_value->unsigned_flag) - tmp_res.set(list_value->value, cs); + if (part_info->column_list) + { + if (part_info->part_field_list.elements > 1U) + tmp_str.append("("); + if (get_partition_column_description(thd, + part_info, + list_value, + tmp_str)) + { + DBUG_RETURN(1); + } + if (part_info->part_field_list.elements > 1U) + tmp_str.append(")"); + } else - tmp_res.set((ulonglong)list_value->value, cs); - tmp_str.append(tmp_res); - if (--no_items != 0) + { + if (!list_value->unsigned_flag) + tmp_res.set(list_value->value, cs); + else + tmp_res.set((ulonglong)list_value->value, cs); + tmp_str.append(tmp_res); + } + if (--num_items != 0) tmp_str.append(","); - }; + } table->field[11]->store(tmp_str.ptr(), tmp_str.length(), cs); table->field[11]->set_notnull(); } @@ -5244,8 +5334,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) is_schema_db(et.dbname.str))) DBUG_RETURN(0); - /* ->field[0] is EVENT_CATALOG and is by default NULL */ - + sch_table->field[ISE_EVENT_CATALOG]->store(STRING_WITH_LEN("def"), scs); sch_table->field[ISE_EVENT_SCHEMA]-> store(et.dbname.str, et.dbname.length,scs); sch_table->field[ISE_EVENT_NAME]-> @@ -5526,10 +5615,12 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables, while ((f_key_info= it++)) { restore_record(table, s->default_values); + table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(db_name->str, db_name->length, cs); table->field[9]->store(table_name->str, table_name->length, cs); table->field[2]->store(f_key_info->forein_id->str, f_key_info->forein_id->length, cs); + table->field[3]->store(STRING_WITH_LEN("def"), cs); table->field[4]->store(f_key_info->referenced_db->str, f_key_info->referenced_db->length, cs); table->field[10]->store(f_key_info->referenced_table->str, @@ -6195,7 +6286,7 @@ int fill_schema_files(THD *thd, TABLE_LIST *tables, COND *cond) ST_FIELD_INFO schema_fields_info[]= { - {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"SCHEMA_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database", SKIP_OPEN_TABLE}, {"DEFAULT_CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, @@ -6209,7 +6300,7 @@ ST_FIELD_INFO schema_fields_info[]= ST_FIELD_INFO tables_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", SKIP_OPEN_TABLE}, @@ -6248,7 +6339,7 @@ ST_FIELD_INFO tables_fields_info[]= ST_FIELD_INFO columns_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field", @@ -6322,7 +6413,7 @@ ST_FIELD_INFO engines_fields_info[]= ST_FIELD_INFO events_fields_info[]= { - {"EVENT_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"EVENT_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"EVENT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db", SKIP_OPEN_TABLE}, {"EVENT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", @@ -6371,7 +6462,7 @@ ST_FIELD_INFO coll_charset_app_fields_info[]= ST_FIELD_INFO proc_fields_info[]= { {"SPECIFIC_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, - {"ROUTINE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"ROUTINE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"ROUTINE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db", SKIP_OPEN_TABLE}, {"ROUTINE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", @@ -6393,7 +6484,7 @@ ST_FIELD_INFO proc_fields_info[]= {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Created", SKIP_OPEN_TABLE}, {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Modified", SKIP_OPEN_TABLE}, {"SQL_MODE", 32*256, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, - {"ROUTINE_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment", + {"ROUTINE_COMMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Comment", SKIP_OPEN_TABLE}, {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE}, {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, @@ -6408,7 +6499,7 @@ ST_FIELD_INFO proc_fields_info[]= ST_FIELD_INFO stat_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table", OPEN_FRM_ONLY}, {"NON_UNIQUE", 1, MYSQL_TYPE_LONGLONG, 0, 0, "Non_unique", OPEN_FRM_ONLY}, @@ -6432,7 +6523,7 @@ ST_FIELD_INFO stat_fields_info[]= ST_FIELD_INFO view_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, {"VIEW_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, @@ -6451,7 +6542,7 @@ ST_FIELD_INFO view_fields_info[]= ST_FIELD_INFO user_privileges_fields_info[]= { {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} @@ -6461,7 +6552,7 @@ ST_FIELD_INFO user_privileges_fields_info[]= ST_FIELD_INFO schema_privileges_fields_info[]= { {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, @@ -6472,7 +6563,7 @@ ST_FIELD_INFO schema_privileges_fields_info[]= ST_FIELD_INFO table_privileges_fields_info[]= { {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, @@ -6484,7 +6575,7 @@ ST_FIELD_INFO table_privileges_fields_info[]= ST_FIELD_INFO column_privileges_fields_info[]= { {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, @@ -6496,7 +6587,7 @@ ST_FIELD_INFO column_privileges_fields_info[]= ST_FIELD_INFO table_constraints_fields_info[]= { - {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, @@ -6511,12 +6602,12 @@ ST_FIELD_INFO table_constraints_fields_info[]= ST_FIELD_INFO key_column_usage_fields_info[]= { - {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, @@ -6535,7 +6626,7 @@ ST_FIELD_INFO key_column_usage_fields_info[]= ST_FIELD_INFO table_names_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Tables_in_", SKIP_OPEN_TABLE}, @@ -6558,12 +6649,12 @@ ST_FIELD_INFO open_tables_fields_info[]= ST_FIELD_INFO triggers_fields_info[]= { - {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"TRIGGER_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"TRIGGER_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger", OPEN_FULL_TABLE}, {"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event", OPEN_FULL_TABLE}, - {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, + {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"EVENT_OBJECT_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, @@ -6596,7 +6687,7 @@ ST_FIELD_INFO triggers_fields_info[]= ST_FIELD_INFO partitions_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"TABLE_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"PARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, @@ -6606,7 +6697,7 @@ ST_FIELD_INFO partitions_fields_info[]= (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, {"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, - {"PARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"PARTITION_METHOD", 18, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, {"SUBPARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, {"PARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, {"SUBPARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, @@ -6686,7 +6777,7 @@ ST_FIELD_INFO files_fields_info[]= {"FILE_TYPE", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, - {"TABLE_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, {"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, @@ -6747,12 +6838,12 @@ void init_fill_schema_files_row(TABLE* table) ST_FIELD_INFO referential_constraints_fields_info[]= { - {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, - {"UNIQUE_CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, + {"UNIQUE_CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"UNIQUE_CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, @@ -6805,11 +6896,12 @@ ST_SCHEMA_TABLE schema_tables[]= fill_variables, make_old_format, 0, 0, -1, 0, 0}, {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table, get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0, - OPEN_TABLE_ONLY}, + OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, {"OPEN_TABLES", open_tables_fields_info, create_schema_table, fill_open_tables, make_old_format, 0, -1, -1, 1, 0}, {"PARTITIONS", partitions_fields_info, create_schema_table, - get_all_tables, 0, get_schema_partitions_record, 1, 2, 0, OPEN_TABLE_ONLY}, + get_all_tables, 0, get_schema_partitions_record, 1, 2, 0, + OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, {"PLUGINS", plugin_fields_info, create_schema_table, fill_plugins, make_old_format, 0, -1, -1, 0, 0}, {"PROCESSLIST", processlist_fields_info, create_schema_table, @@ -6819,7 +6911,7 @@ ST_SCHEMA_TABLE schema_tables[]= NULL, -1, -1, false, 0}, {"REFERENTIAL_CONSTRAINTS", referential_constraints_fields_info, create_schema_table, get_all_tables, 0, get_referential_constraints_record, - 1, 9, 0, OPEN_TABLE_ONLY}, + 1, 9, 0, OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, {"ROUTINES", proc_fields_info, create_schema_table, fill_schema_proc, make_proc_old_format, 0, -1, -1, 0, 0}, {"SCHEMATA", schema_fields_info, create_schema_table, @@ -6839,14 +6931,15 @@ ST_SCHEMA_TABLE schema_tables[]= get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0, OPTIMIZE_I_S_TABLE}, {"TABLE_CONSTRAINTS", table_constraints_fields_info, create_schema_table, - get_all_tables, 0, get_schema_constraints_record, 3, 4, 0, OPEN_TABLE_ONLY}, + get_all_tables, 0, get_schema_constraints_record, 3, 4, 0, + OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, {"TABLE_NAMES", table_names_fields_info, create_schema_table, get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0}, {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table, fill_schema_table_privileges, 0, 0, -1, -1, 0, 0}, {"TRIGGERS", triggers_fields_info, create_schema_table, get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0, - OPEN_TABLE_ONLY}, + OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, fill_schema_user_privileges, 0, 0, -1, -1, 0, 0}, {"VARIABLES", variables_fields_info, create_schema_table, fill_variables, @@ -7007,7 +7100,7 @@ static bool show_create_trigger_impl(THD *thd, fields.push_back(new Item_empty_string("Database Collation", MY_CS_NAME_SIZE)); - if (p->send_fields(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + if (p->send_result_set_metadata(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) return TRUE; /* Send data. */ @@ -7166,7 +7259,7 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) if (!lst) return TRUE; - if (check_table_access(thd, TRIGGER_ACL, lst, 1, TRUE)) + if (check_table_access(thd, TRIGGER_ACL, lst, FALSE, 1, TRUE)) { my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "TRIGGER"); return TRUE; @@ -7220,3 +7313,95 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) status and client connection will be closed. */ } + +/* + Convert a string in character set in column character set format + to utf8 character set if possible, the utf8 character set string + will later possibly be converted to character set used by client. + Thus we attempt conversion from column character set to both + utf8 and to character set client. + + Examples of strings that should fail conversion to utf8 are unassigned + characters as e.g. 0x81 in cp1250 (Windows character set for for countries + like Czech and Poland). Example of string that should fail conversion to + character set on client (e.g. if this is latin1) is 0x2020 (daggger) in + ucs2. + + If the conversion fails we will as a fall back convert the string to + hex encoded format. The caller of the function can also ask for hex + encoded format of output string unconditionally. + + SYNOPSIS + get_cs_converted_string_value() + thd Thread object + input_str Input string in cs character set + output_str Output string to be produced in utf8 + cs Character set of input string + use_hex Use hex string unconditionally + + + RETURN VALUES + No return value +*/ + +static void get_cs_converted_string_value(THD *thd, + String *input_str, + String *output_str, + CHARSET_INFO *cs, + bool use_hex) +{ + + output_str->length(0); + if (input_str->length() == 0) + { + output_str->append("''"); + return; + } + if (!use_hex) + { + String try_val; + uint try_conv_error= 0; + + try_val.copy(input_str->ptr(), input_str->length(), cs, + thd->variables.character_set_client, &try_conv_error); + if (!try_conv_error) + { + String val; + uint conv_error= 0; + + val.copy(input_str->ptr(), input_str->length(), cs, + system_charset_info, &conv_error); + if (!conv_error) + { + append_unescaped(output_str, val.ptr(), val.length()); + return; + } + } + /* We had a conversion error, use hex encoded string for safety */ + } + { + const uchar *ptr; + uint i, len; + char buf[3]; + + output_str->append("_"); + output_str->append(cs->csname); + output_str->append(" "); + output_str->append("0x"); + len= input_str->length(); + ptr= (uchar*)input_str->ptr(); + for (i= 0; i < len; i++) + { + uint high, low; + + high= (*ptr) >> 4; + low= (*ptr) & 0x0F; + buf[0]= _dig_vec_upper[high]; + buf[1]= _dig_vec_upper[low]; + buf[2]= 0; + output_str->append((const char*)buf); + ptr++; + } + } + return; +} diff --git a/sql/sql_string.h b/sql/sql_string.h index 7b10aafbff1..75dc1163eec 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -123,6 +123,11 @@ public: (void) realloc(str_length); return Ptr; } + LEX_STRING lex_string() const + { + LEX_STRING lex_string = { (char*) ptr(), length() }; + return lex_string; + } void set(String &str,uint32 offset,uint32 arg_length) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 7cf45e65b28..b82f3bc1096 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1585,7 +1585,9 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) { if (!(part_syntax_buf= generate_partition_syntax(part_info, &syntax_len, - TRUE, TRUE))) + TRUE, TRUE, + lpt->create_info, + lpt->alter_info))) { DBUG_RETURN(TRUE); } @@ -1677,7 +1679,9 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) char *tmp_part_syntax_str; if (!(part_syntax_buf= generate_partition_syntax(part_info, &syntax_len, - TRUE, TRUE))) + TRUE, TRUE, + lpt->create_info, + lpt->alter_info))) { error= 1; goto err; @@ -2056,6 +2060,12 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, } DBUG_PRINT("table", ("table: 0x%lx s: 0x%lx", (long) table->table, table->table ? (long) table->table->s : (long) -1)); + + DBUG_EXECUTE_IF("bug43138", + my_printf_error(ER_BAD_TABLE_ERROR, + ER(ER_BAD_TABLE_ERROR), MYF(0), + table->table_name);); + } /* It's safe to unlock LOCK_open: we have an exclusive lock @@ -2498,6 +2508,39 @@ int prepare_create_field(Create_field *sql_field, DBUG_RETURN(0); } + +/* + Get character set from field object generated by parser using + default values when not set. + + SYNOPSIS + get_sql_field_charset() + sql_field The sql_field object + create_info Info generated by parser + + RETURN VALUES + cs Character set +*/ + +CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, + HA_CREATE_INFO *create_info) +{ + CHARSET_INFO *cs= sql_field->charset; + + if (!cs) + cs= create_info->default_table_charset; + /* + table_charset is set only in ALTER TABLE t1 CONVERT TO CHARACTER SET csname + if we want change character set for all varchar/char columns. + But the table charset must not affect the BLOB fields, so don't + allow to change my_charset_bin to somethig else. + */ + if (create_info->table_charset && cs != &my_charset_bin) + cs= create_info->table_charset; + return cs; +} + + /* Preparation for table creation @@ -2561,18 +2604,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, 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; - /* - table_charset is set in ALTER TABLE if we want change character set - for all varchar/char columns. - But the table charset must not affect the BLOB fields, so don't - allow to change my_charset_bin to somethig else. - */ - if (create_info->table_charset && sql_field->charset != &my_charset_bin) - sql_field->charset= create_info->table_charset; - - save_cs= sql_field->charset; + save_cs= sql_field->charset= get_sql_field_charset(sql_field, + create_info); if ((sql_field->flags & BINCMP_FLAG) && !(sql_field->charset= get_charset_by_csname(sql_field->charset->csname, MY_CS_BINSORT,MYF(0)))) @@ -2882,9 +2915,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, while ((key=key_iterator++)) { - DBUG_PRINT("info", ("key name: '%s' type: %d", key->name ? key->name : + DBUG_PRINT("info", ("key name: '%s' type: %d", key->name.str ? key->name.str : "(none)" , key->type)); - LEX_STRING key_name_str; if (key->type == Key::FOREIGN_KEY) { fk_key_count++; @@ -2893,7 +2925,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, fk_key->ref_columns.elements != fk_key->columns.elements) { my_error(ER_WRONG_FK_DEF, MYF(0), - (fk_key->name ? fk_key->name : "foreign key without name"), + (fk_key->name.str ? fk_key->name.str : + "foreign key without name"), ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF)); DBUG_RETURN(TRUE); } @@ -2906,12 +2939,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp); DBUG_RETURN(TRUE); } - key_name_str.str= (char*) key->name; - key_name_str.length= key->name ? strlen(key->name) : 0; - if (check_string_char_length(&key_name_str, "", NAME_CHAR_LEN, + if (check_string_char_length(&key->name, "", NAME_CHAR_LEN, system_charset_info, 1)) { - my_error(ER_TOO_LONG_IDENT, MYF(0), key->name); + my_error(ER_TOO_LONG_IDENT, MYF(0), key->name.str); DBUG_RETURN(TRUE); } key_iterator2.rewind (); @@ -2925,7 +2956,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Then we do not need the generated shorter key. */ if ((key2->type != Key::FOREIGN_KEY && - key2->name != ignore_key && + key2->name.str != ignore_key && !foreign_key_prefix(key, key2))) { /* TODO: issue warning message */ @@ -2933,10 +2964,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (!key2->generated || (key->generated && key->columns.elements < key2->columns.elements)) - key->name= ignore_key; + key->name.str= ignore_key; else { - key2->name= ignore_key; + key2->name.str= ignore_key; key_parts-= key2->columns.elements; (*key_count)--; } @@ -2944,14 +2975,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } } - if (key->name != ignore_key) + if (key->name.str != ignore_key) key_parts+=key->columns.elements; else (*key_count)--; - if (key->name && !tmp_table && (key->type != Key::PRIMARY) && - !my_strcasecmp(system_charset_info,key->name,primary_key_name)) + if (key->name.str && !tmp_table && (key->type != Key::PRIMARY) && + !my_strcasecmp(system_charset_info, key->name.str, primary_key_name)) { - my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name); + my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str); DBUG_RETURN(TRUE); } } @@ -2974,12 +3005,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, uint key_length=0; Key_part_spec *column; - if (key->name == ignore_key) + if (key->name.str == ignore_key) { /* ignore redundant keys */ do key=key_iterator++; - while (key && key->name == ignore_key); + while (key && key->name.str == ignore_key); if (!key) break; } @@ -3092,22 +3123,22 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, field=0; while ((sql_field=it++) && my_strcasecmp(system_charset_info, - column->field_name, + column->field_name.str, sql_field->field_name)) field++; if (!sql_field) { - my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name); + my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } while ((dup_column= cols2++) != column) { if (!my_strcasecmp(system_charset_info, - column->field_name, dup_column->field_name)) + column->field_name.str, dup_column->field_name.str)) { my_printf_error(ER_DUP_FIELDNAME, ER(ER_DUP_FIELDNAME),MYF(0), - column->field_name); + column->field_name.str); DBUG_RETURN(TRUE); } } @@ -3121,7 +3152,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet (ft_key_charset && sql_field->charset != ft_key_charset)) { - my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name); + my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name.str); DBUG_RETURN(-1); } ft_key_charset=sql_field->charset; @@ -3149,7 +3180,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS)) { - my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name); + my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type == @@ -3157,7 +3188,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, column->length= 25; if (!column->length) { - my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name); + my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } } @@ -3188,7 +3219,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, key_info->flags|= HA_NULL_PART_KEY; if (!(file->ha_table_flags() & HA_NULL_IN_KEY)) { - my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name); + my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } if (key->type == Key::SPATIAL) @@ -3238,13 +3269,21 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } else if (!f_is_geom(sql_field->pack_flag) && - (column->length > length || - !Field::type_can_have_key_part (sql_field->sql_type) || - ((f_is_packed(sql_field->pack_flag) || - ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) && - (key_info->flags & HA_NOSAME))) && - column->length != length))) - { + ((column->length > length && + !Field::type_can_have_key_part (sql_field->sql_type)) || + ((f_is_packed(sql_field->pack_flag) || + ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) && + (key_info->flags & HA_NOSAME))) && + column->length != length))) + { + /* Catch invalid uses of partial keys. + A key is identified as 'partial' if column->length != length. + A partial key is invalid if they data type does + not allow it, or the field is packed (as in MyISAM), + or the storage engine doesn't allow prefixed search and + the key is primary key. + */ + my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0)); DBUG_RETURN(TRUE); } @@ -3253,7 +3292,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } else if (length == 0) { - my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name); + my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } if (length > file->max_key_part_length() && key->type != Key::FULLTEXT) @@ -3311,7 +3350,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, key_name=primary_key_name; primary_key=1; } - else if (!(key_name = key->name)) + else if (!(key_name= key->name.str)) key_name=make_unique_key_name(sql_field->field_name, *key_info_buffer, key_info); if (check_if_keyname_exists(key_name, *key_info_buffer, key_info)) @@ -3622,6 +3661,9 @@ bool mysql_create_table_no_lock(THD *thd, } if (check_engine(thd, table_name, create_info)) DBUG_RETURN(TRUE); + + set_table_default_charset(thd, create_info, (char*) db); + db_options= create_info->table_options; if (create_info->row_type == ROW_TYPE_DYNAMIC) db_options|=HA_OPTION_PACK_RECORD; @@ -3715,7 +3757,7 @@ bool mysql_create_table_no_lock(THD *thd, ha_resolve_storage_engine_name(part_info->default_engine_type), ha_resolve_storage_engine_name(create_info->db_type))); if (part_info->check_partition_info(thd, &engine_type, file, - create_info, TRUE)) + create_info, FALSE)) goto err; part_info->default_engine_type= engine_type; @@ -3725,7 +3767,9 @@ bool mysql_create_table_no_lock(THD *thd, */ if (!(part_syntax_buf= generate_partition_syntax(part_info, &syntax_len, - TRUE, TRUE))) + TRUE, TRUE, + create_info, + alter_info))) goto err; part_info->part_info_string= part_syntax_buf; part_info->part_info_len= syntax_len; @@ -3751,9 +3795,9 @@ bool mysql_create_table_no_lock(THD *thd, creates a proper .par file. The current part_info object is only used to create the frm-file and .par-file. */ - if (part_info->use_default_no_partitions && - part_info->no_parts && - (int)part_info->no_parts != + if (part_info->use_default_num_partitions && + part_info->num_parts && + (int)part_info->num_parts != file->get_default_no_partitions(create_info)) { uint i; @@ -3764,13 +3808,13 @@ bool mysql_create_table_no_lock(THD *thd, (part_it++)->part_state= PART_TO_BE_DROPPED; } else if (part_info->is_sub_partitioned() && - part_info->use_default_no_subpartitions && - part_info->no_subparts && - (int)part_info->no_subparts != + part_info->use_default_num_subpartitions && + part_info->num_subparts && + (int)part_info->num_subparts != file->get_default_no_partitions(create_info)) { DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE); - part_info->no_subparts= file->get_default_no_partitions(create_info); + part_info->num_subparts= file->get_default_no_partitions(create_info); } } else if (create_info->db_type != engine_type) @@ -3792,8 +3836,6 @@ bool mysql_create_table_no_lock(THD *thd, } #endif - set_table_default_charset(thd, create_info, (char*) db); - if (mysql_prepare_create_table(thd, create_info, alter_info, internal_tmp_table, &db_options, file, @@ -3849,7 +3891,7 @@ bool mysql_create_table_no_lock(THD *thd, Then she could create the table. This case is pretty obscure and therefore we don't introduce a new error message only for it. */ - if (get_cached_table_share(db, alias)) + if (get_cached_table_share(db, table_name)) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); goto unlock_and_end; @@ -3983,7 +4025,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, /* Wait for any database locks */ pthread_mutex_lock(&LOCK_lock_db); while (!thd->killed && - hash_search(&lock_db_cache,(uchar*) db, strlen(db))) + my_hash_search(&lock_db_cache,(uchar*) db, strlen(db))) { wait_for_condition(thd, &LOCK_lock_db, &COND_refresh); pthread_mutex_lock(&LOCK_lock_db); @@ -4523,7 +4565,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, item->maybe_null = 1; field_list.push_back(item = new Item_empty_string("Msg_text", 255)); item->maybe_null = 1; - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -4583,11 +4625,11 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0)); DBUG_RETURN(TRUE); } - uint no_parts_found; - uint no_parts_opt= alter_info->partition_names.elements; - no_parts_found= set_part_state(alter_info, table->table->part_info, - PART_ADMIN); - if (no_parts_found != no_parts_opt && + uint num_parts_found; + uint num_parts_opt= alter_info->partition_names.elements; + num_parts_found= set_part_state(alter_info, table->table->part_info, + PART_ADMIN); + if (num_parts_found != num_parts_opt && (!(alter_info->flags & ALTER_ALL_PARTITION))) { char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE]; @@ -6241,6 +6283,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } key_part_length /= key_part->field->charset()->mbmaxlen; key_parts.push_back(new Key_part_spec(cfield->field_name, + strlen(cfield->field_name), key_part_length)); } if (key_parts.elements) @@ -6270,7 +6313,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, else key_type= Key::MULTIPLE; - key= new Key(key_type, key_name, + key= new Key(key_type, key_name, strlen(key_name), &key_create_info, test(key_info->flags & HA_GENERATED_KEY), key_parts); @@ -6283,10 +6326,10 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, { if (key->type != Key::FOREIGN_KEY) new_key_list.push_back(key); - if (key->name && - !my_strcasecmp(system_charset_info,key->name,primary_key_name)) + if (key->name.str && + !my_strcasecmp(system_charset_info, key->name.str, primary_key_name)) { - my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name); + my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str); goto err; } } @@ -7485,7 +7528,7 @@ view_err: end_temporary: my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO), (ulong) (copied + deleted), (ulong) deleted, - (ulong) thd->cuted_fields); + (ulong) thd->warning_info->statement_warn_count()); my_ok(thd, copied + deleted, 0L, tmp_name); thd->some_tables_deleted=0; DBUG_RETURN(FALSE); @@ -7827,7 +7870,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, field_list.push_back(item= new Item_int("Checksum", (longlong) 1, MY_INT64_NUM_DECIMAL_DIGITS)); item->maybe_null= 1; - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); diff --git a/sql/sql_test.cc b/sql/sql_test.cc index eeb9a21b6f5..6c0cb08cc79 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -85,7 +85,7 @@ void print_cached_tables(void) for (idx=unused=0 ; idx < open_cache.records ; idx++) { - TABLE *entry=(TABLE*) hash_element(&open_cache,idx); + TABLE *entry=(TABLE*) my_hash_element(&open_cache,idx); printf("%-14.14s %-32s%6ld%8ld%6d %s\n", entry->s->db.str, entry->s->table_name.str, entry->s->version, entry->in_use ? entry->in_use->thread_id : 0L, @@ -113,7 +113,7 @@ void print_cached_tables(void) if (count != unused) printf("Unused_links (%d) doesn't match open_cache: %d\n", count,unused); printf("\nCurrent refresh version: %ld\n",refresh_version); - if (hash_check(&open_cache)) + if (my_hash_check(&open_cache)) printf("Error: File hash table is corrupted\n"); fflush(stdout); VOID(pthread_mutex_unlock(&LOCK_open)); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index c055268ecca..7b689167783 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -422,7 +422,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last; thd->lex->query_tables_own_last= 0; - err_status= check_table_access(thd, TRIGGER_ACL, tables, 1, FALSE); + err_status= check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, FALSE); thd->lex->query_tables_own_last= save_query_tables_own_last; diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index c60dac42fb8..141bf76df1a 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -124,10 +124,10 @@ void udf_init() init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0); THD *new_thd = new THD; if (!new_thd || - hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0)) + my_hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0)) { sql_print_error("Can't allocate memory for udf structures"); - hash_free(&udf_hash); + my_hash_free(&udf_hash); free_root(&mem,MYF(0)); delete new_thd; DBUG_VOID_RETURN; @@ -239,20 +239,20 @@ void udf_free() DBUG_ENTER("udf_free"); for (uint idx=0 ; idx < udf_hash.records ; idx++) { - udf_func *udf=(udf_func*) hash_element(&udf_hash,idx); + udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx); if (udf->dlhandle) // Not closed before { /* Mark all versions using the same handler as closed */ for (uint j=idx+1 ; j < udf_hash.records ; j++) { - udf_func *tmp=(udf_func*) hash_element(&udf_hash,j); + udf_func *tmp=(udf_func*) my_hash_element(&udf_hash,j); if (udf->dlhandle == tmp->dlhandle) tmp->dlhandle=0; // Already closed } dlclose(udf->dlhandle); } } - hash_free(&udf_hash); + my_hash_free(&udf_hash); free_root(&mem,MYF(0)); if (initialized) { @@ -268,7 +268,7 @@ static void del_udf(udf_func *udf) DBUG_ENTER("del_udf"); if (!--udf->usage_count) { - hash_delete(&udf_hash,(uchar*) udf); + my_hash_delete(&udf_hash,(uchar*) udf); using_udf_functions=udf_hash.records != 0; } else @@ -282,7 +282,7 @@ static void del_udf(udf_func *udf) uint name_length=udf->name.length; udf->name.str=(char*) "*"; udf->name.length=1; - hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length); + my_hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length); } DBUG_VOID_RETURN; } @@ -302,7 +302,7 @@ void free_udf(udf_func *udf) We come here when someone has deleted the udf function while another thread still was using the udf */ - hash_delete(&udf_hash,(uchar*) udf); + my_hash_delete(&udf_hash,(uchar*) udf); using_udf_functions=udf_hash.records != 0; if (!find_udf_dl(udf->dl)) dlclose(udf->dlhandle); @@ -328,8 +328,8 @@ udf_func *find_udf(const char *name,uint length,bool mark_used) else rw_rdlock(&THR_LOCK_udf); /* Called during parsing */ - if ((udf=(udf_func*) hash_search(&udf_hash,(uchar*) name, - length ? length : (uint) strlen(name)))) + if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) name, + length ? length : (uint) strlen(name)))) { if (!udf->dlhandle) udf=0; // Could not be opened @@ -351,7 +351,7 @@ static void *find_udf_dl(const char *dl) */ for (uint idx=0 ; idx < udf_hash.records ; idx++) { - udf_func *udf=(udf_func*) hash_element(&udf_hash,idx); + udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx); if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL) DBUG_RETURN(udf->dlhandle); } @@ -441,7 +441,7 @@ int mysql_create_function(THD *thd,udf_func *udf) thd->clear_current_stmt_binlog_row_based(); rw_wrlock(&THR_LOCK_udf); - if ((hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length))) + if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length))) { my_error(ER_UDF_EXISTS, MYF(0), udf->name.str); goto err; @@ -544,8 +544,8 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) thd->clear_current_stmt_binlog_row_based(); rw_wrlock(&THR_LOCK_udf); - if (!(udf=(udf_func*) hash_search(&udf_hash,(uchar*) udf_name->str, - (uint) udf_name->length))) + if (!(udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) udf_name->str, + (uint) udf_name->length))) { my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); goto err; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index f5c4b85e904..cf6bf5d0633 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -833,7 +833,7 @@ int mysql_update(THD *thd, char buff[STRING_BUFFER_USUAL_SIZE]; my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated, - (ulong) thd->warning_info->statement_warn_count()); + (ulong) thd->warning_info->statement_warn_count()); thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; my_ok(thd, (ulong) thd->row_count_func, id, buff); @@ -922,7 +922,6 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0))) { update_non_unique_table_error(table_list, "UPDATE", duplicate); - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); DBUG_RETURN(TRUE); } } @@ -1077,7 +1076,7 @@ reopen_tables: if (check_access(thd, want_privilege, tl->db, &tl->grant.privilege, 0, 0, test(tl->schema_table)) || - check_grant(thd, want_privilege, tl, 0, 1, 0)) + check_grant(thd, want_privilege, tl, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); } } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 43d0b9fade0..87092fb14ca 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -269,11 +269,11 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, */ if ((check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege, 0, 0, is_schema_db(view->db)) || - check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) || + check_grant(thd, CREATE_VIEW_ACL, view, FALSE, 1, FALSE)) || (mode != VIEW_CREATE_NEW && (check_access(thd, DROP_ACL, view->db, &view->grant.privilege, 0, 0, is_schema_db(view->db)) || - check_grant(thd, DROP_ACL, view, 0, 1, 0)))) + check_grant(thd, DROP_ACL, view, FALSE, 1, FALSE)))) goto err; for (sl= select_lex; sl; sl= sl->next_select()) @@ -323,7 +323,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, { if (check_access(thd, SELECT_ACL, tbl->db, &tbl->grant.privilege, 0, 0, test(tbl->schema_table)) || - check_grant(thd, SELECT_ACL, tbl, 0, 1, 0)) + check_grant(thd, SELECT_ACL, tbl, FALSE, 1, FALSE)) goto err; } } @@ -1233,8 +1233,9 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, if (!table->prelocking_placeholder && (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe)) { - if (check_table_access(thd, SELECT_ACL, view_tables, UINT_MAX, TRUE) && - check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, TRUE)) + if (check_table_access(thd, SELECT_ACL, view_tables, FALSE, + UINT_MAX, TRUE) && + check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, TRUE)) { my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0)); goto err; @@ -1244,7 +1245,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, (old_lex->sql_command == SQLCOM_SHOW_CREATE) && !table->belong_to_view) { - if (check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, FALSE)) + if (check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, FALSE)) goto err; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index bf8f7a0ebe7..e67c34510a5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -56,8 +56,6 @@ int yylex(void *yylval, void *yythd); -const LEX_STRING null_lex_str= {0,0}; - #define yyoverflow(A,B,C,D,E,F) \ { \ ulong val= *(F); \ @@ -507,7 +505,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, struct sp_cond_type *spcondtype; struct { int vars, conds, hndlrs, curs; } spblock; sp_name *spname; - struct st_lex *lex; + LEX *lex; sp_head *sphead; struct p_elem_val *p_elem_value; enum index_hint_type index_hint; @@ -1155,6 +1153,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); IDENT_sys TEXT_STRING_sys TEXT_STRING_literal NCHAR_STRING opt_component key_cache_name sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty + opt_constraint constraint opt_ident %type <lex_str_ptr> opt_table_alias @@ -1163,8 +1162,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); table_ident table_ident_nodb references xid %type <simple_string> - remember_name remember_end opt_ident opt_db text_or_password - opt_constraint constraint + remember_name remember_end opt_db text_or_password %type <string> text_string opt_gconcat_separator @@ -1187,9 +1185,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <ulonglong_number> ulonglong_num real_ulonglong_num size_number -%type <p_elem_value> - part_bit_expr - %type <lock_type> replace_lock_option opt_low_priority insert_lock_option load_data_lock @@ -1325,6 +1320,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); view_check_option trigger_tail sp_tail sf_tail udf_tail event_tail install uninstall partition_entry binlog_base64_event init_key_options key_options key_opts key_opt key_using_alg + part_column_list server_def server_options_list server_option definer_opt no_definer definer END_OF_INPUT @@ -1396,7 +1392,7 @@ query: Lex_input_stream *lip = YYLIP; if ((YYTHD->client_capabilities & CLIENT_MULTI_QUERIES) && - ! lip->stmt_prepare_mode && + lip->multi_statements && ! lip->eof()) { /* @@ -1811,7 +1807,7 @@ create: my_parse_error(ER(ER_SYNTAX_ERROR)); MYSQL_YYABORT; } - key= new Key($2, $4.str, &lex->key_create_info, 0, + key= new Key($2, $4, &lex->key_create_info, 0, lex->col_list); if (key == NULL) MYSQL_YYABORT; @@ -4062,25 +4058,28 @@ partition_entry: ; partition: - BY part_type_def opt_no_parts opt_sub_part part_defs + BY part_type_def opt_num_parts opt_sub_part part_defs ; part_type_def: opt_linear KEY_SYM '(' part_field_list ')' { - LEX *lex= Lex; - lex->part_info->list_of_part_fields= TRUE; - lex->part_info->part_type= HASH_PARTITION; + partition_info *part_info= Lex->part_info; + part_info->list_of_part_fields= TRUE; + part_info->column_list= FALSE; + part_info->part_type= HASH_PARTITION; } | opt_linear HASH_SYM { Lex->part_info->part_type= HASH_PARTITION; } part_func {} - | RANGE_SYM + | RANGE_SYM part_func { Lex->part_info->part_type= RANGE_PARTITION; } - part_func {} - | LIST_SYM + | RANGE_SYM part_column_list + { Lex->part_info->part_type= RANGE_PARTITION; } + | LIST_SYM part_func + { Lex->part_info->part_type= LIST_PARTITION; } + | LIST_SYM part_column_list { Lex->part_info->part_type= LIST_PARTITION; } - part_func {} ; opt_linear: @@ -4102,59 +4101,66 @@ part_field_item_list: part_field_item: ident { - if (Lex->part_info->part_field_list.push_back($1.str)) + partition_info *part_info= Lex->part_info; + part_info->num_columns++; + if (part_info->part_field_list.push_back($1.str)) { mem_alloc_error(1); MYSQL_YYABORT; } + if (part_info->num_columns > MAX_REF_PARTS) + { + my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), + "list of partition fields"); + MYSQL_YYABORT; + } } ; +part_column_list: + COLUMNS '(' part_field_list ')' + { + partition_info *part_info= Lex->part_info; + part_info->column_list= TRUE; + part_info->list_of_part_fields= TRUE; + } + ; + + part_func: '(' remember_name part_func_expr remember_end ')' { - LEX *lex= Lex; - uint expr_len= (uint)($4 - $2) - 1; - lex->part_info->list_of_part_fields= FALSE; - lex->part_info->part_expr= $3; - char *func_string= (char*) sql_memdup($2+1, expr_len); - if (func_string == NULL) - MYSQL_YYABORT; - lex->part_info->part_func_string= func_string; - lex->part_info->part_func_len= expr_len; + partition_info *part_info= Lex->part_info; + if (part_info->set_part_expr($2+1, $3, $4, FALSE)) + { MYSQL_YYABORT; } + part_info->num_columns= 1; + part_info->column_list= FALSE; } ; sub_part_func: '(' remember_name part_func_expr remember_end ')' { - LEX *lex= Lex; - uint expr_len= (uint)($4 - $2) - 1; - lex->part_info->list_of_subpart_fields= FALSE; - lex->part_info->subpart_expr= $3; - char *func_string= (char*) sql_memdup($2+1, expr_len); - if (func_string == NULL) - MYSQL_YYABORT; - lex->part_info->subpart_func_string= func_string; - lex->part_info->subpart_func_len= expr_len; + if (Lex->part_info->set_part_expr($2+1, $3, $4, TRUE)) + { MYSQL_YYABORT; } } ; -opt_no_parts: +opt_num_parts: /* empty */ {} | PARTITIONS_SYM real_ulong_num { - uint no_parts= $2; - LEX *lex= Lex; - if (no_parts == 0) + uint num_parts= $2; + partition_info *part_info= Lex->part_info; + if (num_parts == 0) { my_error(ER_NO_PARTS_ERROR, MYF(0), "partitions"); MYSQL_YYABORT; } - lex->part_info->no_parts= no_parts; - lex->part_info->use_default_no_partitions= FALSE; + part_info->num_parts= num_parts; + part_info->use_default_num_partitions= FALSE; } ; @@ -4162,15 +4168,15 @@ opt_sub_part: /* empty */ {} | SUBPARTITION_SYM BY opt_linear HASH_SYM sub_part_func { Lex->part_info->subpart_type= HASH_PARTITION; } - opt_no_subparts {} + opt_num_subparts {} | SUBPARTITION_SYM BY opt_linear KEY_SYM '(' sub_part_field_list ')' { - LEX *lex= Lex; - lex->part_info->subpart_type= HASH_PARTITION; - lex->part_info->list_of_subpart_fields= TRUE; + partition_info *part_info= Lex->part_info; + part_info->subpart_type= HASH_PARTITION; + part_info->list_of_subpart_fields= TRUE; } - opt_no_subparts {} + opt_num_subparts {} ; sub_part_field_list: @@ -4181,11 +4187,18 @@ sub_part_field_list: sub_part_field_item: ident { - if (Lex->part_info->subpart_field_list.push_back($1.str)) + partition_info *part_info= Lex->part_info; + if (part_info->subpart_field_list.push_back($1.str)) { mem_alloc_error(1); MYSQL_YYABORT; } + if (part_info->subpart_field_list.elements > MAX_REF_PARTS) + { + my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), + "list of subpartition fields"); + MYSQL_YYABORT; + } } ; @@ -4205,33 +4218,46 @@ part_func_expr: } ; -opt_no_subparts: +opt_num_subparts: /* empty */ {} | SUBPARTITIONS_SYM real_ulong_num { - uint no_parts= $2; + uint num_parts= $2; LEX *lex= Lex; - if (no_parts == 0) + if (num_parts == 0) { my_error(ER_NO_PARTS_ERROR, MYF(0), "subpartitions"); MYSQL_YYABORT; } - lex->part_info->no_subparts= no_parts; - lex->part_info->use_default_no_subpartitions= FALSE; + lex->part_info->num_subparts= num_parts; + lex->part_info->use_default_num_subpartitions= FALSE; } ; part_defs: /* empty */ - {} + { + partition_info *part_info= Lex->part_info; + if (part_info->part_type == RANGE_PARTITION) + { + my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), + "RANGE"); + MYSQL_YYABORT; + } + else if (part_info->part_type == LIST_PARTITION) + { + my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), + "LIST"); + MYSQL_YYABORT; + } + } | '(' part_def_list ')' { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; + partition_info *part_info= Lex->part_info; uint count_curr_parts= part_info->partitions.elements; - if (part_info->no_parts != 0) + if (part_info->num_parts != 0) { - if (part_info->no_parts != + if (part_info->num_parts != count_curr_parts) { my_parse_error(ER(ER_PARTITION_WRONG_NO_PART_ERROR)); @@ -4240,7 +4266,7 @@ part_defs: } else if (count_curr_parts > 0) { - part_info->no_parts= count_curr_parts; + part_info->num_parts= count_curr_parts; } part_info->count_curr_subparts= 0; } @@ -4254,8 +4280,7 @@ part_def_list: part_definition: PARTITION_SYM { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; + partition_info *part_info= Lex->part_info; partition_element *p_elem= new partition_element(); if (!p_elem || part_info->partitions.push_back(p_elem)) @@ -4267,7 +4292,7 @@ part_definition: part_info->curr_part_elem= p_elem; part_info->current_partition= p_elem; part_info->use_default_partitions= FALSE; - part_info->use_default_no_partitions= FALSE; + part_info->use_default_num_partitions= FALSE; } part_name opt_part_values @@ -4279,8 +4304,7 @@ part_definition: part_name: ident { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; + partition_info *part_info= Lex->part_info; partition_element *p_elem= part_info->curr_part_elem; p_elem->partition_name= $1.str; } @@ -4290,15 +4314,16 @@ opt_part_values: /* empty */ { LEX *lex= Lex; + partition_info *part_info= lex->part_info; if (! lex->is_partition_management()) { - if (lex->part_info->part_type == RANGE_PARTITION) + if (part_info->part_type == RANGE_PARTITION) { my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN"); MYSQL_YYABORT; } - if (lex->part_info->part_type == LIST_PARTITION) + if (part_info->part_type == LIST_PARTITION) { my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "LIST", "IN"); @@ -4306,14 +4331,15 @@ opt_part_values: } } else - lex->part_info->part_type= HASH_PARTITION; + part_info->part_type= HASH_PARTITION; } - | VALUES LESS_SYM THAN_SYM part_func_max + | VALUES LESS_SYM THAN_SYM { LEX *lex= Lex; + partition_info *part_info= lex->part_info; if (! lex->is_partition_management()) { - if (Lex->part_info->part_type != RANGE_PARTITION) + if (part_info->part_type != RANGE_PARTITION) { my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN"); @@ -4321,153 +4347,183 @@ opt_part_values: } } else - lex->part_info->part_type= RANGE_PARTITION; + part_info->part_type= RANGE_PARTITION; } - | VALUES IN_SYM '(' part_list_func ')' + part_func_max {} + | VALUES IN_SYM { LEX *lex= Lex; + partition_info *part_info= lex->part_info; if (! lex->is_partition_management()) { - if (Lex->part_info->part_type != LIST_PARTITION) + if (part_info->part_type != LIST_PARTITION) { my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), - "LIST", "IN"); + "LIST", "IN"); MYSQL_YYABORT; } } else - lex->part_info->part_type= LIST_PARTITION; + part_info->part_type= LIST_PARTITION; } + part_values_in {} ; part_func_max: - max_value_sym + MAX_VALUE_SYM { - LEX *lex= Lex; - if (lex->part_info->defined_max_value) + partition_info *part_info= Lex->part_info; + + if (part_info->num_columns && + part_info->num_columns != 1U) { - my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR)); + part_info->print_debug("Kilroy II", NULL); + my_parse_error(ER(ER_PARTITION_COLUMN_LIST_ERROR)); MYSQL_YYABORT; } - lex->part_info->defined_max_value= TRUE; - lex->part_info->curr_part_elem->max_value= TRUE; - lex->part_info->curr_part_elem->range_value= LONGLONG_MAX; - } - | part_range_func - { - if (Lex->part_info->defined_max_value) + else + part_info->num_columns= 1U; + if (part_info->init_column_part()) { - my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR)); MYSQL_YYABORT; } - if (Lex->part_info->curr_part_elem->has_null_value) + if (part_info->add_max_value()) { - my_parse_error(ER(ER_NULL_IN_VALUES_LESS_THAN)); MYSQL_YYABORT; } } + | part_value_item {} ; -max_value_sym: - MAX_VALUE_SYM - | '(' MAX_VALUE_SYM ')' - ; - -part_range_func: - '(' part_bit_expr ')' +part_values_in: + part_value_item + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + part_info->print_debug("part_values_in: part_value_item", NULL); + + if (part_info->num_columns != 1U) + { + if (!lex->is_partition_management() || + part_info->num_columns == 0 || + part_info->num_columns > MAX_REF_PARTS) + { + part_info->print_debug("Kilroy III", NULL); + my_parse_error(ER(ER_PARTITION_COLUMN_LIST_ERROR)); + MYSQL_YYABORT; + } + /* + Reorganize the current large array into a list of small + arrays with one entry in each array. This can happen + in the first partition of an ALTER TABLE statement where + we ADD or REORGANIZE partitions. Also can only happen + for LIST partitions. + */ + if (part_info->reorganize_into_single_field_col_val()) + { + MYSQL_YYABORT; + } + } + } + | '(' part_value_list ')' { partition_info *part_info= Lex->part_info; - if (!($2->unsigned_flag)) - part_info->curr_part_elem->signed_flag= TRUE; - part_info->curr_part_elem->range_value= $2->value; + if (part_info->num_columns < 2U) + { + my_parse_error(ER(ER_ROW_SINGLE_PARTITION_FIELD_ERROR)); + MYSQL_YYABORT; + } } ; -part_list_func: - part_list_item {} - | part_list_func ',' part_list_item {} +part_value_list: + part_value_item {} + | part_value_list ',' part_value_item {} ; -part_list_item: - part_bit_expr +part_value_item: + '(' { - part_elem_value *value_ptr= $1; partition_info *part_info= Lex->part_info; - if (!value_ptr->unsigned_flag) - part_info->curr_part_elem->signed_flag= TRUE; - if (!value_ptr->null_value && - part_info->curr_part_elem-> - list_val_list.push_back(value_ptr)) + part_info->print_debug("( part_value_item", NULL); + /* Initialisation code needed for each list of value expressions */ + if (!(part_info->part_type == LIST_PARTITION && + part_info->num_columns == 1U) && + part_info->init_column_part()) { - mem_alloc_error(sizeof(part_elem_value)); MYSQL_YYABORT; } } - ; - -part_bit_expr: - bit_expr + part_value_item_list {} + ')' { - Item *part_expr= $1; - THD *thd= YYTHD; - LEX *lex= thd->lex; - Name_resolution_context *context= &lex->current_select->context; - TABLE_LIST *save_list= context->table_list; - const char *save_where= thd->where; - - context->table_list= 0; - thd->where= "partition function"; - - part_elem_value *value_ptr= - (part_elem_value*)sql_alloc(sizeof(part_elem_value)); - if (!value_ptr) + LEX *lex= Lex; + partition_info *part_info= Lex->part_info; + part_info->print_debug(") part_value_item", NULL); + if (part_info->num_columns == 0) + part_info->num_columns= part_info->curr_list_object; + if (part_info->num_columns != part_info->curr_list_object) { - mem_alloc_error(sizeof(part_elem_value)); + /* + All value items lists must be of equal length, in some cases + which is covered by the above if-statement we don't know yet + how many columns is in the partition so the assignment above + ensures that we only report errors when we know we have an + error. + */ + part_info->print_debug("Kilroy I", NULL); + my_parse_error(ER(ER_PARTITION_COLUMN_LIST_ERROR)); MYSQL_YYABORT; } - if (part_expr->walk(&Item::check_partition_func_processor, 0, - NULL)) + part_info->curr_list_object= 0; + } + ; + +part_value_item_list: + part_value_expr_item {} + | part_value_item_list ',' part_value_expr_item {} + ; + +part_value_expr_item: + MAX_VALUE_SYM + { + partition_info *part_info= Lex->part_info; + part_column_list_val *col_val; + if (part_info->part_type == LIST_PARTITION) { - my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + my_parse_error(ER(ER_MAXVALUE_IN_VALUES_IN)); MYSQL_YYABORT; } - if (part_expr->fix_fields(YYTHD, (Item**)0) || - ((context->table_list= save_list), FALSE) || - (!part_expr->const_item()) || - (!lex->safe_to_cache_query)) + if (part_info->add_max_value()) { - my_error(ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR, MYF(0)); MYSQL_YYABORT; } - thd->where= save_where; - value_ptr->value= part_expr->val_int(); - value_ptr->unsigned_flag= TRUE; - if (!part_expr->unsigned_flag && - value_ptr->value < 0) - value_ptr->unsigned_flag= FALSE; - if ((value_ptr->null_value= part_expr->null_value)) + } + | bit_expr + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + Item *part_expr= $1; + + if (!lex->safe_to_cache_query) { - if (Lex->part_info->curr_part_elem->has_null_value) - { - my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0)); - MYSQL_YYABORT; - } - Lex->part_info->curr_part_elem->has_null_value= TRUE; + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + MYSQL_YYABORT; } - else if (part_expr->result_type() != INT_RESULT) + if (part_info->add_column_list_value(YYTHD, part_expr)) { - my_parse_error(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR)); MYSQL_YYABORT; } - $$= value_ptr; } ; + opt_sub_partition: /* empty */ { - if (Lex->part_info->no_subparts != 0 && - !Lex->part_info->use_default_subpartitions) + partition_info *part_info= Lex->part_info; + if (part_info->num_subparts != 0 && + !part_info->use_default_subpartitions) { /* We come here when we have defined subpartitions on the first @@ -4479,11 +4535,10 @@ opt_sub_partition: } | '(' sub_part_list ')' { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; - if (part_info->no_subparts != 0) + partition_info *part_info= Lex->part_info; + if (part_info->num_subparts != 0) { - if (part_info->no_subparts != + if (part_info->num_subparts != part_info->count_curr_subparts) { my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); @@ -4497,7 +4552,7 @@ opt_sub_partition: my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); MYSQL_YYABORT; } - part_info->no_subparts= part_info->count_curr_subparts; + part_info->num_subparts= part_info->count_curr_subparts; } part_info->count_curr_subparts= 0; } @@ -4511,8 +4566,7 @@ sub_part_list: sub_part_definition: SUBPARTITION_SYM { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; + partition_info *part_info= Lex->part_info; partition_element *curr_part= part_info->current_partition; partition_element *sub_p_elem= new partition_element(curr_part); if (part_info->use_default_subpartitions && @@ -4540,7 +4594,7 @@ sub_part_definition: } part_info->curr_part_elem= sub_p_elem; part_info->use_default_subpartitions= FALSE; - part_info->use_default_no_subpartitions= FALSE; + part_info->use_default_num_subpartitions= FALSE; part_info->count_curr_subparts++; } sub_name opt_part_options {} @@ -4566,9 +4620,9 @@ opt_part_option: { Lex->part_info->curr_part_elem->tablespace_name= $3.str; } | opt_storage ENGINE_SYM opt_equal storage_engines { - LEX *lex= Lex; - lex->part_info->curr_part_elem->engine_type= $4; - lex->part_info->default_engine_type= $4; + partition_info *part_info= Lex->part_info; + part_info->curr_part_elem->engine_type= $4; + part_info->default_engine_type= $4; } | NODEGROUP_SYM opt_equal real_ulong_num { Lex->part_info->curr_part_elem->nodegroup_id= (uint16) $3; } @@ -4946,8 +5000,7 @@ key_def: '(' key_list ')' key_options { LEX *lex=Lex; - const char *key_name= $3 ? $3 : $1; - Key *key= new Key($2, key_name, &lex->key_create_info, 0, + Key *key= new Key($2, $3.str ? $3 : $1, &lex->key_create_info, 0, lex->col_list); if (key == NULL) MYSQL_YYABORT; @@ -4957,9 +5010,7 @@ key_def: | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references { LEX *lex=Lex; - const char *key_name= $1 ? $1 : $4; - const char *fkey_name = $4 ? $4 : key_name; - Key *key= new Foreign_key(fkey_name, lex->col_list, + Key *key= new Foreign_key($4.str ? $4 : $1, lex->col_list, $8, lex->ref_list, lex->fk_delete_opt, @@ -4968,7 +5019,7 @@ key_def: if (key == NULL) MYSQL_YYABORT; lex->alter_info.key_list.push_back(key); - key= new Key(Key::MULTIPLE, key_name, + key= new Key(Key::MULTIPLE, $1.str ? $1 : $4, &default_key_create_info, 1, lex->col_list); if (key == NULL) @@ -4998,7 +5049,7 @@ check_constraint: ; opt_constraint: - /* empty */ { $$=(char*) 0; } + /* empty */ { $$= null_lex_str; } | constraint { $$= $1; } ; @@ -5503,14 +5554,14 @@ opt_ref_list: ref_list: ref_list ',' ident { - Key_part_spec *key= new Key_part_spec($3.str); + Key_part_spec *key= new Key_part_spec($3, 0); if (key == NULL) MYSQL_YYABORT; Lex->ref_list.push_back(key); } | ident { - Key_part_spec *key= new Key_part_spec($1.str); + Key_part_spec *key= new Key_part_spec($1, 0); if (key == NULL) MYSQL_YYABORT; Lex->ref_list.push_back(key); @@ -5657,7 +5708,7 @@ key_list: key_part: ident { - $$= new Key_part_spec($1.str); + $$= new Key_part_spec($1, 0); if ($$ == NULL) MYSQL_YYABORT; } @@ -5668,15 +5719,15 @@ key_part: { my_error(ER_KEY_PART_0, MYF(0), $1.str); } - $$= new Key_part_spec($1.str,(uint) key_part_len); + $$= new Key_part_spec($1, (uint) key_part_len); if ($$ == NULL) MYSQL_YYABORT; } ; opt_ident: - /* empty */ { $$=(char*) 0; /* Default length */ } - | field_ident { $$=$1.str; } + /* empty */ { $$= null_lex_str; } + | field_ident { $$= $1; } ; opt_component: @@ -5975,7 +6026,7 @@ alter_commands: LEX *lex= Lex; lex->alter_info.flags|= ALTER_COALESCE_PARTITION; lex->no_write_to_binlog= $3; - lex->alter_info.no_parts= $4; + lex->alter_info.num_parts= $4; } | TRUNCATE_SYM PARTITION_SYM all_or_alt_part_name_list { @@ -6024,12 +6075,11 @@ add_part_extra: | '(' part_def_list ')' { LEX *lex= Lex; - lex->part_info->no_parts= lex->part_info->partitions.elements; + lex->part_info->num_parts= lex->part_info->partitions.elements; } | PARTITIONS_SYM real_ulong_num { - LEX *lex= Lex; - lex->part_info->no_parts= $2; + Lex->part_info->num_parts= $2; } ; @@ -6059,8 +6109,8 @@ reorg_parts_rule: } INTO '(' part_def_list ')' { - LEX *lex= Lex; - lex->part_info->no_parts= lex->part_info->partitions.elements; + partition_info *part_info= Lex->part_info; + part_info->num_parts= part_info->partitions.elements; } ; @@ -8249,7 +8299,7 @@ udf_expr: sum_expr: AVG_SYM '(' in_sum_expr ')' { - $$= new (YYTHD->mem_root) Item_sum_avg($3); + $$= new (YYTHD->mem_root) Item_sum_avg($3, FALSE); if ($$ == NULL) MYSQL_YYABORT; } @@ -8357,7 +8407,7 @@ sum_expr: } | SUM_SYM '(' in_sum_expr ')' { - $$= new (YYTHD->mem_root) Item_sum_sum($3); + $$= new (YYTHD->mem_root) Item_sum_sum($3, FALSE); if ($$ == NULL) MYSQL_YYABORT; } @@ -10282,11 +10332,6 @@ show_param: if (prepare_schema_table(YYTHD, lex, $3, SCH_STATISTICS)) MYSQL_YYABORT; } - | COLUMN_SYM TYPES_SYM - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_COLUMN_TYPES; - } | TABLE_SYM TYPES_SYM { LEX *lex=Lex; @@ -10474,23 +10519,13 @@ show_param: } | PROCEDURE CODE_SYM sp_name { -#ifdef DBUG_OFF - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; -#else Lex->sql_command= SQLCOM_SHOW_PROC_CODE; Lex->spname= $3; -#endif } | FUNCTION_SYM CODE_SYM sp_name { -#ifdef DBUG_OFF - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; -#else Lex->sql_command= SQLCOM_SHOW_FUNC_CODE; Lex->spname= $3; -#endif } | CREATE EVENT_SYM sp_name { @@ -11880,7 +11915,6 @@ keyword_sp: | MAX_SIZE_SYM {} | MAX_UPDATES_PER_HOUR {} | MAX_USER_CONNECTIONS_SYM {} - | MAX_VALUE_SYM {} | MEDIUM_SYM {} | MEMORY_SYM {} | MERGE_SYM {} @@ -12822,6 +12856,7 @@ object_privilege: | CREATE USER { Lex->grant |= CREATE_USER_ACL; } | EVENT_SYM { Lex->grant |= EVENT_ACL;} | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; } + | CREATE TABLESPACE { Lex->grant |= CREATE_TABLESPACE_ACL; } ; opt_and: diff --git a/sql/structs.h b/sql/structs.h index bcda7b1e787..33b7148c4b3 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -19,8 +19,9 @@ /* The old structures from unireg */ -struct st_table; +struct TABLE; class Field; +class THD; typedef struct st_date_time_format { uchar positions[8]; @@ -100,7 +101,7 @@ typedef struct st_key { union { int bdb_return_if_eq; } handler; - struct st_table *table; + TABLE *table; } KEY; @@ -118,30 +119,6 @@ typedef struct st_reginfo { /* Extra info about reg */ } REGINFO; -struct st_read_record; /* For referense later */ -class SQL_SELECT; -class THD; -class handler; - -typedef struct st_read_record { /* Parameter to read_record */ - struct st_table *table; /* Head-form */ - handler *file; - struct st_table **forms; /* head and ref forms */ - int (*read_record)(struct st_read_record *); - THD *thd; - SQL_SELECT *select; - uint cache_records; - uint ref_length,struct_length,reclength,rec_cache_size,error_offset; - uint index; - uchar *ref_pos; /* pointer to form->refpos */ - uchar *record; - uchar *rec_buf; /* to read field values after filesort */ - uchar *cache,*cache_pos,*cache_end,*read_positions; - IO_CACHE *io_cache; - bool print_error, ignore_not_found_rows; -} READ_RECORD; - - /* Originally MySQL used MYSQL_TIME structure inside server only, but since 4.1 it's exported to user in the new client API. Define aliases for diff --git a/sql/table.cc b/sql/table.cc index 96a10e52dd3..22b4b2f9b5e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -423,7 +423,7 @@ void free_table_share(TABLE_SHARE *share) pthread_mutex_destroy(&share->mutex); pthread_cond_destroy(&share->cond); } - hash_free(&share->name_hash); + my_hash_free(&share->name_hash); plugin_unlock(NULL, share->db_plugin); share->db_plugin= NULL; @@ -1148,10 +1148,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH; if (use_hash) - use_hash= !hash_init(&share->name_hash, - system_charset_info, - share->fields,0,0, - (hash_get_key) get_field_name,0,0); + use_hash= !my_hash_init(&share->name_hash, + system_charset_info, + share->fields,0,0, + (my_hash_get_key) get_field_name,0,0); for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { @@ -1591,7 +1591,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, delete handler_file; #ifndef DBUG_OFF if (use_hash) - (void) hash_check(&share->name_hash); + (void) my_hash_check(&share->name_hash); #endif DBUG_RETURN (0); @@ -1602,7 +1602,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, x_free((uchar*) disk_buff); delete crypted; delete handler_file; - hash_free(&share->name_hash); + my_hash_free(&share->name_hash); + if (share->ha_data_destroy) + share->ha_data_destroy(share->ha_data); open_table_error(share, error, share->open_errno, errarg); DBUG_RETURN(error); @@ -2933,7 +2935,7 @@ table_check_intact(TABLE *table, const uint table_f_count, Create Item_field for each column in the table. SYNPOSIS - st_table::fill_item_list() + TABLE::fill_item_list() item_list a pointer to an empty list used to store items DESCRIPTION @@ -2946,7 +2948,7 @@ table_check_intact(TABLE *table, const uint table_f_count, 1 out of memory */ -bool st_table::fill_item_list(List<Item> *item_list) const +bool TABLE::fill_item_list(List<Item> *item_list) const { /* All Item_field's created using a direct pointer to a field @@ -2966,7 +2968,7 @@ bool st_table::fill_item_list(List<Item> *item_list) const Fields of this table. SYNPOSIS - st_table::fill_item_list() + TABLE::fill_item_list() item_list a non-empty list with Item_fields DESCRIPTION @@ -2976,7 +2978,7 @@ bool st_table::fill_item_list(List<Item> *item_list) const is the same as the number of columns in the table. */ -void st_table::reset_item_list(List<Item> *item_list) const +void TABLE::reset_item_list(List<Item> *item_list) const { List_iterator_fast<Item> it(*item_list); for (Field **ptr= field; *ptr; ptr++) @@ -3918,14 +3920,14 @@ const char *Natural_join_column::db_name() return table_ref->view_db.str; /* - Test that TABLE_LIST::db is the same as st_table_share::db to + Test that TABLE_LIST::db is the same as 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.str) || (table_ref->schema_table && - table_ref->table->s->db.str[0] == 0)); + is_schema_db(table_ref->table->s->db.str))); return table_ref->db; } @@ -4137,13 +4139,13 @@ const char *Field_iterator_table_ref::get_db_name() return natural_join_it.column_ref()->db_name(); /* - Test that TABLE_LIST::db is the same as st_table_share::db to + Test that TABLE_LIST::db is the same as 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.str) || (table_ref->schema_table && - table_ref->table->s->db.str[0] == 0)); + is_schema_db(table_ref->table->s->db.str))); return table_ref->db; } @@ -4313,7 +4315,7 @@ Field_iterator_table_ref::get_natural_column_ref() /* Reset all columns bitmaps */ -void st_table::clear_column_bitmaps() +void TABLE::clear_column_bitmaps() { /* Reset column read/write usage. It's identical to: @@ -4334,9 +4336,9 @@ void st_table::clear_column_bitmaps() key fields. */ -void st_table::prepare_for_position() +void TABLE::prepare_for_position() { - DBUG_ENTER("st_table::prepare_for_position"); + DBUG_ENTER("TABLE::prepare_for_position"); if ((file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && s->primary_key < MAX_KEY) @@ -4355,14 +4357,14 @@ void st_table::prepare_for_position() NOTE: This changes the bitmap to use the tmp bitmap After this, you can't access any other columns in the table until - bitmaps are reset, for example with st_table::clear_column_bitmaps() - or st_table::restore_column_maps_after_mark_index() + bitmaps are reset, for example with TABLE::clear_column_bitmaps() + or TABLE::restore_column_maps_after_mark_index() */ -void st_table::mark_columns_used_by_index(uint index) +void TABLE::mark_columns_used_by_index(uint index) { MY_BITMAP *bitmap= &tmp_set; - DBUG_ENTER("st_table::mark_columns_used_by_index"); + DBUG_ENTER("TABLE::mark_columns_used_by_index"); (void) file->extra(HA_EXTRA_KEYREAD); bitmap_clear_all(bitmap); @@ -4383,9 +4385,9 @@ void st_table::mark_columns_used_by_index(uint index) when calling mark_columns_used_by_index */ -void st_table::restore_column_maps_after_mark_index() +void TABLE::restore_column_maps_after_mark_index() { - DBUG_ENTER("st_table::restore_column_maps_after_mark_index"); + DBUG_ENTER("TABLE::restore_column_maps_after_mark_index"); key_read= 0; (void) file->extra(HA_EXTRA_NO_KEYREAD); @@ -4399,7 +4401,7 @@ void st_table::restore_column_maps_after_mark_index() mark columns used by key, but don't reset other fields */ -void st_table::mark_columns_used_by_index_no_reset(uint index, +void TABLE::mark_columns_used_by_index_no_reset(uint index, MY_BITMAP *bitmap) { KEY_PART_INFO *key_part= key_info[index].key_part; @@ -4418,7 +4420,7 @@ void st_table::mark_columns_used_by_index_no_reset(uint index, always set and sometimes read. */ -void st_table::mark_auto_increment_column() +void TABLE::mark_auto_increment_column() { DBUG_ASSERT(found_next_number_field); /* @@ -4451,7 +4453,7 @@ void st_table::mark_auto_increment_column() retrieve the row again. */ -void st_table::mark_columns_needed_for_delete() +void TABLE::mark_columns_needed_for_delete() { if (triggers) triggers->mark_fields_used(TRG_EVENT_DELETE); @@ -4501,7 +4503,7 @@ void st_table::mark_columns_needed_for_delete() retrieve the row again. */ -void st_table::mark_columns_needed_for_update() +void TABLE::mark_columns_needed_for_update() { DBUG_ENTER("mark_columns_needed_for_update"); if (triggers) @@ -4544,7 +4546,7 @@ void st_table::mark_columns_needed_for_update() as changed. */ -void st_table::mark_columns_needed_for_insert() +void TABLE::mark_columns_needed_for_insert() { if (triggers) { @@ -4574,7 +4576,7 @@ void st_table::mark_columns_needed_for_insert() TABLEs. Each of these TABLEs is called a part of a MERGE table. */ -bool st_table::is_children_attached(void) +bool TABLE::is_children_attached(void) { return((child_l && children_attached) || (parent && parent->children_attached)); @@ -4638,9 +4640,9 @@ Item_subselect *TABLE_LIST::containing_subselect() DESCRIPTION The parser collects the index hints for each table in a "tagged list" (TABLE_LIST::index_hints). Using the information in this tagged list - this function sets the members st_table::keys_in_use_for_query, + this function sets the members st_table::keys_in_use_for_query, st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by, - st_table::force_index, st_table::force_index_order, + st_table::force_index, st_table::force_index_order, st_table::force_index_group and st_table::covering_keys. Current implementation of the runtime does not allow mixing FORCE INDEX diff --git a/sql/table.h b/sql/table.h index f9da0637b4b..49a97958807 100644 --- a/sql/table.h +++ b/sql/table.h @@ -293,9 +293,9 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, instance of table share per one table in the database. */ -typedef struct st_table_share +struct TABLE_SHARE { - st_table_share() {} /* Remove gcc warning */ + TABLE_SHARE() {} /* Remove gcc warning */ /** Category of this table. */ TABLE_CATEGORY table_category; @@ -308,11 +308,7 @@ typedef struct st_table_share TYPELIB *intervals; /* pointer to interval info */ pthread_mutex_t mutex; /* For locking the share */ pthread_cond_t cond; /* To signal that share is ready */ - struct st_table_share *next, /* Link to unused shares */ - **prev; -#ifdef NOT_YET - struct st_table *open_tables; /* link to open tables */ -#endif + TABLE_SHARE *next, **prev; /* Link to unused shares */ /* The following is copied to each TABLE on OPEN */ Field **field; @@ -426,6 +422,7 @@ typedef struct st_table_share /** place to store storage engine specific data */ void *ha_data; + void (*ha_data_destroy)(void *); /* An optional destructor for ha_data */ /* @@ -595,7 +592,7 @@ typedef struct st_table_share return (tmp_table == SYSTEM_TMP_TABLE || is_view) ? 0 : table_map_id; } -} TABLE_SHARE; +}; extern ulong refresh_version; @@ -608,19 +605,16 @@ enum index_hint_type INDEX_HINT_FORCE }; -struct st_table { - st_table() {} /* Remove gcc warning */ +struct TABLE +{ + TABLE() {} /* Remove gcc warning */ TABLE_SHARE *s; handler *file; -#ifdef NOT_YET - struct st_table *used_next, **used_prev; /* Link to used tables */ - struct st_table *open_next, **open_prev; /* Link to open tables */ -#endif - struct st_table *next, *prev; + TABLE *next, *prev; /* For the below MERGE related members see top comment in ha_myisammrg.cc */ - struct st_table *parent; /* Set in MERGE child. Ptr to parent */ + TABLE *parent; /* Set in MERGE child. Ptr to parent */ TABLE_LIST *child_l; /* Set in MERGE parent. List of children */ TABLE_LIST **child_last_l; /* Set in MERGE parent. End of list */ @@ -1014,7 +1008,6 @@ typedef struct st_schema_table /** The threshold size a blob field buffer before it is freed */ #define MAX_TDC_BLOB_SIZE 65536 -struct st_lex; class select_union; class TMP_TABLE_PARAM; @@ -1092,6 +1085,7 @@ public: (TABLE_LIST::join_using_fields != NULL) */ +struct LEX; class Index_hint; struct TABLE_LIST { @@ -1212,7 +1206,7 @@ struct TABLE_LIST TMP_TABLE_PARAM *schema_table_param; /* link to select_lex where this table was used */ st_select_lex *select_lex; - st_lex *view; /* link on VIEW lex for merging */ + LEX *view; /* link on VIEW lex for merging */ Field_translator *field_translation; /* array of VIEW fields */ /* pointer to element after last one in translation table above */ Field_translator *field_translation_end; @@ -1371,6 +1365,8 @@ struct TABLE_LIST the parsed tree is created. */ uint8 trg_event_map; + /* TRUE <=> this table is a const one and was optimized away. */ + bool optimized_away; uint i_s_requested_object; bool has_db_lookup_value; @@ -1427,9 +1423,9 @@ struct TABLE_LIST Item_subselect *containing_subselect(); /* - Compiles the tagged hints list and fills up st_table::keys_in_use_for_query, - st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by, - st_table::force_index and st_table::covering_keys. + Compiles the tagged hints list and fills up TABLE::keys_in_use_for_query, + TABLE::keys_in_use_for_group_by, TABLE::keys_in_use_for_order_by, + TABLE::force_index and TABLE::covering_keys. */ bool process_index_hints(TABLE *table); diff --git a/sql/tztime.cc b/sql/tztime.cc index 6757798ad32..650678c721b 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1581,17 +1581,17 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) lex_start(thd); /* Init all memory structures that require explicit destruction */ - if (hash_init(&tz_names, &my_charset_latin1, 20, - 0, 0, (hash_get_key) my_tz_names_get_key, 0, 0)) + if (my_hash_init(&tz_names, &my_charset_latin1, 20, + 0, 0, (my_hash_get_key) my_tz_names_get_key, 0, 0)) { sql_print_error("Fatal error: OOM while initializing time zones"); goto end; } - if (hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0, - (hash_get_key)my_offset_tzs_get_key, 0, 0)) + if (my_hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0, + (my_hash_get_key)my_offset_tzs_get_key, 0, 0)) { sql_print_error("Fatal error: OOM while initializing time zones"); - hash_free(&tz_names); + my_hash_free(&tz_names); goto end; } init_alloc_root(&tz_storage, 32 * 1024, 0); @@ -1774,8 +1774,8 @@ void my_tz_free() { tz_inited= 0; VOID(pthread_mutex_destroy(&tz_LOCK)); - hash_free(&offset_tzs); - hash_free(&tz_names); + my_hash_free(&offset_tzs); + my_hash_free(&tz_names); free_root(&tz_storage, MYF(0)); } } @@ -2267,9 +2267,9 @@ my_tz_find(THD *thd, const String *name) if (!str_to_offset(name->ptr(), name->length(), &offset)) { - if (!(result_tz= (Time_zone_offset *)hash_search(&offset_tzs, - (const uchar *)&offset, - sizeof(long)))) + if (!(result_tz= (Time_zone_offset *)my_hash_search(&offset_tzs, + (const uchar *)&offset, + sizeof(long)))) { DBUG_PRINT("info", ("Creating new Time_zone_offset object")); @@ -2285,9 +2285,10 @@ my_tz_find(THD *thd, const String *name) else { result_tz= 0; - if ((tmp_tzname= (Tz_names_entry *)hash_search(&tz_names, - (const uchar *)name->ptr(), - name->length()))) + if ((tmp_tzname= (Tz_names_entry *)my_hash_search(&tz_names, + (const uchar *) + name->ptr(), + name->length()))) result_tz= tmp_tzname->tz; else if (time_zone_tables_exist) { |