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

/* Copyright (c) 2011, 2017, 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 NdbQueryBuilder_H #define NdbQueryBuilder_H #include <stdlib.h> #include <ndb_types.h> #include <ndb_version.h> // this file is currently not located in include/ndbapi // skip includes...and require them to be included first // BUH! class Ndb; class NdbQueryDef; class NdbQueryDefImpl; class NdbQueryBuilderImpl; class NdbQueryOptionsImpl; class NdbQueryOperandImpl; class NdbQueryOperationDefImpl; /** * This is the API interface for building a (composite) query definition, * possibly existing of multiple operations linked together (aka 'joined') * * A query mainly consist of two types of objects: * - NdbQueryOperationDef defines a lookup, or scan on a single table. * - NdbQueryOperand defines a single value which may be used to * define a key, filter or bound on a NdbQueryOperationDef. * * Construction of these objects are through the NdbQueryBuilder factory. * To enforce this restriction, c'tor, d'tor operator * for the NdbQuery objects has been declared 'private'. * NdbQuery objects should not be copied - Copy constructor and assignment * operand has been private declared to enforce this restriction. * */ /** * NdbQueryOperand, a construct for specifying values which are used * to specify lookup keys, bounds or filters in the query tree. */ class NdbQueryOperand // A base class specifying a single value { public: // Column which this operand relates to const NdbDictionary::Column* getColumn() const; NdbQueryOperandImpl& getImpl() const; protected: // Enforce object creation through NdbQueryBuilder factory explicit NdbQueryOperand(NdbQueryOperandImpl& impl); ~NdbQueryOperand(); private: // Copying disallowed: NdbQueryOperand(const NdbQueryOperand& other); NdbQueryOperand& operator = (const NdbQueryOperand& other); NdbQueryOperandImpl& m_impl; }; // A NdbQueryOperand is either of these: class NdbConstOperand : public NdbQueryOperand { private: friend class NdbConstOperandImpl; explicit NdbConstOperand(NdbQueryOperandImpl& impl); ~NdbConstOperand(); }; class NdbLinkedOperand : public NdbQueryOperand { private: friend class NdbLinkedOperandImpl; explicit NdbLinkedOperand(NdbQueryOperandImpl& impl); ~NdbLinkedOperand(); }; class NdbParamOperand : public NdbQueryOperand { public: const char* getName() const; Uint32 getEnum() const; private: friend class NdbParamOperandImpl; explicit NdbParamOperand(NdbQueryOperandImpl& impl); ~NdbParamOperand(); }; /** * NdbQueryOptions used to pass options when building a NdbQueryOperationDef. * * It will normally be constructed on the stack, the required options specified * with the set'ers methods, and then supplied as an argument when creating the * NdbQueryOperationDef. */ class NdbQueryOptions { public: /** * Different match criteria may be specified for an operation. * These controls when rows are considdered equal, and a result row * is produced (or accepted). * * These are hints only. * The implementation is allowed to take a conservative approach * and produce more rows than specified by the MatchType. * However, not more rows than specified by 'MatchAll' should be produced. * As additional rows should be expected, the receiver should be prepared to * filter away unwanted rows if another MatchType than 'MatchAll' was specified. */ enum MatchType { MatchAll, // DEFAULT: Output all matches, including duplicates. // Append a single NULL complemented row for non-matching childs. MatchNonNull, // Output all matches, including duplicates. // Parents without any matches are discarded. MatchNullOnly, // Output only parent rows without any child matches. // Append a single NULL complemented row for the non_matching child MatchSingle, // Output a single row when >=1 child matches. // One of the matching child row is included in the output. Default = MatchAll }; /** Ordering of scan results when scanning ordered indexes.*/ enum ScanOrdering { /** Undefined (not yet set). */ ScanOrdering_void, /** Results will not be ordered.*/ ScanOrdering_unordered, ScanOrdering_ascending, ScanOrdering_descending }; explicit NdbQueryOptions(); ~NdbQueryOptions(); /** Define result ordering. Alternatively, ordering may be set when the * query definition has been instantiated, using * NdbQueryOperation::setOrdering(). * @param ordering The desired ordering of results. */ int setOrdering(ScanOrdering ordering); /** Define how NULL values and duplicates should be handled when equal tuples are matched. */ int setMatchType(MatchType matchType); /** * Define an (additional) parent dependency on the specified parent operation. * If linkedValues are also defined for the operation which setParent() applies to, * all implicit parents specified as part of the 'linkedValues' should be * grandparents of 'parent'. */ int setParent(const class NdbQueryOperationDef* parent); /** * 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 class NdbInterpretedCode& code); const NdbQueryOptionsImpl& getImpl() const; private: // Copying disallowed: NdbQueryOptions(const NdbQueryOptions& other); NdbQueryOptions& operator = (const NdbQueryOptions& other); NdbQueryOptionsImpl* m_pimpl; }; // class NdbQueryOptions /** * NdbQueryOperationDef defines an operation on a single NDB table */ class NdbQueryOperationDef // Base class for all operation definitions { public: /** * Different access / query operation types */ enum Type { PrimaryKeyAccess, ///< Read using pk UniqueIndexAccess, ///< Read using unique index TableScan, ///< Full table scan OrderedIndexScan ///< Ordered index scan, optionaly w/ bounds }; static const char* getTypeName(Type type); /** * Get the ordinal position of this operation within the QueryDef. */ Uint32 getOpNo() const; Uint32 getNoOfParentOperations() const; const NdbQueryOperationDef* getParentOperation(Uint32 i) const; Uint32 getNoOfChildOperations() const; const NdbQueryOperationDef* getChildOperation(Uint32 i) const; Type getType() const; /** * Get table object for this operation */ const NdbDictionary::Table* getTable() const; /** * Get index object for this operation if relevant, * return NULL else. */ const NdbDictionary::Index* getIndex() const; NdbQueryOperationDefImpl& getImpl() const; protected: // Enforce object creation through NdbQueryBuilder factory explicit NdbQueryOperationDef(NdbQueryOperationDefImpl& impl); ~NdbQueryOperationDef(); private: // Copying disallowed: NdbQueryOperationDef(const NdbQueryOperationDef& other); NdbQueryOperationDef& operator = (const NdbQueryOperationDef& other); NdbQueryOperationDefImpl& m_impl; }; // class NdbQueryOperationDef class NdbQueryLookupOperationDef : public NdbQueryOperationDef { public: private: // Enforce object creation through NdbQueryBuilder factory friend class NdbQueryLookupOperationDefImpl; explicit NdbQueryLookupOperationDef(NdbQueryOperationDefImpl& impl); ~NdbQueryLookupOperationDef(); }; // class NdbQueryLookupOperationDef class NdbQueryScanOperationDef : public NdbQueryOperationDef // Base class for scans { protected: // Enforce object creation through NdbQueryBuilder factory explicit NdbQueryScanOperationDef(NdbQueryOperationDefImpl& impl); ~NdbQueryScanOperationDef(); }; // class NdbQueryScanOperationDef class NdbQueryTableScanOperationDef : public NdbQueryScanOperationDef { private: // Enforce object creation through NdbQueryBuilder factory friend class NdbQueryTableScanOperationDefImpl; explicit NdbQueryTableScanOperationDef(NdbQueryOperationDefImpl& impl); ~NdbQueryTableScanOperationDef(); }; // class NdbQueryTableScanOperationDef class NdbQueryIndexScanOperationDef : public NdbQueryScanOperationDef { public: private: // Enforce object creation through NdbQueryBuilder factory friend class NdbQueryIndexScanOperationDefImpl; explicit NdbQueryIndexScanOperationDef(NdbQueryOperationDefImpl& impl); ~NdbQueryIndexScanOperationDef(); }; // class NdbQueryIndexScanOperationDef /** * class NdbQueryIndexBound is an argument container for defining * a NdbQueryIndexScanOperationDef. * The contents of this object is copied into the * NdbQueryIndexScanOperationDef and does not have to be * persistent after the NdbQueryBuilder::scanIndex() call */ class NdbQueryIndexBound { public: // C'tor for an equal bound: NdbQueryIndexBound(const NdbQueryOperand* const *eqKey) : m_low(eqKey), m_lowInclusive(true), m_high(eqKey), m_highInclusive(true) {}; // C'tor for a normal range including low & high limit: NdbQueryIndexBound(const NdbQueryOperand* const *low, const NdbQueryOperand* const *high) : m_low(low), m_lowInclusive(true), m_high(high), m_highInclusive(true) {}; // Complete C'tor where limits might be exluded: NdbQueryIndexBound(const NdbQueryOperand* const *low, bool lowIncl, const NdbQueryOperand* const *high, bool highIncl) : m_low(low), m_lowInclusive(lowIncl), m_high(high), m_highInclusive(highIncl) {} private: friend class NdbQueryBuilder; friend class NdbQueryIndexScanOperationDefImpl; const NdbQueryOperand* const *m_low; // 'Pointer to array of pointers', NULL terminated const bool m_lowInclusive; const NdbQueryOperand* const *m_high; // 'Pointer to array of pointers', NULL terminated const bool m_highInclusive; }; /** * * The Query builder constructs a NdbQueryDef which is a collection of * (possibly linked) NdbQueryOperationDefs * Each NdbQueryOperationDef may use NdbQueryOperands to specify keys and bounds. * * LIFETIME: * - All NdbQueryOperand and NdbQueryOperationDef objects created in the * context of a NdbQueryBuilder has a lifetime restricted by: * 1. The NdbQueryDef created by the ::prepare() methode. * 2. The NdbQueryBuilder *if* the builder is destructed before the * query was prepared. * A single NdbQueryOperand or NdbQueryOperationDef object may be * used/referrer multiple times during the build process whenever * we need a reference to the same value/node during the * build phase. * * - The NdbQueryDef produced by the ::prepare() method has a lifetime * until it is explicit released by NdbQueryDef::release() * */ class NdbQueryBuilder { friend class NdbQueryBuilderImpl; private: // Constructor is private, since application should use 'create()'. explicit NdbQueryBuilder(NdbQueryBuilderImpl& impl); // Destructor is private, since application should use 'destroy()' ~NdbQueryBuilder(); // No copying of NdbQueryBuilder objects. NdbQueryBuilder(const NdbQueryBuilder&); NdbQueryBuilder& operator=(const NdbQueryBuilder&); public: /** * Allocate an instance. * @return New instance, or NULL if allocation failed. */ static NdbQueryBuilder* create(); /** * Release this object and any resources held by it. */ void destroy(); /** * Complete building a queryTree from 'this' NdbQueryBuilder */ #ifdef NDBD_SPJ_MULTIFRAG_SCAN const NdbQueryDef* prepare(const Ndb *ndb); #else const NdbQueryDef* prepare(); #endif // NdbQueryOperand builders: // // ::constValue constructors variants, considder to added/removed variants // Typechecking is provided and will reject constValues/() which is // incompatible with the type of the column it is used against. // Some very basic typeconversion is available to match destination column // with different numeric presicion and spacepad character strings to defined length. NdbConstOperand* constValue(Int32 value); NdbConstOperand* constValue(Uint32 value); NdbConstOperand* constValue(Int64 value); NdbConstOperand* constValue(Uint64 value); NdbConstOperand* constValue(double value); NdbConstOperand* constValue(const char* value); // Null terminated char/varchar C-type string // Raw constValue data with specified length - No typeconversion performed to match destination format. // Fixed sized datatypes requires 'len' to exactly match the destination column. // Fixed sized character values should be spacepadded to required length. // Variable sized datatypes requires 'len <= max(len)'. NdbConstOperand* constValue(const void* value, Uint32 len); // ::paramValue() is a placeholder for a parameter value to be specified when // a query instance is created for execution. NdbParamOperand* paramValue(const char* name = 0); // Parameterized // ::linkedValue() defines a value available from execution of a previously defined // NdbQueryOperationDef. This NdbQueryOperationDef will become the 'parent' of the // NdbQueryOperationDef which uses this linkedValue() in any expression. NdbLinkedOperand* linkedValue(const NdbQueryOperationDef*, const char* attr); // Linked value // NdbQueryOperationDef builders: // // Common (optional) arguments: // // - 'ident' may be used to identify each NdbQueryOperationDef with a name. // This may later be used to find the corresponding NdbQueryOperation instance when // the NdbQueryDef is executed. // Each NdbQueryOperationDef will also be assigned an numeric ident (starting from 0) // as an alternative way of locating the NdbQueryOperation. const NdbQueryLookupOperationDef* readTuple( const NdbDictionary::Table*, // Primary key lookup const NdbQueryOperand* const keys[], // Terminated by NULL element const NdbQueryOptions* options = 0, const char* ident = 0); const NdbQueryLookupOperationDef* readTuple( const NdbDictionary::Index*, // Unique key lookup w/ index const NdbDictionary::Table*, const NdbQueryOperand* const keys[], // Terminated by NULL element const NdbQueryOptions* options = 0, const char* ident = 0); const NdbQueryTableScanOperationDef* scanTable( const NdbDictionary::Table*, const NdbQueryOptions* options = 0, const char* ident = 0); const NdbQueryIndexScanOperationDef* scanIndex( const NdbDictionary::Index*, const NdbDictionary::Table*, const NdbQueryIndexBound* bound = 0, const NdbQueryOptions* options = 0, const char* ident = 0); /** * @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; NdbQueryBuilderImpl& getImpl() const; private: NdbQueryBuilderImpl& m_impl; }; // class NdbQueryBuilder /** * NdbQueryDef represents a ::prepare()'d object from NdbQueryBuilder. * * The NdbQueryDef is reusable in the sense that it may be executed multiple * times. It is valid until it is explicitely released(). * * The NdbQueryDef *must* be keept alive until the last thread * which executing a query based on this NdbQueryDef has called * NdbQuery::close(). * * A NdbQueryDef is scheduled for execution by appending it to an open * transaction - optionally together with a set of parameters specifying * the actuall values required by ::execute() (ie. Lookup an bind keys). * */ class NdbQueryDef { friend class NdbQueryDefImpl; public: /** * The different types of query types supported */ enum QueryType { LookupQuery, ///< All operations are PrimaryKey- or UniqueIndexAccess SingleScanQuery, ///< Root is Table- or OrderedIndexScan, childs are 'lookup' MultiScanQuery ///< Root, and some childs are scans }; Uint32 getNoOfOperations() const; // Get a specific NdbQueryOperationDef by ident specified // when the NdbQueryOperationDef was created. const NdbQueryOperationDef* getQueryOperation(const char* ident) const; const NdbQueryOperationDef* getQueryOperation(Uint32 index) const; // A scan query may return multiple rows, and may be ::close'ed when // the client has completed access to it. bool isScanQuery() const; // Return the 'enum QueryType' as defined above. QueryType getQueryType() const; // Remove this NdbQueryDef including operation and operands it contains void destroy() const; NdbQueryDefImpl& getImpl() const; /** * Print the query in a semi human readable form */ void print() const; private: NdbQueryDefImpl& m_impl; explicit NdbQueryDef(NdbQueryDefImpl& impl); ~NdbQueryDef(); /** Private and undefined. */ NdbQueryDef(const NdbQueryDef& other); /** Private and undefined.*/ NdbQueryDef& operator = (const NdbQueryDef& other); }; #endif