Compare commits
10 Commits
add-gnuplo
...
MG-pmr-pro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8769fa677 | ||
|
|
16245d7ec8 | ||
|
|
0feeae443c | ||
|
|
5338ef5750 | ||
|
|
49ad114366 | ||
|
|
77c8a650fa | ||
|
|
e27485388d | ||
|
|
51b5d81018 | ||
|
|
10cc0c01d2 | ||
|
|
45cd78a435 |
@@ -238,7 +238,7 @@ Value ToBoltValue(const storage::PropertyValue &value) {
|
||||
case storage::PropertyValue::Type::Double:
|
||||
return Value(value.ValueDouble());
|
||||
case storage::PropertyValue::Type::String:
|
||||
return Value(value.ValueString());
|
||||
return Value(std::string{value.ValueString()});
|
||||
case storage::PropertyValue::Type::List: {
|
||||
const auto &values = value.ValueList();
|
||||
std::vector<Value> vec;
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
||||
#include <jemalloc/jemalloc.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <gflags/gflags.h>
|
||||
#include <spdlog/common.h>
|
||||
@@ -44,6 +46,7 @@
|
||||
#include "query/procedure/py_module.hpp"
|
||||
#include "requests/requests.hpp"
|
||||
#include "storage/v2/isolation_level.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "storage/v2/view.hpp"
|
||||
#include "telemetry/telemetry.hpp"
|
||||
@@ -817,7 +820,7 @@ class BoltSession final : public communication::bolt::Session<communication::Inp
|
||||
|
||||
std::pair<std::vector<std::string>, std::optional<int>> Interpret(
|
||||
const std::string &query, const std::map<std::string, communication::bolt::Value> ¶ms) override {
|
||||
std::map<std::string, storage::PropertyValue> params_pv;
|
||||
storage::PropertyValue::TMap params_pv{utils::NewDeleteResource()};
|
||||
for (const auto &kv : params) params_pv.emplace(kv.first, glue::ToPropertyValue(kv.second));
|
||||
const std::string *username{nullptr};
|
||||
if (user_) {
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
#include "query/cypher_query_interpreter.hpp"
|
||||
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/pmr/string.hpp"
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_HIDDEN_bool(query_cost_planner, true, "Use the cost-estimating query planner.");
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -20,7 +23,7 @@ DEFINE_VALIDATED_int32(query_plan_cache_ttl, 60, "Time to live for cached query
|
||||
namespace query {
|
||||
CachedPlan::CachedPlan(std::unique_ptr<LogicalPlan> plan) : plan_(std::move(plan)) {}
|
||||
|
||||
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||
ParsedQuery ParseQuery(const std::string &query_string, const storage::PropertyValue::TMap ¶ms,
|
||||
utils::SkipList<QueryCacheEntry> *cache, utils::SpinLock *antlr_lock,
|
||||
const InterpreterConfig::Query &query_config) {
|
||||
// Strip the query for caching purposes. The process of stripping a query
|
||||
@@ -34,7 +37,7 @@ ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::stri
|
||||
|
||||
// Check that all user-specified parameters are provided.
|
||||
for (const auto ¶m_pair : stripped_query.parameters()) {
|
||||
auto it = params.find(param_pair.second);
|
||||
auto it = params.find(utils::pmr::string{param_pair.second, utils::NewDeleteResource()});
|
||||
|
||||
if (it == params.end()) {
|
||||
throw query::UnprovidedParameterError("Parameter ${} not provided.", param_pair.second);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||
#include "query/frontend/stripped.hpp"
|
||||
#include "query/plan/planner.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
|
||||
@@ -101,7 +102,7 @@ struct PlanCacheEntry {
|
||||
*/
|
||||
struct ParsedQuery {
|
||||
std::string query_string;
|
||||
std::map<std::string, storage::PropertyValue> user_parameters;
|
||||
storage::PropertyValue::TMap user_parameters;
|
||||
Parameters parameters;
|
||||
frontend::StrippedQuery stripped_query;
|
||||
AstStorage ast_storage;
|
||||
@@ -110,7 +111,7 @@ struct ParsedQuery {
|
||||
bool is_cacheable{true};
|
||||
};
|
||||
|
||||
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||
ParsedQuery ParseQuery(const std::string &query_string, const storage::PropertyValue::TMap ¶ms,
|
||||
utils::SkipList<QueryCacheEntry> *cache, utils::SpinLock *antlr_lock,
|
||||
const InterpreterConfig::Query &query_config);
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ template <typename T>
|
||||
void PrintObject(std::ostream *out, const T &arg);
|
||||
|
||||
void PrintObject(std::ostream *out, const std::string &str);
|
||||
void PrintObject(std::ostream *out, const utils::pmr::string &str);
|
||||
|
||||
void PrintObject(std::ostream *out, Aggregation::Op op);
|
||||
|
||||
@@ -95,9 +96,15 @@ void PrintObject(std::ostream *out, const storage::PropertyValue &value);
|
||||
template <typename T>
|
||||
void PrintObject(std::ostream *out, const std::vector<T> &vec);
|
||||
|
||||
template <typename T>
|
||||
void PrintObject(std::ostream *out, const utils::pmr::vector<T> &vec);
|
||||
|
||||
template <typename K, typename V>
|
||||
void PrintObject(std::ostream *out, const std::map<K, V> &map);
|
||||
|
||||
template <typename K, typename V>
|
||||
void PrintObject(std::ostream *out, const utils::pmr::map<K, V> &map);
|
||||
|
||||
template <typename T>
|
||||
void PrintObject(std::ostream *out, const T &arg) {
|
||||
static_assert(!std::is_convertible<T, Expression *>::value,
|
||||
@@ -109,6 +116,7 @@ void PrintObject(std::ostream *out, const T &arg) {
|
||||
}
|
||||
|
||||
void PrintObject(std::ostream *out, const std::string &str) { *out << utils::Escape(str); }
|
||||
void PrintObject(std::ostream *out, const utils::pmr::string &str) { *out << utils::Escape(str); }
|
||||
|
||||
void PrintObject(std::ostream *out, Aggregation::Op op) { *out << Aggregation::OpToString(op); }
|
||||
|
||||
@@ -165,6 +173,13 @@ void PrintObject(std::ostream *out, const std::vector<T> &vec) {
|
||||
*out << "]";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PrintObject(std::ostream *out, const utils::pmr::vector<T> &vec) {
|
||||
*out << "[";
|
||||
utils::PrintIterable(*out, vec, ", ", [](auto &stream, const auto &item) { PrintObject(&stream, item); });
|
||||
*out << "]";
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
void PrintObject(std::ostream *out, const std::map<K, V> &map) {
|
||||
*out << "{";
|
||||
@@ -176,6 +191,17 @@ void PrintObject(std::ostream *out, const std::map<K, V> &map) {
|
||||
*out << "}";
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
void PrintObject(std::ostream *out, const utils::pmr::map<K, V> &map) {
|
||||
*out << "{";
|
||||
utils::PrintIterable(*out, map, ", ", [](auto &stream, const auto &item) {
|
||||
PrintObject(&stream, item.first);
|
||||
stream << ": ";
|
||||
PrintObject(&stream, item.second);
|
||||
});
|
||||
*out << "}";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PrintOperatorArgs(std::ostream *out, const T &arg) {
|
||||
*out << " ";
|
||||
|
||||
@@ -908,7 +908,7 @@ std::optional<plan::ProfilingStatsWithTotalTime> PullPlan::Pull(AnyStream *strea
|
||||
// Also, we want to throw only when the query engine requests more memory and not the storage
|
||||
// so we add the exception to the allocator.
|
||||
// TODO (mferencevic): Tune the parameters accordingly.
|
||||
utils::PoolResource pool_memory(128, 1024, &monotonic_memory);
|
||||
utils::FixedSizePoolResource pool_memory(128, 1024, &monotonic_memory);
|
||||
std::optional<utils::LimitedMemoryResource> maybe_limited_resource;
|
||||
|
||||
if (memory_limit_) {
|
||||
@@ -1503,8 +1503,7 @@ TriggerEventType ToTriggerEventType(const TriggerQuery::EventType event_type) {
|
||||
}
|
||||
}
|
||||
|
||||
Callback CreateTrigger(TriggerQuery *trigger_query,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
Callback CreateTrigger(TriggerQuery *trigger_query, const storage::PropertyValue::TMap &user_parameters,
|
||||
InterpreterContext *interpreter_context, DbAccessor *dba, std::optional<std::string> owner) {
|
||||
return {
|
||||
{},
|
||||
@@ -1554,7 +1553,7 @@ Callback ShowTriggers(InterpreterContext *interpreter_context) {
|
||||
|
||||
PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications, InterpreterContext *interpreter_context,
|
||||
DbAccessor *dba, const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
DbAccessor *dba, const storage::PropertyValue::TMap &user_parameters,
|
||||
const std::string *username) {
|
||||
if (in_explicit_transaction) {
|
||||
throw TriggerModificationInMulticommandTxException();
|
||||
@@ -1604,8 +1603,7 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, const bool in_explic
|
||||
|
||||
PreparedQuery PrepareStreamQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications, InterpreterContext *interpreter_context,
|
||||
DbAccessor *dba,
|
||||
const std::map<std::string, storage::PropertyValue> & /*user_parameters*/,
|
||||
DbAccessor *dba, const storage::PropertyValue::TMap & /*user_parameters*/,
|
||||
const std::string *username) {
|
||||
if (in_explicit_transaction) {
|
||||
throw StreamQueryInMulticommandTxException();
|
||||
@@ -2033,7 +2031,7 @@ void Interpreter::RollbackTransaction() {
|
||||
}
|
||||
|
||||
Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||
const storage::PropertyValue::TMap ¶ms,
|
||||
const std::string *username) {
|
||||
if (!in_explicit_transaction_) {
|
||||
query_executions_.clear();
|
||||
|
||||
@@ -220,7 +220,7 @@ class Interpreter final {
|
||||
*
|
||||
* @throw query::QueryException
|
||||
*/
|
||||
PrepareResult Prepare(const std::string &query, const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||
PrepareResult Prepare(const std::string &query, const storage::PropertyValue::TMap ¶ms,
|
||||
const std::string *username);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1520,7 +1520,7 @@ storage::PropertyValue ToPropertyValue(const mgp_map &map) {
|
||||
storage::PropertyValue result{std::map<std::string, storage::PropertyValue>{}};
|
||||
auto &result_map = result.ValueMap();
|
||||
for (const auto &[key, value] : map.items) {
|
||||
result_map.insert_or_assign(std::string{key}, ToPropertyValue(value));
|
||||
result_map.insert_or_assign(utils::pmr::string{key}, ToPropertyValue(value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "query/serialization/property_value.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
|
||||
namespace query::serialization {
|
||||
|
||||
@@ -46,7 +47,7 @@ nlohmann::json SerializePropertyValue(const storage::PropertyValue &property_val
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json SerializePropertyValueVector(const std::vector<storage::PropertyValue> &values) {
|
||||
nlohmann::json SerializePropertyValueVector(const storage::PropertyValue::TVector &values) {
|
||||
nlohmann::json array = nlohmann::json::array();
|
||||
for (const auto &value : values) {
|
||||
array.push_back(SerializePropertyValue(value));
|
||||
@@ -54,6 +55,18 @@ nlohmann::json SerializePropertyValueVector(const std::vector<storage::PropertyV
|
||||
return array;
|
||||
}
|
||||
|
||||
nlohmann::json SerializePropertyValueMap(const storage::PropertyValue::TMap ¶meters) {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data.emplace("type", static_cast<uint64_t>(ObjectType::MAP));
|
||||
data.emplace("value", nlohmann::json::object());
|
||||
|
||||
for (const auto &[key, value] : parameters) {
|
||||
data["value"][std::string{key}] = SerializePropertyValue(value);
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
nlohmann::json SerializePropertyValueMap(const std::map<std::string, storage::PropertyValue> ¶meters) {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data.emplace("type", static_cast<uint64_t>(ObjectType::MAP));
|
||||
@@ -112,9 +125,9 @@ std::vector<storage::PropertyValue> DeserializePropertyValueList(const nlohmann:
|
||||
return property_values;
|
||||
}
|
||||
|
||||
std::map<std::string, storage::PropertyValue> DeserializePropertyValueMap(const nlohmann::json::object_t &data) {
|
||||
storage::PropertyValue::TMap DeserializePropertyValueMap(const nlohmann::json::object_t &data) {
|
||||
MG_ASSERT(data.at("type").get<ObjectType>() == ObjectType::MAP, "Invalid map serialization");
|
||||
std::map<std::string, storage::PropertyValue> property_values;
|
||||
storage::PropertyValue::TMap property_values{utils::NewDeleteResource()};
|
||||
|
||||
const nlohmann::json::object_t &values = data.at("value");
|
||||
for (const auto &[key, value] : values) {
|
||||
|
||||
@@ -19,7 +19,9 @@ namespace query::serialization {
|
||||
|
||||
nlohmann::json SerializePropertyValue(const storage::PropertyValue &property_value);
|
||||
|
||||
nlohmann::json SerializePropertyValueVector(const std::vector<storage::PropertyValue> &values);
|
||||
nlohmann::json SerializePropertyValueVector(const storage::PropertyValue::TVector &values);
|
||||
|
||||
nlohmann::json SerializePropertyValueMap(const storage::PropertyValue::TMap ¶meters);
|
||||
|
||||
nlohmann::json SerializePropertyValueMap(const std::map<std::string, storage::PropertyValue> ¶meters);
|
||||
|
||||
@@ -27,6 +29,6 @@ storage::PropertyValue DeserializePropertyValue(const nlohmann::json &data);
|
||||
|
||||
std::vector<storage::PropertyValue> DeserializePropertyValueList(const nlohmann::json::array_t &data);
|
||||
|
||||
std::map<std::string, storage::PropertyValue> DeserializePropertyValueMap(const nlohmann::json::object_t &data);
|
||||
storage::PropertyValue::TMap DeserializePropertyValueMap(const nlohmann::json::object_t &data);
|
||||
|
||||
} // namespace query::serialization
|
||||
|
||||
@@ -523,7 +523,7 @@ Streams::StreamsMap::iterator Streams::CreateConsumer(StreamsMap &map, const std
|
||||
interpreter->Abort();
|
||||
}};
|
||||
|
||||
const static std::map<std::string, storage::PropertyValue> empty_parameters{};
|
||||
const static storage::PropertyValue::TMap empty_parameters{utils::NewDeleteResource()};
|
||||
uint32_t i = 0;
|
||||
while (true) {
|
||||
try {
|
||||
|
||||
@@ -150,8 +150,7 @@ std::vector<std::pair<Identifier, TriggerIdentifierTag>> GetPredefinedIdentifier
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Trigger::Trigger(std::string name, const std::string &query,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
Trigger::Trigger(std::string name, const std::string &query, const storage::PropertyValue::TMap &user_parameters,
|
||||
const TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache,
|
||||
DbAccessor *db_accessor, utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
||||
std::optional<std::string> owner, const query::AuthChecker *auth_checker)
|
||||
@@ -333,11 +332,11 @@ void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache
|
||||
}
|
||||
|
||||
void TriggerStore::AddTrigger(std::string name, const std::string &query,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
TriggerEventType event_type, TriggerPhase phase,
|
||||
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
||||
std::optional<std::string> owner, const query::AuthChecker *auth_checker) {
|
||||
const storage::PropertyValue::TMap &user_parameters, TriggerEventType event_type,
|
||||
TriggerPhase phase, utils::SkipList<QueryCacheEntry> *query_cache,
|
||||
DbAccessor *db_accessor, utils::SpinLock *antlr_lock,
|
||||
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
||||
const query::AuthChecker *auth_checker) {
|
||||
std::unique_lock store_guard{store_lock_};
|
||||
if (storage_.Get(name)) {
|
||||
throw utils::BasicException("Trigger with the same name already exists.");
|
||||
|
||||
@@ -32,11 +32,10 @@
|
||||
|
||||
namespace query {
|
||||
struct Trigger {
|
||||
explicit Trigger(std::string name, const std::string &query,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
||||
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor, utils::SpinLock *antlr_lock,
|
||||
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
||||
const query::AuthChecker *auth_checker);
|
||||
explicit Trigger(std::string name, const std::string &query, const storage::PropertyValue::TMap &user_parameters,
|
||||
TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
||||
std::optional<std::string> owner, const query::AuthChecker *auth_checker);
|
||||
|
||||
void Execute(DbAccessor *dba, utils::MonotonicBufferResource *execution_memory, double max_execution_time_sec,
|
||||
std::atomic<bool> *is_shutting_down, const TriggerContext &context,
|
||||
@@ -84,10 +83,9 @@ struct TriggerStore {
|
||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
||||
const query::AuthChecker *auth_checker);
|
||||
|
||||
void AddTrigger(std::string name, const std::string &query,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
||||
TriggerPhase phase, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
||||
void AddTrigger(std::string name, const std::string &query, const storage::PropertyValue::TMap &user_parameters,
|
||||
TriggerEventType event_type, TriggerPhase phase, utils::SkipList<QueryCacheEntry> *query_cache,
|
||||
DbAccessor *db_accessor, utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
||||
std::optional<std::string> owner, const query::AuthChecker *auth_checker);
|
||||
|
||||
void DropTrigger(const std::string &name);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/fnv.hpp"
|
||||
@@ -278,12 +279,18 @@ TypedValue::operator storage::PropertyValue() const {
|
||||
case TypedValue::Type::Double:
|
||||
return storage::PropertyValue(double_v);
|
||||
case TypedValue::Type::String:
|
||||
return storage::PropertyValue(std::string(string_v));
|
||||
case TypedValue::Type::List:
|
||||
return storage::PropertyValue(std::vector<storage::PropertyValue>(list_v.begin(), list_v.end()));
|
||||
return storage::PropertyValue(string_v, memory_);
|
||||
case TypedValue::Type::List: {
|
||||
storage::PropertyValue::TVector list{memory_};
|
||||
list.reserve(list_v.size());
|
||||
for (const auto &tv : list_v) {
|
||||
list.emplace_back(storage::PropertyValue(tv));
|
||||
}
|
||||
return storage::PropertyValue(std::move(list));
|
||||
}
|
||||
case TypedValue::Type::Map: {
|
||||
std::map<std::string, storage::PropertyValue> map;
|
||||
for (const auto &kv : map_v) map.emplace(kv.first, kv.second);
|
||||
storage::PropertyValue::TMap map{memory_};
|
||||
for (const auto &kv : map_v) map.emplace(kv.first, storage::PropertyValue(kv.second));
|
||||
return storage::PropertyValue(std::move(map));
|
||||
}
|
||||
case Type::Date:
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/pmr/map.hpp"
|
||||
#include "utils/pmr/string.hpp"
|
||||
#include "utils/pmr/vector.hpp"
|
||||
|
||||
namespace storage {
|
||||
|
||||
@@ -48,6 +52,12 @@ class PropertyValue {
|
||||
TemporalData = 7
|
||||
};
|
||||
|
||||
using TString = utils::pmr::string;
|
||||
using TVector = utils::pmr::vector<PropertyValue>;
|
||||
using TMap = utils::pmr::map<utils::pmr::string, PropertyValue>;
|
||||
|
||||
using allocator_type = utils::Allocator<PropertyValue>;
|
||||
|
||||
static bool AreComparableTypes(Type a, Type b) {
|
||||
return (a == b) || (a == Type::Int && b == Type::Double) || (a == Type::Double && b == Type::Int);
|
||||
}
|
||||
@@ -56,45 +66,204 @@ class PropertyValue {
|
||||
PropertyValue() : type_(Type::Null) {}
|
||||
|
||||
// constructors for primitive types
|
||||
explicit PropertyValue(const bool value) : type_(Type::Bool) { bool_v = value; }
|
||||
explicit PropertyValue(const int value) : type_(Type::Int) { int_v = value; }
|
||||
explicit PropertyValue(const int64_t value) : type_(Type::Int) { int_v = value; }
|
||||
explicit PropertyValue(const double value) : type_(Type::Double) { double_v = value; }
|
||||
explicit PropertyValue(const TemporalData value) : type_{Type::TemporalData} { temporal_data_v = value; }
|
||||
explicit PropertyValue(const bool value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: type_(Type::Bool), memory_{memory} {
|
||||
bool_v = value;
|
||||
}
|
||||
explicit PropertyValue(const int value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: type_(Type::Int), memory_{memory} {
|
||||
int_v = value;
|
||||
}
|
||||
explicit PropertyValue(const int64_t value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: type_(Type::Int), memory_{memory} {
|
||||
int_v = value;
|
||||
}
|
||||
explicit PropertyValue(const double value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: type_(Type::Double), memory_{memory} {
|
||||
double_v = value;
|
||||
}
|
||||
explicit PropertyValue(const TemporalData value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: type_{Type::TemporalData}, memory_{memory} {
|
||||
temporal_data_v = value;
|
||||
}
|
||||
|
||||
// copy constructors for non-primitive types
|
||||
/// @throw std::bad_alloc
|
||||
explicit PropertyValue(const std::string &value) : type_(Type::String) { new (&string_v) std::string(value); }
|
||||
explicit PropertyValue(const std::string &value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: type_(Type::String), memory_{memory} {
|
||||
new (&string_v) TString(value, memory);
|
||||
}
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error if length of value exceeds
|
||||
/// std::string::max_length().
|
||||
explicit PropertyValue(const char *value) : type_(Type::String) { new (&string_v) std::string(value); }
|
||||
/// @throw std::bad_alloc
|
||||
explicit PropertyValue(const std::vector<PropertyValue> &value) : type_(Type::List) {
|
||||
new (&list_v) std::vector<PropertyValue>(value);
|
||||
}
|
||||
/// @throw std::bad_alloc
|
||||
explicit PropertyValue(const std::map<std::string, PropertyValue> &value) : type_(Type::Map) {
|
||||
new (&map_v) std::map<std::string, PropertyValue>(value);
|
||||
explicit PropertyValue(const char *value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: type_(Type::String), memory_{memory} {
|
||||
new (&string_v) TString(value, memory);
|
||||
}
|
||||
|
||||
// move constructors for non-primitive types
|
||||
explicit PropertyValue(std::string &&value) noexcept : type_(Type::String) {
|
||||
new (&string_v) std::string(std::move(value));
|
||||
/**
|
||||
* Construct a copy of other.
|
||||
* utils::MemoryResource is obtained by calling
|
||||
* std::allocator_traits<>::
|
||||
* select_on_container_copy_construction(other.get_allocator()).
|
||||
* Since we use utils::Allocator, which does not propagate, this means that
|
||||
* memory_ will be the default utils::NewDeleteResource().
|
||||
*/
|
||||
explicit PropertyValue(const TString &other)
|
||||
: PropertyValue(other,
|
||||
std::allocator_traits<utils::Allocator<PropertyValue>>::select_on_container_copy_construction(
|
||||
other.get_allocator())
|
||||
.GetMemoryResource()) {}
|
||||
|
||||
/** Construct a copy using the given utils::MemoryResource */
|
||||
PropertyValue(const TString &other, utils::MemoryResource *memory) : type_(Type::String), memory_{memory} {
|
||||
new (&string_v) TString(other, memory_);
|
||||
}
|
||||
explicit PropertyValue(std::vector<PropertyValue> &&value) noexcept : type_(Type::List) {
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(value));
|
||||
|
||||
/** Construct a copy using the given utils::MemoryResource */
|
||||
explicit PropertyValue(const std::vector<PropertyValue> &value,
|
||||
utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: type_(Type::List), memory_(memory) {
|
||||
new (&list_v) TVector(memory_);
|
||||
list_v.reserve(value.size());
|
||||
list_v.assign(value.begin(), value.end());
|
||||
}
|
||||
explicit PropertyValue(std::map<std::string, PropertyValue> &&value) noexcept : type_(Type::Map) {
|
||||
new (&map_v) std::map<std::string, PropertyValue>(std::move(value));
|
||||
|
||||
/**
|
||||
* Construct a copy of other.
|
||||
* utils::MemoryResource is obtained by calling
|
||||
* std::allocator_traits<>::
|
||||
* select_on_container_copy_construction(other.get_allocator()).
|
||||
* Since we use utils::Allocator, which does not propagate, this means that
|
||||
* memory_ will be the default utils::NewDeleteResource().
|
||||
*/
|
||||
explicit PropertyValue(const TVector &other)
|
||||
: PropertyValue(other,
|
||||
std::allocator_traits<utils::Allocator<PropertyValue>>::select_on_container_copy_construction(
|
||||
other.get_allocator())
|
||||
.GetMemoryResource()) {}
|
||||
|
||||
/** Construct a copy using the given utils::MemoryResource */
|
||||
PropertyValue(const TVector &value, utils::MemoryResource *memory) : type_(Type::List), memory_(memory) {
|
||||
new (&list_v) TVector(value, memory_);
|
||||
}
|
||||
|
||||
/** Construct a copy using the given utils::MemoryResource */
|
||||
explicit PropertyValue(const std::map<std::string, PropertyValue> &value,
|
||||
utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: type_(Type::Map), memory_{memory} {
|
||||
new (&map_v) TMap(memory_);
|
||||
for (const auto &kv : value) map_v.emplace(kv.first, kv.second);
|
||||
}
|
||||
/**
|
||||
* Construct a copy of other.
|
||||
* utils::MemoryResource is obtained by calling
|
||||
* std::allocator_traits<>::
|
||||
* select_on_container_copy_construction(other.get_allocator()).
|
||||
* Since we use utils::Allocator, which does not propagate, this means that
|
||||
* memory_ will be the default utils::NewDeleteResource().
|
||||
*/
|
||||
explicit PropertyValue(const TMap &other)
|
||||
: PropertyValue(other,
|
||||
std::allocator_traits<utils::Allocator<PropertyValue>>::select_on_container_copy_construction(
|
||||
other.get_allocator())
|
||||
.GetMemoryResource()) {}
|
||||
|
||||
/** Construct a copy using the given utils::MemoryResource */
|
||||
PropertyValue(const TMap &value, utils::MemoryResource *memory) : type_(Type::Map), memory_{memory} {
|
||||
new (&map_v) TMap(value, memory_);
|
||||
}
|
||||
/**
|
||||
* Construct with the value of other.
|
||||
* utils::MemoryResource is obtained from other. After the move, other will be
|
||||
* left in unspecified state.
|
||||
*/
|
||||
explicit PropertyValue(TString &&other) noexcept
|
||||
: PropertyValue(std::move(other), other.get_allocator().GetMemoryResource()) {}
|
||||
|
||||
/**
|
||||
* Construct with the value of other and use the given MemoryResource
|
||||
* After the move, other will be left in unspecified state.
|
||||
*/
|
||||
PropertyValue(TString &&other, utils::MemoryResource *memory) : type_(Type::String), memory_{memory} {
|
||||
new (&string_v) TString(std::move(other), memory_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an element-wise move using default utils::NewDeleteResource().
|
||||
* Other will be not be empty, though elements may be Null.
|
||||
*/
|
||||
explicit PropertyValue(std::vector<PropertyValue> &&other)
|
||||
: PropertyValue(std::move(other), utils::NewDeleteResource()) {}
|
||||
|
||||
/**
|
||||
* Perform an element-wise move of the other and use the given MemoryResource.
|
||||
* Other will be not be left empty, though elements may be Null.
|
||||
*/
|
||||
PropertyValue(std::vector<PropertyValue> &&other, utils::MemoryResource *memory)
|
||||
: type_(Type::List), memory_{memory} {
|
||||
new (&list_v) TVector(memory_);
|
||||
list_v.reserve(other.size());
|
||||
// std::vector<PropertyValue> has std::allocator and there's no move
|
||||
// constructor for std::vector using different allocator types. Since
|
||||
// std::allocator is not propagated to elements, it is possible that some
|
||||
// PropertyValue elements have a MemoryResource that is the same as the one we
|
||||
// are given. In such a case we would like to move those PropertyValue
|
||||
// instances, so we use move_iterator.
|
||||
list_v.assign(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()));
|
||||
}
|
||||
/**
|
||||
* Construct with the value of other.
|
||||
* utils::MemoryResource is obtained from other. After the move, other will be
|
||||
* left empty.
|
||||
*/
|
||||
explicit PropertyValue(TVector &&other) noexcept
|
||||
: PropertyValue(std::move(other), other.get_allocator().GetMemoryResource()) {}
|
||||
|
||||
/**
|
||||
* Construct with the value of other and use the given MemoryResource.
|
||||
* If `other.get_allocator() != *memory`, this call will perform an
|
||||
* element-wise move and other is not guaranteed to be empty.
|
||||
*/
|
||||
PropertyValue(TVector &&other, utils::MemoryResource *memory) : type_(Type::List), memory_{memory} {
|
||||
new (&list_v) TVector(std::move(other), memory_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an element-wise move using default utils::NewDeleteResource().
|
||||
* Other will not be left empty, i.e. keys will exist but their values may
|
||||
* be Null.
|
||||
*/
|
||||
explicit PropertyValue(std::map<std::string, PropertyValue> &&other)
|
||||
: PropertyValue(std::move(other), utils::NewDeleteResource()) {}
|
||||
|
||||
/**
|
||||
* Perform an element-wise move using the given MemoryResource.
|
||||
* Other will not be left empty, i.e. keys will exist but their values may
|
||||
* be Null.
|
||||
*/
|
||||
PropertyValue(std::map<std::string, PropertyValue> &&other, utils::MemoryResource *memory)
|
||||
: type_(Type::Map), memory_{memory} {
|
||||
new (&map_v) TMap(memory_);
|
||||
for (auto &kv : other) map_v.emplace(kv.first, std::move(kv.second));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with the value of other.
|
||||
* utils::MemoryResource is obtained from other. After the move, other will be
|
||||
* left empty.
|
||||
*/
|
||||
explicit PropertyValue(TMap &&other) noexcept
|
||||
: PropertyValue(std::move(other), other.get_allocator().GetMemoryResource()) {}
|
||||
|
||||
// copy constructor
|
||||
/// @throw std::bad_alloc
|
||||
PropertyValue(const PropertyValue &other);
|
||||
PropertyValue(const PropertyValue &other, utils::MemoryResource *memory);
|
||||
|
||||
// move constructor
|
||||
PropertyValue(PropertyValue &&other) noexcept;
|
||||
PropertyValue(PropertyValue &&other, utils::MemoryResource *memory) noexcept;
|
||||
|
||||
// copy assignment
|
||||
/// @throw std::bad_alloc
|
||||
@@ -153,7 +322,7 @@ class PropertyValue {
|
||||
|
||||
// const value getters for non-primitive types
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
const std::string &ValueString() const {
|
||||
const TString &ValueString() const {
|
||||
if (type_ != Type::String) {
|
||||
throw PropertyValueException("The value isn't a string!");
|
||||
}
|
||||
@@ -161,7 +330,7 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
const std::vector<PropertyValue> &ValueList() const {
|
||||
const TVector &ValueList() const {
|
||||
if (type_ != Type::List) {
|
||||
throw PropertyValueException("The value isn't a list!");
|
||||
}
|
||||
@@ -169,7 +338,7 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
const std::map<std::string, PropertyValue> &ValueMap() const {
|
||||
const TMap &ValueMap() const {
|
||||
if (type_ != Type::Map) {
|
||||
throw PropertyValueException("The value isn't a map!");
|
||||
}
|
||||
@@ -178,7 +347,7 @@ class PropertyValue {
|
||||
|
||||
// reference value getters for non-primitive types
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
std::string &ValueString() {
|
||||
TString &ValueString() {
|
||||
if (type_ != Type::String) {
|
||||
throw PropertyValueException("The value isn't a string!");
|
||||
}
|
||||
@@ -186,7 +355,7 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
std::vector<PropertyValue> &ValueList() {
|
||||
TVector &ValueList() {
|
||||
if (type_ != Type::List) {
|
||||
throw PropertyValueException("The value isn't a list!");
|
||||
}
|
||||
@@ -194,7 +363,7 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
std::map<std::string, PropertyValue> &ValueMap() {
|
||||
TMap &ValueMap() {
|
||||
if (type_ != Type::Map) {
|
||||
throw PropertyValueException("The value isn't a map!");
|
||||
}
|
||||
@@ -208,13 +377,14 @@ class PropertyValue {
|
||||
bool bool_v;
|
||||
int64_t int_v;
|
||||
double double_v;
|
||||
std::string string_v;
|
||||
std::vector<PropertyValue> list_v;
|
||||
std::map<std::string, PropertyValue> map_v;
|
||||
TString string_v;
|
||||
TVector list_v;
|
||||
TMap map_v;
|
||||
TemporalData temporal_data_v;
|
||||
};
|
||||
|
||||
Type type_;
|
||||
utils::MemoryResource *memory_{utils::NewDeleteResource()};
|
||||
};
|
||||
|
||||
// stream output
|
||||
@@ -329,8 +499,14 @@ inline bool operator<(const PropertyValue &first, const PropertyValue &second) n
|
||||
return first.ValueTemporalData() < second.ValueTemporalData();
|
||||
}
|
||||
}
|
||||
inline PropertyValue::PropertyValue(const PropertyValue &other)
|
||||
: PropertyValue(
|
||||
other,
|
||||
std::allocator_traits<utils::Allocator<PropertyValue>>::select_on_container_copy_construction(other.memory_)
|
||||
.GetMemoryResource()) {}
|
||||
|
||||
inline PropertyValue::PropertyValue(const PropertyValue &other) : type_(other.type_) {
|
||||
inline PropertyValue::PropertyValue(const PropertyValue &other, utils::MemoryResource *memory)
|
||||
: type_(other.type_), memory_{memory} {
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
return;
|
||||
@@ -344,13 +520,13 @@ inline PropertyValue::PropertyValue(const PropertyValue &other) : type_(other.ty
|
||||
this->double_v = other.double_v;
|
||||
return;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
new (&string_v) TString(other.string_v, memory_);
|
||||
return;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(other.list_v);
|
||||
new (&list_v) TVector(other.list_v, memory_);
|
||||
return;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
new (&map_v) TMap(other.map_v, memory_);
|
||||
return;
|
||||
case Type::TemporalData:
|
||||
this->temporal_data_v = other.temporal_data_v;
|
||||
@@ -358,7 +534,10 @@ inline PropertyValue::PropertyValue(const PropertyValue &other) : type_(other.ty
|
||||
}
|
||||
}
|
||||
|
||||
inline PropertyValue::PropertyValue(PropertyValue &&other) noexcept : type_(other.type_) {
|
||||
inline PropertyValue::PropertyValue(PropertyValue &&other) noexcept : PropertyValue(std::move(other), other.memory_) {}
|
||||
|
||||
inline PropertyValue::PropertyValue(PropertyValue &&other, utils::MemoryResource *memory) noexcept
|
||||
: type_(other.type_), memory_{memory} {
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
break;
|
||||
@@ -372,13 +551,13 @@ inline PropertyValue::PropertyValue(PropertyValue &&other) noexcept : type_(othe
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(std::move(other.string_v));
|
||||
new (&string_v) TString(std::move(other.string_v), memory_);
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(other.list_v));
|
||||
new (&list_v) TVector(std::move(other.list_v), memory_);
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
new (&map_v) TMap(std::move(other.map_v), memory_);
|
||||
break;
|
||||
case Type::TemporalData:
|
||||
this->temporal_data_v = other.temporal_data_v;
|
||||
@@ -409,13 +588,13 @@ inline PropertyValue &PropertyValue::operator=(const PropertyValue &other) {
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
new (&string_v) TString(other.string_v, memory_);
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(other.list_v);
|
||||
new (&list_v) TVector(other.list_v, memory_);
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
new (&map_v) TMap(other.map_v, memory_);
|
||||
break;
|
||||
case Type::TemporalData:
|
||||
this->temporal_data_v = other.temporal_data_v;
|
||||
@@ -444,13 +623,13 @@ inline PropertyValue &PropertyValue::operator=(PropertyValue &&other) noexcept {
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(std::move(other.string_v));
|
||||
new (&string_v) TString(std::move(other.string_v), memory_);
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(other.list_v));
|
||||
new (&list_v) TVector(std::move(other.list_v), memory_);
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
new (&map_v) TMap(std::move(other.map_v), memory_);
|
||||
break;
|
||||
case Type::TemporalData:
|
||||
this->temporal_data_v = other.temporal_data_v;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
@@ -164,8 +165,9 @@ void *Pool::Allocate() {
|
||||
--chunk->blocks_available;
|
||||
return available_block;
|
||||
};
|
||||
if (last_alloc_chunk_ && last_alloc_chunk_->blocks_available > 0U)
|
||||
if (last_alloc_chunk_ && last_alloc_chunk_->blocks_available > 0U) {
|
||||
return allocate_block_from_chunk(last_alloc_chunk_);
|
||||
}
|
||||
// Find a Chunk with available memory.
|
||||
for (auto &chunk : chunks_) {
|
||||
if (chunk.blocks_available > 0U) {
|
||||
@@ -347,4 +349,141 @@ void PoolResource::Release() {
|
||||
|
||||
// PoolResource END
|
||||
|
||||
namespace impl {
|
||||
namespace {
|
||||
constexpr size_t kInitialChunkSize = 1;
|
||||
} // namespace
|
||||
FixedSizePool::FixedSizePool(const size_t block_size, const size_t max_blocks_per_chunk_,
|
||||
utils::MemoryResource *upstream)
|
||||
: block_size_{block_size},
|
||||
max_blocks_per_chunk_{max_blocks_per_chunk_},
|
||||
chunk_size_{kInitialChunkSize},
|
||||
upstream_{upstream} {
|
||||
internal_block_size_ = RoundUp(block_size, alignof(std::max_align_t));
|
||||
}
|
||||
|
||||
void *FixedSizePool::Allocate() {
|
||||
if (begin_ == end_) {
|
||||
if (free_list_) {
|
||||
Link *p = free_list_;
|
||||
free_list_ = p->next;
|
||||
return p;
|
||||
}
|
||||
|
||||
Replenish();
|
||||
}
|
||||
|
||||
auto *p = begin_;
|
||||
begin_ += internal_block_size_;
|
||||
return p;
|
||||
}
|
||||
|
||||
void FixedSizePool::Deallocate(void *p) {
|
||||
static_cast<Link *>(p)->next = free_list_;
|
||||
free_list_ = static_cast<Link *>(p);
|
||||
}
|
||||
|
||||
void FixedSizePool::Release() {
|
||||
chunk_list_.Release(upstream_);
|
||||
|
||||
chunk_size_ = kInitialChunkSize;
|
||||
begin_ = nullptr;
|
||||
end_ = nullptr;
|
||||
free_list_ = nullptr;
|
||||
}
|
||||
|
||||
FixedSizePool::~FixedSizePool() { Release(); }
|
||||
|
||||
void FixedSizePool::Replenish() {
|
||||
begin_ = static_cast<std::byte *>(chunk_list_.Allocate(chunk_size_ * internal_block_size_, upstream_));
|
||||
end_ = begin_ + chunk_size_ * internal_block_size_;
|
||||
|
||||
// TODO (antonio2368): Add more growth strategies
|
||||
const auto next_chunk_size = chunk_size_ * 2;
|
||||
if (next_chunk_size <= max_blocks_per_chunk_) {
|
||||
chunk_size_ = next_chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
namespace {
|
||||
inline uint64_t CeilLog2(const uint64_t x) { return utils::Log2(x) + !utils::IsPow2(x); }
|
||||
} // namespace
|
||||
|
||||
FixedSizePoolResource::FixedSizePoolResource(const size_t max_blocks_per_chunk, const size_t max_block_size,
|
||||
utils::MemoryResource *upstream)
|
||||
: upstream_{upstream} {
|
||||
max_block_size_ = min_block_size_;
|
||||
|
||||
num_pools_ = CeilLog2(max_block_size) - CeilLog2(min_block_size_) + 1;
|
||||
MG_ASSERT(num_pools_ > 0, "Invalid max size of the block.");
|
||||
|
||||
auto allocator = Allocator<impl::FixedSizePool>(upstream_);
|
||||
pools_ = allocator.allocate(num_pools_);
|
||||
|
||||
for (size_t i = 0; i < num_pools_; ++i) {
|
||||
allocator.construct(pools_ + i, max_block_size_, max_blocks_per_chunk, upstream);
|
||||
MG_ASSERT(max_block_size_ <= std::numeric_limits<size_t>::max() / 2);
|
||||
max_block_size_ *= 2;
|
||||
}
|
||||
|
||||
max_block_size_ /= 2;
|
||||
}
|
||||
|
||||
void *FixedSizePoolResource::DoAllocate(const size_t bytes, const size_t alignment) {
|
||||
MG_ASSERT(bytes > 0);
|
||||
void *ret{nullptr};
|
||||
if (bytes <= max_block_size_) {
|
||||
auto *pool = FindPool(bytes);
|
||||
ret = pool->Allocate();
|
||||
} else {
|
||||
// add to the list of allocated chunks
|
||||
ret = large_blocks_.Allocate(bytes, upstream_);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void FixedSizePoolResource::DoDeallocate(void *p, const size_t bytes, const size_t alignment) {
|
||||
auto *pool = FindPool(bytes);
|
||||
|
||||
if (pool == nullptr) {
|
||||
large_blocks_.Deallocate(p, upstream_);
|
||||
} else {
|
||||
pool->Deallocate(p);
|
||||
}
|
||||
}
|
||||
|
||||
impl::FixedSizePool *FixedSizePoolResource::FindPool(const size_t size) const {
|
||||
if (size > max_block_size_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *pool = pools_;
|
||||
while (size > pool->BlockSize()) {
|
||||
++pool;
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
void FixedSizePoolResource::Release() {
|
||||
for (size_t i = 0; i < num_pools_; ++i) {
|
||||
pools_[i].Release();
|
||||
}
|
||||
|
||||
large_blocks_.Release(upstream_);
|
||||
}
|
||||
|
||||
FixedSizePoolResource::~FixedSizePoolResource() {
|
||||
Release();
|
||||
|
||||
for (size_t i = 0; i < num_pools_; ++i) {
|
||||
pools_[i].~FixedSizePool();
|
||||
}
|
||||
|
||||
upstream_->Deallocate(pools_, sizeof(impl::FixedSizePool) * num_pools_, alignof(impl::FixedSizePool));
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
@@ -23,6 +24,9 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#ifndef NDEBUG
|
||||
#include <iostream>
|
||||
#endif
|
||||
// Although <memory_resource> is in C++17, gcc libstdc++ still needs to
|
||||
// implement it fully. It should be available in the next major release
|
||||
// version, i.e. gcc 9.x.
|
||||
@@ -564,6 +568,217 @@ class LimitedMemoryResource final : public utils::MemoryResource {
|
||||
bool DoIsEqual(const MemoryResource &other) const noexcept override { return this == &other; }
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
// https://stackoverflow.com/a/9194117
|
||||
// if y is power 2 we can do (x + y - 1) & ~(y - 1);
|
||||
// https://stackoverflow.com/a/14561794
|
||||
constexpr size_t RoundUp(const size_t x, const size_t y) {
|
||||
MG_ASSERT(y >= 1);
|
||||
return (x + y - 1) / y * y;
|
||||
}
|
||||
|
||||
struct LinkedListNode {
|
||||
LinkedListNode *next;
|
||||
LinkedListNode *previous;
|
||||
};
|
||||
|
||||
struct BlockLinkedListHeaderBase : public LinkedListNode {
|
||||
size_t size;
|
||||
|
||||
using NodeType = LinkedListNode;
|
||||
};
|
||||
|
||||
struct SingleLinkedListNode {
|
||||
SingleLinkedListNode *next;
|
||||
};
|
||||
|
||||
struct BlockSingleLinkedListHeaderBase : public SingleLinkedListNode {
|
||||
size_t size;
|
||||
|
||||
using NodeType = SingleLinkedListNode;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept BlockLinkedListHeader = std::is_base_of_v<BlockLinkedListHeaderBase, T>;
|
||||
|
||||
template <typename T>
|
||||
concept BlockSingleLinkedListHeader = std::is_base_of_v<BlockSingleLinkedListHeaderBase, T>;
|
||||
|
||||
template <typename T>
|
||||
concept BlockListHeader = BlockLinkedListHeader<T> || BlockSingleLinkedListHeader<T>;
|
||||
|
||||
template <BlockListHeader THeader>
|
||||
class BlockList {
|
||||
using NodeType = typename THeader::NodeType;
|
||||
|
||||
public:
|
||||
static const size_t header_size = RoundUp(sizeof(THeader), alignof(std::max_align_t));
|
||||
|
||||
explicit BlockList() {
|
||||
// create a circular linked list
|
||||
list_.next = &list_;
|
||||
|
||||
if constexpr (BlockLinkedListHeader<THeader>) {
|
||||
list_.previous = &list_;
|
||||
}
|
||||
}
|
||||
|
||||
void *Allocate(const size_t bytes, utils::MemoryResource *rsrc) {
|
||||
if (bytes > static_cast<size_t>(-1) - header_size) {
|
||||
throw utils::BadAlloc("Requested too many bytes to allocate");
|
||||
}
|
||||
|
||||
void *p = rsrc->Allocate(bytes + header_size);
|
||||
auto *node = new (p) THeader;
|
||||
node->size = bytes + header_size;
|
||||
|
||||
auto *next = list_.next;
|
||||
node->next = next;
|
||||
list_.next = node;
|
||||
|
||||
if constexpr (BlockLinkedListHeader<THeader>) {
|
||||
node->previous = &list_;
|
||||
next->previous = node;
|
||||
}
|
||||
|
||||
return static_cast<std::byte *>(p) + header_size;
|
||||
}
|
||||
|
||||
template <typename T = THeader>
|
||||
requires BlockLinkedListHeader<T> void Deallocate(void *p, utils::MemoryResource *rsrc) {
|
||||
auto *header = static_cast<THeader *>(static_cast<void *>(static_cast<std::byte *>(p) - header_size));
|
||||
auto *next = header->next;
|
||||
auto *previous = header->previous;
|
||||
previous->next = next;
|
||||
next->previous = previous;
|
||||
|
||||
const size_t size = header->size;
|
||||
header->~THeader();
|
||||
rsrc->Deallocate(header, size, alignof(std::max_align_t));
|
||||
}
|
||||
|
||||
void Release(utils::MemoryResource *rsrc) {
|
||||
NodeType *node = list_.next;
|
||||
while (node != &list_) {
|
||||
auto *header = static_cast<THeader *>(node);
|
||||
node = node->next;
|
||||
const size_t size = header->size;
|
||||
header->~THeader();
|
||||
rsrc->Deallocate(header, size, alignof(std::max_align_t));
|
||||
}
|
||||
|
||||
// create a circular linked list
|
||||
list_.next = &list_;
|
||||
|
||||
if constexpr (BlockLinkedListHeader<THeader>) {
|
||||
list_.previous = &list_;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NodeType list_;
|
||||
};
|
||||
|
||||
// https://github.com/bloomberg/bde/blob/master/groups/bdl/bdlma/bdlma_pool.h
|
||||
class FixedSizePool final {
|
||||
public:
|
||||
FixedSizePool(size_t block_size, size_t max_blocks_per_chunk, utils::MemoryResource *upstream);
|
||||
|
||||
FixedSizePool(const FixedSizePool &) = delete;
|
||||
FixedSizePool &operator=(const FixedSizePool &) = delete;
|
||||
|
||||
FixedSizePool(FixedSizePool &&) = default;
|
||||
FixedSizePool &operator=(FixedSizePool &&) = default;
|
||||
|
||||
void *Allocate();
|
||||
void Deallocate(void *p);
|
||||
|
||||
void Release();
|
||||
|
||||
auto BlockSize() const { return block_size_; }
|
||||
|
||||
~FixedSizePool();
|
||||
|
||||
private:
|
||||
struct Link {
|
||||
Link *next;
|
||||
};
|
||||
|
||||
std::byte *begin_{nullptr};
|
||||
std::byte *end_{nullptr};
|
||||
|
||||
Link *free_list_{nullptr};
|
||||
|
||||
size_t internal_block_size_;
|
||||
size_t block_size_;
|
||||
size_t max_blocks_per_chunk_;
|
||||
size_t chunk_size_;
|
||||
|
||||
BlockList<BlockSingleLinkedListHeaderBase> chunk_list_;
|
||||
|
||||
utils::MemoryResource *upstream_;
|
||||
|
||||
void Replenish();
|
||||
};
|
||||
} // namespace impl
|
||||
|
||||
// https://github.com/bloomberg/bde/blob/master/groups/bdl/bdlma/bdlma_multipool.h
|
||||
class FixedSizePoolResource : public MemoryResource {
|
||||
public:
|
||||
explicit FixedSizePoolResource(size_t max_blocks_per_chunk, size_t max_block_size,
|
||||
utils::MemoryResource *upstream = utils::NewDeleteResource());
|
||||
|
||||
void Release();
|
||||
|
||||
FixedSizePoolResource(const FixedSizePoolResource &) = delete;
|
||||
FixedSizePoolResource &operator=(const FixedSizePoolResource &) = delete;
|
||||
|
||||
FixedSizePoolResource(FixedSizePoolResource &&) = default;
|
||||
FixedSizePoolResource &operator=(FixedSizePoolResource &&) = default;
|
||||
|
||||
~FixedSizePoolResource() override;
|
||||
|
||||
private:
|
||||
impl::FixedSizePool *FindPool(size_t size) const;
|
||||
|
||||
void *DoAllocate(size_t bytes, size_t alignment) override;
|
||||
void DoDeallocate(void *p, size_t bytes, size_t alignment) override;
|
||||
|
||||
bool DoIsEqual(const MemoryResource &other) const noexcept override { return this == &other; }
|
||||
|
||||
impl::FixedSizePool *pools_{nullptr};
|
||||
size_t max_block_size_;
|
||||
size_t min_block_size_{8};
|
||||
size_t num_pools_{0};
|
||||
|
||||
impl::BlockList<impl::BlockLinkedListHeaderBase> large_blocks_;
|
||||
|
||||
utils::MemoryResource *upstream_;
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
class PrintMemoryResource : public MemoryResource {
|
||||
public:
|
||||
explicit PrintMemoryResource(utils::MemoryResource *memory) : memory_(memory) {}
|
||||
|
||||
private:
|
||||
void *DoAllocate(size_t bytes, size_t alignment) override {
|
||||
std::cout << "Allocating " << bytes << std::endl;
|
||||
return memory_->Allocate(bytes, alignment);
|
||||
}
|
||||
|
||||
void DoDeallocate(void *p, size_t bytes, size_t alignment) override {
|
||||
std::cout << "Deallocating " << bytes << std::endl;
|
||||
return memory_->Deallocate(p, bytes, alignment);
|
||||
}
|
||||
|
||||
bool DoIsEqual(const utils::MemoryResource &other) const noexcept override { return memory_->IsEqual(other); }
|
||||
|
||||
utils::MemoryResource *memory_;
|
||||
};
|
||||
#endif
|
||||
|
||||
// Allocate memory with the OutOfMemoryException enabled if the requested size
|
||||
// puts total allocated amount over the limit.
|
||||
class ResourceWithOutOfMemoryException : public MemoryResource {
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
#include "query/interpreter.hpp"
|
||||
#include "query/typed_value.hpp"
|
||||
#include "storage/v2/isolation_level.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
|
||||
class ExpansionBenchFixture : public benchmark::Fixture {
|
||||
protected:
|
||||
@@ -64,7 +66,7 @@ BENCHMARK_DEFINE_F(ExpansionBenchFixture, Match)(benchmark::State &state) {
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
ResultStreamFaker results(&*db);
|
||||
interpreter->Prepare(query, {}, nullptr);
|
||||
interpreter->Prepare(query, storage::PropertyValue::TMap{utils::NewDeleteResource()}, nullptr);
|
||||
interpreter->PullAll(&results);
|
||||
}
|
||||
}
|
||||
@@ -79,7 +81,7 @@ BENCHMARK_DEFINE_F(ExpansionBenchFixture, Expand)(benchmark::State &state) {
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
ResultStreamFaker results(&*db);
|
||||
interpreter->Prepare(query, {}, nullptr);
|
||||
interpreter->Prepare(query, storage::PropertyValue::TMap{utils::NewDeleteResource()}, nullptr);
|
||||
interpreter->PullAll(&results);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <mgclient.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "storage/v2/isolation_level.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/on_scope_exit.hpp"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@@ -35,7 +36,8 @@ int main(int argc, char *argv[]) {
|
||||
query::Interpreter interpreter{&interpreter_context};
|
||||
|
||||
ResultStreamFaker stream(&db);
|
||||
auto [header, _, qid] = interpreter.Prepare(argv[1], {}, nullptr);
|
||||
auto [header, _, qid] =
|
||||
interpreter.Prepare(argv[1], storage::PropertyValue::TMap{utils::NewDeleteResource()}, nullptr);
|
||||
stream.Header(header);
|
||||
auto summary = interpreter.PullAll(&stream);
|
||||
stream.Summary(summary);
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "utils/csv_parsing.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -40,6 +41,14 @@ auto ToEdgeList(const communication::bolt::Value &v) {
|
||||
return list;
|
||||
};
|
||||
|
||||
auto ToPropertyValueMap(const std::map<std::string, storage::PropertyValue> &map) {
|
||||
storage::PropertyValue::TMap property_value_map{utils::NewDeleteResource()};
|
||||
for (const auto &[key, value] : map) {
|
||||
property_value_map.emplace(utils::pmr::string{key, utils::NewDeleteResource()}, std::move(value));
|
||||
}
|
||||
return property_value_map;
|
||||
}
|
||||
|
||||
struct InterpreterFaker {
|
||||
InterpreterFaker(storage::Storage *db, const query::InterpreterConfig config,
|
||||
const std::filesystem::path &data_directory)
|
||||
@@ -50,7 +59,7 @@ struct InterpreterFaker {
|
||||
auto Prepare(const std::string &query, const std::map<std::string, storage::PropertyValue> ¶ms = {}) {
|
||||
ResultStreamFaker stream(interpreter_context.db);
|
||||
|
||||
const auto [header, _, qid] = interpreter.Prepare(query, params, nullptr);
|
||||
const auto [header, _, qid] = interpreter.Prepare(query, ToPropertyValueMap(params), nullptr);
|
||||
stream.Header(header);
|
||||
return std::make_pair(std::move(stream), qid);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, Null) {
|
||||
@@ -423,7 +424,7 @@ TEST(PropertyValue, MapCopy) {
|
||||
{
|
||||
const auto &ret = pv.ValueMap();
|
||||
ASSERT_EQ(ret.size(), 1);
|
||||
ASSERT_EQ(ret.at("nandare").ValueInt(), 123);
|
||||
ASSERT_EQ(ret.at({"nandare", utils::NewDeleteResource()}).ValueInt(), 123);
|
||||
}
|
||||
|
||||
const auto &cpv = pv;
|
||||
@@ -436,7 +437,7 @@ TEST(PropertyValue, MapCopy) {
|
||||
{
|
||||
const auto &ret = cpv.ValueMap();
|
||||
ASSERT_EQ(ret.size(), 1);
|
||||
ASSERT_EQ(ret.at("nandare").ValueInt(), 123);
|
||||
ASSERT_EQ(ret.at({"nandare", utils::NewDeleteResource()}).ValueInt(), 123);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -476,7 +477,7 @@ TEST(PropertyValue, MapMove) {
|
||||
{
|
||||
const auto &ret = pv.ValueMap();
|
||||
ASSERT_EQ(ret.size(), 1);
|
||||
ASSERT_EQ(ret.at("nandare").ValueInt(), 123);
|
||||
ASSERT_EQ(ret.at({"nandare", utils::NewDeleteResource()}).ValueInt(), 123);
|
||||
}
|
||||
|
||||
const auto &cpv = pv;
|
||||
@@ -489,7 +490,7 @@ TEST(PropertyValue, MapMove) {
|
||||
{
|
||||
const auto &ret = cpv.ValueMap();
|
||||
ASSERT_EQ(ret.size(), 1);
|
||||
ASSERT_EQ(ret.at("nandare").ValueInt(), 123);
|
||||
ASSERT_EQ(ret.at({"nandare", utils::NewDeleteResource()}).ValueInt(), 123);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
#include "query/dump.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "query/typed_value.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/temporal.hpp"
|
||||
|
||||
const char *kPropertyId = "property_id";
|
||||
@@ -207,7 +209,7 @@ auto Execute(storage::Storage *db, const std::string &query) {
|
||||
query::Interpreter interpreter(&context);
|
||||
ResultStreamFaker stream(db);
|
||||
|
||||
auto [header, _, qid] = interpreter.Prepare(query, {}, nullptr);
|
||||
auto [header, _, qid] = interpreter.Prepare(query, storage::PropertyValue::TMap{utils::NewDeleteResource()}, nullptr);
|
||||
stream.Header(header);
|
||||
auto summary = interpreter.PullAll(&stream);
|
||||
stream.Summary(summary);
|
||||
@@ -399,7 +401,12 @@ TEST(DumpTest, PropertyValue) {
|
||||
auto bool_value = storage::PropertyValue(true);
|
||||
auto double_value = storage::PropertyValue(-1.2);
|
||||
auto str_value = storage::PropertyValue("hello 'world'");
|
||||
auto map_value = storage::PropertyValue({{"prop 1", int_value}, {"prop`2`", bool_value}});
|
||||
storage::PropertyValue map_value = storage::PropertyValue(std::invoke([&] {
|
||||
storage::PropertyValue::TMap map_value{utils::NewDeleteResource()};
|
||||
map_value.emplace(storage::PropertyValue::TString{"prop 1", utils::NewDeleteResource()}, int_value);
|
||||
map_value.emplace(storage::PropertyValue::TString{"prop 2", utils::NewDeleteResource()}, bool_value);
|
||||
return map_value;
|
||||
}));
|
||||
auto dt = storage::PropertyValue(
|
||||
storage::TemporalData(storage::TemporalType::Date, utils::Date({1994, 12, 7}).MicrosecondsSinceEpoch()));
|
||||
auto lt = storage::PropertyValue(storage::TemporalData(
|
||||
@@ -409,7 +416,8 @@ TEST(DumpTest, PropertyValue) {
|
||||
utils::LocalDateTime({1994, 12, 7}, {14, 10, 44, 99, 99}).MicrosecondsSinceEpoch()));
|
||||
auto dur = storage::PropertyValue(
|
||||
storage::TemporalData(storage::TemporalType::Duration, utils::Duration({3, 4, 5, 6, 10, 11}).microseconds));
|
||||
auto list_value = storage::PropertyValue({map_value, null_value, double_value, dt, lt, ldt, dur});
|
||||
auto list_value = storage::PropertyValue(storage::PropertyValue::TVector{
|
||||
{map_value, null_value, double_value, dt, lt, ldt, dur}, utils::NewDeleteResource()});
|
||||
CreateVertex(&dba, {}, {{"p1", list_value}, {"p2", str_value}}, false);
|
||||
ASSERT_FALSE(dba.Commit().HasError());
|
||||
}
|
||||
@@ -751,7 +759,8 @@ class StatefulInterpreter {
|
||||
auto Execute(const std::string &query) {
|
||||
ResultStreamFaker stream(db_);
|
||||
|
||||
auto [header, _, qid] = interpreter_.Prepare(query, {}, nullptr);
|
||||
auto [header, _, qid] =
|
||||
interpreter_.Prepare(query, storage::PropertyValue::TMap{utils::NewDeleteResource()}, nullptr);
|
||||
stream.Header(header);
|
||||
auto summary = interpreter_.PullAll(&stream);
|
||||
stream.Summary(summary);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "communication/result_stream_faker.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
|
||||
DECLARE_bool(query_cost_planner);
|
||||
|
||||
@@ -53,7 +54,8 @@ class QueryExecution : public testing::Test {
|
||||
auto Execute(const std::string &query) {
|
||||
ResultStreamFaker stream(&*db_);
|
||||
|
||||
auto [header, _, qid] = interpreter_->Prepare(query, {}, nullptr);
|
||||
auto [header, _, qid] =
|
||||
interpreter_->Prepare(query, storage::PropertyValue::TMap{utils::NewDeleteResource()}, nullptr);
|
||||
stream.Header(header);
|
||||
auto summary = interpreter_->PullAll(&stream);
|
||||
stream.Summary(summary);
|
||||
|
||||
@@ -877,11 +877,15 @@ TEST_F(TriggerStoreTest, Restore) {
|
||||
const auto event_type = query::TriggerEventType::VERTEX_CREATE;
|
||||
const std::string owner{"owner"};
|
||||
store->AddTrigger(trigger_name_before, trigger_statement,
|
||||
std::map<std::string, storage::PropertyValue>{{"parameter", storage::PropertyValue{1}}}, event_type,
|
||||
query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
storage::PropertyValue::TMap{
|
||||
{{utils::pmr::string{"parameter", utils::NewDeleteResource()}, storage::PropertyValue{1}}},
|
||||
utils::NewDeleteResource()},
|
||||
event_type, query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||
store->AddTrigger(trigger_name_after, trigger_statement,
|
||||
std::map<std::string, storage::PropertyValue>{{"parameter", storage::PropertyValue{"value"}}},
|
||||
storage::PropertyValue::TMap{
|
||||
{{utils::pmr::string{"parameter", utils::NewDeleteResource()}, storage::PropertyValue{1}}},
|
||||
utils::NewDeleteResource()},
|
||||
event_type, query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, {owner}, &auth_checker);
|
||||
|
||||
@@ -932,34 +936,38 @@ TEST_F(TriggerStoreTest, AddTrigger) {
|
||||
query::TriggerStore store{testing_directory};
|
||||
|
||||
// Invalid query in statements
|
||||
ASSERT_THROW(store.AddTrigger("trigger", "RETUR 1", {}, query::TriggerEventType::VERTEX_CREATE,
|
||||
query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
utils::BasicException);
|
||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN createdEdges", {}, query::TriggerEventType::VERTEX_CREATE,
|
||||
query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
ASSERT_THROW(store.AddTrigger("trigger", "RETUR 1", storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::VERTEX_CREATE, query::TriggerPhase::BEFORE_COMMIT, &ast_cache,
|
||||
&*dba, &antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
utils::BasicException);
|
||||
ASSERT_THROW(
|
||||
store.AddTrigger("trigger", "RETURN createdEdges", storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::VERTEX_CREATE, query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||
&antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
utils::BasicException);
|
||||
|
||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN $parameter", {}, query::TriggerEventType::VERTEX_CREATE,
|
||||
query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
utils::BasicException);
|
||||
ASSERT_THROW(
|
||||
store.AddTrigger("trigger", "RETURN $parameter", storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::VERTEX_CREATE, query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||
&antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
utils::BasicException);
|
||||
|
||||
ASSERT_NO_THROW(
|
||||
store.AddTrigger("trigger", "RETURN $parameter",
|
||||
std::map<std::string, storage::PropertyValue>{{"parameter", storage::PropertyValue{1}}},
|
||||
storage::PropertyValue::TMap{
|
||||
{{utils::pmr::string{"parameter", utils::NewDeleteResource()}, storage::PropertyValue{1}}},
|
||||
utils::NewDeleteResource()},
|
||||
query::TriggerEventType::VERTEX_CREATE, query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||
&antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &auth_checker));
|
||||
|
||||
// Inserting with the same name
|
||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, query::TriggerEventType::VERTEX_CREATE,
|
||||
query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::VERTEX_CREATE, query::TriggerPhase::BEFORE_COMMIT, &ast_cache,
|
||||
&*dba, &antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
utils::BasicException);
|
||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, query::TriggerEventType::VERTEX_CREATE,
|
||||
query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::VERTEX_CREATE, query::TriggerPhase::AFTER_COMMIT, &ast_cache,
|
||||
&*dba, &antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||
utils::BasicException);
|
||||
|
||||
ASSERT_EQ(store.GetTriggerInfo().size(), 1);
|
||||
@@ -973,9 +981,9 @@ TEST_F(TriggerStoreTest, DropTrigger) {
|
||||
ASSERT_THROW(store.DropTrigger("Unknown"), utils::BasicException);
|
||||
|
||||
const auto *trigger_name = "trigger";
|
||||
store.AddTrigger(trigger_name, "RETURN 1", {}, query::TriggerEventType::VERTEX_CREATE,
|
||||
query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||
store.AddTrigger(trigger_name, "RETURN 1", storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::VERTEX_CREATE, query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||
&antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||
|
||||
ASSERT_THROW(store.DropTrigger("Unknown"), utils::BasicException);
|
||||
ASSERT_NO_THROW(store.DropTrigger(trigger_name));
|
||||
@@ -986,9 +994,9 @@ TEST_F(TriggerStoreTest, TriggerInfo) {
|
||||
query::TriggerStore store{testing_directory};
|
||||
|
||||
std::vector<query::TriggerStore::TriggerInfo> expected_info;
|
||||
store.AddTrigger("trigger", "RETURN 1", {}, query::TriggerEventType::VERTEX_CREATE,
|
||||
query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||
store.AddTrigger("trigger", "RETURN 1", storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::VERTEX_CREATE, query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||
&antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||
expected_info.push_back(
|
||||
{"trigger", "RETURN 1", query::TriggerEventType::VERTEX_CREATE, query::TriggerPhase::BEFORE_COMMIT});
|
||||
|
||||
@@ -1006,9 +1014,9 @@ TEST_F(TriggerStoreTest, TriggerInfo) {
|
||||
|
||||
check_trigger_info();
|
||||
|
||||
store.AddTrigger("edge_update_trigger", "RETURN 1", {}, query::TriggerEventType::EDGE_UPDATE,
|
||||
query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock, query::InterpreterConfig::Query{},
|
||||
std::nullopt, &auth_checker);
|
||||
store.AddTrigger("edge_update_trigger", "RETURN 1", storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::EDGE_UPDATE, query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
|
||||
&antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||
expected_info.push_back(
|
||||
{"edge_update_trigger", "RETURN 1", query::TriggerEventType::EDGE_UPDATE, query::TriggerPhase::AFTER_COMMIT});
|
||||
|
||||
@@ -1119,7 +1127,8 @@ TEST_F(TriggerStoreTest, AnyTriggerAllKeywords) {
|
||||
SCOPED_TRACE(query::TriggerEventTypeToString(event_type));
|
||||
for (const auto keyword : keywords) {
|
||||
SCOPED_TRACE(keyword);
|
||||
EXPECT_NO_THROW(store.AddTrigger(trigger_name, fmt::format("RETURN {}", keyword), {}, event_type,
|
||||
EXPECT_NO_THROW(store.AddTrigger(trigger_name, fmt::format("RETURN {}", keyword),
|
||||
storage::PropertyValue::TMap{utils::NewDeleteResource()}, event_type,
|
||||
query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &auth_checker));
|
||||
store.DropTrigger(trigger_name);
|
||||
@@ -1143,12 +1152,13 @@ TEST_F(TriggerStoreTest, AuthCheckerUsage) {
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_checker, IsUserAuthorized(owner, ElementsAre(Privilege::CREATE))).Times(1).WillOnce(Return(true));
|
||||
|
||||
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_1", "CREATE (n:VERTEX) RETURN n", {},
|
||||
query::TriggerEventType::EDGE_UPDATE, query::TriggerPhase::AFTER_COMMIT, &ast_cache,
|
||||
&*dba, &antlr_lock, query::InterpreterConfig::Query{}, std::nullopt,
|
||||
&mock_checker));
|
||||
ASSERT_NO_THROW(store->AddTrigger(
|
||||
"successfull_trigger_1", "CREATE (n:VERTEX) RETURN n", storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::EDGE_UPDATE, query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
||||
query::InterpreterConfig::Query{}, std::nullopt, &mock_checker));
|
||||
|
||||
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_2", "CREATE (n:VERTEX) RETURN n", {},
|
||||
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_2", "CREATE (n:VERTEX) RETURN n",
|
||||
storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::EDGE_UPDATE, query::TriggerPhase::AFTER_COMMIT, &ast_cache,
|
||||
&*dba, &antlr_lock, query::InterpreterConfig::Query{}, owner, &mock_checker));
|
||||
|
||||
@@ -1156,7 +1166,8 @@ TEST_F(TriggerStoreTest, AuthCheckerUsage) {
|
||||
.Times(1)
|
||||
.WillOnce(Return(false));
|
||||
|
||||
ASSERT_THROW(store->AddTrigger("unprivileged_trigger", "MATCH (n:VERTEX) RETURN n", {},
|
||||
ASSERT_THROW(store->AddTrigger("unprivileged_trigger", "MATCH (n:VERTEX) RETURN n",
|
||||
storage::PropertyValue::TMap{utils::NewDeleteResource()},
|
||||
query::TriggerEventType::EDGE_UPDATE, query::TriggerPhase::AFTER_COMMIT, &ast_cache,
|
||||
&*dba, &antlr_lock, query::InterpreterConfig::Query{}, std::nullopt, &mock_checker);
|
||||
, utils::BasicException);
|
||||
|
||||
@@ -321,6 +321,46 @@ TEST(PoolResource, BlockDeallocation) {
|
||||
EXPECT_EQ(test_mem.new_count_, 0U);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(FixedSizePoolResource, SmallBlockAllocations) {
|
||||
TestMemory test_mem;
|
||||
const size_t max_blocks_per_chunk = 3U;
|
||||
const size_t max_block_size = 1024U;
|
||||
utils::FixedSizePoolResource mem(max_blocks_per_chunk, max_block_size, &test_mem);
|
||||
std::array<void *, max_block_size> blocks;
|
||||
for (size_t i = 0; i < max_block_size; ++i) {
|
||||
blocks[i] = CheckAllocation(&mem, i + 1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < max_block_size; ++i) {
|
||||
mem.Deallocate(blocks[i], i + 1);
|
||||
}
|
||||
|
||||
mem.Release();
|
||||
CheckAllocation(&mem, 64U);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(FixedSizePoolResource, BigBlockAllocations) {
|
||||
TestMemory test_mem;
|
||||
const size_t max_blocks_per_chunk = 3U;
|
||||
const size_t max_block_size = 1024U;
|
||||
utils::FixedSizePoolResource mem(max_blocks_per_chunk, max_block_size, &test_mem);
|
||||
|
||||
constexpr size_t block_num = 10;
|
||||
std::array<void *, block_num> blocks;
|
||||
for (size_t i = 0; i < block_num; ++i) {
|
||||
blocks[i] = CheckAllocation(&mem, max_block_size * (i + 1));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < block_num; ++i) {
|
||||
mem.Deallocate(blocks[i], max_block_size * (i + 1));
|
||||
}
|
||||
|
||||
mem.Release();
|
||||
CheckAllocation(&mem, max_block_size * block_num);
|
||||
}
|
||||
|
||||
class AllocationTrackingMemory final : public utils::MemoryResource {
|
||||
public:
|
||||
std::vector<size_t> allocated_sizes_;
|
||||
|
||||
Reference in New Issue
Block a user