/* 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(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