Compare commits
69 Commits
T0850-MG-s
...
T0985-MG-g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d201e69679 | ||
|
|
929e9433ea | ||
|
|
d5041c219e | ||
|
|
c55183bd97 | ||
|
|
5de88ea24b | ||
|
|
f78b5b2f99 | ||
|
|
3c84ad16cb | ||
|
|
6706d0701f | ||
|
|
2285790521 | ||
|
|
5505257daa | ||
|
|
93a2de96c1 | ||
|
|
30638dbc75 | ||
|
|
026ccc85ee | ||
|
|
b776313c80 | ||
|
|
765145dbdf | ||
|
|
006aadac6e | ||
|
|
c2b80afd0f | ||
|
|
7d41c580dc | ||
|
|
c65ba1618b | ||
|
|
7bbc8f3c4f | ||
|
|
259d774539 | ||
|
|
d373365710 | ||
|
|
f762a7cdc8 | ||
|
|
5d7238fad7 | ||
|
|
08c6a09c77 | ||
|
|
2775fdaea3 | ||
|
|
cf09181139 | ||
|
|
7c053bd87e | ||
|
|
3281d606ef | ||
|
|
ff43f9b2d1 | ||
|
|
30864af98b | ||
|
|
80845b0233 | ||
|
|
e4a3ba7a2b | ||
|
|
8fb770568f | ||
|
|
78c459de1a | ||
|
|
60c1412416 | ||
|
|
d27ea7b869 | ||
|
|
a205030a15 | ||
|
|
e70e445899 | ||
|
|
12979c3048 | ||
|
|
261072af89 | ||
|
|
b7c8dc8ae7 | ||
|
|
094d4f282d | ||
|
|
3dd2657320 | ||
|
|
6fe474282a | ||
|
|
7fc0fb6520 | ||
|
|
063e297e1e | ||
|
|
86b1688192 | ||
|
|
f629de7e60 | ||
|
|
b737e53456 | ||
|
|
10ca68bb2a | ||
|
|
bfbd8538d4 | ||
|
|
3e0e17d469 | ||
|
|
b57f91fcfc | ||
|
|
066a96c0ae | ||
|
|
1ae6b71c5f | ||
|
|
cbe15e7f44 | ||
|
|
65a7ba01da | ||
|
|
7a2bbd4bb3 | ||
|
|
589e0e098b | ||
|
|
41d4185156 | ||
|
|
599c0a641f | ||
|
|
1fb49c4865 | ||
|
|
df1485aeec | ||
|
|
b2e1056389 | ||
|
|
e4c9411e63 | ||
|
|
a0bc1371dd | ||
|
|
21ad5d4328 | ||
|
|
8e3ab1ad0f |
@@ -61,7 +61,9 @@ Checks: '*,
|
||||
-readability-magic-numbers,
|
||||
-readability-named-parameter,
|
||||
-misc-no-recursion,
|
||||
-concurrency-mt-unsafe'
|
||||
-concurrency-mt-unsafe,
|
||||
-bugprone-easily-swappable-parameters'
|
||||
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: 'src/.*'
|
||||
AnalyzeTemporaryDtors: false
|
||||
@@ -88,4 +90,3 @@ CheckOptions:
|
||||
- key: modernize-use-nullptr.NullMacros
|
||||
value: 'NULL'
|
||||
...
|
||||
|
||||
|
||||
5
.github/workflows/diff.yaml
vendored
5
.github/workflows/diff.yaml
vendored
@@ -1,4 +1,7 @@
|
||||
name: Diff
|
||||
concurrency:
|
||||
group: ${{ github.head_ref || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -112,7 +115,7 @@ jobs:
|
||||
source /opt/toolchain-v4/activate
|
||||
|
||||
# Restrict clang-tidy results only to the modified parts
|
||||
git diff -U0 master... -- src ':!*.hpp' | ./tools/github/clang-tidy/clang-tidy-diff.py -p 1 -j $THREADS -path build | tee ./build/clang_tidy_output.txt
|
||||
git diff -U0 master... -- src | ./tools/github/clang-tidy/clang-tidy-diff.py -p 1 -j $THREADS -path build | tee ./build/clang_tidy_output.txt
|
||||
|
||||
# Fail if any warning is reported
|
||||
! cat ./build/clang_tidy_output.txt | ./tools/github/clang-tidy/grep_error_lines.sh > /dev/null
|
||||
|
||||
76
.github/workflows/package_all.yaml
vendored
76
.github/workflows/package_all.yaml
vendored
@@ -6,11 +6,11 @@ on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
centos-7:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
@@ -22,29 +22,29 @@ jobs:
|
||||
name: centos-7
|
||||
path: build/output/centos-7/memgraph*.rpm
|
||||
|
||||
centos-8:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
centos-9:
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package centos-8
|
||||
./release/package/run.sh package centos-9
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: centos-8
|
||||
path: build/output/centos-8/memgraph*.rpm
|
||||
name: centos-9
|
||||
path: build/output/centos-9/memgraph*.rpm
|
||||
|
||||
debian-10:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
@@ -57,11 +57,11 @@ jobs:
|
||||
path: build/output/debian-10/memgraph*.deb
|
||||
|
||||
debian-11:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
@@ -74,11 +74,11 @@ jobs:
|
||||
path: build/output/debian-11/memgraph*.deb
|
||||
|
||||
docker:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
@@ -93,11 +93,11 @@ jobs:
|
||||
path: build/output/docker/memgraph*.tar.gz
|
||||
|
||||
ubuntu-1804:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
@@ -110,11 +110,11 @@ jobs:
|
||||
path: build/output/ubuntu-18.04/memgraph*.deb
|
||||
|
||||
ubuntu-2004:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
@@ -126,12 +126,29 @@ jobs:
|
||||
name: ubuntu-2004
|
||||
path: build/output/ubuntu-20.04/memgraph*.deb
|
||||
|
||||
debian-11-platform:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
ubuntu-2204:
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
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
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ubuntu-2204
|
||||
path: build/output/ubuntu-22.04/memgraph*.deb
|
||||
|
||||
debian-11-platform:
|
||||
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"
|
||||
@@ -142,3 +159,20 @@ jobs:
|
||||
with:
|
||||
name: debian-11-platform
|
||||
path: build/output/debian-11/memgraph*.deb
|
||||
|
||||
debian-11-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 debian-11-arm
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: debian-11-arm
|
||||
path: build/output/debian-11-arm/memgraph*.deb
|
||||
|
||||
22
.github/workflows/release_docker.yaml
vendored
22
.github/workflows/release_docker.yaml
vendored
@@ -4,8 +4,12 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Memgraph binary version to publish on Dockerhub."
|
||||
description: "Memgraph binary version to publish on DockerHub."
|
||||
required: true
|
||||
force_release:
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
docker_publish:
|
||||
@@ -36,6 +40,22 @@ jobs:
|
||||
curl -L https://download.memgraph.com/memgraph/v${{ github.event.inputs.version }}/debian-11/memgraph_${{ github.event.inputs.version }}-1_amd64.deb > memgraph-amd64.deb
|
||||
curl -L https://download.memgraph.com/memgraph/v${{ github.event.inputs.version }}/debian-11-aarch64/memgraph_${{ github.event.inputs.version }}-1_arm64.deb > memgraph-arm64.deb
|
||||
|
||||
- name: Check if specified version is already pushed
|
||||
run: |
|
||||
EXISTS=$(docker manifest inspect $DOCKER_ORGANIZATION_NAME/$DOCKER_REPOSITORY_NAME:${{ github.event.inputs.version }} > /dev/null; echo $?)
|
||||
echo $EXISTS
|
||||
if [[ ${EXISTS} -eq 0 ]]; then
|
||||
echo 'The specified version has been already released to DockerHub.'
|
||||
if [[ ${{ github.event.inputs.force_release }} = true ]]; then
|
||||
echo 'Forcing the release!'
|
||||
else
|
||||
echo 'Stopping the release!'
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo 'All good the specified version has not been release to DockerHub.'
|
||||
fi
|
||||
|
||||
- name: Build & push docker images
|
||||
run: |
|
||||
cd release/docker
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
* @antaljanosbenjamin @kostasrim
|
||||
@@ -5,6 +5,9 @@ set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "centos-7"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc gcc-c++ make # generic build tools
|
||||
wget # used for archive download
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils-common gcc gcc-c++ make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg2 # used for archive signature verification
|
||||
tar gzip bzip2 xz unzip # used for archive unpacking
|
||||
zlib-devel # zlib library used for all builds
|
||||
expat-devel libipt libipt-devel libbabeltrace-devel xz-devel python36-devel texinfo # for gdb
|
||||
libcurl-devel # for cmake
|
||||
curl # snappy
|
||||
readline-devel # for cmake and llvm
|
||||
libffi-devel libxml2-devel # for llvm
|
||||
libedit-devel pcre-devel automake bison # for swig
|
||||
file
|
||||
openssl-devel
|
||||
gmp-devel
|
||||
gperf
|
||||
patch
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz # used for archive unpacking
|
||||
zlib # zlib library used for all builds
|
||||
expat libipt libbabeltrace xz-libs python36 # for gdb
|
||||
readline # for cmake and llvm
|
||||
libffi libxml2 # for llvm
|
||||
openssl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make pkgconf-pkg-config # build system
|
||||
curl wget # for downloading libs
|
||||
libuuid-devel java-11-openjdk # required by antlr
|
||||
readline-devel # for memgraph console
|
||||
python36-devel # for query modules
|
||||
openssl-devel
|
||||
libseccomp-devel
|
||||
python36 python3-virtualenv python3-pip nmap-ncat # for qa, macro_benchmark and stress tests
|
||||
#
|
||||
# IMPORTANT: python3-yaml does NOT exist on CentOS
|
||||
# Install it manually using `pip3 install PyYAML`
|
||||
#
|
||||
PyYAML # Package name here does not correspond to the yum package!
|
||||
libcurl-devel # mg-requests
|
||||
rpm-build rpmlint # for RPM package building
|
||||
doxygen graphviz # source documentation generators
|
||||
which mono-complete dotnet-sdk-3.1 nodejs golang zip unzip java-11-openjdk-devel # for driver tests
|
||||
sbcl # for custom Lisp C++ preprocessing
|
||||
autoconf # for jemalloc code generation
|
||||
libtool # for protobuf code generation
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
local missing=""
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == "PyYAML" ]; then
|
||||
if ! python3 -c "import yaml" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if ! yum list installed "$pkg" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
done
|
||||
if [ "$missing" != "" ]; then
|
||||
echo "MISSING PACKAGES: $missing"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
cd "$DIR"
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Please run as root."
|
||||
exit 1
|
||||
fi
|
||||
# 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
|
||||
dnf install -y epel-release
|
||||
dnf install -y 'dnf-command(config-manager)'
|
||||
dnf config-manager --set-enabled powertools # Required to install texinfo.
|
||||
dnf update -y
|
||||
dnf install -y wget git python36 python3-pip
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == libipt ]; then
|
||||
if ! dnf list installed libipt >/dev/null 2>/dev/null; then
|
||||
dnf install -y http://repo.okay.com.mx/centos/8/x86_64/release/libipt-1.6.1-8.el8.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == libipt-devel ]; then
|
||||
if ! yum list installed libipt-devel >/dev/null 2>/dev/null; then
|
||||
dnf install -y http://repo.okay.com.mx/centos/8/x86_64/release/libipt-devel-1.6.1-8.el8.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
# Install GDB dependencies not present in the standard repos.
|
||||
# https://bugs.centos.org/view.php?id=17068
|
||||
# https://centos.pkgs.org
|
||||
# Since 2020, there is Babeltrace2 (https://babeltrace.org). Not used
|
||||
# within GDB yet (an assumption).
|
||||
# http://mirror.centos.org/centos/8/PowerTools/x86_64/os/Packages/libbabeltrace-devel-1.5.4-3.el8.x86_64.rpm not working
|
||||
if [ "$pkg" == libbabeltrace-devel ]; then
|
||||
if ! dnf list installed libbabeltrace-devel >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://rpmfind.net/linux/centos/8-stream/PowerTools/x86_64/os/Packages/libbabeltrace-devel-1.5.4-3.el8.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == sbcl ]; then
|
||||
if ! dnf list installed cl-asdf >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://pkgs.dyn.su/el8/base/x86_64/cl-asdf-20101028-18.el8.noarch.rpm
|
||||
fi
|
||||
if ! dnf list installed common-lisp-controller >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://pkgs.dyn.su/el8/base/x86_64/common-lisp-controller-7.4-20.el8.noarch.rpm
|
||||
fi
|
||||
if ! dnf list installed sbcl >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://pkgs.dyn.su/el8/base/x86_64/sbcl-2.0.1-4.el8.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == dotnet-sdk-3.1 ]; then
|
||||
if ! dnf list installed dotnet-sdk-3.1 >/dev/null 2>/dev/null; then
|
||||
wget -nv https://packages.microsoft.com/config/centos/8/packages-microsoft-prod.rpm -O packages-microsoft-prod.rpm
|
||||
rpm -Uvh https://packages.microsoft.com/config/centos/8/packages-microsoft-prod.rpm
|
||||
dnf update -y
|
||||
dnf install -y dotnet-sdk-3.1
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == PyYAML ]; then
|
||||
if [ -z ${SUDO_USER+x} ]; then # Running as root (e.g. Docker).
|
||||
pip3 install --user PyYAML
|
||||
else # Running using sudo.
|
||||
sudo -H -u "$SUDO_USER" bash -c "pip3 install --user PyYAML"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
dnf install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
@@ -5,15 +5,16 @@ set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "centos-9"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc gcc-c++ make # generic build tools
|
||||
coreutils-common gcc gcc-c++ make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg2 # used for archive signature verification
|
||||
tar gzip bzip2 xz unzip # used for archive unpacking
|
||||
zlib-devel # zlib library used for all builds
|
||||
expat-devel xz-devel python3-devel texinfo # for gdb
|
||||
libcurl-devel # for cmake
|
||||
curl # snappy
|
||||
expat-devel xz-devel python3-devel texinfo libbabeltrace-devel # for gdb
|
||||
readline-devel # for cmake and llvm
|
||||
libffi-devel libxml2-devel # for llvm
|
||||
libedit-devel pcre-devel automake bison # for swig
|
||||
@@ -21,6 +22,9 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
openssl-devel
|
||||
gmp-devel
|
||||
gperf
|
||||
diffutils
|
||||
libipt libipt-devel # intel
|
||||
patch
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
@@ -31,18 +35,19 @@ TOOLCHAIN_RUN_DEPS=(
|
||||
readline # for cmake and llvm
|
||||
libffi libxml2 # for llvm
|
||||
openssl-devel
|
||||
perl # for openssl
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make pkgconf-pkg-config # build system
|
||||
curl wget # for downloading libs
|
||||
wget # for downloading libs
|
||||
libuuid-devel java-11-openjdk # required by antlr
|
||||
readline-devel # for memgraph console
|
||||
python3-devel # for query modules
|
||||
openssl-devel
|
||||
libseccomp-devel
|
||||
python3 python3-virtualenv python3-pip nmap-ncat # for qa, macro_benchmark and stress tests
|
||||
python3 python3-pip python3-virtualenv nmap-ncat # for qa, macro_benchmark and stress tests
|
||||
#
|
||||
# IMPORTANT: python3-yaml does NOT exist on CentOS
|
||||
# Install it manually using `pip3 install PyYAML`
|
||||
@@ -73,12 +78,6 @@ check() {
|
||||
if [ "$pkg" == "python3-virtualenv" ]; then
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == sbcl ]; then
|
||||
if ! sbcl --version &> /dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if ! yum list installed "$pkg" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
@@ -105,13 +104,37 @@ install() {
|
||||
yum update -y
|
||||
yum install -y wget git python3 python3-pip
|
||||
for pkg in $1; do
|
||||
# Since there is no support for libipt-devel for CentOS 9 we install
|
||||
# Fedoras version of same libs, they are the same version but released
|
||||
# for different OS
|
||||
# TODO Update when libipt-devel releases for CentOS 9
|
||||
if [ "$pkg" == libipt ]; then
|
||||
if ! dnf list installed libipt >/dev/null 2>/dev/null; then
|
||||
dnf install -y http://repo.okay.com.mx/centos/8/x86_64/release/libipt-1.6.1-8.el8.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == libipt-devel ]; then
|
||||
if ! dnf list installed libipt-devel >/dev/null 2>/dev/null; then
|
||||
dnf install -y http://repo.okay.com.mx/centos/8/x86_64/release/libipt-devel-1.6.1-8.el8.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == libbabeltrace-devel ]; then
|
||||
if ! dnf list installed libbabeltrace-devel >/dev/null 2>/dev/null; then
|
||||
dnf install -y http://mirror.stream.centos.org/9-stream/CRB/x86_64/os/Packages/libbabeltrace-devel-1.5.8-10.el9.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == sbcl ]; then
|
||||
if ! sbcl --version &> /dev/null; then
|
||||
curl -s https://altushost-swe.dl.sourceforge.net/project/sbcl/sbcl/1.4.2/sbcl-1.4.2-arm64-linux-binary.tar.bz2 -o /tmp/sbcl-arm64.tar.bz2
|
||||
tar xvjf /tmp/sbcl-arm64.tar.bz2 -C /tmp
|
||||
pushd /tmp/sbcl-1.4.2-arm64-linux
|
||||
INSTALL_ROOT=/usr/local sh install.sh
|
||||
popd
|
||||
if ! dnf list installed cl-asdf >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://pkgs.dyn.su/el8/base/x86_64/cl-asdf-20101028-18.el8.noarch.rpm
|
||||
fi
|
||||
if ! dnf list installed common-lisp-controller >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://pkgs.dyn.su/el8/base/x86_64/common-lisp-controller-7.4-20.el8.noarch.rpm
|
||||
fi
|
||||
if ! dnf list installed sbcl >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://pkgs.dyn.su/el8/base/x86_64/sbcl-2.0.1-4.el8.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
@@ -125,9 +148,11 @@ install() {
|
||||
fi
|
||||
if [ "$pkg" == python3-virtualenv ]; then
|
||||
if [ -z ${SUDO_USER+x} ]; then # Running as root (e.g. Docker).
|
||||
pip3 install --user virtualenv
|
||||
pip3 install virtualenv
|
||||
pip3 install virtualenvwrapper
|
||||
else # Running using sudo.
|
||||
sudo -H -u "$SUDO_USER" bash -c "pip3 install --user virtualenv"
|
||||
sudo -H -u "$SUDO_USER" bash -c "pip3 install virtualenv"
|
||||
sudo -H -u "$SUDO_USER" bash -c "pip3 install virtualenvwrapper"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -5,6 +5,9 @@ set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "debian-10"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # used for archive download
|
||||
|
||||
@@ -5,6 +5,9 @@ set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "debian-11"
|
||||
check_architecture "arm64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # used for archive download
|
||||
@@ -87,15 +90,6 @@ EOF
|
||||
fi
|
||||
apt install -y wget
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == dotnet-sdk-3.1 ]; then
|
||||
if ! dpkg -s "$pkg" 2>/dev/null >/dev/null; then
|
||||
wget -nv https://packages.microsoft.com/config/debian/10/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-3.1
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
apt install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "debian-11"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # used for archive download
|
||||
@@ -87,7 +90,7 @@ EOF
|
||||
fi
|
||||
apt install -y wget
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == dotnet-sdk-3.1 ]; then
|
||||
if [ "$pkg" == dotnet-sdk-3.1 ]; then
|
||||
if ! dpkg -s "$pkg" 2>/dev/null >/dev/null; then
|
||||
wget -nv https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
|
||||
dpkg -i packages-microsoft-prod.deb
|
||||
|
||||
@@ -5,6 +5,8 @@ set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "todo-os-name"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
pkg
|
||||
)
|
||||
|
||||
@@ -5,6 +5,9 @@ set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "ubuntu-18.04"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # archive download
|
||||
|
||||
@@ -5,6 +5,9 @@ set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "ubuntu-20.04"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # used for archive download
|
||||
|
||||
96
environment/os/ubuntu-22.04.sh
Executable file
96
environment/os/ubuntu-22.04.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/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 "x86_64"
|
||||
|
||||
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 libipt-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 libipt2 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}"
|
||||
@@ -675,7 +675,7 @@ PROXYGEN_SHA256=5360a8ccdfb2f5a6c7b3eed331ec7ab0e2c792d579c6fff499c85c516c11fe14
|
||||
SNAPPY_SHA256=75c1fbb3d618dd3a0483bff0e26d0a92b495bbe5059c8b4f1c962b478b6e06e7
|
||||
SNAPPY_VERSION=1.1.9
|
||||
XZ_VERSION=5.2.5 # for LZMA
|
||||
ZLIB_VERSION=1.2.11
|
||||
ZLIB_VERSION=1.2.12
|
||||
ZSTD_VERSION=1.5.0
|
||||
WANGLE_SHA256=1002e9c32b6f4837f6a760016e3b3e22f3509880ef3eaad191c80dc92655f23f
|
||||
|
||||
@@ -1178,12 +1178,21 @@ popd
|
||||
|
||||
# create toolchain archive
|
||||
if [ ! -f $NAME-binaries-$DISTRO.tar.gz ]; then
|
||||
DISTRO_FULL_NAME=$DISTRO
|
||||
if [ "$for_arm" = true ]; then
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-aarch64"
|
||||
DISTRO_FULL_NAME=${DISTRO}
|
||||
if [[ "${DISTRO}" == centos* ]]; then
|
||||
if [[ "$for_arm" = "true" ]]; then
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-aarch64"
|
||||
else
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-x86_64"
|
||||
fi
|
||||
else
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-x86_64"
|
||||
if [[ "$for_arm" = "true" ]]; then
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-arm64"
|
||||
else
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-amd64"
|
||||
fi
|
||||
fi
|
||||
|
||||
tar --owner=root --group=root -cpvzf $NAME-binaries-$DISTRO_FULL_NAME.tar.gz -C /opt $NAME
|
||||
fi
|
||||
|
||||
|
||||
@@ -5,10 +5,28 @@ operating_system() {
|
||||
sort | cut -d '=' -f 2- | sed 's/"//g' | paste -s -d '-'
|
||||
}
|
||||
|
||||
check_operating_system() {
|
||||
if [ "$(operating_system)" != "$1" ]; then
|
||||
echo "Not the right operating system!"
|
||||
exit 1
|
||||
else
|
||||
echo "The right operating system."
|
||||
fi
|
||||
}
|
||||
|
||||
architecture() {
|
||||
uname -m
|
||||
}
|
||||
|
||||
check_architecture() {
|
||||
if [ "$(architecture)" != "$1" ]; then
|
||||
echo "Not the right architecture!"
|
||||
exit 1
|
||||
else
|
||||
echo "The right architecture."
|
||||
fi
|
||||
}
|
||||
|
||||
check_all_yum() {
|
||||
local missing=""
|
||||
for pkg in $1; do
|
||||
|
||||
686
include/mgp.py
686
include/mgp.py
File diff suppressed because it is too large
Load Diff
7
init
7
init
@@ -24,7 +24,7 @@ function setup_virtualenv () {
|
||||
fi
|
||||
|
||||
# create new virtualenv
|
||||
virtualenv -p python3 ve3 || exit 1
|
||||
python3 -m virtualenv -p python3 ve3 || exit 1
|
||||
source ve3/bin/activate
|
||||
pip --timeout 1000 install -r requirements.txt || exit 1
|
||||
deactivate
|
||||
@@ -66,7 +66,7 @@ fi
|
||||
|
||||
DISTRO=$(operating_system)
|
||||
ARCHITECTURE=$(architecture)
|
||||
if [ "${ARCHITECTURE}" = "arm64" ]; then
|
||||
if [ "${ARCHITECTURE}" = "arm64" ] || [ "${ARCHITECTURE}" = "aarch64" ]; then
|
||||
OS_SCRIPT=$DIR/environment/os/$DISTRO-arm.sh
|
||||
else
|
||||
OS_SCRIPT=$DIR/environment/os/$DISTRO.sh
|
||||
@@ -139,3 +139,6 @@ done;
|
||||
# Install precommit hook
|
||||
python3 -m pip install pre-commit
|
||||
python3 -m pre_commit install
|
||||
|
||||
# Link `include/mgp.py` with `release/mgp/mgp.py`
|
||||
ln -v -f include/mgp.py release/mgp/mgp.py
|
||||
|
||||
1
libs/.gitignore
vendored
1
libs/.gitignore
vendored
@@ -4,5 +4,4 @@
|
||||
!cleanup.sh
|
||||
!CMakeLists.txt
|
||||
!__main.cpp
|
||||
!jemalloc.cmake
|
||||
!pulsar.patch
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
set(JEMALLOC_DIR "${LIB_DIR}/jemalloc")
|
||||
|
||||
set(JEMALLOC_SRCS
|
||||
${JEMALLOC_DIR}/src/arena.c
|
||||
${JEMALLOC_DIR}/src/background_thread.c
|
||||
${JEMALLOC_DIR}/src/base.c
|
||||
${JEMALLOC_DIR}/src/bin.c
|
||||
${JEMALLOC_DIR}/src/bitmap.c
|
||||
${JEMALLOC_DIR}/src/ckh.c
|
||||
${JEMALLOC_DIR}/src/ctl.c
|
||||
${JEMALLOC_DIR}/src/div.c
|
||||
${JEMALLOC_DIR}/src/extent.c
|
||||
${JEMALLOC_DIR}/src/extent_dss.c
|
||||
${JEMALLOC_DIR}/src/extent_mmap.c
|
||||
${JEMALLOC_DIR}/src/hash.c
|
||||
${JEMALLOC_DIR}/src/hook.c
|
||||
${JEMALLOC_DIR}/src/jemalloc.c
|
||||
${JEMALLOC_DIR}/src/large.c
|
||||
${JEMALLOC_DIR}/src/log.c
|
||||
${JEMALLOC_DIR}/src/malloc_io.c
|
||||
${JEMALLOC_DIR}/src/mutex.c
|
||||
${JEMALLOC_DIR}/src/mutex_pool.c
|
||||
${JEMALLOC_DIR}/src/nstime.c
|
||||
${JEMALLOC_DIR}/src/pages.c
|
||||
${JEMALLOC_DIR}/src/prng.c
|
||||
${JEMALLOC_DIR}/src/prof.c
|
||||
${JEMALLOC_DIR}/src/rtree.c
|
||||
${JEMALLOC_DIR}/src/sc.c
|
||||
${JEMALLOC_DIR}/src/stats.c
|
||||
${JEMALLOC_DIR}/src/sz.c
|
||||
${JEMALLOC_DIR}/src/tcache.c
|
||||
${JEMALLOC_DIR}/src/test_hooks.c
|
||||
${JEMALLOC_DIR}/src/ticker.c
|
||||
${JEMALLOC_DIR}/src/tsd.c
|
||||
${JEMALLOC_DIR}/src/witness.c
|
||||
${JEMALLOC_DIR}/src/safety_check.c
|
||||
)
|
||||
|
||||
add_library(jemalloc ${JEMALLOC_SRCS})
|
||||
target_include_directories(jemalloc PUBLIC "${JEMALLOC_DIR}/include")
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(jemalloc PUBLIC Threads::Threads)
|
||||
|
||||
target_compile_definitions(jemalloc PRIVATE -DJEMALLOC_NO_PRIVATE_NAMESPACE)
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "DEBUG")
|
||||
target_compile_definitions(jemalloc PRIVATE -DJEMALLOC_DEBUG=1 -DJEMALLOC_PROF=1)
|
||||
endif()
|
||||
|
||||
target_compile_options(jemalloc PRIVATE -Wno-redundant-decls)
|
||||
# for RTLD_NEXT
|
||||
target_compile_definitions(jemalloc PRIVATE _GNU_SOURCE)
|
||||
|
||||
set_property(TARGET jemalloc APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS USE_JEMALLOC=1)
|
||||
@@ -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")
|
||||
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,31 +0,0 @@
|
||||
FROM dokken/centos-stream-9
|
||||
# NOTE: If you change the base distro update release/package as well.
|
||||
|
||||
ARG release
|
||||
|
||||
RUN yum update && yum install -y \
|
||||
openssl libcurl libseccomp python3 python3-pip \
|
||||
--nobest --allowerasing \
|
||||
&& rm -rf /tmp/* \
|
||||
&& yum clean all
|
||||
|
||||
RUN pip3 install networkx==2.4 numpy==1.21.4 scipy==1.7.3
|
||||
|
||||
COPY ${release} /
|
||||
|
||||
# Install memgraph package
|
||||
RUN rpm -i ${release}
|
||||
|
||||
# Memgraph listens for Bolt Protocol on this port by default.
|
||||
EXPOSE 7687
|
||||
# Snapshots and logging volumes
|
||||
VOLUME /var/log/memgraph
|
||||
VOLUME /var/lib/memgraph
|
||||
# Configuration volume
|
||||
VOLUME /etc/memgraph
|
||||
|
||||
USER memgraph
|
||||
WORKDIR /usr/lib/memgraph
|
||||
|
||||
ENTRYPOINT ["/usr/lib/memgraph/memgraph"]
|
||||
CMD [""]
|
||||
3
release/mgp/.gitignore
vendored
Normal file
3
release/mgp/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.venv
|
||||
dist
|
||||
mgp.py
|
||||
201
release/mgp/LICENSE
Normal file
201
release/mgp/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
4
release/mgp/README.md
Normal file
4
release/mgp/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# mgp
|
||||
|
||||
PyPi package used for type hinting when creating MAGE modules. The get started
|
||||
using MAGE repository checkout the repository here: https://github.com/memgraph/mage.
|
||||
255
release/mgp/_mgp.py
Normal file
255
release/mgp/_mgp.py
Normal file
@@ -0,0 +1,255 @@
|
||||
from typing import Any
|
||||
|
||||
|
||||
class MgpIterable:
|
||||
def get() -> Any:
|
||||
pass
|
||||
|
||||
def next() -> Any:
|
||||
pass
|
||||
|
||||
|
||||
class Vertex:
|
||||
def is_valid() -> bool: # type: ignore
|
||||
pass
|
||||
|
||||
def underlying_graph_is_mutable() -> bool: # type: ignore
|
||||
pass
|
||||
|
||||
def iter_properties() -> MgpIterable: # type: ignore
|
||||
pass
|
||||
|
||||
def get_property(self, property_name: str) -> "Property": # type: ignore
|
||||
pass
|
||||
|
||||
def set_property(self, property_name: str, value: Any) -> "Property": # type: ignore
|
||||
pass
|
||||
|
||||
def get_id() -> "VertexId": # type: ignore
|
||||
pass
|
||||
|
||||
def label_at(self, index: int) -> "Label": # type: ignore
|
||||
pass
|
||||
|
||||
def labels_count() -> int: # type: ignore
|
||||
pass
|
||||
|
||||
def add_label(self, label: Any):
|
||||
pass
|
||||
|
||||
def remove_label(self, label: Any):
|
||||
pass
|
||||
|
||||
def iter_in_edges() -> MgpIterable: # type: ignore
|
||||
pass
|
||||
|
||||
def iter_out_edges() -> MgpIterable: # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class Edge:
|
||||
def is_valid() -> bool: # type: ignore
|
||||
pass
|
||||
|
||||
def underlying_graph_is_mutable() -> bool: # type: ignore
|
||||
pass
|
||||
|
||||
def iter_properties() -> MgpIterable: # type: ignore
|
||||
pass
|
||||
|
||||
def get_property(self, property_name: str) -> "Property": # type: ignore
|
||||
pass
|
||||
|
||||
def set_property(self, property_name: str, valuse: Any) -> "Property": # type: ignore
|
||||
pass
|
||||
|
||||
def get_type_name() -> str: # type: ignore
|
||||
pass
|
||||
|
||||
def get_id() -> "EdgeId": # type: ignore
|
||||
pass
|
||||
|
||||
def from_vertex() -> Vertex: # type: ignore
|
||||
pass
|
||||
|
||||
def to_vertex() -> Vertex: # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class Path:
|
||||
def is_valid() -> bool: # type: ignore
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def make_with_start(vertex: Vertex) -> "Path": # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class Graph:
|
||||
def is_valid() -> bool: # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class CypherType:
|
||||
pass
|
||||
|
||||
|
||||
class Message:
|
||||
def is_valid() -> bool: # type: ignore
|
||||
pass
|
||||
|
||||
def source_type() -> str: # type: ignore
|
||||
pass
|
||||
|
||||
def topic_name() -> str: # type: ignore
|
||||
pass
|
||||
|
||||
def key() -> bytes: # type: ignore
|
||||
pass
|
||||
|
||||
def timestamp() -> int: # type: ignore
|
||||
pass
|
||||
|
||||
def offset() -> int: # type: ignore
|
||||
pass
|
||||
|
||||
def payload() -> bytes: # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class Messages:
|
||||
def is_valid() -> bool: # type: ignore
|
||||
pass
|
||||
|
||||
def message_at(self, id: int) -> Message: # type: ignore
|
||||
pass
|
||||
|
||||
def total_messages() -> int: # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class UnknownError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UnableToAllocateError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InsufficientBufferError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class OutOfRangeError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class LogicErrorError(Exception):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class DeletedObjectError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidArgumentError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class KeyAlreadyExistsError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ImmutableObjectError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ValueConversionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class SerializationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def type_nullable(elem: Any):
|
||||
pass
|
||||
|
||||
|
||||
def type_list(elem: Any):
|
||||
pass
|
||||
|
||||
|
||||
def type_bool():
|
||||
pass
|
||||
|
||||
|
||||
def type_string():
|
||||
pass
|
||||
|
||||
|
||||
def type_int():
|
||||
pass
|
||||
|
||||
|
||||
def type_float():
|
||||
pass
|
||||
|
||||
|
||||
def type_number():
|
||||
pass
|
||||
|
||||
|
||||
def type_map():
|
||||
pass
|
||||
|
||||
|
||||
def type_node():
|
||||
pass
|
||||
|
||||
|
||||
def type_relationship():
|
||||
pass
|
||||
|
||||
|
||||
def type_path():
|
||||
pass
|
||||
|
||||
|
||||
def type_date():
|
||||
pass
|
||||
|
||||
|
||||
def type_local_time():
|
||||
pass
|
||||
|
||||
|
||||
def type_local_date_time():
|
||||
pass
|
||||
|
||||
|
||||
def type_duration():
|
||||
pass
|
||||
|
||||
|
||||
def type_any():
|
||||
pass
|
||||
|
||||
|
||||
class _MODULE:
|
||||
@staticmethod
|
||||
def add_read_procedure(wrapper):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def add_write_procedure(wrapper):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def add_transformation(wrapper):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def add_function(wrapper):
|
||||
pass
|
||||
22
release/mgp/instructions.md
Normal file
22
release/mgp/instructions.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# How to publish new versions
|
||||
## Prerequisites
|
||||
1. Installed poetry
|
||||
```
|
||||
pip install poetry
|
||||
```
|
||||
2. Set up [API tokens](https://pypi.org/help/#apitoken)
|
||||
3. Be a collaborator on [pypi](https://pypi.org/project/mgp/)
|
||||
|
||||
## Making changes
|
||||
1. Make changes to the package
|
||||
2. Bump version in `pyproject.tml`
|
||||
3. `poetry build`
|
||||
4. `poetry publish`
|
||||
|
||||
## Why is this not automatized?
|
||||
|
||||
Because someone always has to manually bump up the version in `pyproject.toml`
|
||||
|
||||
## Why does `_mgp.py` exists?
|
||||
Because we are mocking here all the types that are created by Memgraph
|
||||
in order to fix typing errors in `mgp.py`.
|
||||
23
release/mgp/pyproject.toml
Normal file
23
release/mgp/pyproject.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[tool.poetry]
|
||||
name = "mgp"
|
||||
version = "1.0.0"
|
||||
description = "Memgraph's module for developing MAGE modules. Used only for type hinting!"
|
||||
authors = [
|
||||
"MasterMedo <mislav.vuletic@gmail.com>",
|
||||
"jbajic <jure.bajic@memgraph.io>",
|
||||
"katarinasupe <katarina.supe@memgraph.io>",
|
||||
"antejavor <ante.javor@memgraph.io>",
|
||||
"antaljanosbenjamin <benjamin.antal@memgraph.io>",
|
||||
]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
include = ["mgp.py", "_mgp.py"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.7"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
@@ -7,8 +7,8 @@ RUN yum -y update \
|
||||
# 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-centos-7.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-centos-7.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-centos-7.tar.gz -C /opt
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-centos-7-x86_64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-centos-7-x86_64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-centos-7-x86_64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
FROM centos:8
|
||||
|
||||
ARG TOOLCHAIN_VERSION
|
||||
|
||||
RUN dnf -y update \
|
||||
&& dnf 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-centos-8.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-centos-8.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-centos-8.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
14
release/package/centos-9/Dockerfile
Normal file
14
release/package/centos-9/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM quay.io/centos/centos:stream9
|
||||
|
||||
ARG TOOLCHAIN_VERSION
|
||||
|
||||
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-centos-9-x86_64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-centos-9-x86_64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-centos-9-x86_64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
@@ -10,8 +10,8 @@ RUN apt update && apt install -y \
|
||||
# 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-debian-10.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-debian-10.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-10.tar.gz -C /opt
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-debian-10-amd64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-debian-10-amd64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-10-amd64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
|
||||
17
release/package/debian-11-arm/Dockerfile
Normal file
17
release/package/debian-11-arm/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM debian:11
|
||||
|
||||
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-debian-11-arm64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-debian-11-arm64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-11-arm64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
@@ -10,8 +10,8 @@ RUN apt update && apt install -y \
|
||||
# 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-debian-11.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-debian-11.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-11.tar.gz -C /opt
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-debian-11-amd64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-debian-11-amd64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-11-amd64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
|
||||
@@ -4,10 +4,10 @@ services:
|
||||
build:
|
||||
context: centos-7
|
||||
container_name: "mgbuild_centos-7"
|
||||
mgbuild_centos-8:
|
||||
mgbuild_centos-9:
|
||||
build:
|
||||
context: centos-8
|
||||
container_name: "mgbuild_centos-8"
|
||||
context: centos-9
|
||||
container_name: "mgbuild_centos-9"
|
||||
mgbuild_debian-10:
|
||||
build:
|
||||
context: debian-10
|
||||
@@ -24,3 +24,7 @@ services:
|
||||
build:
|
||||
context: ubuntu-20.04
|
||||
container_name: "mgbuild_ubuntu-20.04"
|
||||
mgbuild_ubuntu-22.04:
|
||||
build:
|
||||
context: ubuntu-22.04
|
||||
container_name: "mgbuild_ubuntu-22.04"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
SUPPORTED_OS=(centos-7 centos-8 debian-10 debian-11 ubuntu-18.04 ubuntu-20.04)
|
||||
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"
|
||||
@@ -67,14 +67,18 @@ make_package () {
|
||||
# environment/os/{os}.sh does not come within the toolchain package. When
|
||||
# migrating to the next version of toolchain do that, and remove the
|
||||
# TOOLCHAIN_RUN_DEPS installation from here.
|
||||
echo "Installing dependencies..."
|
||||
echo "Installing dependencies using '/memgraph/environment/os/$os.sh' script..."
|
||||
docker exec "$build_container" bash -c "/memgraph/environment/os/$os.sh install TOOLCHAIN_RUN_DEPS"
|
||||
docker exec "$build_container" bash -c "/memgraph/environment/os/$os.sh install MEMGRAPH_BUILD_DEPS"
|
||||
|
||||
echo "Building targeted package..."
|
||||
docker exec "$build_container" bash -c "cd /memgraph && $ACTIVATE_TOOLCHAIN && ./init"
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && rm -rf ./*"
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && cmake -DCMAKE_BUILD_TYPE=release $telemetry_id_override_flag .."
|
||||
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 .."
|
||||
fi
|
||||
# ' is used instead of " because we need to run make within the allowed
|
||||
# container resources.
|
||||
# shellcheck disable=SC2016
|
||||
|
||||
@@ -10,8 +10,8 @@ RUN apt update && apt install -y \
|
||||
# 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-18.04.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04.tar.gz -C /opt
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04-amd64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04-amd64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04-amd64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
|
||||
@@ -10,8 +10,8 @@ RUN apt update && apt install -y \
|
||||
# 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-20.04.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04.tar.gz -C /opt
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04-amd64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04-amd64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04-amd64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
|
||||
17
release/package/ubuntu-22.04/Dockerfile
Normal file
17
release/package/ubuntu-22.04/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
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-amd64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-amd64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-amd64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
@@ -37,7 +37,7 @@ const std::vector<Permission> kPermissionsAll = {
|
||||
Permission::CONSTRAINT, Permission::DUMP, Permission::AUTH, Permission::REPLICATION,
|
||||
Permission::DURABILITY, Permission::READ_FILE, Permission::FREE_MEMORY, Permission::TRIGGER,
|
||||
Permission::CONFIG, Permission::STREAM, Permission::MODULE_READ, Permission::MODULE_WRITE,
|
||||
Permission::WEBSOCKET, Permission::SCHEMA};
|
||||
Permission::WEBSOCKET};
|
||||
} // namespace
|
||||
|
||||
std::string PermissionToString(Permission permission) {
|
||||
@@ -84,8 +84,6 @@ std::string PermissionToString(Permission permission) {
|
||||
return "MODULE_WRITE";
|
||||
case Permission::WEBSOCKET:
|
||||
return "WEBSOCKET";
|
||||
case Permission::SCHEMA:
|
||||
return "SCHEMA";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,7 @@ enum class Permission : uint64_t {
|
||||
STREAM = 1U << 17U,
|
||||
MODULE_READ = 1U << 18U,
|
||||
MODULE_WRITE = 1U << 19U,
|
||||
WEBSOCKET = 1U << 20U,
|
||||
SCHEMA = 1U << 21U
|
||||
WEBSOCKET = 1U << 20U
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -1,19 +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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace memgraph::common {
|
||||
enum class SchemaType : uint8_t { BOOL, INT, STRING, DATE, LOCALTIME, LOCALDATETIME, DURATION };
|
||||
|
||||
} // namespace memgraph::common
|
||||
@@ -167,6 +167,7 @@ class Value {
|
||||
Value(const Edge &value) : type_(Type::Edge) { new (&edge_v) Edge(value); }
|
||||
Value(const UnboundedEdge &value) : type_(Type::UnboundedEdge) { new (&unbounded_edge_v) UnboundedEdge(value); }
|
||||
Value(const Path &value) : type_(Type::Path) { new (&path_v) Path(value); }
|
||||
|
||||
Value(const utils::Date &date) : type_(Type::Date) { new (&date_v) utils::Date(date); }
|
||||
Value(const utils::LocalTime &time) : type_(Type::LocalTime) { new (&local_time_v) utils::LocalTime(time); }
|
||||
Value(const utils::LocalDateTime &date_time) : type_(Type::LocalDateTime) {
|
||||
|
||||
@@ -105,9 +105,16 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
||||
boost::asio::socket_base::keep_alive option(true);
|
||||
|
||||
// Set a decorator to change the Server of the handshake
|
||||
ws_.set_option(boost::beast::websocket::stream_base::decorator([](boost::beast::websocket::response_type &res) {
|
||||
ws_.set_option(boost::beast::websocket::stream_base::decorator([&req](boost::beast::websocket::response_type &res) {
|
||||
res.set(boost::beast::http::field::server, std::string("Memgraph Bolt WS"));
|
||||
res.set(boost::beast::http::field::sec_websocket_protocol, "binary");
|
||||
|
||||
// We need to do this to support WASM clients, which explicitly send this flag
|
||||
// in their upgrade request
|
||||
// Neo4j client breaks when this flag is sent
|
||||
if (const auto secondary_protocol = req.base().find(boost::beast::http::field::sec_websocket_protocol);
|
||||
secondary_protocol != res.base().end() && secondary_protocol->value() == "binary") {
|
||||
res.set(boost::beast::http::field::sec_websocket_protocol, "binary");
|
||||
}
|
||||
}));
|
||||
ws_.binary(true);
|
||||
|
||||
@@ -162,7 +169,7 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
||||
boost::asio::bind_executor(strand_, std::bind_front(&WebsocketSession::OnRead, shared_from_this())));
|
||||
}
|
||||
|
||||
void OnRead(const boost::system::error_code &ec, [[maybe_unused]] const size_t bytes_transferred) {
|
||||
void OnRead(const boost::system::error_code &ec, const size_t bytes_transferred) {
|
||||
// This indicates that the WebsocketSession was closed
|
||||
if (ec == boost::beast::websocket::error::closed) {
|
||||
return;
|
||||
@@ -322,7 +329,8 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
socket.lowest_layer().non_blocking(false);
|
||||
});
|
||||
timeout_timer_.expires_at(boost::asio::steady_timer::time_point::max());
|
||||
spdlog::info("Accepted a connection from {}:", service_name_, remote_endpoint_.address(), remote_endpoint_.port());
|
||||
spdlog::info("Accepted a connection from {}: {}:{}", service_name_, remote_endpoint_.address(),
|
||||
remote_endpoint_.port());
|
||||
}
|
||||
|
||||
void DoRead() {
|
||||
|
||||
@@ -57,8 +57,6 @@ auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege) {
|
||||
return auth::Permission::MODULE_WRITE;
|
||||
case query::AuthQuery::Privilege::WEBSOCKET:
|
||||
return auth::Permission::WEBSOCKET;
|
||||
case query::AuthQuery::Privilege::SCHEMA:
|
||||
return auth::Permission::SCHEMA;
|
||||
}
|
||||
}
|
||||
} // namespace memgraph::glue
|
||||
|
||||
@@ -127,6 +127,10 @@ storage::Result<Value> ToBoltValue(const query::TypedValue &value, const storage
|
||||
return Value(value.ValueLocalDateTime());
|
||||
case query::TypedValue::Type::Duration:
|
||||
return Value(value.ValueDuration());
|
||||
case query::TypedValue::Type::Graph:
|
||||
auto maybe_graph = ToBoltGraph(value.ValueGraph(), db, view);
|
||||
if (maybe_graph.HasError()) return maybe_graph.GetError();
|
||||
return Value(std::move(*maybe_graph));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +187,30 @@ storage::Result<communication::bolt::Path> ToBoltPath(const query::Path &path, c
|
||||
return communication::bolt::Path(vertices, edges);
|
||||
}
|
||||
|
||||
storage::Result<std::map<std::string, Value>> ToBoltGraph(const query::Graph &graph, const storage::Storage &db,
|
||||
storage::View view) {
|
||||
std::map<std::string, Value> map;
|
||||
std::vector<Value> vertices;
|
||||
vertices.reserve(graph.vertices().size());
|
||||
for (const auto &v : graph.vertices()) {
|
||||
auto maybe_vertex = ToBoltVertex(v, db, view);
|
||||
if (maybe_vertex.HasError()) return maybe_vertex.GetError();
|
||||
vertices.emplace_back(Value(std::move(*maybe_vertex)));
|
||||
}
|
||||
map.emplace(std::make_pair("nodes", Value(vertices)));
|
||||
|
||||
std::vector<Value> edges;
|
||||
edges.reserve(graph.edges().size());
|
||||
for (const auto &e : graph.edges()) {
|
||||
auto maybe_edge = ToBoltEdge(e, db, view);
|
||||
if (maybe_edge.HasError()) return maybe_edge.GetError();
|
||||
edges.emplace_back(Value(std::move(*maybe_edge)));
|
||||
}
|
||||
map.emplace(std::make_pair("edges", Value(edges)));
|
||||
|
||||
return std::move(map);
|
||||
}
|
||||
|
||||
storage::PropertyValue ToPropertyValue(const Value &value) {
|
||||
switch (value.type()) {
|
||||
case Value::Type::Null:
|
||||
|
||||
@@ -51,6 +51,15 @@ storage::Result<communication::bolt::Edge> ToBoltEdge(const storage::EdgeAccesso
|
||||
storage::Result<communication::bolt::Path> ToBoltPath(const query::Path &path, const storage::Storage &db,
|
||||
storage::View view);
|
||||
|
||||
/// @param query::Graph for converting to communication::bolt::Graph.
|
||||
/// @param storage::Storage for ToBoltVertex and ToBoltEdge.
|
||||
/// @param storage::View for ToBoltVertex and ToBoltEdge.
|
||||
///
|
||||
/// @throw std::bad_alloc
|
||||
storage::Result<std::map<std::string, communication::bolt::Value>> ToBoltGraph(const query::Graph &graph,
|
||||
const storage::Storage &db,
|
||||
storage::View view);
|
||||
|
||||
/// @param query::TypedValue for converting to communication::bolt::Value.
|
||||
/// @param storage::Storage for ToBoltVertex and ToBoltEdge.
|
||||
/// @param storage::View for ToBoltVertex and ToBoltEdge.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
namespace memgraph::integrations {
|
||||
|
||||
inline constexpr int64_t kDefaultCheckBatchLimit{1};
|
||||
inline constexpr int64_t kMinimumStartBatchLimit{1};
|
||||
inline constexpr std::chrono::milliseconds kDefaultCheckTimeout{30000};
|
||||
inline constexpr std::chrono::milliseconds kMinimumInterval{1};
|
||||
inline constexpr int64_t kMinimumSize{1};
|
||||
|
||||
@@ -74,6 +74,36 @@ utils::BasicResult<std::string, std::vector<Message>> GetBatch(RdKafka::KafkaCon
|
||||
|
||||
return std::move(batch);
|
||||
}
|
||||
|
||||
void CheckAndDestroyLastAssignmentIfNeeded(RdKafka::KafkaConsumer &consumer, const ConsumerInfo &info,
|
||||
std::vector<RdKafka::TopicPartition *> &last_assignment) {
|
||||
if (!last_assignment.empty()) {
|
||||
if (const auto err = consumer.assign(last_assignment); err != RdKafka::ERR_NO_ERROR) {
|
||||
throw ConsumerStartFailedException(info.consumer_name,
|
||||
fmt::format("Couldn't restore commited offsets: '{}'", RdKafka::err2str(err)));
|
||||
}
|
||||
RdKafka::TopicPartition::destroy(last_assignment);
|
||||
}
|
||||
}
|
||||
|
||||
void TryToConsumeBatch(RdKafka::KafkaConsumer &consumer, const ConsumerInfo &info,
|
||||
const ConsumerFunction &consumer_function, const std::vector<Message> &batch) {
|
||||
consumer_function(batch);
|
||||
std::vector<RdKafka::TopicPartition *> partitions;
|
||||
utils::OnScopeExit clear_partitions([&]() { RdKafka::TopicPartition::destroy(partitions); });
|
||||
|
||||
if (const auto err = consumer.assignment(partitions); err != RdKafka::ERR_NO_ERROR) {
|
||||
throw ConsumerCommitFailedException(
|
||||
info.consumer_name, fmt::format("Couldn't get assignment to commit offsets: {}", RdKafka::err2str(err)));
|
||||
}
|
||||
if (const auto err = consumer.position(partitions); err != RdKafka::ERR_NO_ERROR) {
|
||||
throw ConsumerCommitFailedException(info.consumer_name,
|
||||
fmt::format("Couldn't get offsets from librdkafka {}", RdKafka::err2str(err)));
|
||||
}
|
||||
if (const auto err = consumer.commitSync(partitions); err != RdKafka::ERR_NO_ERROR) {
|
||||
throw ConsumerCommitFailedException(info.consumer_name, RdKafka::err2str(err));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Message::Message(std::unique_ptr<RdKafka::Message> &&message) : message_{std::move(message)} {
|
||||
@@ -221,10 +251,21 @@ void Consumer::Start() {
|
||||
StartConsuming();
|
||||
}
|
||||
|
||||
void Consumer::StartIfStopped() {
|
||||
if (!is_running_) {
|
||||
StartConsuming();
|
||||
void Consumer::StartWithLimit(const uint64_t limit_batches, std::optional<std::chrono::milliseconds> timeout) const {
|
||||
if (is_running_) {
|
||||
throw ConsumerRunningException(info_.consumer_name);
|
||||
}
|
||||
if (limit_batches < kMinimumStartBatchLimit) {
|
||||
throw ConsumerStartFailedException(
|
||||
info_.consumer_name, fmt::format("Batch limit has to be greater than or equal to {}", kMinimumStartBatchLimit));
|
||||
}
|
||||
if (timeout.value_or(kMinimumInterval) < kMinimumInterval) {
|
||||
throw ConsumerStartFailedException(
|
||||
info_.consumer_name,
|
||||
fmt::format("Timeout has to be greater than or equal to {} milliseconds", kMinimumInterval.count()));
|
||||
}
|
||||
|
||||
StartConsumingWithLimit(limit_batches, timeout);
|
||||
}
|
||||
|
||||
void Consumer::Stop() {
|
||||
@@ -244,7 +285,7 @@ void Consumer::StopIfRunning() {
|
||||
}
|
||||
}
|
||||
|
||||
void Consumer::Check(std::optional<std::chrono::milliseconds> timeout, std::optional<int64_t> limit_batches,
|
||||
void Consumer::Check(std::optional<std::chrono::milliseconds> timeout, std::optional<uint64_t> limit_batches,
|
||||
const ConsumerFunction &check_consumer_function) const {
|
||||
// NOLINTNEXTLINE (modernize-use-nullptr)
|
||||
if (timeout.value_or(kMinimumInterval) < kMinimumInterval) {
|
||||
@@ -344,13 +385,7 @@ void Consumer::StartConsuming() {
|
||||
|
||||
is_running_.store(true);
|
||||
|
||||
if (!last_assignment_.empty()) {
|
||||
if (const auto err = consumer_->assign(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
|
||||
throw ConsumerStartFailedException(info_.consumer_name,
|
||||
fmt::format("Couldn't restore commited offsets: '{}'", RdKafka::err2str(err)));
|
||||
}
|
||||
RdKafka::TopicPartition::destroy(last_assignment_);
|
||||
}
|
||||
CheckAndDestroyLastAssignmentIfNeeded(*consumer_, info_, last_assignment_);
|
||||
|
||||
thread_ = std::thread([this] {
|
||||
static constexpr auto kMaxThreadNameSize = utils::GetMaxThreadNameSize();
|
||||
@@ -361,33 +396,18 @@ void Consumer::StartConsuming() {
|
||||
while (is_running_) {
|
||||
auto maybe_batch = GetBatch(*consumer_, info_, is_running_);
|
||||
if (maybe_batch.HasError()) {
|
||||
spdlog::warn("Error happened in consumer {} while fetching messages: {}!", info_.consumer_name,
|
||||
maybe_batch.GetError());
|
||||
break;
|
||||
throw ConsumerReadMessagesFailedException(info_.consumer_name, maybe_batch.GetError());
|
||||
}
|
||||
const auto &batch = maybe_batch.GetValue();
|
||||
|
||||
if (batch.empty()) continue;
|
||||
if (batch.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
spdlog::info("Kafka consumer {} is processing a batch", info_.consumer_name);
|
||||
|
||||
try {
|
||||
consumer_function_(batch);
|
||||
std::vector<RdKafka::TopicPartition *> partitions;
|
||||
utils::OnScopeExit clear_partitions([&]() { RdKafka::TopicPartition::destroy(partitions); });
|
||||
|
||||
if (const auto err = consumer_->assignment(partitions); err != RdKafka::ERR_NO_ERROR) {
|
||||
throw ConsumerCheckFailedException(
|
||||
info_.consumer_name, fmt::format("Couldn't get assignment to commit offsets: {}", RdKafka::err2str(err)));
|
||||
}
|
||||
if (const auto err = consumer_->position(partitions); err != RdKafka::ERR_NO_ERROR) {
|
||||
throw ConsumerCheckFailedException(
|
||||
info_.consumer_name, fmt::format("Couldn't get offsets from librdkafka {}", RdKafka::err2str(err)));
|
||||
}
|
||||
if (const auto err = consumer_->commitSync(partitions); err != RdKafka::ERR_NO_ERROR) {
|
||||
spdlog::warn("Committing offset of consumer {} failed: {}", info_.consumer_name, RdKafka::err2str(err));
|
||||
break;
|
||||
}
|
||||
TryToConsumeBatch(*consumer_, info_, consumer_function_, batch);
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::warn("Error happened in consumer {} while processing a batch: {}!", info_.consumer_name, e.what());
|
||||
break;
|
||||
@@ -398,6 +418,44 @@ void Consumer::StartConsuming() {
|
||||
});
|
||||
}
|
||||
|
||||
void Consumer::StartConsumingWithLimit(uint64_t limit_batches, std::optional<std::chrono::milliseconds> timeout) const {
|
||||
MG_ASSERT(!is_running_, "Cannot start already running consumer!");
|
||||
|
||||
if (is_running_.exchange(true)) {
|
||||
throw ConsumerRunningException(info_.consumer_name);
|
||||
}
|
||||
utils::OnScopeExit restore_is_running([this] { is_running_.store(false); });
|
||||
|
||||
CheckAndDestroyLastAssignmentIfNeeded(*consumer_, info_, last_assignment_);
|
||||
|
||||
const auto timeout_to_use = timeout.value_or(kDefaultCheckTimeout);
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
|
||||
for (uint64_t batch_count = 0; batch_count < limit_batches;) {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now - start >= timeout_to_use) {
|
||||
throw ConsumerStartFailedException(info_.consumer_name, "Timeout reached");
|
||||
}
|
||||
|
||||
const auto maybe_batch = GetBatch(*consumer_, info_, is_running_);
|
||||
if (maybe_batch.HasError()) {
|
||||
throw ConsumerReadMessagesFailedException(info_.consumer_name, maybe_batch.GetError());
|
||||
}
|
||||
const auto &batch = maybe_batch.GetValue();
|
||||
|
||||
if (batch.empty()) {
|
||||
continue;
|
||||
}
|
||||
++batch_count;
|
||||
|
||||
spdlog::info("Kafka consumer {} is processing a batch", info_.consumer_name);
|
||||
|
||||
TryToConsumeBatch(*consumer_, info_, consumer_function_, batch);
|
||||
|
||||
spdlog::info("Kafka consumer {} finished processing", info_.consumer_name);
|
||||
}
|
||||
}
|
||||
|
||||
void Consumer::StopConsuming() {
|
||||
is_running_.store(false);
|
||||
if (thread_.joinable()) thread_.join();
|
||||
|
||||
@@ -113,11 +113,19 @@ class Consumer final : public RdKafka::EventCb {
|
||||
/// This method will start a new thread which will poll all the topics for messages.
|
||||
///
|
||||
/// @throws ConsumerRunningException if the consumer is already running
|
||||
/// @throws ConsumerStartFailedException if the commited offsets cannot be restored
|
||||
void Start();
|
||||
|
||||
/// Starts consuming messages if it is not started already.
|
||||
/// Starts consuming messages.
|
||||
///
|
||||
void StartIfStopped();
|
||||
/// This method will start a new thread which will poll all the topics for messages.
|
||||
///
|
||||
/// @param limit_batches the consumer will only consume the given number of batches.
|
||||
/// @param timeout the maximum duration during which the command should run.
|
||||
///
|
||||
/// @throws ConsumerRunningException if the consumer is already running
|
||||
/// @throws ConsumerStartFailedException if the commited offsets cannot be restored
|
||||
void StartWithLimit(uint64_t limit_batches, std::optional<std::chrono::milliseconds> timeout) const;
|
||||
|
||||
/// Stops consuming messages.
|
||||
///
|
||||
@@ -136,9 +144,9 @@ class Consumer final : public RdKafka::EventCb {
|
||||
/// used.
|
||||
/// @param check_consumer_function a function to feed the received messages in, only used during this dry-run.
|
||||
///
|
||||
/// @throws ConsumerRunningException if the consumer is alredy running.
|
||||
/// @throws ConsumerRunningException if the consumer is already running.
|
||||
/// @throws ConsumerCheckFailedException if check isn't successful.
|
||||
void Check(std::optional<std::chrono::milliseconds> timeout, std::optional<int64_t> limit_batches,
|
||||
void Check(std::optional<std::chrono::milliseconds> timeout, std::optional<uint64_t> limit_batches,
|
||||
const ConsumerFunction &check_consumer_function) const;
|
||||
|
||||
/// Returns true if the consumer is actively consuming messages.
|
||||
@@ -157,6 +165,7 @@ class Consumer final : public RdKafka::EventCb {
|
||||
void event_cb(RdKafka::Event &event) override;
|
||||
|
||||
void StartConsuming();
|
||||
void StartConsumingWithLimit(uint64_t limit_batches, std::optional<std::chrono::milliseconds> timeout) const;
|
||||
|
||||
void StopConsuming();
|
||||
|
||||
@@ -178,7 +187,6 @@ class Consumer final : public RdKafka::EventCb {
|
||||
ConsumerFunction consumer_function_;
|
||||
mutable std::atomic<bool> is_running_{false};
|
||||
mutable std::vector<RdKafka::TopicPartition *> last_assignment_; // Protected by is_running_
|
||||
std::optional<int64_t> limit_batches_{std::nullopt};
|
||||
std::unique_ptr<RdKafka::KafkaConsumer, std::function<void(RdKafka::KafkaConsumer *)>> consumer_;
|
||||
std::thread thread_;
|
||||
ConsumerRebalanceCb cb_;
|
||||
|
||||
@@ -64,4 +64,16 @@ class TopicNotFoundException : public KafkaStreamException {
|
||||
TopicNotFoundException(const std::string_view consumer_name, const std::string_view topic_name)
|
||||
: KafkaStreamException("Kafka consumer {} cannot find topic {}", consumer_name, topic_name) {}
|
||||
};
|
||||
|
||||
class ConsumerCommitFailedException : public KafkaStreamException {
|
||||
public:
|
||||
ConsumerCommitFailedException(const std::string_view consumer_name, const std::string_view error)
|
||||
: KafkaStreamException("Committing offset of consumer {} failed: {}", consumer_name, error) {}
|
||||
};
|
||||
|
||||
class ConsumerReadMessagesFailedException : public KafkaStreamException {
|
||||
public:
|
||||
ConsumerReadMessagesFailedException(const std::string_view consumer_name, const std::string_view error)
|
||||
: KafkaStreamException("Error happened in consumer {} while fetching messages: {}", consumer_name, error) {}
|
||||
};
|
||||
} // namespace memgraph::integrations::kafka
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
|
||||
#include "integrations/pulsar/consumer.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <pulsar/Client.h>
|
||||
#include <pulsar/InitialPosition.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "integrations/constants.hpp"
|
||||
#include "integrations/pulsar/exceptions.hpp"
|
||||
#include "utils/concepts.hpp"
|
||||
@@ -33,6 +34,10 @@ namespace {
|
||||
template <typename T>
|
||||
concept PulsarConsumer = utils::SameAsAnyOf<T, pulsar_client::Consumer, pulsar_client::Reader>;
|
||||
|
||||
template <typename TFunc>
|
||||
concept PulsarMessageGetter =
|
||||
std::same_as<const pulsar_client::Message &, std::invoke_result_t<TFunc, const Message &>>;
|
||||
|
||||
pulsar_client::Result ConsumeMessage(pulsar_client::Consumer &consumer, pulsar_client::Message &message,
|
||||
int remaining_timeout_in_ms) {
|
||||
return consumer.receive(message, remaining_timeout_in_ms);
|
||||
@@ -97,6 +102,26 @@ pulsar_client::Client CreateClient(const std::string &service_url) {
|
||||
conf.setLogger(new SpdlogLoggerFactory);
|
||||
return {service_url, conf};
|
||||
}
|
||||
|
||||
template <PulsarConsumer TConsumer, PulsarMessageGetter TPulsarMessageGetter>
|
||||
void TryToConsumeBatch(TConsumer &consumer, const ConsumerInfo &info, const ConsumerFunction &consumer_function,
|
||||
pulsar_client::MessageId &last_message_id, const std::vector<Message> &batch,
|
||||
const TPulsarMessageGetter &message_getter) {
|
||||
consumer_function(batch);
|
||||
|
||||
auto has_message_failed = [&consumer, &info, &last_message_id, &message_getter](const auto &message) {
|
||||
if (const auto result = consumer.acknowledge(message_getter(message)); result != pulsar_client::ResultOk) {
|
||||
spdlog::warn("Acknowledging a message of consumer {} failed: {}", info.consumer_name, result);
|
||||
return true;
|
||||
}
|
||||
last_message_id = message_getter(message).getMessageId();
|
||||
return false;
|
||||
};
|
||||
|
||||
if (std::ranges::any_of(batch, has_message_failed)) {
|
||||
throw ConsumerAcknowledgeMessagesFailedException(info.consumer_name);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Message::Message(pulsar_client::Message &&message) : message_{std::move(message)} {}
|
||||
@@ -137,6 +162,24 @@ void Consumer::Start() {
|
||||
StartConsuming();
|
||||
}
|
||||
|
||||
void Consumer::StartWithLimit(const uint64_t limit_batches,
|
||||
const std::optional<std::chrono::milliseconds> timeout) const {
|
||||
if (is_running_) {
|
||||
throw ConsumerRunningException(info_.consumer_name);
|
||||
}
|
||||
if (limit_batches < kMinimumStartBatchLimit) {
|
||||
throw ConsumerStartFailedException(
|
||||
info_.consumer_name, fmt::format("Batch limit has to be greater than or equal to {}", kMinimumStartBatchLimit));
|
||||
}
|
||||
if (timeout.value_or(kMinimumInterval) < kMinimumInterval) {
|
||||
throw ConsumerStartFailedException(
|
||||
info_.consumer_name,
|
||||
fmt::format("Timeout has to be greater than or equal to {} milliseconds", kMinimumInterval.count()));
|
||||
}
|
||||
|
||||
StartConsumingWithLimit(limit_batches, timeout);
|
||||
}
|
||||
|
||||
void Consumer::Stop() {
|
||||
if (!is_running_) {
|
||||
throw ConsumerStoppedException(info_.consumer_name);
|
||||
@@ -154,7 +197,7 @@ void Consumer::StopIfRunning() {
|
||||
}
|
||||
}
|
||||
|
||||
void Consumer::Check(std::optional<std::chrono::milliseconds> timeout, std::optional<int64_t> limit_batches,
|
||||
void Consumer::Check(std::optional<std::chrono::milliseconds> timeout, std::optional<uint64_t> limit_batches,
|
||||
const ConsumerFunction &check_consumer_function) const {
|
||||
// NOLINTNEXTLINE (modernize-use-nullptr)
|
||||
if (timeout.value_or(kMinimumInterval) < kMinimumInterval) {
|
||||
@@ -240,9 +283,7 @@ void Consumer::StartConsuming() {
|
||||
auto maybe_batch = GetBatch(consumer_, info_, is_running_, last_message_id_);
|
||||
|
||||
if (maybe_batch.HasError()) {
|
||||
spdlog::warn("Error happened in consumer {} while fetching messages: {}!", info_.consumer_name,
|
||||
maybe_batch.GetError());
|
||||
break;
|
||||
throw ConsumerReadMessagesFailedException(info_.consumer_name, maybe_batch.GetError());
|
||||
}
|
||||
|
||||
const auto &batch = maybe_batch.GetValue();
|
||||
@@ -254,18 +295,8 @@ void Consumer::StartConsuming() {
|
||||
spdlog::info("Pulsar consumer {} is processing a batch", info_.consumer_name);
|
||||
|
||||
try {
|
||||
consumer_function_(batch);
|
||||
|
||||
if (std::any_of(batch.begin(), batch.end(), [&](const auto &message) {
|
||||
if (const auto result = consumer_.acknowledge(message.message_); result != pulsar_client::ResultOk) {
|
||||
spdlog::warn("Acknowledging a message of consumer {} failed: {}", info_.consumer_name, result);
|
||||
return true;
|
||||
}
|
||||
last_message_id_ = message.message_.getMessageId();
|
||||
return false;
|
||||
})) {
|
||||
break;
|
||||
}
|
||||
TryToConsumeBatch(consumer_, info_, consumer_function_, last_message_id_, batch,
|
||||
[&](const Message &message) -> const pulsar_client::Message & { return message.message_; });
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::warn("Error happened in consumer {} while processing a batch: {}!", info_.consumer_name, e.what());
|
||||
break;
|
||||
@@ -277,6 +308,43 @@ void Consumer::StartConsuming() {
|
||||
});
|
||||
}
|
||||
|
||||
void Consumer::StartConsumingWithLimit(uint64_t limit_batches, std::optional<std::chrono::milliseconds> timeout) const {
|
||||
if (is_running_.exchange(true)) {
|
||||
throw ConsumerRunningException(info_.consumer_name);
|
||||
}
|
||||
utils::OnScopeExit restore_is_running([this] { is_running_.store(false); });
|
||||
|
||||
const auto timeout_to_use = timeout.value_or(kDefaultCheckTimeout);
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
|
||||
for (uint64_t batch_count = 0; batch_count < limit_batches;) {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now - start >= timeout_to_use) {
|
||||
throw ConsumerCheckFailedException(info_.consumer_name, "Timeout reached");
|
||||
}
|
||||
|
||||
const auto maybe_batch = GetBatch(consumer_, info_, is_running_, last_message_id_);
|
||||
|
||||
if (maybe_batch.HasError()) {
|
||||
throw ConsumerReadMessagesFailedException(info_.consumer_name, maybe_batch.GetError());
|
||||
}
|
||||
|
||||
const auto &batch = maybe_batch.GetValue();
|
||||
|
||||
if (batch.empty()) {
|
||||
continue;
|
||||
}
|
||||
++batch_count;
|
||||
|
||||
spdlog::info("Pulsar consumer {} is processing a batch", info_.consumer_name);
|
||||
|
||||
TryToConsumeBatch(consumer_, info_, consumer_function_, last_message_id_, batch,
|
||||
[](const Message &message) -> const pulsar_client::Message & { return message.message_; });
|
||||
|
||||
spdlog::info("Pulsar consumer {} finished processing", info_.consumer_name);
|
||||
}
|
||||
}
|
||||
|
||||
void Consumer::StopConsuming() {
|
||||
is_running_.store(false);
|
||||
if (thread_.joinable()) {
|
||||
|
||||
@@ -58,25 +58,27 @@ class Consumer final {
|
||||
|
||||
bool IsRunning() const;
|
||||
void Start();
|
||||
void StartWithLimit(uint64_t limit_batches, std::optional<std::chrono::milliseconds> timeout) const;
|
||||
void Stop();
|
||||
void StopIfRunning();
|
||||
|
||||
void Check(std::optional<std::chrono::milliseconds> timeout, std::optional<int64_t> limit_batches,
|
||||
void Check(std::optional<std::chrono::milliseconds> timeout, std::optional<uint64_t> limit_batches,
|
||||
const ConsumerFunction &check_consumer_function) const;
|
||||
|
||||
const ConsumerInfo &Info() const;
|
||||
|
||||
private:
|
||||
void StartConsuming();
|
||||
void StartConsumingWithLimit(uint64_t limit_batches, std::optional<std::chrono::milliseconds> timeout) const;
|
||||
void StopConsuming();
|
||||
|
||||
ConsumerInfo info_;
|
||||
mutable pulsar_client::Client client_;
|
||||
pulsar_client::Consumer consumer_;
|
||||
mutable pulsar_client::Consumer consumer_;
|
||||
ConsumerFunction consumer_function_;
|
||||
|
||||
mutable std::atomic<bool> is_running_{false};
|
||||
pulsar_client::MessageId last_message_id_{pulsar_client::MessageId::earliest()};
|
||||
mutable pulsar_client::MessageId last_message_id_{pulsar_client::MessageId::earliest()}; // Protected by is_running_
|
||||
std::thread thread_;
|
||||
};
|
||||
} // namespace memgraph::integrations::pulsar
|
||||
|
||||
@@ -55,4 +55,16 @@ class TopicNotFoundException : public PulsarStreamException {
|
||||
TopicNotFoundException(const std::string &consumer_name, const std::string &topic_name)
|
||||
: PulsarStreamException("Pulsar consumer {} cannot find topic {}", consumer_name, topic_name) {}
|
||||
};
|
||||
|
||||
class ConsumerReadMessagesFailedException : public PulsarStreamException {
|
||||
public:
|
||||
ConsumerReadMessagesFailedException(const std::string_view consumer_name, const std::string_view error)
|
||||
: PulsarStreamException("Error happened in consumer {} while fetching messages: {}", consumer_name, error) {}
|
||||
};
|
||||
|
||||
class ConsumerAcknowledgeMessagesFailedException : public PulsarStreamException {
|
||||
public:
|
||||
explicit ConsumerAcknowledgeMessagesFailedException(const std::string_view consumer_name)
|
||||
: PulsarStreamException("Acknowledging a message of consumer {} has failed!", consumer_name) {}
|
||||
};
|
||||
} // namespace memgraph::integrations::pulsar
|
||||
|
||||
@@ -216,6 +216,11 @@ DEFINE_bool(telemetry_enabled, false,
|
||||
"the database runtime (vertex and edge counts and resource usage) "
|
||||
"to allow for easier improvement of the product.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_restore_replicas_on_startup, true,
|
||||
"Controls replicas should be restored automatically."); // TODO(42jeremy) this must be removed once T0835
|
||||
// is implemented.
|
||||
|
||||
// Streams flags
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint32(
|
||||
@@ -252,6 +257,11 @@ DEFINE_double(query_execution_timeout_sec, 600,
|
||||
"Maximum allowed query execution time. Queries exceeding this "
|
||||
"limit will be aborted. Value of 0 means no limit.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint64(replication_replica_check_frequency_sec, 1,
|
||||
"The time duration between two replica checks/pings. If < 1, replicas will NOT be checked at all. NOTE: "
|
||||
"The MAIN instance allocates a new thread for each REPLICA.");
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint64(
|
||||
memory_limit, 0,
|
||||
@@ -1190,7 +1200,8 @@ int main(int argc, char **argv) {
|
||||
.snapshot_retention_count = FLAGS_storage_snapshot_retention_count,
|
||||
.wal_file_size_kibibytes = FLAGS_storage_wal_file_size_kib,
|
||||
.wal_file_flush_every_n_tx = FLAGS_storage_wal_file_flush_every_n_tx,
|
||||
.snapshot_on_exit = FLAGS_storage_snapshot_on_exit},
|
||||
.snapshot_on_exit = FLAGS_storage_snapshot_on_exit,
|
||||
.restore_replicas_on_startup = FLAGS_storage_restore_replicas_on_startup},
|
||||
.transaction = {.isolation_level = ParseIsolationLevel()}};
|
||||
if (FLAGS_storage_snapshot_interval_sec == 0) {
|
||||
if (FLAGS_storage_wal_enabled) {
|
||||
@@ -1216,6 +1227,7 @@ int main(int argc, char **argv) {
|
||||
&db,
|
||||
{.query = {.allow_load_csv = FLAGS_allow_load_csv},
|
||||
.execution_timeout_sec = FLAGS_query_execution_timeout_sec,
|
||||
.replication_replica_check_frequency = std::chrono::seconds(FLAGS_replication_replica_check_frequency_sec),
|
||||
.default_kafka_bootstrap_servers = FLAGS_kafka_bootstrap_servers,
|
||||
.default_pulsar_service_url = FLAGS_pulsar_service_url,
|
||||
.stream_transaction_conflict_retries = FLAGS_stream_transaction_conflict_retries,
|
||||
|
||||
@@ -9,4 +9,5 @@ target_link_libraries(mg-memory mg-utils fmt)
|
||||
|
||||
if (ENABLE_JEMALLOC)
|
||||
target_link_libraries(mg-memory Jemalloc::Jemalloc)
|
||||
target_compile_definitions(mg-memory PRIVATE USE_JEMALLOC=1)
|
||||
endif()
|
||||
|
||||
@@ -170,7 +170,7 @@ enum class CsvParserState {
|
||||
EXPECT_DELIMITER,
|
||||
};
|
||||
|
||||
bool SubstringStartsWith(const std::string_view &str, size_t pos, const std::string_view &what) {
|
||||
bool SubstringStartsWith(const std::string_view str, size_t pos, const std::string_view what) {
|
||||
return memgraph::utils::StartsWith(memgraph::utils::Substr(str, pos), what);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@ set(mg_query_sources
|
||||
stream/common.cpp
|
||||
trigger.cpp
|
||||
trigger_context.cpp
|
||||
typed_value.cpp)
|
||||
typed_value.cpp
|
||||
graph.cpp
|
||||
db_accessor.cpp)
|
||||
|
||||
find_package(Boost REQUIRED)
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ bool TypedValueCompare(const TypedValue &a, const TypedValue &b) {
|
||||
case TypedValue::Type::Vertex:
|
||||
case TypedValue::Type::Edge:
|
||||
case TypedValue::Type::Path:
|
||||
case TypedValue::Type::Graph:
|
||||
throw QueryRuntimeException("Comparison is not defined for values of type {}.", a.type());
|
||||
case TypedValue::Type::Null:
|
||||
LOG_FATAL("Invalid type");
|
||||
|
||||
@@ -21,6 +21,8 @@ struct InterpreterConfig {
|
||||
|
||||
// The default execution timeout is 10 minutes.
|
||||
double execution_timeout_sec{600.0};
|
||||
// The same as \ref memgraph::storage::replication::ReplicationClientConfig
|
||||
std::chrono::seconds replication_replica_check_frequency{1};
|
||||
|
||||
std::string default_kafka_bootstrap_servers;
|
||||
std::string default_pulsar_service_url;
|
||||
|
||||
155
src/query/db_accessor.cpp
Normal file
155
src/query/db_accessor.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
// 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.
|
||||
|
||||
#include "query/db_accessor.hpp"
|
||||
|
||||
#include "query/graph.hpp"
|
||||
|
||||
#include <cppitertools/filter.hpp>
|
||||
#include <cppitertools/imap.hpp>
|
||||
#include "utils/pmr/unordered_set.hpp"
|
||||
|
||||
namespace memgraph::query {
|
||||
SubgraphDbAccessor::SubgraphDbAccessor(query::DbAccessor *db_accessor, Graph *graph)
|
||||
: db_accessor_(db_accessor), graph_(graph) {}
|
||||
|
||||
SubgraphDbAccessor *SubgraphDbAccessor::MakeSubgraphDbAccessor(DbAccessor *db_accessor, Graph *graph) {
|
||||
return new SubgraphDbAccessor(db_accessor, graph);
|
||||
}
|
||||
|
||||
storage::PropertyId SubgraphDbAccessor::NameToProperty(const std::string_view name) {
|
||||
return db_accessor_->NameToProperty(name);
|
||||
}
|
||||
|
||||
storage::LabelId SubgraphDbAccessor::NameToLabel(const std::string_view name) {
|
||||
return db_accessor_->NameToLabel(name);
|
||||
}
|
||||
|
||||
storage::EdgeTypeId SubgraphDbAccessor::NameToEdgeType(const std::string_view name) {
|
||||
return db_accessor_->NameToEdgeType(name);
|
||||
}
|
||||
|
||||
const std::string &SubgraphDbAccessor::PropertyToName(storage::PropertyId prop) const {
|
||||
return db_accessor_->PropertyToName(prop);
|
||||
}
|
||||
|
||||
const std::string &SubgraphDbAccessor::LabelToName(storage::LabelId label) const {
|
||||
return db_accessor_->LabelToName(label);
|
||||
}
|
||||
|
||||
const std::string &SubgraphDbAccessor::EdgeTypeToName(storage::EdgeTypeId type) const {
|
||||
return db_accessor_->EdgeTypeToName(type);
|
||||
}
|
||||
|
||||
storage::Result<std::optional<EdgeAccessor>> SubgraphDbAccessor::RemoveEdge(EdgeAccessor *edge) {
|
||||
auto result = db_accessor_->RemoveEdge(edge);
|
||||
if (result.HasError() || !*result) {
|
||||
return result;
|
||||
}
|
||||
return this->graph_->RemoveEdge(*edge);
|
||||
}
|
||||
|
||||
storage::Result<EdgeAccessor> SubgraphDbAccessor::InsertEdge(SubgraphVertexAccessor *from, SubgraphVertexAccessor *to,
|
||||
const storage::EdgeTypeId &edge_type) {
|
||||
VertexAccessor *from_impl = &from->impl_;
|
||||
VertexAccessor *to_impl = &to->impl_;
|
||||
|
||||
auto result = db_accessor_->InsertEdge(from_impl, to_impl, edge_type);
|
||||
if (result.HasError()) {
|
||||
return result;
|
||||
}
|
||||
this->graph_->InsertEdge(*result);
|
||||
return result;
|
||||
}
|
||||
|
||||
storage::Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>>
|
||||
SubgraphDbAccessor::DetachRemoveVertex(
|
||||
VertexAccessor *) { // NOLINT(hicpp-named-parameter, readability-convert-member-functions-to-static)
|
||||
throw std::logic_error{"Such operation not possible on subgraph"};
|
||||
}
|
||||
|
||||
storage::Result<std::optional<VertexAccessor>> SubgraphDbAccessor::RemoveVertex(
|
||||
SubgraphVertexAccessor *subgraphvertex_accessor) {
|
||||
VertexAccessor *vertex_accessor = &subgraphvertex_accessor->impl_;
|
||||
auto result = db_accessor_->RemoveVertex(vertex_accessor);
|
||||
if (result.HasError() || !*result) {
|
||||
return result;
|
||||
}
|
||||
return this->graph_->RemoveVertex(*vertex_accessor);
|
||||
}
|
||||
|
||||
SubgraphVertexAccessor SubgraphDbAccessor::InsertVertex() {
|
||||
VertexAccessor vertex = db_accessor_->InsertVertex();
|
||||
this->graph_->InsertVertex(vertex);
|
||||
return SubgraphVertexAccessor(vertex, this->getGraph());
|
||||
}
|
||||
|
||||
VerticesIterable SubgraphDbAccessor::Vertices(storage::View) {
|
||||
return VerticesIterable(graph_->vertices());
|
||||
} // NOLINT(hicpp-named-parameter)
|
||||
|
||||
std::optional<VertexAccessor> SubgraphDbAccessor::FindVertex(storage::Gid gid, storage::View view) {
|
||||
std::optional<VertexAccessor> maybe_vertex = db_accessor_->FindVertex(gid, view);
|
||||
if (maybe_vertex && this->graph_->ContainsVertex(*maybe_vertex)) {
|
||||
return *maybe_vertex;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
query::Graph *SubgraphDbAccessor::getGraph() { return graph_; }
|
||||
|
||||
VertexAccessor SubgraphVertexAccessor::GetVertexAccessor() const { return impl_; }
|
||||
|
||||
auto SubgraphVertexAccessor::OutEdges(storage::View view) const -> decltype(impl_.OutEdges(view)) {
|
||||
auto maybe_edges = impl_.impl_.OutEdges(view, {});
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
auto edges = std::move(*maybe_edges);
|
||||
auto graph_edges = graph_->edges();
|
||||
|
||||
std::unordered_set<storage::EdgeAccessor> graph_edges_storage;
|
||||
|
||||
for (auto e : graph_edges) {
|
||||
graph_edges_storage.insert(e.impl_);
|
||||
}
|
||||
|
||||
std::vector<storage::EdgeAccessor> filteredOutEdges;
|
||||
for (auto &edge : edges) {
|
||||
if (std::find(begin(graph_edges_storage), end(graph_edges_storage), edge) != std::end(graph_edges_storage)) {
|
||||
filteredOutEdges.push_back(edge);
|
||||
}
|
||||
}
|
||||
|
||||
return iter::imap(VertexAccessor::MakeEdgeAccessor, std::move(filteredOutEdges));
|
||||
}
|
||||
|
||||
auto SubgraphVertexAccessor::InEdges(storage::View view) const -> decltype(impl_.InEdges(view)) {
|
||||
auto maybe_edges = impl_.impl_.InEdges(view, {});
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
auto edges = std::move(*maybe_edges);
|
||||
auto graph_edges = graph_->edges();
|
||||
|
||||
std::unordered_set<storage::EdgeAccessor> graph_edges_storage;
|
||||
|
||||
for (auto e : graph_edges) {
|
||||
graph_edges_storage.insert(e.impl_);
|
||||
}
|
||||
|
||||
std::vector<storage::EdgeAccessor> filteredOutEdges;
|
||||
for (auto &edge : edges) {
|
||||
if (std::find(begin(graph_edges_storage), end(graph_edges_storage), edge) != std::end(graph_edges_storage)) {
|
||||
filteredOutEdges.push_back(edge);
|
||||
}
|
||||
}
|
||||
|
||||
return iter::imap(VertexAccessor::MakeEdgeAccessor, std::move(filteredOutEdges));
|
||||
}
|
||||
|
||||
} // namespace memgraph::query
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/result.hpp"
|
||||
#include "utils/pmr/unordered_set.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Our communication layer and query engine don't mix
|
||||
@@ -45,6 +47,7 @@
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
class Graph;
|
||||
class VertexAccessor;
|
||||
|
||||
class EdgeAccessor final {
|
||||
@@ -185,38 +188,115 @@ inline VertexAccessor EdgeAccessor::From() const { return VertexAccessor(impl_.F
|
||||
|
||||
inline bool EdgeAccessor::IsCycle() const { return To() == From(); }
|
||||
|
||||
class DbAccessor final {
|
||||
storage::Storage::Accessor *accessor_;
|
||||
class SubgraphVertexAccessor final {
|
||||
public:
|
||||
query::VertexAccessor impl_;
|
||||
query::Graph *graph_;
|
||||
|
||||
class VerticesIterable final {
|
||||
storage::VerticesIterable iterable_;
|
||||
explicit SubgraphVertexAccessor(query::VertexAccessor impl, query::Graph *graph_) : impl_(impl), graph_(graph_) {}
|
||||
|
||||
bool operator==(const SubgraphVertexAccessor &v) const noexcept {
|
||||
static_assert(noexcept(impl_ == v.impl_));
|
||||
return impl_ == v.impl_;
|
||||
}
|
||||
|
||||
auto InEdges(storage::View view) const -> decltype(impl_.OutEdges(view));
|
||||
|
||||
auto OutEdges(storage::View view) const -> decltype(impl_.OutEdges(view));
|
||||
|
||||
auto Labels(storage::View view) const { return impl_.Labels(view); }
|
||||
|
||||
storage::Result<bool> AddLabel(storage::LabelId label) { return impl_.AddLabel(label); }
|
||||
|
||||
storage::Result<bool> RemoveLabel(storage::LabelId label) { return impl_.RemoveLabel(label); }
|
||||
|
||||
storage::Result<bool> HasLabel(storage::View view, storage::LabelId label) const {
|
||||
return impl_.HasLabel(view, label);
|
||||
}
|
||||
|
||||
auto Properties(storage::View view) const { return impl_.Properties(view); }
|
||||
|
||||
storage::Result<storage::PropertyValue> GetProperty(storage::View view, storage::PropertyId key) const {
|
||||
return impl_.GetProperty(view, key);
|
||||
}
|
||||
|
||||
storage::Gid Gid() const noexcept { return impl_.Gid(); }
|
||||
|
||||
storage::Result<storage::PropertyValue> SetProperty(storage::PropertyId key, const storage::PropertyValue &value) {
|
||||
return impl_.SetProperty(key, value);
|
||||
}
|
||||
VertexAccessor GetVertexAccessor() const;
|
||||
};
|
||||
} // namespace memgraph::query
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<memgraph::query::VertexAccessor> {
|
||||
size_t operator()(const memgraph::query::VertexAccessor &v) const { return std::hash<decltype(v.impl_)>{}(v.impl_); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<memgraph::query::EdgeAccessor> {
|
||||
size_t operator()(const memgraph::query::EdgeAccessor &e) const { return std::hash<decltype(e.impl_)>{}(e.impl_); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
class VerticesIterable final {
|
||||
std::variant<storage::VerticesIterable, std::unordered_set<VertexAccessor, std::hash<VertexAccessor>,
|
||||
std::equal_to<void>, utils::Allocator<VertexAccessor>>>
|
||||
iterable_;
|
||||
|
||||
public:
|
||||
class Iterator final {
|
||||
std::variant<storage::VerticesIterable::Iterator,
|
||||
std::unordered_set<VertexAccessor, std::hash<VertexAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<VertexAccessor>>::iterator>
|
||||
it_;
|
||||
|
||||
public:
|
||||
class Iterator final {
|
||||
storage::VerticesIterable::Iterator it_;
|
||||
explicit Iterator(storage::VerticesIterable::Iterator it) : it_(it) {}
|
||||
explicit Iterator(std::unordered_set<VertexAccessor, std::hash<VertexAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<VertexAccessor>>::iterator it)
|
||||
: it_(it) {}
|
||||
|
||||
public:
|
||||
explicit Iterator(storage::VerticesIterable::Iterator it) : it_(it) {}
|
||||
VertexAccessor operator*() const {
|
||||
return std::visit(memgraph::utils::Overloaded{[](auto it_) { return VertexAccessor(*it_); }}, it_);
|
||||
}
|
||||
|
||||
VertexAccessor operator*() const { return VertexAccessor(*it_); }
|
||||
Iterator &operator++() {
|
||||
std::visit(memgraph::utils::Overloaded{[this](auto it_) { this->it_ = ++it_; }}, it_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator &operator++() {
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const Iterator &other) const { return it_ == other.it_; }
|
||||
|
||||
bool operator==(const Iterator &other) const { return it_ == other.it_; }
|
||||
|
||||
bool operator!=(const Iterator &other) const { return !(other == *this); }
|
||||
};
|
||||
|
||||
explicit VerticesIterable(storage::VerticesIterable iterable) : iterable_(std::move(iterable)) {}
|
||||
|
||||
Iterator begin() { return Iterator(iterable_.begin()); }
|
||||
|
||||
Iterator end() { return Iterator(iterable_.end()); }
|
||||
bool operator!=(const Iterator &other) const { return !(other == *this); }
|
||||
};
|
||||
|
||||
explicit VerticesIterable(storage::VerticesIterable iterable) : iterable_(std::move(iterable)) {}
|
||||
explicit VerticesIterable(std::unordered_set<VertexAccessor, std::hash<VertexAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<VertexAccessor>>
|
||||
vertices)
|
||||
: iterable_(vertices) {}
|
||||
|
||||
Iterator begin() {
|
||||
return std::visit(memgraph::utils::Overloaded{[](auto &iterable_) { return Iterator(iterable_.begin()); }},
|
||||
iterable_);
|
||||
}
|
||||
|
||||
Iterator end() {
|
||||
return std::visit(memgraph::utils::Overloaded{[](auto &iterable_) { return Iterator(iterable_.end()); }},
|
||||
iterable_);
|
||||
}
|
||||
};
|
||||
|
||||
class DbAccessor final {
|
||||
storage::Storage::Accessor *accessor_;
|
||||
|
||||
public:
|
||||
explicit DbAccessor(storage::Storage::Accessor *accessor) : accessor_(accessor) {}
|
||||
|
||||
@@ -310,11 +390,11 @@ class DbAccessor final {
|
||||
return std::make_optional<VertexAccessor>(*value);
|
||||
}
|
||||
|
||||
storage::PropertyId NameToProperty(const std::string_view &name) { return accessor_->NameToProperty(name); }
|
||||
storage::PropertyId NameToProperty(const std::string_view name) { return accessor_->NameToProperty(name); }
|
||||
|
||||
storage::LabelId NameToLabel(const std::string_view &name) { return accessor_->NameToLabel(name); }
|
||||
storage::LabelId NameToLabel(const std::string_view name) { return accessor_->NameToLabel(name); }
|
||||
|
||||
storage::EdgeTypeId NameToEdgeType(const std::string_view &name) { return accessor_->NameToEdgeType(name); }
|
||||
storage::EdgeTypeId NameToEdgeType(const std::string_view name) { return accessor_->NameToEdgeType(name); }
|
||||
|
||||
const std::string &PropertyToName(storage::PropertyId prop) const { return accessor_->PropertyToName(prop); }
|
||||
|
||||
@@ -358,18 +438,44 @@ class DbAccessor final {
|
||||
storage::ConstraintsInfo ListAllConstraints() const { return accessor_->ListAllConstraints(); }
|
||||
};
|
||||
|
||||
class SubgraphDbAccessor final {
|
||||
DbAccessor *db_accessor_;
|
||||
Graph *graph_;
|
||||
|
||||
public:
|
||||
explicit SubgraphDbAccessor(DbAccessor *db_accessor, Graph *graph);
|
||||
|
||||
static SubgraphDbAccessor *MakeSubgraphDbAccessor(DbAccessor *db_accessor, Graph *graph);
|
||||
|
||||
storage::PropertyId NameToProperty(std::string_view name);
|
||||
|
||||
storage::LabelId NameToLabel(std::string_view name);
|
||||
|
||||
storage::EdgeTypeId NameToEdgeType(std::string_view name);
|
||||
|
||||
const std::string &PropertyToName(storage::PropertyId prop) const;
|
||||
|
||||
const std::string &LabelToName(storage::LabelId label) const;
|
||||
|
||||
const std::string &EdgeTypeToName(storage::EdgeTypeId type) const;
|
||||
|
||||
storage::Result<std::optional<EdgeAccessor>> RemoveEdge(EdgeAccessor *edge);
|
||||
|
||||
storage::Result<EdgeAccessor> InsertEdge(SubgraphVertexAccessor *from, SubgraphVertexAccessor *to,
|
||||
const storage::EdgeTypeId &edge_type);
|
||||
|
||||
storage::Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachRemoveVertex(
|
||||
VertexAccessor *vertex_accessor);
|
||||
|
||||
storage::Result<std::optional<VertexAccessor>> RemoveVertex(SubgraphVertexAccessor *vertex_accessor);
|
||||
|
||||
SubgraphVertexAccessor InsertVertex();
|
||||
|
||||
VerticesIterable Vertices(storage::View view);
|
||||
|
||||
std::optional<VertexAccessor> FindVertex(storage::Gid gid, storage::View view);
|
||||
|
||||
Graph *getGraph();
|
||||
};
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<memgraph::query::VertexAccessor> {
|
||||
size_t operator()(const memgraph::query::VertexAccessor &v) const { return std::hash<decltype(v.impl_)>{}(v.impl_); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<memgraph::query::EdgeAccessor> {
|
||||
size_t operator()(const memgraph::query::EdgeAccessor &e) const { return std::hash<decltype(e.impl_)>{}(e.impl_); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
@@ -46,7 +46,7 @@ const char *kInternalPropertyId = "__mg_id__";
|
||||
const char *kInternalVertexLabel = "__mg_vertex__";
|
||||
|
||||
/// A helper function that escapes label, edge type and property names.
|
||||
std::string EscapeName(const std::string_view &value) {
|
||||
std::string EscapeName(const std::string_view value) {
|
||||
std::string out;
|
||||
out.reserve(value.size() + 2);
|
||||
out.append(1, '`');
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "common/types.hpp"
|
||||
#include "query/frontend/ast/ast_visitor.hpp"
|
||||
#include "query/frontend/semantic/symbol.hpp"
|
||||
#include "query/interpret/awesome_memgraph_functions.hpp"
|
||||
@@ -465,7 +464,7 @@ cpp<#
|
||||
:documentation "Symbol table position of the symbol this Aggregation is mapped to."))
|
||||
(:public
|
||||
(lcp:define-enum op
|
||||
(count min max sum avg collect-list collect-map)
|
||||
(count min max sum avg collect-list collect-map project)
|
||||
(:serialize))
|
||||
#>cpp
|
||||
Aggregation() = default;
|
||||
@@ -476,10 +475,11 @@ cpp<#
|
||||
static const constexpr char *const kSum = "SUM";
|
||||
static const constexpr char *const kAvg = "AVG";
|
||||
static const constexpr char *const kCollect = "COLLECT";
|
||||
static const constexpr char *const kProject = "PROJECT";
|
||||
|
||||
static std::string OpToString(Op op) {
|
||||
const char *op_strings[] = {kCount, kMin, kMax, kSum,
|
||||
kAvg, kCollect, kCollect};
|
||||
kAvg, kCollect, kCollect, kProject};
|
||||
return op_strings[static_cast<int>(op)];
|
||||
}
|
||||
|
||||
@@ -2254,7 +2254,7 @@ cpp<#
|
||||
(lcp:define-enum privilege
|
||||
(create delete match merge set remove index stats auth constraint
|
||||
dump replication durability read_file free_memory trigger config stream module_read module_write
|
||||
websocket schema)
|
||||
websocket)
|
||||
(:serialize))
|
||||
#>cpp
|
||||
AuthQuery() = default;
|
||||
@@ -2296,7 +2296,7 @@ const std::vector<AuthQuery::Privilege> kPrivilegesAll = {
|
||||
AuthQuery::Privilege::FREE_MEMORY, AuthQuery::Privilege::TRIGGER,
|
||||
AuthQuery::Privilege::CONFIG, AuthQuery::Privilege::STREAM,
|
||||
AuthQuery::Privilege::MODULE_READ, AuthQuery::Privilege::MODULE_WRITE,
|
||||
AuthQuery::Privilege::WEBSOCKET, AuthQuery::Privilege::SCHEMA};
|
||||
AuthQuery::Privilege::WEBSOCKET};
|
||||
cpp<#
|
||||
|
||||
(lcp:define-class info-query (query)
|
||||
@@ -2376,10 +2376,7 @@ cpp<#
|
||||
(port "Expression *" :initval "nullptr" :scope :public
|
||||
:slk-save #'slk-save-ast-pointer
|
||||
:slk-load (slk-load-ast-pointer "Expression"))
|
||||
(sync_mode "SyncMode" :scope :public)
|
||||
(timeout "Expression *" :initval "nullptr" :scope :public
|
||||
:slk-save #'slk-save-ast-pointer
|
||||
:slk-load (slk-load-ast-pointer "Expression")))
|
||||
(sync_mode "SyncMode" :scope :public))
|
||||
|
||||
(:public
|
||||
(lcp:define-enum action
|
||||
@@ -2392,6 +2389,9 @@ cpp<#
|
||||
(lcp:define-enum sync-mode
|
||||
(sync async)
|
||||
(:serialize))
|
||||
(lcp:define-enum replica-state
|
||||
(ready replicating recovery invalid)
|
||||
(:serialize))
|
||||
#>cpp
|
||||
ReplicationQuery() = default;
|
||||
|
||||
@@ -2666,37 +2666,5 @@ cpp<#
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
(lcp:define-class schema-query (query)
|
||||
((action "Action" :scope :public)
|
||||
(label "LabelIx" :scope :public
|
||||
:slk-load (lambda (member)
|
||||
#>cpp
|
||||
slk::Load(&self->${member}, reader, storage);
|
||||
cpp<#)
|
||||
:clone (lambda (source dest)
|
||||
#>cpp
|
||||
${dest} = storage->GetLabelIx(${source}.name);
|
||||
cpp<#))
|
||||
(schema_type_map "std::unordered_map<PropertyIx, common::SchemaType>"
|
||||
:slk-save #'slk-save-property-map
|
||||
:slk-load #'slk-load-property-map
|
||||
:scope :public))
|
||||
|
||||
(:public
|
||||
(lcp:define-enum action
|
||||
(create-schema drop-schema show-schema show-schemas)
|
||||
(:serialize))
|
||||
#>cpp
|
||||
SchemaQuery() = default;
|
||||
|
||||
DEFVISITABLE(QueryVisitor<void>);
|
||||
cpp<#)
|
||||
(:private
|
||||
#>cpp
|
||||
friend class AstStorage;
|
||||
cpp<#)
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
(lcp:pop-namespace) ;; namespace query
|
||||
(lcp:pop-namespace) ;; namespace memgraph
|
||||
|
||||
@@ -94,7 +94,6 @@ class StreamQuery;
|
||||
class SettingQuery;
|
||||
class VersionQuery;
|
||||
class Foreach;
|
||||
class SchemaQuery;
|
||||
|
||||
using TreeCompositeVisitor = utils::CompositeVisitor<
|
||||
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
|
||||
@@ -126,9 +125,9 @@ class ExpressionVisitor
|
||||
None, ParameterLookup, Identifier, PrimitiveLiteral, RegexMatch> {};
|
||||
|
||||
template <class TResult>
|
||||
class QueryVisitor : public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery,
|
||||
InfoQuery, ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery,
|
||||
FreeMemoryQuery, TriggerQuery, IsolationLevelQuery, CreateSnapshotQuery,
|
||||
StreamQuery, SettingQuery, VersionQuery, SchemaQuery> {};
|
||||
class QueryVisitor
|
||||
: public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery, InfoQuery,
|
||||
ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery, FreeMemoryQuery, TriggerQuery,
|
||||
IsolationLevelQuery, CreateSnapshotQuery, StreamQuery, SettingQuery, VersionQuery> {};
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
|
||||
#include "common/types.hpp"
|
||||
#include "query/exceptions.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/frontend/ast/ast_visitor.hpp"
|
||||
@@ -276,18 +275,7 @@ antlrcpp::Any CypherMainVisitor::visitRegisterReplica(MemgraphCypher::RegisterRe
|
||||
replication_query->replica_name_ = ctx->replicaName()->symbolicName()->accept(this).as<std::string>();
|
||||
if (ctx->SYNC()) {
|
||||
replication_query->sync_mode_ = memgraph::query::ReplicationQuery::SyncMode::SYNC;
|
||||
if (ctx->WITH() && ctx->TIMEOUT()) {
|
||||
if (ctx->timeout->numberLiteral()) {
|
||||
// we accept both double and integer literals
|
||||
replication_query->timeout_ = ctx->timeout->accept(this);
|
||||
} else {
|
||||
throw SemanticException("Timeout should be a integer or double literal!");
|
||||
}
|
||||
}
|
||||
} else if (ctx->ASYNC()) {
|
||||
if (ctx->WITH() && ctx->TIMEOUT()) {
|
||||
throw SyntaxException("Timeout can be set only for the SYNC replication mode!");
|
||||
}
|
||||
replication_query->sync_mode_ = memgraph::query::ReplicationQuery::SyncMode::ASYNC;
|
||||
}
|
||||
|
||||
@@ -776,6 +764,23 @@ antlrcpp::Any CypherMainVisitor::visitDropStream(MemgraphCypher::DropStreamConte
|
||||
antlrcpp::Any CypherMainVisitor::visitStartStream(MemgraphCypher::StartStreamContext *ctx) {
|
||||
auto *stream_query = storage_->Create<StreamQuery>();
|
||||
stream_query->action_ = StreamQuery::Action::START_STREAM;
|
||||
|
||||
if (ctx->BATCH_LIMIT()) {
|
||||
if (!ctx->batchLimit->numberLiteral() || !ctx->batchLimit->numberLiteral()->integerLiteral()) {
|
||||
throw SemanticException("Batch limit should be an integer literal!");
|
||||
}
|
||||
stream_query->batch_limit_ = ctx->batchLimit->accept(this);
|
||||
}
|
||||
if (ctx->TIMEOUT()) {
|
||||
if (!ctx->timeout->numberLiteral() || !ctx->timeout->numberLiteral()->integerLiteral()) {
|
||||
throw SemanticException("Timeout should be an integer literal!");
|
||||
}
|
||||
if (!ctx->BATCH_LIMIT()) {
|
||||
throw SemanticException("Parameter TIMEOUT can only be defined if BATCH_LIMIT is defined");
|
||||
}
|
||||
stream_query->timeout_ = ctx->timeout->accept(this);
|
||||
}
|
||||
|
||||
stream_query->stream_name_ = ctx->streamName()->symbolicName()->accept(this).as<std::string>();
|
||||
return stream_query;
|
||||
}
|
||||
@@ -1339,7 +1344,6 @@ antlrcpp::Any CypherMainVisitor::visitPrivilege(MemgraphCypher::PrivilegeContext
|
||||
if (ctx->MODULE_READ()) return AuthQuery::Privilege::MODULE_READ;
|
||||
if (ctx->MODULE_WRITE()) return AuthQuery::Privilege::MODULE_WRITE;
|
||||
if (ctx->WEBSOCKET()) return AuthQuery::Privilege::WEBSOCKET;
|
||||
if (ctx->SCHEMA()) return AuthQuery::Privilege::SCHEMA;
|
||||
LOG_FATAL("Should not get here - unknown privilege!");
|
||||
}
|
||||
|
||||
@@ -2104,6 +2108,10 @@ antlrcpp::Any CypherMainVisitor::visitFunctionInvocation(MemgraphCypher::Functio
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::COLLECT_LIST));
|
||||
}
|
||||
if (function_name == Aggregation::kProject) {
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::PROJECT));
|
||||
}
|
||||
}
|
||||
|
||||
if (expressions.size() == 2U && function_name == Aggregation::kCollect) {
|
||||
@@ -2338,94 +2346,6 @@ antlrcpp::Any CypherMainVisitor::visitForeach(MemgraphCypher::ForeachContext *ct
|
||||
return for_each;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitSchemaQuery(MemgraphCypher::SchemaQueryContext *ctx) {
|
||||
MG_ASSERT(ctx->children.size() == 1, "SchemaQuery should have exactly one child!");
|
||||
auto *schema_query = ctx->children[0]->accept(this).as<SchemaQuery *>();
|
||||
query_ = schema_query;
|
||||
return schema_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitShowSchema(MemgraphCypher::ShowSchemaContext *ctx) {
|
||||
auto *schema_query = storage_->Create<SchemaQuery>();
|
||||
schema_query->action_ = SchemaQuery::Action::SHOW_SCHEMA;
|
||||
schema_query->label_ = AddLabel(ctx->labelName()->accept(this));
|
||||
query_ = schema_query;
|
||||
return schema_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitShowSchemas(MemgraphCypher::ShowSchemasContext * /*ctx*/) {
|
||||
auto *schema_query = storage_->Create<SchemaQuery>();
|
||||
schema_query->action_ = SchemaQuery::Action::SHOW_SCHEMAS;
|
||||
query_ = schema_query;
|
||||
return schema_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitPropertyType(MemgraphCypher::PropertyTypeContext *ctx) {
|
||||
MG_ASSERT(ctx->symbolicName());
|
||||
const auto property_type = utils::ToLowerCase(ctx->symbolicName()->accept(this).as<std::string>());
|
||||
if (property_type == "bool") {
|
||||
return common::SchemaType::BOOL;
|
||||
}
|
||||
if (property_type == "string") {
|
||||
return common::SchemaType::STRING;
|
||||
}
|
||||
if (property_type == "integer") {
|
||||
return common::SchemaType::INT;
|
||||
}
|
||||
if (property_type == "date") {
|
||||
return common::SchemaType::DATE;
|
||||
}
|
||||
if (property_type == "duration") {
|
||||
return common::SchemaType::DURATION;
|
||||
}
|
||||
if (property_type == "localdatetime") {
|
||||
return common::SchemaType::LOCALDATETIME;
|
||||
}
|
||||
if (property_type == "localtime") {
|
||||
return common::SchemaType::LOCALTIME;
|
||||
}
|
||||
throw SyntaxException("Property type must be one of the supported types!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any CypherMainVisitor::visitSchemaTypeMap(MemgraphCypher::SchemaTypeMapContext *ctx) {
|
||||
std::unordered_map<PropertyIx, common::SchemaType> map;
|
||||
for (auto *property_key_pair : ctx->propertyKeyTypePair()) {
|
||||
PropertyIx key = property_key_pair->propertyKeyName()->accept(this);
|
||||
common::SchemaType type = property_key_pair->propertyType()->accept(this);
|
||||
if (!map.insert({key, type}).second) {
|
||||
throw SemanticException("Same property name can't appear twice in a schema map.");
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitCreateSchema(MemgraphCypher::CreateSchemaContext *ctx) {
|
||||
auto *schema_query = storage_->Create<SchemaQuery>();
|
||||
schema_query->action_ = SchemaQuery::Action::CREATE_SCHEMA;
|
||||
schema_query->label_ = AddLabel(ctx->labelName()->accept(this));
|
||||
if (!ctx->schemaTypeMap()) {
|
||||
throw SemanticException("Schema property map must exist!");
|
||||
}
|
||||
schema_query->schema_type_map_ =
|
||||
ctx->schemaTypeMap()->accept(this).as<std::unordered_map<PropertyIx, common::SchemaType>>();
|
||||
query_ = schema_query;
|
||||
return schema_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any CypherMainVisitor::visitDropSchema(MemgraphCypher::DropSchemaContext *ctx) {
|
||||
auto *schema_query = storage_->Create<SchemaQuery>();
|
||||
schema_query->action_ = SchemaQuery::Action::DROP_SCHEMA;
|
||||
schema_query->label_ = AddLabel(ctx->labelName()->accept(this));
|
||||
query_ = schema_query;
|
||||
return schema_query;
|
||||
}
|
||||
|
||||
LabelIx CypherMainVisitor::AddLabel(const std::string &name) { return storage_->GetLabelIx(name); }
|
||||
|
||||
PropertyIx CypherMainVisitor::AddProperty(const std::string &name) { return storage_->GetPropertyIx(name); }
|
||||
|
||||
@@ -849,41 +849,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
*/
|
||||
antlrcpp::Any visitForeach(MemgraphCypher::ForeachContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any visitPropertyType(MemgraphCypher::PropertyTypeContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any visitSchemaTypeMap(MemgraphCypher::SchemaTypeMapContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any visitSchemaQuery(MemgraphCypher::SchemaQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any visitShowSchema(MemgraphCypher::ShowSchemaContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any visitShowSchemas(MemgraphCypher::ShowSchemasContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any visitCreateSchema(MemgraphCypher::CreateSchemaContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any visitDropSchema(MemgraphCypher::DropSchemaContext *ctx) override;
|
||||
|
||||
public:
|
||||
Query *query() { return query_; }
|
||||
const static std::string kAnonPrefix;
|
||||
|
||||
@@ -46,10 +46,10 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| DROP
|
||||
| DUMP
|
||||
| EXECUTE
|
||||
| FREE
|
||||
| FROM
|
||||
| FOR
|
||||
| FOREACH
|
||||
| FREE
|
||||
| FROM
|
||||
| GLOBAL
|
||||
| GRANT
|
||||
| HEADER
|
||||
@@ -76,8 +76,6 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| ROLE
|
||||
| ROLES
|
||||
| QUOTE
|
||||
| SCHEMA
|
||||
| SCHEMAS
|
||||
| SESSION
|
||||
| SETTING
|
||||
| SETTINGS
|
||||
@@ -124,7 +122,6 @@ query : cypherQuery
|
||||
| streamQuery
|
||||
| settingQuery
|
||||
| versionQuery
|
||||
| schemaQuery
|
||||
;
|
||||
|
||||
authQuery : createRole
|
||||
@@ -195,12 +192,6 @@ settingQuery : setSetting
|
||||
| showSettings
|
||||
;
|
||||
|
||||
schemaQuery : showSchema
|
||||
| showSchemas
|
||||
| createSchema
|
||||
| dropSchema
|
||||
;
|
||||
|
||||
loadCsv : LOAD CSV FROM csvFile ( WITH | NO ) HEADER
|
||||
( IGNORE BAD ) ?
|
||||
( DELIMITER delimiter ) ?
|
||||
@@ -263,7 +254,6 @@ privilege : CREATE
|
||||
| MODULE_READ
|
||||
| MODULE_WRITE
|
||||
| WEBSOCKET
|
||||
| SCHEMA
|
||||
;
|
||||
|
||||
privilegeList : privilege ( ',' privilege )* ;
|
||||
@@ -286,7 +276,6 @@ replicaName : symbolicName ;
|
||||
socketAddress : literal ;
|
||||
|
||||
registerReplica : REGISTER REPLICA replicaName ( SYNC | ASYNC )
|
||||
( WITH TIMEOUT timeout=literal ) ?
|
||||
TO socketAddress ;
|
||||
|
||||
dropReplica : DROP REPLICA replicaName ;
|
||||
@@ -361,7 +350,7 @@ pulsarCreateStream : CREATE PULSAR STREAM streamName ( pulsarCreateStreamConfig
|
||||
|
||||
dropStream : DROP STREAM streamName ;
|
||||
|
||||
startStream : START STREAM streamName ;
|
||||
startStream : START STREAM streamName ( BATCH_LIMIT batchLimit=literal ) ? ( TIMEOUT timeout=literal ) ? ;
|
||||
|
||||
startAllStreams : START ALL STREAMS ;
|
||||
|
||||
@@ -384,17 +373,3 @@ showSetting : SHOW DATABASE SETTING settingName ;
|
||||
showSettings : SHOW DATABASE SETTINGS ;
|
||||
|
||||
versionQuery : SHOW VERSION ;
|
||||
|
||||
showSchema : SHOW SCHEMA ON ':' labelName ;
|
||||
|
||||
showSchemas : SHOW SCHEMAS ;
|
||||
|
||||
propertyType : symbolicName ;
|
||||
|
||||
propertyKeyTypePair : propertyKeyName propertyType ;
|
||||
|
||||
schemaTypeMap : '(' propertyKeyTypePair ( ',' propertyKeyTypePair )* ')' ;
|
||||
|
||||
createSchema : CREATE SCHEMA ON ':' labelName schemaTypeMap ;
|
||||
|
||||
dropSchema : DROP SCHEMA ON ':' labelName ;
|
||||
|
||||
@@ -89,8 +89,6 @@ REVOKE : R E V O K E ;
|
||||
ROLE : R O L E ;
|
||||
ROLES : R O L E S ;
|
||||
QUOTE : Q U O T E ;
|
||||
SCHEMA : S C H E M A ;
|
||||
SCHEMAS : S C H E M A S ;
|
||||
SERVICE_URL : S E R V I C E UNDERSCORE U R L ;
|
||||
SESSION : S E S S I O N ;
|
||||
SETTING : S E T T I N G ;
|
||||
|
||||
@@ -80,8 +80,6 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
|
||||
|
||||
void Visit(VersionQuery & /*version_query*/) override { AddPrivilege(AuthQuery::Privilege::STATS); }
|
||||
|
||||
void Visit(SchemaQuery & /*schema_query*/) override { AddPrivilege(AuthQuery::Privilege::SCHEMA); }
|
||||
|
||||
bool PreVisit(Create & /*unused*/) override {
|
||||
AddPrivilege(AuthQuery::Privilege::CREATE);
|
||||
return false;
|
||||
|
||||
@@ -204,9 +204,8 @@ const trie::Trie kKeywords = {"union",
|
||||
"pulsar",
|
||||
"service_url",
|
||||
"version",
|
||||
"websocket",
|
||||
"foreach",
|
||||
"schema"};
|
||||
"websocket"
|
||||
"foreach"};
|
||||
|
||||
// Unicode codepoints that are allowed at the start of the unescaped name.
|
||||
const std::bitset<kBitsetSize> kUnescapedNameAllowedStarts(
|
||||
|
||||
91
src/query/graph.cpp
Normal file
91
src/query/graph.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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.
|
||||
|
||||
#include "query/graph.hpp"
|
||||
#include "query/path.hpp"
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
Graph::Graph(utils::MemoryResource *memory) : vertices_(memory), edges_(memory) {}
|
||||
|
||||
Graph::Graph(const Graph &other, utils::MemoryResource *memory)
|
||||
: vertices_(other.vertices_, memory), edges_(other.edges_, memory) {}
|
||||
|
||||
Graph::Graph(Graph &&other) noexcept : Graph(std::move(other), other.GetMemoryResource()) {}
|
||||
|
||||
Graph::Graph(const Graph &other)
|
||||
: Graph(other,
|
||||
std::allocator_traits<allocator_type>::select_on_container_copy_construction(other.GetMemoryResource())
|
||||
.GetMemoryResource()) {}
|
||||
|
||||
Graph::Graph(Graph &&other, utils::MemoryResource *memory)
|
||||
: vertices_(std::move(other.vertices_), memory), edges_(std::move(other.edges_), memory) {}
|
||||
|
||||
void Graph::Expand(const Path &path) {
|
||||
const auto &path_vertices_ = path.vertices();
|
||||
const auto &path_edges_ = path.edges();
|
||||
std::for_each(path_vertices_.begin(), path_vertices_.end(), [this](const VertexAccessor v) { vertices_.insert(v); });
|
||||
std::for_each(path_edges_.begin(), path_edges_.end(), [this](const EdgeAccessor e) { edges_.insert(e); });
|
||||
}
|
||||
|
||||
void Graph::InsertVertex(const VertexAccessor &vertex) { vertices_.insert(vertex); }
|
||||
|
||||
void Graph::InsertEdge(const EdgeAccessor &edge) { edges_.insert(edge); }
|
||||
|
||||
bool Graph::ContainsVertex(const VertexAccessor &vertex) {
|
||||
return std::find(begin(vertices_), end(vertices_), vertex) != std::end(vertices_);
|
||||
}
|
||||
|
||||
std::optional<VertexAccessor> Graph::RemoveVertex(const VertexAccessor &vertex) {
|
||||
if (!ContainsVertex(vertex)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto value = vertices_.erase(vertex);
|
||||
if (value == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return vertex;
|
||||
}
|
||||
|
||||
std::optional<EdgeAccessor> Graph::RemoveEdge(const EdgeAccessor &edge) {
|
||||
auto value = edges_.erase(edge);
|
||||
if (value == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return edge;
|
||||
}
|
||||
|
||||
std::vector<EdgeAccessor> Graph::OutEdges(VertexAccessor vertex_accessor) {
|
||||
std::vector<EdgeAccessor> out_edges;
|
||||
for (const auto &edge : edges_) {
|
||||
if (edge.From() == vertex_accessor) {
|
||||
out_edges.emplace_back(edge);
|
||||
}
|
||||
}
|
||||
return out_edges;
|
||||
}
|
||||
|
||||
/** Copy assign other, utils::MemoryResource of `this` is used */
|
||||
Graph &Graph::operator=(const Graph &) = default;
|
||||
|
||||
/** Move assign other, utils::MemoryResource of `this` is used. */
|
||||
Graph &Graph::operator=(Graph &&) noexcept = default;
|
||||
|
||||
Graph::~Graph() = default;
|
||||
|
||||
utils::pmr::unordered_set<VertexAccessor> &Graph::vertices() { return vertices_; }
|
||||
utils::pmr::unordered_set<EdgeAccessor> &Graph::edges() { return edges_; }
|
||||
const utils::pmr::unordered_set<VertexAccessor> &Graph::vertices() const { return vertices_; }
|
||||
const utils::pmr::unordered_set<EdgeAccessor> &Graph::edges() const { return edges_; }
|
||||
|
||||
utils::MemoryResource *Graph::GetMemoryResource() const { return vertices_.get_allocator().GetMemoryResource(); }
|
||||
|
||||
} // namespace memgraph::query
|
||||
111
src/query/graph.hpp
Normal file
111
src/query/graph.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/pmr/unordered_set.hpp"
|
||||
#include "utils/pmr/vector.hpp"
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
class Path;
|
||||
/**
|
||||
* A data structure that holds a graph. A graph consists of at least one
|
||||
* vertex, and zero or more edges.
|
||||
*/
|
||||
class Graph final {
|
||||
public:
|
||||
/** Allocator type so that STL containers are aware that we need one */
|
||||
using allocator_type = utils::Allocator<char>;
|
||||
|
||||
/**
|
||||
* Create the graph with no elements
|
||||
* Allocations are done using the given MemoryResource.
|
||||
*/
|
||||
explicit Graph(utils::MemoryResource *memory);
|
||||
|
||||
/**
|
||||
* Construct a copy of other.
|
||||
* utils::MemoryResource is obtained by calling
|
||||
* std::allocator_traits<>::
|
||||
* select_on_container_copy_construction(other.GetMemoryResource()).
|
||||
* Since we use utils::Allocator, which does not propagate, this means that we
|
||||
* will default to utils::NewDeleteResource().
|
||||
*/
|
||||
Graph(const Graph &other);
|
||||
|
||||
/** Construct a copy using the given utils::MemoryResource */
|
||||
Graph(const Graph &other, utils::MemoryResource *memory);
|
||||
|
||||
/**
|
||||
* Construct with the value of other.
|
||||
* utils::MemoryResource is obtained from other. After the move, other will be
|
||||
* empty.
|
||||
*/
|
||||
Graph(Graph &&other) noexcept;
|
||||
|
||||
/**
|
||||
* Construct with the value of other, but use the given utils::MemoryResource.
|
||||
* After the move, other may not be empty if `*memory !=
|
||||
* *other.GetMemoryResource()`, because an element-wise move will be
|
||||
* performed.
|
||||
*/
|
||||
Graph(Graph &&other, utils::MemoryResource *memory);
|
||||
|
||||
/** Expands the graph with the given path. */
|
||||
void Expand(const Path &path);
|
||||
|
||||
/** Inserts the vertex in the graph. */
|
||||
void InsertVertex(const VertexAccessor &vertex);
|
||||
|
||||
/** Inserts the edge in the graph. */
|
||||
void InsertEdge(const EdgeAccessor &edge);
|
||||
|
||||
/** Checks whether the graph contains the vertex. */
|
||||
bool ContainsVertex(const VertexAccessor &vertex);
|
||||
|
||||
/** Removes the vertex from the graph if the vertex is in the graph. */
|
||||
std::optional<VertexAccessor> RemoveVertex(const VertexAccessor &vertex);
|
||||
|
||||
/** Removes the vertex from the graph if the vertex is in the graph. */
|
||||
std::optional<EdgeAccessor> RemoveEdge(const EdgeAccessor &edge);
|
||||
|
||||
/** Return the out edges of the given vertex. */
|
||||
std::vector<EdgeAccessor> OutEdges(VertexAccessor vertex_accessor);
|
||||
|
||||
/** Copy assign other, utils::MemoryResource of `this` is used */
|
||||
Graph &operator=(const Graph &);
|
||||
|
||||
/** Move assign other, utils::MemoryResource of `this` is used. */
|
||||
Graph &operator=(Graph &&) noexcept;
|
||||
|
||||
~Graph();
|
||||
|
||||
utils::pmr::unordered_set<VertexAccessor> &vertices();
|
||||
utils::pmr::unordered_set<EdgeAccessor> &edges();
|
||||
const utils::pmr::unordered_set<VertexAccessor> &vertices() const;
|
||||
const utils::pmr::unordered_set<EdgeAccessor> &edges() const;
|
||||
|
||||
utils::MemoryResource *GetMemoryResource() const;
|
||||
|
||||
private:
|
||||
// Contains all the vertices in the Graph.
|
||||
utils::pmr::unordered_set<VertexAccessor> vertices_;
|
||||
// Contains all the edges in the Graph
|
||||
utils::pmr::unordered_set<EdgeAccessor> edges_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::query
|
||||
@@ -587,6 +587,8 @@ TypedValue ValueType(const TypedValue *args, int64_t nargs, const FunctionContex
|
||||
return TypedValue("LOCAL_DATE_TIME", ctx.memory);
|
||||
case TypedValue::Type::Duration:
|
||||
return TypedValue("DURATION", ctx.memory);
|
||||
case TypedValue::Type::Graph:
|
||||
throw QueryRuntimeException("Trying to get graph");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,21 +884,36 @@ TypedValue Id(const TypedValue *args, int64_t nargs, const FunctionContext &ctx)
|
||||
}
|
||||
|
||||
TypedValue ToString(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) {
|
||||
FType<Or<Null, String, Number, Bool>>("toString", args, nargs);
|
||||
FType<Or<Null, String, Number, Date, LocalTime, LocalDateTime, Duration, Bool>>("toString", args, nargs);
|
||||
const auto &arg = args[0];
|
||||
if (arg.IsNull()) {
|
||||
return TypedValue(ctx.memory);
|
||||
} else if (arg.IsString()) {
|
||||
}
|
||||
if (arg.IsString()) {
|
||||
return TypedValue(arg, ctx.memory);
|
||||
} else if (arg.IsInt()) {
|
||||
}
|
||||
if (arg.IsInt()) {
|
||||
// TODO: This is making a pointless copy of std::string, we may want to
|
||||
// use a different conversion to string
|
||||
return TypedValue(std::to_string(arg.ValueInt()), ctx.memory);
|
||||
} else if (arg.IsDouble()) {
|
||||
return TypedValue(std::to_string(arg.ValueDouble()), ctx.memory);
|
||||
} else {
|
||||
return TypedValue(arg.ValueBool() ? "true" : "false", ctx.memory);
|
||||
}
|
||||
if (arg.IsDouble()) {
|
||||
return TypedValue(std::to_string(arg.ValueDouble()), ctx.memory);
|
||||
}
|
||||
if (arg.IsDate()) {
|
||||
return TypedValue(arg.ValueDate().ToString(), ctx.memory);
|
||||
}
|
||||
if (arg.IsLocalTime()) {
|
||||
return TypedValue(arg.ValueLocalTime().ToString(), ctx.memory);
|
||||
}
|
||||
if (arg.IsLocalDateTime()) {
|
||||
return TypedValue(arg.ValueLocalDateTime().ToString(), ctx.memory);
|
||||
}
|
||||
if (arg.IsDuration()) {
|
||||
return TypedValue(arg.ValueDuration().ToString(), ctx.memory);
|
||||
}
|
||||
|
||||
return TypedValue(arg.ValueBool() ? "true" : "false", ctx.memory);
|
||||
}
|
||||
|
||||
TypedValue Timestamp(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) {
|
||||
|
||||
@@ -111,7 +111,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
TypedValue Visit(IfOperator &if_operator) override {
|
||||
auto condition = if_operator.condition_->Accept(*this);
|
||||
if (condition.IsNull()) {
|
||||
return if_operator.then_expression_->Accept(*this);
|
||||
return if_operator.else_expression_->Accept(*this);
|
||||
}
|
||||
if (condition.type() != TypedValue::Type::Bool) {
|
||||
// At the moment IfOperator is used only in CASE construct.
|
||||
@@ -365,6 +365,9 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
throw QueryRuntimeException("Invalid property name {} for LocalDateTime", prop_name);
|
||||
}
|
||||
case TypedValue::Type::Graph: {
|
||||
throw QueryRuntimeException("Invalid operation for Graph");
|
||||
}
|
||||
default:
|
||||
throw QueryRuntimeException("Only nodes, edges, maps and temporal types have properties to be looked-up.");
|
||||
}
|
||||
@@ -716,7 +719,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
|
||||
template <class TRecordAccessor>
|
||||
storage::PropertyValue GetProperty(const TRecordAccessor &record_accessor, const std::string_view &name) {
|
||||
storage::PropertyValue GetProperty(const TRecordAccessor &record_accessor, const std::string_view name) {
|
||||
auto maybe_prop = record_accessor.GetProperty(view_, dba_->NameToProperty(name));
|
||||
if (maybe_prop.HasError() && maybe_prop.GetError() == storage::Error::NONEXISTENT_OBJECT) {
|
||||
// This is a very nasty and temporary hack in order to make MERGE work.
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
#include "query/trigger.hpp"
|
||||
#include "query/typed_value.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/schemas.hpp"
|
||||
#include "storage/v2/replication/enums.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
#include "utils/csv_parsing.hpp"
|
||||
#include "utils/event_counter.hpp"
|
||||
@@ -161,7 +161,8 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
void RegisterReplica(const std::string &name, const std::string &socket_address,
|
||||
const ReplicationQuery::SyncMode sync_mode, const std::optional<double> timeout) override {
|
||||
const ReplicationQuery::SyncMode sync_mode,
|
||||
const std::chrono::seconds replica_check_frequency) override {
|
||||
if (db_->GetReplicationRole() == storage::ReplicationRole::REPLICA) {
|
||||
// replica can't register another replica
|
||||
throw QueryRuntimeException("Replica can't register another replica!");
|
||||
@@ -183,8 +184,9 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
|
||||
io::network::Endpoint::ParseSocketOrIpAddress(socket_address, query::kDefaultReplicationPort);
|
||||
if (maybe_ip_and_port) {
|
||||
auto [ip, port] = *maybe_ip_and_port;
|
||||
auto ret =
|
||||
db_->RegisterReplica(name, {std::move(ip), port}, repl_mode, {.timeout = timeout, .ssl = std::nullopt});
|
||||
auto ret = db_->RegisterReplica(name, {std::move(ip), port}, repl_mode,
|
||||
storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
|
||||
{.replica_check_frequency = replica_check_frequency, .ssl = std::nullopt});
|
||||
if (ret.HasError()) {
|
||||
throw QueryRuntimeException(fmt::format("Couldn't register replica '{}'!", name));
|
||||
}
|
||||
@@ -227,8 +229,24 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
|
||||
replica.sync_mode = ReplicationQuery::SyncMode::ASYNC;
|
||||
break;
|
||||
}
|
||||
if (repl_info.timeout) {
|
||||
replica.timeout = *repl_info.timeout;
|
||||
|
||||
replica.current_timestamp_of_replica = repl_info.timestamp_info.current_timestamp_of_replica;
|
||||
replica.current_number_of_timestamp_behind_master =
|
||||
repl_info.timestamp_info.current_number_of_timestamp_behind_master;
|
||||
|
||||
switch (repl_info.state) {
|
||||
case storage::replication::ReplicaState::READY:
|
||||
replica.state = ReplicationQuery::ReplicaState::READY;
|
||||
break;
|
||||
case storage::replication::ReplicaState::REPLICATING:
|
||||
replica.state = ReplicationQuery::ReplicaState::REPLICATING;
|
||||
break;
|
||||
case storage::replication::ReplicaState::RECOVERY:
|
||||
replica.state = ReplicationQuery::ReplicaState::RECOVERY;
|
||||
break;
|
||||
case storage::replication::ReplicaState::INVALID:
|
||||
replica.state = ReplicationQuery::ReplicaState::INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
return replica;
|
||||
@@ -449,7 +467,7 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
return callback;
|
||||
}
|
||||
case ReplicationQuery::Action::SHOW_REPLICATION_ROLE: {
|
||||
callback.header = {"replication mode"};
|
||||
callback.header = {"replication role"};
|
||||
callback.fn = [handler = ReplQueryHandler{interpreter_context->db}] {
|
||||
auto mode = handler.ShowReplicationRole();
|
||||
switch (mode) {
|
||||
@@ -467,22 +485,18 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
const auto &name = repl_query->replica_name_;
|
||||
const auto &sync_mode = repl_query->sync_mode_;
|
||||
auto socket_address = repl_query->socket_address_->Accept(evaluator);
|
||||
auto timeout = EvaluateOptionalExpression(repl_query->timeout_, &evaluator);
|
||||
std::optional<double> maybe_timeout;
|
||||
if (timeout.IsDouble()) {
|
||||
maybe_timeout = timeout.ValueDouble();
|
||||
} else if (timeout.IsInt()) {
|
||||
maybe_timeout = static_cast<double>(timeout.ValueInt());
|
||||
}
|
||||
const auto replica_check_frequency = interpreter_context->config.replication_replica_check_frequency;
|
||||
|
||||
callback.fn = [handler = ReplQueryHandler{interpreter_context->db}, name, socket_address, sync_mode,
|
||||
maybe_timeout]() mutable {
|
||||
handler.RegisterReplica(name, std::string(socket_address.ValueString()), sync_mode, maybe_timeout);
|
||||
replica_check_frequency]() mutable {
|
||||
handler.RegisterReplica(name, std::string(socket_address.ValueString()), sync_mode, replica_check_frequency);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::REGISTER_REPLICA,
|
||||
fmt::format("Replica {} is registered.", repl_query->replica_name_));
|
||||
return callback;
|
||||
}
|
||||
|
||||
case ReplicationQuery::Action::DROP_REPLICA: {
|
||||
const auto &name = repl_query->replica_name_;
|
||||
callback.fn = [handler = ReplQueryHandler{interpreter_context->db}, name]() mutable {
|
||||
@@ -493,8 +507,11 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
fmt::format("Replica {} is dropped.", repl_query->replica_name_));
|
||||
return callback;
|
||||
}
|
||||
|
||||
case ReplicationQuery::Action::SHOW_REPLICAS: {
|
||||
callback.header = {"name", "socket_address", "sync_mode", "timeout"};
|
||||
callback.header = {
|
||||
"name", "socket_address", "sync_mode", "current_timestamp_of_replica", "number_of_timestamp_behind_master",
|
||||
"state"};
|
||||
callback.fn = [handler = ReplQueryHandler{interpreter_context->db}, replica_nfields = callback.header.size()] {
|
||||
const auto &replicas = handler.ShowReplicas();
|
||||
auto typed_replicas = std::vector<std::vector<TypedValue>>{};
|
||||
@@ -505,6 +522,7 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
|
||||
typed_replica.emplace_back(TypedValue(replica.name));
|
||||
typed_replica.emplace_back(TypedValue(replica.socket_address));
|
||||
|
||||
switch (replica.sync_mode) {
|
||||
case ReplicationQuery::SyncMode::SYNC:
|
||||
typed_replica.emplace_back(TypedValue("sync"));
|
||||
@@ -513,11 +531,24 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
typed_replica.emplace_back(TypedValue("async"));
|
||||
break;
|
||||
}
|
||||
typed_replica.emplace_back(TypedValue(static_cast<int64_t>(replica.sync_mode)));
|
||||
if (replica.timeout) {
|
||||
typed_replica.emplace_back(TypedValue(*replica.timeout));
|
||||
} else {
|
||||
typed_replica.emplace_back(TypedValue());
|
||||
|
||||
typed_replica.emplace_back(TypedValue(static_cast<int64_t>(replica.current_timestamp_of_replica)));
|
||||
typed_replica.emplace_back(
|
||||
TypedValue(static_cast<int64_t>(replica.current_number_of_timestamp_behind_master)));
|
||||
|
||||
switch (replica.state) {
|
||||
case ReplicationQuery::ReplicaState::READY:
|
||||
typed_replica.emplace_back(TypedValue("ready"));
|
||||
break;
|
||||
case ReplicationQuery::ReplicaState::REPLICATING:
|
||||
typed_replica.emplace_back(TypedValue("replicating"));
|
||||
break;
|
||||
case ReplicationQuery::ReplicaState::RECOVERY:
|
||||
typed_replica.emplace_back(TypedValue("recovery"));
|
||||
break;
|
||||
case ReplicationQuery::ReplicaState::INVALID:
|
||||
typed_replica.emplace_back(TypedValue("invalid"));
|
||||
break;
|
||||
}
|
||||
|
||||
typed_replicas.emplace_back(std::move(typed_replica));
|
||||
@@ -653,12 +684,26 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::START_STREAM: {
|
||||
callback.fn = [interpreter_context, stream_name = stream_query->stream_name_]() {
|
||||
interpreter_context->streams.Start(stream_name);
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::START_STREAM,
|
||||
fmt::format("Started stream {}.", stream_query->stream_name_));
|
||||
const auto batch_limit = GetOptionalValue<int64_t>(stream_query->batch_limit_, evaluator);
|
||||
const auto timeout = GetOptionalValue<std::chrono::milliseconds>(stream_query->timeout_, evaluator);
|
||||
|
||||
if (batch_limit.has_value()) {
|
||||
if (batch_limit.value() < 0) {
|
||||
throw utils::BasicException("Parameter BATCH_LIMIT cannot hold negative value");
|
||||
}
|
||||
|
||||
callback.fn = [interpreter_context, stream_name = stream_query->stream_name_, batch_limit, timeout]() {
|
||||
interpreter_context->streams.StartWithLimit(stream_name, static_cast<uint64_t>(batch_limit.value()), timeout);
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
} else {
|
||||
callback.fn = [interpreter_context, stream_name = stream_query->stream_name_]() {
|
||||
interpreter_context->streams.Start(stream_name);
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::START_STREAM,
|
||||
fmt::format("Started stream {}.", stream_query->stream_name_));
|
||||
}
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::START_ALL_STREAMS: {
|
||||
@@ -727,10 +772,16 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::CHECK_STREAM: {
|
||||
callback.header = {"query", "parameters"};
|
||||
callback.header = {"queries", "raw messages"};
|
||||
|
||||
const auto batch_limit = GetOptionalValue<int64_t>(stream_query->batch_limit_, evaluator);
|
||||
if (batch_limit.has_value() && batch_limit.value() < 0) {
|
||||
throw utils::BasicException("Parameter BATCH_LIMIT cannot hold negative value");
|
||||
}
|
||||
|
||||
callback.fn = [interpreter_context, stream_name = stream_query->stream_name_,
|
||||
timeout = GetOptionalValue<std::chrono::milliseconds>(stream_query->timeout_, evaluator),
|
||||
batch_limit = GetOptionalValue<int64_t>(stream_query->batch_limit_, evaluator)]() mutable {
|
||||
batch_limit]() mutable {
|
||||
return interpreter_context->streams.Check(stream_name, timeout, batch_limit);
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::CHECK_STREAM,
|
||||
@@ -821,108 +872,6 @@ Callback HandleSettingQuery(SettingQuery *setting_query, const Parameters ¶m
|
||||
}
|
||||
}
|
||||
|
||||
Callback HandleSchemaQuery(SchemaQuery *schema_query, InterpreterContext *interpreter_context,
|
||||
std::vector<Notification> *notifications) {
|
||||
Callback callback;
|
||||
switch (schema_query->action_) {
|
||||
case SchemaQuery::Action::SHOW_SCHEMAS: {
|
||||
callback.header = {"label", "primary_key", "primary_key_type"};
|
||||
callback.fn = [interpreter_context]() {
|
||||
auto *db = interpreter_context->db;
|
||||
auto schemas_info = db->ListAllSchemas();
|
||||
std::vector<std::vector<TypedValue>> results;
|
||||
results.reserve(schemas_info.schemas.size());
|
||||
|
||||
for (const auto &[label_id, schema_types] : schemas_info.schemas) {
|
||||
std::vector<TypedValue> schema_info_row;
|
||||
schema_info_row.reserve(3);
|
||||
|
||||
schema_info_row.emplace_back(db->LabelToName(label_id));
|
||||
std::vector<std::string> primary_key_properties;
|
||||
primary_key_properties.reserve(schema_types.size());
|
||||
std::transform(schema_types.begin(), schema_types.end(), std::back_inserter(primary_key_properties),
|
||||
[&db](const auto &schema_type) {
|
||||
return db->PropertyToName(schema_type.property_id) +
|
||||
"::" + storage::SchemaTypeToString(schema_type.type);
|
||||
});
|
||||
|
||||
schema_info_row.emplace_back(utils::Join(primary_key_properties, ", "));
|
||||
schema_info_row.emplace_back(schema_types.size() == 1 ? "Single" : "Composite");
|
||||
|
||||
results.push_back(std::move(schema_info_row));
|
||||
}
|
||||
return results;
|
||||
};
|
||||
return callback;
|
||||
}
|
||||
case SchemaQuery::Action::SHOW_SCHEMA: {
|
||||
callback.header = {"property_name", "property_type"};
|
||||
callback.fn = [interpreter_context, primary_label = schema_query->label_]() {
|
||||
auto *db = interpreter_context->db;
|
||||
const auto label = db->NameToLabel(primary_label.name);
|
||||
const auto schemas_info = db->GetSchema(label);
|
||||
MG_ASSERT(schemas_info.schemas.size() < 2, "There can be only one schema under single label!");
|
||||
std::vector<std::vector<TypedValue>> results;
|
||||
if (!schemas_info.schemas.empty()) {
|
||||
const auto schema = schemas_info.schemas[0];
|
||||
|
||||
for (const auto &schema_property : schema.second) {
|
||||
std::vector<TypedValue> schema_info_row;
|
||||
schema_info_row.reserve(2);
|
||||
|
||||
schema_info_row.emplace_back(db->PropertyToName(schema_property.property_id));
|
||||
schema_info_row.emplace_back(storage::SchemaTypeToString(schema_property.type));
|
||||
|
||||
results.push_back(std::move(schema_info_row));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
return callback;
|
||||
}
|
||||
case SchemaQuery::Action::CREATE_SCHEMA: {
|
||||
auto schema_type_map = schema_query->schema_type_map_;
|
||||
if (schema_query->schema_type_map_.empty()) {
|
||||
throw SyntaxException("One or more types have to be defined in schema definition.");
|
||||
}
|
||||
callback.fn = [interpreter_context, primary_label = schema_query->label_,
|
||||
schema_type_map = std::move(schema_type_map)]() {
|
||||
auto *db = interpreter_context->db;
|
||||
const auto label = db->NameToLabel(primary_label.name);
|
||||
std::vector<storage::SchemaPropertyType> schemas_types;
|
||||
schemas_types.reserve(schema_type_map.size());
|
||||
for (const auto &schema_type : schema_type_map) {
|
||||
auto property_id = db->NameToProperty(schema_type.first.name);
|
||||
schemas_types.push_back({schema_type.second, property_id});
|
||||
}
|
||||
if (!db->CreateSchema(label, schemas_types)) {
|
||||
throw QueryException(fmt::format("Schema on label :{} already exists!", primary_label.name));
|
||||
}
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::CREATE_SCHEMA,
|
||||
fmt::format("Create schema on label :{}", schema_query->label_.name));
|
||||
return callback;
|
||||
}
|
||||
case SchemaQuery::Action::DROP_SCHEMA: {
|
||||
callback.fn = [interpreter_context, primary_label = schema_query->label_]() {
|
||||
auto *db = interpreter_context->db;
|
||||
const auto label = db->NameToLabel(primary_label.name);
|
||||
|
||||
if (!db->DropSchema(label)) {
|
||||
throw QueryException(fmt::format("Schema on label :{} does not exist!", primary_label.name));
|
||||
}
|
||||
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::DROP_SCHEMA,
|
||||
fmt::format("Dropped schema on label :{}", schema_query->label_.name));
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
return callback;
|
||||
}
|
||||
|
||||
// Struct for lazy pulling from a vector
|
||||
struct PullPlanVector {
|
||||
explicit PullPlanVector(std::vector<std::vector<TypedValue>> values) : values_(std::move(values)) {}
|
||||
@@ -2118,32 +2067,6 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
|
||||
RWType::NONE};
|
||||
}
|
||||
|
||||
PreparedQuery PrepareSchemaQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
InterpreterContext *interpreter_context, std::vector<Notification> *notifications) {
|
||||
if (in_explicit_transaction) {
|
||||
throw ConstraintInMulticommandTxException();
|
||||
}
|
||||
auto *schema_query = utils::Downcast<SchemaQuery>(parsed_query.query);
|
||||
MG_ASSERT(schema_query);
|
||||
auto callback = HandleSchemaQuery(schema_query, interpreter_context, notifications);
|
||||
|
||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||
[handler = std::move(callback.fn), action = QueryHandlerResult::NOTHING,
|
||||
pull_plan = std::shared_ptr<PullPlanVector>(nullptr)](
|
||||
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
||||
if (!pull_plan) {
|
||||
auto results = handler();
|
||||
pull_plan = std::make_shared<PullPlanVector>(std::move(results));
|
||||
}
|
||||
|
||||
if (pull_plan->Pull(stream, n)) {
|
||||
return action;
|
||||
}
|
||||
return std::nullopt;
|
||||
},
|
||||
RWType::NONE};
|
||||
}
|
||||
|
||||
void Interpreter::BeginTransaction() {
|
||||
const auto prepared_query = PrepareTransactionQuery("BEGIN");
|
||||
prepared_query.query_handler(nullptr, {});
|
||||
@@ -2277,9 +2200,6 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
prepared_query = PrepareSettingQuery(std::move(parsed_query), in_explicit_transaction_, &*execution_db_accessor_);
|
||||
} else if (utils::Downcast<VersionQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareVersionQuery(std::move(parsed_query), in_explicit_transaction_);
|
||||
} else if (utils::Downcast<SchemaQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareSchemaQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
|
||||
&query_execution->notifications);
|
||||
} else {
|
||||
LOG_FATAL("Should not get here -- unknown query type!");
|
||||
}
|
||||
|
||||
@@ -127,6 +127,9 @@ class ReplicationQueryHandler {
|
||||
std::string socket_address;
|
||||
ReplicationQuery::SyncMode sync_mode;
|
||||
std::optional<double> timeout;
|
||||
uint64_t current_timestamp_of_replica;
|
||||
uint64_t current_number_of_timestamp_behind_master;
|
||||
ReplicationQuery::ReplicaState state;
|
||||
};
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
@@ -137,7 +140,8 @@ class ReplicationQueryHandler {
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void RegisterReplica(const std::string &name, const std::string &socket_address,
|
||||
const ReplicationQuery::SyncMode sync_mode, const std::optional<double> timeout) = 0;
|
||||
ReplicationQuery::SyncMode sync_mode,
|
||||
const std::chrono::seconds replica_check_frequency) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void DropReplica(const std::string &replica_name) = 0;
|
||||
|
||||
@@ -38,8 +38,6 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
|
||||
return "CreateIndex"sv;
|
||||
case NotificationCode::CREATE_STREAM:
|
||||
return "CreateStream"sv;
|
||||
case NotificationCode::CREATE_SCHEMA:
|
||||
return "CreateSchema"sv;
|
||||
case NotificationCode::CHECK_STREAM:
|
||||
return "CheckStream"sv;
|
||||
case NotificationCode::CREATE_TRIGGER:
|
||||
@@ -50,8 +48,6 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
|
||||
return "DropReplica"sv;
|
||||
case NotificationCode::DROP_INDEX:
|
||||
return "DropIndex"sv;
|
||||
case NotificationCode::DROP_SCHEMA:
|
||||
return "DropSchema"sv;
|
||||
case NotificationCode::DROP_STREAM:
|
||||
return "DropStream"sv;
|
||||
case NotificationCode::DROP_TRIGGER:
|
||||
@@ -72,10 +68,6 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
|
||||
return "ReplicaPortWarning"sv;
|
||||
case NotificationCode::SET_REPLICA:
|
||||
return "SetReplica"sv;
|
||||
case NotificationCode::SHOW_SCHEMA:
|
||||
return "ShowSchema"sv;
|
||||
case NotificationCode::SHOW_SCHEMAS:
|
||||
return "ShowSchemas"sv;
|
||||
case NotificationCode::START_STREAM:
|
||||
return "StartStream"sv;
|
||||
case NotificationCode::START_ALL_STREAMS:
|
||||
@@ -122,4 +114,4 @@ std::string ExecutionStatsKeyToString(const ExecutionStats::Key key) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace memgraph::query
|
||||
} // namespace memgraph::query
|
||||
@@ -26,14 +26,12 @@ enum class SeverityLevel : uint8_t { INFO, WARNING };
|
||||
enum class NotificationCode : uint8_t {
|
||||
CREATE_CONSTRAINT,
|
||||
CREATE_INDEX,
|
||||
CREATE_SCHEMA,
|
||||
CHECK_STREAM,
|
||||
CREATE_STREAM,
|
||||
CREATE_TRIGGER,
|
||||
DROP_CONSTRAINT,
|
||||
DROP_INDEX,
|
||||
DROP_REPLICA,
|
||||
DROP_SCHEMA,
|
||||
DROP_STREAM,
|
||||
DROP_TRIGGER,
|
||||
EXISTANT_INDEX,
|
||||
@@ -44,8 +42,6 @@ enum class NotificationCode : uint8_t {
|
||||
REPLICA_PORT_WARNING,
|
||||
REGISTER_REPLICA,
|
||||
SET_REPLICA,
|
||||
SHOW_SCHEMA,
|
||||
SHOW_SCHEMAS,
|
||||
START_STREAM,
|
||||
START_ALL_STREAMS,
|
||||
STOP_STREAM,
|
||||
|
||||
@@ -31,6 +31,12 @@ class Path {
|
||||
/** Allocator type so that STL containers are aware that we need one */
|
||||
using allocator_type = utils::Allocator<char>;
|
||||
|
||||
/**
|
||||
* Create the path with no elements
|
||||
* Allocations are done using the given MemoryResource.
|
||||
*/
|
||||
explicit Path(utils::MemoryResource *memory) : vertices_(memory), edges_(memory) {}
|
||||
|
||||
/**
|
||||
* Create the path starting with the given vertex.
|
||||
* Allocations are done using the given MemoryResource.
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "query/exceptions.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/frontend/semantic/symbol_table.hpp"
|
||||
#include "query/graph.hpp"
|
||||
#include "query/interpret/eval.hpp"
|
||||
#include "query/path.hpp"
|
||||
#include "query/plan/scoped_profile.hpp"
|
||||
@@ -2607,10 +2608,12 @@ TypedValue DefaultAggregationOpValue(const Aggregate::Element &element, utils::M
|
||||
case Aggregation::Op::MAX:
|
||||
case Aggregation::Op::AVG:
|
||||
return TypedValue(memory);
|
||||
case Aggregation::Op::COLLECT_LIST:
|
||||
return TypedValue(TypedValue::TVector(memory));
|
||||
case Aggregation::Op::COLLECT_MAP:
|
||||
return TypedValue(TypedValue::TMap(memory));
|
||||
case Aggregation::Op::COLLECT_LIST:
|
||||
return TypedValue(TypedValue::TVector(memory));
|
||||
case Aggregation::Op::PROJECT:
|
||||
return TypedValue(query::Graph(memory));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@@ -2775,7 +2778,6 @@ class AggregateCursor : public Cursor {
|
||||
"Expected as much AggregationValue.counts_ as there are "
|
||||
"aggregations.");
|
||||
|
||||
// we iterate over counts, values and aggregation info at the same time
|
||||
auto count_it = agg_value->counts_.begin();
|
||||
auto value_it = agg_value->values_.begin();
|
||||
auto agg_elem_it = self_.aggregations_.begin();
|
||||
@@ -2814,6 +2816,11 @@ class AggregateCursor : public Cursor {
|
||||
case Aggregation::Op::COLLECT_LIST:
|
||||
value_it->ValueList().push_back(input_value);
|
||||
break;
|
||||
case Aggregation::Op::PROJECT: {
|
||||
EnsureOkForProject(input_value);
|
||||
value_it->ValueGraph().Expand(input_value.ValuePath());
|
||||
break;
|
||||
}
|
||||
case Aggregation::Op::COLLECT_MAP:
|
||||
auto key = agg_elem_it->key->Accept(*evaluator);
|
||||
if (key.type() != TypedValue::Type::String) throw QueryRuntimeException("Map key must be a string.");
|
||||
@@ -2862,6 +2869,11 @@ class AggregateCursor : public Cursor {
|
||||
case Aggregation::Op::COLLECT_LIST:
|
||||
value_it->ValueList().push_back(input_value);
|
||||
break;
|
||||
case Aggregation::Op::PROJECT: {
|
||||
EnsureOkForProject(input_value);
|
||||
value_it->ValueGraph().Expand(input_value.ValuePath());
|
||||
break;
|
||||
}
|
||||
case Aggregation::Op::COLLECT_MAP:
|
||||
auto key = agg_elem_it->key->Accept(*evaluator);
|
||||
if (key.type() != TypedValue::Type::String) throw QueryRuntimeException("Map key must be a string.");
|
||||
@@ -2898,6 +2910,18 @@ class AggregateCursor : public Cursor {
|
||||
throw QueryRuntimeException("Only numeric values allowed in SUM and AVG aggregations.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if the given TypedValue is legal in PROJECT and PROJECT_TRANSITIVE. If not
|
||||
* an appropriate exception is thrown. */
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
void EnsureOkForProject(const TypedValue &value) const {
|
||||
switch (value.type()) {
|
||||
case TypedValue::Type::Path:
|
||||
return;
|
||||
default:
|
||||
throw QueryRuntimeException("Only path values allowed in PROJECT aggregation.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UniqueCursorPtr Aggregate::MakeCursor(utils::MemoryResource *mem) const {
|
||||
@@ -3697,7 +3721,7 @@ std::unordered_map<std::string, int64_t> CallProcedure::GetAndResetCounters() {
|
||||
|
||||
namespace {
|
||||
|
||||
void CallCustomProcedure(const std::string_view &fully_qualified_procedure_name, const mgp_proc &proc,
|
||||
void CallCustomProcedure(const std::string_view fully_qualified_procedure_name, const mgp_proc &proc,
|
||||
const std::vector<Expression *> &args, mgp_graph &graph, ExpressionEvaluator *evaluator,
|
||||
utils::MemoryResource *memory, std::optional<size_t> memory_limit, mgp_result *result) {
|
||||
static_assert(std::uses_allocator_v<mgp_value, utils::Allocator<mgp_value>>,
|
||||
@@ -3710,6 +3734,15 @@ void CallCustomProcedure(const std::string_view &fully_qualified_procedure_name,
|
||||
for (auto *expression : args) {
|
||||
args_list.emplace_back(expression->Accept(*evaluator));
|
||||
}
|
||||
|
||||
if (!args_list.empty() && args_list.front().type() == TypedValue::Type::Graph) {
|
||||
auto *subgraph =
|
||||
new query::Graph(std::move(args_list.front().ValueGraph()), args_list.front().ValueGraph().GetMemoryResource());
|
||||
args_list.erase(args_list.begin());
|
||||
|
||||
graph.impl = query::SubgraphDbAccessor::MakeSubgraphDbAccessor(std::get<query::DbAccessor *>(graph.impl), subgraph);
|
||||
}
|
||||
|
||||
procedure::ConstructArguments(args_list, proc, fully_qualified_procedure_name, proc_args, graph);
|
||||
if (memory_limit) {
|
||||
SPDLOG_INFO("Running '{}' with memory limit of {}", fully_qualified_procedure_name,
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
#include "utils/temporal.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
#include <cppitertools/filter.hpp>
|
||||
#include <cppitertools/imap.hpp>
|
||||
|
||||
// This file contains implementation of top level C API functions, but this is
|
||||
// all actually part of memgraph::query::procedure. So use that namespace for simplicity.
|
||||
// NOLINTNEXTLINE(google-build-using-namespace)
|
||||
@@ -290,6 +293,8 @@ mgp_value_type FromTypedValueType(memgraph::query::TypedValue::Type type) {
|
||||
return MGP_VALUE_TYPE_LOCAL_DATE_TIME;
|
||||
case memgraph::query::TypedValue::Type::Duration:
|
||||
return MGP_VALUE_TYPE_DURATION;
|
||||
case memgraph::query::TypedValue::Type::Graph:
|
||||
throw std::logic_error{"No graph type"};
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@@ -324,17 +329,19 @@ memgraph::query::TypedValue ToTypedValue(const mgp_value &val, memgraph::utils::
|
||||
return memgraph::query::TypedValue(std::move(tv_map));
|
||||
}
|
||||
case MGP_VALUE_TYPE_VERTEX:
|
||||
return memgraph::query::TypedValue(val.vertex_v->impl, memory);
|
||||
return memgraph::query::TypedValue(val.vertex_v->getImpl(), memory);
|
||||
case MGP_VALUE_TYPE_EDGE:
|
||||
return memgraph::query::TypedValue(val.edge_v->impl, memory);
|
||||
case MGP_VALUE_TYPE_PATH: {
|
||||
const auto *path = val.path_v;
|
||||
MG_ASSERT(!path->vertices.empty());
|
||||
MG_ASSERT(path->vertices.size() == path->edges.size() + 1);
|
||||
memgraph::query::Path tv_path(path->vertices[0].impl, memory);
|
||||
|
||||
;
|
||||
memgraph::query::Path tv_path(path->vertices[0].getImpl(), memory);
|
||||
for (size_t i = 0; i < path->edges.size(); ++i) {
|
||||
tv_path.Expand(path->edges[i].impl);
|
||||
tv_path.Expand(path->vertices[i + 1].impl);
|
||||
tv_path.Expand(path->vertices[i + 1].getImpl());
|
||||
}
|
||||
return memgraph::query::TypedValue(std::move(tv_path));
|
||||
}
|
||||
@@ -454,12 +461,31 @@ mgp_value::mgp_value(const memgraph::query::TypedValue &tv, mgp_graph *graph, me
|
||||
}
|
||||
case MGP_VALUE_TYPE_VERTEX: {
|
||||
memgraph::utils::Allocator<mgp_vertex> allocator(m);
|
||||
vertex_v = allocator.new_object<mgp_vertex>(tv.ValueVertex(), graph);
|
||||
vertex_v = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[&](memgraph::query::DbAccessor *) { return allocator.new_object<mgp_vertex>(tv.ValueVertex(), graph); },
|
||||
[&](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
return allocator.new_object<mgp_vertex>(
|
||||
memgraph::query::SubgraphVertexAccessor(tv.ValueVertex(), impl->getGraph()), graph);
|
||||
}},
|
||||
graph->impl);
|
||||
|
||||
break;
|
||||
}
|
||||
case MGP_VALUE_TYPE_EDGE: {
|
||||
memgraph::utils::Allocator<mgp_edge> allocator(m);
|
||||
edge_v = allocator.new_object<mgp_edge>(tv.ValueEdge(), graph);
|
||||
|
||||
edge_v = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[&tv, graph, &allocator](memgraph::query::DbAccessor *) {
|
||||
return allocator.new_object<mgp_edge>(tv.ValueEdge(), graph);
|
||||
},
|
||||
[&tv, graph, &allocator](memgraph::query::SubgraphDbAccessor *db_impl) {
|
||||
return allocator.new_object<mgp_edge>(
|
||||
tv.ValueEdge(), memgraph::query::SubgraphVertexAccessor(tv.ValueEdge().From(), db_impl->getGraph()),
|
||||
memgraph::query::SubgraphVertexAccessor(tv.ValueEdge().To(), db_impl->getGraph()), graph);
|
||||
}},
|
||||
graph->impl);
|
||||
break;
|
||||
}
|
||||
case MGP_VALUE_TYPE_PATH: {
|
||||
@@ -470,11 +496,24 @@ mgp_value::mgp_value(const memgraph::query::TypedValue &tv, mgp_graph *graph, me
|
||||
mgp_path tmp_path(m);
|
||||
tmp_path.vertices.reserve(tv.ValuePath().vertices().size());
|
||||
for (const auto &v : tv.ValuePath().vertices()) {
|
||||
tmp_path.vertices.emplace_back(v, graph);
|
||||
std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[&v, graph, &tmp_path](memgraph::query::DbAccessor *) { tmp_path.vertices.emplace_back(v, graph); },
|
||||
[&v, graph, &tmp_path](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
tmp_path.vertices.emplace_back(memgraph::query::SubgraphVertexAccessor(v, impl->getGraph()), graph);
|
||||
}},
|
||||
graph->impl);
|
||||
}
|
||||
tmp_path.edges.reserve(tv.ValuePath().edges().size());
|
||||
for (const auto &e : tv.ValuePath().edges()) {
|
||||
tmp_path.edges.emplace_back(e, graph);
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[&e, graph, &tmp_path](memgraph::query::DbAccessor *) { tmp_path.edges.emplace_back(e, graph); },
|
||||
[&e, graph, &tmp_path](memgraph::query::SubgraphDbAccessor *db_impl) {
|
||||
tmp_path.edges.emplace_back(
|
||||
e, memgraph::query::SubgraphVertexAccessor(e.From(), db_impl->getGraph()),
|
||||
memgraph::query::SubgraphVertexAccessor(e.To(), db_impl->getGraph()), graph);
|
||||
}},
|
||||
graph->impl);
|
||||
}
|
||||
memgraph::utils::Allocator<mgp_path> allocator(m);
|
||||
path_v = allocator.new_object<mgp_path>(std::move(tmp_path));
|
||||
@@ -799,7 +838,15 @@ mgp_value::mgp_value(mgp_value &&other, memgraph::utils::MemoryResource *m) : ty
|
||||
mgp_value::~mgp_value() noexcept { DeleteValueMember(this); }
|
||||
|
||||
mgp_edge *mgp_edge::Copy(const mgp_edge &edge, mgp_memory &memory) {
|
||||
return NewRawMgpObject<mgp_edge>(&memory, edge.impl, edge.from.graph);
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[&](memgraph::query::DbAccessor *) { return NewRawMgpObject<mgp_edge>(&memory, edge.impl, edge.from.graph); },
|
||||
[&](memgraph::query::SubgraphDbAccessor *db_impl) {
|
||||
return NewRawMgpObject<mgp_edge>(
|
||||
&memory, edge.impl, memgraph::query::SubgraphVertexAccessor(edge.impl.From(), db_impl->getGraph()),
|
||||
memgraph::query::SubgraphVertexAccessor(edge.impl.To(), db_impl->getGraph()), edge.to.graph);
|
||||
}},
|
||||
edge.to.graph->impl);
|
||||
}
|
||||
|
||||
void mgp_value_destroy(mgp_value *val) { DeleteRawMgpObject(val); }
|
||||
@@ -1130,7 +1177,7 @@ mgp_error mgp_path_equal(mgp_path *p1, mgp_path *p2, int *result) {
|
||||
}
|
||||
const auto *start1 = Call<mgp_vertex *>(mgp_path_vertex_at, p1, 0);
|
||||
const auto *start2 = Call<mgp_vertex *>(mgp_path_vertex_at, p2, 0);
|
||||
static_assert(noexcept(start1->impl == start2->impl));
|
||||
static_assert(noexcept(start1 == start2));
|
||||
if (*start1 != *start2) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1497,9 +1544,15 @@ mgp_error mgp_properties_iterator_next(mgp_properties_iterator *it, mgp_property
|
||||
return nullptr;
|
||||
}
|
||||
memgraph::utils::OnScopeExit clean_up([it] { it->current = std::nullopt; });
|
||||
it->current.emplace(memgraph::utils::pmr::string(it->graph->impl->PropertyToName(it->current_it->first),
|
||||
it->GetMemoryResource()),
|
||||
mgp_value(it->current_it->second, it->GetMemoryResource()));
|
||||
auto propToName = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[it](auto *impl) {
|
||||
return memgraph::utils::pmr::string(impl->PropertyToName(it->current_it->first),
|
||||
it->GetMemoryResource());
|
||||
},
|
||||
},
|
||||
it->graph->impl);
|
||||
it->current.emplace(propToName, mgp_value(it->current_it->second, it->GetMemoryResource()));
|
||||
it->property.name = it->current->first.c_str();
|
||||
it->property.value = &it->current->second;
|
||||
clean_up.Disable();
|
||||
@@ -1509,7 +1562,12 @@ mgp_error mgp_properties_iterator_next(mgp_properties_iterator *it, mgp_property
|
||||
}
|
||||
|
||||
mgp_error mgp_vertex_get_id(mgp_vertex *v, mgp_vertex_id *result) {
|
||||
return WrapExceptions([v] { return mgp_vertex_id{.as_int = v->impl.Gid().AsInt()}; }, result);
|
||||
return WrapExceptions(
|
||||
[v] {
|
||||
return mgp_vertex_id{
|
||||
.as_int = std::visit(memgraph::utils::Overloaded{[](auto &impl) { return impl.Gid().AsInt(); }}, v->impl)};
|
||||
},
|
||||
result);
|
||||
}
|
||||
|
||||
mgp_error mgp_vertex_underlying_graph_is_mutable(mgp_vertex *v, int *result) {
|
||||
@@ -1581,8 +1639,15 @@ mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_nam
|
||||
if (!MgpVertexIsMutable(*v)) {
|
||||
throw ImmutableObjectException{"Cannot set a property on an immutable vertex!"};
|
||||
}
|
||||
const auto prop_key = v->graph->impl->NameToProperty(property_name);
|
||||
const auto result = v->impl.SetProperty(prop_key, ToPropertyValue(*property_value));
|
||||
|
||||
const auto prop_key = std::visit(
|
||||
memgraph::utils::Overloaded{[property_name](auto *impl) { return impl->NameToProperty(property_name); }},
|
||||
v->graph->impl);
|
||||
|
||||
const auto result = std::visit(memgraph::utils::Overloaded{[prop_key, property_value](auto &impl) {
|
||||
return impl.SetProperty(prop_key, ToPropertyValue(*property_value));
|
||||
}},
|
||||
v->impl);
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -1608,11 +1673,11 @@ mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_nam
|
||||
}
|
||||
const auto old_value = memgraph::query::TypedValue(*result);
|
||||
if (property_value->type == mgp_value_type::MGP_VALUE_TYPE_NULL) {
|
||||
trigger_ctx_collector->RegisterRemovedObjectProperty(v->impl, prop_key, old_value);
|
||||
trigger_ctx_collector->RegisterRemovedObjectProperty(v->getImpl(), prop_key, old_value);
|
||||
return;
|
||||
}
|
||||
const auto new_value = ToTypedValue(*property_value, property_value->memory);
|
||||
trigger_ctx_collector->RegisterSetObjectProperty(v->impl, prop_key, old_value, new_value);
|
||||
trigger_ctx_collector->RegisterSetObjectProperty(v->getImpl(), prop_key, old_value, new_value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1621,8 +1686,18 @@ mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) {
|
||||
if (!MgpVertexIsMutable(*v)) {
|
||||
throw ImmutableObjectException{"Cannot add a label to an immutable vertex!"};
|
||||
}
|
||||
const auto label_id = v->graph->impl->NameToLabel(label.name);
|
||||
const auto result = v->impl.AddLabel(label_id);
|
||||
const auto label_id = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[label](auto *impl) { return impl->NameToLabel(label.name); },
|
||||
},
|
||||
v->graph->impl);
|
||||
|
||||
const auto result = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[label_id](auto &impl) { return impl.AddLabel(label_id); },
|
||||
|
||||
},
|
||||
v->impl);
|
||||
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
@@ -1643,7 +1718,7 @@ mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) {
|
||||
ctx->execution_stats[memgraph::query::ExecutionStats::Key::CREATED_LABELS] += 1;
|
||||
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterSetVertexLabel(v->impl, label_id);
|
||||
ctx->trigger_context_collector->RegisterSetVertexLabel(v->getImpl(), label_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1653,8 +1728,17 @@ mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, mgp_label label) {
|
||||
if (!MgpVertexIsMutable(*v)) {
|
||||
throw ImmutableObjectException{"Cannot remove a label from an immutable vertex!"};
|
||||
}
|
||||
const auto label_id = v->graph->impl->NameToLabel(label.name);
|
||||
const auto result = v->impl.RemoveLabel(label_id);
|
||||
const auto label_id = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[label](auto *impl) { return impl->NameToLabel(label.name); },
|
||||
},
|
||||
v->graph->impl);
|
||||
const auto result = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[label_id](auto &impl) { return impl.RemoveLabel(label_id); },
|
||||
|
||||
},
|
||||
v->impl);
|
||||
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
@@ -1675,7 +1759,7 @@ mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, mgp_label label) {
|
||||
ctx->execution_stats[memgraph::query::ExecutionStats::Key::DELETED_LABELS] += 1;
|
||||
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterRemovedVertexLabel(v->impl, label_id);
|
||||
ctx->trigger_context_collector->RegisterRemovedVertexLabel(v->getImpl(), label_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1696,7 +1780,8 @@ mgp_error mgp_vertex_equal(mgp_vertex *v1, mgp_vertex *v2, int *result) {
|
||||
mgp_error mgp_vertex_labels_count(mgp_vertex *v, size_t *result) {
|
||||
return WrapExceptions(
|
||||
[v]() -> size_t {
|
||||
auto maybe_labels = v->impl.Labels(v->graph->view);
|
||||
auto maybe_labels = std::visit(
|
||||
memgraph::utils::Overloaded{[v](const auto &impl) { return impl.Labels(v->graph->view); }}, v->impl);
|
||||
if (maybe_labels.HasError()) {
|
||||
switch (maybe_labels.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -1718,7 +1803,11 @@ mgp_error mgp_vertex_label_at(mgp_vertex *v, size_t i, mgp_label *result) {
|
||||
return WrapExceptions(
|
||||
[v, i]() -> const char * {
|
||||
// TODO: Maybe it's worth caching this in mgp_vertex.
|
||||
auto maybe_labels = v->impl.Labels(v->graph->view);
|
||||
auto maybe_labels = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[v](const auto &impl) { return impl.Labels(v->graph->view); },
|
||||
},
|
||||
v->impl);
|
||||
if (maybe_labels.HasError()) {
|
||||
switch (maybe_labels.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -1735,10 +1824,16 @@ mgp_error mgp_vertex_label_at(mgp_vertex *v, size_t i, mgp_label *result) {
|
||||
throw std::out_of_range("Label cannot be retrieved, because index exceeds the number of labels!");
|
||||
}
|
||||
const auto &label = (*maybe_labels)[i];
|
||||
static_assert(std::is_lvalue_reference_v<decltype(v->graph->impl->LabelToName(label))>,
|
||||
static_assert(std::is_lvalue_reference_v<
|
||||
decltype(std::get<memgraph::query::DbAccessor *>(v->graph->impl)->LabelToName(label))>,
|
||||
"Expected LabelToName to return a pointer or reference, so we "
|
||||
"don't have to take a copy and manage memory.");
|
||||
const auto &name = v->graph->impl->LabelToName(label);
|
||||
|
||||
const auto &name = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[label](const auto *impl) { return impl->LabelToName(label); },
|
||||
},
|
||||
v->graph->impl);
|
||||
return name.c_str();
|
||||
},
|
||||
&result->name);
|
||||
@@ -1748,9 +1843,17 @@ mgp_error mgp_vertex_has_label_named(mgp_vertex *v, const char *name, int *resul
|
||||
return WrapExceptions(
|
||||
[v, name] {
|
||||
memgraph::storage::LabelId label;
|
||||
label = v->graph->impl->NameToLabel(name);
|
||||
label = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[name](auto *impl) { return impl->NameToLabel(name); },
|
||||
},
|
||||
v->graph->impl);
|
||||
|
||||
auto maybe_has_label = v->impl.HasLabel(v->graph->view, label);
|
||||
auto maybe_has_label = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[v, label](auto &impl) { return impl.HasLabel(v->graph->view, label); },
|
||||
},
|
||||
v->impl);
|
||||
if (maybe_has_label.HasError()) {
|
||||
switch (maybe_has_label.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -1778,8 +1881,17 @@ mgp_error mgp_vertex_has_label(mgp_vertex *v, mgp_label label, int *result) {
|
||||
mgp_error mgp_vertex_get_property(mgp_vertex *v, const char *name, mgp_memory *memory, mgp_value **result) {
|
||||
return WrapExceptions(
|
||||
[v, name, memory]() -> mgp_value * {
|
||||
const auto &key = v->graph->impl->NameToProperty(name);
|
||||
auto maybe_prop = v->impl.GetProperty(v->graph->view, key);
|
||||
const auto &key = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[name](auto *impl) { return impl->NameToProperty(name); },
|
||||
},
|
||||
v->graph->impl);
|
||||
|
||||
auto maybe_prop = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[v, key](auto &impl) { return impl.GetProperty(v->graph->view, key); },
|
||||
},
|
||||
v->impl);
|
||||
if (maybe_prop.HasError()) {
|
||||
switch (maybe_prop.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -1805,7 +1917,11 @@ mgp_error mgp_vertex_iter_properties(mgp_vertex *v, mgp_memory *memory, mgp_prop
|
||||
// will probably require a different API in storage.
|
||||
return WrapExceptions(
|
||||
[v, memory] {
|
||||
auto maybe_props = v->impl.Properties(v->graph->view);
|
||||
auto maybe_props = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[v](auto &impl) { return impl.Properties(v->graph->view); },
|
||||
},
|
||||
v->impl);
|
||||
if (maybe_props.HasError()) {
|
||||
switch (maybe_props.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -1833,7 +1949,11 @@ mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_
|
||||
auto it = NewMgpObject<mgp_edges_iterator>(memory, *v);
|
||||
MG_ASSERT(it != nullptr);
|
||||
|
||||
auto maybe_edges = v->impl.InEdges(v->graph->view);
|
||||
auto maybe_edges = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[v](auto &impl) { return impl.InEdges(v->graph->view); },
|
||||
},
|
||||
v->impl);
|
||||
if (maybe_edges.HasError()) {
|
||||
switch (maybe_edges.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -1851,7 +1971,19 @@ mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_
|
||||
it->in.emplace(std::move(*maybe_edges));
|
||||
it->in_it.emplace(it->in->begin());
|
||||
if (*it->in_it != it->in->end()) {
|
||||
it->current_e.emplace(**it->in_it, v->graph, it->GetMemoryResource());
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[&](memgraph::query::DbAccessor *) {
|
||||
it->current_e.emplace(**it->in_it, (**it->in_it).From(), (**it->in_it).To(), v->graph,
|
||||
it->GetMemoryResource());
|
||||
},
|
||||
[&](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
it->current_e.emplace(
|
||||
**it->in_it,
|
||||
memgraph::query::SubgraphVertexAccessor((**it->in_it).From(), impl->getGraph()),
|
||||
memgraph::query::SubgraphVertexAccessor((**it->in_it).To(), impl->getGraph()), v->graph,
|
||||
it->GetMemoryResource());
|
||||
}},
|
||||
v->graph->impl);
|
||||
}
|
||||
|
||||
return it.release();
|
||||
@@ -1864,8 +1996,12 @@ mgp_error mgp_vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges
|
||||
[v, memory] {
|
||||
auto it = NewMgpObject<mgp_edges_iterator>(memory, *v);
|
||||
MG_ASSERT(it != nullptr);
|
||||
auto maybe_edges = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[v](auto &impl) { return impl.OutEdges(v->graph->view); },
|
||||
},
|
||||
v->impl);
|
||||
|
||||
auto maybe_edges = v->impl.OutEdges(v->graph->view);
|
||||
if (maybe_edges.HasError()) {
|
||||
switch (maybe_edges.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -1880,10 +2016,23 @@ mgp_error mgp_vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges
|
||||
LOG_FATAL("Unexpected error when getting the outbound edges of a vertex.");
|
||||
}
|
||||
}
|
||||
|
||||
it->out.emplace(std::move(*maybe_edges));
|
||||
it->out_it.emplace(it->out->begin());
|
||||
if (*it->out_it != it->out->end()) {
|
||||
it->current_e.emplace(**it->out_it, v->graph, it->GetMemoryResource());
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[&](memgraph::query::DbAccessor *) {
|
||||
it->current_e.emplace(**it->out_it, (**it->out_it).From(), (**it->out_it).To(), v->graph,
|
||||
it->GetMemoryResource());
|
||||
},
|
||||
[&](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
it->current_e.emplace(
|
||||
**it->out_it,
|
||||
memgraph::query::SubgraphVertexAccessor((**it->out_it).From(), impl->getGraph()),
|
||||
memgraph::query::SubgraphVertexAccessor((**it->out_it).To(), impl->getGraph()), v->graph,
|
||||
it->GetMemoryResource());
|
||||
}},
|
||||
v->graph->impl);
|
||||
}
|
||||
|
||||
return it.release();
|
||||
@@ -1921,7 +2070,19 @@ mgp_error mgp_edges_iterator_next(mgp_edges_iterator *it, mgp_edge **result) {
|
||||
it->current_e = std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
it->current_e.emplace(**impl_it, it->source_vertex.graph, it->GetMemoryResource());
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[&](memgraph::query::DbAccessor *) {
|
||||
it->current_e.emplace(**impl_it, (**impl_it).From(), (**impl_it).To(),
|
||||
it->source_vertex.graph, it->GetMemoryResource());
|
||||
},
|
||||
[&](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
it->current_e.emplace(
|
||||
**impl_it, memgraph::query::SubgraphVertexAccessor((**impl_it).From(), impl->getGraph()),
|
||||
memgraph::query::SubgraphVertexAccessor((**impl_it).To(), impl->getGraph()),
|
||||
it->source_vertex.graph, it->GetMemoryResource());
|
||||
}},
|
||||
it->source_vertex.graph->impl);
|
||||
|
||||
return &*it->current_e;
|
||||
};
|
||||
if (it->in_it) {
|
||||
@@ -1956,10 +2117,14 @@ mgp_error mgp_edge_equal(mgp_edge *e1, mgp_edge *e2, int *result) {
|
||||
mgp_error mgp_edge_get_type(mgp_edge *e, mgp_edge_type *result) {
|
||||
return WrapExceptions(
|
||||
[e] {
|
||||
const auto &name = e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType());
|
||||
static_assert(std::is_lvalue_reference_v<decltype(e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType()))>,
|
||||
"Expected EdgeTypeToName to return a pointer or reference, so we "
|
||||
"don't have to take a copy and manage memory.");
|
||||
const auto &name = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[e](const auto *impl) { return impl->EdgeTypeToName(e->impl.EdgeType()); },
|
||||
},
|
||||
e->from.graph->impl);
|
||||
// static_assert(std::is_lvalue_reference_v<decltype(e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType()))>,
|
||||
// "Expected EdgeTypeToName to return a pointer or reference, so we "
|
||||
// "don't have to take a copy and manage memory.");
|
||||
return name.c_str();
|
||||
},
|
||||
&result->name);
|
||||
@@ -1978,7 +2143,11 @@ mgp_error mgp_edge_get_to(mgp_edge *e, mgp_vertex **result) {
|
||||
mgp_error mgp_edge_get_property(mgp_edge *e, const char *name, mgp_memory *memory, mgp_value **result) {
|
||||
return WrapExceptions(
|
||||
[e, name, memory] {
|
||||
const auto &key = e->from.graph->impl->NameToProperty(name);
|
||||
const auto &key = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[name](auto *impl) { return impl->NameToProperty(name); },
|
||||
},
|
||||
e->from.graph->impl);
|
||||
auto view = e->from.graph->view;
|
||||
auto maybe_prop = e->impl.GetProperty(view, key);
|
||||
if (maybe_prop.HasError()) {
|
||||
@@ -2004,7 +2173,11 @@ mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, m
|
||||
if (!MgpEdgeIsMutable(*e)) {
|
||||
throw ImmutableObjectException{"Cannot set a property on an immutable edge!"};
|
||||
}
|
||||
const auto prop_key = e->from.graph->impl->NameToProperty(property_name);
|
||||
const auto prop_key = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[property_name](auto *impl) { return impl->NameToProperty(property_name); },
|
||||
},
|
||||
e->from.graph->impl);
|
||||
const auto result = e->impl.SetProperty(prop_key, ToPropertyValue(*property_value));
|
||||
|
||||
if (result.HasError()) {
|
||||
@@ -2071,9 +2244,24 @@ mgp_error mgp_edge_iter_properties(mgp_edge *e, mgp_memory *memory, mgp_properti
|
||||
mgp_error mgp_graph_get_vertex_by_id(mgp_graph *graph, mgp_vertex_id id, mgp_memory *memory, mgp_vertex **result) {
|
||||
return WrapExceptions(
|
||||
[graph, id, memory]() -> mgp_vertex * {
|
||||
auto maybe_vertex = graph->impl->FindVertex(memgraph::storage::Gid::FromInt(id.as_int), graph->view);
|
||||
std::optional<memgraph::query::VertexAccessor> maybe_vertex = std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[graph, id](auto *impl) {
|
||||
return impl->FindVertex(memgraph::storage::Gid::FromInt(id.as_int), graph->view);
|
||||
},
|
||||
},
|
||||
graph->impl);
|
||||
if (maybe_vertex) {
|
||||
return NewRawMgpObject<mgp_vertex>(memory, *maybe_vertex, graph);
|
||||
return std::visit(memgraph::utils::Overloaded{
|
||||
[memory, graph, maybe_vertex](memgraph::query::DbAccessor *) {
|
||||
return NewRawMgpObject<mgp_vertex>(memory, *maybe_vertex, graph);
|
||||
},
|
||||
[memory, graph, maybe_vertex](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
return NewRawMgpObject<mgp_vertex>(
|
||||
memory, memgraph::query::SubgraphVertexAccessor(*maybe_vertex, impl->getGraph()),
|
||||
graph);
|
||||
}},
|
||||
graph->impl);
|
||||
}
|
||||
return nullptr;
|
||||
},
|
||||
@@ -2091,15 +2279,18 @@ mgp_error mgp_graph_create_vertex(struct mgp_graph *graph, mgp_memory *memory, m
|
||||
if (!MgpGraphIsMutable(*graph)) {
|
||||
throw ImmutableObjectException{"Cannot create a vertex in an immutable graph!"};
|
||||
}
|
||||
auto vertex = graph->impl->InsertVertex();
|
||||
auto *vertex = std::visit(memgraph::utils::Overloaded{[=](auto *impl) {
|
||||
return NewRawMgpObject<mgp_vertex>(memory, impl->InsertVertex(), graph);
|
||||
}},
|
||||
graph->impl);
|
||||
|
||||
auto &ctx = graph->ctx;
|
||||
ctx->execution_stats[memgraph::query::ExecutionStats::Key::CREATED_NODES] += 1;
|
||||
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterCreatedObject(vertex);
|
||||
ctx->trigger_context_collector->RegisterCreatedObject(vertex->getImpl());
|
||||
}
|
||||
return NewRawMgpObject<mgp_vertex>(memory, vertex, graph);
|
||||
return vertex;
|
||||
},
|
||||
result);
|
||||
}
|
||||
@@ -2109,7 +2300,24 @@ mgp_error mgp_graph_delete_vertex(struct mgp_graph *graph, mgp_vertex *vertex) {
|
||||
if (!MgpGraphIsMutable(*graph)) {
|
||||
throw ImmutableObjectException{"Cannot remove a vertex from an immutable graph!"};
|
||||
}
|
||||
const auto result = graph->impl->RemoveVertex(&vertex->impl);
|
||||
|
||||
const auto result =
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[&](memgraph::query::DbAccessor *impl) {
|
||||
if (std::holds_alternative<memgraph::query::SubgraphVertexAccessor>(vertex->impl)) {
|
||||
throw std::logic_error{
|
||||
"Remove vertex for DbAccessor should not get reference to SubgraphVertexAccessor"};
|
||||
}
|
||||
return impl->RemoveVertex(&std::get<memgraph::query::VertexAccessor>(vertex->impl));
|
||||
},
|
||||
[&](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
if (std::holds_alternative<memgraph::query::VertexAccessor>(vertex->impl)) {
|
||||
throw std::logic_error{
|
||||
"Remove vertex for SubgraphDbAccessor should not get reference to VertexAccessor"};
|
||||
}
|
||||
return impl->RemoveVertex(&(std::get<memgraph::query::SubgraphVertexAccessor>(vertex->impl)));
|
||||
}},
|
||||
graph->impl);
|
||||
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
@@ -2144,7 +2352,23 @@ mgp_error mgp_graph_detach_delete_vertex(struct mgp_graph *graph, mgp_vertex *ve
|
||||
if (!MgpGraphIsMutable(*graph)) {
|
||||
throw ImmutableObjectException{"Cannot remove a vertex from an immutable graph!"};
|
||||
}
|
||||
const auto result = graph->impl->DetachRemoveVertex(&vertex->impl);
|
||||
const auto result =
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[vertex](memgraph::query::DbAccessor *impl) {
|
||||
if (std::holds_alternative<memgraph::query::SubgraphVertexAccessor>(vertex->impl)) {
|
||||
throw std::logic_error{"Wrong type"};
|
||||
}
|
||||
return impl->DetachRemoveVertex(&std::get<memgraph::query::VertexAccessor>(vertex->impl));
|
||||
},
|
||||
[vertex](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
// todo antoniofilipovic change this, it is wrong here since it needs to be
|
||||
// SubgraphVertexAccessor
|
||||
if (std::holds_alternative<memgraph::query::SubgraphVertexAccessor>(vertex->impl)) {
|
||||
throw std::logic_error{"Wrong type"};
|
||||
}
|
||||
return impl->DetachRemoveVertex(&std::get<memgraph::query::VertexAccessor>(vertex->impl));
|
||||
}},
|
||||
graph->impl);
|
||||
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
@@ -2191,8 +2415,28 @@ mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *
|
||||
if (!MgpGraphIsMutable(*graph)) {
|
||||
throw ImmutableObjectException{"Cannot create an edge in an immutable graph!"};
|
||||
}
|
||||
auto edge =
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[from, to, type](memgraph::query::DbAccessor *impl) {
|
||||
if (std::holds_alternative<memgraph::query::SubgraphVertexAccessor>(from->impl) ||
|
||||
std::holds_alternative<memgraph::query::SubgraphVertexAccessor>(to->impl)) {
|
||||
throw std::logic_error{"Both vertices must be of VertexAccessor type"};
|
||||
}
|
||||
return impl->InsertEdge(&std::get<memgraph::query::VertexAccessor>(from->impl),
|
||||
&std::get<memgraph::query::VertexAccessor>(to->impl),
|
||||
impl->NameToEdgeType(type.name));
|
||||
},
|
||||
[from, to, type](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
if (std::holds_alternative<memgraph::query::VertexAccessor>(from->impl) ||
|
||||
std::holds_alternative<memgraph::query::VertexAccessor>(to->impl)) {
|
||||
throw std::logic_error{"Both vertices must be of SubgraphVertexAccessor type"};
|
||||
}
|
||||
return impl->InsertEdge(&std::get<memgraph::query::SubgraphVertexAccessor>(from->impl),
|
||||
&std::get<memgraph::query::SubgraphVertexAccessor>(to->impl),
|
||||
impl->NameToEdgeType(type.name));
|
||||
}},
|
||||
graph->impl);
|
||||
|
||||
auto edge = graph->impl->InsertEdge(&from->impl, &to->impl, from->graph->impl->NameToEdgeType(type.name));
|
||||
if (edge.HasError()) {
|
||||
switch (edge.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -2213,7 +2457,18 @@ mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterCreatedObject(*edge);
|
||||
}
|
||||
return NewRawMgpObject<mgp_edge>(memory, edge.GetValue(), from->graph);
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[memory, edge, from](memgraph::query::DbAccessor *) {
|
||||
return NewRawMgpObject<mgp_edge>(memory->impl, edge.GetValue(), from->graph);
|
||||
},
|
||||
[memory, edge, from](memgraph::query::SubgraphDbAccessor *db_impl) {
|
||||
const auto &v_from =
|
||||
memgraph::query::SubgraphVertexAccessor(edge.GetValue().From(), db_impl->getGraph());
|
||||
const auto &v_to = memgraph::query::SubgraphVertexAccessor(edge.GetValue().To(), db_impl->getGraph());
|
||||
return NewRawMgpObject<mgp_edge>(memory->impl, edge.GetValue(), v_from, v_to, from->graph);
|
||||
}},
|
||||
graph->impl);
|
||||
},
|
||||
result);
|
||||
}
|
||||
@@ -2223,8 +2478,9 @@ mgp_error mgp_graph_delete_edge(struct mgp_graph *graph, mgp_edge *edge) {
|
||||
if (!MgpGraphIsMutable(*graph)) {
|
||||
throw ImmutableObjectException{"Cannot remove an edge from an immutable graph!"};
|
||||
}
|
||||
const auto result = graph->impl->RemoveEdge(&edge->impl);
|
||||
|
||||
const auto result = std::visit(
|
||||
memgraph::utils::Overloaded{[edge](auto *impl) { return impl->RemoveEdge(&edge->impl); }}, graph->impl);
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
case memgraph::storage::Error::NONEXISTENT_OBJECT:
|
||||
@@ -2285,7 +2541,18 @@ mgp_error mgp_vertices_iterator_next(mgp_vertices_iterator *it, mgp_vertex **res
|
||||
return nullptr;
|
||||
}
|
||||
memgraph::utils::OnScopeExit clean_up([it] { it->current_v = std::nullopt; });
|
||||
it->current_v.emplace(*it->current_it, it->graph, it->GetMemoryResource());
|
||||
std::visit(memgraph::utils::Overloaded{[it](memgraph::query::DbAccessor *) {
|
||||
it->current_v.emplace(*it->current_it, it->graph,
|
||||
it->GetMemoryResource());
|
||||
;
|
||||
},
|
||||
[it](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
it->current_v.emplace(memgraph::query::SubgraphVertexAccessor(
|
||||
*it->current_it, impl->getGraph()),
|
||||
it->graph, it->GetMemoryResource());
|
||||
}},
|
||||
it->graph->impl);
|
||||
|
||||
clean_up.Disable();
|
||||
return &*it->current_v;
|
||||
},
|
||||
@@ -2568,6 +2835,7 @@ std::ostream &PrintValue(const TypedValue &value, std::ostream *stream) {
|
||||
case TypedValue::Type::Vertex:
|
||||
case TypedValue::Type::Edge:
|
||||
case TypedValue::Type::Path:
|
||||
case TypedValue::Type::Graph:
|
||||
LOG_FATAL("value must not be a graph element");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "utils/pmr/string.hpp"
|
||||
#include "utils/pmr/vector.hpp"
|
||||
#include "utils/temporal.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
/// Wraps memory resource used in custom procedures.
|
||||
///
|
||||
/// This should have been `using mgp_memory = memgraph::utils::MemoryResource`, but that's
|
||||
@@ -442,21 +443,69 @@ struct mgp_vertex {
|
||||
mgp_vertex(memgraph::query::VertexAccessor v, mgp_graph *graph, memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), impl(v), graph(graph) {}
|
||||
|
||||
mgp_vertex(memgraph::query::SubgraphVertexAccessor v, mgp_graph *graph,
|
||||
memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), impl(v), graph(graph) {}
|
||||
|
||||
mgp_vertex(const mgp_vertex &other, memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), impl(other.impl), graph(other.graph) {}
|
||||
: memory(memory), impl(other.impl), graph(other.graph) {
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; },
|
||||
[](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }},
|
||||
other.impl);
|
||||
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; },
|
||||
[](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }},
|
||||
this->impl);
|
||||
}
|
||||
|
||||
mgp_vertex(mgp_vertex &&other, memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), impl(other.impl), graph(other.graph) {}
|
||||
: memory(memory), impl(other.impl), graph(other.graph) {
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; },
|
||||
[](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }},
|
||||
other.impl);
|
||||
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; },
|
||||
[](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }},
|
||||
this->impl);
|
||||
}
|
||||
|
||||
mgp_vertex(mgp_vertex &&other) noexcept : memory(other.memory), impl(other.impl), graph(other.graph) {}
|
||||
|
||||
memgraph::query::VertexAccessor getImpl() const {
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{[](memgraph::query::VertexAccessor impl) { return impl; },
|
||||
[](memgraph::query::SubgraphVertexAccessor impl) { return impl.impl_; }},
|
||||
this->impl);
|
||||
}
|
||||
|
||||
/// Copy construction without memgraph::utils::MemoryResource is not allowed.
|
||||
mgp_vertex(const mgp_vertex &) = delete;
|
||||
|
||||
mgp_vertex &operator=(const mgp_vertex &) = delete;
|
||||
mgp_vertex &operator=(mgp_vertex &&) = delete;
|
||||
|
||||
bool operator==(const mgp_vertex &other) const noexcept { return this->impl == other.impl; }
|
||||
bool operator==(const mgp_vertex &other) const noexcept {
|
||||
return std::visit(memgraph::utils::Overloaded{
|
||||
[&other](memgraph::query::VertexAccessor impl) {
|
||||
if (std::holds_alternative<memgraph::query::SubgraphVertexAccessor>(other.impl)) {
|
||||
return false;
|
||||
}
|
||||
return impl == std::get<memgraph::query::VertexAccessor>(other.impl);
|
||||
},
|
||||
[&other](memgraph::query::SubgraphVertexAccessor impl) -> bool {
|
||||
if (std::holds_alternative<memgraph::query::VertexAccessor>(other.impl)) {
|
||||
return false;
|
||||
}
|
||||
return impl == std::get<memgraph::query::SubgraphVertexAccessor>(other.impl);
|
||||
},
|
||||
},
|
||||
this->impl);
|
||||
}
|
||||
|
||||
bool operator!=(const mgp_vertex &other) const noexcept { return !(*this == other); };
|
||||
|
||||
~mgp_vertex() = default;
|
||||
@@ -464,10 +513,38 @@ struct mgp_vertex {
|
||||
memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
|
||||
|
||||
memgraph::utils::MemoryResource *memory;
|
||||
memgraph::query::VertexAccessor impl;
|
||||
std::variant<memgraph::query::VertexAccessor, memgraph::query::SubgraphVertexAccessor> impl;
|
||||
mgp_graph *graph;
|
||||
};
|
||||
|
||||
struct mgp_graph {
|
||||
std::variant<memgraph::query::DbAccessor *, memgraph::query::SubgraphDbAccessor *> impl;
|
||||
memgraph::storage::View view;
|
||||
// TODO: Merge `mgp_graph` and `mgp_memory` into a single `mgp_context`. The
|
||||
// `ctx` field is out of place here.
|
||||
memgraph::query::ExecutionContext *ctx;
|
||||
|
||||
// memgraph:::query::Graph *subraph;
|
||||
|
||||
static mgp_graph WritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view,
|
||||
memgraph::query::ExecutionContext &ctx) {
|
||||
return mgp_graph{&acc, view, &ctx};
|
||||
}
|
||||
|
||||
static mgp_graph NonWritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view) {
|
||||
return mgp_graph{&acc, view, nullptr};
|
||||
}
|
||||
|
||||
static mgp_graph WritableGraph(memgraph::query::SubgraphDbAccessor &acc, memgraph::storage::View view,
|
||||
memgraph::query::ExecutionContext &ctx) {
|
||||
return mgp_graph{&acc, view, &ctx};
|
||||
}
|
||||
|
||||
static mgp_graph NonWritableGraph(memgraph::query::SubgraphDbAccessor &acc, memgraph::storage::View view) {
|
||||
return mgp_graph{&acc, view, nullptr};
|
||||
}
|
||||
};
|
||||
|
||||
struct mgp_edge {
|
||||
/// Allocator type so that STL containers are aware that we need one.
|
||||
/// We don't actually need this, but it simplifies the C API, because we store
|
||||
@@ -484,6 +561,16 @@ struct mgp_edge {
|
||||
memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), impl(impl), from(impl.From(), graph, memory), to(impl.To(), graph, memory) {}
|
||||
|
||||
mgp_edge(const memgraph::query::EdgeAccessor &impl, const memgraph::query::VertexAccessor &from_v,
|
||||
const memgraph::query::VertexAccessor &to_v, mgp_graph *graph,
|
||||
memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), impl(impl), from(from_v, graph, memory), to(to_v, graph, memory) {}
|
||||
|
||||
mgp_edge(const memgraph::query::EdgeAccessor &impl, const memgraph::query::SubgraphVertexAccessor &from_v,
|
||||
const memgraph::query::SubgraphVertexAccessor &to_v, mgp_graph *graph,
|
||||
memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), impl(impl), from(from_v, graph, memory), to(to_v, graph, memory) {}
|
||||
|
||||
mgp_edge(const mgp_edge &other, memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), impl(other.impl), from(other.from, memory), to(other.to, memory) {}
|
||||
|
||||
@@ -570,23 +657,6 @@ struct mgp_func_result {
|
||||
std::optional<memgraph::utils::pmr::string> error_msg;
|
||||
};
|
||||
|
||||
struct mgp_graph {
|
||||
memgraph::query::DbAccessor *impl;
|
||||
memgraph::storage::View view;
|
||||
// TODO: Merge `mgp_graph` and `mgp_memory` into a single `mgp_context`. The
|
||||
// `ctx` field is out of place here.
|
||||
memgraph::query::ExecutionContext *ctx;
|
||||
|
||||
static mgp_graph WritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view,
|
||||
memgraph::query::ExecutionContext &ctx) {
|
||||
return mgp_graph{&acc, view, &ctx};
|
||||
}
|
||||
|
||||
static mgp_graph NonWritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view) {
|
||||
return mgp_graph{&acc, view, nullptr};
|
||||
}
|
||||
};
|
||||
|
||||
// Prevents user to use ExecutionContext in writable callables
|
||||
struct mgp_func_context {
|
||||
memgraph::query::DbAccessor *impl;
|
||||
@@ -615,8 +685,18 @@ struct mgp_properties_iterator {
|
||||
mgp_properties_iterator(mgp_graph *graph, decltype(pvs) pvs, memgraph::utils::MemoryResource *memory)
|
||||
: memory(memory), graph(graph), pvs(std::move(pvs)), current_it(this->pvs.begin()) {
|
||||
if (current_it != this->pvs.end()) {
|
||||
current.emplace(memgraph::utils::pmr::string(graph->impl->PropertyToName(current_it->first), memory),
|
||||
mgp_value(current_it->second, memory));
|
||||
auto value =
|
||||
std::visit(memgraph::utils::Overloaded{
|
||||
[this, memory](const memgraph::query::DbAccessor *impl) {
|
||||
return memgraph::utils::pmr::string(impl->PropertyToName(current_it->first), memory);
|
||||
},
|
||||
[this, memory](const memgraph::query::SubgraphDbAccessor *impl) {
|
||||
return memgraph::utils::pmr::string(impl->PropertyToName(current_it->first), memory);
|
||||
},
|
||||
},
|
||||
graph->impl);
|
||||
|
||||
current.emplace(value, mgp_value(current_it->second, memory));
|
||||
property.name = current->first.c_str();
|
||||
property.value = ¤t->second;
|
||||
}
|
||||
@@ -635,7 +715,6 @@ struct mgp_properties_iterator {
|
||||
|
||||
struct mgp_edges_iterator {
|
||||
using allocator_type = memgraph::utils::Allocator<mgp_edges_iterator>;
|
||||
|
||||
// Hopefully mgp_vertex copy constructor remains noexcept, so that we can
|
||||
// have everything noexcept here.
|
||||
static_assert(std::is_nothrow_constructible_v<mgp_vertex, const mgp_vertex &, memgraph::utils::MemoryResource *>);
|
||||
@@ -662,9 +741,14 @@ struct mgp_edges_iterator {
|
||||
|
||||
memgraph::utils::MemoryResource *memory;
|
||||
mgp_vertex source_vertex;
|
||||
std::optional<std::remove_reference_t<decltype(*source_vertex.impl.InEdges(source_vertex.graph->view))>> in;
|
||||
|
||||
std::optional<std::remove_reference_t<
|
||||
decltype(*std::get<memgraph::query::VertexAccessor>(source_vertex.impl).InEdges(source_vertex.graph->view))>>
|
||||
in;
|
||||
std::optional<decltype(in->begin())> in_it;
|
||||
std::optional<std::remove_reference_t<decltype(*source_vertex.impl.OutEdges(source_vertex.graph->view))>> out;
|
||||
std::optional<std::remove_reference_t<
|
||||
decltype(*std::get<memgraph::query::VertexAccessor>(source_vertex.impl).InEdges(source_vertex.graph->view))>>
|
||||
out;
|
||||
std::optional<decltype(out->begin())> out_it;
|
||||
std::optional<mgp_edge> current_e;
|
||||
};
|
||||
@@ -674,9 +758,20 @@ struct mgp_vertices_iterator {
|
||||
|
||||
/// @throw anything VerticesIterable may throw
|
||||
mgp_vertices_iterator(mgp_graph *graph, memgraph::utils::MemoryResource *memory)
|
||||
: memory(memory), graph(graph), vertices(graph->impl->Vertices(graph->view)), current_it(vertices.begin()) {
|
||||
: memory(memory),
|
||||
graph(graph),
|
||||
vertices(std::visit(memgraph::utils::Overloaded{[graph](auto *impl) { return impl->Vertices(graph->view); }},
|
||||
graph->impl)),
|
||||
current_it(vertices.begin()) {
|
||||
if (current_it != vertices.end()) {
|
||||
current_v.emplace(*current_it, graph, memory);
|
||||
std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[this, graph, memory](memgraph::query::DbAccessor *) { current_v.emplace(*current_it, graph, memory); },
|
||||
[this, graph, memory](memgraph::query::SubgraphDbAccessor *impl) {
|
||||
current_v.emplace(memgraph::query::SubgraphVertexAccessor(*current_it, impl->getGraph()), graph,
|
||||
memory);
|
||||
}},
|
||||
graph->impl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,7 +779,7 @@ struct mgp_vertices_iterator {
|
||||
|
||||
memgraph::utils::MemoryResource *memory;
|
||||
mgp_graph *graph;
|
||||
decltype(graph->impl->Vertices(graph->view)) vertices;
|
||||
memgraph::query::VerticesIterable vertices;
|
||||
decltype(vertices.begin()) current_it;
|
||||
std::optional<mgp_vertex> current_v;
|
||||
};
|
||||
|
||||
@@ -1054,7 +1054,7 @@ std::unique_ptr<Module> LoadModuleFromFile(const std::filesystem::path &path) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ModuleRegistry::RegisterModule(const std::string_view &name, std::unique_ptr<Module> module) {
|
||||
bool ModuleRegistry::RegisterModule(const std::string_view name, std::unique_ptr<Module> module) {
|
||||
MG_ASSERT(!name.empty(), "Module name cannot be empty");
|
||||
MG_ASSERT(module, "Tried to register an invalid module");
|
||||
if (modules_.find(name) != modules_.end()) {
|
||||
@@ -1163,7 +1163,7 @@ void ModuleRegistry::UnloadAndLoadModulesFromDirectories() {
|
||||
}
|
||||
}
|
||||
|
||||
ModulePtr ModuleRegistry::GetModuleNamed(const std::string_view &name) const {
|
||||
ModulePtr ModuleRegistry::GetModuleNamed(const std::string_view name) const {
|
||||
std::shared_lock<utils::RWLock> guard(lock_);
|
||||
auto found_it = modules_.find(name);
|
||||
if (found_it == modules_.end()) return nullptr;
|
||||
|
||||
@@ -77,7 +77,7 @@ class ModuleRegistry final {
|
||||
mutable utils::RWLock lock_{utils::RWLock::Priority::WRITE};
|
||||
std::unique_ptr<utils::MemoryResource> shared_{std::make_unique<utils::ResourceWithOutOfMemoryException>()};
|
||||
|
||||
bool RegisterModule(const std::string_view &name, std::unique_ptr<Module> module);
|
||||
bool RegisterModule(std::string_view name, std::unique_ptr<Module> module);
|
||||
|
||||
void DoUnloadAllModules();
|
||||
|
||||
@@ -105,7 +105,7 @@ class ModuleRegistry final {
|
||||
///
|
||||
/// Return true if the module was loaded or reloaded successfully, false
|
||||
/// otherwise.
|
||||
bool LoadOrReloadModuleFromName(const std::string_view name);
|
||||
bool LoadOrReloadModuleFromName(std::string_view name);
|
||||
|
||||
/// Atomically unload all modules and then load all possible modules from the
|
||||
/// set directories.
|
||||
@@ -115,7 +115,7 @@ class ModuleRegistry final {
|
||||
|
||||
/// Find a module with given name or return nullptr.
|
||||
/// Takes a read lock.
|
||||
ModulePtr GetModuleNamed(const std::string_view &name) const;
|
||||
ModulePtr GetModuleNamed(std::string_view name) const;
|
||||
|
||||
/// Remove all loaded (non-builtin) modules.
|
||||
/// Takes a write lock.
|
||||
@@ -175,7 +175,7 @@ extern ModuleRegistry gModuleRegistry;
|
||||
/// inside this function. ModulePtr must be kept alive to make sure it won't be
|
||||
/// unloaded.
|
||||
std::optional<std::pair<procedure::ModulePtr, const mgp_proc *>> FindProcedure(
|
||||
const ModuleRegistry &module_registry, const std::string_view fully_qualified_procedure_name,
|
||||
const ModuleRegistry &module_registry, std::string_view fully_qualified_procedure_name,
|
||||
utils::MemoryResource *memory);
|
||||
|
||||
/// Return the ModulePtr and `mgp_trans *` of the found transformation after resolving
|
||||
@@ -183,7 +183,7 @@ std::optional<std::pair<procedure::ModulePtr, const mgp_proc *>> FindProcedure(
|
||||
/// inside this function. ModulePtr must be kept alive to make sure it won't be
|
||||
/// unloaded.
|
||||
std::optional<std::pair<procedure::ModulePtr, const mgp_trans *>> FindTransformation(
|
||||
const ModuleRegistry &module_registry, const std::string_view fully_qualified_transformation_name,
|
||||
const ModuleRegistry &module_registry, std::string_view fully_qualified_transformation_name,
|
||||
utils::MemoryResource *memory);
|
||||
|
||||
/// Return the ModulePtr and `mgp_func *` of the found function after resolving
|
||||
@@ -191,7 +191,7 @@ std::optional<std::pair<procedure::ModulePtr, const mgp_trans *>> FindTransforma
|
||||
/// std::nullopt is returned. `memory` is used for temporary allocations
|
||||
/// inside this function. ModulePtr must be kept alive to make sure it won't be unloaded.
|
||||
std::optional<std::pair<procedure::ModulePtr, const mgp_func *>> FindFunction(
|
||||
const ModuleRegistry &module_registry, const std::string_view fully_qualified_function_name,
|
||||
const ModuleRegistry &module_registry, std::string_view fully_qualified_function_name,
|
||||
utils::MemoryResource *memory);
|
||||
|
||||
template <typename T>
|
||||
@@ -235,6 +235,7 @@ void ConstructArguments(const std::vector<TypedValue> &args, const TCall &callab
|
||||
throw QueryRuntimeException("'{}' argument named '{}' at position {} must be of type {}.", fully_qualified_name,
|
||||
name, i, type->GetPresentableName());
|
||||
}
|
||||
// why do we move graph here?
|
||||
args_list.elems.emplace_back(std::move(arg), &graph);
|
||||
}
|
||||
// Fill missing optional arguments with their default values.
|
||||
|
||||
@@ -52,10 +52,11 @@ concept Stream = requires(TStream stream) {
|
||||
typename TStream::Message;
|
||||
TStream{std::string{""}, typename TStream::StreamInfo{}, ConsumerFunction<typename TStream::Message>{}};
|
||||
{ stream.Start() } -> std::same_as<void>;
|
||||
{ stream.StartWithLimit(uint64_t{}, std::optional<std::chrono::milliseconds>{}) } -> std::same_as<void>;
|
||||
{ stream.Stop() } -> std::same_as<void>;
|
||||
{ stream.IsRunning() } -> std::same_as<bool>;
|
||||
{
|
||||
stream.Check(std::optional<std::chrono::milliseconds>{}, std::optional<int64_t>{},
|
||||
stream.Check(std::optional<std::chrono::milliseconds>{}, std::optional<uint64_t>{},
|
||||
ConsumerFunction<typename TStream::Message>{})
|
||||
} -> std::same_as<void>;
|
||||
requires std::same_as<std::decay_t<decltype(std::declval<typename TStream::StreamInfo>().common_info)>,
|
||||
|
||||
@@ -44,10 +44,13 @@ KafkaStream::StreamInfo KafkaStream::Info(std::string transformation_name) const
|
||||
}
|
||||
|
||||
void KafkaStream::Start() { consumer_->Start(); }
|
||||
void KafkaStream::StartWithLimit(uint64_t batch_limit, std::optional<std::chrono::milliseconds> timeout) const {
|
||||
consumer_->StartWithLimit(batch_limit, timeout);
|
||||
}
|
||||
void KafkaStream::Stop() { consumer_->Stop(); }
|
||||
bool KafkaStream::IsRunning() const { return consumer_->IsRunning(); }
|
||||
|
||||
void KafkaStream::Check(std::optional<std::chrono::milliseconds> timeout, std::optional<int64_t> batch_limit,
|
||||
void KafkaStream::Check(std::optional<std::chrono::milliseconds> timeout, std::optional<uint64_t> batch_limit,
|
||||
const ConsumerFunction<integrations::kafka::Message> &consumer_function) const {
|
||||
consumer_->Check(timeout, batch_limit, consumer_function);
|
||||
}
|
||||
@@ -106,10 +109,12 @@ PulsarStream::StreamInfo PulsarStream::Info(std::string transformation_name) con
|
||||
}
|
||||
|
||||
void PulsarStream::Start() { consumer_->Start(); }
|
||||
void PulsarStream::StartWithLimit(uint64_t batch_limit, std::optional<std::chrono::milliseconds> timeout) const {
|
||||
consumer_->StartWithLimit(batch_limit, timeout);
|
||||
}
|
||||
void PulsarStream::Stop() { consumer_->Stop(); }
|
||||
bool PulsarStream::IsRunning() const { return consumer_->IsRunning(); }
|
||||
|
||||
void PulsarStream::Check(std::optional<std::chrono::milliseconds> timeout, std::optional<int64_t> batch_limit,
|
||||
void PulsarStream::Check(std::optional<std::chrono::milliseconds> timeout, std::optional<uint64_t> batch_limit,
|
||||
const ConsumerFunction<Message> &consumer_function) const {
|
||||
consumer_->Check(timeout, batch_limit, consumer_function);
|
||||
}
|
||||
|
||||
@@ -36,10 +36,11 @@ struct KafkaStream {
|
||||
StreamInfo Info(std::string transformation_name) const;
|
||||
|
||||
void Start();
|
||||
void StartWithLimit(uint64_t batch_limit, std::optional<std::chrono::milliseconds> timeout) const;
|
||||
void Stop();
|
||||
bool IsRunning() const;
|
||||
|
||||
void Check(std::optional<std::chrono::milliseconds> timeout, std::optional<int64_t> batch_limit,
|
||||
void Check(std::optional<std::chrono::milliseconds> timeout, std::optional<uint64_t> batch_limit,
|
||||
const ConsumerFunction<Message> &consumer_function) const;
|
||||
|
||||
utils::BasicResult<std::string> SetStreamOffset(int64_t offset);
|
||||
@@ -71,10 +72,11 @@ struct PulsarStream {
|
||||
StreamInfo Info(std::string transformation_name) const;
|
||||
|
||||
void Start();
|
||||
void StartWithLimit(uint64_t batch_limit, std::optional<std::chrono::milliseconds> timeout) const;
|
||||
void Stop();
|
||||
bool IsRunning() const;
|
||||
|
||||
void Check(std::optional<std::chrono::milliseconds> timeout, std::optional<int64_t> batch_limit,
|
||||
void Check(std::optional<std::chrono::milliseconds> timeout, std::optional<uint64_t> batch_limit,
|
||||
const ConsumerFunction<Message> &consumer_function) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -43,6 +43,7 @@ extern const Event MessagesConsumed;
|
||||
namespace memgraph::query::stream {
|
||||
namespace {
|
||||
inline constexpr auto kExpectedTransformationResultSize = 2;
|
||||
inline constexpr auto kCheckStreamResultSize = 2;
|
||||
const utils::pmr::string query_param_name{"query", utils::NewDeleteResource()};
|
||||
const utils::pmr::string params_param_name{"parameters", utils::NewDeleteResource()};
|
||||
|
||||
@@ -455,7 +456,7 @@ void Streams::Create(const std::string &stream_name, typename TStream::StreamInf
|
||||
|
||||
try {
|
||||
std::visit(
|
||||
[&](auto &&stream_data) {
|
||||
[&](const auto &stream_data) {
|
||||
const auto stream_source_ptr = stream_data.stream_source->ReadLock();
|
||||
Persist(CreateStatus(stream_name, stream_data.transformation_name, stream_data.owner, *stream_source_ptr));
|
||||
},
|
||||
@@ -574,7 +575,7 @@ void Streams::RestoreStreams() {
|
||||
auto it = CreateConsumer<T>(*locked_streams_map, stream_name, std::move(status.info), std::move(status.owner));
|
||||
if (status.is_running) {
|
||||
std::visit(
|
||||
[&](auto &&stream_data) {
|
||||
[&](const auto &stream_data) {
|
||||
auto stream_source_ptr = stream_data.stream_source->Lock();
|
||||
stream_source_ptr->Start();
|
||||
},
|
||||
@@ -616,12 +617,12 @@ void Streams::Drop(const std::string &stream_name) {
|
||||
// function can be executing with the consumer, nothing else.
|
||||
// By acquiring the write lock here for the consumer, we make sure there is
|
||||
// no running Test function for this consumer, therefore it can be erased.
|
||||
std::visit([&](auto &&stream_data) { stream_data.stream_source->Lock(); }, it->second);
|
||||
std::visit([&](const auto &stream_data) { stream_data.stream_source->Lock(); }, it->second);
|
||||
|
||||
locked_streams->erase(it);
|
||||
if (!storage_.Delete(stream_name)) {
|
||||
throw StreamsException("Couldn't delete stream '{}' from persistent store!", stream_name);
|
||||
}
|
||||
locked_streams->erase(it);
|
||||
|
||||
// TODO(antaljanosbenjamin) Release the transformation
|
||||
}
|
||||
@@ -631,7 +632,7 @@ void Streams::Start(const std::string &stream_name) {
|
||||
auto it = GetStream(*locked_streams, stream_name);
|
||||
|
||||
std::visit(
|
||||
[&, this](auto &&stream_data) {
|
||||
[&, this](const auto &stream_data) {
|
||||
auto stream_source_ptr = stream_data.stream_source->Lock();
|
||||
stream_source_ptr->Start();
|
||||
Persist(CreateStatus(stream_name, stream_data.transformation_name, stream_data.owner, *stream_source_ptr));
|
||||
@@ -639,12 +640,27 @@ void Streams::Start(const std::string &stream_name) {
|
||||
it->second);
|
||||
}
|
||||
|
||||
void Streams::StartWithLimit(const std::string &stream_name, uint64_t batch_limit,
|
||||
std::optional<std::chrono::milliseconds> timeout) const {
|
||||
std::optional locked_streams{streams_.ReadLock()};
|
||||
auto it = GetStream(**locked_streams, stream_name);
|
||||
|
||||
std::visit(
|
||||
[&](const auto &stream_data) {
|
||||
const auto locked_stream_source = stream_data.stream_source->ReadLock();
|
||||
locked_streams.reset();
|
||||
|
||||
locked_stream_source->StartWithLimit(batch_limit, timeout);
|
||||
},
|
||||
it->second);
|
||||
}
|
||||
|
||||
void Streams::Stop(const std::string &stream_name) {
|
||||
auto locked_streams = streams_.Lock();
|
||||
auto it = GetStream(*locked_streams, stream_name);
|
||||
|
||||
std::visit(
|
||||
[&, this](auto &&stream_data) {
|
||||
[&, this](const auto &stream_data) {
|
||||
auto stream_source_ptr = stream_data.stream_source->Lock();
|
||||
stream_source_ptr->Stop();
|
||||
|
||||
@@ -656,7 +672,7 @@ void Streams::Stop(const std::string &stream_name) {
|
||||
void Streams::StartAll() {
|
||||
for (auto locked_streams = streams_.Lock(); auto &[stream_name, stream_data] : *locked_streams) {
|
||||
std::visit(
|
||||
[&stream_name = stream_name, this](auto &&stream_data) {
|
||||
[&stream_name = stream_name, this](const auto &stream_data) {
|
||||
auto locked_stream_source = stream_data.stream_source->Lock();
|
||||
if (!locked_stream_source->IsRunning()) {
|
||||
locked_stream_source->Start();
|
||||
@@ -671,7 +687,7 @@ void Streams::StartAll() {
|
||||
void Streams::StopAll() {
|
||||
for (auto locked_streams = streams_.Lock(); auto &[stream_name, stream_data] : *locked_streams) {
|
||||
std::visit(
|
||||
[&stream_name = stream_name, this](auto &&stream_data) {
|
||||
[&stream_name = stream_name, this](const auto &stream_data) {
|
||||
auto locked_stream_source = stream_data.stream_source->Lock();
|
||||
if (locked_stream_source->IsRunning()) {
|
||||
locked_stream_source->Stop();
|
||||
@@ -688,7 +704,7 @@ std::vector<StreamStatus<>> Streams::GetStreamInfo() const {
|
||||
{
|
||||
for (auto locked_streams = streams_.ReadLock(); const auto &[stream_name, stream_data] : *locked_streams) {
|
||||
std::visit(
|
||||
[&, &stream_name = stream_name](auto &&stream_data) {
|
||||
[&, &stream_name = stream_name](const auto &stream_data) {
|
||||
auto locked_stream_source = stream_data.stream_source->ReadLock();
|
||||
auto info = locked_stream_source->Info(stream_data.transformation_name);
|
||||
result.emplace_back(StreamStatus<>{stream_name, StreamType(*locked_stream_source),
|
||||
@@ -702,12 +718,12 @@ std::vector<StreamStatus<>> Streams::GetStreamInfo() const {
|
||||
}
|
||||
|
||||
TransformationResult Streams::Check(const std::string &stream_name, std::optional<std::chrono::milliseconds> timeout,
|
||||
std::optional<int64_t> batch_limit) const {
|
||||
std::optional<uint64_t> batch_limit) const {
|
||||
std::optional locked_streams{streams_.ReadLock()};
|
||||
auto it = GetStream(**locked_streams, stream_name);
|
||||
|
||||
return std::visit(
|
||||
[&](auto &&stream_data) {
|
||||
[&](const auto &stream_data) {
|
||||
// This depends on the fact that Drop will first acquire a write lock to the consumer, and erase it only after
|
||||
// that
|
||||
const auto locked_stream_source = stream_data.stream_source->ReadLock();
|
||||
@@ -724,15 +740,27 @@ TransformationResult Streams::Check(const std::string &stream_name, std::optiona
|
||||
auto accessor = interpreter_context->db->Access();
|
||||
CallCustomTransformation(transformation_name, messages, result, accessor, *memory_resource, stream_name);
|
||||
|
||||
for (auto &row : result.rows) {
|
||||
auto [query, parameters] = ExtractTransformationResult(row.values, transformation_name, stream_name);
|
||||
std::vector<TypedValue> result_row;
|
||||
result_row.reserve(kExpectedTransformationResultSize);
|
||||
result_row.push_back(std::move(query));
|
||||
result_row.push_back(std::move(parameters));
|
||||
auto result_row = std::vector<TypedValue>();
|
||||
result_row.reserve(kCheckStreamResultSize);
|
||||
|
||||
test_result.push_back(std::move(result_row));
|
||||
}
|
||||
auto queries_and_parameters = std::vector<TypedValue>(result.rows.size());
|
||||
std::transform(
|
||||
result.rows.cbegin(), result.rows.cend(), queries_and_parameters.begin(), [&](const auto &row) {
|
||||
auto [query, parameters] = ExtractTransformationResult(row.values, transformation_name, stream_name);
|
||||
|
||||
return std::map<std::string, TypedValue>{{"query", std::move(query)},
|
||||
{"parameters", std::move(parameters)}};
|
||||
});
|
||||
result_row.emplace_back(std::move(queries_and_parameters));
|
||||
|
||||
auto messages_list = std::vector<TypedValue>(messages.size());
|
||||
std::transform(messages.cbegin(), messages.cend(), messages_list.begin(), [](const auto &message) {
|
||||
return std::string_view(message.Payload().data(), message.Payload().size());
|
||||
});
|
||||
|
||||
result_row.emplace_back(std::move(messages_list));
|
||||
|
||||
test_result.emplace_back(std::move(result_row));
|
||||
};
|
||||
|
||||
locked_stream_source->Check(timeout, batch_limit, consumer_function);
|
||||
|
||||
@@ -115,6 +115,17 @@ class Streams final {
|
||||
/// @throws ConsumerRunningException if the consumer is already running
|
||||
void Start(const std::string &stream_name);
|
||||
|
||||
/// Start consuming from a stream.
|
||||
///
|
||||
/// @param stream_name name of the stream that needs to be started
|
||||
/// @param batch_limit number of batches we want to consume before stopping
|
||||
/// @param timeout the maximum duration during which the command should run.
|
||||
///
|
||||
/// @throws StreamsException if the stream doesn't exist
|
||||
/// @throws ConsumerRunningException if the consumer is already running
|
||||
void StartWithLimit(const std::string &stream_name, uint64_t batch_limit,
|
||||
std::optional<std::chrono::milliseconds> timeout) const;
|
||||
|
||||
/// Stop consuming from a stream.
|
||||
///
|
||||
/// @param stream_name name of the stream that needs to be stopped
|
||||
@@ -142,6 +153,7 @@ class Streams final {
|
||||
///
|
||||
/// @param stream_name name of the stream we want to test
|
||||
/// @param batch_limit number of batches we want to test before stopping
|
||||
/// @param timeout the maximum duration during which the command should run.
|
||||
///
|
||||
/// @returns A vector of vectors of TypedValue. Each subvector contains two elements, the query string and the
|
||||
/// nullable parameters map.
|
||||
@@ -151,7 +163,7 @@ class Streams final {
|
||||
/// @throws ConsumerCheckFailedException if the transformation function throws any std::exception during processing
|
||||
TransformationResult Check(const std::string &stream_name,
|
||||
std::optional<std::chrono::milliseconds> timeout = std::nullopt,
|
||||
std::optional<int64_t> batch_limit = std::nullopt) const;
|
||||
std::optional<uint64_t> batch_limit = std::nullopt) const;
|
||||
|
||||
private:
|
||||
template <Stream TStream>
|
||||
@@ -176,7 +188,7 @@ class Streams final {
|
||||
void Persist(StreamStatus<TStream> &&status) {
|
||||
const std::string stream_name = status.name;
|
||||
if (!storage_.Put(stream_name, nlohmann::json(std::move(status)).dump())) {
|
||||
throw StreamsException{"Couldn't persist steam data for stream '{}'", stream_name};
|
||||
throw StreamsException{"Couldn't persist stream data for stream '{}'", stream_name};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,6 +214,9 @@ TypedValue::TypedValue(const TypedValue &other, utils::MemoryResource *memory) :
|
||||
case Type::Duration:
|
||||
new (&duration_v) utils::Duration(other.duration_v);
|
||||
return;
|
||||
case Type::Graph:
|
||||
new (&graph_v) Graph(other.graph_v, memory_);
|
||||
return;
|
||||
}
|
||||
LOG_FATAL("Unsupported TypedValue::Type");
|
||||
}
|
||||
@@ -263,6 +266,8 @@ TypedValue::TypedValue(TypedValue &&other, utils::MemoryResource *memory) : memo
|
||||
case Type::Duration:
|
||||
new (&duration_v) utils::Duration(other.duration_v);
|
||||
break;
|
||||
case Type::Graph:
|
||||
new (&graph_v) Graph(std::move(other.graph_v), memory_);
|
||||
}
|
||||
other.DestroyValue();
|
||||
}
|
||||
@@ -297,6 +302,8 @@ TypedValue::operator storage::PropertyValue() const {
|
||||
storage::TemporalData{storage::TemporalType::LocalDateTime, local_date_time_v.MicrosecondsSinceEpoch()});
|
||||
case Type::Duration:
|
||||
return storage::PropertyValue(storage::TemporalData{storage::TemporalType::Duration, duration_v.microseconds});
|
||||
case Type::Graph:
|
||||
throw TypedValueException("Unsupported copy from StorageValue to TypedValue");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -331,6 +338,7 @@ DEFINE_VALUE_AND_TYPE_GETTERS(utils::Date, Date, date_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(utils::LocalTime, LocalTime, local_time_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(utils::LocalDateTime, LocalDateTime, local_date_time_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(utils::Duration, Duration, duration_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(Graph, Graph, graph_v)
|
||||
|
||||
#undef DEFINE_VALUE_AND_TYPE_GETTERS
|
||||
|
||||
@@ -387,6 +395,8 @@ std::ostream &operator<<(std::ostream &os, const TypedValue::Type &type) {
|
||||
return os << "local_date_time";
|
||||
case TypedValue::Type::Duration:
|
||||
return os << "duration";
|
||||
case TypedValue::Type::Graph:
|
||||
return os << "graph";
|
||||
}
|
||||
LOG_FATAL("Unsupported TypedValue::Type");
|
||||
}
|
||||
@@ -407,7 +417,7 @@ DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(int, Int, int_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(bool, Bool, bool_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(int64_t, Int, int_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(double, Double, double_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const std::string_view &, String, string_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const std::string_view, String, string_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const TypedValue::TVector &, List, list_v)
|
||||
|
||||
TypedValue &TypedValue::operator=(const std::vector<TypedValue> &other) {
|
||||
@@ -522,6 +532,9 @@ TypedValue &TypedValue::operator=(const TypedValue &other) {
|
||||
case TypedValue::Type::Path:
|
||||
new (&path_v) Path(other.path_v, memory_);
|
||||
return *this;
|
||||
case TypedValue::Type::Graph:
|
||||
new (&graph_v) Graph(other.graph_v, memory_);
|
||||
return *this;
|
||||
case Type::Date:
|
||||
new (&date_v) utils::Date(other.date_v);
|
||||
return *this;
|
||||
@@ -593,6 +606,9 @@ TypedValue &TypedValue::operator=(TypedValue &&other) noexcept(false) {
|
||||
case Type::Duration:
|
||||
new (&duration_v) utils::Duration(other.duration_v);
|
||||
break;
|
||||
case Type::Graph:
|
||||
new (&graph_v) Graph(std::move(other.graph_v), memory_);
|
||||
break;
|
||||
}
|
||||
other.DestroyValue();
|
||||
}
|
||||
@@ -633,6 +649,9 @@ void TypedValue::DestroyValue() {
|
||||
case Type::LocalDateTime:
|
||||
case Type::Duration:
|
||||
break;
|
||||
case Type::Graph:
|
||||
graph_v.~Graph();
|
||||
break;
|
||||
}
|
||||
|
||||
type_ = TypedValue::Type::Null;
|
||||
@@ -792,6 +811,8 @@ TypedValue operator==(const TypedValue &a, const TypedValue &b) {
|
||||
return TypedValue(a.ValueLocalDateTime() == b.ValueLocalDateTime(), a.GetMemoryResource());
|
||||
case TypedValue::Type::Duration:
|
||||
return TypedValue(a.ValueDuration() == b.ValueDuration(), a.GetMemoryResource());
|
||||
case TypedValue::Type::Graph:
|
||||
throw TypedValueException("Unsupported comparison operator");
|
||||
default:
|
||||
LOG_FATAL("Unhandled comparison for types");
|
||||
}
|
||||
@@ -1100,6 +1121,8 @@ size_t TypedValue::Hash::operator()(const TypedValue &value) const {
|
||||
case TypedValue::Type::Duration:
|
||||
return utils::DurationHash{}(value.ValueDuration());
|
||||
break;
|
||||
case TypedValue::Type::Graph:
|
||||
throw TypedValueException("Unsupported hash function for Graph");
|
||||
}
|
||||
LOG_FATAL("Unhandled TypedValue.type() in hash function");
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/graph.hpp"
|
||||
#include "query/path.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
@@ -82,7 +83,8 @@ class TypedValue {
|
||||
Date,
|
||||
LocalTime,
|
||||
LocalDateTime,
|
||||
Duration
|
||||
Duration,
|
||||
Graph
|
||||
};
|
||||
|
||||
// TypedValue at this exact moment of compilation is an incomplete type, and
|
||||
@@ -185,7 +187,7 @@ class TypedValue {
|
||||
new (&string_v) TString(value, memory_);
|
||||
}
|
||||
|
||||
explicit TypedValue(const std::string_view &value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
explicit TypedValue(const std::string_view value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: memory_(memory), type_(Type::String) {
|
||||
new (&string_v) TString(value, memory_);
|
||||
}
|
||||
@@ -401,6 +403,22 @@ class TypedValue {
|
||||
new (&path_v) Path(std::move(path), memory_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with the value of graph.
|
||||
* utils::MemoryResource is obtained from graph. After the move, graph will be
|
||||
* left empty.
|
||||
*/
|
||||
explicit TypedValue(Graph &&graph) noexcept : TypedValue(std::move(graph), graph.GetMemoryResource()) {}
|
||||
|
||||
/**
|
||||
* Construct with the value of graph and use the given MemoryResource.
|
||||
* If `*graph.GetMemoryResource() != *memory`, this call will perform an
|
||||
* element-wise move and graph is not guaranteed to be empty.
|
||||
*/
|
||||
TypedValue(Graph &&graph, utils::MemoryResource *memory) : memory_(memory), type_(Type::Graph) {
|
||||
new (&graph_v) Graph(std::move(graph), memory_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with the value of other.
|
||||
* Default utils::NewDeleteResource() is used for allocations. After the move,
|
||||
@@ -420,7 +438,7 @@ class TypedValue {
|
||||
TypedValue &operator=(bool);
|
||||
TypedValue &operator=(int64_t);
|
||||
TypedValue &operator=(double);
|
||||
TypedValue &operator=(const std::string_view &);
|
||||
TypedValue &operator=(std::string_view);
|
||||
TypedValue &operator=(const TVector &);
|
||||
TypedValue &operator=(const std::vector<TypedValue> &);
|
||||
TypedValue &operator=(const TMap &);
|
||||
@@ -486,6 +504,7 @@ class TypedValue {
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(utils::LocalTime, LocalTime)
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(utils::LocalDateTime, LocalDateTime)
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(utils::Duration, Duration)
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(Graph, Graph)
|
||||
|
||||
#undef DECLARE_VALUE_AND_TYPE_GETTERS
|
||||
|
||||
@@ -528,6 +547,7 @@ class TypedValue {
|
||||
utils::LocalTime local_time_v;
|
||||
utils::LocalDateTime local_date_time_v;
|
||||
utils::Duration duration_v;
|
||||
Graph graph_v;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -166,7 +166,7 @@ inline void Save(const char *obj, Builder *builder) {
|
||||
builder->Save(reinterpret_cast<const uint8_t *>(obj), size);
|
||||
}
|
||||
|
||||
inline void Save(const std::string_view &obj, Builder *builder) {
|
||||
inline void Save(const std::string_view obj, Builder *builder) {
|
||||
uint64_t size = obj.size();
|
||||
Save(size, builder);
|
||||
builder->Save(reinterpret_cast<const uint8_t *>(obj.data()), size);
|
||||
|
||||
@@ -10,11 +10,9 @@ set(storage_v2_src_files
|
||||
indices.cpp
|
||||
property_store.cpp
|
||||
vertex_accessor.cpp
|
||||
schemas.cpp
|
||||
storage.cpp)
|
||||
|
||||
##### Replication #####
|
||||
|
||||
define_add_lcp(add_lcp_storage lcp_storage_cpp_files generated_lcp_storage_files)
|
||||
|
||||
add_lcp_storage(replication/rpc.lcp SLK_SERIALIZE)
|
||||
@@ -27,10 +25,10 @@ set(storage_v2_src_files
|
||||
replication/replication_server.cpp
|
||||
replication/serialization.cpp
|
||||
replication/slk.cpp
|
||||
replication/replication_persistence_helper.cpp
|
||||
${lcp_storage_cpp_files})
|
||||
|
||||
#######################
|
||||
|
||||
find_package(gflags REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ struct Config {
|
||||
uint64_t wal_file_flush_every_n_tx{100000};
|
||||
|
||||
bool snapshot_on_exit{false};
|
||||
|
||||
bool restore_replicas_on_startup{false};
|
||||
} durability;
|
||||
|
||||
struct Transaction {
|
||||
|
||||
@@ -178,7 +178,6 @@ std::optional<RecoveryInfo> RecoverData(const std::filesystem::path &snapshot_di
|
||||
|
||||
RecoveryInfo recovery_info;
|
||||
RecoveredIndicesAndConstraints indices_constraints;
|
||||
memgraph::storage::SchemasMap recovered_schemas;
|
||||
std::optional<uint64_t> snapshot_timestamp;
|
||||
if (!snapshot_files.empty()) {
|
||||
spdlog::info("Try recovering from snapshot directory {}.", snapshot_directory);
|
||||
|
||||
@@ -38,7 +38,6 @@ enum class Marker : uint8_t {
|
||||
SECTION_DELTA = 0x26,
|
||||
SECTION_EPOCH_HISTORY = 0x27,
|
||||
SECTION_OFFSETS = 0x42,
|
||||
SECTION_SCHEMAS = 0x43,
|
||||
|
||||
DELTA_VERTEX_CREATE = 0x50,
|
||||
DELTA_VERTEX_DELETE = 0x51,
|
||||
@@ -57,8 +56,6 @@ enum class Marker : uint8_t {
|
||||
DELTA_EXISTENCE_CONSTRAINT_DROP = 0x5e,
|
||||
DELTA_UNIQUE_CONSTRAINT_CREATE = 0x5f,
|
||||
DELTA_UNIQUE_CONSTRAINT_DROP = 0x60,
|
||||
DELTA_SCHEMA_CREATE = 0x61,
|
||||
DELTA_SCHEMA_DROP = 0x62,
|
||||
|
||||
VALUE_FALSE = 0x00,
|
||||
VALUE_TRUE = 0xff,
|
||||
@@ -66,7 +63,7 @@ enum class Marker : uint8_t {
|
||||
|
||||
/// List of all available markers.
|
||||
/// IMPORTANT: Don't forget to update this list when you add a new Marker.
|
||||
constexpr Marker kMarkersAll[] = {
|
||||
static const Marker kMarkersAll[] = {
|
||||
Marker::TYPE_NULL,
|
||||
Marker::TYPE_BOOL,
|
||||
Marker::TYPE_INT,
|
||||
@@ -102,8 +99,6 @@ constexpr Marker kMarkersAll[] = {
|
||||
Marker::DELTA_EXISTENCE_CONSTRAINT_DROP,
|
||||
Marker::DELTA_UNIQUE_CONSTRAINT_CREATE,
|
||||
Marker::DELTA_UNIQUE_CONSTRAINT_DROP,
|
||||
Marker::DELTA_SCHEMA_CREATE,
|
||||
Marker::DELTA_SCHEMA_DROP,
|
||||
Marker::VALUE_FALSE,
|
||||
Marker::VALUE_TRUE,
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ static const std::string kSnapshotDirectory{"snapshots"};
|
||||
static const std::string kWalDirectory{"wal"};
|
||||
static const std::string kBackupDirectory{".backup"};
|
||||
static const std::string kLockFile{".lock"};
|
||||
static const std::string kReplicationDirectory{"replication"};
|
||||
|
||||
// This is the prefix used for Snapshot and WAL filenames. It is a timestamp
|
||||
// format that equals to: YYYYmmddHHMMSSffffff
|
||||
|
||||
@@ -27,7 +27,7 @@ void WriteSize(Encoder *encoder, uint64_t size) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Encoder::Initialize(const std::filesystem::path &path, const std::string_view &magic, uint64_t version) {
|
||||
void Encoder::Initialize(const std::filesystem::path &path, const std::string_view magic, uint64_t version) {
|
||||
file_.Open(path, utils::OutputFile::Mode::OVERWRITE_EXISTING);
|
||||
Write(reinterpret_cast<const uint8_t *>(magic.data()), magic.size());
|
||||
auto version_encoded = utils::HostToLittleEndian(version);
|
||||
@@ -73,7 +73,7 @@ void Encoder::WriteDouble(double value) {
|
||||
Write(reinterpret_cast<const uint8_t *>(&value_uint), sizeof(value_uint));
|
||||
}
|
||||
|
||||
void Encoder::WriteString(const std::string_view &value) {
|
||||
void Encoder::WriteString(const std::string_view value) {
|
||||
WriteMarker(Marker::TYPE_STRING);
|
||||
WriteSize(this, value.size());
|
||||
Write(reinterpret_cast<const uint8_t *>(value.data()), value.size());
|
||||
@@ -333,7 +333,6 @@ std::optional<PropertyValue> Decoder::ReadPropertyValue() {
|
||||
case Marker::SECTION_DELTA:
|
||||
case Marker::SECTION_EPOCH_HISTORY:
|
||||
case Marker::SECTION_OFFSETS:
|
||||
case Marker::SECTION_SCHEMAS:
|
||||
case Marker::DELTA_VERTEX_CREATE:
|
||||
case Marker::DELTA_VERTEX_DELETE:
|
||||
case Marker::DELTA_VERTEX_ADD_LABEL:
|
||||
@@ -351,8 +350,6 @@ std::optional<PropertyValue> Decoder::ReadPropertyValue() {
|
||||
case Marker::DELTA_EXISTENCE_CONSTRAINT_DROP:
|
||||
case Marker::DELTA_UNIQUE_CONSTRAINT_CREATE:
|
||||
case Marker::DELTA_UNIQUE_CONSTRAINT_DROP:
|
||||
case Marker::DELTA_SCHEMA_CREATE:
|
||||
case Marker::DELTA_SCHEMA_DROP:
|
||||
case Marker::VALUE_FALSE:
|
||||
case Marker::VALUE_TRUE:
|
||||
return std::nullopt;
|
||||
@@ -435,7 +432,6 @@ bool Decoder::SkipPropertyValue() {
|
||||
case Marker::SECTION_DELTA:
|
||||
case Marker::SECTION_EPOCH_HISTORY:
|
||||
case Marker::SECTION_OFFSETS:
|
||||
case Marker::SECTION_SCHEMAS:
|
||||
case Marker::DELTA_VERTEX_CREATE:
|
||||
case Marker::DELTA_VERTEX_DELETE:
|
||||
case Marker::DELTA_VERTEX_ADD_LABEL:
|
||||
@@ -453,8 +449,6 @@ bool Decoder::SkipPropertyValue() {
|
||||
case Marker::DELTA_EXISTENCE_CONSTRAINT_DROP:
|
||||
case Marker::DELTA_UNIQUE_CONSTRAINT_CREATE:
|
||||
case Marker::DELTA_UNIQUE_CONSTRAINT_DROP:
|
||||
case Marker::DELTA_SCHEMA_CREATE:
|
||||
case Marker::DELTA_SCHEMA_DROP:
|
||||
case Marker::VALUE_FALSE:
|
||||
case Marker::VALUE_TRUE:
|
||||
return false;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user