198 lines
7.4 KiB
C++
198 lines
7.4 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 */
|
|
|
|
#ifndef SQL_RANGE_OPTIMIZER_ROWID_ORDERED_RETRIEVAL_H_
|
|
#define SQL_RANGE_OPTIMIZER_ROWID_ORDERED_RETRIEVAL_H_
|
|
|
|
#include <assert.h>
|
|
#include <sys/types.h>
|
|
#include <vector>
|
|
|
|
#include "my_alloc.h"
|
|
#include "my_inttypes.h"
|
|
#include "priority_queue.h"
|
|
#include "sql/handler.h"
|
|
#include "sql/malloc_allocator.h"
|
|
#include "sql/range_optimizer/index_range_scan.h"
|
|
#include "sql/range_optimizer/range_optimizer.h"
|
|
#include "sql/sql_list.h"
|
|
#include "sql/table.h"
|
|
|
|
class String;
|
|
class THD;
|
|
struct MY_BITMAP;
|
|
|
|
/*
|
|
Rowid-Ordered Retrieval (ROR) index intersection quick select.
|
|
This quick select produces intersection of row sequences returned
|
|
by several IndexRangeScanIterators it "merges".
|
|
|
|
All merged IndexRangeScanIterators must return rowids in rowid order.
|
|
RowIDIntersectionIterator will return rows in rowid order, too.
|
|
|
|
All merged quick selects retrieve {rowid, covered_fields} tuples (not full
|
|
table records).
|
|
RowIDIntersectionIterator retrieves full records if it is not being used
|
|
by RowIDUnionIterator and all merged quick selects together don't
|
|
cover needed all fields.
|
|
|
|
If one of the merged quick selects is a Clustered PK range scan, it is
|
|
used only to filter rowid sequence produced by other merged quick selects.
|
|
*/
|
|
|
|
class RowIDIntersectionIterator : public RowIDCapableRowIterator {
|
|
public:
|
|
RowIDIntersectionIterator(
|
|
THD *thd, MEM_ROOT *return_mem_root, TABLE *table_arg,
|
|
bool retrieve_full_rows, bool need_rows_in_rowid_order,
|
|
Mem_root_array<unique_ptr_destroy_only<RowIterator>> children,
|
|
unique_ptr_destroy_only<RowIterator> cpk_child);
|
|
~RowIDIntersectionIterator() override;
|
|
|
|
bool Init() override;
|
|
int Read() override;
|
|
uchar *last_rowid() const override {
|
|
assert(need_rows_in_rowid_order);
|
|
return m_last_rowid;
|
|
}
|
|
|
|
private:
|
|
/*
|
|
Range quick selects this intersection consists of, not including
|
|
cpk_quick.
|
|
*/
|
|
Mem_root_array<unique_ptr_destroy_only<RowIterator>> m_children;
|
|
|
|
/*
|
|
Merged quick select that uses Clustered PK, if there is one. This quick
|
|
select is not used for row retrieval, it is used for row retrieval.
|
|
*/
|
|
unique_ptr_destroy_only<RowIterator> m_cpk_child;
|
|
|
|
/*
|
|
If true, do retrieve full table rows.
|
|
|
|
The way this works is somewhat convoluted; this is my (sgunders')
|
|
understanding as of September 2021:
|
|
|
|
For covering indexes (for some complicated value of “covering” if there are
|
|
multiple indexes involved), we always use index-only scans; otherwise,
|
|
the index range scan uses a normal scan (table->file->set_keyread(false)),
|
|
which does first a lookup into the index, and then the secondary lookup to
|
|
get the actual row.
|
|
|
|
However, for intersection scans, we don't actually need all sub-scans to
|
|
fetch the actual row; that's just a waste, especially since in most cases,
|
|
we won't need the row. So in this case, the _intention_ is that we'd always
|
|
turn on index-only scans, although it seems the code for this was never
|
|
written. The idea is that the intersection iterator then is responsible for
|
|
doing a kind of “fetch after the fact” once the intersection has yielded a
|
|
row (unless we're covering). This is done by
|
|
|
|
table->file->ha_rnd_pos(table->record[0], rowid);
|
|
|
|
although index merge uses position() instead of ha_rnd_pos().
|
|
Both seem to have the (undocumented?) side effect of actually fetching the
|
|
row even on an index-only scan. This is the reason why we need the
|
|
intersection iterator to reuse the handler reuse for MyISAM; otherwise, we'd
|
|
never actually get the row, since it's stored privately in MI_INFO and not
|
|
in the row ID.
|
|
|
|
But if there's something above the intersection scan again (which can only
|
|
be a union), it's the same game; when we find a row, it might be a duplicate
|
|
of the same row ID from another sub-iterator of the union (whether a range
|
|
scan or an intersection of range scans), and then it's not worth it to fetch
|
|
the entire row. So that's why the intersection scan needs to be told “no,
|
|
don't do ha_rnd_pos; your parent will be doing that if it's interested”. And
|
|
that is what this variable is for.
|
|
*/
|
|
bool retrieve_full_rows;
|
|
|
|
/* in top-level quick select, true if merged scans where initialized */
|
|
bool scans_inited;
|
|
|
|
const bool need_rows_in_rowid_order;
|
|
uchar *m_last_rowid;
|
|
bool inited = false;
|
|
|
|
bool init_ror_merged_scan();
|
|
};
|
|
|
|
/*
|
|
Comparison function to be used RowIDUnionIterator::queue priority
|
|
queue.
|
|
*/
|
|
struct Quick_ror_union_less {
|
|
explicit Quick_ror_union_less(const handler *file) : m_file(file) {}
|
|
bool operator()(RowIterator *a, RowIterator *b) {
|
|
RowIDCapableRowIterator *real_a =
|
|
down_cast<RowIDCapableRowIterator *>(a->real_iterator());
|
|
RowIDCapableRowIterator *real_b =
|
|
down_cast<RowIDCapableRowIterator *>(b->real_iterator());
|
|
return m_file->cmp_ref(real_a->last_rowid(), real_b->last_rowid()) > 0;
|
|
}
|
|
const handler *m_file;
|
|
};
|
|
|
|
/*
|
|
Rowid-Ordered Retrieval index union select.
|
|
This quick select produces union of row sequences returned by several
|
|
quick select it "merges".
|
|
|
|
All merged quick selects must return rowids in rowid order.
|
|
RowIDUnionIterator will return rows in rowid order, too.
|
|
|
|
All merged quick selects are set not to retrieve full table records.
|
|
ROR-union quick select always retrieves full records.
|
|
|
|
*/
|
|
|
|
class RowIDUnionIterator : public TableRowIterator {
|
|
public:
|
|
RowIDUnionIterator(
|
|
THD *thd, MEM_ROOT *return_mem_root, TABLE *table,
|
|
Mem_root_array<unique_ptr_destroy_only<RowIterator>> children);
|
|
~RowIDUnionIterator() override;
|
|
|
|
bool Init() override;
|
|
int Read() override;
|
|
|
|
private:
|
|
Mem_root_array<unique_ptr_destroy_only<RowIterator>> m_children;
|
|
|
|
Priority_queue<RowIterator *,
|
|
std::vector<RowIterator *, Malloc_allocator<RowIterator *>>,
|
|
Quick_ror_union_less>
|
|
queue; /* Priority queue for merge operation */
|
|
|
|
MEM_ROOT *mem_root; /* Memory pool for this and merged quick selects data. */
|
|
uchar *cur_rowid; /* buffer used in Read() */
|
|
uchar *prev_rowid; /* rowid of last row returned by Read() */
|
|
bool have_prev_rowid; /* true if prev_rowid has valid data */
|
|
uint rowid_length; /* table rowid length */
|
|
|
|
bool scans_inited;
|
|
bool inited = false;
|
|
};
|
|
|
|
#endif // SQL_RANGE_OPTIMIZER_ROWID_ORDERED_RETRIEVAL_H_
|