BuildDarkFlame/_deps/mysql-src/include/mysqlx/devapi/document.h
2022-01-02 18:29:32 -06:00

1077 lines
19 KiB
C++

/*
* Copyright (c) 2015, 2020, 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 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.
*
* Without limiting anything contained in the foregoing, this file,
* which is part of MySQL Connector/C++, is also subject to the
* Universal FOSS Exception, version 1.0, a copy of which can be found at
* http://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 MYSQLX_DOCUMENT_H
#define MYSQLX_DOCUMENT_H
/**
@file
Declaration of DbDoc and related classes.
*/
#include "common.h"
#include <memory>
#include <stdint.h>
#include <limits>
#include <vector>
#include <assert.h>
#undef min
#undef max
namespace mysqlx {
MYSQLX_ABI_BEGIN(2,0)
class Value;
class DbDoc;
class DocResult;
class SessionSettings;
namespace internal{
class Schema_detail;
} //internal
using Field = std::string;
// Document class
// ==============
/**
Represents a collection of key-value pairs where value can be a scalar
or another document.
@note Internal document implementation is shared among DbDoc instances
and thus using DbDoc objects should be cheap.
@ingroup devapi_res
*/
class PUBLIC_API DbDoc
: public common::Printable
{
// TODO: move PUBLIC_API stuff to a detail class
class INTERNAL Impl;
DLL_WARNINGS_PUSH
std::shared_ptr<Impl> m_impl;
DLL_WARNINGS_POP
INTERNAL DbDoc(const std::shared_ptr<Impl>&);
const char* get_json() const;
public:
/**
Create null document instance.
@note Null document is different from empty document that has
no fields.
*/
DbDoc() {}
/**
Creates DbDoc instance out of given JSON string description.
*/
explicit DbDoc(const std::string&);
explicit DbDoc(std::string&&);
/**
Check if document is null
*/
bool isNull() const { return NULL == m_impl.get(); }
operator bool() const { return !isNull(); }
/**
Check if named field is a top-level field in the document.
*/
virtual bool hasField(const Field&) const;
/**
Return Value::XXX constant that identifies type of value
stored at given field.
*/
virtual int fieldType(const Field&) const;
/**
Return value of given field.
*/
virtual const Value& operator[](const Field&) const;
const Value& operator[](const char *name) const
{
return this->operator[](Field(name));
}
const Value& operator[](const mysqlx::string &name) const
{
return this->operator[](Field(name));
}
/**
Print JSON description of the document.
*/
virtual void print(std::ostream&) const;
/**
Iterator instance can iterate over (top-level) fields of a document.
A new iterator is obtained from begin() method.
@note Only one instance of an iterator can be used at a time (not
thread safe!).
*/
class Iterator;
virtual Iterator begin();
virtual Iterator end();
friend Impl;
friend DocResult;
friend Value;
friend internal::Schema_detail;
};
class PUBLIC_API DbDoc::Iterator
{
DLL_WARNINGS_PUSH
std::shared_ptr<DbDoc::Impl> m_impl;
DLL_WARNINGS_POP
bool m_end;
public:
Iterator& operator++();
bool operator==(const Iterator&) const;
bool operator!=(const Iterator &other) const { return !(*this == other); }
const Field& operator*();
friend DbDoc;
};
// Value class
// ===========
/**
%Value object can store value of scalar type, string, array or document.
Implicit conversions to and from corresponding C++ types are defined.
If conversion to wrong type is attempted, an error is thrown. If Value
object holds an array or document, then array elements or fields of
the document can be accessed using operator[]. Array values can be used
as STL containers.
Only direct conversions of stored value to the corresponding C++ type
are supported. There are no implicit number->string conversions etc.
Values of type RAW can refer to a region of memory containing raw bytes.
Such values are created from `bytes` and can by casted to `bytes` type.
@note Value object copies the values it stores. Thus, after storing value
in Value object, the original value can be destroyed without invalidating
the copy. This includes RAW Values which hold a copy of bytes.
@ingroup devapi_res
*/
class Value
: public virtual common::Printable
, protected common::Value
{
public:
using string = mysqlx::string;
/**
Possible types of values.
@sa getType()
*/
enum Type
{
VNULL, ///< Null value
UINT64, ///< Unsigned integer
INT64, ///< Signed integer
FLOAT, ///< Float number
DOUBLE, ///< Double number
BOOL, ///< Boolean
STRING, ///< String
DOCUMENT, ///< Document
RAW, ///< Raw bytes
ARRAY, ///< Array of values
};
typedef std::vector<Value>::iterator iterator;
typedef std::vector<Value>::const_iterator const_iterator;
///@name Value Constructors
///@{
Value(); ///< Constructs Null value.
Value(std::nullptr_t); ///< Constructs Null value.
Value(const mysqlx::string &str);
Value(mysqlx::string &&str);
//Value(const char16_t *str) : Value(mysqlx::string(str)) {}
Value(const std::string &str);
Value(std::string &&str);
Value(const char *str) : Value(std::string(str)) {}
template <typename C>
Value(const std::basic_string<C> &str)
: Value(mysqlx::string(str))
{}
template <typename C>
Value(const C *str)
: Value(mysqlx::string(str))
{}
Value(const bytes&);
Value(int64_t);
Value(uint64_t);
Value(float);
Value(double);
Value(bool);
Value(const DbDoc& doc);
Value(const std::initializer_list<Value> &list);
template <typename Iterator>
Value(Iterator begin_, Iterator end_);
///@}
Value(common::Value &&other);
Value(const common::Value &other);
Value(const Value&) = default;
#ifdef HAVE_MOVE_CTORS
Value(Value&&) = default;
#else
// Note move ctor implemented using move assignment defined below.
Value(Value &&other)
{
*this = std::move(other);
}
#endif
/*
Note: These templates are needed to disambiguate constructor resolution
for integer types.
*/
template <
typename T,
typename std::enable_if<std::is_signed<T>::value>::type* = nullptr
>
Value(T x)
: Value(static_cast<int64_t>(x))
{}
template <
typename T,
typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr
>
Value(T x)
: Value(static_cast<uint64_t>(x))
{}
Value& operator=(const Value&) = default;
/*
Note: Move assignment is defined explicitly to avoid problems with
virtual Printable base.
*/
Value& operator=(Value&&);
/*
Assignment is implemented in terms of constructors: first an instance
is created from the input data and then move assignment is used to place
the result into this instance.
*/
template<typename T>
Value& operator=(T&& x)
{
try {
*this = Value(std::forward<T>(x));
return *this;
}
CATCH_AND_WRAP
}
public:
/**
@name Conversion to C++ Types
Attempt to convert value of non-compatible type throws an error.
*/
//@{
operator int() const;
operator unsigned() const;
operator int64_t() const;
operator uint64_t() const;
operator float() const;
operator double() const;
explicit operator bool() const;
operator mysqlx::string() const;
explicit operator std::string() const;
template <typename C>
explicit operator std::basic_string<C>() const
{
return this->operator mysqlx::string();
}
explicit operator bytes() const;
operator DbDoc() const;
template<typename T>
T get() const;
//@}
bytes getRawBytes() const
{
try {
size_t len;
const byte *ptr = get_bytes(&len);
return { ptr, len };
}
CATCH_AND_WRAP
}
/**
Return type of the value stored in this instance (or VNULL if no
value is stored).
*/
Type getType() const;
/// Convenience method for checking if value is null.
bool isNull() const
{
return VNULL == getType();
}
/**
Check if document value contains given (top-level) field.
Throws error if this is not a document value.
*/
bool hasField(const Field&) const;
/**
If this value is not a document, throws error. Otherwise
returns value of given field of the document.
*/
const Value& operator[](const Field&) const;
const Value& operator[](const char *name) const
{ return (*this)[Field(name)]; }
const Value& operator[](const mysqlx::string &name) const
{ return (*this)[Field(name)]; }
/**
Access to elements of an array value.
If non-array value is accessed like an array, an error is thrown.
*/
//@{
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
size_t elementCount() const;
const Value& operator[](unsigned) const;
const Value& operator[](int pos) const
{
assert(pos >= 0);
return operator[]((unsigned)pos);
}
//@}
/// Print the value to a stream.
void print(std::ostream &out) const
{
switch (m_type)
{
case DOC: out << m_doc; return;
case ARR:
{
bool first = true;
out << "[";
for (auto it = m_arr->begin();it!=m_arr->end();++it)
{
if (!first)
{
out << ", ";
}
else
{
first = false;
}
switch (it->get_type())
{
case common::Value::STRING:
case common::Value::USTRING:
case common::Value::EXPR:
out << R"(")" << *it << R"(")";
break;
default:
out << *it;
break;
}
}
out << "]";
return;
}
default: common::Value::print(out); return;
}
}
protected:
enum { VAL, ARR, DOC } m_type = VAL;
void check_type(Type t) const
{
if (getType() != t)
throw Error("Invalid value type");
}
bool is_expr() const
{
return VAL == m_type && common::Value::EXPR == common::Value::get_type();
}
void set_as_expr()
{
common::Value::m_type = common::Value::EXPR;
}
/*
TODO: Instead extend common::Value with array and document types. Requires
moving DbDoc code to the common layer.
*/
typedef std::vector<Value> Array;
DLL_WARNINGS_PUSH
DbDoc m_doc;
// Note: shared with other Value instances for the same array.
std::shared_ptr<Array> m_arr;
DLL_WARNINGS_POP
public:
friend SessionSettings;
friend DbDoc;
///@cond IGNORE
friend mysqlx::string;
///@endcond IGNORE
struct INTERNAL Access;
friend Access;
};
static const Value nullvalue;
inline
Value& Value::operator=(Value &&other)
{
m_type = other.m_type;
switch (m_type)
{
case VAL:
common::Value::operator=(std::move(other));
break;
case DOC: m_doc = std::move(other.m_doc); break;
case ARR: m_arr = std::move(other.m_arr); break;
default: break;
}
return *this;
}
namespace internal {
/*
Helper class to identify usage of expressions
*/
class Expression
: public mysqlx::Value
{
Expression()
{}
template <typename V>
Expression(V&& val)
: Value(std::forward<V>(val))
{
set_as_expr();
}
template <typename V>
Expression(const V& val)
: Value(val)
{
set_as_expr();
}
friend Expression expr(std::string&& s);
friend Expression expr(const std::string& s);
};
/**
Function which indicates that a given string should be treated
as expression.
If `s` is a string value, then in contexts where values are
expected, `expr(s)` treats `s` as a DevAPI expression. For
example statement
table.select("foo > 1").execute();
returns the string `"foo 1"` for each row in the table while
table.select(expr("foo > 1")).execute();
returns true/false, depending on the value of the expression.
@ingroup devapi
*/
inline
internal::Expression expr(std::string&& e)
{
return std::forward<std::string>(e);
}
inline
internal::Expression expr(const std::string& e)
{
return e;
}
} // internal
using internal::expr;
inline
Value::Type Value::getType() const
{
switch (m_type)
{
case ARR: return ARRAY;
case DOC: return DOCUMENT;
case VAL:
switch (common::Value::get_type())
{
case common::Value::VNULL: return VNULL;
case common::Value::UINT64: return UINT64;
case common::Value::INT64: return INT64;
case common::Value::FLOAT: return FLOAT;
case common::Value::DOUBLE: return DOUBLE;
case common::Value::BOOL: return BOOL;
case common::Value::STRING: return STRING;
case common::Value::USTRING: return STRING;
case common::Value::RAW: return RAW;
case common::Value::EXPR: return STRING;
case common::Value::JSON: return DOCUMENT;
}
}
return VNULL; // quiet compiler warning
}
/*
Value type conversions
----------------------
TODO: more informative errors
*/
inline
Value::Value(const std::initializer_list<Value> &list)
: m_type(ARR)
{
try {
m_arr = std::make_shared<Array>(list);
}
CATCH_AND_WRAP
}
template <typename Iterator>
inline
Value::Value(Iterator begin_, Iterator end_)
: m_type(ARR)
{
try {
m_arr = std::make_shared<Array>(begin_, end_);
}
CATCH_AND_WRAP
}
inline
Value::Value(common::Value &&other)
try
: common::Value(std::move(other))
{}
CATCH_AND_WRAP
inline
Value::Value(const common::Value &other)
try
: common::Value(other)
{}
CATCH_AND_WRAP
inline Value::Value()
{}
inline Value::Value(std::nullptr_t)
{}
inline Value::Value(int64_t val)
try
: common::Value(val)
{}
CATCH_AND_WRAP
inline Value::Value(uint64_t val)
try
: common::Value(val)
{}
CATCH_AND_WRAP
template<>
inline
int Value::get<int>() const
{
try {
int64_t val = get_sint();
if (val > std::numeric_limits<int>::max())
throw Error("Numeric conversion overflow");
if (val < std::numeric_limits<int>::min())
throw Error("Numeric conversion overflow");
return (int)val;
}
CATCH_AND_WRAP
}
inline
Value::operator int() const
{
return get<int>();
}
template<>
inline unsigned Value::get<unsigned>() const
{
try {
uint64_t val = get_uint();
if (val > std::numeric_limits<unsigned>::max())
throw Error("Numeric conversion overflow");
return (unsigned)val;
}
CATCH_AND_WRAP
}
inline
Value::operator unsigned() const
{
return get<unsigned>();
}
template<>
inline int64_t Value::get<int64_t>() const
{
try {
return get_sint();
}
CATCH_AND_WRAP
}
inline
Value::operator int64_t() const
{
return get<int64_t>();
}
template<>
inline uint64_t Value::get<uint64_t>() const
{
try {
return get_uint();
}
CATCH_AND_WRAP
}
inline
Value::operator uint64_t() const
{
return get<uint64_t>();
}
inline Value::Value(float val)
try
: common::Value(val)
{}
CATCH_AND_WRAP
template<>
inline
float Value::get<float>() const
{
try {
return get_float();
}
CATCH_AND_WRAP
}
inline
Value::operator float() const
{
return get<float>();
}
inline Value::Value(double val)
try
: common::Value(val)
{}
CATCH_AND_WRAP
template<>
inline
double Value::get<double>() const
{
try {
return get_double();
}
CATCH_AND_WRAP
}
inline
Value::operator double() const
{
return get<double>();
}
inline Value::Value(bool val)
try
: common::Value(val)
{}
CATCH_AND_WRAP
template<>
inline
bool Value::get<bool>() const
{
try {
return get_bool();
}
CATCH_AND_WRAP
}
inline
Value::operator bool() const
{
return get<bool>();
}
inline Value::Value(const DbDoc &doc)
try
: m_type(DOC)
, m_doc(doc)
{}
CATCH_AND_WRAP
inline Value::Value(const mysqlx::string &val)
try
: common::Value(val)
{}
CATCH_AND_WRAP
inline Value::Value(mysqlx::string &&val)
try
: common::Value(std::move(val))
{}
CATCH_AND_WRAP
inline Value::Value(const std::string &val)
try
: common::Value(val)
{}
CATCH_AND_WRAP
inline Value::Value(std::string &&val)
try
: common::Value(std::move(val))
{}
CATCH_AND_WRAP
template<>
inline
std::wstring Value::get<std::wstring>() const
{
try {
return mysqlx::string(this->get_ustring());
}
CATCH_AND_WRAP
}
template<>
inline
std::string Value::get<std::string>() const
{
try {
return get_string();
}
CATCH_AND_WRAP
}
inline
Value::operator std::string() const
{
return get<std::string>();
}
template<>
inline
mysqlx::string Value::get<mysqlx::string>() const
{
try {
return this->get_ustring();
}
CATCH_AND_WRAP
}
inline
Value::operator mysqlx::string() const
{
return get<mysqlx::string>();
}
inline Value::Value(const bytes &data)
try
: common::Value(data.begin(), data.length())
{}
CATCH_AND_WRAP
template<>
inline
bytes Value::get<bytes>() const
{
return getRawBytes();
}
inline
Value::operator bytes() const
{
return get<bytes>();
}
template<>
inline
DbDoc Value::get<DbDoc>() const
{
check_type(DOCUMENT);
return m_doc;
}
inline
Value::operator DbDoc() const
{
return get<DbDoc>();
}
inline
bool Value::hasField(const Field &fld) const
{
check_type(DOCUMENT);
return m_doc.hasField(fld);
}
inline
const Value& Value::operator[](const Field &fld) const
{
check_type(DOCUMENT);
return m_doc[fld];
}
inline
int DbDoc::fieldType(const Field &fld) const
{
return (*this)[fld].getType();
}
// Array access
inline
Value::iterator Value::begin()
{
if (ARR != m_type)
throw Error("Attempt to iterate over non-array value");
return m_arr->begin();
}
inline
Value::const_iterator Value::begin() const
{
if (ARR != m_type)
throw Error("Attempt to iterate over non-array value");
return m_arr->begin();
}
inline
Value::iterator Value::end()
{
if (ARR != m_type)
throw Error("Attempt to iterate over non-array value");
return m_arr->end();
}
inline
Value::const_iterator Value::end() const
{
if (ARR != m_type)
throw Error("Attempt to iterate over non-array value");
return m_arr->end();
}
inline
const Value& Value::operator[](unsigned pos) const
{
check_type(ARRAY);
return m_arr->at(pos);
}
inline
size_t Value::elementCount() const
{
check_type(ARRAY);
return m_arr->size();
}
MYSQLX_ABI_END(2,0)
} // mysqlx
#endif