Compare commits
2 Commits
release/2.
...
update-wor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff739d2dea | ||
|
|
858a79d311 |
2
.github/workflows/diff.yaml
vendored
2
.github/workflows/diff.yaml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
- "**/*.md"
|
||||
- ".clang-format"
|
||||
- "CODEOWNERS"
|
||||
- "licenses/*"
|
||||
- licenses/**
|
||||
|
||||
jobs:
|
||||
community_build:
|
||||
|
||||
34
.github/workflows/package_all.yaml
vendored
34
.github/workflows/package_all.yaml
vendored
@@ -160,23 +160,6 @@ jobs:
|
||||
name: debian-11-platform
|
||||
path: build/output/debian-11/memgraph*.deb
|
||||
|
||||
fedora-36:
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package fedora-36
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: fedora-36
|
||||
path: build/output/fedora-36/memgraph*.rpm
|
||||
|
||||
debian-11-arm:
|
||||
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
|
||||
timeout-minutes: 60
|
||||
@@ -193,20 +176,3 @@ jobs:
|
||||
with:
|
||||
name: debian-11-arm
|
||||
path: build/output/debian-11-arm/memgraph*.deb
|
||||
|
||||
ubuntu-2204-arm:
|
||||
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package ubuntu-22.04-arm
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ubuntu-22.04-arm
|
||||
path: build/output/ubuntu-22.04-arm/memgraph*.deb
|
||||
|
||||
@@ -6,7 +6,7 @@ repos:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.8.0
|
||||
rev: 22.10.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pycqa/isort
|
||||
|
||||
@@ -54,7 +54,7 @@ option(MG_ENTERPRISE "Build Memgraph Enterprise Edition" ON)
|
||||
# Set the current version here to override the automatic version detection. The
|
||||
# version must be specified as `X.Y.Z`. Primarily used when building new patch
|
||||
# versions.
|
||||
set(MEMGRAPH_OVERRIDE_VERSION "2.5.2")
|
||||
set(MEMGRAPH_OVERRIDE_VERSION "")
|
||||
|
||||
# Custom suffix that this version should have. The suffix can be any arbitrary
|
||||
# string. Primarily used when building a version for a specific customer.
|
||||
|
||||
@@ -22,7 +22,7 @@ Build modern, graph-based applications on top of your streaming data in minutes.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/memgraph/memgraph">
|
||||
<img src="https://img.shields.io/github/actions/workflow/status/memgraph/memgraph/release_debian10.yaml?branch=master&label=build%20and%20test&logo=github"/>
|
||||
<img src="https://img.shields.io/github/workflow/status/memgraph/memgraph/Release%20Ubuntu%2020.04/master" alt="build" title="build"/>
|
||||
</a>
|
||||
<a href="https://memgraph.com/docs/" alt="Documentation">
|
||||
<img src="https://img.shields.io/badge/documentation-Memgraph-orange" />
|
||||
|
||||
@@ -26,7 +26,6 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
diffutils
|
||||
libipt libipt-devel # intel
|
||||
patch
|
||||
perl # for openssl
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
@@ -37,6 +36,7 @@ TOOLCHAIN_RUN_DEPS=(
|
||||
readline # for cmake and llvm
|
||||
libffi libxml2 # for llvm
|
||||
openssl-devel
|
||||
perl # for openssl
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
@@ -64,10 +64,6 @@ list() {
|
||||
|
||||
check() {
|
||||
local missing=""
|
||||
# On Fedora yum/dnf and python10 use newer glibc which is not compatible
|
||||
# with ours, so we need to momentarely disable env
|
||||
local OLD_LD_LIBRARY_PATH=${LD_LIBRARY_PATH}
|
||||
LD_LIBRARY_PATH=""
|
||||
for pkg in $1; do
|
||||
if ! dnf list installed "$pkg" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
@@ -77,7 +73,6 @@ check() {
|
||||
echo "MISSING PACKAGES: $missing"
|
||||
exit 1
|
||||
fi
|
||||
LD_LIBRARY_PATH=${OLD_LD_LIBRARY_PATH}
|
||||
}
|
||||
|
||||
install() {
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "ubuntu-22.04"
|
||||
check_architecture "arm64" "aarch64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg # used for archive signature verification
|
||||
tar gzip bzip2 xz-utils unzip # used for archive unpacking
|
||||
zlib1g-dev # zlib library used for all builds
|
||||
libexpat1-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
|
||||
libcurl4-openssl-dev # for cmake
|
||||
libreadline-dev # for cmake and llvm
|
||||
libffi-dev libxml2-dev # for llvm
|
||||
curl # snappy
|
||||
file
|
||||
git # for thrift
|
||||
libgmp-dev # for gdb
|
||||
gperf # for proxygen
|
||||
libssl-dev
|
||||
libedit-dev libpcre3-dev automake bison # for swig
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz-utils # used for archive unpacking
|
||||
zlib1g # zlib library used for all builds
|
||||
libexpat1 libbabeltrace1 liblzma5 python3 # for gdb
|
||||
libcurl4 # for cmake
|
||||
libreadline8 # for cmake and llvm
|
||||
libffi7 libxml2 # for llvm
|
||||
libssl-dev # for libevent
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make pkg-config # build system
|
||||
curl wget # for downloading libs
|
||||
uuid-dev default-jre-headless # required by antlr
|
||||
libreadline-dev # for memgraph console
|
||||
libpython3-dev python3-dev # for query modules
|
||||
libssl-dev
|
||||
libseccomp-dev
|
||||
netcat # tests are using nc to wait for memgraph
|
||||
python3 python3-virtualenv python3-pip # for qa, macro_benchmark and stress tests
|
||||
python3-yaml # for the configuration generator
|
||||
libcurl4-openssl-dev # mg-requests
|
||||
sbcl # for custom Lisp C++ preprocessing
|
||||
doxygen graphviz # source documentation generators
|
||||
mono-runtime mono-mcs zip unzip default-jdk-headless # for driver tests
|
||||
dotnet-sdk-6.0 golang nodejs npm
|
||||
autoconf # for jemalloc code generation
|
||||
libtool # for protobuf code generation
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
check_all_dpkg "$1"
|
||||
}
|
||||
|
||||
install() {
|
||||
cd "$DIR"
|
||||
apt update
|
||||
# If GitHub Actions runner is installed, append LANG to the environment.
|
||||
# Python related tests doesn't work the LANG export.
|
||||
if [ -d "/home/gh/actions-runner" ]; then
|
||||
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
|
||||
else
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
fi
|
||||
apt install -y wget
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == dotnet-sdk-6.0 ]; then
|
||||
if ! dpkg -s dotnet-sdk-6.0 2>/dev/null >/dev/null; then
|
||||
wget -nv https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
|
||||
dpkg -i packages-microsoft-prod.deb
|
||||
apt-get update
|
||||
apt-get install -y apt-transport-https dotnet-sdk-6.0
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
apt install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
@@ -51,19 +51,11 @@ CPPCHECK_VERSION=2.6
|
||||
LLVM_VERSION=13.0.0
|
||||
SWIG_VERSION=4.0.2 # used only for LLVM compilation
|
||||
|
||||
# Set the right env script
|
||||
ENV_SCRIPT="$DIR/../os/$DISTRO.sh"
|
||||
if [[ "$for_arm" = true ]]; then
|
||||
ENV_SCRIPT="$DIR/../os/$DISTRO-arm.sh"
|
||||
fi
|
||||
|
||||
# Check for the toolchain build dependencies.
|
||||
echo "ALL BUILD PACKAGES: $(${ENV_SCRIPT} list TOOLCHAIN_BUILD_DEPS)"
|
||||
${ENV_SCRIPT} check TOOLCHAIN_BUILD_DEPS
|
||||
|
||||
# Check for the toolchain run dependencies.
|
||||
echo "ALL RUN PACKAGES: $(${ENV_SCRIPT} list TOOLCHAIN_RUN_DEPS)"
|
||||
${ENV_SCRIPT} check TOOLCHAIN_RUN_DEPS
|
||||
# Check for the dependencies.
|
||||
echo "ALL BUILD PACKAGES: $($DIR/../os/$DISTRO.sh list TOOLCHAIN_BUILD_DEPS)"
|
||||
$DIR/../os/$DISTRO.sh check TOOLCHAIN_BUILD_DEPS
|
||||
echo "ALL RUN PACKAGES: $($DIR/../os/$DISTRO.sh list TOOLCHAIN_RUN_DEPS)"
|
||||
$DIR/../os/$DISTRO.sh check TOOLCHAIN_RUN_DEPS
|
||||
|
||||
# check installation directory
|
||||
NAME=toolchain-v$TOOLCHAIN_VERSION
|
||||
@@ -387,34 +379,6 @@ if [ ! -f $PREFIX/bin/gdb ]; then
|
||||
--without-babeltrace \
|
||||
--enable-tui \
|
||||
--with-python=python3
|
||||
elif [[ "${DISTRO}" == fedora* ]]; then
|
||||
# Remove readline, gdb does not compile
|
||||
env \
|
||||
CC=gcc \
|
||||
CXX=g++ \
|
||||
CFLAGS="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security" \
|
||||
CXXFLAGS="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security" \
|
||||
CPPFLAGS="-Wdate-time -D_FORTIFY_SOURCE=2 -fPIC" \
|
||||
LDFLAGS="-Wl,-z,relro" \
|
||||
PYTHON="" \
|
||||
../configure \
|
||||
--build=x86_64-linux-gnu \
|
||||
--host=x86_64-linux-gnu \
|
||||
--prefix=$PREFIX \
|
||||
--disable-maintainer-mode \
|
||||
--disable-dependency-tracking \
|
||||
--disable-silent-rules \
|
||||
--disable-gdbtk \
|
||||
--disable-shared \
|
||||
--without-guile \
|
||||
--with-system-gdbinit=$PREFIX/etc/gdb/gdbinit \
|
||||
--with-expat \
|
||||
--with-system-zlib \
|
||||
--with-lzma \
|
||||
--with-babeltrace \
|
||||
--with-intel-pt \
|
||||
--enable-tui \
|
||||
--with-python=python3
|
||||
else
|
||||
# https://buildd.debian.org/status/fetch.php?pkg=gdb&arch=amd64&ver=8.2.1-2&stamp=1550831554&raw=0
|
||||
env \
|
||||
@@ -630,7 +594,7 @@ In order to be able to run all of these tools you should install the following
|
||||
packages:
|
||||
|
||||
\`\`\`
|
||||
$($DIR/../os/$ENV_SCRIPT.sh list TOOLCHAIN_RUN_DEPS)
|
||||
$($DIR/../os/$DISTRO.sh list TOOLCHAIN_RUN_DEPS)
|
||||
\`\`\`
|
||||
|
||||
## Usage
|
||||
@@ -687,7 +651,6 @@ export PS1="($NAME) \$PS1"
|
||||
export LD_LIBRARY_PATH=$PREFIX/lib:$PREFIX/lib64
|
||||
export CXXFLAGS=-isystem\ $PREFIX/include\ \$CXXFLAGS
|
||||
export CFLAGS=-isystem\ $PREFIX/include\ \$CFLAGS
|
||||
export VENV=$PREFIX
|
||||
|
||||
# disable root
|
||||
function su () {
|
||||
@@ -739,7 +702,7 @@ PROXYGEN_SHA256=5360a8ccdfb2f5a6c7b3eed331ec7ab0e2c792d579c6fff499c85c516c11fe14
|
||||
SNAPPY_SHA256=75c1fbb3d618dd3a0483bff0e26d0a92b495bbe5059c8b4f1c962b478b6e06e7
|
||||
SNAPPY_VERSION=1.1.9
|
||||
XZ_VERSION=5.2.5 # for LZMA
|
||||
ZLIB_VERSION=1.2.13
|
||||
ZLIB_VERSION=1.2.12
|
||||
ZSTD_VERSION=1.5.0
|
||||
WANGLE_SHA256=1002e9c32b6f4837f6a760016e3b3e22f3509880ef3eaad191c80dc92655f23f
|
||||
|
||||
@@ -1263,7 +1226,7 @@ popd
|
||||
# create toolchain archive
|
||||
if [ ! -f $NAME-binaries-$DISTRO.tar.gz ]; then
|
||||
DISTRO_FULL_NAME=${DISTRO}
|
||||
if [[ "${DISTRO}" == centos* ]] || [[ "${DISTRO}" == fedora* ]]; then
|
||||
if [[ "${DISTRO}" == centos* ]]; then
|
||||
if [[ "$for_arm" = "true" ]]; then
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-aarch64"
|
||||
else
|
||||
|
||||
@@ -19,16 +19,13 @@ function architecture() {
|
||||
}
|
||||
|
||||
check_architecture() {
|
||||
local ARCH=$(architecture)
|
||||
for arch in "$@"; do
|
||||
if [ "${ARCH}" = "$arch" ]; then
|
||||
if [ "$(architecture)" = "$arch" ]; then
|
||||
echo "The right architecture!"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
echo "Not the right architecture!"
|
||||
echo "Expected: $@"
|
||||
echo "Actual: ${ARCH}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -378,10 +378,6 @@ inline mgp_value *vertex_get_property(mgp_vertex *v, const char *property_name,
|
||||
return MgInvoke<mgp_value *>(mgp_vertex_get_property, v, property_name, memory);
|
||||
}
|
||||
|
||||
inline void vertex_set_property(mgp_vertex *v, const char *property_name, mgp_value *property_value) {
|
||||
MgInvokeVoid(mgp_vertex_set_property, v, property_name, property_value);
|
||||
}
|
||||
|
||||
inline mgp_properties_iterator *vertex_iter_properties(mgp_vertex *v, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_properties_iterator *>(mgp_vertex_iter_properties, v, memory);
|
||||
}
|
||||
@@ -414,10 +410,6 @@ inline mgp_value *edge_get_property(mgp_edge *e, const char *property_name, mgp_
|
||||
return MgInvoke<mgp_value *>(mgp_edge_get_property, e, property_name, memory);
|
||||
}
|
||||
|
||||
inline void edge_set_property(mgp_edge *e, const char *property_name, mgp_value *property_value) {
|
||||
MgInvokeVoid(mgp_edge_set_property, e, property_name, property_value);
|
||||
}
|
||||
|
||||
inline mgp_properties_iterator *edge_iter_properties(mgp_edge *e, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_properties_iterator *>(mgp_edge_iter_properties, e, memory);
|
||||
}
|
||||
|
||||
216
include/mgp.hpp
216
include/mgp.hpp
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -66,12 +66,9 @@ struct MapItem;
|
||||
class Duration;
|
||||
class Value;
|
||||
|
||||
struct StealType {};
|
||||
inline constexpr StealType steal{};
|
||||
|
||||
inline mgp_memory *memory{nullptr};
|
||||
|
||||
/* #region Graph (Id, Graph, Nodes, GraphRelationships, Relationships & Labels) */
|
||||
/* #region Graph (Id, Graph, Nodes, GraphRelationships, Relationships, Properties & Labels) */
|
||||
|
||||
/// Wrapper for int64_t IDs to prevent dangerous implicit conversions.
|
||||
class Id {
|
||||
@@ -162,7 +159,7 @@ class Nodes {
|
||||
|
||||
explicit Iterator(mgp_vertices_iterator *nodes_iterator);
|
||||
|
||||
Iterator(const Iterator &other) noexcept;
|
||||
Iterator(const Iterator &other);
|
||||
Iterator &operator=(const Iterator &other) = delete;
|
||||
|
||||
~Iterator();
|
||||
@@ -210,7 +207,7 @@ class GraphRelationships {
|
||||
|
||||
explicit Iterator(mgp_vertices_iterator *nodes_iterator);
|
||||
|
||||
Iterator(const Iterator &other) noexcept;
|
||||
Iterator(const Iterator &other);
|
||||
Iterator &operator=(const Iterator &other) = delete;
|
||||
|
||||
~Iterator();
|
||||
@@ -256,7 +253,7 @@ class Relationships {
|
||||
|
||||
explicit Iterator(mgp_edges_iterator *relationships_iterator);
|
||||
|
||||
Iterator(const Iterator &other) noexcept;
|
||||
Iterator(const Iterator &other);
|
||||
Iterator &operator=(const Iterator &other) = delete;
|
||||
|
||||
~Iterator();
|
||||
@@ -284,12 +281,46 @@ class Relationships {
|
||||
mgp_edges_iterator *relationships_iterator_ = nullptr;
|
||||
};
|
||||
|
||||
/// @brief View of node properties.
|
||||
class Properties {
|
||||
public:
|
||||
explicit Properties(mgp_properties_iterator *properties_iterator);
|
||||
|
||||
/// @brief Returns the size of the properties map.
|
||||
size_t Size() const;
|
||||
/// @brief Returns whether the properties map is empty.
|
||||
bool Empty() const;
|
||||
|
||||
/// @brief Returns the value associated with the given `key`. If there’s no such value, the behavior is undefined.
|
||||
/// @note Each key-value pair needs to be checked, ensuing O(n) time complexity.
|
||||
Value operator[](const std::string_view key) const;
|
||||
|
||||
std::map<std::string_view, Value>::const_iterator begin() const;
|
||||
std::map<std::string_view, Value>::const_iterator end() const;
|
||||
|
||||
std::map<std::string_view, Value>::const_iterator cbegin() const;
|
||||
std::map<std::string_view, Value>::const_iterator cend() const;
|
||||
|
||||
/// @brief Returns the key-value iterator for the given `key`. If there’s no such pair, returns the end of the
|
||||
/// iterator.
|
||||
/// @note Each key-value pair needs to be checked, ensuing O(n) time complexity.
|
||||
std::map<std::string_view, Value>::const_iterator find(const std::string_view key) const;
|
||||
|
||||
/// @exception std::runtime_error Map contains value(s) of unknown type.
|
||||
bool operator==(const Properties &other) const;
|
||||
/// @exception std::runtime_error Map contains value(s) of unknown type.
|
||||
bool operator!=(const Properties &other) const;
|
||||
|
||||
private:
|
||||
std::map<const std::string_view, Value> property_map_;
|
||||
};
|
||||
|
||||
/// @brief View of node labels.
|
||||
class Labels {
|
||||
public:
|
||||
explicit Labels(mgp_vertex *node_ptr);
|
||||
|
||||
Labels(const Labels &other) noexcept;
|
||||
Labels(const Labels &other);
|
||||
Labels(Labels &&other) noexcept;
|
||||
|
||||
Labels &operator=(const Labels &other) noexcept;
|
||||
@@ -332,7 +363,6 @@ class Labels {
|
||||
private:
|
||||
mgp_vertex *node_ptr_;
|
||||
};
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Types */
|
||||
@@ -367,7 +397,7 @@ class List {
|
||||
/// @brief Creates a List from the given initializer_list.
|
||||
explicit List(const std::initializer_list<Value> list);
|
||||
|
||||
List(const List &other) noexcept;
|
||||
List(const List &other);
|
||||
List(List &&other) noexcept;
|
||||
|
||||
List &operator=(const List &other) noexcept;
|
||||
@@ -459,7 +489,7 @@ class Map {
|
||||
/// @brief Creates a Map from the given initializer_list (map items correspond to initializer list pairs).
|
||||
Map(const std::initializer_list<std::pair<std::string_view, Value>> items);
|
||||
|
||||
Map(const Map &other) noexcept;
|
||||
Map(const Map &other);
|
||||
Map(Map &&other) noexcept;
|
||||
|
||||
Map &operator=(const Map &other) noexcept;
|
||||
@@ -489,7 +519,7 @@ class Map {
|
||||
|
||||
explicit Iterator(mgp_map_items_iterator *map_items_iterator);
|
||||
|
||||
Iterator(const Iterator &other) noexcept;
|
||||
Iterator(const Iterator &other);
|
||||
Iterator &operator=(const Iterator &other) = delete;
|
||||
|
||||
~Iterator();
|
||||
@@ -548,7 +578,7 @@ class Node {
|
||||
/// @brief Creates a Node from the copy of the given @ref mgp_vertex.
|
||||
explicit Node(const mgp_vertex *const_ptr);
|
||||
|
||||
Node(const Node &other) noexcept;
|
||||
Node(const Node &other);
|
||||
Node(Node &&other) noexcept;
|
||||
|
||||
Node &operator=(const Node &other) noexcept;
|
||||
@@ -560,19 +590,16 @@ class Node {
|
||||
mgp::Id Id() const;
|
||||
|
||||
/// @brief Returns an iterable & indexable structure of the node’s labels.
|
||||
mgp::Labels Labels() const;
|
||||
class Labels Labels() const;
|
||||
|
||||
/// @brief Returns whether the node has the given `label`.
|
||||
bool HasLabel(std::string_view label) const;
|
||||
|
||||
/// @brief Returns an std::map of the node’s properties.
|
||||
std::map<std::string, Value> Properties() const;
|
||||
/// @brief Returns an iterable & indexable structure of the node’s properties.
|
||||
class Properties Properties() const;
|
||||
|
||||
/// @brief Sets the chosen property to the given value.
|
||||
void SetProperty(std::string property, Value value);
|
||||
|
||||
/// @brief Retrieves the value of the chosen property.
|
||||
Value GetProperty(const std::string &property) const;
|
||||
/// @brief Returns the value of the node’s `property_name` property.
|
||||
Value operator[](const std::string_view property_name) const;
|
||||
|
||||
/// @brief Returns an iterable structure of the node’s inbound relationships.
|
||||
Relationships InRelationships() const;
|
||||
@@ -608,7 +635,7 @@ class Relationship {
|
||||
/// @brief Creates a Relationship from the copy of the given @ref mgp_edge.
|
||||
explicit Relationship(const mgp_edge *const_ptr);
|
||||
|
||||
Relationship(const Relationship &other) noexcept;
|
||||
Relationship(const Relationship &other);
|
||||
Relationship(Relationship &&other) noexcept;
|
||||
|
||||
Relationship &operator=(const Relationship &other) noexcept;
|
||||
@@ -622,14 +649,11 @@ class Relationship {
|
||||
/// @brief Returns the relationship’s type.
|
||||
std::string_view Type() const;
|
||||
|
||||
/// @brief Returns an std::map of the relationship’s properties.
|
||||
std::map<std::string, Value> Properties() const;
|
||||
/// @brief Returns an iterable & indexable structure of the relationship’s properties.
|
||||
class Properties Properties() const;
|
||||
|
||||
/// @brief Sets the chosen property to the given value.
|
||||
void SetProperty(std::string property, Value value);
|
||||
|
||||
/// @brief Retrieves the value of the chosen property.
|
||||
Value GetProperty(const std::string &property) const;
|
||||
/// @brief Returns the value of the relationship’s `property_name` property.
|
||||
Value operator[](const std::string_view property_name) const;
|
||||
|
||||
/// @brief Returns the relationship’s source node.
|
||||
Node From() const;
|
||||
@@ -664,7 +688,7 @@ class Path {
|
||||
/// @brief Creates a Path starting with the given `start_node`.
|
||||
explicit Path(const Node &start_node);
|
||||
|
||||
Path(const Path &other) noexcept;
|
||||
Path(const Path &other);
|
||||
Path(Path &&other) noexcept;
|
||||
|
||||
Path &operator=(const Path &other) noexcept;
|
||||
@@ -720,7 +744,7 @@ class Date {
|
||||
/// @brief Creates a Date object with the given `year`, `month`, and `day` properties.
|
||||
Date(int year, int month, int day);
|
||||
|
||||
Date(const Date &other) noexcept;
|
||||
Date(const Date &other);
|
||||
Date(Date &&other) noexcept;
|
||||
|
||||
Date &operator=(const Date &other) noexcept;
|
||||
@@ -775,7 +799,7 @@ class LocalTime {
|
||||
/// properties.
|
||||
LocalTime(int hour, int minute, int second, int millisecond, int microsecond);
|
||||
|
||||
LocalTime(const LocalTime &other) noexcept;
|
||||
LocalTime(const LocalTime &other);
|
||||
LocalTime(LocalTime &&other) noexcept;
|
||||
|
||||
LocalTime &operator=(const LocalTime &other) noexcept;
|
||||
@@ -834,7 +858,7 @@ class LocalDateTime {
|
||||
/// `millisecond`, and `microsecond` properties.
|
||||
LocalDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond);
|
||||
|
||||
LocalDateTime(const LocalDateTime &other) noexcept;
|
||||
LocalDateTime(const LocalDateTime &other);
|
||||
LocalDateTime(LocalDateTime &&other) noexcept;
|
||||
|
||||
LocalDateTime &operator=(const LocalDateTime &other) noexcept;
|
||||
@@ -905,7 +929,7 @@ class Duration {
|
||||
/// `microsecond` properties.
|
||||
Duration(double day, double hour, double minute, double second, double millisecond, double microsecond);
|
||||
|
||||
Duration(const Duration &other) noexcept;
|
||||
Duration(const Duration &other);
|
||||
Duration(Duration &&other) noexcept;
|
||||
|
||||
Duration &operator=(const Duration &other) noexcept;
|
||||
@@ -962,8 +986,6 @@ class Value {
|
||||
|
||||
explicit Value(mgp_value *ptr);
|
||||
|
||||
explicit Value(StealType /*steal*/, mgp_value *ptr);
|
||||
|
||||
// Null constructor:
|
||||
explicit Value();
|
||||
|
||||
@@ -1034,7 +1056,7 @@ class Value {
|
||||
/// @note The behavior of accessing `duration` after performing this operation is undefined.
|
||||
explicit Value(Duration &&duration);
|
||||
|
||||
Value(const Value &other) noexcept;
|
||||
Value(const Value &other);
|
||||
Value(Value &&other) noexcept;
|
||||
|
||||
Value &operator=(const Value &other) noexcept;
|
||||
@@ -1682,7 +1704,7 @@ inline Nodes::Iterator::Iterator(mgp_vertices_iterator *nodes_iterator) : nodes_
|
||||
}
|
||||
}
|
||||
|
||||
inline Nodes::Iterator::Iterator(const Iterator &other) noexcept : Iterator(other.nodes_iterator_) {}
|
||||
inline Nodes::Iterator::Iterator(const Iterator &other) : Iterator(other.nodes_iterator_) {}
|
||||
|
||||
inline Nodes::Iterator::~Iterator() {
|
||||
if (nodes_iterator_ != nullptr) {
|
||||
@@ -1773,7 +1795,7 @@ inline GraphRelationships::Iterator::Iterator(mgp_vertices_iterator *nodes_itera
|
||||
}
|
||||
}
|
||||
|
||||
inline GraphRelationships::Iterator::Iterator(const Iterator &other) noexcept : Iterator(other.nodes_iterator_) {}
|
||||
inline GraphRelationships::Iterator::Iterator(const Iterator &other) : Iterator(other.nodes_iterator_) {}
|
||||
|
||||
inline GraphRelationships::Iterator::~Iterator() {
|
||||
if (nodes_iterator_ != nullptr) {
|
||||
@@ -1792,12 +1814,10 @@ inline GraphRelationships::Iterator &GraphRelationships::Iterator::operator++()
|
||||
if (out_relationships_iterator_ != nullptr) {
|
||||
auto next = mgp::edges_iterator_next(out_relationships_iterator_);
|
||||
|
||||
if (next != nullptr) {
|
||||
return *this;
|
||||
if (next == nullptr) {
|
||||
mgp::edges_iterator_destroy(out_relationships_iterator_);
|
||||
out_relationships_iterator_ = nullptr;
|
||||
}
|
||||
|
||||
mgp::edges_iterator_destroy(out_relationships_iterator_);
|
||||
out_relationships_iterator_ = nullptr;
|
||||
}
|
||||
|
||||
// 2. Move onto the next nodes
|
||||
@@ -1884,7 +1904,7 @@ inline Relationships::Iterator::Iterator(mgp_edges_iterator *relationships_itera
|
||||
}
|
||||
}
|
||||
|
||||
inline Relationships::Iterator::Iterator(const Iterator &other) noexcept : Iterator(other.relationships_iterator_) {}
|
||||
inline Relationships::Iterator::Iterator(const Iterator &other) : Iterator(other.relationships_iterator_) {}
|
||||
|
||||
inline Relationships::Iterator::~Iterator() {
|
||||
if (relationships_iterator_ != nullptr) {
|
||||
@@ -1943,11 +1963,40 @@ inline Relationships::Iterator Relationships::cbegin() const { return Iterator(r
|
||||
|
||||
inline Relationships::Iterator Relationships::cend() const { return Iterator(nullptr); }
|
||||
|
||||
// Properties:
|
||||
|
||||
inline Properties::Properties(mgp_properties_iterator *properties_iterator) {
|
||||
for (auto property = mgp::properties_iterator_get(properties_iterator); property;
|
||||
property = mgp::properties_iterator_next(properties_iterator)) {
|
||||
auto value = Value(property->value);
|
||||
property_map_.emplace(property->name, value);
|
||||
}
|
||||
mgp::properties_iterator_destroy(properties_iterator);
|
||||
}
|
||||
|
||||
inline size_t Properties::Size() const { return property_map_.size(); }
|
||||
|
||||
inline bool Properties::Empty() const { return Size() == 0; }
|
||||
|
||||
inline Value Properties::operator[](const std::string_view key) const { return property_map_.at(key); }
|
||||
|
||||
inline std::map<std::string_view, Value>::const_iterator Properties::begin() const { return property_map_.begin(); }
|
||||
|
||||
inline std::map<std::string_view, Value>::const_iterator Properties::end() const { return property_map_.end(); }
|
||||
|
||||
inline std::map<std::string_view, Value>::const_iterator Properties::cbegin() const { return property_map_.cbegin(); }
|
||||
|
||||
inline std::map<std::string_view, Value>::const_iterator Properties::cend() const { return property_map_.cend(); }
|
||||
|
||||
inline bool Properties::operator==(const Properties &other) const { return property_map_ == other.property_map_; }
|
||||
|
||||
inline bool Properties::operator!=(const Properties &other) const { return !(*this == other); }
|
||||
|
||||
// Labels:
|
||||
|
||||
inline Labels::Labels(mgp_vertex *node_ptr) : node_ptr_(mgp::vertex_copy(node_ptr, memory)) {}
|
||||
|
||||
inline Labels::Labels(const Labels &other) noexcept : Labels(other.node_ptr_) {}
|
||||
inline Labels::Labels(const Labels &other) : Labels(other.node_ptr_) {}
|
||||
|
||||
inline Labels::Labels(Labels &&other) noexcept : node_ptr_(other.node_ptr_) { other.node_ptr_ = nullptr; }
|
||||
|
||||
@@ -2037,7 +2086,7 @@ inline List::List(const std::initializer_list<Value> values) : ptr_(mgp::list_ma
|
||||
}
|
||||
}
|
||||
|
||||
inline List::List(const List &other) noexcept : List(other.ptr_) {}
|
||||
inline List::List(const List &other) : List(other.ptr_) {}
|
||||
|
||||
inline List::List(List &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
|
||||
|
||||
@@ -2145,7 +2194,7 @@ inline Map::Map(const std::initializer_list<std::pair<std::string_view, Value>>
|
||||
}
|
||||
}
|
||||
|
||||
inline Map::Map(const Map &other) noexcept : Map(other.ptr_) {}
|
||||
inline Map::Map(const Map &other) : Map(other.ptr_) {}
|
||||
|
||||
inline Map::Map(Map &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
|
||||
|
||||
@@ -2190,7 +2239,7 @@ inline Map::Iterator::Iterator(mgp_map_items_iterator *map_items_iterator) : map
|
||||
}
|
||||
}
|
||||
|
||||
inline Map::Iterator::Iterator(const Iterator &other) noexcept : Iterator(other.map_items_iterator_) {}
|
||||
inline Map::Iterator::Iterator(const Iterator &other) : Iterator(other.map_items_iterator_) {}
|
||||
|
||||
inline Map::Iterator::~Iterator() {
|
||||
if (map_items_iterator_ != nullptr) {
|
||||
@@ -2257,6 +2306,10 @@ inline void Map::Insert(std::string_view key, Value &&value) {
|
||||
value.ptr_ = nullptr;
|
||||
}
|
||||
|
||||
inline std::map<std::string_view, Value>::const_iterator Properties::find(const std::string_view key) const {
|
||||
return property_map_.find(key);
|
||||
}
|
||||
|
||||
inline bool Map::operator==(const Map &other) const { return util::MapsEqual(ptr_, other.ptr_); }
|
||||
|
||||
inline bool Map::operator!=(const Map &other) const { return !(*this == other); }
|
||||
@@ -2271,7 +2324,7 @@ inline Node::Node(mgp_vertex *ptr) : ptr_(mgp::vertex_copy(ptr, memory)) {}
|
||||
|
||||
inline Node::Node(const mgp_vertex *const_ptr) : ptr_(mgp::vertex_copy(const_cast<mgp_vertex *>(const_ptr), memory)) {}
|
||||
|
||||
inline Node::Node(const Node &other) noexcept : Node(other.ptr_) {}
|
||||
inline Node::Node(const Node &other) : Node(other.ptr_) {}
|
||||
|
||||
inline Node::Node(Node &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
|
||||
|
||||
@@ -2302,7 +2355,7 @@ inline Node::~Node() {
|
||||
|
||||
inline mgp::Id Node::Id() const { return Id::FromInt(mgp::vertex_get_id(ptr_).as_int); }
|
||||
|
||||
inline mgp::Labels Node::Labels() const { return mgp::Labels(ptr_); }
|
||||
inline class Labels Node::Labels() const { return mgp::Labels(ptr_); }
|
||||
|
||||
inline bool Node::HasLabel(std::string_view label) const {
|
||||
for (const auto node_label : Labels()) {
|
||||
@@ -2313,6 +2366,10 @@ inline bool Node::HasLabel(std::string_view label) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline class Properties Node::Properties() const { return mgp::Properties(mgp::vertex_iter_properties(ptr_, memory)); }
|
||||
|
||||
inline Value Node::operator[](const std::string_view property_name) const { return Properties()[property_name]; }
|
||||
|
||||
inline Relationships Node::InRelationships() const {
|
||||
auto relationship_iterator = mgp::vertex_iter_in_edges(ptr_, memory);
|
||||
if (relationship_iterator == nullptr) {
|
||||
@@ -2333,26 +2390,6 @@ inline void Node::AddLabel(const std::string_view label) {
|
||||
mgp::vertex_add_label(this->ptr_, mgp_label{.name = label.data()});
|
||||
}
|
||||
|
||||
inline std::map<std::string, Value> Node::Properties() const {
|
||||
mgp_properties_iterator *properties_iterator = mgp::vertex_iter_properties(ptr_, memory);
|
||||
std::map<std::string, Value> property_map;
|
||||
for (auto *property = mgp::properties_iterator_get(properties_iterator); property;
|
||||
property = mgp::properties_iterator_next(properties_iterator)) {
|
||||
property_map.emplace(std::string(property->name), Value(property->value));
|
||||
}
|
||||
mgp::properties_iterator_destroy(properties_iterator);
|
||||
return property_map;
|
||||
}
|
||||
|
||||
inline void Node::SetProperty(std::string property, Value value) {
|
||||
mgp::vertex_set_property(ptr_, property.data(), value.ptr());
|
||||
}
|
||||
|
||||
inline Value Node::GetProperty(const std::string &property) const {
|
||||
mgp_value *vertex_prop = mgp::vertex_get_property(ptr_, property.data(), memory);
|
||||
return Value(steal, vertex_prop);
|
||||
}
|
||||
|
||||
inline bool Node::operator<(const Node &other) const { return Id() < other.Id(); }
|
||||
|
||||
inline bool Node::operator==(const Node &other) const { return util::NodesEqual(ptr_, other.ptr_); }
|
||||
@@ -2366,7 +2403,7 @@ inline Relationship::Relationship(mgp_edge *ptr) : ptr_(mgp::edge_copy(ptr, memo
|
||||
inline Relationship::Relationship(const mgp_edge *const_ptr)
|
||||
: ptr_(mgp::edge_copy(const_cast<mgp_edge *>(const_ptr), memory)) {}
|
||||
|
||||
inline Relationship::Relationship(const Relationship &other) noexcept : Relationship(other.ptr_) {}
|
||||
inline Relationship::Relationship(const Relationship &other) : Relationship(other.ptr_) {}
|
||||
|
||||
inline Relationship::Relationship(Relationship &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
|
||||
|
||||
@@ -2399,24 +2436,12 @@ inline mgp::Id Relationship::Id() const { return Id::FromInt(mgp::edge_get_id(pt
|
||||
|
||||
inline std::string_view Relationship::Type() const { return mgp::edge_get_type(ptr_).name; }
|
||||
|
||||
inline std::map<std::string, Value> Relationship::Properties() const {
|
||||
mgp_properties_iterator *properties_iterator = mgp::edge_iter_properties(ptr_, memory);
|
||||
std::map<std::string, Value> property_map;
|
||||
for (mgp_property *property = mgp::properties_iterator_get(properties_iterator); property;
|
||||
property = mgp::properties_iterator_next(properties_iterator)) {
|
||||
property_map.emplace(property->name, Value(property->value));
|
||||
}
|
||||
mgp::properties_iterator_destroy(properties_iterator);
|
||||
return property_map;
|
||||
inline class Properties Relationship::Properties() const {
|
||||
return mgp::Properties(mgp::edge_iter_properties(ptr_, memory));
|
||||
}
|
||||
|
||||
inline void Relationship::SetProperty(std::string property, Value value) {
|
||||
mgp::edge_set_property(ptr_, property.data(), value.ptr());
|
||||
}
|
||||
|
||||
inline Value Relationship::GetProperty(const std::string &property) const {
|
||||
mgp_value *edge_prop = mgp::edge_get_property(ptr_, property.data(), memory);
|
||||
return Value(steal, edge_prop);
|
||||
inline Value Relationship::operator[](const std::string_view property_name) const {
|
||||
return Properties()[property_name];
|
||||
}
|
||||
|
||||
inline Node Relationship::From() const { return Node(mgp::edge_get_from(ptr_)); }
|
||||
@@ -2439,7 +2464,7 @@ inline Path::Path(const mgp_path *const_ptr) : ptr_(mgp::path_copy(const_cast<mg
|
||||
|
||||
inline Path::Path(const Node &start_node) : ptr_(mgp::path_make_with_start(start_node.ptr_, memory)) {}
|
||||
|
||||
inline Path::Path(const Path &other) noexcept : Path(other.ptr_) {}
|
||||
inline Path::Path(const Path &other) : Path(other.ptr_) {}
|
||||
|
||||
inline Path::Path(Path &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
|
||||
|
||||
@@ -2508,7 +2533,7 @@ inline Date::Date(int year, int month, int day) {
|
||||
ptr_ = mgp::date_from_parameters(¶ms, memory);
|
||||
}
|
||||
|
||||
inline Date::Date(const Date &other) noexcept : Date(other.ptr_) {}
|
||||
inline Date::Date(const Date &other) : Date(other.ptr_) {}
|
||||
|
||||
inline Date::Date(Date &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
|
||||
|
||||
@@ -2602,7 +2627,7 @@ inline LocalTime::LocalTime(int hour, int minute, int second, int millisecond, i
|
||||
ptr_ = mgp::local_time_from_parameters(¶ms, memory);
|
||||
}
|
||||
|
||||
inline LocalTime::LocalTime(const LocalTime &other) noexcept : LocalTime(other.ptr_) {}
|
||||
inline LocalTime::LocalTime(const LocalTime &other) : LocalTime(other.ptr_) {}
|
||||
|
||||
inline LocalTime::LocalTime(LocalTime &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; };
|
||||
|
||||
@@ -2707,7 +2732,7 @@ inline LocalDateTime::LocalDateTime(int year, int month, int day, int hour, int
|
||||
ptr_ = mgp::local_date_time_from_parameters(¶ms, memory);
|
||||
}
|
||||
|
||||
inline LocalDateTime::LocalDateTime(const LocalDateTime &other) noexcept : LocalDateTime(other.ptr_) {}
|
||||
inline LocalDateTime::LocalDateTime(const LocalDateTime &other) : LocalDateTime(other.ptr_) {}
|
||||
|
||||
inline LocalDateTime::LocalDateTime(LocalDateTime &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; };
|
||||
|
||||
@@ -2820,7 +2845,7 @@ inline Duration::Duration(double day, double hour, double minute, double second,
|
||||
ptr_ = mgp::duration_from_parameters(¶ms, memory);
|
||||
}
|
||||
|
||||
inline Duration::Duration(const Duration &other) noexcept : Duration(other.ptr_) {}
|
||||
inline Duration::Duration(const Duration &other) : Duration(other.ptr_) {}
|
||||
|
||||
inline Duration::Duration(Duration &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; };
|
||||
|
||||
@@ -2892,7 +2917,6 @@ inline bool Duration::operator<(const Duration &other) const {
|
||||
/* #region Value */
|
||||
|
||||
inline Value::Value(mgp_value *ptr) : ptr_(mgp::value_copy(ptr, memory)) {}
|
||||
inline Value::Value(StealType /*steal*/, mgp_value *ptr) : ptr_{ptr} {}
|
||||
|
||||
inline Value::Value() : ptr_(mgp::value_make_null(memory)) {}
|
||||
|
||||
@@ -2973,7 +2997,7 @@ inline Value::Value(Duration &&duration) {
|
||||
duration.ptr_ = nullptr;
|
||||
}
|
||||
|
||||
inline Value::Value(const Value &other) noexcept : Value(other.ptr_) {}
|
||||
inline Value::Value(const Value &other) : Value(other.ptr_) {}
|
||||
|
||||
inline Value::Value(Value &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
|
||||
|
||||
|
||||
@@ -1283,7 +1283,7 @@ class Graph:
|
||||
raise InvalidContextError()
|
||||
self._graph.detach_delete_vertex(vertex._vertex)
|
||||
|
||||
def create_edge(self, from_vertex: Vertex, to_vertex: Vertex, edge_type: EdgeType) -> Edge:
|
||||
def create_edge(self, from_vertex: Vertex, to_vertex: Vertex, edge_type: EdgeType) -> None:
|
||||
"""
|
||||
Create an edge.
|
||||
|
||||
@@ -1292,16 +1292,13 @@ class Graph:
|
||||
to_vertex: `Vertex' to where edge is directed.
|
||||
edge_type: `EdgeType` defines the type of edge.
|
||||
|
||||
Returns:
|
||||
Created `Edge`.
|
||||
|
||||
Raises:
|
||||
ImmutableObjectError: If `graph` is immutable.
|
||||
UnableToAllocateError: If unable to allocate an edge.
|
||||
DeletedObjectError: If `from_vertex` or `to_vertex` has been deleted.
|
||||
SerializationError: If `from_vertex` or `to_vertex` has been modified by another transaction.
|
||||
Examples:
|
||||
```edge = graph.create_edge(from_vertex, vertex, edge_type)```
|
||||
```graph.create_edge(from_vertex, vertex, edge_type)```
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidContextError()
|
||||
|
||||
8
init
8
init
@@ -113,8 +113,8 @@ if [[ "$setup_libs" == "true" ]]; then
|
||||
fi
|
||||
|
||||
# Fix for centos 7 during release
|
||||
if [ "${DISTRO}" = "centos-7" ] || [ "${DISTRO}" = "debian-11" ]; then
|
||||
python3 -m pip uninstall -y virtualenv
|
||||
if [ "${ARCHITECTURE}" = "centos-7" ]; then
|
||||
python3 -m pip uninstall virtualenv
|
||||
python3 -m pip install virtualenv
|
||||
fi
|
||||
|
||||
@@ -149,9 +149,9 @@ python3 -m pre_commit install
|
||||
|
||||
# Install py format tools
|
||||
echo "Install black formatter"
|
||||
python3 -m pip install black==22.8.*
|
||||
python3 -m pip install black==22.*
|
||||
echo "Install isort"
|
||||
python3 -m pip install isort==5.10.*
|
||||
python3 -m pip install isort==5.*
|
||||
|
||||
# Link `include/mgp.py` with `release/mgp/mgp.py`
|
||||
ln -v -f include/mgp.py release/mgp/mgp.py
|
||||
|
||||
@@ -36,7 +36,7 @@ ADDITIONAL USE GRANT: You may use the Licensed Work in accordance with the
|
||||
3. using the Licensed Work to create a work or solution
|
||||
which competes (or might reasonably be expected to
|
||||
compete) with the Licensed Work.
|
||||
CHANGE DATE: 2027-26-01
|
||||
CHANGE DATE: 2026-07-11
|
||||
CHANGE LICENSE: Apache License, Version 2.0
|
||||
|
||||
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.
|
||||
|
||||
@@ -67,7 +67,7 @@ It aims to deliver developers the speed, simplicity and scale required to build
|
||||
the next generation of applications driver by real-time connected data.")
|
||||
# Add `openssl` package to dependencies list. Used to generate SSL certificates.
|
||||
# We also depend on `python3` because we embed it in Memgraph.
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.0.0, curl >= 7.29.0, python3 >= 3.5.0, libstdc++ >= 6, logrotate")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.0.0, curl >= 7.29.0, python3 >= 3.5.0, libstdc >= 6, logrotate")
|
||||
|
||||
# All variables must be set before including.
|
||||
include(CPack)
|
||||
|
||||
1
release/mgp/.gitignore
vendored
1
release/mgp/.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
.venv
|
||||
dist
|
||||
mgp.py
|
||||
poetry.lock
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "mgp"
|
||||
version = "1.1.1"
|
||||
version = "1.1.0"
|
||||
description = "Memgraph's module for developing MAGE modules. Used only for type hinting!"
|
||||
authors = [
|
||||
"katarinasupe <katarina.supe@memgraph.io>",
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
debian-11-arm:
|
||||
build:
|
||||
context: debian-11-arm
|
||||
container_name: "mgbuild_debian-11-arm"
|
||||
ubuntu-2204-arm:
|
||||
build:
|
||||
context: ubuntu-22.04-arm
|
||||
container_name: "mgbuild_ubuntu-22.04-arm"
|
||||
@@ -28,7 +28,3 @@ services:
|
||||
build:
|
||||
context: ubuntu-22.04
|
||||
container_name: "mgbuild_ubuntu-22.04"
|
||||
mgbuild_fedora-36:
|
||||
build:
|
||||
context: fedora-36
|
||||
container_name: "mgbuild_fedora-36"
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
FROM fedora:36
|
||||
|
||||
ARG TOOLCHAIN_VERSION
|
||||
|
||||
# Stops tzdata interactive configuration.
|
||||
RUN yum -y update \
|
||||
&& yum install -y wget git
|
||||
# Do NOT be smart here and clean the cache because the container is used in the
|
||||
# stateful context.
|
||||
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
@@ -3,7 +3,7 @@
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
SUPPORTED_OS=(centos-7 centos-9 debian-10 debian-11 ubuntu-18.04 ubuntu-20.04 ubuntu-22.04 debian-11-arm fedora-36 ubuntu-22.04-arm)
|
||||
SUPPORTED_OS=(centos-7 centos-9 debian-10 debian-11 ubuntu-18.04 ubuntu-20.04 ubuntu-22.04 debian-11-arm)
|
||||
PROJECT_ROOT="$SCRIPT_DIR/../.."
|
||||
TOOLCHAIN_VERSION="toolchain-v4"
|
||||
ACTIVATE_TOOLCHAIN="source /opt/${TOOLCHAIN_VERSION}/activate"
|
||||
@@ -23,9 +23,9 @@ make_package () {
|
||||
echo "Building Memgraph for $os on $build_container..."
|
||||
|
||||
package_command=""
|
||||
if [[ "$os" =~ ^"centos".* ]] || [[ "$os" =~ ^"fedora".* ]]; then
|
||||
if [[ "$os" =~ ^"centos".* ]]; then
|
||||
docker exec "$build_container" bash -c "yum -y update"
|
||||
package_command=" cpack -G RPM --config ../CPackConfig.cmake && rpmlint --file='../../release/rpm/rpmlintrc' memgraph*.rpm "
|
||||
package_command=" cpack -G RPM --config ../CPackConfig.cmake && rpmlint memgraph*.rpm "
|
||||
fi
|
||||
if [[ "$os" =~ ^"debian".* ]]; then
|
||||
docker exec "$build_container" bash -c "apt update"
|
||||
@@ -76,7 +76,7 @@ make_package () {
|
||||
docker exec "$build_container" bash -c "cd /memgraph && git config --global --add safe.directory '*'"
|
||||
docker exec "$build_container" bash -c "cd /memgraph && $ACTIVATE_TOOLCHAIN && ./init"
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && rm -rf ./*"
|
||||
if [[ "$os" =~ "-arm" ]]; then
|
||||
if [[ "$os" == "debian-11-arm" ]]; then
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && cmake -DCMAKE_BUILD_TYPE=release -DMG_ARCH="ARM64" $telemetry_id_override_flag .."
|
||||
else
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && cmake -DCMAKE_BUILD_TYPE=release $telemetry_id_override_flag .."
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG TOOLCHAIN_VERSION
|
||||
|
||||
# Stops tzdata interactive configuration.
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt update && apt install -y \
|
||||
ca-certificates wget git
|
||||
# Do NOT be smart here and clean the cache because the container is used in the
|
||||
# stateful context.
|
||||
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
@@ -30,7 +30,7 @@ BuildRequires: systemd
|
||||
# This is needed to prevent Python compilation errors when building the RPM
|
||||
# package
|
||||
# https://github.com/scylladb/scylla/issues/2235
|
||||
%if 0%{?rhel} && 0%{?rhel} < 8
|
||||
%if 0%{?rhel} < 8
|
||||
%global __os_install_post \
|
||||
/usr/lib/rpm/redhat/brp-compress \
|
||||
%{!?__debug_package:\
|
||||
@@ -40,9 +40,7 @@ BuildRequires: systemd
|
||||
/usr/lib/rpm/redhat/brp-strip-static-archive %{__strip} \
|
||||
%{!?__jar_repack:/usr/lib/rpm/redhat/brp-java-repack-jars} \
|
||||
%{nil}
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora} && 0%{?fedora} < 35
|
||||
%else
|
||||
%global __os_install_post \
|
||||
/usr/lib/rpm/brp-compress \
|
||||
%{!?__debug_package:\
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# from https://github.com/google/earthenterprise/blob/master/earth_enterprise/rpmlintrc
|
||||
|
||||
# We are not packaging log dir
|
||||
addFilter("E: logrotate-log-dir-not-packaged")
|
||||
@@ -146,10 +146,9 @@ QueryData Client::Execute(const std::string &query, const std::map<std::string,
|
||||
throw ServerMalformedDataException();
|
||||
}
|
||||
|
||||
auto &header = fields.ValueMap();
|
||||
|
||||
QueryData ret{{}, std::move(records), std::move(metadata.ValueMap())};
|
||||
|
||||
auto &header = fields.ValueMap();
|
||||
if (header.find("fields") == header.end()) {
|
||||
throw ServerMalformedDataException();
|
||||
}
|
||||
@@ -165,10 +164,6 @@ QueryData Client::Execute(const std::string &query, const std::map<std::string,
|
||||
ret.fields.emplace_back(std::move(field_item.ValueString()));
|
||||
}
|
||||
|
||||
if (header.contains("qid")) {
|
||||
ret.metadata["qid"] = header["qid"];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -73,6 +73,40 @@ inline std::pair<std::string, std::string> ExceptionToErrorMessage(const std::ex
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename TSession>
|
||||
State HandleRun(TSession &session, const State state, const Value &query, const Value ¶ms) {
|
||||
if (state != State::Idle) {
|
||||
// Client could potentially recover if we move to error state, but there is
|
||||
// no legitimate situation in which well working client would end up in this
|
||||
// situation.
|
||||
spdlog::trace("Unexpected RUN command!");
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
|
||||
|
||||
spdlog::debug("[Run] '{}'", query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
const auto [header, qid] = session.Interpret(query.ValueString(), params.ValueMap());
|
||||
// Convert std::string to Value
|
||||
std::vector<Value> vec;
|
||||
std::map<std::string, Value> data;
|
||||
vec.reserve(header.size());
|
||||
for (auto &i : header) vec.emplace_back(std::move(i));
|
||||
data.emplace("fields", std::move(vec));
|
||||
// Send the header.
|
||||
if (!session.encoder_.MessageSuccess(data)) {
|
||||
spdlog::trace("Couldn't send query header!");
|
||||
return State::Close;
|
||||
}
|
||||
return State::Result;
|
||||
} catch (const std::exception &e) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool is_pull, typename TSession>
|
||||
State HandlePullDiscard(TSession &session, std::optional<int> n, std::optional<int> qid) {
|
||||
try {
|
||||
@@ -195,36 +229,7 @@ State HandleRunV1(TSession &session, const State state, const Marker marker) {
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
if (state != State::Idle) {
|
||||
// Client could potentially recover if we move to error state, but there is
|
||||
// no legitimate situation in which well working client would end up in this
|
||||
// situation.
|
||||
spdlog::trace("Unexpected RUN command!");
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
|
||||
|
||||
spdlog::debug("[Run] '{}'", query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
const auto [header, qid] = session.Interpret(query.ValueString(), params.ValueMap());
|
||||
// Convert std::string to Value
|
||||
std::vector<Value> vec;
|
||||
std::map<std::string, Value> data;
|
||||
vec.reserve(header.size());
|
||||
for (auto &i : header) vec.emplace_back(std::move(i));
|
||||
data.emplace("fields", std::move(vec));
|
||||
// Send the header.
|
||||
if (!session.encoder_.MessageSuccess(data)) {
|
||||
spdlog::trace("Couldn't send query header!");
|
||||
return State::Close;
|
||||
}
|
||||
return State::Result;
|
||||
} catch (const std::exception &e) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
return details::HandleRun(session, state, query, params);
|
||||
}
|
||||
|
||||
template <typename TSession>
|
||||
@@ -252,40 +257,7 @@ State HandleRunV4(TSession &session, const State state, const Marker marker) {
|
||||
spdlog::trace("Couldn't read extra field!");
|
||||
}
|
||||
|
||||
if (state != State::Idle) {
|
||||
// Client could potentially recover if we move to error state, but there is
|
||||
// no legitimate situation in which well working client would end up in this
|
||||
// situation.
|
||||
spdlog::trace("Unexpected RUN command!");
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
|
||||
|
||||
spdlog::debug("[Run] '{}'", query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
const auto [header, qid] = session.Interpret(query.ValueString(), params.ValueMap());
|
||||
// Convert std::string to Value
|
||||
std::vector<Value> vec;
|
||||
std::map<std::string, Value> data;
|
||||
vec.reserve(header.size());
|
||||
for (auto &i : header) vec.emplace_back(std::move(i));
|
||||
data.emplace("fields", std::move(vec));
|
||||
if (qid.has_value()) {
|
||||
data.emplace("qid", Value{*qid});
|
||||
}
|
||||
|
||||
// Send the header.
|
||||
if (!session.encoder_.MessageSuccess(data)) {
|
||||
spdlog::trace("Couldn't send query header!");
|
||||
return State::Close;
|
||||
}
|
||||
return State::Result;
|
||||
} catch (const std::exception &e) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
return details::HandleRun(session, state, query, params);
|
||||
}
|
||||
|
||||
template <typename TSession>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -64,6 +64,14 @@ auto SymbolGenerator::CreateSymbol(const std::string &name, bool user_declared,
|
||||
return symbol;
|
||||
}
|
||||
|
||||
auto SymbolGenerator::GetOrCreateSymbolLocalScope(const std::string &name, bool user_declared, Symbol::Type type) {
|
||||
auto &scope = scopes_.back();
|
||||
if (auto maybe_symbol = FindSymbolInScope(name, scope, type); maybe_symbol) {
|
||||
return *maybe_symbol;
|
||||
}
|
||||
return CreateSymbol(name, user_declared, type);
|
||||
}
|
||||
|
||||
auto SymbolGenerator::GetOrCreateSymbol(const std::string &name, bool user_declared, Symbol::Type type) {
|
||||
// NOLINTNEXTLINE
|
||||
for (auto scope = scopes_.rbegin(); scope != scopes_.rend(); ++scope) {
|
||||
@@ -198,7 +206,7 @@ bool SymbolGenerator::PreVisit(CallProcedure &call_proc) {
|
||||
|
||||
bool SymbolGenerator::PostVisit(CallProcedure &call_proc) {
|
||||
for (auto *ident : call_proc.result_identifiers_) {
|
||||
if (HasSymbol(ident->name_)) {
|
||||
if (HasSymbolLocalScope(ident->name_)) {
|
||||
throw RedeclareVariableError(ident->name_);
|
||||
}
|
||||
ident->MapTo(CreateSymbol(ident->name_, true));
|
||||
@@ -209,7 +217,7 @@ bool SymbolGenerator::PostVisit(CallProcedure &call_proc) {
|
||||
bool SymbolGenerator::PreVisit(LoadCsv &load_csv) { return false; }
|
||||
|
||||
bool SymbolGenerator::PostVisit(LoadCsv &load_csv) {
|
||||
if (HasSymbol(load_csv.row_var_->name_)) {
|
||||
if (HasSymbolLocalScope(load_csv.row_var_->name_)) {
|
||||
throw RedeclareVariableError(load_csv.row_var_->name_);
|
||||
}
|
||||
load_csv.row_var_->MapTo(CreateSymbol(load_csv.row_var_->name_, true));
|
||||
@@ -257,7 +265,7 @@ bool SymbolGenerator::PostVisit(Merge &) {
|
||||
|
||||
bool SymbolGenerator::PostVisit(Unwind &unwind) {
|
||||
const auto &name = unwind.named_expression_->name_;
|
||||
if (HasSymbol(name)) {
|
||||
if (HasSymbolLocalScope(name)) {
|
||||
throw RedeclareVariableError(name);
|
||||
}
|
||||
unwind.named_expression_->MapTo(CreateSymbol(name, true));
|
||||
@@ -274,7 +282,7 @@ bool SymbolGenerator::PostVisit(Match &) {
|
||||
// Check variables in property maps after visiting Match, so that they can
|
||||
// reference symbols out of bind order.
|
||||
for (auto &ident : scope.identifiers_in_match) {
|
||||
if (!HasSymbol(ident->name_) && !ConsumePredefinedIdentifier(ident->name_))
|
||||
if (!HasSymbolLocalScope(ident->name_) && !ConsumePredefinedIdentifier(ident->name_))
|
||||
throw UnboundVariableError(ident->name_);
|
||||
ident->MapTo(scope.symbols[ident->name_]);
|
||||
}
|
||||
@@ -306,7 +314,7 @@ SymbolGenerator::ReturnType SymbolGenerator::Visit(Identifier &ident) {
|
||||
if (scope.in_pattern && !(scope.in_node_atom || scope.visiting_edge)) {
|
||||
// If we are in the pattern, and outside of a node or an edge, the
|
||||
// identifier is the pattern name.
|
||||
symbol = GetOrCreateSymbol(ident.name_, ident.user_declared_, Symbol::Type::PATH);
|
||||
symbol = GetOrCreateSymbolLocalScope(ident.name_, ident.user_declared_, Symbol::Type::PATH);
|
||||
} else if (scope.in_pattern && scope.in_pattern_atom_identifier) {
|
||||
// Patterns used to create nodes and edges cannot redeclare already
|
||||
// established bindings. Declaration only happens in single node
|
||||
@@ -314,19 +322,19 @@ SymbolGenerator::ReturnType SymbolGenerator::Visit(Identifier &ident) {
|
||||
// `MATCH (n) CREATE (n)` should throw an error that `n` is already
|
||||
// declared. While `MATCH (n) CREATE (n) -[:R]-> (n)` is allowed,
|
||||
// since `n` now references the bound node instead of declaring it.
|
||||
if ((scope.in_create_node || scope.in_create_edge) && HasSymbol(ident.name_)) {
|
||||
if ((scope.in_create_node || scope.in_create_edge) && HasSymbolLocalScope(ident.name_)) {
|
||||
throw RedeclareVariableError(ident.name_);
|
||||
}
|
||||
auto type = Symbol::Type::VERTEX;
|
||||
if (scope.visiting_edge) {
|
||||
// Edge referencing is not allowed (like in Neo4j):
|
||||
// `MATCH (n) - [r] -> (n) - [r] -> (n) RETURN r` is not allowed.
|
||||
if (HasSymbol(ident.name_)) {
|
||||
if (HasSymbolLocalScope(ident.name_)) {
|
||||
throw RedeclareVariableError(ident.name_);
|
||||
}
|
||||
type = scope.visiting_edge->IsVariable() ? Symbol::Type::EDGE_LIST : Symbol::Type::EDGE;
|
||||
}
|
||||
symbol = GetOrCreateSymbol(ident.name_, ident.user_declared_, type);
|
||||
symbol = GetOrCreateSymbolLocalScope(ident.name_, ident.user_declared_, type);
|
||||
} else if (scope.in_pattern && !scope.in_pattern_atom_identifier && scope.in_match) {
|
||||
if (scope.in_edge_range && scope.visiting_edge->identifier_->name_ == ident.name_) {
|
||||
// Prevent variable path bounds to reference the identifier which is bound
|
||||
@@ -453,7 +461,7 @@ bool SymbolGenerator::PreVisit(NodeAtom &node_atom) {
|
||||
auto &scope = scopes_.back();
|
||||
auto check_node_semantic = [&node_atom, &scope, this](const bool props_or_labels) {
|
||||
const auto &node_name = node_atom.identifier_->name_;
|
||||
if ((scope.in_create || scope.in_merge) && props_or_labels && HasSymbol(node_name)) {
|
||||
if ((scope.in_create || scope.in_merge) && props_or_labels && HasSymbolLocalScope(node_name)) {
|
||||
throw SemanticException("Cannot create node '" + node_name +
|
||||
"' with labels or properties, because it is already declared.");
|
||||
}
|
||||
@@ -547,11 +555,11 @@ bool SymbolGenerator::PreVisit(EdgeAtom &edge_atom) {
|
||||
edge_atom.identifier_->Accept(*this);
|
||||
scope.in_pattern_atom_identifier = false;
|
||||
if (edge_atom.total_weight_) {
|
||||
if (HasSymbol(edge_atom.total_weight_->name_)) {
|
||||
if (HasSymbolLocalScope(edge_atom.total_weight_->name_)) {
|
||||
throw RedeclareVariableError(edge_atom.total_weight_->name_);
|
||||
}
|
||||
edge_atom.total_weight_->MapTo(GetOrCreateSymbol(edge_atom.total_weight_->name_,
|
||||
edge_atom.total_weight_->user_declared_, Symbol::Type::NUMBER));
|
||||
edge_atom.total_weight_->MapTo(GetOrCreateSymbolLocalScope(
|
||||
edge_atom.total_weight_->name_, edge_atom.total_weight_->user_declared_, Symbol::Type::NUMBER));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -594,6 +602,10 @@ bool SymbolGenerator::HasSymbol(const std::string &name) const {
|
||||
return std::ranges::any_of(scopes_, [&name](const auto &scope) { return scope.symbols.contains(name); });
|
||||
}
|
||||
|
||||
bool SymbolGenerator::HasSymbolLocalScope(const std::string &name) const {
|
||||
return scopes_.back().symbols.contains(name);
|
||||
}
|
||||
|
||||
bool SymbolGenerator::ConsumePredefinedIdentifier(const std::string &name) {
|
||||
auto it = predefined_identifiers_.find(name);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -134,6 +134,7 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
|
||||
static std::optional<Symbol> FindSymbolInScope(const std::string &name, const Scope &scope, Symbol::Type type);
|
||||
|
||||
bool HasSymbol(const std::string &name) const;
|
||||
bool HasSymbolLocalScope(const std::string &name) const;
|
||||
|
||||
// @return true if it added a predefined identifier with that name
|
||||
bool ConsumePredefinedIdentifier(const std::string &name);
|
||||
@@ -146,6 +147,7 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
|
||||
auto GetOrCreateSymbol(const std::string &name, bool user_declared, Symbol::Type type = Symbol::Type::ANY);
|
||||
// Returns the symbol by name. If the mapping already exists, checks if the
|
||||
// types match. Otherwise, returns a new symbol.
|
||||
auto GetOrCreateSymbolLocalScope(const std::string &name, bool user_declared, Symbol::Type type = Symbol::Type::ANY);
|
||||
|
||||
void VisitReturnBody(ReturnBody &body, Where *where = nullptr);
|
||||
|
||||
|
||||
@@ -1044,7 +1044,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::NewDeleteResource());
|
||||
utils::PoolResource pool_memory(128, 1024, &monotonic_memory);
|
||||
std::optional<utils::LimitedMemoryResource> maybe_limited_resource;
|
||||
|
||||
if (memory_limit_) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -2020,7 +2020,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
next_edges_.clear();
|
||||
traversal_stack_.clear();
|
||||
|
||||
expand_from_vertex(*start_vertex, TypedValue(), 0);
|
||||
pq_.push({TypedValue(), 0, *start_vertex, std::nullopt});
|
||||
visited_cost_.emplace(*start_vertex, 0);
|
||||
frame[self_.common_.edge_symbol] = TypedValue::TVector(memory);
|
||||
}
|
||||
@@ -2029,28 +2029,33 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
while (!pq_.empty()) {
|
||||
if (MustAbort(context)) throw HintedAbortError();
|
||||
|
||||
const auto [current_weight, current_depth, current_vertex, directed_edge] = pq_.top();
|
||||
auto [current_weight, current_depth, current_vertex, maybe_directed_edge] = pq_.top();
|
||||
pq_.pop();
|
||||
|
||||
const auto &[current_edge, direction, weight] = directed_edge;
|
||||
if (expanded_.contains(current_edge)) continue;
|
||||
expanded_.emplace(current_edge);
|
||||
|
||||
// Expand only if what we've just expanded is less than max depth.
|
||||
if (current_depth < upper_bound_) {
|
||||
if (maybe_directed_edge) {
|
||||
auto &[current_edge, direction, weight] = *maybe_directed_edge;
|
||||
if (expanded_.find(current_edge) != expanded_.end()) continue;
|
||||
expanded_.emplace(current_edge);
|
||||
}
|
||||
expand_from_vertex(current_vertex, current_weight, current_depth);
|
||||
}
|
||||
|
||||
// Searching for a previous vertex in the expansion
|
||||
auto prev_vertex = direction == EdgeAtom::Direction::IN ? current_edge.To() : current_edge.From();
|
||||
// if current vertex is not starting vertex, maybe_directed_edge will not be nullopt
|
||||
if (maybe_directed_edge) {
|
||||
auto &[current_edge, direction, weight] = *maybe_directed_edge;
|
||||
// Searching for a previous vertex in the expansion
|
||||
auto prev_vertex = direction == EdgeAtom::Direction::IN ? current_edge.To() : current_edge.From();
|
||||
|
||||
// Update the parent
|
||||
if (next_edges_.find({prev_vertex, current_depth - 1}) == next_edges_.end()) {
|
||||
utils::pmr::list<DirectedEdge> empty(memory);
|
||||
next_edges_[{prev_vertex, current_depth - 1}] = std::move(empty);
|
||||
// Update the parent
|
||||
if (next_edges_.find({prev_vertex, current_depth - 1}) == next_edges_.end()) {
|
||||
utils::pmr::list<DirectedEdge> empty(memory);
|
||||
next_edges_[{prev_vertex, current_depth - 1}] = std::move(empty);
|
||||
}
|
||||
|
||||
next_edges_.at({prev_vertex, current_depth - 1}).emplace_back(*maybe_directed_edge);
|
||||
}
|
||||
|
||||
next_edges_.at({prev_vertex, current_depth - 1}).emplace_back(directed_edge);
|
||||
}
|
||||
|
||||
if (start_vertex && next_edges_.find({*start_vertex, 0}) != next_edges_.end()) {
|
||||
@@ -2107,8 +2112,8 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
// Priority queue comparator. Keep lowest weight on top of the queue.
|
||||
class PriorityQueueComparator {
|
||||
public:
|
||||
bool operator()(const std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge> &lhs,
|
||||
const std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge> &rhs) {
|
||||
bool operator()(const std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<DirectedEdge>> &lhs,
|
||||
const std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<DirectedEdge>> &rhs) {
|
||||
const auto &lhs_weight = std::get<0>(lhs);
|
||||
const auto &rhs_weight = std::get<0>(rhs);
|
||||
// Null defines minimum value for all types
|
||||
@@ -2127,8 +2132,8 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
|
||||
// Priority queue - core element of the algorithm.
|
||||
// Stores: {weight, depth, next vertex, edge and direction}
|
||||
std::priority_queue<std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge>,
|
||||
utils::pmr::vector<std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge>>,
|
||||
std::priority_queue<std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<DirectedEdge>>,
|
||||
utils::pmr::vector<std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<DirectedEdge>>>,
|
||||
PriorityQueueComparator>
|
||||
pq_;
|
||||
|
||||
@@ -4523,24 +4528,24 @@ auto ToOptionalString(ExpressionEvaluator *evaluator, Expression *expression) ->
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
TypedValue CsvRowToTypedList(csv::Reader::Row &row) {
|
||||
TypedValue CsvRowToTypedList(csv::Reader::Row row) {
|
||||
auto *mem = row.get_allocator().GetMemoryResource();
|
||||
auto typed_columns = utils::pmr::vector<TypedValue>(mem);
|
||||
typed_columns.reserve(row.size());
|
||||
for (auto &column : row) {
|
||||
typed_columns.emplace_back(std::move(column));
|
||||
}
|
||||
return {std::move(typed_columns), mem};
|
||||
return TypedValue(typed_columns, mem);
|
||||
}
|
||||
|
||||
TypedValue CsvRowToTypedMap(csv::Reader::Row &row, csv::Reader::Header header) {
|
||||
TypedValue CsvRowToTypedMap(csv::Reader::Row row, csv::Reader::Header header) {
|
||||
// a valid row has the same number of elements as the header
|
||||
auto *mem = row.get_allocator().GetMemoryResource();
|
||||
utils::pmr::map<utils::pmr::string, TypedValue> m(mem);
|
||||
for (auto i = 0; i < row.size(); ++i) {
|
||||
m.emplace(std::move(header[i]), std::move(row[i]));
|
||||
}
|
||||
return {std::move(m), mem};
|
||||
return TypedValue(m, mem);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -4579,17 +4584,18 @@ class LoadCsvCursor : public Cursor {
|
||||
// have to read at most cardinality(n) rows (but we can read less and stop
|
||||
// pulling MATCH).
|
||||
if (!input_is_once_ && !input_pulled) return false;
|
||||
auto row = reader_->GetNextRow(context.evaluation_context.memory);
|
||||
if (!row) {
|
||||
return false;
|
||||
|
||||
if (auto row = reader_->GetNextRow(context.evaluation_context.memory)) {
|
||||
if (!reader_->HasHeader()) {
|
||||
frame[self_->row_var_] = CsvRowToTypedList(std::move(*row));
|
||||
} else {
|
||||
frame[self_->row_var_] = CsvRowToTypedMap(
|
||||
std::move(*row), csv::Reader::Header(reader_->GetHeader(), context.evaluation_context.memory));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!reader_->HasHeader()) {
|
||||
frame[self_->row_var_] = CsvRowToTypedList(*row);
|
||||
} else {
|
||||
frame[self_->row_var_] =
|
||||
CsvRowToTypedMap(*row, csv::Reader::Header(reader_->GetHeader(), context.evaluation_context.memory));
|
||||
}
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Reset() override { input_cursor_->Reset(); }
|
||||
|
||||
@@ -91,25 +91,18 @@ bool ReadWriteTypeChecker::PreVisit([[maybe_unused]] Foreach &op) {
|
||||
bool ReadWriteTypeChecker::Visit(Once &) { return false; } // NOLINT(hicpp-named-parameter)
|
||||
|
||||
void ReadWriteTypeChecker::UpdateType(RWType op_type) {
|
||||
// Update type only if it's not the NONE type and the current operator's type
|
||||
// is different than the one that's currently inferred.
|
||||
if (type != RWType::NONE && type != op_type) {
|
||||
type = RWType::RW;
|
||||
}
|
||||
// Stop inference because RW is the most "dominant" type, i.e. it isn't
|
||||
// affected by the type of nodes in the plan appearing after the node for
|
||||
// which the type is set to RW.
|
||||
if (type == RWType::RW) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if op_type is NONE, type doesn't change.
|
||||
if (op_type == RWType::NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update type only if it's not the NONE type and the current operator's type
|
||||
// is different than the one that's currently inferred.
|
||||
if (type != RWType::NONE && type != op_type) {
|
||||
type = RWType::RW;
|
||||
}
|
||||
|
||||
if (type == RWType::NONE) {
|
||||
if (type == RWType::NONE && op_type != RWType::NONE) {
|
||||
type = op_type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace memgraph::query::plan {
|
||||
|
||||
struct ReadWriteTypeChecker : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
class ReadWriteTypeChecker : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
public:
|
||||
ReadWriteTypeChecker() = default;
|
||||
|
||||
@@ -89,6 +89,7 @@ struct ReadWriteTypeChecker : public virtual HierarchicalLogicalOperatorVisitor
|
||||
|
||||
bool Visit(Once &) override;
|
||||
|
||||
private:
|
||||
void UpdateType(RWType op_type);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -444,8 +444,6 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
|
||||
bool PreVisit(Foreach &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
op.input()->Accept(*this);
|
||||
RewriteBranch(&op.update_clauses_);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -174,11 +174,7 @@ class RuleBasedPlanner {
|
||||
input_op = PlanMatching(match_ctx, std::move(input_op));
|
||||
for (const auto &matching : query_part.optional_matching) {
|
||||
MatchContext opt_ctx{matching, *context.symbol_table, context.bound_symbols};
|
||||
|
||||
std::vector<Symbol> bound_symbols(context_->bound_symbols.begin(), context_->bound_symbols.end());
|
||||
auto once_with_symbols = std::make_unique<Once>(bound_symbols);
|
||||
|
||||
auto match_op = PlanMatching(opt_ctx, std::move(once_with_symbols));
|
||||
auto match_op = PlanMatching(opt_ctx, nullptr);
|
||||
if (match_op) {
|
||||
input_op = std::make_unique<Optional>(std::move(input_op), std::move(match_op), opt_ctx.new_symbols);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "storage/v2/edge_accessor.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include "storage/v2/mvcc.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
@@ -22,47 +21,8 @@
|
||||
namespace memgraph::storage {
|
||||
|
||||
bool EdgeAccessor::IsVisible(const View view) const {
|
||||
bool exists = true;
|
||||
bool deleted = true;
|
||||
// When edges don't have properties, their isolation level is still dictated by MVCC ->
|
||||
// iterate over the deltas of the from_vertex_ and see which deltas can be applied on edges.
|
||||
if (!config_.properties_on_edges) {
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(from_vertex_->lock);
|
||||
// Initialize deleted by checking if out edges contain edge_
|
||||
deleted = std::find_if(from_vertex_->out_edges.begin(), from_vertex_->out_edges.end(), [&](const auto &out_edge) {
|
||||
return std::get<2>(out_edge) == edge_;
|
||||
}) == from_vertex_->out_edges.end();
|
||||
delta = from_vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::RECREATE_OBJECT:
|
||||
case Delta::Action::DELETE_OBJECT:
|
||||
break;
|
||||
case Delta::Action::ADD_OUT_EDGE: { // relevant for the from_vertex_ -> we just deleted the edge
|
||||
if (delta.vertex_edge.edge == edge_) {
|
||||
deleted = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::REMOVE_OUT_EDGE: { // also relevant for the from_vertex_ -> we just added the edge
|
||||
if (delta.vertex_edge.edge == edge_) {
|
||||
exists = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return exists && (for_deleted_ || !deleted);
|
||||
}
|
||||
|
||||
bool exists = true;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock);
|
||||
@@ -89,6 +49,7 @@ bool EdgeAccessor::IsVisible(const View view) const {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return exists && (for_deleted_ || !deleted);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ std::optional<utils::pmr::string> Reader::GetNextLine(utils::MemoryResource *mem
|
||||
return std::nullopt;
|
||||
}
|
||||
++line_count_;
|
||||
return std::move(line);
|
||||
return line;
|
||||
}
|
||||
|
||||
Reader::ParsingResult Reader::ParseHeader() {
|
||||
|
||||
@@ -251,10 +251,9 @@ void Pool::Release() {
|
||||
|
||||
} // namespace impl
|
||||
|
||||
PoolResource::PoolResource(size_t max_blocks_per_chunk, size_t max_block_size, MemoryResource *memory_pools,
|
||||
MemoryResource *memory_unpooled)
|
||||
: pools_(memory_pools),
|
||||
unpooled_(memory_unpooled),
|
||||
PoolResource::PoolResource(size_t max_blocks_per_chunk, size_t max_block_size, MemoryResource *memory)
|
||||
: pools_(memory),
|
||||
unpooled_(memory),
|
||||
max_blocks_per_chunk_(std::min(max_blocks_per_chunk, static_cast<size_t>(impl::Pool::MaxBlocksInChunk()))),
|
||||
max_block_size_(max_block_size) {
|
||||
MG_ASSERT(max_blocks_per_chunk_ > 0U, "Invalid number of blocks per chunk");
|
||||
@@ -274,14 +273,14 @@ void *PoolResource::DoAllocate(size_t bytes, size_t alignment) {
|
||||
if (block_size % alignment != 0) throw BadAlloc("Requested bytes must be a multiple of alignment");
|
||||
if (block_size > max_block_size_) {
|
||||
// Allocate a big block.
|
||||
BigBlock big_block{bytes, alignment, GetUpstreamResourceBlocks()->Allocate(bytes, alignment)};
|
||||
BigBlock big_block{bytes, alignment, GetUpstreamResource()->Allocate(bytes, alignment)};
|
||||
// Insert the big block in the sorted position.
|
||||
auto it = std::lower_bound(unpooled_.begin(), unpooled_.end(), big_block,
|
||||
[](const auto &a, const auto &b) { return a.data < b.data; });
|
||||
try {
|
||||
unpooled_.insert(it, big_block);
|
||||
} catch (...) {
|
||||
GetUpstreamResourceBlocks()->Deallocate(big_block.data, bytes, alignment);
|
||||
GetUpstreamResource()->Deallocate(big_block.data, bytes, alignment);
|
||||
throw;
|
||||
}
|
||||
return big_block.data;
|
||||
@@ -319,7 +318,7 @@ void PoolResource::DoDeallocate(void *p, size_t bytes, size_t alignment) {
|
||||
MG_ASSERT(it != unpooled_.end(), "Failed deallocation");
|
||||
MG_ASSERT(it->data == p && it->bytes == bytes && it->alignment == alignment, "Failed deallocation");
|
||||
unpooled_.erase(it);
|
||||
GetUpstreamResourceBlocks()->Deallocate(p, bytes, alignment);
|
||||
GetUpstreamResource()->Deallocate(p, bytes, alignment);
|
||||
return;
|
||||
}
|
||||
// Deallocate a regular block, first check if last_dealloc_pool_ is suitable.
|
||||
@@ -340,7 +339,7 @@ void PoolResource::Release() {
|
||||
for (auto &pool : pools_) pool.Release();
|
||||
pools_.clear();
|
||||
for (auto &big_block : unpooled_)
|
||||
GetUpstreamResourceBlocks()->Deallocate(big_block.data, big_block.bytes, big_block.alignment);
|
||||
GetUpstreamResource()->Deallocate(big_block.data, big_block.bytes, big_block.alignment);
|
||||
unpooled_.clear();
|
||||
last_alloc_pool_ = nullptr;
|
||||
last_dealloc_pool_ = nullptr;
|
||||
|
||||
@@ -469,8 +469,7 @@ class PoolResource final : public MemoryResource {
|
||||
/// impl::Pool::MaxBlocksInChunk()) as the real maximum number of blocks per
|
||||
/// chunk. Allocation requests exceeding max_block_size are simply forwarded
|
||||
/// to upstream memory.
|
||||
PoolResource(size_t max_blocks_per_chunk, size_t max_block_size, MemoryResource *memory_pools = NewDeleteResource(),
|
||||
MemoryResource *memory_unpooled = NewDeleteResource());
|
||||
PoolResource(size_t max_blocks_per_chunk, size_t max_block_size, MemoryResource *memory = NewDeleteResource());
|
||||
|
||||
PoolResource(const PoolResource &) = delete;
|
||||
PoolResource &operator=(const PoolResource &) = delete;
|
||||
@@ -481,7 +480,6 @@ class PoolResource final : public MemoryResource {
|
||||
~PoolResource() override { Release(); }
|
||||
|
||||
MemoryResource *GetUpstreamResource() const { return pools_.get_allocator().GetMemoryResource(); }
|
||||
MemoryResource *GetUpstreamResourceBlocks() const { return unpooled_.get_allocator().GetMemoryResource(); }
|
||||
|
||||
/// Release all allocated memory.
|
||||
void Release();
|
||||
|
||||
@@ -20,10 +20,3 @@ add_subdirectory(procedures)
|
||||
add_dependencies(memgraph__e2e__triggers__on_create memgraph__e2e__triggers__write.py)
|
||||
add_dependencies(memgraph__e2e__triggers__on_update memgraph__e2e__triggers__write.py)
|
||||
add_dependencies(memgraph__e2e__triggers__on_delete memgraph__e2e__triggers__write.py)
|
||||
|
||||
function(copy_triggers_e2e_python_files FILE_NAME)
|
||||
copy_e2e_python_files(triggers ${FILE_NAME})
|
||||
endfunction()
|
||||
|
||||
copy_triggers_e2e_python_files(common.py)
|
||||
copy_triggers_e2e_python_files(triggers_properties_false.py)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# Copyright 2022 Memgraph Ltd.
|
||||
#
|
||||
# Use of this software is governed by the Business Source License
|
||||
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
# License, and you may not use this file except in compliance with the Business Source License.
|
||||
#
|
||||
# As of the Change Date specified in that file, in accordance with
|
||||
# the Business Source License, use of this software will be governed
|
||||
# by the Apache License, Version 2.0, included in the file
|
||||
# licenses/APL.txt.
|
||||
|
||||
import typing
|
||||
|
||||
import mgclient
|
||||
import pytest
|
||||
|
||||
|
||||
def execute_and_fetch_all(cursor: mgclient.Cursor, query: str, params: dict = {}) -> typing.List[tuple]:
|
||||
cursor.execute(query, params)
|
||||
return cursor.fetchall()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def connect(**kwargs) -> mgclient.Connection:
|
||||
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
|
||||
connection.autocommit = True
|
||||
triggers_list = execute_and_fetch_all(connection.cursor(), "SHOW TRIGGERS;")
|
||||
for trigger in triggers_list:
|
||||
execute_and_fetch_all(connection.cursor(), f"DROP TRIGGER {trigger[0]}")
|
||||
execute_and_fetch_all(connection.cursor(), "MATCH (n) DETACH DELETE n")
|
||||
yield connection
|
||||
for trigger in triggers_list:
|
||||
execute_and_fetch_all(connection.cursor(), f"DROP TRIGGER {trigger[0]}")
|
||||
execute_and_fetch_all(connection.cursor(), "MATCH (n) DETACH DELETE n")
|
||||
@@ -1,170 +0,0 @@
|
||||
# Copyright 2022 Memgraph Ltd.
|
||||
#
|
||||
# Use of this software is governed by the Business Source License
|
||||
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
# License, and you may not use this file except in compliance with the Business Source License.
|
||||
#
|
||||
# As of the Change Date specified in that file, in accordance with
|
||||
# the Business Source License, use of this software will be governed
|
||||
# by the Apache License, Version 2.0, included in the file
|
||||
# licenses/APL.txt.
|
||||
|
||||
import sys
|
||||
|
||||
import mgclient
|
||||
import pytest
|
||||
from common import connect, execute_and_fetch_all
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ba_commit", ["BEFORE COMMIT", "AFTER COMMIT"])
|
||||
def test_create_on_create(ba_commit, connect):
|
||||
"""
|
||||
Args:
|
||||
ba_commit (str): BEFORE OR AFTER commit
|
||||
"""
|
||||
cursor = connect.cursor()
|
||||
QUERY_TRIGGER_CREATE = f"""
|
||||
CREATE TRIGGER CreateTriggerEdgesCount
|
||||
ON --> CREATE
|
||||
{ba_commit}
|
||||
EXECUTE
|
||||
CREATE (n:CreatedEdge {{count: size(createdEdges)}})
|
||||
"""
|
||||
execute_and_fetch_all(cursor, QUERY_TRIGGER_CREATE)
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 1})")
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 2})")
|
||||
res = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
|
||||
assert len(res) == 2
|
||||
res2 = execute_and_fetch_all(cursor, "MATCH (n:CreatedEdge) RETURN n")
|
||||
assert len(res2) == 0
|
||||
QUERY_CREATE_EDGE = """
|
||||
MATCH (n:Node {id: 1}), (m:Node {id: 2})
|
||||
CREATE (n)-[r:TYPE]->(m);
|
||||
"""
|
||||
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE)
|
||||
# See if trigger was triggered
|
||||
nodes = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
|
||||
assert len(nodes) == 2
|
||||
created_edges = execute_and_fetch_all(cursor, "MATCH (n:CreatedEdge) RETURN n")
|
||||
assert len(created_edges) == 1
|
||||
# execute_and_fetch_all(cursor, "DROP TRIGGER CreateTriggerEdgesCount")
|
||||
# execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n;")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ba_commit", ["AFTER COMMIT", "BEFORE COMMIT"])
|
||||
def test_create_on_delete(ba_commit, connect):
|
||||
"""
|
||||
Args:
|
||||
ba_commit (str): BEFORE OR AFTER commit
|
||||
"""
|
||||
cursor = connect.cursor()
|
||||
QUERY_TRIGGER_CREATE = f"""
|
||||
CREATE TRIGGER DeleteTriggerEdgesCount
|
||||
ON --> DELETE
|
||||
{ba_commit}
|
||||
EXECUTE
|
||||
CREATE (n:DeletedEdge {{count: size(deletedEdges)}})
|
||||
"""
|
||||
# Setup queries
|
||||
execute_and_fetch_all(cursor, QUERY_TRIGGER_CREATE)
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 1})")
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 2})")
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 3})")
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 4})")
|
||||
res = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
|
||||
assert len(res) == 4
|
||||
res2 = execute_and_fetch_all(cursor, "MATCH (n:DeletedEdge) RETURN n")
|
||||
assert len(res2) == 0
|
||||
# create an edge that will be deleted
|
||||
QUERY_CREATE_EDGE = """
|
||||
MATCH (n:Node {id: 1}), (m:Node {id: 2})
|
||||
CREATE (n)-[r:TYPE]->(m);
|
||||
"""
|
||||
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE)
|
||||
# create an edge that won't be deleted
|
||||
QUERY_CREATE_EDGE_NO_DELETE = """
|
||||
MATCH (n:Node {id: 3}), (m:Node {id: 4})
|
||||
CREATE (n)-[r:NO_DELETE_EDGE]->(m);
|
||||
"""
|
||||
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE_NO_DELETE)
|
||||
# Delete only one type of the edger
|
||||
QUERY_DELETE_EDGE = """
|
||||
MATCH ()-[r:TYPE]->()
|
||||
DELETE r;
|
||||
"""
|
||||
execute_and_fetch_all(cursor, QUERY_DELETE_EDGE)
|
||||
# See if trigger was triggered
|
||||
nodes = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
|
||||
assert len(nodes) == 4
|
||||
# Check how many edges got deleted
|
||||
deleted_edges = execute_and_fetch_all(cursor, "MATCH (n:DeletedEdge) RETURN n")
|
||||
assert len(deleted_edges) == 1
|
||||
# execute_and_fetch_all(cursor, "DROP TRIGGER DeleteTriggerEdgesCount")
|
||||
# execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")``
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ba_commit", ["BEFORE COMMIT", "AFTER COMMIT"])
|
||||
def test_create_on_delete_explicit_transaction(ba_commit):
|
||||
"""
|
||||
Args:
|
||||
ba_commit (str): BEFORE OR AFTER commit
|
||||
"""
|
||||
connection_with_autocommit = mgclient.connect(host="localhost", port=7687)
|
||||
connection_with_autocommit.autocommit = True
|
||||
cursor_autocommit = connection_with_autocommit.cursor()
|
||||
QUERY_TRIGGER_CREATE = f"""
|
||||
CREATE TRIGGER DeleteTriggerEdgesCountExplicit
|
||||
ON --> DELETE
|
||||
{ba_commit}
|
||||
EXECUTE
|
||||
CREATE (n:DeletedEdge {{count: size(deletedEdges)}})
|
||||
"""
|
||||
# Setup queries
|
||||
execute_and_fetch_all(cursor_autocommit, QUERY_TRIGGER_CREATE)
|
||||
# Start explicit transaction on the execution of the first command
|
||||
connection_without_autocommit = mgclient.connect(host="localhost", port=7687)
|
||||
connection_without_autocommit.autocommit = False
|
||||
cursor = connection_without_autocommit.cursor()
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 1})")
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 2})")
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 3})")
|
||||
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 4})")
|
||||
res = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
|
||||
assert len(res) == 4
|
||||
res2 = execute_and_fetch_all(cursor, "MATCH (n:DeletedEdge) RETURN n;")
|
||||
assert len(res2) == 0
|
||||
QUERY_CREATE_EDGE = """
|
||||
MATCH (n:Node {id: 1}), (m:Node {id: 2})
|
||||
CREATE (n)-[r:TYPE]->(m);
|
||||
"""
|
||||
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE)
|
||||
# create an edge that won't be deleted
|
||||
QUERY_CREATE_EDGE_NO_DELETE = """
|
||||
MATCH (n:Node {id: 3}), (m:Node {id: 4})
|
||||
CREATE (n)-[r:NO_DELETE_EDGE]->(m);
|
||||
"""
|
||||
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE_NO_DELETE)
|
||||
# Delete only one type of the edger
|
||||
QUERY_DELETE_EDGE = """
|
||||
MATCH ()-[r:TYPE]->()
|
||||
DELETE r;
|
||||
"""
|
||||
execute_and_fetch_all(cursor, QUERY_DELETE_EDGE)
|
||||
connection_without_autocommit.commit() # finish explicit transaction
|
||||
nodes = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
|
||||
assert len(nodes) == 4
|
||||
# Check how many edges got deleted
|
||||
deleted_nodes_edges = execute_and_fetch_all(cursor, "MATCH (n:DeletedEdge) RETURN n")
|
||||
assert len(deleted_nodes_edges) == 0
|
||||
# Delete with the original cursor because triggers aren't allowed in multi-transaction environment
|
||||
execute_and_fetch_all(cursor_autocommit, "DROP TRIGGER DeleteTriggerEdgesCountExplicit")
|
||||
execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")
|
||||
connection_without_autocommit.commit() # finish explicit transaction
|
||||
nodes = execute_and_fetch_all(cursor_autocommit, "MATCH (n) RETURN n")
|
||||
assert len(nodes) == 0
|
||||
connection_with_autocommit.close()
|
||||
connection_without_autocommit.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main([__file__, "-rA"]))
|
||||
@@ -2,14 +2,7 @@ bolt_port: &bolt_port "7687"
|
||||
template_cluster: &template_cluster
|
||||
cluster:
|
||||
main:
|
||||
args: ["--bolt-port", *bolt_port, "--log-level=TRACE", "--storage-properties-on-edges=True"]
|
||||
log_file: "triggers-e2e.log"
|
||||
setup_queries: []
|
||||
validation_queries: []
|
||||
storage_properties_edges_false: &storage_properties_edges_false
|
||||
cluster:
|
||||
main:
|
||||
args: ["--bolt-port", *bolt_port, "--log-level=TRACE", "--also-log-to-stderr", "--storage-properties-on-edges=False"]
|
||||
args: ["--bolt-port", *bolt_port, "--log-level=TRACE"]
|
||||
log_file: "triggers-e2e.log"
|
||||
setup_queries: []
|
||||
validation_queries: []
|
||||
@@ -25,7 +18,7 @@ workloads:
|
||||
args: ["--bolt-port", *bolt_port]
|
||||
proc: "tests/e2e/triggers/procedures/"
|
||||
<<: *template_cluster
|
||||
- name: "ON DELETE Triggers Storage Properties On Edges True"
|
||||
- name: "ON DELETE Triggers"
|
||||
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_delete"
|
||||
args: ["--bolt-port", *bolt_port]
|
||||
proc: "tests/e2e/triggers/procedures/"
|
||||
@@ -34,8 +27,5 @@ workloads:
|
||||
binary: "tests/e2e/triggers/memgraph__e2e__triggers__privileges"
|
||||
args: ["--bolt-port", *bolt_port]
|
||||
<<: *template_cluster
|
||||
- name: "ON DELETE Triggers Storage Properties On Edges False" # should be the same as the python file
|
||||
binary: "tests/e2e/pytest_runner.sh"
|
||||
proc: "tests/e2e/triggers/procedures/"
|
||||
args: ["triggers/triggers_properties_false.py"]
|
||||
<<: *storage_properties_edges_false
|
||||
|
||||
|
||||
|
||||
@@ -66,13 +66,29 @@ Feature: Foreach
|
||||
| 4 |
|
||||
And no side effects
|
||||
|
||||
Scenario: Foreach shadowing in create
|
||||
Given an empty graph
|
||||
And having executed
|
||||
"""
|
||||
FOREACH (i IN [1] | FOREACH (j IN [3,4] | CREATE (i {prop: j})));
|
||||
"""
|
||||
When executing query:
|
||||
"""
|
||||
MATCH (n) RETURN n.prop
|
||||
"""
|
||||
Then the result should be:
|
||||
| n.prop |
|
||||
| 3 |
|
||||
| 4 |
|
||||
And no side effects
|
||||
|
||||
Scenario: Foreach set
|
||||
Given an empty graph
|
||||
And having executed
|
||||
"""
|
||||
CREATE (n1 { marked: false })-[:RELATES]->(n2 { marked: false })
|
||||
"""
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
MATCH p=(n1)-[*]->(n2)
|
||||
FOREACH (n IN nodes(p) | SET n.marked = true)
|
||||
@@ -94,7 +110,7 @@ Feature: Foreach
|
||||
"""
|
||||
CREATE (n1 { marked: false })-[:RELATES]->(n2 { marked: false })
|
||||
"""
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
MATCH p=(n1)-[*]->(n2)
|
||||
FOREACH (n IN nodes(p) | REMOVE n.marked)
|
||||
@@ -116,7 +132,7 @@ Feature: Foreach
|
||||
"""
|
||||
CREATE (n1 { marked: false })-[:RELATES]->(n2 { marked: false })
|
||||
"""
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
MATCH p=(n1)-[*]->(n2)
|
||||
FOREACH (n IN nodes(p) | DETACH delete n)
|
||||
@@ -132,7 +148,7 @@ Feature: Foreach
|
||||
|
||||
Scenario: Foreach merge
|
||||
Given an empty graph
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
FOREACH (i IN [1, 2, 3] | MERGE (n { age : i }))
|
||||
"""
|
||||
@@ -150,7 +166,7 @@ Feature: Foreach
|
||||
|
||||
Scenario: Foreach nested
|
||||
Given an empty graph
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
FOREACH (i IN [1, 2, 3] | FOREACH( j IN [1] | CREATE (k { prop : j })))
|
||||
"""
|
||||
@@ -167,11 +183,11 @@ Feature: Foreach
|
||||
|
||||
Scenario: Foreach multiple update clauses
|
||||
Given an empty graph
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
CREATE (n1 { marked1: false, marked2: false })-[:RELATES]->(n2 { marked1: false, marked2: false })
|
||||
"""
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
MATCH p=(n1)-[*]->(n2)
|
||||
FOREACH (n IN nodes(p) | SET n.marked1 = true SET n.marked2 = true)
|
||||
@@ -189,11 +205,11 @@ Feature: Foreach
|
||||
|
||||
Scenario: Foreach multiple nested update clauses
|
||||
Given an empty graph
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
CREATE (n1 { marked1: false, marked2: false })-[:RELATES]->(n2 { marked1: false, marked2: false })
|
||||
"""
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
MATCH p=(n1)-[*]->(n2)
|
||||
FOREACH (n IN nodes(p) | FOREACH (j IN [1] | SET n.marked1 = true SET n.marked2 = true))
|
||||
@@ -211,7 +227,7 @@ Feature: Foreach
|
||||
|
||||
Scenario: Foreach match foreach return
|
||||
Given an empty graph
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
CREATE (n {prop: [[], [1,2]]});
|
||||
"""
|
||||
@@ -226,7 +242,7 @@ Feature: Foreach
|
||||
|
||||
Scenario: Foreach on null value
|
||||
Given an empty graph
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
CREATE (n);
|
||||
"""
|
||||
@@ -238,9 +254,9 @@ Feature: Foreach
|
||||
| |
|
||||
And no side effects
|
||||
|
||||
Scenario: Foreach nested merge
|
||||
Scenario: Foreach nested merge
|
||||
Given an empty graph
|
||||
And having executed
|
||||
And having executed
|
||||
"""
|
||||
FOREACH(i in [1, 2, 3] | foreach(j in [1] | MERGE (n { age : i })));
|
||||
"""
|
||||
|
||||
@@ -15,26 +15,6 @@ Feature: All Shortest Path
|
||||
| '1' |
|
||||
| '3' |
|
||||
|
||||
Scenario: Test match allShortest upper bound 2
|
||||
Given an empty graph
|
||||
And having executed:
|
||||
"""
|
||||
CREATE (a {a:'0'})-[:r {w: 2}]->(b {a:'1'})-[:r {w: 3}]->(c {a:'2'}),
|
||||
(a)-[:re {w: 2}]->(b),
|
||||
(b)-[:re {w:3}]->(c),
|
||||
({a: '4'})<-[:r {w: 1}]-(a),
|
||||
({a: '5'})<-[:r {w: 1}]-(a),
|
||||
(c)-[:r {w: 1}]->({a: '6'}),
|
||||
(c)-[:r {w: 1}]->({a: '7'})
|
||||
"""
|
||||
When executing query:
|
||||
"""
|
||||
MATCH path=(n {a:'0'})-[r *allShortest ..2 (e, n | 1 ) w]->(m {a:'2'}) RETURN COUNT(path) AS c
|
||||
"""
|
||||
Then the result should be:
|
||||
| c |
|
||||
| 4 |
|
||||
|
||||
Scenario: Test match allShortest filtered
|
||||
Given an empty graph
|
||||
And having executed:
|
||||
|
||||
@@ -45,21 +45,6 @@ class BoltClient : public ::testing::Test {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExecuteAndCheckQid(const std::string &query, int qid, const std::string &message = "") {
|
||||
try {
|
||||
auto ret = client_.Execute(query, {});
|
||||
if (ret.metadata["qid"].ValueInt() != qid) {
|
||||
return false;
|
||||
}
|
||||
} catch (const ClientQueryException &e) {
|
||||
if (message != "") {
|
||||
EXPECT_EQ(e.what(), message);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t GetCount() {
|
||||
auto ret = client_.Execute("match (n) return count(n)", {});
|
||||
EXPECT_EQ(ret.records.size(), 1);
|
||||
@@ -476,18 +461,6 @@ TEST_F(BoltClient, MixedCaseAndWhitespace) {
|
||||
EXPECT_FALSE(TransactionActive());
|
||||
}
|
||||
|
||||
TEST_F(BoltClient, TestQid) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
EXPECT_TRUE(Execute("match (n) return count(n)"));
|
||||
}
|
||||
EXPECT_TRUE(Execute("begin"));
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
EXPECT_TRUE(ExecuteAndCheckQid("match (n) return count(n)", i + 1));
|
||||
}
|
||||
EXPECT_TRUE(Execute("commit"));
|
||||
EXPECT_FALSE(TransactionActive());
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
@@ -142,7 +142,7 @@ parser.add_argument(
|
||||
with the presence of 300 write queries from write type or 30%""",
|
||||
)
|
||||
|
||||
parser.add_argument("--tail-latency", type=int, default=0, help="Number of queries for the tail latency statistics")
|
||||
parser.add_argument("--tail-latency", type=int, default=100, help="Number of queries for the tail latency statistics")
|
||||
|
||||
parser.add_argument(
|
||||
"--performance-tracking",
|
||||
@@ -223,17 +223,8 @@ def filter_benchmarks(generators, patterns):
|
||||
patterns,
|
||||
):
|
||||
current[group].append((query_name, query_func))
|
||||
if len(current) == 0:
|
||||
continue
|
||||
|
||||
# Ignore benchgraph "basic" queries in standard CI/CD run
|
||||
for pattern in patterns:
|
||||
res = pattern.count("*")
|
||||
key = "basic"
|
||||
if res >= 2 and key in current.keys():
|
||||
current.pop(key)
|
||||
|
||||
filtered.append((generator(variant, args.vendor_name), dict(current)))
|
||||
if len(current) > 0:
|
||||
filtered.append((generator(variant, args.vendor_name), dict(current)))
|
||||
return filtered
|
||||
|
||||
|
||||
@@ -250,34 +241,30 @@ def warmup(client):
|
||||
|
||||
|
||||
def tail_latency(vendor, client, func):
|
||||
vendor.start_benchmark("tail_latency")
|
||||
if args.warmup_run:
|
||||
warmup(client)
|
||||
latency = []
|
||||
iteration = args.tail_latency
|
||||
if iteration >= 10:
|
||||
vendor.start_benchmark("tail_latency")
|
||||
if args.warmup_run:
|
||||
warmup(client)
|
||||
latency = []
|
||||
|
||||
query_list = get_queries(func, iteration)
|
||||
for i in range(0, iteration):
|
||||
ret = client.execute(queries=[query_list[i]], num_workers=1)
|
||||
latency.append(ret[0]["duration"])
|
||||
latency.sort()
|
||||
query_stats = {
|
||||
"iterations": iteration,
|
||||
"min": latency[0],
|
||||
"max": latency[iteration - 1],
|
||||
"mean": statistics.mean(latency),
|
||||
"p99": latency[math.floor(iteration * 0.99) - 1],
|
||||
"p95": latency[math.floor(iteration * 0.95) - 1],
|
||||
"p90": latency[math.floor(iteration * 0.90) - 1],
|
||||
"p75": latency[math.floor(iteration * 0.75) - 1],
|
||||
"p50": latency[math.floor(iteration * 0.50) - 1],
|
||||
}
|
||||
print("Query statistics for tail latency: ")
|
||||
print(query_stats)
|
||||
vendor.stop("tail_latency")
|
||||
else:
|
||||
query_stats = {}
|
||||
query_list = get_queries(func, iteration)
|
||||
for i in range(0, iteration):
|
||||
ret = client.execute(queries=[query_list[i]], num_workers=1)
|
||||
latency.append(ret[0]["duration"])
|
||||
latency.sort()
|
||||
query_stats = {
|
||||
"iterations": iteration,
|
||||
"min": latency[0],
|
||||
"max": latency[iteration - 1],
|
||||
"mean": statistics.mean(latency),
|
||||
"p99": latency[math.floor(iteration * 0.99) - 1],
|
||||
"p95": latency[math.floor(iteration * 0.95) - 1],
|
||||
"p90": latency[math.floor(iteration * 0.90) - 1],
|
||||
"p75": latency[math.floor(iteration * 0.75) - 1],
|
||||
"p50": latency[math.floor(iteration * 0.50) - 1],
|
||||
}
|
||||
print("Query statistics for tail latency: ")
|
||||
print(query_stats)
|
||||
vendor.stop("tail_latency")
|
||||
return query_stats
|
||||
|
||||
|
||||
|
||||
@@ -147,8 +147,6 @@ def run_full_benchmarks(vendor, binary, dataset_size, dataset_group, realistic,
|
||||
"12",
|
||||
"--no-authorization",
|
||||
"pokec/" + dataset_size + "/" + dataset_group + "/*",
|
||||
"--tail-latency",
|
||||
"100",
|
||||
]
|
||||
|
||||
for config in configurations:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -58,7 +58,7 @@ TEST_F(CppApiTestFixture, TestGraph) {
|
||||
ASSERT_EQ(graph.Order(), 2);
|
||||
ASSERT_EQ(graph.Size(), 0);
|
||||
|
||||
auto relationship_1 = graph.CreateRelationship(node_1, node_2, "edge_type");
|
||||
auto relationship = graph.CreateRelationship(node_1, node_2, "edge_type");
|
||||
|
||||
ASSERT_EQ(graph.Order(), 2);
|
||||
ASSERT_EQ(graph.Size(), 1);
|
||||
@@ -66,23 +66,7 @@ TEST_F(CppApiTestFixture, TestGraph) {
|
||||
ASSERT_EQ(graph.ContainsNode(node_1), true);
|
||||
ASSERT_EQ(graph.ContainsNode(node_2), true);
|
||||
|
||||
ASSERT_EQ(graph.ContainsRelationship(relationship_1), true);
|
||||
|
||||
auto node_3 = graph.CreateNode();
|
||||
auto relationship_2 = graph.CreateRelationship(node_1, node_3, "edge_type");
|
||||
auto relationship_3 = graph.CreateRelationship(node_2, node_3, "edge_type");
|
||||
|
||||
for (const auto &n : graph.Nodes()) {
|
||||
ASSERT_EQ(graph.ContainsNode(n), true);
|
||||
}
|
||||
|
||||
std::uint64_t n_rels = 0;
|
||||
for (const auto &r : graph.Relationships()) {
|
||||
ASSERT_EQ(graph.ContainsRelationship(r), true);
|
||||
n_rels++;
|
||||
}
|
||||
|
||||
ASSERT_EQ(n_rels, 3);
|
||||
ASSERT_EQ(graph.ContainsRelationship(relationship), true);
|
||||
}
|
||||
|
||||
TEST_F(CppApiTestFixture, TestId) {
|
||||
@@ -211,7 +195,7 @@ TEST_F(CppApiTestFixture, TestNode) {
|
||||
ASSERT_EQ(node_1.HasLabel("L1"), true);
|
||||
ASSERT_EQ(node_1.HasLabel("L2"), true);
|
||||
|
||||
ASSERT_EQ(node_1.Properties().size(), 0);
|
||||
ASSERT_EQ(node_1.Properties().Size(), 0);
|
||||
|
||||
auto node_2 = graph.GetNodeById(node_1.Id());
|
||||
|
||||
@@ -280,7 +264,7 @@ TEST_F(CppApiTestFixture, TestRelationship) {
|
||||
auto relationship = graph.CreateRelationship(node_1, node_2, "edge_type");
|
||||
|
||||
ASSERT_EQ(relationship.Type(), "edge_type");
|
||||
ASSERT_EQ(relationship.Properties().size(), 0);
|
||||
ASSERT_EQ(relationship.Properties().Size(), 0);
|
||||
ASSERT_EQ(relationship.From().Id(), node_1.Id());
|
||||
ASSERT_EQ(relationship.To().Id(), node_2.Id());
|
||||
|
||||
@@ -435,19 +419,3 @@ TEST_F(CppApiTestFixture, TestDuration) {
|
||||
// Use Value move constructor
|
||||
auto value_y = mgp::Value(mgp::Duration("PT2M2.33S"));
|
||||
}
|
||||
|
||||
TEST_F(CppApiTestFixture, TestNodeProperties) {
|
||||
mgp_graph raw_graph = CreateGraph(memgraph::storage::View::NEW);
|
||||
auto graph = mgp::Graph(&raw_graph);
|
||||
|
||||
auto node_1 = graph.CreateNode();
|
||||
|
||||
ASSERT_EQ(node_1.Properties().size(), 0);
|
||||
|
||||
std::map<std::string, mgp::Value> node1_prop = node_1.Properties();
|
||||
node_1.SetProperty("b", mgp::Value("b"));
|
||||
|
||||
ASSERT_EQ(node_1.Properties().size(), 1);
|
||||
ASSERT_EQ(node_1.Properties()["b"].ValueString(), "b");
|
||||
ASSERT_EQ(node_1.GetProperty("b").ValueString(), "b");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -662,30 +662,6 @@ TYPED_TEST(TestPlanner, MatchOptionalMatchWhereReturn) {
|
||||
DeleteListContent(&optional);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPlanner, MatchOptionalMatchNodePropertyWithIndex) {
|
||||
// Test MATCH (n:Label) OPTIONAL MATCH (m:Label) WHERE n.prop = m.prop RETURN n
|
||||
AstStorage storage;
|
||||
FakeDbAccessor dba;
|
||||
|
||||
const auto label_name = "label";
|
||||
const auto label = dba.Label(label_name);
|
||||
const auto property = PROPERTY_PAIR("prop");
|
||||
dba.SetIndexCount(label, property.second, 0);
|
||||
|
||||
auto *query = QUERY(SINGLE_QUERY(
|
||||
MATCH(PATTERN(NODE("n", label_name))), OPTIONAL_MATCH(PATTERN(NODE("m", label_name))),
|
||||
WHERE(EQ(PROPERTY_LOOKUP("n", property.second), PROPERTY_LOOKUP("m", property.second))), RETURN("n")));
|
||||
|
||||
auto symbol_table = memgraph::query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
|
||||
|
||||
auto m_prop = PROPERTY_LOOKUP("m", property);
|
||||
std::list<BaseOpChecker *> optional{new ExpectScanAllByLabelPropertyValue(label, property, m_prop)};
|
||||
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectOptional(optional), ExpectProduce());
|
||||
DeleteListContent(&optional);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPlanner, MatchUnwindReturn) {
|
||||
// Test MATCH (n) UNWIND [1,2,3] AS x RETURN n, x
|
||||
AstStorage storage;
|
||||
@@ -1709,30 +1685,5 @@ TYPED_TEST(TestPlanner, Foreach) {
|
||||
QUERY(SINGLE_QUERY(FOREACH(i, {CREATE(PATTERN(NODE("n")))}), FOREACH(j, {CREATE(PATTERN(NODE("n")))})));
|
||||
CheckPlan<TypeParam>(query, storage, ExpectForeach(input, updates), ExpectEmptyResult());
|
||||
}
|
||||
|
||||
{
|
||||
// FOREACH with index
|
||||
// FOREACH (n in [...] | MERGE (v:Label));
|
||||
const auto label_name = "label";
|
||||
const auto label = dba.Label(label_name);
|
||||
dba.SetIndexCount(label, 0);
|
||||
|
||||
auto *n = NEXPR("n", IDENT("n"));
|
||||
auto *query = QUERY(SINGLE_QUERY(FOREACH(n, {MERGE(PATTERN(NODE("v", label_name)))})));
|
||||
|
||||
auto symbol_table = memgraph::query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
|
||||
|
||||
std::list<BaseOpChecker *> on_match{new ExpectScanAllByLabel()};
|
||||
std::list<BaseOpChecker *> on_create{new ExpectCreateNode()};
|
||||
|
||||
auto create = ExpectMerge(on_match, on_create);
|
||||
std::list<BaseOpChecker *> updates{&create};
|
||||
std::list<BaseOpChecker *> input;
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectForeach(input, updates), ExpectEmptyResult());
|
||||
|
||||
DeleteListContent(&on_match);
|
||||
DeleteListContent(&on_create);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -253,31 +253,3 @@ TEST_F(ReadWriteTypeCheckTest, Foreach) {
|
||||
std::shared_ptr<LogicalOperator> foreach = std::make_shared<plan::Foreach>(nullptr, nullptr, nullptr, x);
|
||||
CheckPlanType(foreach.get(), RWType::RW);
|
||||
}
|
||||
|
||||
TEST_F(ReadWriteTypeCheckTest, CheckUpdateType) {
|
||||
std::array<std::array<RWType, 3>, 16> scenarios = {{
|
||||
{RWType::NONE, RWType::NONE, RWType::NONE},
|
||||
{RWType::NONE, RWType::R, RWType::R},
|
||||
{RWType::NONE, RWType::W, RWType::W},
|
||||
{RWType::NONE, RWType::RW, RWType::RW},
|
||||
{RWType::R, RWType::NONE, RWType::R},
|
||||
{RWType::R, RWType::R, RWType::R},
|
||||
{RWType::R, RWType::W, RWType::RW},
|
||||
{RWType::R, RWType::RW, RWType::RW},
|
||||
{RWType::W, RWType::NONE, RWType::W},
|
||||
{RWType::W, RWType::R, RWType::RW},
|
||||
{RWType::W, RWType::W, RWType::W},
|
||||
{RWType::W, RWType::RW, RWType::RW},
|
||||
{RWType::RW, RWType::NONE, RWType::RW},
|
||||
{RWType::RW, RWType::R, RWType::RW},
|
||||
{RWType::RW, RWType::W, RWType::RW},
|
||||
{RWType::RW, RWType::RW, RWType::RW},
|
||||
}};
|
||||
|
||||
auto rw_type_checker = ReadWriteTypeChecker();
|
||||
for (auto scenario : scenarios) {
|
||||
rw_type_checker.type = scenario[0];
|
||||
rw_type_checker.UpdateType(scenario[1]);
|
||||
EXPECT_EQ(scenario[2], rw_type_checker.type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
|
||||
@@ -252,21 +252,20 @@ TEST(PoolResource, MultipleSmallBlockAllocations) {
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PoolResource, BigBlockAllocations) {
|
||||
TestMemory test_mem;
|
||||
TestMemory test_mem_unpooled;
|
||||
const size_t max_blocks_per_chunk = 3U;
|
||||
const size_t max_block_size = 64U;
|
||||
memgraph::utils::PoolResource mem(max_blocks_per_chunk, max_block_size, &test_mem, &test_mem_unpooled);
|
||||
memgraph::utils::PoolResource mem(max_blocks_per_chunk, max_block_size, &test_mem);
|
||||
CheckAllocation(&mem, max_block_size + 1, 1U);
|
||||
// May allocate more than once per block due to bookkeeping.
|
||||
EXPECT_GE(test_mem_unpooled.new_count_, 1U);
|
||||
EXPECT_GE(test_mem.new_count_, 1U);
|
||||
CheckAllocation(&mem, max_block_size + 1, 1U);
|
||||
EXPECT_GE(test_mem_unpooled.new_count_, 2U);
|
||||
EXPECT_GE(test_mem.new_count_, 2U);
|
||||
auto *ptr = CheckAllocation(&mem, max_block_size * 2, 1U);
|
||||
EXPECT_GE(test_mem_unpooled.new_count_, 3U);
|
||||
EXPECT_GE(test_mem.new_count_, 3U);
|
||||
mem.Deallocate(ptr, max_block_size * 2, 1U);
|
||||
EXPECT_GE(test_mem_unpooled.delete_count_, 1U);
|
||||
EXPECT_GE(test_mem.delete_count_, 1U);
|
||||
mem.Release();
|
||||
EXPECT_GE(test_mem_unpooled.delete_count_, 3U);
|
||||
EXPECT_GE(test_mem.delete_count_, 3U);
|
||||
CheckAllocation(&mem, max_block_size + 1, 1U);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user