2140 lines
50 KiB
C
2140 lines
50 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 MYSQL_DEVAPI_H
|
||
|
#define MYSQL_DEVAPI_H
|
||
|
|
||
|
#ifndef __cplusplus
|
||
|
#error This header can be only used with C++ code
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
@defgroup devapi X DevAPI Classes
|
||
|
|
||
|
X DevAPI Classes and types. See @ref devapi_ref for introduction.
|
||
|
|
||
|
@defgroup devapi_op Database operations
|
||
|
@ingroup devapi
|
||
|
|
||
|
Classes representing yet-to-be-executed database operations.
|
||
|
|
||
|
Such operations are created by various methods of
|
||
|
@link mysqlx::abi2::r0::Collection `Collection`@endlink or
|
||
|
@link mysqlx::abi2::r0::Table `Table`@endlink classes. Database operation
|
||
|
classes define methods that specify additional operation characteristics
|
||
|
before it gets executed with `execute()` method. The latter
|
||
|
returns a @link mysqlx::abi2::r0::Result `Result`@endlink,
|
||
|
@link mysqlx::abi2::r0::DocResult `DocResult`@endlink or
|
||
|
@link mysqlx::abi2::r0::RowResult `RowResult`@endlink object,
|
||
|
depending on the type of the operation.
|
||
|
|
||
|
@defgroup devapi_res Classes for result processing
|
||
|
@ingroup devapi
|
||
|
|
||
|
Classes used to examine results of a statement and documents or
|
||
|
rows contained in a result.
|
||
|
|
||
|
@defgroup devapi_aux Auxiliary types
|
||
|
@ingroup devapi
|
||
|
*/
|
||
|
|
||
|
|
||
|
/**
|
||
|
@file
|
||
|
The main header for MySQL Connector/C++ DevAPI.
|
||
|
|
||
|
This header should be included by C++ code which uses the DevAPI implemented
|
||
|
by MySQL Connector/C++.
|
||
|
|
||
|
@sa result.h, document.h
|
||
|
|
||
|
@ingroup devapi
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
X DevAPI public classes are declared in this and other headers included from
|
||
|
devapi/ folder. The main public API classes, such as Session below, contain
|
||
|
declarations of public interface methods. Any obscure details of the public
|
||
|
API, which must be defined in the public header, are factored out
|
||
|
to Session_detail class from which the main Session class inherits.
|
||
|
Among other things, Session_detail declares the implementation class for
|
||
|
Session. This implementation class is opaque and its details are not defined
|
||
|
in the public headers - only in the implementation part. Definitions of
|
||
|
XXX_detail classes can be found in devapi/detail/ sub-folder.
|
||
|
*/
|
||
|
|
||
|
#include "devapi/common.h"
|
||
|
#include "devapi/result.h"
|
||
|
#include "devapi/collection_crud.h"
|
||
|
#include "devapi/table_crud.h"
|
||
|
#include "devapi/settings.h"
|
||
|
#include "devapi/detail/session.h"
|
||
|
|
||
|
namespace mysqlx {
|
||
|
MYSQLX_ABI_BEGIN(2,0)
|
||
|
|
||
|
class Session;
|
||
|
|
||
|
namespace internal {
|
||
|
|
||
|
template <class Base> class Sch_object;
|
||
|
|
||
|
} // internal
|
||
|
|
||
|
|
||
|
/// Collection create/modify Validation options
|
||
|
|
||
|
class CollectionOptions;
|
||
|
|
||
|
/**
|
||
|
The CollectionValidation class defines collection schema and level of
|
||
|
validation.
|
||
|
*/
|
||
|
|
||
|
class CollectionValidation
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
#define COLLECTION_VALIDATION_ENUM(x,y) x=y,
|
||
|
|
||
|
|
||
|
/**
|
||
|
Collection validation level options
|
||
|
|
||
|
\anchor CollectionValidation_Level
|
||
|
*/
|
||
|
|
||
|
enum Level
|
||
|
{
|
||
|
COLLECTION_VALIDATION_LEVEL(COLLECTION_VALIDATION_ENUM)
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
\anchor CollectionValidation_Option
|
||
|
Collection validation options
|
||
|
*/
|
||
|
|
||
|
enum Option
|
||
|
{
|
||
|
COLLECTION_VALIDATION_OPTION(COLLECTION_VALIDATION_ENUM)
|
||
|
LAST
|
||
|
};
|
||
|
|
||
|
private:
|
||
|
|
||
|
struct Data
|
||
|
{
|
||
|
std::string validation_level;
|
||
|
DbDoc validation_schema;
|
||
|
std::bitset<LAST> used;
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
|
||
|
CollectionValidation()
|
||
|
{}
|
||
|
|
||
|
CollectionValidation(const char* json_doc)
|
||
|
: CollectionValidation(DbDoc(json_doc))
|
||
|
{}
|
||
|
|
||
|
/**
|
||
|
Constructor using a document.
|
||
|
|
||
|
Document example:
|
||
|
~~~~~~
|
||
|
{
|
||
|
"level": "Strict",
|
||
|
"schema":
|
||
|
{
|
||
|
"id": "http://json-schema.org/geo",
|
||
|
"$schema": "http://json-schema.org/draft-06/schema#",
|
||
|
"description": "A geographical coordinate",
|
||
|
"type": "object",
|
||
|
"properties":
|
||
|
{
|
||
|
"latitude":
|
||
|
{
|
||
|
"type": "number"
|
||
|
},
|
||
|
"longitude":
|
||
|
{
|
||
|
"type": "number"
|
||
|
}
|
||
|
},
|
||
|
"required": ["latitude", "longitude"]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
~~~~~~
|
||
|
|
||
|
Document keys:
|
||
|
- `level`: See CollectionValidation::LEVEL;
|
||
|
- `schema`: See CollectionValidation::SCHEMA;
|
||
|
|
||
|
*/
|
||
|
|
||
|
CollectionValidation(DbDoc doc)
|
||
|
{
|
||
|
for(auto el : doc)
|
||
|
{
|
||
|
if(el == "level")
|
||
|
{
|
||
|
try {
|
||
|
_set(LEVEL, doc[el].get<std::string>());
|
||
|
} catch (const Error& e)
|
||
|
{
|
||
|
std::string err("Unexpected level type: ");
|
||
|
err+=e.what();
|
||
|
throw Error(err.c_str());
|
||
|
}
|
||
|
}
|
||
|
else if(el == "schema")
|
||
|
{
|
||
|
_set(SCHEMA,doc[el].get<DbDoc>());
|
||
|
}
|
||
|
else {
|
||
|
std::string err("Unexpected schema validation field ");
|
||
|
err+=el;
|
||
|
throw Error(err.c_str());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Construct CollectionValidation from list of Option and value pairs.
|
||
|
See @ref CollectionValidation_Option "CollectionValidation::Option"
|
||
|
for possible options.
|
||
|
*/
|
||
|
template<typename... Rest>
|
||
|
CollectionValidation(Option opt, Rest&&... rest)
|
||
|
{
|
||
|
set(opt, std::forward<Rest>(rest)...);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Set list of Option and value pairs.
|
||
|
@see CollectionValidation::CollectionValidation
|
||
|
*/
|
||
|
template<typename... Rest>
|
||
|
void set(Rest&&... options)
|
||
|
{
|
||
|
Data tmp_data(m_data);
|
||
|
try {
|
||
|
_set(std::forward<Rest>(options)...);
|
||
|
} catch (...) {
|
||
|
m_data = tmp_data;
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
|
||
|
/// @cond DISABLED
|
||
|
// Note: Doxygen gets confused here and renders docs incorrectly.
|
||
|
template<typename T,typename... Rest>
|
||
|
void _set(Option opt, T&& v, Rest&&... options)
|
||
|
{
|
||
|
#define SCHEMA_VALIDATION_SET(x,y) case CollectionValidation::x:\
|
||
|
do_set<CollectionValidation::x>(std::forward<T>(v)); break;
|
||
|
|
||
|
switch (opt)
|
||
|
{
|
||
|
COLLECTION_VALIDATION_OPTION(SCHEMA_VALIDATION_SET)
|
||
|
case CollectionValidation::LAST: throw_error("Invalid option."); ; break;
|
||
|
}
|
||
|
|
||
|
_set(std::forward<Rest>(options)...);
|
||
|
}
|
||
|
/// @endcond
|
||
|
|
||
|
void _set() {}
|
||
|
|
||
|
template<CollectionValidation::Option, typename T>
|
||
|
void do_set(T)
|
||
|
{
|
||
|
throw_error("Invalid option value type.");
|
||
|
}
|
||
|
|
||
|
Data m_data;
|
||
|
|
||
|
friend CollectionOptions;
|
||
|
friend Schema;
|
||
|
friend mysqlx::internal::Schema_detail;
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
The CollectionOptions class defines collection create/modify options.
|
||
|
*/
|
||
|
|
||
|
class CollectionOptions
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
#define COLLECTION_OPTIONS_ENUM(x,y) x=y,
|
||
|
|
||
|
/**
|
||
|
\anchor CollectionOptions_Option
|
||
|
Collection options
|
||
|
*/
|
||
|
|
||
|
enum Option
|
||
|
{
|
||
|
COLLECTION_OPTIONS_OPTION(COLLECTION_OPTIONS_ENUM)
|
||
|
LAST
|
||
|
};
|
||
|
|
||
|
private:
|
||
|
|
||
|
struct Data{
|
||
|
CollectionValidation validation;
|
||
|
std::bitset<LAST> used;
|
||
|
bool reuse = false;
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
|
||
|
|
||
|
CollectionOptions()
|
||
|
{}
|
||
|
|
||
|
CollectionOptions(const char* options)
|
||
|
: CollectionOptions(DbDoc(options))
|
||
|
{}
|
||
|
|
||
|
CollectionOptions(const std::string& options)
|
||
|
: CollectionOptions(DbDoc(options))
|
||
|
{}
|
||
|
|
||
|
/**
|
||
|
Constructor using a document.
|
||
|
|
||
|
Document example:
|
||
|
~~~~~~
|
||
|
{
|
||
|
"reuseExisting": true,
|
||
|
"validation":
|
||
|
{
|
||
|
"level": "Strict",
|
||
|
"schema":
|
||
|
{
|
||
|
"id": "http://json-schema.org/geo",
|
||
|
"$schema": "http://json-schema.org/draft-06/schema#",
|
||
|
"description": "A geographical coordinate",
|
||
|
"type": "object",
|
||
|
"properties":
|
||
|
{
|
||
|
"latitude":
|
||
|
{
|
||
|
"type": "number"
|
||
|
},
|
||
|
"longitude":
|
||
|
{
|
||
|
"type": "number"
|
||
|
}
|
||
|
},
|
||
|
"required": ["latitude", "longitude"]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
~~~~~~
|
||
|
|
||
|
Document keys:
|
||
|
- `reuseExisting` : Same as CollectionOptions::REUSE;
|
||
|
- `validation` : Same as CollectionOptions::VALIDATION;
|
||
|
|
||
|
|
||
|
*/
|
||
|
CollectionOptions(DbDoc options)
|
||
|
{
|
||
|
for(auto el : options)
|
||
|
{
|
||
|
if(el == "reuseExisting")
|
||
|
{
|
||
|
try {
|
||
|
_set(REUSE, options["reuseExisting"].get<bool>());
|
||
|
} catch (const Error& e)
|
||
|
{
|
||
|
std::string err("Wrong value for reuseExisting option: ");
|
||
|
err+=e.what();
|
||
|
throw Error(err.c_str());
|
||
|
}
|
||
|
}
|
||
|
else if(el == "validation")
|
||
|
{
|
||
|
_set(VALIDATION, CollectionValidation(options["validation"].get<DbDoc>()));
|
||
|
}
|
||
|
else {
|
||
|
std::string err("Unexpected collection option ");
|
||
|
err+=el;
|
||
|
throw Error(err.c_str());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CollectionOptions(CollectionValidation validation)
|
||
|
{
|
||
|
set(VALIDATION, validation);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Construct CollectionOptions from list of Option and value pairs.
|
||
|
@ref CollectionOptions_Option "CollectionOptions::Option" and
|
||
|
@ref CollectionValidation_Option "CollectionValidation::Option" can both be
|
||
|
used.
|
||
|
|
||
|
Example:
|
||
|
~~~~
|
||
|
schema.createCollection(
|
||
|
"collection_test",
|
||
|
CollectionValidation::LEVEL, CollectionValidation::STRICT,
|
||
|
CollectionOptions::REUSE, true,
|
||
|
CollectionValidation::SCHEMA,
|
||
|
R"(
|
||
|
{
|
||
|
"id": "http://json-schema.org/geo",
|
||
|
"$schema": "http://json-schema.org/draft-06/schema#",
|
||
|
"description": "A geographical coordinate",
|
||
|
"type": "object",
|
||
|
"properties":
|
||
|
{
|
||
|
"latitude": {
|
||
|
"type": "number"
|
||
|
},
|
||
|
"longitude": {
|
||
|
"type": "number"
|
||
|
}
|
||
|
},
|
||
|
"required": ["latitude", "longitude"]
|
||
|
})"
|
||
|
);
|
||
|
|
||
|
~~~~
|
||
|
|
||
|
*/
|
||
|
|
||
|
template<typename... Rest>
|
||
|
CollectionOptions(Option opt, Rest&&... rest)
|
||
|
{
|
||
|
set(opt, std::forward<Rest>(rest)...);
|
||
|
}
|
||
|
|
||
|
|
||
|
template<typename... Rest>
|
||
|
CollectionOptions(CollectionValidation::Option opt, Rest&&... rest)
|
||
|
{
|
||
|
set(opt, std::forward<Rest>(rest)...);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Set list of option and value pairs.
|
||
|
@see CollectionOptions::CollectionOptions
|
||
|
*/
|
||
|
template<typename... Rest>
|
||
|
void set(Rest&&... rest)
|
||
|
{
|
||
|
Data tmp_data(m_data);
|
||
|
try {
|
||
|
_set(std::forward<Rest>(rest)...);
|
||
|
} catch (...) {
|
||
|
m_data = std::move(tmp_data);
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
protected:
|
||
|
|
||
|
/// @cond DISABLED
|
||
|
// Note: Doxygen gets confused here and renders docs incorrectly.
|
||
|
template<typename T,typename... Rest>
|
||
|
void _set(Option opt, T&& v, Rest&&... rest)
|
||
|
{
|
||
|
#define COLLECTION_OPTIONS_SET(x,y) case x:\
|
||
|
do_set<x>(std::forward<T>(v)); break;
|
||
|
|
||
|
switch (opt)
|
||
|
{
|
||
|
COLLECTION_OPTIONS_OPTION(COLLECTION_OPTIONS_SET)
|
||
|
case LAST: throw_error("Invalid option."); ; break;
|
||
|
}
|
||
|
|
||
|
_set(std::forward<Rest>(rest)...);
|
||
|
}
|
||
|
/// @endcond
|
||
|
|
||
|
template<typename T,typename... Rest>
|
||
|
void _set(CollectionValidation::Option opt, T&& v, Rest&&... rest)
|
||
|
{
|
||
|
m_data.validation._set(opt, std::forward<T>(v));
|
||
|
_set(std::forward<Rest>(rest)...);
|
||
|
}
|
||
|
|
||
|
void _set(){}
|
||
|
|
||
|
template<CollectionOptions::Option O,typename T>
|
||
|
void do_set(T)
|
||
|
{
|
||
|
throw_error("Invalid option value type.");
|
||
|
}
|
||
|
|
||
|
Data m_data;
|
||
|
|
||
|
friend mysqlx::internal::Schema_detail;
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
Represents a database schema.
|
||
|
|
||
|
A `Schema` instance can be obtained from `Session::getSchema()`
|
||
|
method:
|
||
|
|
||
|
~~~~~~
|
||
|
Session session;
|
||
|
Schema mySchema;
|
||
|
|
||
|
mySchema= session.getSchema("My Schema");
|
||
|
~~~~~~
|
||
|
|
||
|
or it can be directly constructed as follows:
|
||
|
|
||
|
~~~~~~
|
||
|
Session session;
|
||
|
Schema mySchema(session, "My Schema");
|
||
|
~~~~~~
|
||
|
|
||
|
Each `Schema` instance is tied to a particular session and all
|
||
|
the operations on the schema and its objects are performed using
|
||
|
that session. If the session is destroyed, an attempt to use a schema of
|
||
|
that session yields an error.
|
||
|
|
||
|
When creating a `Schema` object, by default no checks are made that
|
||
|
it actually exists in the database. An operation that is executed
|
||
|
on the server and involves such a non-existent schema throws
|
||
|
an error.
|
||
|
|
||
|
@note A `Schema` object should be used by at most one thread at a time. It is
|
||
|
not safe to call its methods by several threads simultaneously. It is
|
||
|
responsibility of the user to ensure this using a synchronization mechanism
|
||
|
such as mutexes.
|
||
|
|
||
|
@ingroup devapi
|
||
|
*/
|
||
|
|
||
|
class Schema
|
||
|
: protected internal::Schema_detail
|
||
|
{
|
||
|
Session *m_sess;
|
||
|
|
||
|
public:
|
||
|
|
||
|
/**
|
||
|
Construct an object representing the named schema.
|
||
|
*/
|
||
|
|
||
|
Schema(Session &sess, const string &name);
|
||
|
|
||
|
/**
|
||
|
Construct an object representing the default schema of the session.
|
||
|
|
||
|
The default schema is the one specified by session creation options.
|
||
|
*/
|
||
|
|
||
|
Schema(Session&);
|
||
|
|
||
|
|
||
|
/**
|
||
|
Get schema name
|
||
|
*/
|
||
|
|
||
|
const string& getName() const
|
||
|
{
|
||
|
return m_name;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get Session object
|
||
|
*/
|
||
|
|
||
|
Session& getSession()
|
||
|
{
|
||
|
return *m_sess;
|
||
|
}
|
||
|
|
||
|
const Session& getSession() const
|
||
|
{
|
||
|
return const_cast<Schema*>(this)->getSession();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Check if this schema exists in the database.
|
||
|
|
||
|
@note Involves communication with the server.
|
||
|
*/
|
||
|
|
||
|
bool existsInDatabase() const
|
||
|
{
|
||
|
try {
|
||
|
/*
|
||
|
Note: We get from server a list of schemata filtered by the name of
|
||
|
this schema - if the schema exists the list should not be empty.
|
||
|
*/
|
||
|
internal::Session_detail::Name_src list(*m_sess, m_name);
|
||
|
list.iterator_start();
|
||
|
return list.iterator_next();
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Create a new collection in the schema.
|
||
|
|
||
|
Returns the created collection. Set `reuse` flag to true to return
|
||
|
an already existing collection with the same name. Otherwise, an attempt
|
||
|
to create a collection which already exists throws an error.
|
||
|
*/
|
||
|
|
||
|
|
||
|
Collection createCollection(const string &name);
|
||
|
Collection createCollection(const string &name, bool);
|
||
|
|
||
|
/**
|
||
|
Create a new collection in the schema, optionally specifying creation
|
||
|
options. Arguments following `name`, if any, are used to construct
|
||
|
CollectionOptions object. See CollectionOptions for possible ways of
|
||
|
specifying the options.
|
||
|
|
||
|
Returns the created collection.
|
||
|
*/
|
||
|
template<typename... Rest>
|
||
|
Collection createCollection(const string &name,
|
||
|
Rest&&... rest);
|
||
|
|
||
|
/**
|
||
|
Modify a collection in the schema specifying modify options. Arguments
|
||
|
following `name` are used to construct CollectionOptions object. See
|
||
|
CollectionOptions for possible ways of specifying the options.
|
||
|
|
||
|
@note CollectionOptions::REUSE is not allowed and, if used, will throw
|
||
|
error.
|
||
|
*/
|
||
|
template<typename... Rest>
|
||
|
void modifyCollection(const string &name, Rest&&... options);
|
||
|
|
||
|
|
||
|
/**
|
||
|
Return an object representing a collection with the given name.
|
||
|
|
||
|
To check if the collection actually exists in the database set
|
||
|
`check_existence` flag to true. Otherwise the returned object can refer to
|
||
|
a non-existing collection. An attempt to use such a non-existing collection
|
||
|
in a database operation throws an error.
|
||
|
|
||
|
@note Checking the existence of a collection involves communication with
|
||
|
the server. If `check_exists` is false, on the other hand, no I/O is
|
||
|
involved when creating a `Collection` object.
|
||
|
*/
|
||
|
|
||
|
Collection getCollection(const string &name, bool check_exists = false);
|
||
|
|
||
|
/**
|
||
|
Return an object representing a table or a view with the given name.
|
||
|
|
||
|
To check if the table actually exists in the database set
|
||
|
`check_existence` flag to true. Otherwise the returned object can refer to
|
||
|
a non-existing table. An attempt to use such a non-existing table
|
||
|
in a database operation throws an error.
|
||
|
|
||
|
@note The returned `Table` object can represent a plain table or
|
||
|
a view. See `Table` class description.
|
||
|
|
||
|
@note Checking the existence of a table involves communication with
|
||
|
the server. If `check_exists` is false, on the other hand, no I/O is
|
||
|
involved when creating a `Table` object.
|
||
|
*/
|
||
|
|
||
|
Table getTable(const string &name, bool check_exists = false);
|
||
|
|
||
|
/**
|
||
|
Get a list of all collections in the schema.
|
||
|
|
||
|
The returned value can be stored in a container that holds `Collection`
|
||
|
objects, such as `std::vector<Collection>`.
|
||
|
*/
|
||
|
|
||
|
CollectionList getCollections()
|
||
|
{
|
||
|
try {
|
||
|
return Collection_src(*this, "%");
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get a list of names of all collections in the schema.
|
||
|
|
||
|
The returned value can be stored in a container that holds strings, such as
|
||
|
`std::vector<string>`.
|
||
|
*/
|
||
|
|
||
|
StringList getCollectionNames()
|
||
|
{
|
||
|
try {
|
||
|
return Name_src(*this, COLLECTION, "%");
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get a list of all tables and views in the schema.
|
||
|
|
||
|
The returned value can be stored in a container that holds `Table`
|
||
|
objects, such as `std::vector<Table>`.
|
||
|
|
||
|
@note The list also contains views which are represented by `Table` objects
|
||
|
- see `Table` class description.
|
||
|
*/
|
||
|
|
||
|
TableList getTables()
|
||
|
{
|
||
|
try {
|
||
|
return Table_src(*this, "%");
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get a list of names of all tables and views in the schema.
|
||
|
|
||
|
The returned value can be stored in a container that holds strings, such as
|
||
|
`std::vector<string>`.
|
||
|
|
||
|
@note The list also contains names of views as views are represented
|
||
|
by `Table` objects - see `Table` class description.
|
||
|
*/
|
||
|
|
||
|
StringList getTableNames()
|
||
|
{
|
||
|
try {
|
||
|
return Name_src(*this, TABLE, "%");
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return a table corresponding to the given collection.
|
||
|
|
||
|
The table has two columns: `_id` and `doc`. For each document in
|
||
|
the collection there is one row in the table with `doc` filed holding
|
||
|
the document as a JSON value and `_id` field holding document's identifier.
|
||
|
|
||
|
To check if the collection actually exists in the database set
|
||
|
`check_existence` flag to true. Otherwise the returned table can refer to
|
||
|
a non-existing collection. An attempt to use such a non-existing collection
|
||
|
table throws an error.
|
||
|
|
||
|
@note Checking the existence of a collection involves communication with
|
||
|
the server. If `check_exists` is false, on the other hand, no I/O is
|
||
|
involved when creating the `Table` object.
|
||
|
*/
|
||
|
|
||
|
Table getCollectionAsTable(const string &name, bool check_exists = true);
|
||
|
|
||
|
|
||
|
/**
|
||
|
Drop the given collection from the schema.
|
||
|
|
||
|
This method silently succeeds if a collection with the given name does
|
||
|
not exist.
|
||
|
|
||
|
@note If a table name is passed to the method, it behaves like
|
||
|
dropTable().
|
||
|
*/
|
||
|
|
||
|
void dropCollection(const mysqlx::string& name)
|
||
|
{
|
||
|
try {
|
||
|
Schema_detail::drop_collection(name);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
friend Collection;
|
||
|
friend Table;
|
||
|
|
||
|
///@cond IGNORE
|
||
|
friend internal::Schema_detail::Name_src;
|
||
|
template <class Base> friend class internal::Sch_object;
|
||
|
///@endcond
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
Database objects that belong to some schema
|
||
|
===========================================
|
||
|
*/
|
||
|
|
||
|
|
||
|
namespace internal {
|
||
|
|
||
|
// Common base for schema objects defining the common API methods.
|
||
|
|
||
|
template <class Base = Db_obj_base>
|
||
|
class Sch_object
|
||
|
: public Base
|
||
|
{
|
||
|
protected:
|
||
|
|
||
|
Schema m_schema;
|
||
|
|
||
|
Sch_object(const Schema &sch, const string &name);
|
||
|
|
||
|
public:
|
||
|
|
||
|
using string = mysqlx::string;
|
||
|
|
||
|
/**
|
||
|
Get database object name
|
||
|
*/
|
||
|
|
||
|
const string& getName() const
|
||
|
{
|
||
|
return Base::m_name;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get Session object
|
||
|
*/
|
||
|
|
||
|
Session& getSession()
|
||
|
{
|
||
|
return m_schema.getSession();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get schema object
|
||
|
*/
|
||
|
|
||
|
const Schema& getSchema() const
|
||
|
{
|
||
|
return m_schema;
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
|
||
|
std::shared_ptr<common::Session_impl> get_session();
|
||
|
|
||
|
Schema_detail& get_schema()
|
||
|
{
|
||
|
return m_schema;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // internal
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
Represents a collection of documents in a schema.
|
||
|
|
||
|
A collection object can be obtained from `Schema::getCollection()`
|
||
|
method:
|
||
|
|
||
|
~~~~~~
|
||
|
Schema db;
|
||
|
Collection myColl;
|
||
|
|
||
|
myColl= db.getCollection("My Collection");
|
||
|
~~~~~~
|
||
|
|
||
|
or directly constructed as follows:
|
||
|
|
||
|
~~~~~~
|
||
|
Schema db;
|
||
|
Collection myColl(db, "My Collection");
|
||
|
~~~~~~
|
||
|
|
||
|
When creating a `Collection` object, by default no checks are made that
|
||
|
it actually exists in the database. An operation that is executed
|
||
|
on the server and involves such a non-existent collection throws
|
||
|
an error. Call `existsInDatabase()` to check the existence of the collection.
|
||
|
|
||
|
@note A `Collection` object should be used by at most one thread at a time.
|
||
|
It is not safe to call its methods by several threads simultaneously. It is
|
||
|
responsibility of the user to ensure this using a synchronization mechanism
|
||
|
such as mutexes.
|
||
|
|
||
|
@ingroup devapi
|
||
|
*/
|
||
|
|
||
|
class Collection
|
||
|
: protected internal::Sch_object<internal::Collection_detail>
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
Collection(const Schema &sch, const string &name)
|
||
|
try
|
||
|
: Sch_object(sch, name)
|
||
|
{}
|
||
|
CATCH_AND_WRAP
|
||
|
|
||
|
|
||
|
using Sch_object::getName;
|
||
|
using Sch_object::getSession;
|
||
|
using Sch_object::getSchema;
|
||
|
|
||
|
bool existsInDatabase() const
|
||
|
{
|
||
|
try {
|
||
|
Schema::StringList list(m_schema, Schema::COLLECTION, m_name);
|
||
|
return list.begin() != list.end();
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get the number of documents in the collection.
|
||
|
*/
|
||
|
|
||
|
uint64_t count();
|
||
|
|
||
|
/*
|
||
|
CRUD operations on a collection
|
||
|
-------------------------------
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
Return an operation which fetches all documents from the collection.
|
||
|
|
||
|
Call `execute()` on the returned operation object to execute it and get
|
||
|
a `DocResult` object that gives access to the documents. Specify additional
|
||
|
query parameters, such as ordering of the documents, using chained methods
|
||
|
of `CollectionFind` class before making the final call to `execute()`.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `CollectionFind`
|
||
|
*/
|
||
|
|
||
|
CollectionFind find()
|
||
|
{
|
||
|
try {
|
||
|
return CollectionFind(*this);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return an operation which finds documents that satisfy given criteria.
|
||
|
|
||
|
The criteria are specified as a Boolean expression string.
|
||
|
Call `execute()` on the returned operation object to execute it and get
|
||
|
a `DocResult` object that gives access to the documents. Specify additional
|
||
|
query parameters, such as ordering of the documents, using chained methods
|
||
|
of `CollectionFind` class before making the final call to `execute()`.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `CollectionFind`
|
||
|
*/
|
||
|
|
||
|
CollectionFind find(const string &cond)
|
||
|
{
|
||
|
try {
|
||
|
return CollectionFind(*this, cond);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return an operation which adds documents to the collection.
|
||
|
|
||
|
Specify documents to be added in the same way as when calling
|
||
|
`CollectionAdd::add()` method. Make additional calls to `add()` method on
|
||
|
the returned operation object to add more documents. Call `execute()`
|
||
|
to execute the operation and add all specified documents to the collection.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `CollectionAdd`
|
||
|
*/
|
||
|
|
||
|
template <typename... Types>
|
||
|
CollectionAdd add(Types... args)
|
||
|
{
|
||
|
try {
|
||
|
|
||
|
CollectionAdd add(*this);
|
||
|
return add.add(args...);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return an operation which removes documents satisfying given criteria.
|
||
|
|
||
|
The criteria are specified as a Boolean expression string.
|
||
|
Call `execute()` on the returned operation object to execute it and remove
|
||
|
the matching documents. Use chained methods of `CollectionRemove` class
|
||
|
before the final call to `execute()` to further limit the set of documents
|
||
|
that are removed.
|
||
|
|
||
|
@note To remove all documents in the collection, pass "true" as selection
|
||
|
criteria.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `CollectionRemove`
|
||
|
*/
|
||
|
|
||
|
CollectionRemove remove(const string &cond)
|
||
|
{
|
||
|
try {
|
||
|
return CollectionRemove(*this, cond);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return an operation which modifies documents that satisfy given criteria.
|
||
|
|
||
|
The criteria are specified as a Boolean expression string.
|
||
|
Specify modifications to be applied to each document using chained methods
|
||
|
of `CollectionModify` class on the returned operation object. Call
|
||
|
`execute()` to execute the operation and modify matching documents
|
||
|
as specified.
|
||
|
|
||
|
@note To modify all documents in the collection, pass "true" as selection
|
||
|
criteria.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `CollectionModify`
|
||
|
*/
|
||
|
|
||
|
CollectionModify modify(const string &expr)
|
||
|
{
|
||
|
try {
|
||
|
return CollectionModify(*this, expr);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return the document with the given id.
|
||
|
|
||
|
Returns null document if a document with the given id does not exist in
|
||
|
the collection.
|
||
|
*/
|
||
|
|
||
|
DbDoc getOne(const string &id)
|
||
|
{
|
||
|
return find("_id = :id").bind("id", id).execute().fetchOne();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Remove the document with the given id.
|
||
|
|
||
|
Does nothing if a document with the given id does not exist in
|
||
|
the collection.
|
||
|
*/
|
||
|
|
||
|
Result removeOne(const string &id)
|
||
|
{
|
||
|
return remove("_id = :id").bind("id", id).execute();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Replace the document with the given id by a new one.
|
||
|
|
||
|
Specify the new document as either a `DbDoc` object, a JSON string or
|
||
|
an `expr(docexpr)` argument, where `docexpr` is like a JSON string but
|
||
|
field values are given by expressions to be evaluated on the server.
|
||
|
|
||
|
If a document with the given id does not exist in the collection, nothing
|
||
|
is done and the returned `Result` object indicates that no documents were
|
||
|
modified.
|
||
|
|
||
|
@note If expressions are used, they can not use named parameters because
|
||
|
it is not possible to bind values prior to execution of `replaceOne()`
|
||
|
operation.
|
||
|
*/
|
||
|
|
||
|
Result replaceOne(const string &id, Value &&document)
|
||
|
{
|
||
|
try {
|
||
|
return
|
||
|
Collection_detail::add_or_replace_one(id, std::move(document), true);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Add a new document or replace an existing document with the given id.
|
||
|
|
||
|
Specify the new document as either a `DbDoc` object, a JSON string or
|
||
|
an `expr(docexpr)` argument, where `docexpr` is like a JSON string but
|
||
|
field values are given by expressions to be evaluated on the server.
|
||
|
|
||
|
If a document with the given id does not exist, the new document is added
|
||
|
to the collection.
|
||
|
|
||
|
@note If expressions are used, they can not use named parameters because
|
||
|
it is not possible to bind values prior to execution of `addOrReplaceOne()`
|
||
|
operation.
|
||
|
*/
|
||
|
|
||
|
Result addOrReplaceOne(const string &id, Value &&document)
|
||
|
{
|
||
|
try {
|
||
|
return
|
||
|
Collection_detail::add_or_replace_one(id, std::move(document), false);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Create index on the collection.
|
||
|
|
||
|
This function creates a named index in the collection using a JSON index
|
||
|
specification.
|
||
|
|
||
|
@param name name for an index to be created
|
||
|
@param idx_spec index specification as a JSON string
|
||
|
|
||
|
@see @ref indexing for information on how to define document
|
||
|
collection indexes.
|
||
|
*/
|
||
|
|
||
|
void createIndex(const string &name, const string &idx_spec)
|
||
|
{
|
||
|
try {
|
||
|
Collection_detail::index_create(name, idx_spec);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Drop index on the collection.
|
||
|
|
||
|
@param name name for an index to be dropped
|
||
|
|
||
|
@ingroup devapi_ddl
|
||
|
*/
|
||
|
|
||
|
void dropIndex(const string &name)
|
||
|
{
|
||
|
try {
|
||
|
Collection_detail::index_drop(name);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
friend CollectionFind;
|
||
|
friend CollectionAdd;
|
||
|
friend CollectionRemove;
|
||
|
friend CollectionModify;
|
||
|
|
||
|
///@cond IGNORE
|
||
|
friend internal::Crud_factory;
|
||
|
///@endcond
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
template<>
|
||
|
inline
|
||
|
void CollectionValidation::do_set<CollectionValidation::SCHEMA>(DbDoc schema)
|
||
|
{
|
||
|
if(m_data.used.test(CollectionValidation::SCHEMA))
|
||
|
throw_error("Validation schema already set.");
|
||
|
m_data.used.set(CollectionValidation::SCHEMA);
|
||
|
m_data.validation_schema = schema;
|
||
|
}
|
||
|
|
||
|
template<>
|
||
|
inline
|
||
|
void CollectionValidation::do_set<CollectionValidation::SCHEMA>(const char* schema)
|
||
|
{
|
||
|
do_set<CollectionValidation::SCHEMA>(DbDoc(schema));
|
||
|
}
|
||
|
|
||
|
template<>
|
||
|
inline
|
||
|
void CollectionValidation::do_set<CollectionValidation::SCHEMA>(std::string schema)
|
||
|
{
|
||
|
do_set<CollectionValidation::SCHEMA>(DbDoc(schema));
|
||
|
}
|
||
|
|
||
|
|
||
|
template<>
|
||
|
inline
|
||
|
void CollectionValidation::do_set<CollectionValidation::LEVEL>(Level level)
|
||
|
{
|
||
|
if(m_data.used.test(CollectionValidation::LEVEL))
|
||
|
throw_error("Validation level already set.");
|
||
|
m_data.used.set(CollectionValidation::LEVEL);
|
||
|
|
||
|
#define SCHEMA_VALIDATION_CASE(x,y) case Level::x: m_data.validation_level = #x; break;
|
||
|
|
||
|
switch (level) {
|
||
|
COLLECTION_VALIDATION_LEVEL(SCHEMA_VALIDATION_CASE)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template<>
|
||
|
inline
|
||
|
void CollectionValidation::do_set<CollectionValidation::LEVEL>(std::string level)
|
||
|
{
|
||
|
if(m_data.used.test(CollectionValidation::LEVEL))
|
||
|
throw_error("Validation level already set.");
|
||
|
m_data.used.set(CollectionValidation::LEVEL);
|
||
|
|
||
|
m_data.validation_level = level;
|
||
|
|
||
|
}
|
||
|
|
||
|
template<>
|
||
|
inline
|
||
|
void CollectionOptions::do_set<CollectionOptions::REUSE>(bool reuse)
|
||
|
{
|
||
|
if(m_data.used[CollectionOptions::REUSE])
|
||
|
throw_error("Option reuse already set.");
|
||
|
m_data.used.set(CollectionOptions::REUSE);
|
||
|
m_data.reuse = reuse;
|
||
|
}
|
||
|
|
||
|
template<>
|
||
|
inline
|
||
|
void CollectionOptions::do_set<CollectionOptions::VALIDATION>(CollectionValidation validation)
|
||
|
{
|
||
|
if(m_data.used.test(CollectionOptions::VALIDATION) ||
|
||
|
m_data.validation.m_data.used.test(CollectionValidation::LEVEL) ||
|
||
|
m_data.validation.m_data.used.test(CollectionValidation::SCHEMA))
|
||
|
throw_error("Validation already set.");
|
||
|
|
||
|
m_data.used.set(CollectionOptions::VALIDATION);
|
||
|
m_data.validation.m_data.used.set(CollectionValidation::LEVEL);
|
||
|
m_data.validation.m_data.used.set(CollectionValidation::SCHEMA);
|
||
|
|
||
|
m_data.validation = validation;
|
||
|
}
|
||
|
|
||
|
|
||
|
inline
|
||
|
Collection Schema::createCollection(const string &name)
|
||
|
{
|
||
|
try {
|
||
|
Schema_detail::create_collection(
|
||
|
name,
|
||
|
CollectionOptions() );
|
||
|
|
||
|
return Collection(*this, name);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
inline
|
||
|
Collection Schema::createCollection(const string &name, bool reuse)
|
||
|
{
|
||
|
try {
|
||
|
Schema_detail::create_collection(
|
||
|
name,
|
||
|
CollectionOptions(
|
||
|
CollectionOptions::REUSE, reuse)
|
||
|
);
|
||
|
|
||
|
return Collection(*this, name);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
template<typename... Rest>
|
||
|
inline
|
||
|
Collection Schema::createCollection(const string &name, Rest&&... rest)
|
||
|
{
|
||
|
try {
|
||
|
Schema_detail::create_collection(name, CollectionOptions(std::forward<Rest>(rest)...));
|
||
|
return Collection(*this, name);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename... Opt>
|
||
|
inline
|
||
|
void Schema::modifyCollection(const string &name, Opt&&... options)
|
||
|
{
|
||
|
try {
|
||
|
Schema_detail::modify_collection(name, CollectionOptions(std::forward<Opt>(options)...));
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
inline
|
||
|
Collection Schema::getCollection(const string &name, bool check_exists)
|
||
|
{
|
||
|
Collection coll(*this, name);
|
||
|
if (check_exists && !coll.existsInDatabase())
|
||
|
throw_error("Collection does not exist");
|
||
|
return coll;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
Table object
|
||
|
============
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
Represents a table in a schema.
|
||
|
|
||
|
A `Table` object can be obtained from `Schema::getTable()`
|
||
|
method:
|
||
|
|
||
|
~~~~~~
|
||
|
Schema db;
|
||
|
Table myTable;
|
||
|
|
||
|
myTable= db.getTable("My Table");
|
||
|
~~~~~~
|
||
|
|
||
|
or directly constructed as follows:
|
||
|
|
||
|
~~~~~~
|
||
|
Schema db;
|
||
|
Table myTable(db, "My Table");
|
||
|
~~~~~~
|
||
|
|
||
|
A `Table` object can refer to a plain table or to a view. In the latter case
|
||
|
method `isView()` called on the object returns true.
|
||
|
|
||
|
When creating a `Table` object, by default no checks are made that
|
||
|
it actually exists in the database. An operation that is executed
|
||
|
on the server and involves such a non-existent table throws
|
||
|
an error. Call `existsInDatabase()` to check the existence of the table.
|
||
|
|
||
|
@note A `Table` object should be used by at most one thread at a time. It is
|
||
|
not safe to call its methods by several threads simultaneously. It is
|
||
|
responsibility of the user to ensure this using a synchronization mechanism
|
||
|
such as mutexes.
|
||
|
|
||
|
@ingroup devapi
|
||
|
*/
|
||
|
|
||
|
class Table
|
||
|
: protected internal::Sch_object<>
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
Table(const Schema &sch, const string &name)
|
||
|
: Sch_object(sch, name)
|
||
|
{}
|
||
|
|
||
|
Table(const Schema &sch, const string &name, bool is_view)
|
||
|
: Sch_object(sch, name)
|
||
|
{
|
||
|
m_type = is_view ? VIEW : TABLE;
|
||
|
}
|
||
|
|
||
|
|
||
|
using Sch_object::getName;
|
||
|
using Sch_object::getSession;
|
||
|
using Sch_object::getSchema;
|
||
|
|
||
|
|
||
|
bool existsInDatabase() const;
|
||
|
|
||
|
|
||
|
/**
|
||
|
Check if this table object corresponds to a view.
|
||
|
|
||
|
@note This check might involve communication with the server.
|
||
|
*/
|
||
|
|
||
|
bool isView();
|
||
|
|
||
|
|
||
|
/**
|
||
|
Get the number of rows in the table.
|
||
|
*/
|
||
|
|
||
|
uint64_t count()
|
||
|
{
|
||
|
try {
|
||
|
RowResult cnt = select("count(*)").execute();
|
||
|
return cnt.fetchOne()[0].get<uint64_t>();
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
CRUD operations
|
||
|
---------------
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
Return an operation which inserts rows into the full table without
|
||
|
restricting the columns.
|
||
|
|
||
|
Specify rows to be inserted using methods of `TableInsert` class chained
|
||
|
on the returned operation object. Call `execute()` to execute the operation
|
||
|
and insert the specified rows.
|
||
|
|
||
|
Each specified row must have the same number of values as the number
|
||
|
of columns of the table. If the value count and the table column count do
|
||
|
not match, server reports error when operation is executed.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `TableInsert`
|
||
|
*/
|
||
|
|
||
|
TableInsert insert()
|
||
|
{
|
||
|
try {
|
||
|
return TableInsert(*this);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return an operation which inserts rows into the table restricting
|
||
|
the columns.
|
||
|
|
||
|
Specify column names as method arguments. Values are inserted only into
|
||
|
the specified columns, other columns are set to null or to column's default
|
||
|
value (depending on the definition of the table). Specify rows to
|
||
|
be inserted using methods of `TableInsert` class chained on the returned
|
||
|
operation object. Call `execute()` to execute the operation and insert
|
||
|
the specified values.
|
||
|
|
||
|
Each specified row must have the same number of values as the number
|
||
|
of columns listed here. If the value count and the column count do
|
||
|
not match, server reports error when operation is executed.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `TableInsert`
|
||
|
*/
|
||
|
|
||
|
template <class... T>
|
||
|
TableInsert insert(const T&... t)
|
||
|
{
|
||
|
try {
|
||
|
return TableInsert(*this, t...);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return an operation which selects rows from the table.
|
||
|
|
||
|
Call `execute()` on the returned operation object to execute it and get
|
||
|
a `RowResult` object that gives access to the rows. Specify query
|
||
|
parameters, such as selection criteria and ordering of the rows, using
|
||
|
chained methods of `TableSelect` class before making the final call to
|
||
|
`execute()`. To project selected rows before returning them in the result,
|
||
|
specify a list of expressions as arguments of this method. These expressions
|
||
|
are evaluated to form a row in the result. An expression can be optionally
|
||
|
followed by "AS <name>" suffix to give a name to the corresponding column
|
||
|
in the result. If no expressions are given, rows are returned as is, without
|
||
|
any projection.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `TableSelect`
|
||
|
*/
|
||
|
|
||
|
template<typename ...PROJ>
|
||
|
TableSelect select(const PROJ&...proj)
|
||
|
{
|
||
|
try {
|
||
|
return TableSelect(*this, proj...);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return an operation which removes rows from the table.
|
||
|
|
||
|
Use chained methods of `TableRemove` class on the returned operation object
|
||
|
to specify the rows to be removed. Call `execute()` to execute the operation
|
||
|
and remove the rows.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `TableRemove`
|
||
|
*/
|
||
|
|
||
|
TableRemove remove()
|
||
|
{
|
||
|
try {
|
||
|
return TableRemove(*this);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return an operation which updates rows in the table.
|
||
|
|
||
|
Use chained methods of `TableUpdate` class on the returned operation object
|
||
|
to specify which rows should be updated and what modifications to apply
|
||
|
to each of them. Call `execute()` to execute the operation and remove
|
||
|
the rows.
|
||
|
|
||
|
@note Any errors related to the operation are reported when the operation
|
||
|
is executed, not when it is created.
|
||
|
|
||
|
@see `TableUpdate`
|
||
|
*/
|
||
|
|
||
|
TableUpdate update()
|
||
|
{
|
||
|
try {
|
||
|
return TableUpdate(*this);
|
||
|
}
|
||
|
CATCH_AND_WRAP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
enum { TABLE, VIEW, UNKNOWN } m_type = UNKNOWN;
|
||
|
|
||
|
friend TableSelect;
|
||
|
friend TableInsert;
|
||
|
friend TableRemove;
|
||
|
friend TableUpdate;
|
||
|
|
||
|
///@cond IGNORE
|
||
|
friend internal::Crud_factory;
|
||
|
///@endcond
|
||
|
};
|
||
|
|
||
|
|
||
|
inline
|
||
|
bool Table::existsInDatabase() const
|
||
|
{
|
||
|
try {
|
||
|
/*
|
||
|
Note: When checking existence, we also determine if this is a view or
|
||
|
a plain table because this information is fetched from the server when
|
||
|
querying for a list of tables.
|
||
|
*/
|
||
|
Schema::TableList list(m_schema, m_name);
|
||
|
auto it = list.begin();
|
||
|
|
||
|
if (!(it != list.end()))
|
||
|
return false;
|
||
|
|
||
|
const_cast<Table*>(this)->m_type = (*it).isView() ? VIEW : TABLE;
|
||
|
return true;
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
inline
|
||
|
bool Table::isView()
|
||
|
{
|
||
|
try {
|
||
|
/*
|
||
|
If view status was not determined yet, do existence check which
|
||
|
determines it as a side effect.
|
||
|
*/
|
||
|
if (UNKNOWN == m_type)
|
||
|
if (!existsInDatabase())
|
||
|
throw_error("Table does not exist");
|
||
|
return VIEW == m_type;
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
inline
|
||
|
Table Schema::getTable(const string &name, bool check_exists)
|
||
|
{
|
||
|
Table tbl(*this, name);
|
||
|
if (check_exists && !tbl.existsInDatabase())
|
||
|
throw_error("Table does not exist");
|
||
|
return tbl;
|
||
|
}
|
||
|
|
||
|
|
||
|
inline
|
||
|
Table Schema::getCollectionAsTable(const string &name, bool check_exists)
|
||
|
{
|
||
|
if (check_exists && !getCollection(name).existsInDatabase())
|
||
|
throw_error("Collection does not exist");
|
||
|
return { *this, name };
|
||
|
}
|
||
|
|
||
|
|
||
|
inline
|
||
|
uint64_t Collection::count()
|
||
|
{
|
||
|
return m_schema.getCollectionAsTable(m_name).count();
|
||
|
}
|
||
|
|
||
|
|
||
|
using SqlStatement = internal::SQL_statement;
|
||
|
|
||
|
|
||
|
/**
|
||
|
Represents a session which gives access to data stored in a data store.
|
||
|
|
||
|
A `Session` object can be created from a connection string, from
|
||
|
`SessionSettings` or by directly specifying a host name, TCP/IP port and user
|
||
|
credentials. Once created, a session is ready to be used. Session destructor
|
||
|
closes the session and cleans up after it.
|
||
|
|
||
|
If it is not possible to create a valid session for some reason, errors
|
||
|
are thrown from session constructor.
|
||
|
|
||
|
Several hosts can be specified by session creation parameters. In that case
|
||
|
a failed connection to one of the hosts triggers a fail-over attempt
|
||
|
to connect to a different host in the list. Only if none of the hosts could
|
||
|
be contacted, session creation fails.
|
||
|
|
||
|
The fail-over logic tries hosts in the order in which they are specified in
|
||
|
session settings unless explicit priorities are assigned to the hosts. In that
|
||
|
case hosts are tried in the decreasing priority order and for hosts with
|
||
|
the same priority the order in which they are tired is random.
|
||
|
|
||
|
Once a valid session is created using one of the hosts, the session is bound
|
||
|
to that host and never re-connected again. If the connection gets broken,
|
||
|
the session fails without making any other fail-over attempts. The fail-over
|
||
|
logic is executed only when establishing a new session.
|
||
|
|
||
|
@note A `Session` object should be used by at most one thread at a time. It is
|
||
|
not safe to call its methods by several threads simultaneously. It is
|
||
|
responsibility of the user to ensure this using a synchronization mechanism
|
||
|
such as mutexes.
|
||
|
|
||
|
@ingroup devapi
|
||
|
*/
|
||
|
|
||
|
class Session
|
||
|
: private internal::Session_detail
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
|
||
|
/**
|
||
|
Create a session specified by a `SessionSettings` object.
|
||
|
*/
|
||
|
|
||
|
Session(SessionSettings settings)
|
||
|
try
|
||
|
: Session_detail(settings)
|
||
|
{}
|
||
|
CATCH_AND_WRAP
|
||
|
|
||
|
|
||
|
/**
|
||
|
Create a session using given session settings.
|
||
|
|
||
|
This constructor forwards arguments to a `SessionSettings` constructor.
|
||
|
Thus all forms of specifying session options are also directly available
|
||
|
in `Session` constructor.
|
||
|
|
||
|
Examples:
|
||
|
~~~~~~
|
||
|
|
||
|
Session from_uri("mysqlx://user:pwd@host:port/db?ssl-mode=disabled");
|
||
|
|
||
|
|
||
|
Session from_options("host", port, "user", "pwd", "db");
|
||
|
|
||
|
Session from_option_list(
|
||
|
SessionOption::USER, "user",
|
||
|
SessionOption::PWD, "pwd",
|
||
|
SessionOption::HOST, "host",
|
||
|
SessionOption::PORT, port,
|
||
|
SessionOption::DB, "db",
|
||
|
SessionOption::SSL_MODE, SSLMode::DISABLED
|
||
|
);
|
||
|
~~~~~~
|
||
|
|
||
|
@see `SessionSettings`
|
||
|
*/
|
||
|
|
||
|
template<typename...T>
|
||
|
Session(T...options)
|
||
|
try
|
||
|
: Session(SessionSettings(options...))
|
||
|
{}CATCH_AND_WRAP
|
||
|
|
||
|
|
||
|
Session(Session &&other)
|
||
|
try
|
||
|
: internal::Session_detail(std::move(other))
|
||
|
{}CATCH_AND_WRAP
|
||
|
|
||
|
|
||
|
Session(Client &client);
|
||
|
|
||
|
/**
|
||
|
Create a new schema.
|
||
|
|
||
|
Returns the created schema. Set `reuse` flag to true to return an already
|
||
|
existing schema with the same name. Otherwise, an attempt to create
|
||
|
a schema which already exists throws an error.
|
||
|
*/
|
||
|
|
||
|
Schema createSchema(const string &name, bool reuse = false)
|
||
|
{
|
||
|
try {
|
||
|
Session_detail::create_schema(name, reuse);
|
||
|
return Schema(*this, name);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Return an object representing a schema with the given name.
|
||
|
|
||
|
To check if the schema actually exists in the database set `check_existence`
|
||
|
flag to true. Otherwise the returned object can refer to a non-existing
|
||
|
schema. An attempt to use such a non-existing schema in a database operation
|
||
|
throws an error.
|
||
|
|
||
|
@note Checking the existence of a schema involves communication with
|
||
|
the server. If `check_exists` is false, on the other hand, no I/O is
|
||
|
involved when creating a `Schema` object.
|
||
|
*/
|
||
|
|
||
|
Schema getSchema(const string &name, bool check_exists = false)
|
||
|
{
|
||
|
Schema sch(*this, name);
|
||
|
if (check_exists && !sch.existsInDatabase())
|
||
|
throw_error("Schema does not exist");
|
||
|
return sch;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get the default schema specified when the session was created.
|
||
|
*/
|
||
|
|
||
|
Schema getDefaultSchema()
|
||
|
{
|
||
|
return Schema(*this, getDefaultSchemaName());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get the name of the default schema specified when the session was created.
|
||
|
*/
|
||
|
|
||
|
string getDefaultSchemaName()
|
||
|
{
|
||
|
try {
|
||
|
return Session_detail::get_default_schema_name();
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Get a list of all database schemas.
|
||
|
|
||
|
The returned value can be stored in a container that holds `Schema` objects,
|
||
|
such as `std::vector<Schema>`.
|
||
|
*/
|
||
|
|
||
|
SchemaList getSchemas()
|
||
|
{
|
||
|
try {
|
||
|
return Schema_src(*this, "%");
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
// TODO: Should we have getSchemaNames() too?
|
||
|
|
||
|
/**
|
||
|
Drop the named schema.
|
||
|
|
||
|
Error is thrown if the schema doesn't exist,
|
||
|
*/
|
||
|
|
||
|
void dropSchema(const string &name)
|
||
|
{
|
||
|
try {
|
||
|
Session_detail::drop_schema(name);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Return an operation which executes an arbitrary SQL statement.
|
||
|
|
||
|
Call `execute()` on the returned operation object to execute the statement
|
||
|
and get an `SqlResult` object representing its results. If the SQL statement
|
||
|
contains `?` placeholders, call `bind()` to define their values
|
||
|
prior to the execution.
|
||
|
|
||
|
@note Errors related to SQL execution are reported when the statement
|
||
|
is executed, not when it is created.
|
||
|
*/
|
||
|
|
||
|
SqlStatement sql(const string &query)
|
||
|
{
|
||
|
try {
|
||
|
return SqlStatement(this, query);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Start a new transaction.
|
||
|
|
||
|
Throws error if previously opened transaction is not closed.
|
||
|
*/
|
||
|
|
||
|
void startTransaction()
|
||
|
{
|
||
|
try {
|
||
|
Session_detail::start_transaction();
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Commit opened transaction, if any.
|
||
|
|
||
|
Does nothing if no transaction was opened. After committing the
|
||
|
transaction is closed.
|
||
|
*/
|
||
|
|
||
|
void commit()
|
||
|
{
|
||
|
try {
|
||
|
Session_detail::commit();
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Roll back opened transaction, if any.
|
||
|
|
||
|
Does nothing if no transaction was opened. Transaction which was
|
||
|
rolled back is closed. To start a new transaction a call to
|
||
|
`startTransaction()` is needed.
|
||
|
*/
|
||
|
|
||
|
void rollback()
|
||
|
{
|
||
|
try {
|
||
|
Session_detail::rollback();
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Roll back opened transaction to specified savepoint.
|
||
|
|
||
|
It rolls back to savepoint, but transaction remains active.
|
||
|
Does nothing if no transaction was opened.
|
||
|
|
||
|
@throws Error If savepoint doesn't exist or is empty.
|
||
|
*/
|
||
|
|
||
|
void rollbackTo(const string &savepoint)
|
||
|
{
|
||
|
try {
|
||
|
if (savepoint.empty())
|
||
|
throw_error("Invalid empty save point name");
|
||
|
Session_detail::rollback(savepoint);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Sets a named transaction savepoint with a name as
|
||
|
identifier.
|
||
|
|
||
|
To use savepoints a transaction has to be started using startTransaction().
|
||
|
|
||
|
@returns string with savepoint name.
|
||
|
|
||
|
@note If the current transaction has a savepoint with the same name,
|
||
|
the old savepoint is deleted and a new one is set.
|
||
|
*/
|
||
|
|
||
|
string setSavepoint(const string &savepoint)
|
||
|
{
|
||
|
try {
|
||
|
if (savepoint.empty())
|
||
|
throw_error("Invalid empty save point name");
|
||
|
return Session_detail::savepoint_set(savepoint);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Creats a transaction savepoint with a generated name as
|
||
|
identifier.
|
||
|
|
||
|
To use savepoints a transaction has to be started using startTransaction().
|
||
|
|
||
|
@returns string with generated savepoint name.
|
||
|
|
||
|
@note If the current transaction has a savepoint with the same name,
|
||
|
the old savepoint is deleted and a new one is set.
|
||
|
*/
|
||
|
|
||
|
string setSavepoint()
|
||
|
{
|
||
|
try {
|
||
|
return Session_detail::savepoint_set();
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Releases savepoint previously added by setSavepoint().
|
||
|
|
||
|
@note Releasing savepoint doesn't affect data.
|
||
|
|
||
|
@throws Error If savepoint doesn't exist.
|
||
|
*/
|
||
|
|
||
|
void releaseSavepoint(const string &savepoint)
|
||
|
{
|
||
|
try {
|
||
|
if (savepoint.empty())
|
||
|
throw_error("Invalid empty save point name");
|
||
|
Session_detail::savepoint_remove(savepoint);
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Close this session.
|
||
|
|
||
|
After the session is closed, any call to other session's methods
|
||
|
throws an error.
|
||
|
*/
|
||
|
|
||
|
void close()
|
||
|
{
|
||
|
try {
|
||
|
Session_detail::close();
|
||
|
}
|
||
|
CATCH_AND_WRAP
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
|
||
|
using internal::Session_detail::m_impl;
|
||
|
|
||
|
public:
|
||
|
|
||
|
friend Schema;
|
||
|
friend Collection;
|
||
|
friend Table;
|
||
|
friend Result;
|
||
|
friend RowResult;
|
||
|
|
||
|
///@cond IGNORE
|
||
|
friend internal::Session_detail;
|
||
|
friend internal::Crud_factory;
|
||
|
friend internal::Result_detail;
|
||
|
template <class Base> friend class internal::Sch_object;
|
||
|
///@endcond
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
Create a client using given client settings.
|
||
|
|
||
|
Client allows the creation of sessions from a session pool.
|
||
|
|
||
|
This constructor forwards arguments to a ClientSettings constructor.
|
||
|
Thus all forms of specifying client options are also directly available
|
||
|
in Client constructor. ClientOptions and SessionOptions can be mixed
|
||
|
when construction Client objects
|
||
|
|
||
|
Examples:
|
||
|
~~~~~~
|
||
|
|
||
|
Client from_uri("mysqlx://user:pwd\@host:port/db?ssl-mode=disabled");
|
||
|
|
||
|
|
||
|
Client from_options("host", port, "user", "pwd", "db");
|
||
|
|
||
|
Client from_option_list(
|
||
|
SessionOption::USER, "user",
|
||
|
SessionOption::PWD, "pwd",
|
||
|
SessionOption::HOST, "host",
|
||
|
SessionOption::PORT, port,
|
||
|
SessionOption::DB, "db",
|
||
|
SessionOption::SSL_MODE, SSLMode::DISABLED
|
||
|
ClientOption::POOLING, true,
|
||
|
ClientOption::POOL_MAX_SIZE, 10,
|
||
|
ClientOption::POOL_QUEUE_TIMEOUT, 1000,
|
||
|
ClientOption::POOL_MAX_IDLE_TIME, 500,
|
||
|
);
|
||
|
~~~~~~
|
||
|
|
||
|
@see ClientSettings
|
||
|
*/
|
||
|
|
||
|
class Client : public internal::Client_detail
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
Client(ClientSettings settings)
|
||
|
try
|
||
|
: Client_detail(settings)
|
||
|
{}
|
||
|
CATCH_AND_WRAP
|
||
|
|
||
|
Client(SessionSettings &settings)
|
||
|
try
|
||
|
: Client_detail(settings)
|
||
|
{}
|
||
|
CATCH_AND_WRAP
|
||
|
|
||
|
template<typename...T>
|
||
|
Client(T...options)
|
||
|
: Client(ClientSettings(options...))
|
||
|
{}
|
||
|
|
||
|
|
||
|
Session getSession()
|
||
|
{
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
Session
|
||
|
*/
|
||
|
|
||
|
inline
|
||
|
Session::Session(Client &client)
|
||
|
try
|
||
|
: internal::Session_detail(client.get_session_pool())
|
||
|
{}CATCH_AND_WRAP
|
||
|
|
||
|
/**
|
||
|
@ingroup devapi
|
||
|
Function to get Session object.
|
||
|
@param p same as needed by Session constructor.
|
||
|
*/
|
||
|
|
||
|
template<class ...P>
|
||
|
Session getSession(P...p)
|
||
|
{
|
||
|
return Session(p...);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@ingroup devapi
|
||
|
Function to get Client object.
|
||
|
@param p same as needed by Client constructor.
|
||
|
*/
|
||
|
|
||
|
template<class ...P>
|
||
|
Client getClient(P...p)
|
||
|
{
|
||
|
return Client(p...);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
Schema class implementation
|
||
|
*/
|
||
|
|
||
|
inline
|
||
|
Schema::Schema(Session &sess, const string &name)
|
||
|
: Schema_detail(sess.m_impl, name)
|
||
|
, m_sess(&sess)
|
||
|
{}
|
||
|
|
||
|
|
||
|
template <class Base>
|
||
|
inline
|
||
|
internal::Sch_object<Base>::Sch_object(const Schema &sch, const string &name)
|
||
|
: Base(sch.getSession().m_impl, name)
|
||
|
, m_schema(sch)
|
||
|
{}
|
||
|
|
||
|
|
||
|
template <class Base>
|
||
|
inline
|
||
|
std::shared_ptr<common::Session_impl>
|
||
|
internal::Sch_object<Base>::get_session()
|
||
|
{
|
||
|
assert(m_schema.m_sess);
|
||
|
return m_schema.m_sess->m_impl;
|
||
|
}
|
||
|
|
||
|
|
||
|
MYSQLX_ABI_END(2,0)
|
||
|
} // mysqlx
|
||
|
|
||
|
#endif
|