361 lines
11 KiB
C++
361 lines
11 KiB
C++
/*
|
|
Copyright (c) 2011, 2013, 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 as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "sql_bitmap.h"
|
|
|
|
class NdbTransaction;
|
|
class NdbQueryBuilder;
|
|
class NdbQueryOperand;
|
|
class NdbQueryOperationDef;
|
|
class ndb_pushed_builder_ctx;
|
|
struct NdbError;
|
|
|
|
namespace AQP{
|
|
class Join_plan;
|
|
class Table_access;
|
|
};
|
|
|
|
void ndbcluster_build_key_map(const NdbDictionary::Table* table,
|
|
const NDB_INDEX_DATA& index,
|
|
const KEY *key_def,
|
|
uint ix_map[]);
|
|
|
|
|
|
/**
|
|
* This type is used in conjunction with AQP::Join_plan and represents a set
|
|
* of the table access operations of the join plan.
|
|
* Had to subclass Bitmap as the default Bitmap<64> c'tor didn't initialize its
|
|
* map.
|
|
*/
|
|
typedef Bitmap<(MAX_TABLES > 64 ? MAX_TABLES : 64)> table_bitmap;
|
|
|
|
class ndb_table_access_map : public table_bitmap
|
|
{
|
|
public:
|
|
explicit ndb_table_access_map()
|
|
: table_bitmap(0)
|
|
{}
|
|
explicit ndb_table_access_map(uint table_no)
|
|
: table_bitmap(0)
|
|
{ set_bit(table_no);
|
|
}
|
|
|
|
void add(const ndb_table_access_map& table_map)
|
|
{ // Require const_cast as signature of class Bitmap::merge is not const correct
|
|
merge(const_cast<ndb_table_access_map&>(table_map));
|
|
}
|
|
void add(uint table_no)
|
|
{
|
|
set_bit(table_no);
|
|
}
|
|
|
|
bool contain(const ndb_table_access_map& table_map) const
|
|
{
|
|
return table_map.is_subset(*this);
|
|
}
|
|
bool contain(uint table_no) const
|
|
{
|
|
return is_set(table_no);
|
|
}
|
|
|
|
uint first_table(uint start= 0) const;
|
|
uint last_table(uint start= MAX_TABLES) const;
|
|
|
|
}; // class ndb_table_access_map
|
|
|
|
|
|
/** This class represents a prepared pushed (N-way) join operation.
|
|
*
|
|
* It might be instantiated multiple times whenever the query,
|
|
* or this subpart of the query, is being (re-)executed by
|
|
* ::createQuery() or it's wrapper method
|
|
* ha_ndbcluster::create_pushed_join().
|
|
*/
|
|
class ndb_pushed_join
|
|
{
|
|
public:
|
|
explicit ndb_pushed_join(const ndb_pushed_builder_ctx& builder_ctx,
|
|
const NdbQueryDef* query_def);
|
|
|
|
~ndb_pushed_join();
|
|
|
|
/**
|
|
* Check that this prepared pushed query matches the type
|
|
* of operation specified by the arguments.
|
|
*/
|
|
bool match_definition(int type, //NdbQueryOperationDef::Type,
|
|
const NDB_INDEX_DATA* idx) const;
|
|
|
|
/** Create an executable instance of this defined query. */
|
|
NdbQuery* make_query_instance(
|
|
NdbTransaction* trans,
|
|
const NdbQueryParamValue* keyFieldParams,
|
|
uint paramCnt) const;
|
|
|
|
/** Get the number of pushed table access operations.*/
|
|
uint get_operation_count() const
|
|
{ return m_operation_count; }
|
|
|
|
/**
|
|
* In a pushed join, fields in lookup keys and scan bounds may refer to
|
|
* result fields of table access operation that execute prior to the pushed
|
|
* join. This method returns the number of such references.
|
|
*/
|
|
uint get_field_referrences_count() const
|
|
{ return m_field_count; }
|
|
|
|
const NdbQueryDef& get_query_def() const
|
|
{ return *m_query_def; }
|
|
|
|
/** Get the table that is accessed by the i'th table access operation.*/
|
|
TABLE* get_table(uint i) const
|
|
{
|
|
DBUG_ASSERT(i < m_operation_count);
|
|
return m_tables[i];
|
|
}
|
|
|
|
/**
|
|
* This is the maximal number of fields in the key of any pushed table
|
|
* access operation.
|
|
*/
|
|
static const uint MAX_KEY_PART= MAX_KEY;
|
|
/**
|
|
* In a pushed join, fields in lookup keys and scan bounds may refer to
|
|
* result fields of table access operation that execute prior to the pushed
|
|
* join. This constant specifies the maximal number of such references for
|
|
* a query.
|
|
*/
|
|
static const uint MAX_REFERRED_FIELDS= 16;
|
|
/**
|
|
* For each table access operation in a pushed join, this is the maximal
|
|
* number of key fields that may refer to the fields of the parent operation.
|
|
*/
|
|
static const uint MAX_LINKED_KEYS= MAX_KEY;
|
|
/**
|
|
* This is the maximal number of table access operations there can be in a
|
|
* single pushed join.
|
|
*/
|
|
static const uint MAX_PUSHED_OPERATIONS= MAX_TABLES;
|
|
|
|
private:
|
|
const NdbQueryDef* const m_query_def; // Definition of pushed join query
|
|
|
|
/** This is the number of table access operations in the pushed join.*/
|
|
uint m_operation_count;
|
|
|
|
/** This is the tables that are accessed by the pushed join.*/
|
|
TABLE* m_tables[MAX_PUSHED_OPERATIONS];
|
|
|
|
/**
|
|
* This is the number of referred fields of table access operation that
|
|
* execute prior to the pushed join.
|
|
*/
|
|
const uint m_field_count;
|
|
|
|
/**
|
|
* These are the referred fields of table access operation that execute
|
|
* prior to the pushed join.
|
|
*/
|
|
Field* m_referred_fields[MAX_REFERRED_FIELDS];
|
|
}; // class ndb_pushed_join
|
|
|
|
|
|
|
|
/**
|
|
* Contains the context and helper methods used during ::make_pushed_join().
|
|
*
|
|
* Interacts with the AQP which provides interface to the query prepared by
|
|
* the mysqld optimizer.
|
|
*
|
|
* Execution plans built for pushed joins are stored inside this builder context.
|
|
*/
|
|
class ndb_pushed_builder_ctx
|
|
{
|
|
friend ndb_pushed_join::ndb_pushed_join(
|
|
const ndb_pushed_builder_ctx& builder_ctx,
|
|
const NdbQueryDef* query_def);
|
|
|
|
public:
|
|
ndb_pushed_builder_ctx(const AQP::Join_plan& plan);
|
|
~ndb_pushed_builder_ctx();
|
|
|
|
/**
|
|
* Build the pushed query identified with 'is_pushable_with_root()'.
|
|
* Returns:
|
|
* = 0: A NdbQueryDef has successfully been prepared for execution.
|
|
* > 0: Returned value is the error code.
|
|
* < 0: There is a pending NdbError to be retrieved with getNdbError()
|
|
*/
|
|
int make_pushed_join(const AQP::Table_access* join_root,
|
|
const ndb_pushed_join* &pushed_join);
|
|
|
|
const NdbError& getNdbError() const;
|
|
|
|
private:
|
|
/**
|
|
* Collect all tables which may be pushed together with 'root'.
|
|
* Returns 'true' if anything is pushable.
|
|
*/
|
|
bool is_pushable_with_root(
|
|
const AQP::Table_access* root);
|
|
|
|
bool is_pushable_as_child(
|
|
const AQP::Table_access* table);
|
|
|
|
bool is_const_item_pushable(
|
|
const Item* key_item,
|
|
const KEY_PART_INFO* key_part);
|
|
|
|
bool is_field_item_pushable(
|
|
const AQP::Table_access* table,
|
|
const Item* key_item,
|
|
const KEY_PART_INFO* key_part,
|
|
ndb_table_access_map& parents);
|
|
|
|
int optimize_query_plan();
|
|
|
|
int build_query();
|
|
|
|
void collect_key_refs(
|
|
const AQP::Table_access* table,
|
|
const Item* key_refs[]) const;
|
|
|
|
int build_key(const AQP::Table_access* table,
|
|
const NdbQueryOperand* op_key[]);
|
|
|
|
uint get_table_no(const Item* key_item) const;
|
|
|
|
private:
|
|
const AQP::Join_plan& m_plan;
|
|
const AQP::Table_access* m_join_root;
|
|
|
|
// Scope of tables covered by this pushed join
|
|
ndb_table_access_map m_join_scope;
|
|
|
|
// Scope of tables evaluated prior to 'm_join_root'
|
|
// These are effectively const or params wrt. the pushed join
|
|
ndb_table_access_map m_const_scope;
|
|
|
|
// Set of tables which 'FirstMatch' algorithm will
|
|
// skip when a FirstMatch has been found.
|
|
// Use a bitmap as there may be multiple usage of FirstMatch
|
|
// in a query, and each FirstMatch may skip multiple tables.
|
|
ndb_table_access_map m_firstmatch_skipped;
|
|
|
|
// Number of internal operations used so far (unique lookups count as two).
|
|
uint m_internal_op_count;
|
|
|
|
uint m_fld_refs;
|
|
Field* m_referred_fields[ndb_pushed_join::MAX_REFERRED_FIELDS];
|
|
|
|
// Handle to the NdbQuery factory.
|
|
// Possibly reused if multiple NdbQuery's are pushed.
|
|
NdbQueryBuilder* m_builder;
|
|
|
|
enum pushability
|
|
{
|
|
PUSHABLE_AS_PARENT= 0x01,
|
|
PUSHABLE_AS_CHILD= 0x02
|
|
} enum_pushability;
|
|
|
|
struct pushed_tables
|
|
{
|
|
pushed_tables() :
|
|
m_maybe_pushable(0),
|
|
m_common_parents(),
|
|
m_extend_parents(),
|
|
m_depend_parents(),
|
|
m_parent(MAX_TABLES),
|
|
m_ancestors(),
|
|
m_fanout(1.0),
|
|
m_child_fanout(1.0),
|
|
m_op(NULL)
|
|
{}
|
|
|
|
int m_maybe_pushable; // OR'ed bits from 'enum_pushability'
|
|
|
|
/**
|
|
* We maintain two sets of parent candidates for each table:
|
|
* - 'common' are those parents for which ::collect_key_refs()
|
|
* will find key_refs[] (possibly through the EQ-sets) such that all
|
|
* linkedValues() refer fields from the same parent.
|
|
* - 'extended' are those parents refered from some of the
|
|
* key_refs[], and having the rest of the key_refs[] available as
|
|
* 'grandparent refs'.
|
|
*/
|
|
ndb_table_access_map m_common_parents;
|
|
ndb_table_access_map m_extend_parents;
|
|
|
|
/**
|
|
* (sub)Set of a parents which *must* be available as ancestors
|
|
* due to dependencies on these parents tables.
|
|
*
|
|
* NOTE1: When the 'm_parent' has been choosen by
|
|
* ::optimize_query_plan(), any remaining grandparent
|
|
* dependencies has to be added the 'depend_parents'
|
|
* of the choosen parents such that it is taken into account
|
|
* when calculating the ancestor tables.
|
|
*
|
|
* NOTE2: These 'depend_parents' place restrictions on which of the
|
|
* 'common', 'extend' parents above we actually may use:
|
|
* As all 'depend_parents' must be joined as (grand)parents
|
|
* prior to one of the selected common/extend parents, only
|
|
* parents >= the last 'depend_parents' are the real candidates.
|
|
*
|
|
* We currently mask away the unusable common/extend parents
|
|
* as part of ::optimize_query_plan() prior to selecting
|
|
* the parent.
|
|
*/
|
|
ndb_table_access_map m_depend_parents;
|
|
|
|
/**
|
|
* The actual parent is choosen among (m_common_parents | m_extend_parents)
|
|
* by ::optimize_query_plan()
|
|
*/
|
|
uint m_parent;
|
|
|
|
/**
|
|
* All ancestors available throught the 'm_parent' chain
|
|
*/
|
|
ndb_table_access_map m_ancestors;
|
|
|
|
/**
|
|
* The fanout of this table.
|
|
*/
|
|
double m_fanout;
|
|
|
|
/**
|
|
* The (cross) product of all child fanouts.
|
|
*/
|
|
double m_child_fanout;
|
|
|
|
const NdbQueryOperationDef* m_op;
|
|
} m_tables[MAX_TABLES];
|
|
|
|
/**
|
|
* There are two different table enumerations used:
|
|
*/
|
|
struct table_remap
|
|
{
|
|
Uint16 to_external; // m_remap[] is indexed with internal table_no
|
|
Uint16 to_internal; // m_remap[] is indexed with external tablenr
|
|
} m_remap[MAX_TABLES];
|
|
|
|
}; // class ndb_pushed_builder_ctx
|
|
|
|
|