You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
502 lines
17 KiB
502 lines
17 KiB
/* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include "sql/parse_tree_partitions.h"
|
|
|
|
#include "m_ctype.h"
|
|
#include "my_dbug.h"
|
|
#include "my_sys.h"
|
|
#include "mysql_com.h"
|
|
#include "mysqld_error.h"
|
|
#include "sql/derror.h"
|
|
#include "sql/item.h"
|
|
#include "sql/parse_location.h"
|
|
#include "sql/sql_class.h"
|
|
#include "sql/sql_const.h"
|
|
#include "sql/sql_lex.h"
|
|
#include "sql/sql_list.h"
|
|
#include "sql/sql_parse.h"
|
|
#include "sql_string.h"
|
|
|
|
Partition_parse_context::Partition_parse_context(
|
|
THD *thd_arg, partition_info *part_info_arg,
|
|
partition_element *current_partition_arg,
|
|
partition_element *curr_part_elem_arg, bool is_add_or_reorganize_partition)
|
|
: Parse_context(thd_arg, thd_arg->lex->current_select()),
|
|
Parser_partition_info(part_info_arg, current_partition_arg,
|
|
curr_part_elem_arg, nullptr, 0),
|
|
is_add_or_reorganize_partition(is_add_or_reorganize_partition) {}
|
|
|
|
bool PT_subpartition::contextualize(Partition_parse_context *pc) {
|
|
partition_info *const part_info = pc->part_info;
|
|
|
|
if (part_info->use_default_subpartitions &&
|
|
part_info->partitions.elements >= 2) {
|
|
/*
|
|
create table t1 (a int)
|
|
partition by list (a) subpartition by hash (a)
|
|
(partition p0 values in (1),
|
|
partition p1 values in (2) subpartition sp11);
|
|
causes us to arrive since we are on the second
|
|
partition, but still use_default_subpartitions
|
|
is set. When we come here we're processing at least
|
|
the second partition (the current partition processed
|
|
have already been put into the partitions list.
|
|
*/
|
|
error(pc, pos, ER_THD(pc->thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR));
|
|
return true;
|
|
}
|
|
|
|
auto *sub_p_elem =
|
|
new (pc->mem_root) partition_element(pc->current_partition);
|
|
if (sub_p_elem == NULL) return true;
|
|
|
|
if (check_string_char_length(to_lex_cstring(name), "", NAME_CHAR_LEN,
|
|
system_charset_info, true)) {
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), name);
|
|
return true;
|
|
}
|
|
|
|
sub_p_elem->partition_name = name;
|
|
|
|
part_info->use_default_subpartitions = false;
|
|
part_info->use_default_num_subpartitions = false;
|
|
pc->count_curr_subparts++;
|
|
|
|
Partition_parse_context subpart_pc(pc->thd, part_info, pc->current_partition,
|
|
sub_p_elem,
|
|
pc->is_add_or_reorganize_partition);
|
|
|
|
if (options != NULL) {
|
|
for (auto option : *options) {
|
|
if (option->contextualize(&subpart_pc)) return true;
|
|
}
|
|
if (sub_p_elem->engine_type != NULL)
|
|
part_info->default_engine_type = sub_p_elem->engine_type;
|
|
}
|
|
if (pc->current_partition->subpartitions.push_back(sub_p_elem)) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_value_item_max::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc)) return true;
|
|
|
|
if (pc->part_info->part_type == partition_type::LIST) {
|
|
error(pc, pos, ER_THD(pc->thd, ER_MAXVALUE_IN_VALUES_IN));
|
|
return true;
|
|
}
|
|
if (pc->add_max_value()) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_value_item_expr::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc) || expr->itemize(pc, &expr)) return true;
|
|
|
|
if (!pc->thd->lex->safe_to_cache_query) {
|
|
error(pc, pos, ER_THD(pc->thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR));
|
|
return true;
|
|
}
|
|
if (pc->add_column_list_value(pc->thd, expr)) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_value_item_list_paren::contextualize(Partition_parse_context *pc) {
|
|
pc->part_info->print_debug("( part_value_item_list_paren", NULL);
|
|
/* Initialisation code needed for each list of value expressions */
|
|
if (!(pc->part_info->part_type == partition_type::LIST &&
|
|
pc->part_info->num_columns == 1U) &&
|
|
pc->init_column_part()) {
|
|
return true;
|
|
}
|
|
|
|
for (auto value : *values) {
|
|
if (value->contextualize(pc)) return true;
|
|
}
|
|
|
|
pc->part_info->print_debug(") part_value_item_list_paren", NULL);
|
|
if (pc->part_info->num_columns == 0)
|
|
pc->part_info->num_columns = pc->curr_list_object;
|
|
if (pc->part_info->num_columns != pc->curr_list_object) {
|
|
/*
|
|
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.
|
|
*/
|
|
pc->part_info->print_debug("Kilroy I", NULL);
|
|
error(pc, paren_pos, ER_THD(pc->thd, ER_PARTITION_COLUMN_LIST_ERROR));
|
|
return true;
|
|
}
|
|
pc->curr_list_object = 0;
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_values_in_item::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc) || item->contextualize(pc)) return true;
|
|
|
|
partition_info *const part_info = pc->part_info;
|
|
part_info->print_debug("part_values_in: part_value_item_list_paren", NULL);
|
|
|
|
if (part_info->num_columns != 1U) {
|
|
if (!pc->is_add_or_reorganize_partition || part_info->num_columns == 0 ||
|
|
part_info->num_columns > MAX_REF_PARTS) {
|
|
part_info->print_debug("Kilroy III", NULL);
|
|
error(pc, pos, ER_THD(pc->thd, ER_PARTITION_COLUMN_LIST_ERROR));
|
|
return true;
|
|
}
|
|
/*
|
|
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 [COLUMNS] partitions.
|
|
*/
|
|
if (pc->reorganize_into_single_field_col_val()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_values_in_list::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc)) return true;
|
|
|
|
for (auto item : *list) {
|
|
if (item->contextualize(pc)) return true;
|
|
}
|
|
|
|
if (pc->part_info->num_columns < 2U) {
|
|
error(pc, pos, ER_THD(pc->thd, ER_ROW_SINGLE_PARTITION_FIELD_ERROR));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_definition::contextualize(Partition_parse_context *pc) {
|
|
DBUG_ASSERT(pc->current_partition == NULL && pc->curr_part_elem == NULL);
|
|
|
|
if (super::contextualize(pc)) return true;
|
|
|
|
partition_info *const part_info = pc->part_info;
|
|
|
|
auto *const curr_part = new (pc->thd->mem_root) partition_element();
|
|
if (curr_part == NULL) return true;
|
|
|
|
Partition_parse_context ppc(pc->thd, part_info, curr_part, curr_part,
|
|
pc->is_add_or_reorganize_partition);
|
|
|
|
if (!curr_part || part_info->partitions.push_back(curr_part)) return true;
|
|
curr_part->part_state = PART_NORMAL;
|
|
part_info->use_default_partitions = false;
|
|
part_info->use_default_num_partitions = false;
|
|
|
|
if (check_string_char_length(to_lex_cstring(name), "", NAME_CHAR_LEN,
|
|
system_charset_info, true)) {
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), name.str);
|
|
return true;
|
|
}
|
|
|
|
curr_part->partition_name = name.str;
|
|
|
|
switch (type) {
|
|
case partition_type::HASH: {
|
|
if (part_info->part_type == partition_type::NONE)
|
|
part_info->part_type = partition_type::HASH;
|
|
else if (part_info->part_type == partition_type::RANGE) {
|
|
errorf(&ppc, pos, ER_THD(pc->thd, ER_PARTITION_REQUIRES_VALUES_ERROR),
|
|
"RANGE", "LESS THAN");
|
|
return true;
|
|
} else if (part_info->part_type == partition_type::LIST) {
|
|
errorf(&ppc, pos, ER_THD(pc->thd, ER_PARTITION_REQUIRES_VALUES_ERROR),
|
|
"LIST", "IN");
|
|
return true;
|
|
}
|
|
} break;
|
|
case partition_type::RANGE: {
|
|
if (part_info->part_type == partition_type::NONE)
|
|
part_info->part_type = partition_type::RANGE;
|
|
else if (part_info->part_type != partition_type::RANGE) {
|
|
my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN");
|
|
return true;
|
|
}
|
|
|
|
if (opt_part_values == NULL) // MAX_VALUE_SYM
|
|
{
|
|
if (part_info->num_columns && part_info->num_columns != 1U) {
|
|
part_info->print_debug("Kilroy II", NULL);
|
|
error(&ppc, values_pos,
|
|
ER_THD(pc->thd, ER_PARTITION_COLUMN_LIST_ERROR));
|
|
return true;
|
|
} else
|
|
part_info->num_columns = 1U;
|
|
if (ppc.init_column_part() || ppc.add_max_value()) return true;
|
|
} else if (opt_part_values->contextualize(&ppc))
|
|
return true;
|
|
} break;
|
|
case partition_type::LIST: {
|
|
if (part_info->part_type == partition_type::NONE)
|
|
part_info->part_type = partition_type::LIST;
|
|
else if (part_info->part_type != partition_type::LIST) {
|
|
my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), "LIST", "IN");
|
|
return true;
|
|
}
|
|
|
|
if (opt_part_values->contextualize(&ppc)) return true;
|
|
} break;
|
|
default:
|
|
DBUG_ASSERT(false);
|
|
error(&ppc, pos, ER_THD(pc->thd, ER_UNKNOWN_ERROR));
|
|
return true;
|
|
}
|
|
|
|
if (opt_part_options != NULL) {
|
|
for (auto option : *opt_part_options) {
|
|
if (option->contextualize(&ppc)) return true;
|
|
}
|
|
if (curr_part->engine_type != NULL)
|
|
part_info->default_engine_type = curr_part->engine_type;
|
|
}
|
|
|
|
if (opt_sub_partitions == NULL) {
|
|
if (part_info->num_subparts != 0 && !part_info->use_default_subpartitions) {
|
|
/*
|
|
We come here when we have defined subpartitions on the first
|
|
partition but not on all the subsequent partitions.
|
|
*/
|
|
error(&ppc, sub_partitions_pos,
|
|
ER_THD(pc->thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR));
|
|
return true;
|
|
}
|
|
} else {
|
|
for (auto subpartition : *opt_sub_partitions) {
|
|
if (subpartition->contextualize(&ppc)) return true;
|
|
}
|
|
|
|
if (part_info->num_subparts != 0) {
|
|
if (part_info->num_subparts != ppc.count_curr_subparts) {
|
|
error(&ppc, sub_partitions_pos,
|
|
ER_THD(pc->thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR));
|
|
return true;
|
|
}
|
|
} else if (ppc.count_curr_subparts > 0) {
|
|
if (part_info->partitions.elements > 1) {
|
|
error(&ppc, sub_partitions_pos,
|
|
ER_THD(pc->thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR));
|
|
return true;
|
|
}
|
|
part_info->num_subparts = ppc.count_curr_subparts;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PT_sub_partition_by_hash::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc) || hash->itemize(pc, &hash)) return true;
|
|
|
|
partition_info *const part_info = pc->part_info;
|
|
|
|
part_info->subpart_type = partition_type::HASH;
|
|
part_info->linear_hash_ind = is_linear;
|
|
|
|
LEX *const lex = pc->thd->lex;
|
|
if (!lex->safe_to_cache_query) {
|
|
error(pc, hash_pos, ER_THD(pc->thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR));
|
|
return true;
|
|
}
|
|
lex->safe_to_cache_query = true;
|
|
|
|
/* TODO: remove const_cast */
|
|
if (part_info->set_part_expr(const_cast<char *>(hash_pos.cpp.start), hash,
|
|
const_cast<char *>(hash_pos.cpp.end), true))
|
|
return true;
|
|
|
|
if (opt_num_subparts > 0) {
|
|
part_info->num_subparts = opt_num_subparts;
|
|
part_info->use_default_num_subpartitions = false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PT_sub_partition_by_key::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc)) return true;
|
|
|
|
partition_info *const part_info = pc->part_info;
|
|
|
|
part_info->subpart_type = partition_type::HASH;
|
|
part_info->list_of_subpart_fields = true;
|
|
|
|
part_info->linear_hash_ind = is_linear;
|
|
part_info->key_algorithm = key_algo;
|
|
|
|
if (field_names->elements > MAX_REF_PARTS) {
|
|
my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0),
|
|
"list of subpartition fields");
|
|
return true;
|
|
}
|
|
part_info->subpart_field_list = *field_names;
|
|
|
|
if (opt_num_subparts > 0) {
|
|
part_info->num_subparts = opt_num_subparts;
|
|
part_info->use_default_num_subpartitions = false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_type_def::set_part_field_list(Partition_parse_context *pc,
|
|
List<char> *list) {
|
|
if (list->elements > MAX_REF_PARTS) {
|
|
my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0),
|
|
"list of partition fields");
|
|
return true;
|
|
}
|
|
pc->part_info->num_columns = list->elements;
|
|
pc->part_info->part_field_list = *list;
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_type_def::itemize_part_expr(Partition_parse_context *pc,
|
|
const POS &pos, Item **item) {
|
|
if ((*item)->itemize(pc, item)) return true;
|
|
|
|
if (!pc->thd->lex->safe_to_cache_query) {
|
|
error(pc, pos, ER_THD(pc->thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR));
|
|
return true;
|
|
}
|
|
pc->thd->lex->safe_to_cache_query = true;
|
|
|
|
if (pc->part_info->set_part_expr(const_cast<char *>(pos.cpp.start), *item,
|
|
const_cast<char *>(pos.cpp.end), false))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_type_def_key::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc)) return true;
|
|
|
|
pc->part_info->part_type = partition_type::HASH;
|
|
pc->part_info->column_list = false;
|
|
pc->part_info->key_algorithm = key_algo;
|
|
pc->part_info->linear_hash_ind = is_linear;
|
|
pc->part_info->list_of_part_fields = true;
|
|
|
|
if (opt_columns != NULL && set_part_field_list(pc, opt_columns)) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_type_def_hash::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc) || itemize_part_expr(pc, expr_pos, &expr))
|
|
return true;
|
|
|
|
pc->part_info->part_type = partition_type::HASH;
|
|
pc->part_info->column_list = false;
|
|
pc->part_info->linear_hash_ind = is_linear;
|
|
pc->part_info->num_columns = 1;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_type_def_range_expr::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc) || itemize_part_expr(pc, expr_pos, &expr))
|
|
return true;
|
|
|
|
pc->part_info->part_type = partition_type::RANGE;
|
|
pc->part_info->column_list = false;
|
|
pc->part_info->num_columns = 1;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_type_def_range_columns::contextualize(
|
|
Partition_parse_context *pc) {
|
|
if (super::contextualize(pc)) return true;
|
|
|
|
pc->part_info->part_type = partition_type::RANGE;
|
|
pc->part_info->column_list = true;
|
|
pc->part_info->list_of_part_fields = true;
|
|
|
|
return set_part_field_list(pc, columns);
|
|
}
|
|
|
|
bool PT_part_type_def_list_expr::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc) || itemize_part_expr(pc, expr_pos, &expr))
|
|
return true;
|
|
|
|
pc->part_info->part_type = partition_type::LIST;
|
|
pc->part_info->column_list = false;
|
|
pc->part_info->num_columns = 1;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PT_part_type_def_list_columns::contextualize(Partition_parse_context *pc) {
|
|
if (super::contextualize(pc)) return true;
|
|
|
|
pc->part_info->part_type = partition_type::LIST;
|
|
pc->part_info->column_list = true;
|
|
pc->part_info->list_of_part_fields = true;
|
|
|
|
return set_part_field_list(pc, columns);
|
|
}
|
|
|
|
bool PT_partition::contextualize(Parse_context *pc) {
|
|
if (super::contextualize(pc)) return true;
|
|
|
|
Partition_parse_context part_pc(pc->thd, &part_info, false);
|
|
if (part_type_def->contextualize(&part_pc)) return true;
|
|
|
|
if (opt_num_parts != 0) {
|
|
part_info.num_parts = opt_num_parts;
|
|
part_info.use_default_num_partitions = false;
|
|
}
|
|
|
|
if (opt_sub_part != NULL && opt_sub_part->contextualize(&part_pc))
|
|
return true;
|
|
|
|
if (part_defs == NULL) {
|
|
if (part_info.part_type == partition_type::RANGE) {
|
|
my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), "RANGE");
|
|
return true;
|
|
} else if (part_info.part_type == partition_type::LIST) {
|
|
my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), "LIST");
|
|
return true;
|
|
}
|
|
} else {
|
|
for (auto part_def : *part_defs) {
|
|
if (part_def->contextualize(&part_pc)) return true;
|
|
}
|
|
uint count_curr_parts = part_info.partitions.elements;
|
|
|
|
if (part_info.num_parts != 0) {
|
|
if (part_info.num_parts != count_curr_parts) {
|
|
error(&part_pc, part_defs_pos,
|
|
ER_THD(pc->thd, ER_PARTITION_WRONG_NO_PART_ERROR));
|
|
return true;
|
|
}
|
|
} else if (count_curr_parts > 0)
|
|
part_info.num_parts = count_curr_parts;
|
|
}
|
|
return false;
|
|
}
|
|
|