411 lines
15 KiB
C++
411 lines
15 KiB
C++
/*
|
|
Copyright (C) 2004-2008 MySQL AB, 2009 Sun Microsystems, Inc.
|
|
Use is subject to license terms.
|
|
|
|
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 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.
|
|
|
|
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 NdbIndexScanOperation_H
|
|
#define NdbIndexScanOperation_H
|
|
|
|
#include "NdbScanOperation.hpp"
|
|
|
|
/**
|
|
* @class NdbIndexScanOperation
|
|
* @brief Class of scan operations for use to scan ordered index
|
|
*/
|
|
class NdbIndexScanOperation : public NdbScanOperation {
|
|
#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL
|
|
friend class Ndb;
|
|
friend class NdbTransaction;
|
|
friend class NdbResultSet;
|
|
friend class NdbOperation;
|
|
friend class NdbScanOperation;
|
|
friend class NdbIndexStat;
|
|
#endif
|
|
|
|
public:
|
|
/**
|
|
* readTuples using ordered index
|
|
* This method is used to specify details for an old Api Index Scan
|
|
* operation.
|
|
*
|
|
* @param lock_mode Lock mode
|
|
* @param scan_flags see @ref ScanFlag
|
|
* @param parallel No of fragments to scan in parallel (0=max)
|
|
*/
|
|
virtual int readTuples(LockMode lock_mode = LM_Read,
|
|
Uint32 scan_flags = 0,
|
|
Uint32 parallel = 0,
|
|
Uint32 batch = 0);
|
|
|
|
#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL
|
|
/**
|
|
* readTuples using ordered index
|
|
*
|
|
* @param lock_mode Lock mode
|
|
* @param batch No of rows to fetch from each fragment at a time
|
|
* @param parallel No of fragments to scan in parallel
|
|
* @param order_by Order result set in index order
|
|
* @param order_desc Order descending, ignored unless order_by
|
|
* @param read_range_no Enable reading of range no using @ref get_range_no
|
|
* @returns 0 for success and -1 for failure
|
|
* @see NdbScanOperation::readTuples
|
|
*/
|
|
inline int readTuples(LockMode lock_mode,
|
|
Uint32 batch,
|
|
Uint32 parallel,
|
|
bool order_by,
|
|
bool order_desc = false,
|
|
bool read_range_no = false,
|
|
bool keyinfo = false,
|
|
bool multi_range = false) {
|
|
Uint32 scan_flags =
|
|
(SF_OrderBy & -(Int32)order_by) |
|
|
(SF_Descending & -(Int32)order_desc) |
|
|
(SF_ReadRangeNo & -(Int32)read_range_no) |
|
|
(SF_KeyInfo & -(Int32)keyinfo) |
|
|
(SF_MultiRange & -(Int32)multi_range);
|
|
|
|
return readTuples(lock_mode, scan_flags, parallel, batch);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Type of ordered index key bound. The values (0-4) will not change
|
|
* and can be used explicitly (e.g. they could be computed).
|
|
*/
|
|
enum BoundType {
|
|
BoundLE = 0, ///< lower bound
|
|
BoundLT = 1, ///< lower bound, strict
|
|
BoundGE = 2, ///< upper bound
|
|
BoundGT = 3, ///< upper bound, strict
|
|
BoundEQ = 4 ///< equality
|
|
};
|
|
|
|
/* Maximum number of ranges that can be supplied to a single
|
|
* NdbIndexScanOperation
|
|
*/
|
|
enum {
|
|
MaxRangeNo= 0xfff
|
|
};
|
|
|
|
/**
|
|
* Define bound on index key in range scan - old Api.
|
|
*
|
|
* Each index key can have lower and/or upper bound. Setting the key
|
|
* equal to a value defines both upper and lower bounds. The bounds
|
|
* can be defined in any order. Conflicting definitions is an error.
|
|
*
|
|
* For equality, it is better to use BoundEQ instead of the equivalent
|
|
* pair of BoundLE and BoundGE. This is especially true when table
|
|
* partition key is an initial part of the index key.
|
|
*
|
|
* The sets of lower and upper bounds must be on initial sequences of
|
|
* index keys. All but possibly the last bound must be non-strict.
|
|
* So "a >= 2 and b > 3" is ok but "a > 2 and b >= 3" is not.
|
|
*
|
|
* The scan may currently return tuples for which the bounds are not
|
|
* satisfied. For example, "a <= 2 and b <= 3" scans the index up to
|
|
* (a=2, b=3) but also returns any (a=1, b=4).
|
|
*
|
|
* NULL is treated like a normal value which is less than any not-NULL
|
|
* value and equal to another NULL value. To compare against NULL use
|
|
* setBound with null pointer (0).
|
|
*
|
|
* An index stores also all-NULL keys. Doing index scan with empty
|
|
* bound set returns all table tuples.
|
|
*
|
|
* @param attr Attribute name, alternatively:
|
|
* @param type Type of bound
|
|
* @param value Pointer to bound value, 0 for NULL
|
|
* @return 0 if successful otherwise -1
|
|
*
|
|
* @note See comment under equal() about data format and length.
|
|
* @note See the two parameter setBound variant for use with NdbRecord
|
|
*/
|
|
#ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED
|
|
int setBound(const char* attr, int type, const void* value, Uint32 len);
|
|
#endif
|
|
int setBound(const char* attr, int type, const void* value);
|
|
|
|
/**
|
|
* Define bound on index key in range scan using index column id.
|
|
* See the other setBound() method for details.
|
|
*/
|
|
#ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED
|
|
int setBound(Uint32 anAttrId, int type, const void* aValue, Uint32 len);
|
|
#endif
|
|
int setBound(Uint32 anAttrId, int type, const void* aValue);
|
|
|
|
/**
|
|
* This method is called to separate sets of bounds (ranges) when
|
|
* defining an Index Scan with multiple ranges
|
|
* It can only be used with scans defined using the SF_MultiRange
|
|
* scan flag.
|
|
* For NdbRecord, ranges are specified using the IndexBound structure
|
|
* and the setBound() API.
|
|
* If an index scan has more than one range then end_of_bound must be
|
|
* called after every range, including the last.
|
|
* If the SF_ReadRangeNo flag is set then the range_no supplied when
|
|
* the range is defined will be associated with each row returned from
|
|
* that range. This can be obtained by calling get_range_no().
|
|
* If SF_ReadRangeNo and SF_OrderBy flags are provided then range_no
|
|
* values must be strictly increasing (i.e. starting at zero and
|
|
* getting larger by 1 for each range specified). This is to ensure
|
|
* that rows are returned in order.
|
|
*/
|
|
int end_of_bound(Uint32 range_no= 0);
|
|
|
|
/**
|
|
* Return range number for current row, as defined in the IndexBound
|
|
* structure used when the scan was defined.
|
|
* Only available if the SF_ReadRangeNo and SF_MultiRange flags were
|
|
* set in the ScanOptions::scan_flags structure passed to scanIndex().
|
|
*/
|
|
int get_range_no();
|
|
|
|
/* Structure used to describe index scan bounds, for NdbRecord scans. */
|
|
struct IndexBound {
|
|
/* Row containing lower bound, or NULL for scan from the start. */
|
|
const char *low_key;
|
|
/* Number of columns in lower bound, for bounding by partial prefix. */
|
|
Uint32 low_key_count;
|
|
/* True for less-than-or-equal, false for strictly less-than. */
|
|
bool low_inclusive;
|
|
/* Row containing upper bound, or NULL for scan to the end. */
|
|
const char * high_key;
|
|
/* Number of columns in upper bound, for bounding by partial prefix. */
|
|
Uint32 high_key_count;
|
|
/* True for greater-than-or-equal, false for strictly greater-than. */
|
|
bool high_inclusive;
|
|
/*
|
|
Value to identify this bound, may be read with get_range_no().
|
|
Must be <= MaxRangeNo (set to zero if not using range_no).
|
|
Note that for ordered scans, the range_no must be strictly increasing
|
|
for each range, or the result set will not be sorted correctly.
|
|
*/
|
|
Uint32 range_no;
|
|
};
|
|
|
|
/**
|
|
* Add a range to an NdbRecord defined Index scan
|
|
*
|
|
* This method is called to add a range to an IndexScan operation
|
|
* which has been defined with a call to NdbTransaction::scanIndex().
|
|
* To add more than one range, the index scan operation must have been
|
|
* defined with the the SF_MultiRange flag set.
|
|
*
|
|
* Where multiple numbered ranges are defined with multiple calls to
|
|
* setBound, and the scan is ordered, the range number for each range
|
|
* must be larger than the range number for the previously defined range.
|
|
*
|
|
* When the application knows that rows in-range will only be found in
|
|
* a particular partition, a PartitionSpecification can be supplied.
|
|
* This may be used to limit the scan to a single partition, improving
|
|
* system efficiency
|
|
* The sizeOfPartInfo parameter should be set to the
|
|
* sizeof(PartitionSpec) to enable backwards compatibility.
|
|
*
|
|
* @param key_record NdbRecord structure for the key the index is
|
|
* defined on
|
|
* @param bound The bound to add
|
|
* @param partInfo Extra information to enable a reduced set of
|
|
* partitions to be scanned.
|
|
* @param sizeOfPartInfo
|
|
*
|
|
* @return 0 for Success, other for Failure.
|
|
*/
|
|
int setBound(const NdbRecord *key_record,
|
|
const IndexBound& bound);
|
|
int setBound(const NdbRecord *key_record,
|
|
const IndexBound& bound,
|
|
const Ndb::PartitionSpec* partInfo,
|
|
Uint32 sizeOfPartInfo= 0);
|
|
|
|
/**
|
|
* Return size of data, in 32-bit words, that will be send to data nodes for
|
|
* all bounds added so far with setBound().
|
|
*
|
|
* This method is only available for NdbRecord index scans.
|
|
*/
|
|
int getCurrentKeySize();
|
|
|
|
/**
|
|
* Is current scan sorted?
|
|
*/
|
|
bool getSorted() const { return m_ordered; }
|
|
|
|
/**
|
|
* Is current scan sorted descending?
|
|
*/
|
|
bool getDescending() const { return m_descending; }
|
|
|
|
private:
|
|
NdbIndexScanOperation(Ndb* aNdb);
|
|
virtual ~NdbIndexScanOperation();
|
|
|
|
int processIndexScanDefs(LockMode lm,
|
|
Uint32 scan_flags,
|
|
Uint32 parallel,
|
|
Uint32 batch);
|
|
int scanIndexImpl(const NdbRecord *key_record,
|
|
const NdbRecord *result_record,
|
|
NdbOperation::LockMode lock_mode,
|
|
const unsigned char *result_mask,
|
|
const NdbIndexScanOperation::IndexBound *bound,
|
|
const NdbScanOperation::ScanOptions *options,
|
|
Uint32 sizeOfOptions);
|
|
|
|
/* Structure used to collect information about an IndexBound
|
|
* as it is provided by the old Api setBound() calls
|
|
*/
|
|
public:
|
|
struct OldApiBoundInfo
|
|
{
|
|
Uint32 highestKey;
|
|
bool highestSoFarIsStrict;
|
|
Uint32 keysPresentBitmap;
|
|
char* key;
|
|
};
|
|
|
|
private:
|
|
struct OldApiScanRangeDefinition
|
|
{
|
|
/* OldApiBoundInfo used during definition
|
|
* IndexBound used once bound defined
|
|
* Todo : For heavy old Api use, consider splitting
|
|
* to allow NdbRecAttr use without malloc
|
|
*/
|
|
union {
|
|
struct {
|
|
OldApiBoundInfo lowBound;
|
|
OldApiBoundInfo highBound;
|
|
} oldBound;
|
|
|
|
IndexBound ib;
|
|
};
|
|
/* Space for key bounds
|
|
* Low bound from offset 0
|
|
* High bound from offset key_record->m_row_size
|
|
*/
|
|
char space[1];
|
|
};
|
|
|
|
int setBoundHelperOldApi(OldApiBoundInfo& boundInfo,
|
|
Uint32 maxKeyRecordBytes,
|
|
Uint32 index_attrId,
|
|
Uint32 valueLen,
|
|
bool inclusive,
|
|
Uint32 byteOffset,
|
|
Uint32 nullbit_byte_offset,
|
|
Uint32 nullbit_bit_in_byte,
|
|
const void *aValue);
|
|
|
|
int setBound(const NdbColumnImpl*, int type, const void* aValue);
|
|
int buildIndexBoundOldApi(int range_no);
|
|
const IndexBound* getIndexBoundFromRecAttr(NdbRecAttr* recAttr);
|
|
void releaseIndexBoundsOldApi();
|
|
int ndbrecord_insert_bound(const NdbRecord *key_record,
|
|
Uint32 column_index,
|
|
const char *row,
|
|
Uint32 bound_type,
|
|
Uint32*& firstWordOfBound);
|
|
int insert_open_bound(const NdbRecord* key_record,
|
|
Uint32*& firstWordOfBound);
|
|
|
|
virtual int equal_impl(const NdbColumnImpl*, const char*);
|
|
virtual NdbRecAttr* getValue_impl(const NdbColumnImpl*, char*);
|
|
|
|
int getDistKeyFromRange(const NdbRecord* key_record,
|
|
const NdbRecord* result_record,
|
|
const char* row,
|
|
Uint32* distKey);
|
|
void fix_get_values();
|
|
int next_result_ordered(bool fetchAllowed, bool forceSend = false);
|
|
int next_result_ordered_ndbrecord(const char * & out_row,
|
|
bool fetchAllowed,
|
|
bool forceSend);
|
|
void ordered_insert_receiver(Uint32 start, NdbReceiver *receiver);
|
|
int ordered_send_scan_wait_for_all(bool forceSend);
|
|
int send_next_scan_ordered(Uint32 idx);
|
|
int compare(Uint32 key, Uint32 cols, const NdbReceiver*, const NdbReceiver*);
|
|
Uint32 m_sort_columns;
|
|
|
|
/* Number of IndexBounds for this scan (NdbRecord only) */
|
|
Uint32 m_num_bounds;
|
|
/* Most recently added IndexBound's range number */
|
|
Uint32 m_previous_range_num;
|
|
|
|
/* Old Scan API range information
|
|
* List of RecAttr structures containing OldApiScanRangeDefinition
|
|
* structures
|
|
* currentRangeOldApi is range currently being defined (if any)
|
|
* Once defined (end_of_bound() / execute()) they are added to
|
|
* the list between first/lastRangeOldApi
|
|
*/
|
|
NdbRecAttr* firstRangeOldApi;
|
|
NdbRecAttr* lastRangeOldApi;
|
|
NdbRecAttr* currentRangeOldApi;
|
|
|
|
friend struct Ndb_free_list_t<NdbIndexScanOperation>;
|
|
|
|
private:
|
|
NdbIndexScanOperation(const NdbIndexScanOperation&); // Not impl.
|
|
NdbIndexScanOperation&operator=(const NdbIndexScanOperation&);
|
|
};
|
|
|
|
inline
|
|
int
|
|
NdbIndexScanOperation::setBound(const char* attr, int type, const void* value,
|
|
Uint32 len)
|
|
{
|
|
(void)len; // unused
|
|
return setBound(attr, type, value);
|
|
}
|
|
|
|
inline
|
|
int
|
|
NdbIndexScanOperation::setBound(Uint32 anAttrId, int type, const void* value,
|
|
Uint32 len)
|
|
{
|
|
(void)len; // unused
|
|
return setBound(anAttrId, type, value);
|
|
}
|
|
|
|
/**
|
|
* Compare keys of the current records of two NdbReceiver objects.
|
|
* @param r1 holds the first record to compare.
|
|
* @param r2 holds the second record to compare.
|
|
* @param key_record specifies the keys to compare.
|
|
* @param result_record specifies the format of full records.
|
|
* @param descending if true, descending sort order is to be used.
|
|
* @param read_range_no if true, range numbers will first be compared, and then keys if range numbers are the same for both records.
|
|
* @return -1 if r1<r2, 0 if r1=r2, 1 if r1> r2 (reversed when using 'descending').
|
|
**/
|
|
int compare_ndbrecord(const NdbReceiver *r1,
|
|
const NdbReceiver *r2,
|
|
const NdbRecord *key_record,
|
|
const NdbRecord *result_record,
|
|
bool descending,
|
|
bool read_range_no);
|
|
#endif
|