Skip to content

Commit

Permalink
Release v1.2.0 (#20)
Browse files Browse the repository at this point in the history
* Added Windows support.
* Added Apple support.
* Added compile-time warnings setup.
* Added `DiscardAll` and `FetchAll` in C++.
* Fixed type checking in C++ `ConstValue::ValueMap` method.
* Fixed pull error handling in the C++ `Client::Execute` method.
* Fixed various compile-time warnings.
* Fixed `mg_session_params_get_client_name` by renaming it to `mg_session_params_get_user_agent` (the one implemented in `src/mgclient.c`).
* Add advanced C++ example.
  • Loading branch information
gitbuda authored Jan 14, 2021
1 parent de9f60a commit dd5dcaa
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
run: |
mkdir build
cd build
cmake -DOPENSSL_ROOT_DIR=/usr/local/Cellar/[email protected]/1.1.1h -DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_TESTING=ON -DBUILD_TESTING_INTEGRATION=ON -DC_WARNINGS_AS_ERRORS=ON -DCPP_WARNINGS_AS_ERRORS=ON ..
cmake -DOPENSSL_ROOT_DIR=/usr/local/Cellar/[email protected]/1.1.1i -DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_TESTING=ON -DBUILD_TESTING_INTEGRATION=ON -DC_WARNINGS_AS_ERRORS=ON -DCPP_WARNINGS_AS_ERRORS=ON ..
make
ctest -E "example|integration"
sudo make install
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

cmake_minimum_required(VERSION 3.8)

project(mgclient VERSION 1.1.0)
project(mgclient VERSION 1.2.0)
# Minor version increase can also mean ABI incompatibility with previous
# versions. IMPORTANT: Take care of the SO version manually.
set(mgclient_SOVERSION 2)
Expand Down
109 changes: 109 additions & 0 deletions examples/advanced.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include <iostream>
#include <numeric>

#include "mgclient.hpp"

void ClearDatabaseData(mg::Client *client) {
if (!client->Execute("MATCH (n) DETACH DELETE n;")) {
std::cerr << "Failed to delete all data from the database." << std::endl;
std::exit(1);
}
client->DiscardAll();
}

int main(int argc, char *argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " [host] [port]\n";
std::exit(1);
}

mg::Client::Init();

{
mg::Client::Params params;
params.host = argv[1];
params.port = static_cast<uint16_t>(atoi(argv[2]));
auto client = mg::Client::Connect(params);
if (!client) {
std::cerr << "Failed to connect." << std::endl;
return 1;
}

ClearDatabaseData(client.get());

if (!client->Execute("CREATE INDEX ON :Person(id);")) {
std::cerr << "Failed to create an index." << std::endl;
return 1;
}
client->DiscardAll();

if (!client->Execute(
"CREATE (:Person:Entrepreneur {id: 0, age: 40, name: 'John', "
"isStudent: false, score: 5.0});")) {
std::cerr << "Failed to add data." << std::endl;
return 1;
}
client->DiscardAll();

if (!client->Execute("MATCH (n) RETURN n;")) {
std::cerr << "Failed to read data." << std::endl;
return 1;
}
if (const auto maybe_data = client->FetchAll()) {
const auto data = *maybe_data;
std::cout << "Number of results: " << data.size() << std::endl;
}

if (!client->Execute("MATCH (n) RETURN n;")) {
std::cerr << "Failed to read data." << std::endl;
return 1;
}
while (const auto maybe_result = client->FetchOne()) {
const auto result = *maybe_result;
if (result.size() < 1) {
continue;
}
const auto value = result[0];
if (value.type() == mg::Value::Type::Node) {
const auto node = value.ValueNode();
auto labels = node.labels();
std::string labels_str = std::accumulate(
labels.begin(), labels.end(), std::string(""),
[](const std::string &acc, const std::string_view value) {
return acc + ":" + std::string(value);
});
const auto props = node.properties();
std::string props_str =
std::accumulate(
props.begin(), props.end(), std::string("{"),
[](const std::string &acc, const auto &key_value) {
const auto &[key, value] = key_value;
std::string value_str;
if (value.type() == mg::Value::Type::Int) {
value_str = std::to_string(value.ValueInt());
} else if (value.type() == mg::Value::Type::String) {
value_str = value.ValueString();
} else if (value.type() == mg::Value::Type::Bool) {
value_str = std::to_string(value.ValueBool());
} else if (value.type() == mg::Value::Type::Double) {
value_str = std::to_string(value.ValueDouble());
} else {
std::cerr
<< "Uncovered converstion from data type to a string"
<< std::endl;
std::exit(1);
}
return acc + " " + std::string(key) + ": " + value_str;
}) +
" }";
std::cout << labels_str << " " << props_str << std::endl;
}
}

ClearDatabaseData(client.get());
}

mg::Client::Finalize();

return 0;
}
47 changes: 24 additions & 23 deletions include/mgclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -739,8 +739,7 @@ MGCLIENT_EXPORT uint32_t mg_map_size(const mg_map *map);
/// Retrieves the key at position \p pos in map \p map.
///
/// \return A pointer to required key. If \p pos is outside of map bounds, \c
/// NULL
/// is returned.
/// NULL is returned.
MGCLIENT_EXPORT const mg_string *mg_map_key_at(const mg_map *, uint32_t pos);

/// Retrieves the value at position \p pos in map \p map.
Expand Down Expand Up @@ -1036,7 +1035,7 @@ MGCLIENT_EXPORT void mg_point_3d_destroy(mg_point_3d *point_3d);
#define MG_SESSION_BAD 2

/// Marks a \ref mg_session which is currently fetching result of a query.
/// Results can be pulled using \ref mg_session_fetch.
/// Results can be fetched using \ref mg_session_fetch.
#define MG_SESSION_FETCHING 3

/// Success code.
Expand Down Expand Up @@ -1233,7 +1232,7 @@ MGCLIENT_EXPORT const char *mg_session_params_get_username(
const mg_session_params *);
MGCLIENT_EXPORT const char *mg_session_params_get_password(
const mg_session_params *);
MGCLIENT_EXPORT const char *mg_session_params_get_client_name(
MGCLIENT_EXPORT const char *mg_session_params_get_user_agent(
const mg_session_params *);
MGCLIENT_EXPORT enum mg_sslmode mg_session_params_get_sslmode(
const mg_session_params *);
Expand Down Expand Up @@ -1287,18 +1286,19 @@ typedef struct mg_result mg_result;
/// next query.
///
/// \param session A \ref mg_session to be used for query
/// execution. \param query Query string. \param params A \ref
/// mg_map containing query parameters. NULL can be
/// supplied instead of an empty parameter map.
/// execution.
/// \param query Query string.
/// \param params A \ref mg_map containing query parameters. NULL
/// can be supplied instead of an empty parameter
/// map.
/// \param columns Names of the columns output by the query
/// execution will be
/// stored in here. This is the same as the value
/// execution will be stored in here. This is the
/// same as the value
/// obtained by \ref mg_result_columns on a pulled
/// \ref mg_result. NULL can be supplied if we're
/// not interested in the columns names.
///
/// \param extra_run_information A \ref mg_map containing extra information for
/// running the statement.
/// running the statement.
/// It can contain the following information:
/// - bookmarks - list of strings containing some
/// kind of bookmark identification
Expand All @@ -1316,9 +1316,9 @@ typedef struct mg_result mg_result;
/// takes place. If no `db` is sent or empty
/// string it implies that it is the default
/// database.
/// \param qid QID for the statement will be stored in here if
/// an Explicit transaction was started. \return Returns 0 if query was
/// submitted for execution successfuly.
/// \param qid QID for the statement will be stored in here
/// if an Explicit transaction was started.
/// \return Returns 0 if query was submitted for execution successfuly.
/// Otherwise, a non-zero error code is returned.
MGCLIENT_EXPORT int mg_session_run(mg_session *session, const char *query,
const mg_map *params,
Expand Down Expand Up @@ -1385,23 +1385,24 @@ MGCLIENT_EXPORT int mg_session_rollback_transaction(mg_session *session,
/// \return On success, 0 or 1 is returned. Exit code 1 means that a new result
/// row was obtained and stored in \p result and its contents may be
/// accessed using \ref mg_result_row. Exit code 0 means that there are
/// now more result rows and that the query execution summary was stored
/// no more result rows and that the query execution summary was stored
/// in \p result. Its contents may be accessed using \ref
/// mg_result_summary. On failure, a non-zero exit code is returned.
MGCLIENT_EXPORT int mg_session_fetch(mg_session *session, mg_result **result);

/// Tries to pull results of a statement.
///
/// \param session A \ref mg_session from which the results should be
/// pulled. \param pull_information A \ref mg_map that contains extra
/// information for pulling the results.
/// pulled.
/// \param pull_information A \ref mg_map that contains extra information for
/// pulling the results.
/// It can contain the following information:
/// - n - how many records to fetch. `n=-1` will fetch
/// all records.
/// - qid - query identification, specifies the result
/// from which statement the results should be pulled .
/// `qid=-1` denotes the last executed statement. This
/// is only for Explicit transactions.
/// - n - how many records to fetch. `n=-1` will fetch
/// all records.
/// - qid - query identification, specifies the result
/// from which statement the results should be pulled.
/// `qid=-1` denotes the last executed statement. This
/// is only for Explicit transactions.
/// \return Returns 0 if the result was pulled successfuly.
/// Otherwise, a non-zero error code is returned.
MGCLIENT_EXPORT int mg_session_pull(mg_session *session,
Expand Down
37 changes: 28 additions & 9 deletions include/mgclient.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@
#include <memory>
#include <optional>

#include "mgclient.h"

#include "mgclient-value.hpp"
#include "mgclient.h"

namespace mg {

Expand All @@ -28,12 +27,12 @@ namespace mg {
class Client {
public:
struct Params {
std::string host;
uint16_t port;
std::string username;
std::string password;
bool use_ssl;
std::string user_agent;
std::string host = "127.0.0.1";
uint16_t port = 7687;
std::string username = "";
std::string password = "";
bool use_ssl = false;
std::string user_agent = "mgclient++/" + std::string(mg_client_version());
};

Client(const Client &) = delete;
Expand Down Expand Up @@ -61,7 +60,8 @@ class Client {
/// \note
/// After executing the statement, the method is blocked until all incoming
/// data (execution results) are handled, i.e. until `FetchOne` method returns
/// `std::nullopt`.
/// `std::nullopt`. Even if the result set is empty, the fetching has to be
/// done/finished to be able to execute another statement.
bool Execute(const std::string &statement);

/// \brief Executes the given Cypher `statement`, supplied with additional
Expand All @@ -79,6 +79,12 @@ class Client {
/// If there is nothing to fetch, `std::nullopt` is returned.
std::optional<std::vector<Value>> FetchOne();

/// \brief Fetches all results and discards them.
void DiscardAll();

/// \brief Fetches all results.
std::optional<std::vector<std::vector<Value>>> FetchAll();

/// \brief Start a transaction.
/// \return true when the transaction was successfully started, false
/// otherwise.
Expand Down Expand Up @@ -189,6 +195,19 @@ inline std::optional<std::vector<Value>> Client::FetchOne() {
return values;
}

inline void Client::DiscardAll() {
while (FetchOne())
;
}

inline std::optional<std::vector<std::vector<Value>>> Client::FetchAll() {
std::vector<std::vector<Value>> data;
while (auto maybe_result = FetchOne()) {
data.emplace_back(std::move(*maybe_result));
}
return data;
}

inline bool Client::BeginTransaction() {
return mg_session_begin_transaction(session_, nullptr) == 0;
}
Expand Down
20 changes: 13 additions & 7 deletions src/mgclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ mg_session_params *mg_session_params_make() {
params->port = 0;
params->username = NULL;
params->password = NULL;
params->user_agent = MG_USER_AGENT_DEFAULT;
params->user_agent = MG_USER_AGENT;
params->sslmode = MG_SSLMODE_DISABLE;
params->sslcert = NULL;
params->sslkey = NULL;
Expand Down Expand Up @@ -767,15 +767,15 @@ int mg_session_run(mg_session *session, const char *query, const mg_map *params,

int mg_session_pull(mg_session *session, const mg_map *pull_information) {
if (session->status == MG_SESSION_BAD) {
mg_session_set_error(session, "bad session");
mg_session_set_error(session, "called pull while bad session");
return MG_ERROR_BAD_CALL;
}
if (session->status == MG_SESSION_READY) {
mg_session_set_error(session, "not executing a query");
mg_session_set_error(session, "called pull while not executing a query");
return MG_ERROR_BAD_CALL;
}
if (session->status == MG_SESSION_FETCHING) {
mg_session_set_error(session, "fetching results from a query");
mg_session_set_error(session, "called pull while still fetching data");
return MG_ERROR_BAD_CALL;
}

Expand Down Expand Up @@ -805,15 +805,15 @@ int mg_session_pull(mg_session *session, const mg_map *pull_information) {

int mg_session_fetch(mg_session *session, mg_result **result) {
if (session->status == MG_SESSION_BAD) {
mg_session_set_error(session, "bad session");
mg_session_set_error(session, "called fetch while bad session");
return MG_ERROR_BAD_CALL;
}
if (session->status == MG_SESSION_READY) {
mg_session_set_error(session, "not executing a query");
mg_session_set_error(session, "called fetch while not executing a query");
return MG_ERROR_BAD_CALL;
}
if (session->status == MG_SESSION_EXECUTING) {
mg_session_set_error(session, "results not pulled");
mg_session_set_error(session, "called fetch without pulling results");
return MG_ERROR_BAD_CALL;
}
assert(session->status == MG_SESSION_FETCHING);
Expand Down Expand Up @@ -1061,13 +1061,19 @@ const mg_list *mg_result_columns(const mg_result *result) {
}

const mg_list *mg_result_row(const mg_result *result) {
if (!result->message) {
return NULL;
}
if (result->message->type != MG_MESSAGE_TYPE_RECORD) {
return NULL;
}
return result->message->record_v->fields;
}

const mg_map *mg_result_summary(const mg_result *result) {
if (!result->message) {
return NULL;
}
if (result->message->type != MG_MESSAGE_TYPE_SUCCESS) {
return NULL;
}
Expand Down
4 changes: 1 addition & 3 deletions src/mgconstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@
#define MG_TINY_SIZE_MAX 15

static const char MG_HANDSHAKE_MAGIC[] = "\x60\x60\xB0\x17";

// TODO(gitbuda): Align this with the mgclient library version.
static const char MG_USER_AGENT_DEFAULT[] = "MemgraphBolt/0.1";
static const char MG_USER_AGENT[] = "mgclient/" MGCLIENT_VERSION;

/// Markers
#define MG_MARKER_NULL 0xC0
Expand Down
4 changes: 4 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,7 @@ add_test(example_basic_c example_basic_c 127.0.0.1 7687 "RETURN 1")
add_executable(example_basic_cpp ${EXAMPLE_DIR}/basic.cpp)
target_link_libraries(example_basic_cpp mgclient-static project_cpp_warnings)
add_test(example_basic_cpp example_basic_cpp 127.0.0.1 7687 "RETURN 1")

add_executable(example_advanced_cpp ${EXAMPLE_DIR}/advanced.cpp)
target_link_libraries(example_advanced_cpp mgclient-static project_cpp_warnings)
add_test(example_advanced_cpp example_advanced_cpp 127.0.0.1 7687)
Loading

0 comments on commit dd5dcaa

Please sign in to comment.