jones-ndb/impl/include/ndb/ndb_util/NdbQueryOperation.hpp (119 lines of code) (raw):

/* Copyright (c) 2011, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef NdbQueryOperation_H #define NdbQueryOperation_H #include <ndb_types.h> // this file is currently not located in include/ndbapi // which means that we need to use <> to include instead of "" // for files located in include/ndbapi // this file is currently not located in include/ndbapi // skip includes...and require them to be included first // BUH! /* There is no way to forward declare nested class NdbDictionary::Column, * so this header file must be included.*/ // #include <NdbDictionary.hpp> // Needed to get NdbQueryOptions::ScanOrdering. // #include "NdbQueryBuilder.hpp" // #include <NdbIndexScanOperation.hpp> class Ndb; struct NdbError; class NdbParamOperand; class NdbQueryOperation; class NdbQueryOperationDef; class NdbRecAttr; class NdbTransaction; class NdbRecord; class NdbInterpretedCode; /** Opaque implementation classes*/ class NdbQueryImpl; class NdbQueryOperationImpl; /********************** OVERVIEW *********************** * * a NdbQuery is created when a NdbQueryDefinition is added to a * NdbTransaction for execution with NdbTransaction::creatQuery(). * * A NdbQuery is associated with a collection of NdbQueryOperation which * are instantiated (1::1) to reflect the NdbQueryOperationDef objects * which the NdbQueryDef consists of. The same NdbQueryDef may be used to * instantiate multiple NdbQuery obejects. * * When we have an instantiated NdbQuery, we should either bind result buffers * for retrieving entire rows from each operation, * (aka NdbRecord interface,::setResultRowRef(), ::setResultRowBuf()) * or set up retrieval operations for each attribute values, (::getValue()). * * Optionally we may also: * - Specify a scan ordering for the result set (parent only) * - Add multiple bounds to a range scan, (::setBound()) (parent only) * - Append a filter condition for each operation (aka mysqlds pushed condition) * * The NdbQuery is then executed together with other pending operations * in the next NdbTransaction::execute(). * The resultset available from a NdbQuery is natively a 'left outer join' * between the parent / child operations. If an application is not interested * in the 'outer part' of the resultset, it is its own responsibility to * filter these rows. Same is valid for any filter condition which has * not been appended to the NdbQuery. * * * We provide two different interfaces for iterating the result set: * * The 'local cursor' (NdbQueryOperation::firstResult(), ::nextResult()) * Will navigate the resultset, and fetch results, from this specific operation. * It will only be possible to navigate within those rows which depends * on the current row(s) from any ancestor of the operation. * The local cursor will only retrieve the results, or a NULL row, * resulting from its own operation. -> All child operations of a * renavigated local cursor should be navigated to ::firstResult() * to ensure that they contain results related to the renavigated parent. * * The 'global cursor' (NdbQuery::nextResult()) * Will present the result set as a scan on the root operation * with rows from its child operations appearing in an unpredictable * order. A new set of results, or NULL rows, from *all* operations * in the query tree are retrieved for each ::nextResult(). * NULL rows resulting from the outer joins may appear anywhere * inside the resultset. * * As the global cursor is implemented on top of the local cursors, it is * possible to mix the usage of global and local cursors. * ************************************************************************/ class NdbQuery { private: // Only constructable through ::buildQuery() friend class NdbQueryImpl; explicit NdbQuery(NdbQueryImpl& impl); ~NdbQuery(); public: /** Possible return values from nextResult().*/ enum NextResultOutcome{ NextResult_error = -1, NextResult_gotRow = 0, NextResult_scanComplete = 1, NextResult_bufferEmpty = 2 }; Uint32 getNoOfOperations() const; // Get a specific NdbQueryOperation by ident specified // when the NdbQueryOperationDef was created. NdbQueryOperation* getQueryOperation(const char* ident) const; NdbQueryOperation* getQueryOperation(Uint32 index) const; //NdbQueryOperation* getQueryOperation(const NdbQueryOperationDef* def) const; Uint32 getNoOfParameters() const; const NdbParamOperand* getParameter(const char* name) const; const NdbParamOperand* getParameter(Uint32 num) const; int setBound(const NdbRecord *keyRecord, const struct NdbIndexScanOperation::IndexBound *bound); /** * Get the next tuple(s) from the global cursor on the query. * * Result row / columns will be updated in the respective result handlers * as previously specified on each NdbQueryOperation either by assigning a * NdbRecord/rowBuffer or assigning NdbRecAttr to each column to be retrieved. * * @param fetchAllowed If set to false, then fetching is disabled * @param forceSend If true send will occur immediately (see @ref secAdapt) * * When fetchAllowed is set to false, * the NDB API will not request new batches from the NDB Kernel when * all received rows have been exhausted, but will instead return 2 * from nextResult(), indicating that new batches must be * requested. You must then call nextResult with fetchAllowed = true * in order to contact the NDB Kernel for more records, after taking over * locks as appropriate. * * @note: All result returned from a NdbQuery are handled as scan results * in a cursor like interface.(Even single tuple 'lookup' operations!) * - After ::execute() the current position of the result set is 'before' * the first row. There is no valid data yet in the 'RecAttr' * or NdbRecord associated with the NdbQueryOperation! * - ::nextResult() is required to retrieve the first row. This may * also cause any error / status info assicioated with the result set * iself to be returned (Like 'NoData', posible type conversion errors, * or constraint violations associated with each specific row in the * result set.) * * @return * - NextResult_error (-1): if unsuccessful,<br> * - NextResult_gotRow (0): if another tuple was received, and<br> * - NextResult_scanComplete (1): if there are no more tuples to scan. * - NextResult_bufferEmpty (2): if there are no more cached records * in NdbApi */ NextResultOutcome nextResult(bool fetchAllowed = true, bool forceSend = false); /** * Get NdbTransaction object for this query operation */ NdbTransaction* getNdbTransaction() const; /** * Close query. * * Will release most of the internally allocated objects owned * by this NdbQuery and detach itself from the NdbQueryDef * used to instantiate it. * * The application may destruct the NdbQueryDef after * ::close() has been called on *all* NdbQuery objects * instantiated from it. */ void close(bool forceSend = false); /** * @name Error Handling * @{ */ /** * Get error object with information about the latest error. * * @return An error object with information about the latest error. */ const NdbError& getNdbError() const; /** Get object implementing NdbQuery interface.*/ NdbQueryImpl& getImpl() const { return m_impl; } /** * Check if this is a pruned range scan. A range scan is pruned if the ranges * are such that only a subset of the fragments need to be scanned for * matching tuples. * * @param pruned This will be set to true if the operation is a pruned range * scan. * @return 0 if ok, -1 in case of error (call getNdbError() for details.) */ int isPrunable(bool& pruned) const; private: /** Opaque implementation NdbQuery interface.*/ NdbQueryImpl& m_impl; }; // class NdbQuery class NdbQueryOperation { private: // Only constructable through executing a NdbQueryDef friend class NdbQueryOperationImpl; explicit NdbQueryOperation(NdbQueryOperationImpl& impl); ~NdbQueryOperation(); public: // Collection of get'ers to navigate in root, parent/child hierarchy Uint32 getNoOfParentOperations() const; NdbQueryOperation* getParentOperation(Uint32 parentNo) const; Uint32 getNoOfChildOperations() const; NdbQueryOperation* getChildOperation(Uint32 childNo) const; const NdbQueryOperationDef& getQueryOperationDef() const; // Get the entire query object which this operation is part of NdbQuery& getQuery() const; /** * Defines a retrieval operation of an attribute value. * The NDB API allocate memory for the NdbRecAttr object that * will hold the returned attribute value. * * @note Note that it is the applications responsibility * to allocate enough memory for resultBuffer (if non-NULL). * The buffer resultBuffer supplied by the application must be * aligned appropriately. The buffer is used directly * (avoiding a copy penalty) only if it is aligned on a * 4-byte boundary and the attribute size in bytes * (i.e. NdbRecAttr::attrSize times NdbRecAttr::arraySize is * a multiple of 4). * * @note There are three versions of NdbQueryOperation::getValue with * slightly different parameters. * * @note This method does not fetch the attribute value from * the database! The NdbRecAttr object returned by this method * is <em>not</em> readable/printable before the * transaction has been executed with NdbTransaction::execute. * * @param anAttrName Attribute name * @param resultBuffer If this is non-NULL, then the attribute value * will be returned in this parameter.<br> * If NULL, then the attribute value will only * be stored in the returned NdbRecAttr object. * @return An NdbRecAttr object to hold the value of * the attribute, or a NULL pointer * (indicating error). */ NdbRecAttr* getValue(const char* anAttrName, char* resultBuffer = 0); NdbRecAttr* getValue(Uint32 anAttrId, char* resultBuffer = 0); NdbRecAttr* getValue(const NdbDictionary::Column* column, char* resultBuffer = 0); /** * Retrieval of entire or partial rows may also be specified. For partial * retrieval a bitmask should supplied. * * The behaviour of mixing NdbRecord retrieval style with NdbRecAttr is * is undefined - It should probably not be allowed. * * @param rec Is a pointer to a NdbRecord specifying the byte layout of the * result row. * * @resBuffer Defines a buffer sufficient large to hold the result row. * * @bufRef Refers a pointer which will be updated to refer the current result row * for this operand. * * @param result_mask defines as subset of attributes to read. * The column is only affected if 'mask[attrId >> 3] & (1<<(attrId & 7))' is set * @return 0 on success, -1 otherwise (call getNdbError() for details). */ int setResultRowBuf (const NdbRecord *rec, char* resBuffer, const unsigned char* result_mask = 0); int setResultRowRef (const NdbRecord* rec, const char* & bufRef, const unsigned char* result_mask = 0); // TODO: define how BLOB/CLOB should be retrieved. // ... Replicate ::getBlobHandle() from NdbOperation class? /** Get object implementing NdbQueryOperation interface.*/ NdbQueryOperationImpl& getImpl() const { return m_impl; } /** Define result ordering for ordered index scan. It is an error to call * this method on an operation that is not a scan, or to call it if an * ordering was already set on the operation defintion by calling * NdbQueryOperationDef::setOrdering(). * @param ordering The desired ordering of results. * @return 0 if ok, -1 in case of error (call getNdbError() for details.) */ int setOrdering(NdbQueryOptions::ScanOrdering ordering); /** Get the result ordering for this operation.*/ NdbQueryOptions::ScanOrdering getOrdering() const; /** * Set the number of fragments to be scanned in parallel. This only applies * to table scans and non-sorted scans of ordered indexes. This method is * only implemented for then root scan operation. * @return 0 if ok, -1 in case of error (call getNdbError() for details.) */ int setParallelism(Uint32 parallelism); /** * Set the number of fragments to be scanned in parallel to the maximum * possible value. This is the default for the root scan operation. * @return 0 if ok, -1 in case of error (call getNdbError() for details.) */ int setMaxParallelism(); /** * Let the system dynamically choose the number of fragments to scan in * parallel. The system will try to choose a value that gives optimal * performance. This is the default for all scans but the root scan. This * method only implemented for non-root scan operations. * @return 0 if ok, -1 in case of error (call getNdbError() for details.) */ int setAdaptiveParallelism(); /** Set the batch size (max rows per batch) for this operation. This * only applies to scan operations, as lookup operations always will * have the same batch size as its parent operation, or 1 if it is the * root operation. * @param batchSize Batch size (in number of rows). A value of 0 means * use the default batch size. * @return 0 if ok, -1 in case of error (call getNdbError() for details.) */ int setBatchSize(Uint32 batchSize); /** * Set the NdbInterpretedCode needed for defining a conditional filter * (aka: predicate) for this operation. Might be used both on scan * and lookup operations. * * Typically, one would create NdbScanFilter and NdbInterpretedCode objects * on the stack, e.g.: * NdbInterpretedCode code(table); * NdbScanFilter filter(code); * filter.begin(); * filter.ge(0, 5U); // Check if column 1 is greater of equal to 5. * filter.end(); * queryOp->setInterpretedCode(code); * * @param code The interpreted code. This object is copied internally, * meaning that 'code' may be destroyed as soon as this method returns. * @return 0 if ok, -1 in case of error (call getNdbError() for details.) */ int setInterpretedCode(const NdbInterpretedCode& code) const; /** * Local cursor: * * Navigate to first result row in this batch of results which * depends on the current row(s) from all its ancestors. * @return * - NextResult_error (-1): if unsuccessful,<br> * - NextResult_gotRow (0): if another tuple was received, and<br> * - NextResult_scanComplete (1): if there are no more tuples to scan. * - NextResult_bufferEmpty (2): if there are no more cached records * in NdbApi */ NdbQuery::NextResultOutcome firstResult(); /** * Local cursor: * * Get the next tuple(s) from this operation (and all its descendants?) * which depends on the current row(s) from all its ancestors. * * Result row / columns will be updated in the respective result handlers * as previously specified on each NdbQueryOperation either by assigning a * NdbRecord/rowBuffer or assigning NdbRecAttr to each column to be retrieved. * * If the set of cached records in the NdbApi has been consumed, more will be * requested from the datanodes only iff: * - This NdbOperation is the root of the entire pushed NdbQuery. * - 'fetchAllowed==true' * * The arguments fetchAllowed and forceSend are ignored if this operation is * not the root of the pushed query. * * @return * - NextResult_error (-1): if unsuccessful,<br> * - NextResult_gotRow (0): if another tuple was received, and<br> * - NextResult_scanComplete (1): if there are no more tuples to scan. * - NextResult_bufferEmpty (2): if there are no more cached records * in NdbApi */ NdbQuery::NextResultOutcome nextResult( bool fetchAllowed = true, bool forceSend = false); // Result handling for this NdbQueryOperation bool isRowNULL() const; // Row associated with Operation is NULL value? bool isRowChanged() const; // Prev ::nextResult() on NdbQuery retrived a new // value for this NdbQueryOperation private: // Opaque implementation class instance. NdbQueryOperationImpl& m_impl; }; // class NdbQueryOperation class NdbQueryParamValue { public: // Raw data formated according to bound Column format. // NOTE: This is how mysqld prepare parameter values! NdbQueryParamValue(const void* val, bool shrinkVarChar= false); // C-type string, terminated by '\0' NdbQueryParamValue(const char* val); // NULL-value, also used as optional end marker NdbQueryParamValue(); NdbQueryParamValue(Uint16 val); NdbQueryParamValue(Uint32 val); NdbQueryParamValue(Uint64 val); NdbQueryParamValue(double val); // More parameter C'tor to be added when required: //NdbQueryParamValue(Uint8 val); //NdbQueryParamValue(Int8 val); //NdbQueryParamValue(Int16 val); //NdbQueryParamValue(Int32 val); //NdbQueryParamValue(Int64 val); /** * Serialize value into a seuqence of words suitable to be sent to the data * nodes. * @param column Specifies the format that the value should be serialized * into. * @param dst Seralized data are appended to this. * @param len Length of serialized data (in number of bytes). * @param isNull Will be set to true iff this is a NULL value. * @return 0 if ok, otherwise an error code. */ int serializeValue(const class NdbColumnImpl& column, class Uint32Buffer& dst, Uint32& len, bool& isNull) const; private: int m_type; union { Uint8 uint8; Int8 int8; Uint16 uint16; Int16 int16; Uint32 uint32; Int32 int32; Uint64 uint64; Int64 int64; double dbl; const char* string; const void* raw; } m_value; }; #endif