243 lines
8.1 KiB
C++
243 lines
8.1 KiB
C++
/* Copyright (c) 2000, 2024, Oracle and/or its affiliates.
|
|
|
|
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 designed to work 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 either included with
|
|
the program or referenced in the documentation.
|
|
|
|
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/range_optimizer/index_merge.h"
|
|
|
|
#include <stdio.h>
|
|
#include <atomic>
|
|
#include <memory>
|
|
|
|
#include "m_string.h"
|
|
#include "map_helpers.h"
|
|
#include "my_base.h"
|
|
#include "my_dbug.h"
|
|
#include "my_inttypes.h"
|
|
#include "my_sys.h"
|
|
#include "mysql/service_mysql_alloc.h"
|
|
#include "mysqld_error.h"
|
|
#include "scope_guard.h"
|
|
#include "sql/handler.h"
|
|
#include "sql/iterators/row_iterator.h"
|
|
#include "sql/join_optimizer/bit_utils.h"
|
|
#include "sql/key.h"
|
|
#include "sql/psi_memory_key.h"
|
|
#include "sql/sql_base.h"
|
|
#include "sql/sql_class.h"
|
|
#include "sql/sql_const.h"
|
|
#include "sql/sql_executor.h"
|
|
#include "sql/sql_select.h"
|
|
#include "sql/sql_sort.h"
|
|
#include "sql/system_variables.h"
|
|
#include "sql/table.h"
|
|
#include "sql/thr_malloc.h"
|
|
#include "sql/uniques.h"
|
|
#include "sql_string.h"
|
|
|
|
IndexMergeIterator::IndexMergeIterator(
|
|
THD *thd, MEM_ROOT *return_mem_root, TABLE *table,
|
|
unique_ptr_destroy_only<RowIterator> pk_quick_select,
|
|
Mem_root_array<unique_ptr_destroy_only<RowIterator>> children)
|
|
: TableRowIterator(thd, table),
|
|
pk_quick_select(std::move(pk_quick_select)),
|
|
m_children(std::move(children)),
|
|
mem_root(return_mem_root) {}
|
|
|
|
IndexMergeIterator::~IndexMergeIterator() {
|
|
DBUG_TRACE;
|
|
bool disable_unique_filter = false;
|
|
for (unique_ptr_destroy_only<RowIterator> &quick : m_children) {
|
|
IndexRangeScanIterator *range =
|
|
down_cast<IndexRangeScanIterator *>(quick.get()->real_iterator());
|
|
|
|
// Normally it's disabled by dtor of IndexRangeScanIterator, but it can't be
|
|
// done without table's handler
|
|
disable_unique_filter |=
|
|
Overlaps(table()->key_info[range->index].flags, HA_MULTI_VALUED_KEY);
|
|
range->file = nullptr;
|
|
}
|
|
if (disable_unique_filter)
|
|
table()->file->ha_extra(HA_EXTRA_DISABLE_UNIQUE_RECORD_FILTER);
|
|
/* It's ok to call the next two even if they are already deinitialized */
|
|
read_record.reset();
|
|
free_io_cache(table());
|
|
}
|
|
|
|
/*
|
|
Initialize the iterator for a new scan.
|
|
|
|
Perform key scans for all used indexes (except CPK), get rowids and merge
|
|
them into an ordered non-recurrent sequence of rowids.
|
|
|
|
The merge/duplicate removal is performed using Unique class. We put all
|
|
rowids into Unique, get the sorted sequence and destroy the Unique.
|
|
|
|
If table has a clustered primary key that covers all rows (true for bdb
|
|
and innodb currently) and one of the index_merge scans is a scan on PK,
|
|
then rows that will be retrieved by PK scan are not put into Unique and
|
|
primary key scan is not performed here, it is performed later separately.
|
|
|
|
RETURN
|
|
true if error
|
|
*/
|
|
|
|
bool IndexMergeIterator::Init() {
|
|
empty_record(table());
|
|
|
|
handler *file = table()->file;
|
|
DBUG_TRACE;
|
|
|
|
/* We're going to just read rowids. */
|
|
table()->set_keyread(true);
|
|
table()->prepare_for_position();
|
|
|
|
DBUG_EXECUTE_IF("simulate_bug13919180", {
|
|
my_error(ER_UNKNOWN_ERROR, MYF(0));
|
|
return true;
|
|
});
|
|
|
|
size_t sort_buffer_size = thd()->variables.sortbuff_size;
|
|
#ifndef NDEBUG
|
|
if (DBUG_EVALUATE_IF("sortbuff_size_256", 1, 0)) sort_buffer_size = 256;
|
|
#endif /* NDEBUG */
|
|
|
|
if (unique == nullptr) {
|
|
DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", DBUG_ABORT(););
|
|
DBUG_EXECUTE_IF("only_one_Unique_may_be_created",
|
|
DBUG_SET("+d,index_merge_may_not_create_a_Unique"););
|
|
unique.reset(new (mem_root) Unique(refpos_order_cmp, (void *)file,
|
|
file->ref_length, sort_buffer_size));
|
|
if (unique == nullptr) {
|
|
return true;
|
|
}
|
|
} else {
|
|
unique->reset();
|
|
table()->unique_result.sorted_result.reset();
|
|
assert(!table()->unique_result.sorted_result_in_fsbuf);
|
|
table()->unique_result.sorted_result_in_fsbuf = false;
|
|
|
|
if (table()->unique_result.io_cache) {
|
|
close_cached_file(table()->unique_result.io_cache);
|
|
my_free(table()->unique_result.io_cache);
|
|
table()->unique_result.io_cache = nullptr;
|
|
}
|
|
}
|
|
|
|
assert(file->ref_length == unique->get_size());
|
|
assert(sort_buffer_size == unique->get_max_in_memory_size());
|
|
|
|
{
|
|
const Key_map covering_keys_save = table()->covering_keys;
|
|
const bool no_keyread_save = table()->no_keyread;
|
|
auto reset_keys =
|
|
create_scope_guard([covering_keys_save, no_keyread_save, this] {
|
|
table()->covering_keys = covering_keys_save;
|
|
table()->no_keyread = no_keyread_save;
|
|
});
|
|
table()->no_keyread = false;
|
|
for (unique_ptr_destroy_only<RowIterator> &child : m_children) {
|
|
// Init() might reset table->key_read to false. Take care to let
|
|
// it know that index merge needs to read only index entries.
|
|
IndexRangeScanIterator *range_scan_it =
|
|
down_cast<IndexRangeScanIterator *>(child->real_iterator());
|
|
table()->covering_keys.set_bit(range_scan_it->index);
|
|
if (child->Init()) return true;
|
|
// Make sure that index only access is used.
|
|
assert(table()->key_read == true);
|
|
|
|
for (;;) {
|
|
int result = child->Read();
|
|
if (result == -1) {
|
|
break; // EOF.
|
|
} else if (result != 0 || thd()->killed) {
|
|
return true;
|
|
}
|
|
|
|
/* skip row if it will be retrieved by clustered PK scan */
|
|
if (pk_quick_select && down_cast<IndexRangeScanIterator *>(
|
|
pk_quick_select.get()->real_iterator())
|
|
->row_in_ranges()) {
|
|
continue;
|
|
}
|
|
|
|
handler *child_file = range_scan_it->file;
|
|
child_file->position(table()->record[0]);
|
|
if (unique->unique_add(child_file->ref)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Ok all rowids are in the Unique now. The next call will initialize
|
|
table()->sort structure so it can be used to iterate through the rowids
|
|
sequence.
|
|
*/
|
|
if (unique->get(table())) {
|
|
return true;
|
|
}
|
|
|
|
doing_pk_scan = false;
|
|
/* index_merge currently doesn't support "using index" at all */
|
|
table()->set_keyread(false);
|
|
read_record.reset(); // Clear out any previous iterator.
|
|
read_record = init_table_iterator(thd(), table(),
|
|
/*ignore_not_found_rows=*/false,
|
|
/*count_examined_rows=*/false);
|
|
if (read_record == nullptr) return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Get next row for index_merge.
|
|
NOTES
|
|
The rows are read from
|
|
1. rowids stored in Unique.
|
|
2. IndexRangeScanIterator with clustered primary key (if any).
|
|
The sets of rows retrieved in 1) and 2) are guaranteed to be disjoint.
|
|
*/
|
|
|
|
int IndexMergeIterator::Read() {
|
|
int result;
|
|
DBUG_TRACE;
|
|
|
|
if (doing_pk_scan) return pk_quick_select->Read();
|
|
|
|
if ((result = read_record->Read()) == -1) {
|
|
// NOTE: destroying the RowIterator also clears
|
|
// table()->unique_result.io_cache if it is initialized, since it
|
|
// owns the io_cache it is reading from.
|
|
read_record.reset();
|
|
|
|
/* All rows from Unique have been retrieved, do a clustered PK scan */
|
|
if (pk_quick_select) {
|
|
doing_pk_scan = true;
|
|
if (pk_quick_select->Init()) {
|
|
return 1;
|
|
}
|
|
return pk_quick_select->Read();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|