491 lines
16 KiB
C++
491 lines
16 KiB
C++
/* Copyright (c) 2018, 2021, Alibaba 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/PolarDB-X Engine 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/PolarDB-X Engine.
|
|
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/outline/outline.h"
|
|
#include "sql/sql_class.h"
|
|
|
|
namespace im {
|
|
|
|
/**
|
|
Convert the outline type to category.
|
|
|
|
@param[in] type Outline type
|
|
@retval category Outline category in
|
|
outline cache container
|
|
*/
|
|
static Outline_category to_category(Outline_type type) {
|
|
DBUG_ENTER("to_category");
|
|
switch (type) {
|
|
case Outline_type::INDEX_IGNORE:
|
|
case Outline_type::INDEX_USE:
|
|
case Outline_type::INDEX_FORCE:
|
|
DBUG_RETURN(Outline_category::CATEGORY_INDEX);
|
|
case Outline_type::OPTIMIZER:
|
|
DBUG_RETURN(Outline_category::CATEGORY_OPTIMIZER);
|
|
case Outline_type::UNKNOWN:
|
|
DBUG_ASSERT(0);
|
|
}
|
|
DBUG_ASSERT(0);
|
|
DBUG_RETURN(Outline_category::CATEGORY_COUNT);
|
|
}
|
|
/**
|
|
Compare the outline according to position
|
|
|
|
@param[in] first Outline
|
|
@param[in] second Outline
|
|
|
|
@retval true Less than
|
|
@retval false More than
|
|
*/
|
|
static bool compare_outline(const Outline *first, const Outline *second) {
|
|
return first->get_pos() < second->get_pos();
|
|
}
|
|
|
|
/**
|
|
Build the index array by hint column in table.
|
|
*/
|
|
void Outline::build_index_hint() {
|
|
DBUG_ENTER("Outline::build_index_hint");
|
|
if (m_type != Outline_type::OPTIMIZER) {
|
|
split<String_outline, Index_array, true>(m_hint.c_str(),
|
|
INDEX_HINT_SEPARATOR, &m_indexes);
|
|
DBUG_ASSERT(m_indexes.size() > 0);
|
|
} else {
|
|
DBUG_ASSERT(m_indexes.size() == 0);
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
/**
|
|
Evoke index hint instance, and add into list.
|
|
|
|
@param[in] mem_root memory allocator
|
|
@param[in/out] list container
|
|
*/
|
|
void Outline::evoke_index_hint(MEM_ROOT *mem_root, List<Index_hint> *list) {
|
|
DBUG_ENTER("Outline::revoke_index_hint");
|
|
DBUG_ASSERT(m_type != Outline_type::OPTIMIZER);
|
|
|
|
for (uint i = 0; i < m_indexes.size(); i++) {
|
|
String_outline &index = m_indexes[i];
|
|
Index_hint *hint = new (mem_root) Index_hint(
|
|
strmake_root(mem_root, index.c_str(), index.length()), index.length());
|
|
hint->type = static_cast<enum index_hint_type>(m_type);
|
|
hint->clause = m_scope.mask;
|
|
list->push_back(hint);
|
|
}
|
|
inc_hit();
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
/**
|
|
Evoke optimizer hint instance, and add into list.
|
|
|
|
@param[in] mem_root memory allocator
|
|
@param[in/out] list container
|
|
*/
|
|
void Outline::evoke_optimizer_hint(MEM_ROOT *mem_root,
|
|
List<Lex_optimizer_hint> *list) {
|
|
DBUG_ENTER("Outline::revoke_optimizer_hint");
|
|
DBUG_ASSERT(m_type == Outline_type::OPTIMIZER);
|
|
|
|
size_t len = m_hint.length();
|
|
char *str = strmake_root(mem_root, m_hint.c_str(), len);
|
|
Lex_optimizer_hint *hint = new (mem_root) Lex_optimizer_hint(m_pos, str, len);
|
|
list->push_back(hint);
|
|
inc_hit();
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
/**
|
|
Insert outline into statement_outline.
|
|
|
|
@param[in] outline outline
|
|
*/
|
|
void Statement_outline::insert(Outline *outline) {
|
|
DBUG_ENTER("Statement_outline::insert");
|
|
m_outlines.push_back(outline);
|
|
std::sort(m_outlines.begin(), m_outlines.end(), compare_outline);
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
/**
|
|
Delete the outline from array by id;
|
|
|
|
@param[in] outline id
|
|
@retval affected rows
|
|
*/
|
|
size_t Statement_outline::delete_by_id(ulonglong id) {
|
|
size_t num = 0;
|
|
for (auto it = m_outlines.begin(); it != m_outlines.end();) {
|
|
Outline *outline = *it;
|
|
if (outline->get_id() == id) {
|
|
destroy_object<Outline>(outline);
|
|
it = m_outlines.erase(it);
|
|
num++;
|
|
} else {
|
|
it++;
|
|
}
|
|
}
|
|
std::sort(m_outlines.begin(), m_outlines.end(), compare_outline);
|
|
return num;
|
|
}
|
|
|
|
/* Destroy all the outlines in arraylist */
|
|
Statement_outline::~Statement_outline() {
|
|
for (Outline_vector::iterator it = m_outlines.begin(); it != m_outlines.end();
|
|
it++) {
|
|
destroy_object(*it);
|
|
}
|
|
m_outlines.clear();
|
|
}
|
|
|
|
/**
|
|
Contructor of System_outline
|
|
|
|
Init all the statement outline maps, sizes, locks.
|
|
*/
|
|
System_outline::System_outline(size_t partition)
|
|
: m_partition(partition),
|
|
m_elements(partition * static_cast<size_t>(Category::CATEGORY_COUNT)),
|
|
m_maps(key_memory_outline),
|
|
m_sizes(key_memory_outline),
|
|
m_locks(key_memory_outline) {
|
|
for (size_t i = 0; i < m_elements; i++) {
|
|
/* Init statement outline map array */
|
|
Statement_outline_map *elem_map =
|
|
allocate_outline_object<Statement_outline_map>(key_memory_outline);
|
|
m_maps.push_back(elem_map);
|
|
|
|
/* Init size array */
|
|
std::atomic_ullong *elem_atomic =
|
|
allocate_outline_object<std::atomic_ullong>();
|
|
m_sizes.push_back(elem_atomic);
|
|
|
|
/* Init locks array */
|
|
mysql_rwlock_t *lock = allocate_outline_object<mysql_rwlock_t>();
|
|
mysql_rwlock_init(key_rwlock_outline, lock);
|
|
|
|
m_locks.push_back(lock);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Destroy all the container elements.
|
|
*/
|
|
System_outline::~System_outline() {
|
|
for (size_t i = 0; i < m_elements; i++) {
|
|
for (Statement_outline_map::const_iterator it = m_maps[i]->cbegin();
|
|
it != m_maps[i]->cend(); it++) {
|
|
destroy_object<Statement_outline>(
|
|
const_cast<Statement_outline *>(it->second));
|
|
}
|
|
destroy_object(m_maps[i]);
|
|
destroy_object(m_sizes[i]);
|
|
mysql_rwlock_destroy(m_locks[i]);
|
|
destroy_object(m_locks[i]);
|
|
}
|
|
m_maps.clear();
|
|
m_sizes.clear();
|
|
m_locks.clear();
|
|
}
|
|
|
|
/**
|
|
Fill the outlines into maps, clear all if force_clean is true.
|
|
|
|
@param[in] records outline records container.
|
|
@param[in] force_clean Whether clear all maps.
|
|
*/
|
|
void System_outline::fill_records(Conf_records *records, bool force_clean) {
|
|
size_t begin;
|
|
Outline_group group;
|
|
group.classify_hint(records);
|
|
/* fill index hint */
|
|
begin = get_begin(Category::CATEGORY_INDEX);
|
|
for (size_t i = begin; i < m_partition + begin; i++) {
|
|
Lock_helper lock(m_locks[i], true);
|
|
DBUG_ASSERT(lock.effect());
|
|
if (force_clean) clear(i);
|
|
for (auto it : group.index_outlines) {
|
|
add_outline(i, it);
|
|
}
|
|
}
|
|
|
|
/* fill optimizer hint */
|
|
begin = get_begin(Category::CATEGORY_OPTIMIZER);
|
|
for (size_t i = begin; i < m_partition + begin; i++) {
|
|
Lock_helper lock(m_locks[i], true);
|
|
DBUG_ASSERT(lock.effect());
|
|
if (force_clean) clear(i);
|
|
for (auto it : group.optimizer_outlines) {
|
|
add_outline(i, it);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
Clear the statement outline objects from maps at pos.
|
|
|
|
@param[in] pos partition position
|
|
*/
|
|
void System_outline::clear(size_t pos) {
|
|
Statement_outline_map *map = m_maps[pos];
|
|
for (Statement_outline_map::const_iterator it = map->cbegin();
|
|
it != map->cend(); it++) {
|
|
destroy_object<Statement_outline>(
|
|
const_cast<Statement_outline *>(it->second));
|
|
}
|
|
map->clear();
|
|
}
|
|
|
|
/**
|
|
Aggregate the outlines that has the same outline id within
|
|
all partition maps.
|
|
|
|
@param[out] container Result container
|
|
*/
|
|
void System_outline::aggregate_outline(Outline_show_result_container *container) {
|
|
typename Outline_show_result_container::const_iterator orc_it;
|
|
for (size_t i = 0; i < m_elements; i++) {
|
|
/* Loop all the partition */
|
|
Lock_helper lock(m_locks[i], false);
|
|
DBUG_ASSERT(lock.effect());
|
|
for (auto it = m_maps[i]->begin(); it != m_maps[i]->end(); it++) {
|
|
/* Loop all the statement outline in map */
|
|
Statement_outline *stmt_outline =
|
|
const_cast<Statement_outline *>(it->second);
|
|
for (auto outline_ptr : stmt_outline->outlines()) {
|
|
/* Loop all the outline in a statement outline */
|
|
orc_it = container->find(outline_ptr->get_id());
|
|
if (orc_it == container->end()) {
|
|
/* IF not found */
|
|
Outline_show_result result(outline_ptr);
|
|
container->insert(std::pair<ulonglong, Outline_show_result>(
|
|
outline_ptr->get_id(), result));
|
|
} else {
|
|
/* IF found */
|
|
Outline_show_result &result =
|
|
const_cast<Outline_show_result &>(orc_it->second);
|
|
result.aggregate(outline_ptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
Delete the outline from all maps by id
|
|
|
|
@param[in] outline id
|
|
|
|
@retval affected rows
|
|
*/
|
|
size_t System_outline::delete_outline(ulonglong id) {
|
|
size_t num = 0;
|
|
for (size_t i = 0; i < m_elements; i++) {
|
|
Lock_helper lock(m_locks[i], true);
|
|
DBUG_ASSERT(lock.effect());
|
|
for (auto it = m_maps[i]->begin(); it != m_maps[i]->end();) {
|
|
Statement_outline *stmt_outline =
|
|
const_cast<Statement_outline *>(it->second);
|
|
num += stmt_outline->delete_by_id(id);
|
|
/* Erase the hash element if statement outline all has been deleted. */
|
|
if (stmt_outline->outlines().size() == 0) {
|
|
destroy_object<Statement_outline>(stmt_outline);
|
|
it = m_maps[i]->erase(it);
|
|
} else {
|
|
it++;
|
|
}
|
|
}
|
|
lock.unlock();
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
/**
|
|
Add the outling into m_maps at pos.
|
|
|
|
@param[in] pos partition position
|
|
@param[in] outline outline object
|
|
|
|
@retval true Failure
|
|
@retval false Success
|
|
*/
|
|
bool System_outline::add_outline(size_t pos, const Outline *outline) {
|
|
Statement_outline *stmt_outline;
|
|
DBUG_ENTER("System_outline::add_outline");
|
|
if (!(stmt_outline =
|
|
find(pos, outline->get_schema(), outline->get_digest()))) {
|
|
stmt_outline = allocate_outline_object<Statement_outline>(
|
|
outline->get_schema().c_str(), outline->get_digest().c_str());
|
|
m_maps[pos]->insert(Statement_outline_map::value_type(
|
|
Statement_outline_map::key_type(outline->get_schema(),
|
|
outline->get_digest()),
|
|
stmt_outline));
|
|
}
|
|
|
|
Outline *clone = allocate_outline_object<Outline>(*outline);
|
|
stmt_outline->insert(clone);
|
|
DBUG_RETURN(false);
|
|
}
|
|
|
|
void prepare_hints(THD *thd, TABLE_LIST *table, Outline *outline) {
|
|
if (!table->index_hints)
|
|
table->index_hints = new (thd->mem_root) List<Index_hint>();
|
|
outline->evoke_index_hint(thd->mem_root, table->index_hints);
|
|
}
|
|
|
|
void prepare_hints(THD *thd, SELECT_LEX *select_lex, Outline *outline) {
|
|
if (!select_lex->outline_optimizer_list)
|
|
select_lex->outline_optimizer_list =
|
|
new (thd->mem_root) List<Lex_optimizer_hint>();
|
|
|
|
outline->evoke_optimizer_hint(thd->mem_root,
|
|
select_lex->outline_optimizer_list);
|
|
}
|
|
|
|
/**
|
|
Iterate the next TABLE_LIST from global table list.
|
|
|
|
@param[in] table_list
|
|
@retval table_list
|
|
*/
|
|
static TABLE_LIST *iterate_next(TABLE_LIST *table_list) {
|
|
return table_list->next_global;
|
|
}
|
|
|
|
/**
|
|
Iterate the next SELECT_LEX from global SELECT LEX list.
|
|
|
|
@param[in] SELECT_LEX
|
|
@retval SELECT_LEX
|
|
*/
|
|
static SELECT_LEX *iterate_next(SELECT_LEX *select_lex) {
|
|
return select_lex->next_query_block;
|
|
}
|
|
/**
|
|
Find the outlines by schema and digest, and create hint
|
|
add into TABLE_LIST or SELECT_LEX if found.
|
|
|
|
@param[in] thd thread context
|
|
@param[in/out] T TABLE_LISTS or SELECT_LEX list
|
|
@param[in] schema schema name
|
|
@param[in] digest statement digest
|
|
*/
|
|
template <typename T, Outline_category outline_category>
|
|
void System_outline::find_and_fill_hints(THD *thd, T *lex_objects,
|
|
String_outline &schema,
|
|
String_outline &digest) {
|
|
size_t pos;
|
|
Statement_outline *stmt_outline;
|
|
T *element;
|
|
DBUG_ENTER("System_outline::find_and_fill_hints");
|
|
pos = get_pos<outline_category>(thd->thread_id());
|
|
DBUG_ASSERT(pos < m_elements);
|
|
|
|
Lock_helper lock(m_locks[pos], false);
|
|
DBUG_ASSERT(lock.effect());
|
|
if ((stmt_outline = find(pos, schema, digest))) {
|
|
const Statement_outline_vector &outlines = stmt_outline->outlines();
|
|
/* position in TABLE_LIST/SELECT_LEX global list */
|
|
uint element_nr = 1;
|
|
uint outline_nr = 0;
|
|
element = lex_objects;
|
|
uint outline_size = outlines.size();
|
|
/**
|
|
Loop the all elements list and statement outline array
|
|
that is order at the same time.
|
|
*/
|
|
while (element && (outline_nr < outline_size)) {
|
|
/* Index Hint position on table in statement */
|
|
ulonglong hint_pos = outlines[outline_nr]->get_pos();
|
|
/* Table position in table_list equal the hint position in statement */
|
|
if (element_nr == hint_pos) {
|
|
prepare_hints(thd, element, outlines[outline_nr]);
|
|
outline_nr++;
|
|
} else if (element_nr < hint_pos) {
|
|
element = iterate_next(element);
|
|
element_nr++;
|
|
} else if (element_nr > hint_pos) {
|
|
outline_nr++;
|
|
}
|
|
}
|
|
/* Accumulated the overflow hint which is not in table_list */
|
|
while (outline_nr < outline_size) {
|
|
outlines[outline_nr]->inc_overflow();
|
|
outline_nr++;
|
|
}
|
|
}
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
/* Template specialization */
|
|
template void System_outline::find_and_fill_hints<
|
|
TABLE_LIST, Outline_category::CATEGORY_INDEX>(THD *thd,
|
|
TABLE_LIST *lex_objects,
|
|
String_outline &schema,
|
|
String_outline &digest);
|
|
|
|
template void System_outline::find_and_fill_hints<
|
|
SELECT_LEX, Outline_category::CATEGORY_OPTIMIZER>(THD *thd,
|
|
SELECT_LEX *lex_objects,
|
|
String_outline &schema,
|
|
String_outline &digest);
|
|
/**
|
|
Classify the different hint type, and fill into vector.
|
|
|
|
1) One is index hint.
|
|
2) The other is optimizer hint.
|
|
|
|
@param[in] records All kinds of outline
|
|
*/
|
|
void Outline_group::classify_hint(Conf_records *records) {
|
|
DBUG_ENTER("Outline_group::classify_hint");
|
|
for (Conf_records::const_iterator it = records->cbegin();
|
|
it != records->cend(); it++) {
|
|
Outline_record *r = dynamic_cast<Outline_record *>(*it);
|
|
Outline_category c = to_category(r->type);
|
|
if (c == Outline_category::CATEGORY_INDEX)
|
|
index_outlines.push_back(allocate_outline_object<Outline>(r));
|
|
else if (c == Outline_category::CATEGORY_OPTIMIZER)
|
|
optimizer_outlines.push_back(allocate_outline_object<Outline>(r));
|
|
else {
|
|
DBUG_ASSERT(0);
|
|
}
|
|
}
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
/**
|
|
Destructor of two container
|
|
|
|
Should destroy the outlines from allocate_outline_object();
|
|
*/
|
|
Outline_group::~Outline_group() {
|
|
for (auto it : index_outlines) {
|
|
destroy_object<Outline>(it);
|
|
}
|
|
index_outlines.clear();
|
|
for (auto it : optimizer_outlines) {
|
|
destroy_object<Outline>(it);
|
|
}
|
|
optimizer_outlines.clear();
|
|
}
|
|
|
|
} /*namespace im */
|
|
|