diff options
author | unknown <reggie@big_geek.> | 2006-02-16 10:38:33 -0600 |
---|---|---|
committer | unknown <reggie@big_geek.> | 2006-02-16 10:38:33 -0600 |
commit | d047fe77b74b02550d3b72fbb9415d3e8a9f507a (patch) | |
tree | f8ff3f6e12d8493541cca525570bcfc85c2b13c0 | |
parent | 99526f2efb29a7573ed1b4f820c9b2f4cc32753c (diff) | |
download | mariadb-git-d047fe77b74b02550d3b72fbb9415d3e8a9f507a.tar.gz |
BUG#15408: Partitions: subpartition names are not unique
Also, moved some of the code out of handler.h and into partition specific files for better
separation.
Also, moved some of the C funcs into partition_info as formal C++ methods
mysql-test/r/partition_mgm_err.result:
result block for test of bug # 15408
mysql-test/t/partition_mgm_err.test:
test for duplicate subpartition names
sql/Makefile.am:
adding sql_partition.h, partition_info.cpp, partition_info.h, and partition_element.h to the makefile
sql/ha_partition.cc:
using the new members of partition_info
sql/ha_partition.h:
using the new members of partition_info
sql/handler.h:
moved this code into sql_partition.h
sql/mysql_priv.h:
including sql_partition.h also now
sql/opt_range.cc:
using the new members of partition_info
sql/sql_partition.cc:
moved some of the functions out and into the partition_info class
using the new members of partition_info
sql/sql_show.cc:
using the new members of partition_info
win/cmakefiles/sql:
added partition_info.cpp to the sql cmake file
sql/partition_element.h:
New BitKeeper file ``sql/partition_element.h''
sql/partition_info.h:
New BitKeeper file ``sql/partition_info.h''
sql/sql_partition.h:
New BitKeeper file ``sql/sql_partition.h''
-rw-r--r-- | mysql-test/r/partition_mgm_err.result | 5 | ||||
-rw-r--r-- | mysql-test/t/partition_mgm_err.test | 9 | ||||
-rw-r--r-- | sql/Makefile.am | 4 | ||||
-rw-r--r-- | sql/ha_partition.cc | 8 | ||||
-rw-r--r-- | sql/ha_partition.h | 2 | ||||
-rw-r--r-- | sql/handler.h | 496 | ||||
-rw-r--r-- | sql/mysql_priv.h | 2 | ||||
-rw-r--r-- | sql/opt_range.cc | 2 | ||||
-rw-r--r-- | sql/partition_element.h | 70 | ||||
-rw-r--r-- | sql/partition_info.cpp | 347 | ||||
-rw-r--r-- | sql/partition_info.h | 295 | ||||
-rw-r--r-- | sql/sql_partition.cc | 341 | ||||
-rw-r--r-- | sql/sql_partition.h | 218 | ||||
-rw-r--r-- | sql/sql_show.cc | 2 | ||||
-rw-r--r-- | win/cmakefiles/sql | 2 |
15 files changed, 982 insertions, 821 deletions
diff --git a/mysql-test/r/partition_mgm_err.result b/mysql-test/r/partition_mgm_err.result index 58ee2d08947..efa346f7a72 100644 --- a/mysql-test/r/partition_mgm_err.result +++ b/mysql-test/r/partition_mgm_err.result @@ -142,3 +142,8 @@ t1 CREATE TABLE `t1` ( DROP TABLE t1; CREATE TABLE t1 (a INT) PARTITION BY HASH(a); ALTER TABLE t1 ADD PARTITION PARTITIONS 4; +CREATE TABLE t1 (s1 int, s2 int) PARTITION BY LIST (s1) ( +PARTITION p1 VALUES IN (0) (SUBPARTITION p1b), +PARTITION p2 VALUES IN (2) (SUBPARTITION p1b) +); +ERROR HY000: Duplicate partition name p1b diff --git a/mysql-test/t/partition_mgm_err.test b/mysql-test/t/partition_mgm_err.test index 1a24129bcf1..1310e79ab17 100644 --- a/mysql-test/t/partition_mgm_err.test +++ b/mysql-test/t/partition_mgm_err.test @@ -216,3 +216,12 @@ DROP TABLE t1; # CREATE TABLE t1 (a INT) PARTITION BY HASH(a); ALTER TABLE t1 ADD PARTITION PARTITIONS 4; + +# +#BUG 15408: Partitions: subpartition names are not unique +# +--error ER_SAME_NAME_PARTITION +CREATE TABLE t1 (s1 int, s2 int) PARTITION BY LIST (s1) ( +PARTITION p1 VALUES IN (0) (SUBPARTITION p1b), +PARTITION p2 VALUES IN (2) (SUBPARTITION p1b) +); diff --git a/sql/Makefile.am b/sql/Makefile.am index 78eb4857620..685000e0546 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -65,7 +65,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ parse_file.h sql_view.h sql_trigger.h \ sql_array.h sql_cursor.h event.h event_priv.h \ - sql_plugin.h authors.h + sql_plugin.h authors.h sql_partition.h partition_info.h partition_element.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -101,7 +101,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ sp_cache.cc parse_file.cc sql_trigger.cc \ event_executor.cc event.cc event_timed.cc \ sql_plugin.cc sql_binlog.cc \ - handlerton.cc sql_tablespace.cc + handlerton.cc sql_tablespace.cc partition_info.cpp EXTRA_mysqld_SOURCES = ha_innodb.cc ha_berkeley.cc ha_archive.cc \ ha_innodb.h ha_berkeley.h ha_archive.h \ ha_blackhole.cc ha_federated.cc ha_ndbcluster.cc \ diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 0bd300df2e2..a98106eb9a0 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -186,7 +186,7 @@ ha_partition::ha_partition(TABLE_SHARE *share) ha_partition::ha_partition(partition_info *part_info) :handler(&partition_hton, NULL), m_part_info(part_info), m_create_handler(TRUE), - m_is_sub_partitioned(is_sub_partitioned(m_part_info)) + m_is_sub_partitioned(m_part_info->is_sub_partitioned()) { DBUG_ENTER("ha_partition::ha_partition(part_info)"); @@ -331,7 +331,7 @@ int ha_partition::ha_initialise() if (m_create_handler) { - m_tot_parts= get_tot_partitions(m_part_info); + m_tot_parts= m_part_info->get_tot_partitions(); DBUG_ASSERT(m_tot_parts > 0); if (new_handlers_from_part_info()) DBUG_RETURN(1); @@ -1290,7 +1290,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, DBUG_ENTER("ha_partition::change_partitions"); m_reorged_parts= 0; - if (!is_sub_partitioned(m_part_info)) + if (!m_part_info->is_sub_partitioned()) no_subparts= 1; /* @@ -1453,7 +1453,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, if (part_elem->part_state == PART_CHANGED || (part_elem->part_state == PART_TO_BE_ADDED && temp_partitions)) name_variant= TEMP_PART_NAME; - if (is_sub_partitioned(m_part_info)) + if (m_part_info->is_sub_partitioned()) { List_iterator<partition_element> sub_it(part_elem->subpartitions); uint j= 0, part; diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 35435844064..40306ba7da8 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -135,7 +135,7 @@ public: virtual void set_part_info(partition_info *part_info) { m_part_info= part_info; - m_is_sub_partitioned= is_sub_partitioned(part_info); + m_is_sub_partitioned= part_info->is_sub_partitioned(); } /* ------------------------------------------------------------------------- diff --git a/sql/handler.h b/sql/handler.h index 55af6cf4da6..47cc95b8532 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -95,13 +95,6 @@ #define HA_ANY_INDEX_MAY_BE_UNIQUE (1 << 30) #define HA_NO_COPY_ON_ALTER (1 << 31) -/* Flags for partition handlers */ -#define HA_CAN_PARTITION (1 << 0) /* Partition support */ -#define HA_CAN_UPDATE_PARTITION_KEY (1 << 1) -#define HA_CAN_PARTITION_UNIQUE (1 << 2) -#define HA_USE_AUTO_PARTITION (1 << 3) - - /* bits in index_flags(index_number) for what you can do with index */ #define HA_READ_NEXT 1 /* TODO really use this flag */ #define HA_READ_PREV 2 /* supports ::index_prev */ @@ -134,34 +127,6 @@ #define HA_ONLINE_DROP_UNIQUE_INDEX (1L << 9) /*drop uniq. online*/ #define HA_ONLINE_ADD_PK_INDEX (1L << 10)/*add prim. online*/ #define HA_ONLINE_DROP_PK_INDEX (1L << 11)/*drop prim. online*/ -/* - HA_PARTITION_FUNCTION_SUPPORTED indicates that the function is - supported at all. - HA_FAST_CHANGE_PARTITION means that optimised variants of the changes - exists but they are not necessarily done online. - - HA_ONLINE_DOUBLE_WRITE means that the handler supports writing to both - the new partition and to the old partitions when updating through the - old partitioning schema while performing a change of the partitioning. - This means that we can support updating of the table while performing - the copy phase of the change. For no lock at all also a double write - from new to old must exist and this is not required when this flag is - set. - This is actually removed even before it was introduced the first time. - The new idea is that handlers will handle the lock level already in - store_lock for ALTER TABLE partitions. - - HA_PARTITION_ONE_PHASE is a flag that can be set by handlers that take - care of changing the partitions online and in one phase. Thus all phases - needed to handle the change are implemented inside the storage engine. - The storage engine must also support auto-discovery since the frm file - is changed as part of the change and this change must be controlled by - the storage engine. A typical engine to support this is NDB (through - WL #2498). -*/ -#define HA_PARTITION_FUNCTION_SUPPORTED (1L << 12) -#define HA_FAST_CHANGE_PARTITION (1L << 13) -#define HA_PARTITION_ONE_PHASE (1L << 14) /* Index scan will not return records in rowid order. Not guaranteed to be @@ -618,33 +583,6 @@ enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED, enum ndb_distribution { ND_KEYHASH= 0, ND_LINHASH= 1 }; -typedef struct { - uint32 start_part; - uint32 end_part; -} part_id_range; - - -/** - * An enum and a struct to handle partitioning and subpartitioning. - */ -enum partition_type { - NOT_A_PARTITION= 0, - RANGE_PARTITION, - HASH_PARTITION, - LIST_PARTITION -}; - -enum partition_state { - PART_NORMAL= 0, - PART_IS_DROPPED= 1, - PART_TO_BE_DROPPED= 2, - PART_TO_BE_ADDED= 3, - PART_TO_BE_REORGED= 4, - PART_REORGED_DROPPED= 5, - PART_CHANGED= 6, - PART_IS_CHANGED= 7, - PART_IS_ADDED= 8 -}; typedef struct { ulonglong data_file_length; @@ -662,400 +600,12 @@ typedef struct { #define UNDEF_NODEGROUP 65535 class Item; -class partition_element :public Sql_alloc { -public: - List<partition_element> subpartitions; - List<longlong> list_val_list; - ulonglong part_max_rows; - ulonglong part_min_rows; - char *partition_name; - char *tablespace_name; - longlong range_value; - char* part_comment; - char* data_file_name; - char* index_file_name; - handlerton *engine_type; - enum partition_state part_state; - uint16 nodegroup_id; - - partition_element() - : part_max_rows(0), part_min_rows(0), partition_name(NULL), - tablespace_name(NULL), range_value(0), part_comment(NULL), - data_file_name(NULL), index_file_name(NULL), - engine_type(NULL),part_state(PART_NORMAL), - nodegroup_id(UNDEF_NODEGROUP) - { - subpartitions.empty(); - list_val_list.empty(); - } - ~partition_element() {} -}; - -typedef struct { - longlong list_value; - uint32 partition_id; -} LIST_PART_ENTRY; - class partition_info; -typedef int (*get_part_id_func)(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -typedef uint32 (*get_subpart_id_func)(partition_info *part_info); - - struct st_partition_iter; #define NOT_A_PARTITION_ID ((uint32)-1) -/* - A "Get next" function for partition iterator. - SYNOPSIS - partition_iter_func() - part_iter Partition iterator, you call only "iter.get_next(&iter)" - - RETURN - NOT_A_PARTITION_ID if there are no more partitions. - [sub]partition_id of the next partition -*/ - -typedef uint32 (*partition_iter_func)(st_partition_iter* part_iter); - - -/* - Partition set iterator. Used to enumerate a set of [sub]partitions - obtained in partition interval analysis (see get_partitions_in_range_iter). - - For the user, the only meaningful field is get_next, which may be used as - follows: - part_iterator.get_next(&part_iterator); - - Initialization is done by any of the following calls: - - get_partitions_in_range_iter-type function call - - init_single_partition_iterator() - - init_all_partitions_iterator() - Cleanup is not needed. -*/ - -typedef struct st_partition_iter -{ - partition_iter_func get_next; - - struct st_part_num_range - { - uint32 start; - uint32 end; - }; - - struct st_field_value_range - { - longlong start; - longlong end; - }; - - union - { - struct st_part_num_range part_nums; - struct st_field_value_range field_vals; - }; - partition_info *part_info; -} PARTITION_ITERATOR; - - -/* - Get an iterator for set of partitions that match given field-space interval - - 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 - - DESCRIPTION - Functions with this signature are used to perform "Partitioning Interval - Analysis". This analysis is applicable for any type of [sub]partitioning - by some function of a single fieldX. The idea is as follows: - Given an interval "const1 <=? fieldX <=? const2", find a set of partitions - that may contain records with value of fieldX within the given interval. - - The min_val, max_val and flags parameters specify the interval. - The set of partitions is returned by initializing an iterator in *part_iter - - NOTES - There are currently two functions of this type: - - get_part_iter_for_interval_via_walking - - get_part_iter_for_interval_via_mapping - - RETURN - 0 - No matching partitions, iterator not initialized - 1 - Some partitions would match, iterator intialized for traversing them - -1 - All partitions would match, iterator not initialized -*/ - -typedef int (*get_partitions_in_range_iter)(partition_info *part_info, - bool is_subpart, - char *min_val, char *max_val, - uint flags, - PARTITION_ITERATOR *part_iter); - - -class partition_info : public Sql_alloc -{ -public: - /* - * Here comes a set of definitions needed for partitioned table handlers. - */ - List<partition_element> partitions; - List<partition_element> temp_partitions; - - List<char> part_field_list; - List<char> subpart_field_list; - - /* - If there is no subpartitioning, use only this func to get partition ids. - If there is subpartitioning, use the this func to get partition id when - you have both partition and subpartition fields. - */ - get_part_id_func get_partition_id; - - /* Get partition id when we don't have subpartition fields */ - get_part_id_func get_part_partition_id; - - /* - Get subpartition id when we have don't have partition fields by we do - have subpartition ids. - Mikael said that for given constant tuple - {subpart_field1, ..., subpart_fieldN} the subpartition id will be the - same in all subpartitions - */ - get_subpart_id_func get_subpartition_id; - - /* NULL-terminated array of fields used in partitioned expression */ - Field **part_field_array; - /* NULL-terminated array of fields used in subpartitioned expression */ - Field **subpart_field_array; - - /* - Array of all fields used in partition and subpartition expression, - without duplicates, NULL-terminated. - */ - Field **full_part_field_array; - - Item *part_expr; - Item *subpart_expr; - - Item *item_free_list; - - /* - A bitmap of partitions used by the current query. - Usage pattern: - * The handler->extra(HA_EXTRA_RESET) call at query start/end sets all - partitions to be unused. - * Before index/rnd_init(), partition pruning code sets the bits for used - partitions. - */ - MY_BITMAP used_partitions; - - union { - longlong *range_int_array; - LIST_PART_ENTRY *list_array; - }; - - /******************************************** - * INTERVAL ANALYSIS - ********************************************/ - /* - Partitioning interval analysis function for partitioning, or NULL if - interval analysis is not supported for this kind of partitioning. - */ - get_partitions_in_range_iter get_part_iter_for_interval; - /* - Partitioning interval analysis function for subpartitioning, or NULL if - interval analysis is not supported for this kind of partitioning. - */ - get_partitions_in_range_iter get_subpart_iter_for_interval; - - /* - Valid iff - get_part_iter_for_interval=get_part_iter_for_interval_via_walking: - controls how we'll process "field < C" and "field > C" intervals. - If the partitioning function F is strictly increasing, then for any x, y - "x < y" => "F(x) < F(y)" (*), i.e. when we get interval "field < C" - we can perform partition pruning on the equivalent "F(field) < F(C)". - - If the partitioning function not strictly increasing (it is simply - increasing), then instead of (*) we get "x < y" => "F(x) <= F(y)" - i.e. for interval "field < C" we can perform partition pruning for - "F(field) <= F(C)". - */ - bool range_analysis_include_bounds; - /******************************************** - * INTERVAL ANALYSIS ENDS - ********************************************/ - - char* part_info_string; - - char *part_func_string; - char *subpart_func_string; - - uchar *part_state; - - partition_element *curr_part_elem; - partition_element *current_partition; - /* - These key_map's are used for Partitioning to enable quick decisions - on whether we can derive more information about which partition to - scan just by looking at what index is used. - */ - key_map all_fields_in_PF, all_fields_in_PPF, all_fields_in_SPF; - key_map some_fields_in_PF; - - handlerton *default_engine_type; - Item_result part_result_type; - partition_type part_type; - partition_type subpart_type; - - uint part_info_len; - uint part_state_len; - uint part_func_len; - uint subpart_func_len; - - uint no_parts; - uint no_subparts; - uint count_curr_subparts; - - uint part_error_code; - - uint no_list_values; - - uint no_part_fields; - uint no_subpart_fields; - uint no_full_part_fields; - /* - This variable is used to calculate the partition id when using - LINEAR KEY/HASH. This functionality is kept in the MySQL Server - but mainly of use to handlers supporting partitioning. - */ - uint16 linear_hash_mask; - - bool use_default_partitions; - bool use_default_no_partitions; - bool use_default_subpartitions; - bool use_default_no_subpartitions; - bool default_partitions_setup; - bool defined_max_value; - bool list_of_part_fields; - bool list_of_subpart_fields; - bool linear_hash_ind; - bool fixed; - bool from_openfrm; - - partition_info() - : get_partition_id(NULL), get_part_partition_id(NULL), - get_subpartition_id(NULL), - part_field_array(NULL), subpart_field_array(NULL), - full_part_field_array(NULL), - part_expr(NULL), subpart_expr(NULL), item_free_list(NULL), - list_array(NULL), - part_info_string(NULL), - part_func_string(NULL), subpart_func_string(NULL), - part_state(NULL), - curr_part_elem(NULL), current_partition(NULL), - 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), - 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), linear_hash_mask(0), - use_default_partitions(TRUE), - use_default_no_partitions(TRUE), - use_default_subpartitions(TRUE), - use_default_no_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), - from_openfrm(FALSE) - { - all_fields_in_PF.clear_all(); - all_fields_in_PPF.clear_all(); - all_fields_in_SPF.clear_all(); - some_fields_in_PF.clear_all(); - partitions.empty(); - temp_partitions.empty(); - part_field_list.empty(); - subpart_field_list.empty(); - } - ~partition_info() {} -}; - - -#ifdef WITH_PARTITION_STORAGE_ENGINE -uint32 get_next_partition_id_range(struct st_partition_iter* part_iter); - -/* Initialize the iterator to return a single partition with given part_id */ - -static inline void init_single_partition_iterator(uint32 part_id, - PARTITION_ITERATOR *part_iter) -{ - part_iter->part_nums.start= part_id; - part_iter->part_nums.end= part_id+1; - part_iter->get_next= get_next_partition_id_range; -} - -/* Initialize the iterator to enumerate all partitions */ -static inline -void init_all_partitions_iterator(partition_info *part_info, - PARTITION_ITERATOR *part_iter) -{ - part_iter->part_nums.start= 0; - part_iter->part_nums.end= part_info->no_parts; - part_iter->get_next= get_next_partition_id_range; -} - -/* - Answers the question if subpartitioning is used for a certain table - SYNOPSIS - is_sub_partitioned() - part_info A reference to the partition_info struct - RETURN VALUE - Returns true if subpartitioning used and false otherwise - DESCRIPTION - A routine to check for subpartitioning for improved readability of code -*/ -static inline -bool is_sub_partitioned(partition_info *part_info) -{ return (part_info->subpart_type == NOT_A_PARTITION ? FALSE : TRUE); } - - -/* - Returns the total number of partitions on the leaf level. - SYNOPSIS - get_tot_partitions() - part_info A reference to the partition_info struct - RETURN VALUE - Returns the number of partitions - DESCRIPTION - A routine to check for number of partitions for improved readability - of code -*/ -static inline -uint get_tot_partitions(partition_info *part_info) -{ - return part_info->no_parts * - (is_sub_partitioned(part_info) ? part_info->no_subparts : 1); -} - - -#endif typedef struct st_ha_create_information { @@ -1100,52 +650,6 @@ typedef struct st_ha_check_opt } HA_CHECK_OPT; -#ifdef WITH_PARTITION_STORAGE_ENGINE -bool is_partition_in_list(char *part_name, List<char> list_part_names); -char *are_partitions_in_table(partition_info *new_part_info, - partition_info *old_part_info); -bool check_reorganise_list(partition_info *new_part_info, - partition_info *old_part_info, - List<char> list_part_names); -bool set_up_defaults_for_partitioning(partition_info *part_info, - handler *file, - ulonglong max_rows, - uint start_no); -handler *get_ha_partition(partition_info *part_info); -int get_parts_for_update(const byte *old_data, byte *new_data, - const byte *rec0, partition_info *part_info, - uint32 *old_part_id, uint32 *new_part_id, - longlong *func_value); -int get_part_for_delete(const byte *buf, const byte *rec0, - partition_info *part_info, uint32 *part_id); -bool check_partition_info(partition_info *part_info,handlerton **eng_type, - handler *file, ulonglong max_rows); -bool fix_partition_func(THD *thd, const char *name, TABLE *table, - bool create_table_ind); -char *generate_partition_syntax(partition_info *part_info, - uint *buf_length, bool use_sql_alloc, - bool write_all); -bool partition_key_modified(TABLE *table, List<Item> &fields); -void get_partition_set(const TABLE *table, byte *buf, const uint index, - const key_range *key_spec, - part_id_range *part_spec); -void get_full_part_id_from_key(const TABLE *table, byte *buf, - KEY *key_info, - const key_range *key_spec, - part_id_range *part_spec); -bool mysql_unpack_partition(THD *thd, const uchar *part_buf, - uint part_info_len, - uchar *part_state, uint part_state_len, - TABLE *table, bool is_create_table_ind, - handlerton *default_db_type); -void make_used_partitions_str(partition_info *part_info, String *parts_str); -uint32 get_list_array_idx_for_endpoint(partition_info *part_info, - bool left_endpoint, - bool include_endpoint); -uint32 get_partition_id_range_for_endpoint(partition_info *part_info, - bool left_endpoint, - bool include_endpoint); -#endif /* diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index fc777c47818..e477fdcb979 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -503,6 +503,8 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, #include "protocol.h" #include "sql_plugin.h" #include "sql_udf.h" +#include "sql_partition.h" + class user_var_entry; class Security_context; enum enum_var_type diff --git a/sql/opt_range.cc b/sql/opt_range.cc index c0f1abe597c..fcf5127a116 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2976,7 +2976,7 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) ppar->last_subpart_partno= used_subpart_fields?(int)(used_part_fields + used_subpart_fields - 1): -1; - if (is_sub_partitioned(part_info)) + if (part_info->is_sub_partitioned()) { ppar->mark_full_partition_used= mark_full_partition_used_with_parts; ppar->get_top_partition_id_func= part_info->get_part_partition_id; diff --git a/sql/partition_element.h b/sql/partition_element.h new file mode 100644 index 00000000000..06007076405 --- /dev/null +++ b/sql/partition_element.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 + +/** + * An enum and a struct to handle partitioning and subpartitioning. + */ +enum partition_type { + NOT_A_PARTITION= 0, + RANGE_PARTITION, + HASH_PARTITION, + LIST_PARTITION +}; + +enum partition_state { + PART_NORMAL= 0, + PART_IS_DROPPED= 1, + PART_TO_BE_DROPPED= 2, + PART_TO_BE_ADDED= 3, + PART_TO_BE_REORGED= 4, + PART_REORGED_DROPPED= 5, + PART_CHANGED= 6, + PART_IS_CHANGED= 7, + PART_IS_ADDED= 8 +}; + +class partition_element :public Sql_alloc { +public: + List<partition_element> subpartitions; + List<longlong> list_val_list; + ulonglong part_max_rows; + ulonglong part_min_rows; + char *partition_name; + char *tablespace_name; + longlong range_value; + char* part_comment; + char* data_file_name; + char* index_file_name; + handlerton *engine_type; + enum partition_state part_state; + uint16 nodegroup_id; + + partition_element() + : part_max_rows(0), part_min_rows(0), partition_name(NULL), + tablespace_name(NULL), range_value(0), part_comment(NULL), + data_file_name(NULL), index_file_name(NULL), + engine_type(NULL),part_state(PART_NORMAL), + nodegroup_id(UNDEF_NODEGROUP) + { + subpartitions.empty(); + list_val_list.empty(); + } + ~partition_element() {} +}; diff --git a/sql/partition_info.cpp b/sql/partition_info.cpp new file mode 100644 index 00000000000..226ddd8f636 --- /dev/null +++ b/sql/partition_info.cpp @@ -0,0 +1,347 @@ +/* Copyright (C) 2005 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; either version 2 of the License, or + (at your option) any later version. + + 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 */ + +/* + This file was introduced as a container for general functionality related + to partitioning introduced in MySQL version 5.1. It contains functionality + used by all handlers that support partitioning, which in the first version + is the partitioning handler itself and the NDB handler. + + The first version was written by Mikael Ronstrom. + + This 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. +*/ + +/* Some general useful functions */ + +#include "mysql_priv.h" +#include "ha_partition.h" + +#ifdef WITH_PARTITION_STORAGE_ENGINE + + +/* + Create a memory area where default partition names are stored and fill it + up with the names. + + SYNOPSIS + create_default_partition_names() + part_no Partition number for subparts + no_parts Number of partitions + start_no Starting partition number + subpart Is it subpartitions + + RETURN VALUE + A pointer to the memory area of the default partition names + + DESCRIPTION + A support routine for the partition code where default values are + generated. + The external routine needing this code is check_partition_info +*/ + +#define MAX_PART_NAME_SIZE 16 + +char *partition_info::create_default_partition_names(uint part_no, uint no_parts, + uint start_no, bool is_subpart) +{ + char *ptr= sql_calloc(no_parts*MAX_PART_NAME_SIZE); + char *move_ptr= ptr; + uint i= 0; + DBUG_ENTER("create_default_partition_names"); + + if (likely(ptr != 0)) + { + do + { + if (is_subpart) + my_sprintf(move_ptr, (move_ptr,"p%usp%u", part_no, (start_no + i))); + else + my_sprintf(move_ptr, (move_ptr,"p%u", (start_no + i))); + move_ptr+=MAX_PART_NAME_SIZE; + } while (++i < no_parts); + } + else + { + mem_alloc_error(no_parts*MAX_PART_NAME_SIZE); + } + DBUG_RETURN(ptr); +} + + +/* + Set up all the default partitions not set-up by the user in the SQL + statement. Also perform a number of checks that the user hasn't tried + to use default values where no defaults exists. + + SYNOPSIS + set_up_default_partitions() + part_info The reference to all partition information + file A reference to a handler of the table + max_rows Maximum number of rows stored in the table + start_no Starting partition number + + RETURN VALUE + TRUE Error, attempted default values not possible + FALSE Ok, default partitions set-up + + DESCRIPTION + The routine uses the underlying handler of the partitioning to define + the default number of partitions. For some handlers this requires + knowledge of the maximum number of rows to be stored in the table. + This routine only accepts HASH and KEY partitioning and thus there is + no subpartitioning if this routine is successful. + The external routine needing this code is check_partition_info +*/ + +bool partition_info::set_up_default_partitions(handler *file, ulonglong max_rows, + uint start_no) +{ + uint i; + char *default_name; + bool result= TRUE; + DBUG_ENTER("partition_info::set_up_default_partitions"); + + if (part_type != HASH_PARTITION) + { + const char *error_string; + if (part_type == RANGE_PARTITION) + error_string= partition_keywords[PKW_RANGE].str; + else + error_string= partition_keywords[PKW_LIST].str; + my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_string); + goto end; + } + if (no_parts == 0) + no_parts= file->get_default_no_partitions(max_rows); + if (unlikely(no_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, + start_no, + FALSE))))) + goto end; + i= 0; + do + { + partition_element *part_elem= new partition_element(); + if (likely(part_elem != 0 && + (!partitions.push_back(part_elem)))) + { + part_elem->engine_type= default_engine_type; + part_elem->partition_name= default_name; + default_name+=MAX_PART_NAME_SIZE; + } + else + { + mem_alloc_error(sizeof(partition_element)); + goto end; + } + } while (++i < no_parts); + result= FALSE; +end: + DBUG_RETURN(result); +} + +/* + Set up all the default subpartitions not set-up by the user in the SQL + statement. Also perform a number of checks that the default partitioning + becomes an allowed partitioning scheme. + + SYNOPSIS + set_up_default_subpartitions() + part_info The reference to all partition information + file A reference to a handler of the table + max_rows Maximum number of rows stored in the table + + RETURN VALUE + TRUE Error, attempted default values not possible + FALSE Ok, default partitions set-up + + DESCRIPTION + The routine uses the underlying handler of the partitioning to define + the default number of partitions. For some handlers this requires + knowledge of the maximum number of rows to be stored in the table. + This routine is only called for RANGE or LIST partitioning and those + need to be specified so only subpartitions are specified. + The external routine needing this code is check_partition_info +*/ + +bool partition_info::set_up_default_subpartitions(handler *file, + ulonglong max_rows) +{ + uint i, j; //, no_parts, no_subparts; + char *default_name, *name_ptr; + bool result= TRUE; + partition_element *part_elem; + 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(max_rows); +// no_parts= part_info->no_parts; + //no_subparts= part_info->no_subparts; + if (unlikely((no_parts * no_subparts) > MAX_PARTITIONS)) + { + my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); + goto end; + } +// if (unlikely((!(default_name= + // create_default_partition_names(no_subparts, (uint)0, TRUE))))) + //goto end; + i= 0; + do + { + part_elem= part_it++; + j= 0; + name_ptr= create_default_partition_names(i, no_subparts, (uint)0, TRUE); + if (unlikely(!name_ptr)) + goto end; + do + { + partition_element *subpart_elem= new partition_element(); + if (likely(subpart_elem != 0 && + (!part_elem->subpartitions.push_back(subpart_elem)))) + { + subpart_elem->engine_type= default_engine_type; + subpart_elem->partition_name= name_ptr; + name_ptr+= MAX_PART_NAME_SIZE; + } + else + { + mem_alloc_error(sizeof(partition_element)); + goto end; + } + } while (++j < no_subparts); + } while (++i < no_parts); + result= FALSE; +end: + DBUG_RETURN(result); +} + +/* + Support routine for check_partition_info + + SYNOPSIS + set_up_defaults_for_partitioning() + part_info The reference to all partition information + file A reference to a handler of the table + max_rows Maximum number of rows stored in the table + start_no Starting partition number + + RETURN VALUE + TRUE Error, attempted default values not possible + FALSE Ok, default partitions set-up + + DESCRIPTION + Set up defaults for partition or subpartition (cannot set-up for both, + this will return an error. +*/ + +bool partition_info::set_up_defaults_for_partitioning(handler *file, + ulonglong max_rows, + uint start_no) +{ + DBUG_ENTER("partition_info::set_up_defaults_for_partitioning"); + + if (!default_partitions_setup) + { + default_partitions_setup= TRUE; + if (use_default_partitions) + DBUG_RETURN(set_up_default_partitions(file, max_rows, start_no)); + if (is_sub_partitioned() && + use_default_subpartitions) + DBUG_RETURN(set_up_default_subpartitions(file, max_rows)); + } + DBUG_RETURN(FALSE); +} + +bool partition_info::has_unique_name(partition_element *element) +{ + DBUG_ENTER("partition_info::has_unique_name"); + + const char *name_to_check= element->partition_name; + List_iterator<partition_element> parts_it(partitions); + + partition_element *el; + while (el= (parts_it++)) + { + if (!(my_strcasecmp(system_charset_info, el->partition_name, + name_to_check)) && el != element) + DBUG_RETURN(FALSE); + + if (el->subpartitions.is_empty()) continue; + List_iterator<partition_element> subparts_it(el->subpartitions); + partition_element *sub_el; + while (sub_el= (subparts_it++)) + { + if (!(my_strcasecmp(system_charset_info, sub_el->partition_name, + name_to_check)) && sub_el != element) + DBUG_RETURN(FALSE); + } + } + DBUG_RETURN(TRUE); +} + + +/* + A support function to check partition names for duplication in a + partitioned table + + SYNOPSIS + has_unique_names() + + RETURN VALUES + TRUE Has unique part and subpart names + FALSE Doesn't + + DESCRIPTION + Checks that the list of names in the partitions doesn't contain any + duplicated names. +*/ +char *partition_info::has_unique_names() +{ + DBUG_ENTER("partition_info::has_unique_names"); + + List_iterator<partition_element> parts_it(partitions); + + partition_element *el; + while (el= (parts_it++)) + { + if (! has_unique_name(el)) + DBUG_RETURN(el->partition_name); + + if (el->subpartitions.is_empty()) continue; + List_iterator<partition_element> subparts_it(el->subpartitions); + partition_element *subel; + while (subel= (subparts_it++)) + { + if (! has_unique_name(subel)) + DBUG_RETURN(subel->partition_name); + } + } + DBUG_RETURN(NULL); +} + +#endif /* WITH_PARTITION_STORAGE_ENGINE */ diff --git a/sql/partition_info.h b/sql/partition_info.h new file mode 100644 index 00000000000..94bafbac7a9 --- /dev/null +++ b/sql/partition_info.h @@ -0,0 +1,295 @@ +/* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 "partition_element.h" + +class partition_info; + +/* Some function typedefs */ +typedef int (*get_part_id_func)(partition_info *part_info, + uint32 *part_id, + longlong *func_value); +typedef uint32 (*get_subpart_id_func)(partition_info *part_info); + + + +class partition_info : public Sql_alloc +{ +public: + /* + * Here comes a set of definitions needed for partitioned table handlers. + */ + List<partition_element> partitions; + List<partition_element> temp_partitions; + + List<char> part_field_list; + List<char> subpart_field_list; + + /* + If there is no subpartitioning, use only this func to get partition ids. + If there is subpartitioning, use the this func to get partition id when + you have both partition and subpartition fields. + */ + get_part_id_func get_partition_id; + + /* Get partition id when we don't have subpartition fields */ + get_part_id_func get_part_partition_id; + + /* + Get subpartition id when we have don't have partition fields by we do + have subpartition ids. + Mikael said that for given constant tuple + {subpart_field1, ..., subpart_fieldN} the subpartition id will be the + same in all subpartitions + */ + get_subpart_id_func get_subpartition_id; + + /* NULL-terminated array of fields used in partitioned expression */ + Field **part_field_array; + /* NULL-terminated array of fields used in subpartitioned expression */ + Field **subpart_field_array; + + /* + Array of all fields used in partition and subpartition expression, + without duplicates, NULL-terminated. + */ + Field **full_part_field_array; + + Item *part_expr; + Item *subpart_expr; + + Item *item_free_list; + + /* + A bitmap of partitions used by the current query. + Usage pattern: + * The handler->extra(HA_EXTRA_RESET) call at query start/end sets all + partitions to be unused. + * Before index/rnd_init(), partition pruning code sets the bits for used + partitions. + */ + MY_BITMAP used_partitions; + + union { + longlong *range_int_array; + LIST_PART_ENTRY *list_array; + }; + + /******************************************** + * INTERVAL ANALYSIS + ********************************************/ + /* + Partitioning interval analysis function for partitioning, or NULL if + interval analysis is not supported for this kind of partitioning. + */ + get_partitions_in_range_iter get_part_iter_for_interval; + /* + Partitioning interval analysis function for subpartitioning, or NULL if + interval analysis is not supported for this kind of partitioning. + */ + get_partitions_in_range_iter get_subpart_iter_for_interval; + + /* + Valid iff + get_part_iter_for_interval=get_part_iter_for_interval_via_walking: + controls how we'll process "field < C" and "field > C" intervals. + If the partitioning function F is strictly increasing, then for any x, y + "x < y" => "F(x) < F(y)" (*), i.e. when we get interval "field < C" + we can perform partition pruning on the equivalent "F(field) < F(C)". + + If the partitioning function not strictly increasing (it is simply + increasing), then instead of (*) we get "x < y" => "F(x) <= F(y)" + i.e. for interval "field < C" we can perform partition pruning for + "F(field) <= F(C)". + */ + bool range_analysis_include_bounds; + /******************************************** + * INTERVAL ANALYSIS ENDS + ********************************************/ + + char* part_info_string; + + char *part_func_string; + char *subpart_func_string; + + uchar *part_state; + + partition_element *curr_part_elem; + partition_element *current_partition; + /* + These key_map's are used for Partitioning to enable quick decisions + on whether we can derive more information about which partition to + scan just by looking at what index is used. + */ + key_map all_fields_in_PF, all_fields_in_PPF, all_fields_in_SPF; + key_map some_fields_in_PF; + + handlerton *default_engine_type; + Item_result part_result_type; + partition_type part_type; + partition_type subpart_type; + + uint part_info_len; + uint part_state_len; + uint part_func_len; + uint subpart_func_len; + + uint no_parts; + uint no_subparts; + uint count_curr_subparts; + + uint part_error_code; + + uint no_list_values; + + uint no_part_fields; + uint no_subpart_fields; + uint no_full_part_fields; + + /* + This variable is used to calculate the partition id when using + LINEAR KEY/HASH. This functionality is kept in the MySQL Server + but mainly of use to handlers supporting partitioning. + */ + uint16 linear_hash_mask; + + bool use_default_partitions; + bool use_default_no_partitions; + bool use_default_subpartitions; + bool use_default_no_subpartitions; + bool default_partitions_setup; + bool defined_max_value; + bool list_of_part_fields; + bool list_of_subpart_fields; + bool linear_hash_ind; + bool fixed; + bool from_openfrm; + + partition_info() + : get_partition_id(NULL), get_part_partition_id(NULL), + get_subpartition_id(NULL), + part_field_array(NULL), subpart_field_array(NULL), + full_part_field_array(NULL), + part_expr(NULL), subpart_expr(NULL), item_free_list(NULL), + list_array(NULL), + part_info_string(NULL), + part_func_string(NULL), subpart_func_string(NULL), + part_state(NULL), + curr_part_elem(NULL), current_partition(NULL), + 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), + 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), linear_hash_mask(0), + use_default_partitions(TRUE), + use_default_no_partitions(TRUE), + use_default_subpartitions(TRUE), + use_default_no_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), + from_openfrm(FALSE) + { + all_fields_in_PF.clear_all(); + all_fields_in_PPF.clear_all(); + all_fields_in_SPF.clear_all(); + some_fields_in_PF.clear_all(); + partitions.empty(); + temp_partitions.empty(); + part_field_list.empty(); + subpart_field_list.empty(); + } + ~partition_info() {} + + bool is_sub_partitioned(); + uint get_tot_partitions(); + bool set_up_defaults_for_partitioning(handler *file, ulonglong max_rows, + uint start_no); + char *has_unique_names(); +private: + bool set_up_default_partitions(handler *file, ulonglong max_rows, + uint start_no); + bool set_up_default_subpartitions(handler *file, ulonglong max_rows); + char *create_default_partition_names(uint part_no, uint no_parts, + uint start_no, bool is_subpart); + bool has_unique_name(partition_element *element); +}; + +/* + Answers the question if subpartitioning is used for a certain table + SYNOPSIS + is_sub_partitioned() + part_info A reference to the partition_info struct + RETURN VALUE + Returns true if subpartitioning used and false otherwise + DESCRIPTION + A routine to check for subpartitioning for improved readability of code +*/ +inline +bool partition_info::is_sub_partitioned() +{ + return (subpart_type == NOT_A_PARTITION ? FALSE : TRUE); +} + + +/* + Returns the total number of partitions on the leaf level. + SYNOPSIS + get_tot_partitions() + part_info A reference to the partition_info struct + RETURN VALUE + Returns the number of partitions + DESCRIPTION + A routine to check for number of partitions for improved readability + of code +*/ +inline +uint partition_info::get_tot_partitions() +{ + return no_parts * (is_sub_partitioned() ? no_subparts : 1); +} + +uint32 get_next_partition_id_range(struct st_partition_iter* part_iter); + +/* Initialize the iterator to return a single partition with given part_id */ + +static inline void init_single_partition_iterator(uint32 part_id, + PARTITION_ITERATOR *part_iter) +{ + part_iter->part_nums.start= part_id; + part_iter->part_nums.end= part_id+1; + part_iter->get_next= get_next_partition_id_range; +} + +/* Initialize the iterator to enumerate all partitions */ +static inline +void init_all_partitions_iterator(partition_info *part_info, + PARTITION_ITERATOR *part_iter) +{ + part_iter->part_nums.start= 0; + part_iter->part_nums.end= part_info->no_parts; + part_iter->get_next= get_next_partition_id_range; +} diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 290d512198f..3c0d098666e 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -181,59 +181,6 @@ bool is_name_in_list(char *name, } -/* - A support function to check partition names for duplication in a - partitioned table - - SYNOPSIS - are_partitions_in_table() - new_part_info New partition info - old_part_info Old partition info - - RETURN VALUES - TRUE Duplicate names found - FALSE Duplicate names not found - - DESCRIPTION - Can handle that the new and old parts are the same in which case it - checks that the list of names in the partitions doesn't contain any - duplicated names. -*/ - -char *are_partitions_in_table(partition_info *new_part_info, - partition_info *old_part_info) -{ - uint no_new_parts= new_part_info->partitions.elements; - uint no_old_parts= old_part_info->partitions.elements; - uint new_count, old_count; - List_iterator<partition_element> new_parts_it(new_part_info->partitions); - bool is_same_part_info= (new_part_info == old_part_info); - DBUG_ENTER("are_partitions_in_table"); - DBUG_PRINT("enter", ("%u", no_new_parts)); - - new_count= 0; - do - { - List_iterator<partition_element> old_parts_it(old_part_info->partitions); - char *new_name= (new_parts_it++)->partition_name; - DBUG_PRINT("info", ("%s", new_name)); - new_count++; - old_count= 0; - do - { - char *old_name= (old_parts_it++)->partition_name; - old_count++; - if (is_same_part_info && old_count == new_count) - break; - if (!(my_strcasecmp(system_charset_info, old_name, new_name))) - { - DBUG_PRINT("info", ("old_name = %s, not ok", old_name)); - DBUG_RETURN(old_name); - } - } while (old_count < no_old_parts); - } while (new_count < no_new_parts); - DBUG_RETURN(NULL); -} /* Set-up defaults for partitions. @@ -262,7 +209,7 @@ bool partition_default_handling(TABLE *table, partition_info *part_info, DBUG_RETURN(TRUE); } } - else if (is_sub_partitioned(part_info) && + else if (part_info->is_sub_partitioned() && part_info->use_default_no_subpartitions) { uint no_parts; @@ -274,8 +221,8 @@ bool partition_default_handling(TABLE *table, partition_info *part_info, part_info->no_subparts= no_parts / part_info->no_parts; DBUG_ASSERT((no_parts % part_info->no_parts) == 0); } - set_up_defaults_for_partitioning(part_info, table->file, - (ulonglong)0, (uint)0); + part_info->set_up_defaults_for_partitioning(table->file, + (ulonglong)0, (uint)0); DBUG_RETURN(FALSE); } @@ -660,244 +607,9 @@ end: } -/* - Create a memory area where default partition names are stored and fill it - up with the names. - - SYNOPSIS - create_default_partition_names() - no_parts Number of partitions - start_no Starting partition number - subpart Is it subpartitions - - RETURN VALUE - A pointer to the memory area of the default partition names - - DESCRIPTION - A support routine for the partition code where default values are - generated. - The external routine needing this code is check_partition_info -*/ - -#define MAX_PART_NAME_SIZE 8 - -static char *create_default_partition_names(uint no_parts, uint start_no, - bool is_subpart) -{ - char *ptr= sql_calloc(no_parts*MAX_PART_NAME_SIZE); - char *move_ptr= ptr; - uint i= 0; - DBUG_ENTER("create_default_partition_names"); - - if (likely(ptr != 0)) - { - do - { - if (is_subpart) - my_sprintf(move_ptr, (move_ptr,"sp%u", (start_no + i))); - else - my_sprintf(move_ptr, (move_ptr,"p%u", (start_no + i))); - move_ptr+=MAX_PART_NAME_SIZE; - } while (++i < no_parts); - } - else - { - mem_alloc_error(no_parts*MAX_PART_NAME_SIZE); - } - DBUG_RETURN(ptr); -} - - -/* - Set up all the default partitions not set-up by the user in the SQL - statement. Also perform a number of checks that the user hasn't tried - to use default values where no defaults exists. - - SYNOPSIS - set_up_default_partitions() - part_info The reference to all partition information - file A reference to a handler of the table - max_rows Maximum number of rows stored in the table - start_no Starting partition number - - RETURN VALUE - TRUE Error, attempted default values not possible - FALSE Ok, default partitions set-up - DESCRIPTION - The routine uses the underlying handler of the partitioning to define - the default number of partitions. For some handlers this requires - knowledge of the maximum number of rows to be stored in the table. - This routine only accepts HASH and KEY partitioning and thus there is - no subpartitioning if this routine is successful. - The external routine needing this code is check_partition_info -*/ -static bool set_up_default_partitions(partition_info *part_info, - handler *file, ulonglong max_rows, - uint start_no) -{ - uint no_parts, i; - char *default_name; - bool result= TRUE; - DBUG_ENTER("set_up_default_partitions"); - if (part_info->part_type != HASH_PARTITION) - { - const char *error_string; - if (part_info->part_type == RANGE_PARTITION) - error_string= partition_keywords[PKW_RANGE].str; - else - error_string= partition_keywords[PKW_LIST].str; - my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_string); - goto end; - } - if (part_info->no_parts == 0) - part_info->no_parts= file->get_default_no_partitions(max_rows); - no_parts= part_info->no_parts; - if (unlikely(no_parts > MAX_PARTITIONS)) - { - my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); - goto end; - } - if (unlikely((!(default_name= create_default_partition_names(no_parts, - start_no, - FALSE))))) - goto end; - i= 0; - do - { - partition_element *part_elem= new partition_element(); - if (likely(part_elem != 0 && - (!part_info->partitions.push_back(part_elem)))) - { - part_elem->engine_type= part_info->default_engine_type; - part_elem->partition_name= default_name; - default_name+=MAX_PART_NAME_SIZE; - } - else - { - mem_alloc_error(sizeof(partition_element)); - goto end; - } - } while (++i < no_parts); - result= FALSE; -end: - DBUG_RETURN(result); -} - - -/* - Set up all the default subpartitions not set-up by the user in the SQL - statement. Also perform a number of checks that the default partitioning - becomes an allowed partitioning scheme. - - SYNOPSIS - set_up_default_subpartitions() - part_info The reference to all partition information - file A reference to a handler of the table - max_rows Maximum number of rows stored in the table - - RETURN VALUE - TRUE Error, attempted default values not possible - FALSE Ok, default partitions set-up - - DESCRIPTION - The routine uses the underlying handler of the partitioning to define - the default number of partitions. For some handlers this requires - knowledge of the maximum number of rows to be stored in the table. - This routine is only called for RANGE or LIST partitioning and those - need to be specified so only subpartitions are specified. - The external routine needing this code is check_partition_info -*/ - -static bool set_up_default_subpartitions(partition_info *part_info, - handler *file, ulonglong max_rows) -{ - uint i, j, no_parts, no_subparts; - char *default_name, *name_ptr; - bool result= TRUE; - partition_element *part_elem; - List_iterator<partition_element> part_it(part_info->partitions); - DBUG_ENTER("set_up_default_subpartitions"); - - if (part_info->no_subparts == 0) - part_info->no_subparts= file->get_default_no_partitions(max_rows); - no_parts= part_info->no_parts; - no_subparts= part_info->no_subparts; - if (unlikely((no_parts * no_subparts) > MAX_PARTITIONS)) - { - my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); - goto end; - } - if (unlikely((!(default_name= - create_default_partition_names(no_subparts, (uint)0, TRUE))))) - goto end; - i= 0; - do - { - part_elem= part_it++; - j= 0; - name_ptr= default_name; - do - { - partition_element *subpart_elem= new partition_element(); - if (likely(subpart_elem != 0 && - (!part_elem->subpartitions.push_back(subpart_elem)))) - { - subpart_elem->engine_type= part_info->default_engine_type; - subpart_elem->partition_name= name_ptr; - name_ptr+= MAX_PART_NAME_SIZE; - } - else - { - mem_alloc_error(sizeof(partition_element)); - goto end; - } - } while (++j < no_subparts); - } while (++i < no_parts); - result= FALSE; -end: - DBUG_RETURN(result); -} - - -/* - Support routine for check_partition_info - - SYNOPSIS - set_up_defaults_for_partitioning() - part_info The reference to all partition information - file A reference to a handler of the table - max_rows Maximum number of rows stored in the table - start_no Starting partition number - - RETURN VALUE - TRUE Error, attempted default values not possible - FALSE Ok, default partitions set-up - - DESCRIPTION - Set up defaults for partition or subpartition (cannot set-up for both, - this will return an error. -*/ - -bool set_up_defaults_for_partitioning(partition_info *part_info, - handler *file, - ulonglong max_rows, uint start_no) -{ - DBUG_ENTER("set_up_defaults_for_partitioning"); - - if (!part_info->default_partitions_setup) - { - part_info->default_partitions_setup= TRUE; - if (part_info->use_default_partitions) - DBUG_RETURN(set_up_default_partitions(part_info, file, max_rows, - start_no)); - if (is_sub_partitioned(part_info) && part_info->use_default_subpartitions) - DBUG_RETURN(set_up_default_subpartitions(part_info, file, max_rows)); - } - DBUG_RETURN(FALSE); -} /* @@ -967,7 +679,7 @@ bool check_partition_info(partition_info *part_info,handlerton **eng_type, char *same_name; DBUG_ENTER("check_partition_info"); - if (unlikely(is_sub_partitioned(part_info) && + if (unlikely(part_info->is_sub_partitioned() && (!(part_info->part_type == RANGE_PARTITION || part_info->part_type == LIST_PARTITION)))) { @@ -975,17 +687,17 @@ bool check_partition_info(partition_info *part_info,handlerton **eng_type, my_error(ER_SUBPARTITION_ERROR, MYF(0)); goto end; } - if (unlikely(set_up_defaults_for_partitioning(part_info, file, - max_rows, (uint)0))) + if (unlikely(part_info->set_up_defaults_for_partitioning(file, + max_rows, + (uint)0))) goto end; - tot_partitions= get_tot_partitions(part_info); + tot_partitions= part_info->get_tot_partitions(); if (unlikely(tot_partitions > MAX_PARTITIONS)) { my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); goto end; } - if (((same_name= are_partitions_in_table(part_info, - part_info)))) + if (same_name= part_info->has_unique_names()) { my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name); goto end; @@ -1001,7 +713,7 @@ bool check_partition_info(partition_info *part_info,handlerton **eng_type, do { partition_element *part_elem= part_it++; - if (!is_sub_partitioned(part_info)) + if (!part_info->is_sub_partitioned()) { if (part_elem->engine_type == NULL) part_elem->engine_type= part_info->default_engine_type; @@ -1194,7 +906,7 @@ static bool create_full_part_field_array(TABLE *table, bool result= FALSE; DBUG_ENTER("create_full_part_field_array"); - if (!is_sub_partitioned(part_info)) + 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; @@ -1751,7 +1463,7 @@ static void set_up_partition_key_maps(TABLE *table, part_info->all_fields_in_PF.set_bit(i); if (some_fields) part_info->some_fields_in_PF.set_bit(i); - if (is_sub_partitioned(part_info)) + if (part_info->is_sub_partitioned()) { check_fields_in_PF(part_info->part_field_array, &all_fields, &some_fields); @@ -1789,7 +1501,7 @@ static void set_up_partition_func_pointers(partition_info *part_info) { DBUG_ENTER("set_up_partition_func_pointers"); - if (is_sub_partitioned(part_info)) + if (part_info->is_sub_partitioned()) { if (part_info->part_type == RANGE_PARTITION) { @@ -2011,7 +1723,7 @@ bool fix_partition_func(THD *thd, const char* name, TABLE *table, DBUG_RETURN(TRUE); } } - if (is_sub_partitioned(part_info)) + if (part_info->is_sub_partitioned()) { DBUG_ASSERT(part_info->subpart_type == HASH_PARTITION); /* @@ -2442,7 +2154,7 @@ char *generate_partition_syntax(partition_info *part_info, err+= add_int(fptr, part_info->no_parts); err+= add_space(fptr); } - if (is_sub_partitioned(part_info)) + if (part_info->is_sub_partitioned()) { err+= add_subpartition_by(fptr); /* Must be hash partitioning for subpartitioning */ @@ -2528,9 +2240,9 @@ char *generate_partition_syntax(partition_info *part_info, err+= add_string(fptr, part_elem->partition_name); err+= add_space(fptr); err+= add_partition_values(fptr, part_info, part_elem); - if (!is_sub_partitioned(part_info)) + if (!part_info->is_sub_partitioned()) err+= add_partition_options(fptr, part_elem); - if (is_sub_partitioned(part_info) && + if (part_info->is_sub_partitioned() && (write_all || (!part_info->use_default_subpartitions))) { err+= add_space(fptr); @@ -3635,7 +3347,7 @@ void get_partition_set(const TABLE *table, byte *buf, const uint index, const key_range *key_spec, part_id_range *part_spec) { partition_info *part_info= table->part_info; - uint no_parts= get_tot_partitions(part_info); + uint no_parts= part_info->get_tot_partitions(); uint i, part_id; uint sub_part= no_parts; uint32 part_part= no_parts; @@ -3671,7 +3383,7 @@ void get_partition_set(const TABLE *table, byte *buf, const uint index, get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec); DBUG_VOID_RETURN; } - else if (is_sub_partitioned(part_info)) + else if (part_info->is_sub_partitioned()) { if (part_info->all_fields_in_SPF.is_set(index)) sub_part= get_sub_part_id_from_key(table, buf, key_info, key_spec); @@ -3713,7 +3425,7 @@ void get_partition_set(const TABLE *table, byte *buf, const uint index, clear_indicator_in_key_fields(key_info); DBUG_VOID_RETURN; } - else if (is_sub_partitioned(part_info)) + else if (part_info->is_sub_partitioned()) { if (check_part_func_bound(part_info->subpart_field_array)) sub_part= get_sub_part_id_from_key(table, buf, key_info, key_spec); @@ -4131,7 +3843,7 @@ static bool check_native_partitioned(HA_CREATE_INFO *create_info,bool *ret_val, do { partition_element *part_elem= part_it++; - if (is_sub_partitioned(part_info) && + if (part_info->is_sub_partitioned() && part_elem->subpartitions.elements) { uint no_subparts= part_elem->subpartitions.elements; @@ -4321,7 +4033,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info, my_error(ER_ADD_PARTITION_NO_NEW_PARTITION, MYF(0)); DBUG_RETURN(TRUE); } - if (is_sub_partitioned(tab_part_info)) + if (tab_part_info->is_sub_partitioned()) { if (alt_part_info->no_subparts == 0) alt_part_info->no_subparts= tab_part_info->no_subparts; @@ -4339,10 +4051,9 @@ uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info, DBUG_RETURN(TRUE); } alt_part_info->part_type= tab_part_info->part_type; - if (set_up_defaults_for_partitioning(alt_part_info, - table->file, - ULL(0), - tab_part_info->no_parts)) + if (alt_part_info->set_up_defaults_for_partitioning(table->file, + ULL(0), + tab_part_info->no_parts)) { DBUG_RETURN(TRUE); } @@ -5550,7 +5261,7 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str) uint partition_id= 0; List_iterator<partition_element> it(part_info->partitions); - if (is_sub_partitioned(part_info)) + if (part_info->is_sub_partitioned()) { partition_element *head_pe; while ((head_pe= it++)) diff --git a/sql/sql_partition.h b/sql/sql_partition.h new file mode 100644 index 00000000000..37c8635dcb4 --- /dev/null +++ b/sql/sql_partition.h @@ -0,0 +1,218 @@ +/* Copyright (C) 2005 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; either version 2 of the License, or + (at your option) any later version. + + 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 __GNUC__ +#pragma interface /* gcc class implementation */ +#endif + +/* Flags for partition handlers */ +#define HA_CAN_PARTITION (1 << 0) /* Partition support */ +#define HA_CAN_UPDATE_PARTITION_KEY (1 << 1) +#define HA_CAN_PARTITION_UNIQUE (1 << 2) +#define HA_USE_AUTO_PARTITION (1 << 3) + +/* + HA_PARTITION_FUNCTION_SUPPORTED indicates that the function is + supported at all. + HA_FAST_CHANGE_PARTITION means that optimised variants of the changes + exists but they are not necessarily done online. + + HA_ONLINE_DOUBLE_WRITE means that the handler supports writing to both + the new partition and to the old partitions when updating through the + old partitioning schema while performing a change of the partitioning. + This means that we can support updating of the table while performing + the copy phase of the change. For no lock at all also a double write + from new to old must exist and this is not required when this flag is + set. + This is actually removed even before it was introduced the first time. + The new idea is that handlers will handle the lock level already in + store_lock for ALTER TABLE partitions. + + HA_PARTITION_ONE_PHASE is a flag that can be set by handlers that take + care of changing the partitions online and in one phase. Thus all phases + needed to handle the change are implemented inside the storage engine. + The storage engine must also support auto-discovery since the frm file + is changed as part of the change and this change must be controlled by + the storage engine. A typical engine to support this is NDB (through + WL #2498). +*/ +#define HA_PARTITION_FUNCTION_SUPPORTED (1L << 12) +#define HA_FAST_CHANGE_PARTITION (1L << 13) +#define HA_PARTITION_ONE_PHASE (1L << 14) + +/*typedef struct { + ulonglong data_file_length; + ulonglong max_data_file_length; + ulonglong index_file_length; + ulonglong delete_length; + ha_rows records; + ulong mean_rec_length; + time_t create_time; + time_t check_time; + time_t update_time; + ulonglong check_sum; +} PARTITION_INFO; +*/ +typedef struct { + longlong list_value; + uint32 partition_id; +} LIST_PART_ENTRY; + +typedef struct { + uint32 start_part; + uint32 end_part; +} part_id_range; + +struct st_partition_iter; +#define NOT_A_PARTITION_ID ((uint32)-1) + +bool is_partition_in_list(char *part_name, List<char> list_part_names); +char *are_partitions_in_table(partition_info *new_part_info, + partition_info *old_part_info); +bool check_reorganise_list(partition_info *new_part_info, + partition_info *old_part_info, + List<char> list_part_names); +handler *get_ha_partition(partition_info *part_info); +int get_parts_for_update(const byte *old_data, byte *new_data, + const byte *rec0, partition_info *part_info, + uint32 *old_part_id, uint32 *new_part_id, + longlong *func_value); +int get_part_for_delete(const byte *buf, const byte *rec0, + partition_info *part_info, uint32 *part_id); +bool check_partition_info(partition_info *part_info,handlerton **eng_type, + handler *file, ulonglong max_rows); +bool fix_partition_func(THD *thd, const char *name, TABLE *table, + bool create_table_ind); +char *generate_partition_syntax(partition_info *part_info, + uint *buf_length, bool use_sql_alloc, + bool write_all); +bool partition_key_modified(TABLE *table, List<Item> &fields); +void get_partition_set(const TABLE *table, byte *buf, const uint index, + const key_range *key_spec, + part_id_range *part_spec); +void get_full_part_id_from_key(const TABLE *table, byte *buf, + KEY *key_info, + const key_range *key_spec, + part_id_range *part_spec); +bool mysql_unpack_partition(THD *thd, const uchar *part_buf, + uint part_info_len, + uchar *part_state, uint part_state_len, + TABLE *table, bool is_create_table_ind, + handlerton *default_db_type); +void make_used_partitions_str(partition_info *part_info, String *parts_str); +uint32 get_list_array_idx_for_endpoint(partition_info *part_info, + bool left_endpoint, + bool include_endpoint); +uint32 get_partition_id_range_for_endpoint(partition_info *part_info, + bool left_endpoint, + bool include_endpoint); + +/* + A "Get next" function for partition iterator. + SYNOPSIS + partition_iter_func() + part_iter Partition iterator, you call only "iter.get_next(&iter)" + + RETURN + NOT_A_PARTITION_ID if there are no more partitions. + [sub]partition_id of the next partition +*/ + +typedef uint32 (*partition_iter_func)(st_partition_iter* part_iter); + + +/* + Partition set iterator. Used to enumerate a set of [sub]partitions + obtained in partition interval analysis (see get_partitions_in_range_iter). + + For the user, the only meaningful field is get_next, which may be used as + follows: + part_iterator.get_next(&part_iterator); + + Initialization is done by any of the following calls: + - get_partitions_in_range_iter-type function call + - init_single_partition_iterator() + - init_all_partitions_iterator() + Cleanup is not needed. +*/ + +typedef struct st_partition_iter +{ + partition_iter_func get_next; + + struct st_part_num_range + { + uint32 start; + uint32 end; + }; + + struct st_field_value_range + { + longlong start; + longlong end; + }; + + union + { + struct st_part_num_range part_nums; + struct st_field_value_range field_vals; + }; + partition_info *part_info; +} PARTITION_ITERATOR; + + +/* + Get an iterator for set of partitions that match given field-space interval + + 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 + + DESCRIPTION + Functions with this signature are used to perform "Partitioning Interval + Analysis". This analysis is applicable for any type of [sub]partitioning + by some function of a single fieldX. The idea is as follows: + Given an interval "const1 <=? fieldX <=? const2", find a set of partitions + that may contain records with value of fieldX within the given interval. + + The min_val, max_val and flags parameters specify the interval. + The set of partitions is returned by initializing an iterator in *part_iter + + NOTES + There are currently two functions of this type: + - get_part_iter_for_interval_via_walking + - get_part_iter_for_interval_via_mapping + + RETURN + 0 - No matching partitions, iterator not initialized + 1 - Some partitions would match, iterator intialized for traversing them + -1 - All partitions would match, iterator not initialized +*/ + +typedef int (*get_partitions_in_range_iter)(partition_info *part_info, + bool is_subpart, + char *min_val, char *max_val, + uint flags, + PARTITION_ITERATOR *part_iter); + +#include "partition_info.h" + diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 981dd3d4915..7723f2d5bf1 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3657,7 +3657,7 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, table->field[9]->set_notnull(); } - if (is_sub_partitioned(part_info)) + if (part_info->is_sub_partitioned()) { /* Subpartition method */ if (part_info->list_of_subpart_fields) diff --git a/win/cmakefiles/sql b/win/cmakefiles/sql index ee53436a1a6..c7d36cc1583 100644 --- a/win/cmakefiles/sql +++ b/win/cmakefiles/sql @@ -40,7 +40,7 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc discover. sql_state.c sql_string.cc sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc sql_update.cc sql_view.cc sql_yacc.h sql_yacc.cc strfunc.cc table.cc thr_malloc.cc time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc rpl_tblmap.cc sql_binlog.cc event_executor.cc - event_timed.cc sql_tablespace.cc event.cc ../sql-common/my_user.c + event_timed.cc sql_tablespace.cc event.cc ../sql-common/my_user.c partition_info.cpp ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h ${PROJECT_SOURCE_DIR}/include/mysqld_error.h |