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