cdk/protocol/mysqlx/builders.h (690 lines of code) (raw):
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates.
*
* 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 designed to work 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 either included with
* the program or referenced in the documentation.
*
* Without limiting anything contained in the foregoing, this file,
* which is part of Connector/C++, is also subject to the
* Universal FOSS Exception, version 1.0, a copy of which can be found at
* https://oss.oracle.com/licenses/universal-foss-exception.
*
* 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 PROTOCOL_MYSQLX_BUILDERS_H
#define PROTOCOL_MYSQLX_BUILDERS_H
#include <mysql/cdk/protocol/mysqlx/expr.h>
namespace cdk {
namespace protocol {
namespace mysqlx {
/*
Message builders
================
A message builder class acts as expression processor and builds protobuf
message which represents given expression. Different types of protobuf
messages are built from different types of expressions - we have a
builder for Mysqlx::Datatypes::Any from Any value as well as a builder
for Mysqlx::Expr::Expr from a full Expression. However, both builders
are generated from the same templates with help of Msg_traits template
which defines specifics of each protobuf message type.
*/
/*
Protobuf message traits
-----------------------
For one of the supported protobuf message types MSG, there are
XXX_msg_traits<MSG> structures which define how to store different
kinds of values in a message of type MSG.
Scalar_msg_traits<MSG> - for a message that can store plain scalars,
defines a static function:
Mysqlx::Datatypes::Scalar& get_scalar(MSG &msg, Scalar::Type type)
which returns a reference to a sub-messgae of type
Mysqlx::Datatypes::Scalar inside msg, where scalar value can be stored.
If needed, msg is marked to indicate that it stores a scalar value.
The `type` field within returned sub-message is initialized to given type.
Arr_msg_traits<MSG> - for a message that can store sequence of values.
This structure defines a sub-message type Msg and a static function:
Msg& add_element(MSG &arr)
which adds a new element to the array and returns reference to the new
sub-message where the next value in the sequence can be stored.
Obj_msg_tratis<MSG> - for a message that can store objects, that is
key-value maps. This structure defines sub-message type Msg and
a static function:
Msg& add_key(MSG &obj, const string &key)
which adds a new key-value pair to the object and returns reference to
the sub-message where the key value can be stored.
Any_msg_traits<MSG> - for messages that can store three kinds of values:
scalars in Mysqx::Datatypes::Scalar sub-message, arrays or documents.
The structure defines sub-message types Scalar, Array and Object for
storing corresponding types of values and functions:
Scalar& get_scalar(MSG &msg) - return a sub-message where scalar value
can be stored.
Array& get_array(MSG &msg) - return a sub-message where array of values
can be stored.
Object& get_object(MSG &msg) - return a sub-message where a key-value
map can be stored.
These functions mark the message to indicate what kind of value it
stores.
*/
template <class MSG> struct Scalar_msg_traits;
template <class MSG> struct Arr_msg_traits;
template <class MSG> struct Obj_msg_traits;
template <class MSG> struct Any_msg_traits;
template <class MSG, class ARR, class OBJ>
struct Msg_traits_common
{
typedef Mysqlx::Datatypes::Scalar Scalar;
typedef ARR Array;
typedef OBJ Object;
typedef MSG Msg;
static Msg& add_element(Array &arr)
{
return *arr.add_value();
}
static Msg& add_key(Object &obj, const std::string &key)
{
typename Object::ObjectField *fld = obj.add_fld();
fld->set_key(key);
return *fld->mutable_value();
}
};
// Common message traits for messages in Mysqlx::Datatypes namespace.
struct Data_msg_traits
: public Msg_traits_common<
Mysqlx::Datatypes::Any,
Mysqlx::Datatypes::Array,
Mysqlx::Datatypes::Object
>
{
static Scalar& get_scalar(Msg &msg)
{
msg.set_type(Msg::SCALAR);
Scalar *s = msg.mutable_scalar();
return *s;
}
static Scalar& get_scalar(Scalar &msg, Scalar::Type type)
{
msg.set_type(type);
return msg;
}
static Array& get_array(Msg &msg)
{
msg.set_type(Msg::ARRAY);
return *msg.mutable_array();
}
static Object& get_object(Msg &msg)
{
msg.set_type(Msg::OBJECT);
return *msg.mutable_obj();
}
};
template<>
struct Scalar_msg_traits<Mysqlx::Datatypes::Scalar> : public Data_msg_traits
{};
template<>
struct Arr_msg_traits<Mysqlx::Datatypes::Array> : public Data_msg_traits
{};
template<>
struct Obj_msg_traits<Mysqlx::Datatypes::Object> : public Data_msg_traits
{};
template<>
struct Any_msg_traits<Mysqlx::Datatypes::Any> : public Data_msg_traits
{};
// Common message traits for messages in Mysqlx::Expr namespace.
struct Expr_msg_traits
: public Msg_traits_common<
Mysqlx::Expr::Expr,
Mysqlx::Expr::Array,
Mysqlx::Expr::Object
>
{
static Msg& get_scalar(Msg &msg)
{
msg.set_type(Msg::LITERAL);
return msg;
}
static Scalar& get_scalar(Msg &msg, Scalar::Type type)
{
get_scalar(msg);
Scalar *s = msg.mutable_literal();
s->set_type(type);
return *s;
}
static Array& get_array(Msg &msg)
{
msg.set_type(Msg::ARRAY);
return *msg.mutable_array();
}
static Object& get_object(Msg &msg)
{
msg.set_type(Msg::OBJECT);
return *msg.mutable_object();
}
};
template<>
struct Scalar_msg_traits<Mysqlx::Expr::Expr> : public Expr_msg_traits
{};
template<>
struct Arr_msg_traits<Mysqlx::Expr::Array> : public Expr_msg_traits
{};
template<>
struct Obj_msg_traits<Mysqlx::Expr::Object> : public Expr_msg_traits
{};
template<>
struct Any_msg_traits<Mysqlx::Expr::Expr> : public Expr_msg_traits
{};
// -----------------------------------------------------------------------
/*
Common base for message builders which defines m_msg member that stores
pointer to the message which is being constructed. Message builder object
is an expression processor and should be used as follows:
bld.reset(msg,conv);
expr.process(bld);
where bld is a message builder instance, msg is a protobuf message and
expr is an expression.
conv is a converter implementing Args_conv interface that is used to map named
placeholders to positional ones. Before used in a builder, such converter must
be initialized first, as is done in set_args() method.
*/
class Args_conv
{
public:
virtual unsigned conv_placeholder(const string &) = 0;
};
template <class MSG, class PRC>
struct Builder_base
: public PRC
, cdk::foundation::nocopy
{
typedef MSG Message;
typedef PRC Processor;
Message *m_msg;
Args_conv *m_args_conv;
Builder_base() : m_msg(NULL), m_args_conv(NULL)
{}
void reset(Message &msg, Args_conv *conv = NULL)
{
m_msg = &msg;
m_args_conv = conv;
}
virtual ~Builder_base() NOEXCEPT {}
};
// -----------------------------------------------------------------------
/*
Builder templates
=================
Templates defined below construct array or object builders from base
builders for plain values.
*/
/*
Array_builder<BLD, MSG> template defines a message builder which builds
an array message of type MSG from a list of expression.
Base builder of type BLD is used to build each expression in
the list. Structure Arr_msg_traits<MSG> must be defined for message type MSG
and the base builder must build sub-messages of the type defined by these
traits. Alternative traits can be specified if needed.
*/
template <class BLD, class MSG, class Traits = Arr_msg_traits<MSG> >
class Array_builder
: public Builder_base<
MSG,
cdk::api::List_processor<typename BLD::Processor>
>
{
typedef Builder_base<
MSG,
cdk::api::List_processor<typename BLD::Processor>
> Base;
using Base::m_msg;
using Base::m_args_conv;
public:
typedef typename Base::Message Message;
typedef typename Base::Processor Processor;
typedef typename Processor::Element_prc Element_prc;
protected:
Element_prc* list_el()
{
BLD *bld = get_el_builder();
bld->reset(Traits::add_element(*m_msg),
this->m_args_conv);
return bld;
}
private:
scoped_ptr<BLD> m_el_builder;
public:
BLD* get_el_builder()
{
if (!m_el_builder)
m_el_builder.reset(new BLD());
return m_el_builder.get();
}
};
/*
Any_builder_base<BLD, MSG> template defines a message builder which builds
a message that can store plain expression or array of expressions or a
document, from an Any value that can be one of these things.
Base builder of type BLD is used to build sub-messages from base (scalar)
values. Structure Any_msg_traits<MSG> must be defined for message type MSG
and the base builder must build sub-messages of the Scalar type defined by
these traits. Builders for arrays and documents are generated from the
base builder.
*/
template <class BLD, class MSG, class Traits = Obj_msg_traits<MSG> >
class Doc_builder_base;
template <class BLD, class MSG, class Traits = Any_msg_traits<MSG> >
class Any_builder_base
: public Builder_base<
MSG,
cdk::api::Any_processor<typename BLD::Processor>
>
{
typedef Builder_base<
MSG,
cdk::api::Any_processor<typename BLD::Processor>
> Base;
using Base::m_msg;
using Base::m_args_conv;
public:
typedef typename Base::Message Message;
typedef typename Base::Processor Processor;
protected:
typedef typename Traits::Object Object;
typedef typename Traits::Array Array;
typedef Doc_builder_base<BLD, Object> Obj_builder;
typedef Array_builder<Any_builder_base, Array> Arr_builder;
typedef typename Processor::Scalar_prc Scalar_prc;
typedef typename Processor::Doc_prc Doc_prc;
typedef typename Processor::List_prc List_prc;
Scalar_prc* scalar()
{
BLD *sb = get_scalar_builder();
sb->reset(Traits::get_scalar(*m_msg),
this->m_args_conv);
return sb;
}
Doc_prc* doc()
{
Obj_builder *ob = get_obj_builder();
ob->reset(Traits::get_object(*m_msg),
this->m_args_conv);
return ob;
}
List_prc* arr()
{
Arr_builder *ab = get_arr_builder();
ab->reset(Traits::get_array(*m_msg),
this->m_args_conv);
return ab;
}
private:
BLD m_scalar_builder;
Arr_builder m_arr_builder;
scoped_ptr<Obj_builder> m_obj_builder;
public:
BLD* get_scalar_builder()
{ return &m_scalar_builder; }
Arr_builder* get_arr_builder()
{ return &m_arr_builder; }
Obj_builder* get_obj_builder()
{
if (!m_obj_builder)
m_obj_builder.reset(new Obj_builder());
return m_obj_builder.get();
}
};
/*
Doc_builder_base<BLD, MSG> template defines a message builder which builds
an object message from a document expression.
Base builder of type BLD is used to build sub-messages from base (scalar)
values. Structure Obj_msg_traits<MSG> must be defined for message type MSG
and the base builder must build sub-messages of the Scalar type defined by
these traits.
In the object keys are mapped to any values which can be either base values,
arrays or sub-documents. The base builder is used only for base values -
builders for arrays and sub-documents are generated from it.
*/
template <class BLD, class MSG, class Traits> // = Obj_msg_traits<typename BLD::Message> >
class Doc_builder_base
: public Builder_base<
MSG,
cdk::api::Doc_processor< typename BLD::Processor >
>
{
typedef Builder_base<
MSG,
cdk::api::Doc_processor< typename BLD::Processor >
> Base;
using Base::m_msg;
using Base::m_args_conv;
public:
typedef typename Base::Message Message;
typedef typename Base::Processor Processor;
typedef typename Traits::Array Array;
typedef typename Traits::Msg Any;
protected:
typedef Any_builder_base<BLD, Any> Any_builder;
using typename Processor::Any_prc;
Any_prc* key_val(const string &key)
{
Any &val = Traits::add_key(*m_msg,key);
Any_builder *ab = get_any_builder();
ab->reset(val, this->m_args_conv);
return ab;
}
private:
Any_builder m_any_builder;
public:
Any_builder* get_any_builder()
{ return &m_any_builder; }
};
// ----------------------------------------------------------------------
/*
Scalar and expression builders
==============================
The following builders are defined below using the generic templates:
Scalar_builder - build Mysqlx::Datatypes::Scalar message from Any::Scalar
expression.
Any_builder - build Mysqlx::Datatypes::Any message from Any expression.
Expr_builder - build Mysqlx::Expr::Expr message from full expression of
type Expression.
Build scalar value given by Any::Processor callbacks and store it in
protobuf message of type MSG. Msg_traits<MSG>.get_scalar() defines
where the scalar is stored within the message.
*/
/*
This scalar builder is used to build either Mysqlx::Datatypes::Scalar
or Mysqlx::Expr::Expr messages. Both types of messages can store plain
scalar values.
*/
template <class MSG>
class Scalar_builder_base
: public Builder_base<MSG, api::Scalar_processor>
{
typedef Builder_base<MSG, api::Scalar_processor> Base;
using Base::m_msg;
typedef api::Scalar_processor::Octets_content_type Octets_content_type;
protected:
typedef Mysqlx::Datatypes::Scalar Scalar;
typedef Scalar::String String;
Scalar& get_scalar(Scalar::Type type)
{
return Scalar_msg_traits<MSG>::get_scalar(*m_msg, type);
}
String& get_string()
{
Scalar &sc= get_scalar(Scalar::V_STRING);
return *sc.mutable_v_string();
}
protected:
void null();
void str(bytes val);
void str(collation_id_t cs, bytes val);
void num(int64_t val);
void num(uint64_t val);
void num(float val);
void num(double val);
void yesno(bool val);
void octets(bytes val, Octets_content_type type);
};
typedef Scalar_builder_base<Mysqlx::Datatypes::Scalar> Scalar_builder;
class Any_builder :
public Any_builder_base<Scalar_builder, Mysqlx::Datatypes::Any>
{
public:
Any_builder()
{}
Any_builder(Mysqlx::Datatypes::Any &msg, Args_conv *conv)
{
reset(msg, conv);
}
};
/*
Builder for base expressions. Below it is extended to full expressions
using Any_builder_base<> template.
*/
class Expr_builder_base
: public Builder_base<Mysqlx::Expr::Expr, api::Expr_processor>
{
public:
typedef Mysqlx::Expr::Expr Expr;
typedef Any_builder_base<Expr_builder_base, Expr> Expr_builder;
using Builder_base<Mysqlx::Expr::Expr, api::Expr_processor>::m_args_conv;
protected:
Scalar_builder_base<Expr> m_scalar_builder;
scoped_ptr<Args_prc> m_args_builder;
template <class MSG>
Args_prc* get_args_builder(MSG&);
Value_prc* val() override
{
m_scalar_builder.reset(Expr_msg_traits::get_scalar(*m_msg), this->m_args_conv);
return &m_scalar_builder;
}
Mysqlx::Expr::Operator &set_op(const char *name);
Args_prc* op(const char *name) override;
Mysqlx::Expr::FunctionCall &set_call(const api::Db_obj& db_obj);
Args_prc* call(const api::Db_obj& db_obj) override;
void var(const string &name)override;
void id(const string &name, const api::Db_obj *coll) override;
void id(const string &name, const api::Db_obj *coll,
const api::Doc_path &path) override;
void id(const api::Doc_path &path) override;
void placeholder() override;
void placeholder(const string &name) override;
void placeholder(unsigned pos) override;
};
class Expr_builder
: public Any_builder_base<Expr_builder_base, Mysqlx::Expr::Expr>
{
public:
Expr_builder()
{}
Expr_builder(Mysqlx::Expr::Expr &msg, Args_conv *conv = NULL)
{
reset(msg, conv);
}
protected:
};
/*
Builder for base expressions on having statments. Below it is extended to full
having expressions using Any_builder_base<> template.
*/
class Having_builder_base
: public Expr_builder_base
{
bool m_first_id = true;
protected:
template <class MSG>
Args_prc* get_args_builder(MSG&);
Args_prc* op(const char *name) override;
Args_prc* call(const api::Db_obj& db_obj) override;
// void id(const string &name, const api::Db_obj *coll) override;
void id(const string &name, const api::Db_obj *coll,
const api::Doc_path &path) override;
void id(const api::Doc_path &path) override;
};
class Having_builder
: public Any_builder_base<Having_builder_base, Mysqlx::Expr::Expr>
{
public:
Having_builder()
{}
Having_builder(Mysqlx::Expr::Expr &msg, Args_conv *conv = NULL)
{
reset(msg, conv);
}
protected:
};
/*
Builder used to store operator or function call arguments inside
a sub-message of Expr message.
BUILDER is a builder class to be used to construct individual arguments in the
list, for example Expr_builder.
*/
template <class MSG, class BUILDER>
struct Args_builder
: public Builder_base<MSG, api::Expr_list::Processor>
{
typedef Builder_base<MSG, api::Expr_list::Processor> Base;
using Base::m_msg;
using Base::m_args_conv;
using Base::reset;
BUILDER m_arg_builder;
Args_builder(MSG &msg, Args_conv *conv = NULL)
{
reset(msg, conv);
}
using typename Builder_base<MSG, api::Expr_list::Processor>::Element_prc;
Element_prc* list_el()
{
m_arg_builder.reset(*m_msg->add_param(), this->m_args_conv);
return &m_arg_builder;
}
};
template <class MSG>
inline
Expr_builder_base::Args_prc*
Expr_builder_base::get_args_builder(MSG &msg)
{
m_args_builder.reset(new Args_builder<MSG,Expr_builder>(msg, this->m_args_conv));
return m_args_builder.get();
}
// ---------------------------------------------------------------------
/*
Scalar builder implementation
=============================
*/
template <class MSG>
inline
void Scalar_builder_base<MSG>::null()
{
get_scalar(Scalar::V_NULL);
}
template <class MSG>
inline
void Scalar_builder_base<MSG>::str(bytes val)
{
//TODO: Default charset handling - must be clarified in protocol specs.
get_string().set_value(val.begin(), val.size());
}
template <class MSG>
inline
void Scalar_builder_base<MSG>::str(collation_id_t cs, bytes val)
{
String &str= get_string();
str.set_collation(cs);
str.set_value(val.begin(), val.size());
}
template <class MSG>
inline
void Scalar_builder_base<MSG>::num(int64_t val)
{
get_scalar(Scalar::V_SINT).set_v_signed_int(val);
}
template <class MSG>
inline
void Scalar_builder_base<MSG>::num(uint64_t val)
{
get_scalar(Scalar::V_UINT).set_v_unsigned_int(val);
}
template <class MSG>
inline
void Scalar_builder_base<MSG>::num(float val)
{
get_scalar(Scalar::V_FLOAT).set_v_float(val);
}
template <class MSG>
inline
void Scalar_builder_base<MSG>::num(double val)
{
get_scalar(Scalar::V_DOUBLE).set_v_double(val);
}
template <class MSG>
inline
void Scalar_builder_base<MSG>::yesno(bool val)
{
get_scalar(Scalar::V_BOOL).set_v_bool(val);
}
template <class MSG>
inline
void Scalar_builder_base<MSG>::octets(bytes val, Octets_content_type type)
{
::Mysqlx::Datatypes::Scalar_Octets *octets =
get_scalar(Scalar::V_OCTETS).mutable_v_octets();
octets->set_value(val.begin(), val.size());
octets->set_content_type(type);
}
// ---------------------------------------------------------------------
/*
Plain expression builder implementation
=======================================
*/
inline
Mysqlx::Expr::Operator&
Expr_builder_base::set_op(const char *name)
{
m_msg->set_type(Expr::OPERATOR);
Mysqlx::Expr::Operator *op = m_msg->mutable_operator_();
op->set_name(name);
return *op;
}
inline
Expr_builder_base::Args_prc*
Expr_builder_base::op(const char *name)
{
return get_args_builder(set_op(name));
}
/*
Callback for FUNC_CALL expression type
*/
inline
Mysqlx::Expr::FunctionCall &
Expr_builder_base::set_call(const api::Db_obj& db_obj)
{
m_msg->set_type(Expr::FUNC_CALL);
Mysqlx::Expr::FunctionCall *fc = m_msg->mutable_function_call();
Mysqlx::Expr::Identifier *id = fc->mutable_name();
id->set_name(db_obj.get_name());
const string *schema = db_obj.get_schema();
if (schema)
id->set_schema_name(*schema);
return *fc;
}
inline
Expr_builder_base::Args_prc*
Expr_builder_base::call(const api::Db_obj& db_obj)
{
return get_args_builder(set_call(db_obj));
}
inline
void Expr_builder_base::var(const string &name)
{
m_msg->set_type(Mysqlx::Expr::Expr_Type_VARIABLE);
m_msg->set_variable(name);
}
/*
Callback for IDENT expression type
*/
inline
void Expr_builder_base::id(const string &name, const api::Db_obj *db_obj)
{
m_msg->set_type(Expr::IDENT);
Mysqlx::Expr::ColumnIdentifier *p_col_id = m_msg->mutable_identifier();
p_col_id->set_name(name);
if (!db_obj)
return;
p_col_id->set_table_name(db_obj->get_name());
const string *schema= db_obj->get_schema();
if (!schema)
return;
p_col_id->set_schema_name(*schema);
}
/*
Callback for IDENT expression type with only Doc_path parameter
*/
inline
void Expr_builder_base::id(const api::Doc_path &doc)
{
m_msg->set_type(Expr::IDENT);
Mysqlx::Expr::ColumnIdentifier *p_col_id = NULL;
if (doc.is_whole_document())
{
// The path "$" is represented as a member without name
if (!p_col_id)
p_col_id = m_msg->mutable_identifier();
Mysqlx::Expr::DocumentPathItem *dpi = p_col_id->add_document_path();
dpi->set_type(Mysqlx::Expr::DocumentPathItem::MEMBER);
return;
}
for (unsigned pos = 0; pos < doc.length(); ++pos)
{
if (!p_col_id)
p_col_id = m_msg->mutable_identifier();
Mysqlx::Expr::DocumentPathItem *dpi = p_col_id->add_document_path();
dpi->set_type(static_cast<Mysqlx::Expr::DocumentPathItem_Type>(doc.get_type(pos)));
switch (doc.get_type(pos))
{
case api::Doc_path::MEMBER:
if (doc.get_name(pos))
dpi->set_value(*doc.get_name(pos));
break;
case api::Doc_path::ARRAY_INDEX:
if (doc.get_index(pos))
dpi->set_index(*doc.get_index(pos));
break;
case api::Doc_path::DOUBLE_ASTERISK:
case api::Doc_path::ARRAY_INDEX_ASTERISK:
case api::Doc_path::MEMBER_ASTERISK:
break;
}
}
}
/*
Callback for IDENT expression type with name and Doc_path parameter
*/
inline
void Expr_builder_base::id(const string &name, const api::Db_obj *db_obj, const api::Doc_path &doc)
{
id(name, db_obj);
id(doc);
}
/*
Callback for PLACEHOLDER expression type
*/
inline
void Expr_builder_base::placeholder()
{
m_msg->set_type(Expr::PLACEHOLDER);
// TODO: Does protocol support anonymous placeholders?
}
/*
Callback for a named PLACEHOLDER expression type with name
*/
inline
void Expr_builder_base::placeholder(const string &name)
{
if (!m_args_conv)
throw_error("Expr builder: Calling placeholder without an Args_conv!");
/*
throw_error(
(boost::format("Calling placeholder(%s) without an Args_conv!")
% name
).str());
*/
placeholder(m_args_conv->conv_placeholder(name));
}
inline
void Expr_builder_base::placeholder(unsigned pos)
{
placeholder();
m_msg->set_position(pos);
}
/*
Having_builder implementation
*/
inline
Expr_builder_base::Args_prc*
Having_builder_base::op(const char *name)
{
return get_args_builder(set_op(name));
}
inline
Expr_builder_base::Args_prc*
Having_builder_base::call(const api::Db_obj& db_obj)
{
return get_args_builder(set_call(db_obj));
}
/*
On table mode, having is reported as alias->$.path so no need to change
anything
*/
inline
void Having_builder_base::id(const string &name, const api::Db_obj *coll,
const api::Doc_path &path)
{
Expr_builder_base::id(name, coll);
Expr_builder_base::id(path);
}
/*
On document mode, having is reported as alias.path so we need to
report to protocol as alias->$.path[1].
This means that the first path position has to be a member and the rest
of path is reported as it used to be.
*/
inline
void Having_builder_base::id(const api::Doc_path &path)
{
if (!m_first_id)
{
Expr_builder_base::id(path);
m_first_id = true;
return;
}
m_first_id = false;
if (path.is_whole_document() || path.get_type(0) != api::Doc_path::MEMBER)
throw_error("Having expression should point to fields alias");
/*
Wrapper around Doc_path object which shifts all path elements by one, so
that a path like "foo.bar.baz" becomes "bar.baz". The first path element is
returned by projection_alias().
*/
struct Doc_path_to_table : public api::Doc_path
{
Doc_path_to_table(const api::Doc_path &path)
: m_path(path)
{}
const api::Doc_path &m_path;
string projection_alias()
{
if (m_path.length() == 0 || m_path.get_type(0) != MEMBER)
throw_error("Having should refer to projection alias");
return *m_path.get_name(0);
}
bool is_whole_document() const override
{
return m_path.is_whole_document();
}
unsigned length() const override
{
auto len = m_path.length();
if (len > 0)
--len;
return len;
}
Type get_type(unsigned pos) const override
{
return m_path.get_type(pos+1);
}
const string* get_name(unsigned pos) const override
{
return m_path.get_name(pos+1);
}
const uint32_t* get_index(unsigned pos) const override
{
return m_path.get_index(pos+1);
}
};
Doc_path_to_table dp(path);
Expr_builder_base::id(dp.projection_alias(), nullptr, dp);
m_first_id = true;
}
template <class MSG>
inline
Having_builder_base::Args_prc*
Having_builder_base::get_args_builder(MSG &msg)
{
m_args_builder.reset(new Args_builder<MSG,Having_builder>(msg, this->m_args_conv));
return m_args_builder.get();
}
/*
Save Arguments and convert them to numeric order.
*/
class Placeholder_conv_imp
: public Args_conv
{
std::map<string, unsigned> m_map;
unsigned m_offset = 0;
public:
virtual ~Placeholder_conv_imp() NOEXCEPT {}
void clear()
{
m_map.clear();
m_offset = 0;
}
void set_offset(unsigned offset)
{
m_offset = offset;
}
unsigned conv_placeholder(const string &name)
{
std::map<string, unsigned>::const_iterator it = m_map.find(name);
if (it == m_map.end())
throw_error("Placeholder converter: Placeholder was not defined on args");
//throw Generic_error((boost::format("Placeholder %s was not defined on args.")
// % name).str());
return it->second;
}
void add_placeholder(const string &name)
{
std::map<string, unsigned>::const_iterator it = m_map.find(name);
if (it != m_map.end())
throw_error("Placeholder converter: Redefined placeholder");
//throw Generic_error((boost::format("Redifined placeholder %s.")
// % name).str());
assert((m_map.size()+m_offset) < std::numeric_limits<unsigned>::max());
unsigned pos = static_cast<unsigned>(m_map.size()+m_offset);
m_map[name] = pos;
}
};
}}} // cdk::protocol::mysqlx
#endif