Compare commits
38 Commits
T0818-MG-f
...
MG-unorder
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9104a8c9d3 | ||
|
|
5c4cbdbaf5 | ||
|
|
803f8c3aad | ||
|
|
95a2078013 | ||
|
|
f7f3f0a955 | ||
|
|
ab8ebdd02b | ||
|
|
521a085b79 | ||
|
|
5ab82c5cfe | ||
|
|
07566ea9d5 | ||
|
|
720762caa0 | ||
|
|
b5404a4fbf | ||
|
|
6f1e1d6120 | ||
|
|
9e0eec2176 | ||
|
|
113184a3a4 | ||
|
|
cd9ade06cb | ||
|
|
989f43b0b3 | ||
|
|
d95bfb3170 | ||
|
|
e01a11ca32 | ||
|
|
2f1c55e2b1 | ||
|
|
cfb9203b43 | ||
|
|
8b1d128c9d | ||
|
|
a30310ce80 | ||
|
|
f1b2ee7fc4 | ||
|
|
d72b23b682 | ||
|
|
74926ab058 | ||
|
|
8abc5ef025 | ||
|
|
a8b4c664a4 | ||
|
|
1eaea91312 | ||
|
|
3b13582868 | ||
|
|
99d17758fe | ||
|
|
05731ffb53 | ||
|
|
f4d8845718 | ||
|
|
4c5bf7c376 | ||
|
|
a8d9d5e754 | ||
|
|
ae7b5976af | ||
|
|
6c45cd8558 | ||
|
|
78ebaa0499 | ||
|
|
7eb0b4579d |
@@ -1,9 +1,7 @@
|
||||
---
|
||||
Checks: '*,
|
||||
-abseil-string-find-str-contains,
|
||||
-altera-id-dependent-backward-branch,
|
||||
-altera-struct-pack-align,
|
||||
-altera-unroll-loops,
|
||||
-android-*,
|
||||
-cert-err58-cpp,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
|
||||
@@ -32,7 +32,7 @@ for file in $modified_files; do
|
||||
fi
|
||||
|
||||
echo "Running header checker..."
|
||||
$project_folder/tools/header-checker.py $tmpdir/$file $file --amend-year
|
||||
$project_folder/tools/header-checker.py $tmpdir/$file
|
||||
code=$?
|
||||
|
||||
if [ $code -ne 0 ]; then
|
||||
|
||||
2
.github/workflows/daily_benchmark.yaml
vendored
2
.github/workflows/daily_benchmark.yaml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
|
||||
28
.github/workflows/diff.yaml
vendored
28
.github/workflows/diff.yaml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: Build community binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
- name: Build combined ASAN, UBSAN and coverage binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests. It is restricted to 2 threads intentionally, because higher concurrency makes the timing related tests unstable.
|
||||
cd build
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
- name: Compute code coverage
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Compute code coverage.
|
||||
cd tools/github
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
|
||||
- name: Run clang-tidy
|
||||
run: |
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/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
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
- name: Build debug binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
- name: Run leftover CTest tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run leftover CTest tests (all except unit and benchmark tests).
|
||||
cd build
|
||||
@@ -181,7 +181,7 @@ jobs:
|
||||
- name: Run cppcheck and clang-format
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run cppcheck and clang-format.
|
||||
cd tools/github
|
||||
@@ -212,7 +212,7 @@ jobs:
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -238,7 +238,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
@@ -272,7 +272,7 @@ jobs:
|
||||
- name: Create enterprise DEB package
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
cd build
|
||||
|
||||
@@ -319,7 +319,7 @@ jobs:
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -360,7 +360,7 @@ jobs:
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
|
||||
6
.github/workflows/full_clang_tidy.yaml
vendored
6
.github/workflows/full_clang_tidy.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Build debug binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -36,10 +36,10 @@ jobs:
|
||||
|
||||
- name: Run clang-tidy
|
||||
run: |
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# The results are also written to standard output in order to retain them in the logs
|
||||
./tools/github/clang-tidy/run-clang-tidy.py -p build -j $THREADS -clang-tidy-binary=/opt/toolchain-v4/bin/clang-tidy "$PWD/src/*" |
|
||||
./tools/github/clang-tidy/run-clang-tidy.py -p build -j $THREADS -clang-tidy-binary=/opt/toolchain-v3/bin/clang-tidy "$PWD/src/*" |
|
||||
tee ./build/full_clang_tidy_output.txt
|
||||
|
||||
- name: Summarize clang-tidy results
|
||||
|
||||
27
.github/workflows/package_all.yaml
vendored
27
.github/workflows/package_all.yaml
vendored
@@ -39,6 +39,23 @@ jobs:
|
||||
name: centos-8
|
||||
path: build/output/centos-8/memgraph*.rpm
|
||||
|
||||
debian-9:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package debian-9
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: debian-9
|
||||
path: build/output/debian-9/memgraph*.deb
|
||||
|
||||
debian-10:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
timeout-minutes: 60
|
||||
@@ -84,7 +101,7 @@ jobs:
|
||||
- name: "Build package"
|
||||
run: |
|
||||
cd release/package
|
||||
./run.sh package debian-11 --for-docker
|
||||
./run.sh package debian-10 --for-docker
|
||||
./run.sh docker
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -126,7 +143,7 @@ jobs:
|
||||
name: ubuntu-2004
|
||||
path: build/output/ubuntu-20.04/memgraph*.deb
|
||||
|
||||
debian-11-platform:
|
||||
debian-10-platform:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
@@ -136,9 +153,9 @@ jobs:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package debian-11 --for-platform
|
||||
./release/package/run.sh package debian-10 --for-platform
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: debian-11-platform
|
||||
path: build/output/debian-11/memgraph*.deb
|
||||
name: debian-10-platform
|
||||
path: build/output/debian-10/memgraph*.deb
|
||||
|
||||
24
.github/workflows/release_centos8.yaml
vendored
24
.github/workflows/release_centos8.yaml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Build community binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
- name: Build coverage binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
- name: Compute code coverage
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Compute code coverage.
|
||||
cd tools/github
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
- name: Build debug binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
- name: Run leftover CTest tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run leftover CTest tests (all except unit and benchmark tests).
|
||||
cd build
|
||||
@@ -166,7 +166,7 @@ jobs:
|
||||
- name: Run cppcheck and clang-format
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run cppcheck and clang-format.
|
||||
cd tools/github
|
||||
@@ -198,7 +198,7 @@ jobs:
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -211,7 +211,7 @@ jobs:
|
||||
- name: Create enterprise RPM package
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
cd build
|
||||
|
||||
@@ -233,7 +233,7 @@ jobs:
|
||||
- name: Run micro benchmark tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run micro benchmark tests.
|
||||
cd build
|
||||
@@ -272,7 +272,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
|
||||
26
.github/workflows/release_debian10.yaml
vendored
26
.github/workflows/release_debian10.yaml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Build community binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
- name: Build coverage binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
- name: Compute code coverage
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Compute code coverage.
|
||||
cd tools/github
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
- name: Build debug binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
- name: Run leftover CTest tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run leftover CTest tests (all except unit and benchmark tests).
|
||||
cd build
|
||||
@@ -166,7 +166,7 @@ jobs:
|
||||
- name: Run cppcheck and clang-format
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run cppcheck and clang-format.
|
||||
cd tools/github
|
||||
@@ -198,7 +198,7 @@ jobs:
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -211,7 +211,7 @@ jobs:
|
||||
- name: Create enterprise DEB package
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
cd build
|
||||
|
||||
@@ -232,7 +232,7 @@ jobs:
|
||||
- name: Run micro benchmark tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run micro benchmark tests.
|
||||
cd build
|
||||
@@ -271,7 +271,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
@@ -333,7 +333,7 @@ jobs:
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
|
||||
24
.github/workflows/release_ubuntu2004.yaml
vendored
24
.github/workflows/release_ubuntu2004.yaml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Build community binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
- name: Build coverage binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
- name: Compute code coverage
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Compute code coverage.
|
||||
cd tools/github
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
- name: Build debug binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
- name: Run leftover CTest tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run leftover CTest tests (all except unit and benchmark tests).
|
||||
cd build
|
||||
@@ -166,7 +166,7 @@ jobs:
|
||||
- name: Run cppcheck and clang-format
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run cppcheck and clang-format.
|
||||
cd tools/github
|
||||
@@ -198,7 +198,7 @@ jobs:
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
@@ -211,7 +211,7 @@ jobs:
|
||||
- name: Create enterprise DEB package
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
cd build
|
||||
|
||||
@@ -232,7 +232,7 @@ jobs:
|
||||
- name: Run micro benchmark tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run micro benchmark tests.
|
||||
cd build
|
||||
@@ -271,7 +271,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
|
||||
@@ -204,8 +204,6 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
|
||||
# release flags
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
||||
|
||||
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -pthread")
|
||||
|
||||
#debug flags
|
||||
set(PREFERRED_DEBUGGER "gdb" CACHE STRING
|
||||
"Tunes the debug output for your preferred debugger (gdb or lldb).")
|
||||
@@ -230,8 +228,6 @@ endif()
|
||||
message(STATUS "CMake build type: ${CMAKE_BUILD_TYPE}")
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
set(MG_ARCH "x86_64" CACHE STRING "Host architecture to build Memgraph on. Supported values are x86_64 (default), ARM64.")
|
||||
|
||||
# setup external dependencies -------------------------------------------------
|
||||
|
||||
# threading
|
||||
@@ -326,8 +322,7 @@ if (UBSAN)
|
||||
# To make the program abort on undefined behavior, use UBSAN_OPTIONS=halt_on_error=1.
|
||||
endif()
|
||||
|
||||
set(MG_PYTHON_VERSION "" CACHE STRING "Specify the exact Python version used by the query modules")
|
||||
set(MG_PYTHON_PATH "" CACHE STRING "Specify the exact Python path used by the query modules")
|
||||
set(MG_PYTHON_VERSION "" CACHE STRING "Specify the exact python version used by the query modules")
|
||||
|
||||
# Add subprojects
|
||||
include_directories(src)
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
# Try to find jemalloc library
|
||||
#
|
||||
# Use this module as:
|
||||
# find_package(Jemalloc)
|
||||
#
|
||||
# or:
|
||||
# find_package(Jemalloc REQUIRED)
|
||||
#
|
||||
# This will define the following variables:
|
||||
#
|
||||
# Jemalloc_FOUND True if the system has the jemalloc library.
|
||||
# Jemalloc_INCLUDE_DIRS Include directories needed to use jemalloc.
|
||||
# Jemalloc_LIBRARIES Libraries needed to link to jemalloc.
|
||||
#
|
||||
# The following cache variables may also be set:
|
||||
#
|
||||
# Jemalloc_INCLUDE_DIR The directory containing jemalloc/jemalloc.h.
|
||||
# Jemalloc_LIBRARY The path to the jemalloc static library.
|
||||
|
||||
find_path(Jemalloc_INCLUDE_DIR NAMES jemalloc/jemalloc.h PATH_SUFFIXES include)
|
||||
|
||||
find_library(Jemalloc_LIBRARY NAMES libjemalloc.a PATH_SUFFIXES lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Jemalloc
|
||||
FOUND_VAR Jemalloc_FOUND
|
||||
REQUIRED_VARS
|
||||
Jemalloc_LIBRARY
|
||||
Jemalloc_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(Jemalloc_FOUND)
|
||||
set(Jemalloc_LIBRARIES ${Jemalloc_LIBRARY})
|
||||
set(Jemalloc_INCLUDE_DIRS ${Jemalloc_INCLUDE_DIR})
|
||||
else()
|
||||
if(Jemalloc_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Cannot find jemalloc!")
|
||||
else()
|
||||
message(WARNING "jemalloc is not found!")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(Jemalloc_FOUND AND NOT TARGET Jemalloc::Jemalloc)
|
||||
add_library(Jemalloc::Jemalloc UNKNOWN IMPORTED)
|
||||
set_target_properties(Jemalloc::Jemalloc
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION "${Jemalloc_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Jemalloc_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
Jemalloc_INCLUDE_DIR
|
||||
Jemalloc_LIBRARY
|
||||
)
|
||||
@@ -11,18 +11,12 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
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 python3-devel # gdb
|
||||
expat-devel libipt-devel libbabeltrace-devel xz-devel python3-devel # gdb
|
||||
texinfo # gdb
|
||||
libcurl-devel # cmake
|
||||
curl # snappy
|
||||
readline-devel # cmake and llvm
|
||||
libffi-devel libxml2-devel perl-Digest-MD5 # llvm
|
||||
libedit-devel pcre-devel automake bison # swig
|
||||
file
|
||||
openssl-devel
|
||||
gmp-devel
|
||||
gperf
|
||||
patch
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
@@ -32,10 +26,10 @@ TOOLCHAIN_RUN_DEPS=(
|
||||
expat libipt libbabeltrace xz-libs python3 # for gdb
|
||||
readline # for cmake and llvm
|
||||
libffi libxml2 # for llvm
|
||||
openssl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make pkgconfig # build system
|
||||
curl wget # for downloading libs
|
||||
libuuid-devel java-11-openjdk # required by antlr
|
||||
@@ -101,12 +95,8 @@ install() {
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
fi
|
||||
yum install -y epel-release
|
||||
yum remove -y ius-release
|
||||
yum install -y \
|
||||
https://repo.ius.io/ius-release-el7.rpm
|
||||
yum update -y
|
||||
yum install -y wget python3 python3-pip
|
||||
yum install -y git224
|
||||
yum install -y wget git python3 python3-pip
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == libipt ]; then
|
||||
if ! yum list installed libipt >/dev/null 2>/dev/null; then
|
||||
|
||||
@@ -6,22 +6,16 @@ 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
|
||||
coreutils 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
|
||||
expat-devel 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=(
|
||||
@@ -31,7 +25,6 @@ TOOLCHAIN_RUN_DEPS=(
|
||||
expat libipt libbabeltrace xz-libs python36 # for gdb
|
||||
readline # for cmake and llvm
|
||||
libffi libxml2 # for llvm
|
||||
openssl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
@@ -117,10 +110,9 @@ install() {
|
||||
# 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
|
||||
dnf install -y http://mirror.centos.org/centos/8/PowerTools/x86_64/os/Packages/libbabeltrace-devel-1.5.4-3.el8.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -1,139 +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 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
|
||||
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
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz # used for archive unpacking
|
||||
zlib # zlib library used for all builds
|
||||
expat xz-libs python3 # 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
|
||||
python3-devel # for query modules
|
||||
openssl-devel
|
||||
libseccomp-devel
|
||||
python3 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 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 [ "$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
|
||||
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
|
||||
yum update -y
|
||||
yum install -y wget git python3 python3-pip
|
||||
for pkg in $1; do
|
||||
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
|
||||
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
|
||||
if [ "$pkg" == python3-virtualenv ]; then
|
||||
if [ -z ${SUDO_USER+x} ]; then # Running as root (e.g. Docker).
|
||||
pip3 install --user virtualenv
|
||||
else # Running using sudo.
|
||||
sudo -H -u "$SUDO_USER" bash -c "pip3 install --user virtualenv"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
yum install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
@@ -15,12 +15,6 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
libcurl4-openssl-dev # for cmake
|
||||
libreadline-dev # for cmake and llvm
|
||||
libffi-dev libxml2-dev # for llvm
|
||||
curl # snappy
|
||||
file # for libunwind
|
||||
libssl-dev # for libevent
|
||||
libgmp-dev # for gdb
|
||||
gperf # for proxygen
|
||||
git # for fbthrift
|
||||
libedit-dev libpcre3-dev automake bison # for swig
|
||||
)
|
||||
|
||||
@@ -32,7 +26,6 @@ TOOLCHAIN_RUN_DEPS=(
|
||||
libcurl4 # for cmake
|
||||
libreadline7 # for cmake and llvm
|
||||
libffi6 libxml2 # for llvm
|
||||
libssl-dev # for libevent
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
|
||||
@@ -16,12 +16,6 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
libreadline-dev # for cmake and llvm
|
||||
libffi-dev libxml2-dev # for llvm
|
||||
libedit-dev libpcre3-dev automake bison # for swig
|
||||
curl # snappy
|
||||
file # for libunwind
|
||||
libssl-dev # for libevent
|
||||
libgmp-dev
|
||||
gperf # for proxygen
|
||||
git # for fbthrift
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
@@ -33,7 +27,6 @@ TOOLCHAIN_RUN_DEPS=(
|
||||
file # for CPack
|
||||
libreadline8 # for cmake and llvm
|
||||
libffi7 libxml2 # for llvm
|
||||
libssl-dev # for libevent
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
|
||||
63
environment/os/debian-9.sh
Executable file
63
environment/os/debian-9.sh
Executable file
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
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 libbabeltrace-ctf-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
|
||||
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 libipt1 libbabeltrace1 libbabeltrace-ctf1 liblzma5 python3 # for gdb
|
||||
libcurl3 # for cmake
|
||||
libreadline7 # for cmake and llvm
|
||||
libffi6 libxml2 # for llvm
|
||||
)
|
||||
|
||||
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
|
||||
python3 python-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 nodejs zip unzip default-jdk-headless # for driver tests
|
||||
autoconf # for jemalloc code generation
|
||||
libtool # for protobuf code generation
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
check_all_dpkg "$1"
|
||||
}
|
||||
|
||||
install() {
|
||||
install_all_apt "$1"
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
@@ -16,12 +16,6 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
libcurl4-openssl-dev # cmake
|
||||
libreadline-dev # cmake and llvm
|
||||
libffi-dev libxml2-dev # llvm
|
||||
curl # snappy
|
||||
file
|
||||
git # for thrift
|
||||
libgmp-dev # for gdb
|
||||
gperf # for proxygen
|
||||
libssl-dev
|
||||
libedit-dev libpcre3-dev automake bison # swig
|
||||
)
|
||||
|
||||
@@ -33,7 +27,6 @@ TOOLCHAIN_RUN_DEPS=(
|
||||
libcurl4 # for cmake
|
||||
libreadline7 # for cmake and llvm
|
||||
libffi6 libxml2 # for llvm
|
||||
libssl-dev # for libevent
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
|
||||
@@ -15,12 +15,6 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
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
|
||||
)
|
||||
|
||||
@@ -32,7 +26,6 @@ TOOLCHAIN_RUN_DEPS=(
|
||||
libcurl4 # for cmake
|
||||
libreadline8 # for cmake and llvm
|
||||
libffi7 libxml2 # for llvm
|
||||
libssl-dev # for libevent
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
diff -ur a/folly/CMakeLists.txt b/folly/CMakeLists.txt
|
||||
--- a/folly/CMakeLists.txt 2021-12-12 23:10:42.000000000 +0100
|
||||
+++ b/folly/CMakeLists.txt 2022-02-03 15:19:41.349693134 +0100
|
||||
@@ -28,7 +28,6 @@
|
||||
)
|
||||
|
||||
add_subdirectory(experimental/exception_tracer)
|
||||
-add_subdirectory(logging/example)
|
||||
|
||||
if (PYTHON_EXTENSIONS)
|
||||
# Create tree of symbolic links in structure required for successful
|
||||
diff -ur a/folly/experimental/exception_tracer/ExceptionTracerLib.cpp b/folly/experimental/exception_tracer/ExceptionTracerLib.cpp
|
||||
--- a/folly/experimental/exception_tracer/ExceptionTracerLib.cpp 2021-12-12 23:10:42.000000000 +0100
|
||||
+++ b/folly/experimental/exception_tracer/ExceptionTracerLib.cpp 2022-02-03 15:19:11.003368891 +0100
|
||||
@@ -96,6 +96,7 @@
|
||||
#define __builtin_unreachable()
|
||||
#endif
|
||||
|
||||
+#if 0
|
||||
namespace __cxxabiv1 {
|
||||
|
||||
void __cxa_throw(
|
||||
@@ -154,5 +155,5 @@
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
-
|
||||
+#endif
|
||||
#endif // defined(__GLIBCXX__)
|
||||
diff -ur a/folly/Portability.h b/folly/Portability.h
|
||||
--- a/folly/Portability.h 2021-12-12 23:10:42.000000000 +0100
|
||||
+++ b/folly/Portability.h 2022-02-03 15:19:11.003368891 +0100
|
||||
@@ -566,7 +566,7 @@
|
||||
#define FOLLY_HAS_COROUTINES 0
|
||||
#elif (__cpp_coroutines >= 201703L || __cpp_impl_coroutine >= 201902L) && \
|
||||
(__has_include(<coroutine>) || __has_include(<experimental/coroutine>))
|
||||
-#define FOLLY_HAS_COROUTINES 1
|
||||
+#define FOLLY_HAS_COROUTINES 0
|
||||
// This is mainly to workaround bugs triggered by LTO, when stack allocated
|
||||
// variables in await_suspend end up on a coroutine frame.
|
||||
#define FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES FOLLY_NOINLINE
|
||||
@@ -1,11 +0,0 @@
|
||||
diff -ur a/cmake/proxygen-config.cmake.in b/cmake/proxygen-config.cmake.in
|
||||
--- a/cmake/proxygen-config.cmake.in 2021-12-13 02:37:05.000000000 +0100
|
||||
+++ b/cmake/proxygen-config.cmake.in 2022-01-27 17:14:28.284810621 +0100
|
||||
@@ -21,7 +21,6 @@
|
||||
find_dependency(folly)
|
||||
find_dependency(wangle)
|
||||
find_dependency(Fizz)
|
||||
-find_dependency(mvfst)
|
||||
# For now, anything that depends on Proxygen has to copy its FindZstd.cmake
|
||||
# and issue a `find_package(Zstd)`. Uncommenting this won't work because
|
||||
# this Zstd module exposes a library called `zstd`. The right fix is
|
||||
@@ -1,29 +0,0 @@
|
||||
diff -ur a/CMakeLists.txt b/CMakeLists.txt
|
||||
--- a/CMakeLists.txt 2021-05-05 00:53:34.000000000 +0200
|
||||
+++ b/CMakeLists.txt 2022-01-27 17:18:34.758302398 +0100
|
||||
@@ -52,9 +52,9 @@
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHs-c-")
|
||||
add_definitions(-D_HAS_EXCEPTIONS=0)
|
||||
|
||||
- # Disable RTTI.
|
||||
- string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
|
||||
+ # # Disable RTTI.
|
||||
+ # string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
+ # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
|
||||
else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
# Use -Wall for clang and gcc.
|
||||
if(NOT CMAKE_CXX_FLAGS MATCHES "-Wall")
|
||||
@@ -77,9 +77,9 @@
|
||||
string(REGEX REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||
|
||||
- # Disable RTTI.
|
||||
- string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
+ # # Disable RTTI.
|
||||
+ # string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
+ # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
|
||||
# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to make
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBEzEOZIBEACxg/IuXERlDB48JBWmF4NxNUuuup1IhJAJyFGFSKh3OGAO2Ard
|
||||
sNuRLjANsFXA7m7P5eTFcG+BoHHuAVYmKnI3PPZtHVLnUt4pGItPczQZ2BE1WpcI
|
||||
ayjGTBJeKItX3Npqg9D/odO9WWS1i3FQPVdrLn0YH37/BA66jeMQCRo7g7GLpaNf
|
||||
IrvYGsqTbxCwsmA37rpE7oyU4Yrf74HT091WBsRIoq/MelhbxTDMR8eu/dUGZQVc
|
||||
Kj3lN55RepwWwUUKyqarY0zMt4HkFJ7v7yRL+Cvzy92Ouv4Wf2FlhNtEs5LE4Tax
|
||||
W0PO5AEmUoKjX87SezQK0f652018b4u6Ex52cY7p+n5TII/UyoowH6+tY8UHo9yb
|
||||
fStrqgNE/mY2bhA6+AwCaOUGsFzVVPTbjtxL3HacUP/jlA1h78V8VTvTs5d55iG7
|
||||
jSqR9o05wje8rwNiXXK0xtiJahyNzL97Kn/DgPSqPIi45G+8nxWSPFM5eunBKRl9
|
||||
vAnsvwrdPRsR6YR3uMHTuVhQX9/CY891MHkaZJ6wydWtKt3yQwJLYqwo5d4DwnUX
|
||||
CduUwSKv+6RmtWI5ZmTQYOcBRcZyGKml9X9Q8iSbm6cnpFXmLrNQwCJN+D3SiYGc
|
||||
MtbltZo0ysPMa6Xj5xFaYqWk/BI4iLb2Gs+ByGo/+a0Eq4XYBMOpitNniQARAQAB
|
||||
tCdMYXNzZSBDb2xsaW4gPGxhc3NlLmNvbGxpbkB0dWthYW5pLm9yZz6JAlEEEwEK
|
||||
ADsCGwMCHgECF4AECwkIBwMVCggFFgIDAQAWIQQ2kMJAzlG0Zw0wrRw47nV9aRhG
|
||||
IAUCYEt9dQUJFxeR4wAKCRA47nV9aRhGIBNDEACxD6vJ+enZwe3IgkJh5JtLsC9b
|
||||
MWCQRlPW1EVMsg96Cb5Rtron1eN1pp1TlzENJu1/C7C/VEsr9WwOPg26Men7fNf/
|
||||
O21QM9IBWd/uB0Pu333WqKh92ESS5x9ST9DrG39nVGSPkQQBMuia72VrA+crPnwT
|
||||
/h/u1IN6/sff5VDIU24rUiqW2Npy733dANruj7Ny0scRXVPltnVdhqwPHt6qNjC1
|
||||
t+/cCnwHgW1BR1RYXBPpB42z/m29dL9rPrG0YPGWs2Bc+EATUICfEE6eIvwfciue
|
||||
IJTjKT9Y9DrogJC2AYFhjC7N04OKdCB2hFs4BjexJwr4X0GJO7LhFl03c951AsIE
|
||||
GHwrucRPB5bo2vmvQ8IvZn7CmtdUJzXv9JlyU6p+MIK1pz7TK6GgSOSffQIXZn6e
|
||||
nUPtm9mEwuncOfmW8/ODYPs1gCWYgyiFJx8h7eEu+M4MxHSFBs7MwXf/Ae2fSp+M
|
||||
P/p198qB8fC5oVBnF95qb0Qi0uc1D+Gb+gpBF+ymMb+s/VBOR3QWiym7AzBrJ62g
|
||||
UnbC9jMLGnSRI+7p7raUfMTgXr5/oQoBw7ExJVltSSRrim2YH/t4CV47mO6dR9J3
|
||||
1RtsTFIRNhz+07XPsETcuCV/dgqeC8fOFLt9MY17Sufhb1DcGy4urZBOIhXcpTV7
|
||||
vHVj5IYH5nYOT49NRYkCOAQTAQIAIgUCTMQ5kgIbAwYLCQgHAwIGFQgCCQoLBBYC
|
||||
AwECHgECF4AACgkQOO51fWkYRiAg4A/7BXKwoRaXrMbMPOW7vuVF7c2IKB2Yqzn1
|
||||
vLBCwuEHkqY237lDcXY4/5LR+1gcZ3Duw1n/BRSm0FBdvyX/JTWiWNSDUkKAO/0l
|
||||
T2Tg44YLrDT3bzwu8dbU9xQt6kH+SCOHvv5Oe4k79l5mro6fF3H1M0bN63x/YoFY
|
||||
ojy09D7/JptY82oR4f/VdKnfZLJcCViCb0wp8SD2NkDAudKg+K+7PD8HlTWklQQg
|
||||
TZdRXxVZKIJeU42aJDqnRbAhJd64YHyClhqut9F5LUmiP5qfLfNhkKDhNOwk2Blr
|
||||
BGBJkSd7wPyzcX4Mun/L6YspHjbeVMt9TD7HQlo+OOd2OjAHCx6pqwkXnzeLPEaE
|
||||
cPdQ1SHgrBViAxX3DNPubLP0Knw8XwFu96EuhHZgexE1W7bB4LFsJyXAc5k1PqPD
|
||||
CLsAauxmvI2OfI7opG/8wyxDvNgoPjG8fZNAgY0REqPC0JnTXChH31IxUmhNotH8
|
||||
tD3DDTZOHw05n5MwwUrEE9xiETVDfFQcMLfxZ9KLz+BC2g1t5LYublRgnCMNJzFg
|
||||
sNUMM02CphABzl/LCLnumr0eyQQ/weV4twEhLwSDmqLYHL0EdYW0Y3CnnU9vmYxQ
|
||||
cXKbstS71sEJJYBBmSBbf9GxkOY8BRNtwVwY0kPgxv1WqdVBiAFvfB+pyAsrax9B
|
||||
3UeB7ZSwRD6JAhwEEAEKAAYFAlS25GwACgkQlbYYGy0z6ew92Q//ZA9/6piQtoW4
|
||||
PwP/1DtWGyKU8hwR+9FG669iPk/dAG+yoEJtFMOUpg/FUFmCX8Bc4oEHsCVyLxKt
|
||||
DcCVUIRcYNSFi5hTZaBEbwsOlDT37gtlfIIu34hhHRccKaLnN/N9gNMNw8wGh9xg
|
||||
Q/KtxZwcbk/bZIlDkKTJkFBRAekdEGAFDWb/AZOy+LQxS8ZAh1eWkfV0i8opmK9k
|
||||
gPXtLE0WSsqtYyGs58z+BFE9NH3tEUwK6jSvtuLwQl4UrICNbKthcpb8WwH6UXzb
|
||||
q3QNSYVOpf/cqRdBJA6bvb/ku/xyKVL08lGmxD9v1b137R7mafDAFPTsvH2Mt/0V
|
||||
YuhtWav3r1Bl9QksDxt2DTS8wiWDUBetGqOVdcw7vBrXPEWDNBmxeJXsiJ7zJlR+
|
||||
9wrJOm6RV2+l1IPxu96EaPS+kTNBijKrhxb67bww8BTEWTd0wcdJmgWRkM8SIstp
|
||||
IKqd0L2TFYph2/NtrBhRg+DIEPJPpSTGsUMcCEXCZPQ+cIdlQKsWpk0tZ62DlvEl
|
||||
r7E+wgUSQolRfx5KrpZifiS2zQlhzdXv28CJhsVbLyw5fUAWUKIH/dCo5NKsNLk2
|
||||
Lc5DH9VWnFgxAAtW290FqeK/4ulMq7Vs1dQSwyHM2Ni3QqqeaiOrh8gbSY5CMLFN
|
||||
Y3HYRwuTYPa3AobsozCzBj0Zdf/6AFe5Ag0ETMQ5kgEQAL/FwKdjxgPxtSpgq1SM
|
||||
zgZtTTyLqhgGD3NZfadHWHYRIL38NDV3JeTA79Y2zj2dj7KQPDT+0aqeizTV2E3j
|
||||
P3iCQ53VOT4consBaQAgKexpptnS+T1DobtICFJ0GGzf0HRj6KO2zSOuOitWPWlU
|
||||
wbvX7M0LLI2+hqlx0jTPqbJFZ/Za6KTtbS6xdCPVUpUqYZQpokEZcwQmUp8Q+lGo
|
||||
JD2sNYCZyap63X/aAOgCGr2RXYddOH5e8vGzGW+mwtCv+WQ9Ay35mGqI5MqkbZd1
|
||||
Qbuv2b1647E/QEEucfRHVbJVKGGPpFMUJtcItyyIt5jo+r9CCL4Cs47dF/9/RNwu
|
||||
NvpvHXUyqMBQdWNZRMx4k/NGD/WviPi9m6mIMui6rOQsSOaqYdcUX4Nq2Orr3Oaz
|
||||
2JPQdUfeI23iot1vK8hxvUCQTV3HfJghizN6spVl0yQOKBiE8miJRgrjHilH3hTb
|
||||
xoo42xDkNAq+CQo3QAm1ibDxKCDq0RcWPjcCRAN/Q5MmpcodpdKkzV0yGIS4g7s5
|
||||
frVrgV/kox2r4/Yxsr8K909+4H82AjTKGX/BmsQFCTAqBk6p7I0zxjIqJ/w33TZB
|
||||
Q0Pn4r3WIlUPafzY6a9/LAvN1fHRxf9SpCByJsszD03Qu5f5TB8gthsdnVmTo7jj
|
||||
iordEKMtw2aEMLzdWWTQ/TNVABEBAAGJAjwEGAEKACYCGwwWIQQ2kMJAzlG0Zw0w
|
||||
rRw47nV9aRhGIAUCYEt9YAUJFxeRzgAKCRA47nV9aRhGIMLtD/9HuKM4pngImcuz
|
||||
YwzQmdv4j26YYyh4jVsKEmVWTiRcehEgUIlrWkCu3qzd5NK+RetS7kJ8MPnzEUfj
|
||||
YbpdC6yrF6n1mSrZZ4VJMkV2ev37bIgXM+Wp1mCAGbjNxQnjn9RabT/gjIqmGuRn
|
||||
AP7RsSeOSuO/gO9h2Pteciz23ussTilB+8cTooQEQQZe6Kv/zukvL+ccSehLHsZ7
|
||||
qVfRUAmtt8nFkXXE+s8jfLfhqstaI2/RJu5witaPcXM8Mnz2E95aASAbZy0eQot9
|
||||
0Pvf07n9yuC3tueTvzvlXx3h5U3yT44tIOmzANIQjay1TGdm+RBJ2ZYyhyLawlZ2
|
||||
NVUXXSp4QZZXPA0UWbF+pb7Q9cdKDNFVuvGBljuea0Yd0T2o+ibDq43HziX9ll+l
|
||||
SXk9mqvW1UcDOaxWrSsm1Gc1O9g3wqH5xHAhtY8GPh/7VgAawskPkmnlkMW6pYPy
|
||||
zibbeISJL1gd1jIT63y6aoVrtNoo+wYJm280ROflh4+5QOo6QJ+jm70fkXSG/qJ5
|
||||
a8/qCPTHkJc/rpkL6/TDQAJURi9RhDAC0gb40HtusbN1LZEA+i0cWTmYXap+DB4Y
|
||||
R4pApilpaG87M+VUokR4xpnx7vTb2MPa7Mdenvi9FEGnKXadmT8038vlfzz5GGUT
|
||||
MlVin9BQPTpdA+PpRiJvKJgVDeAFOg==
|
||||
=asTC
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
@@ -1405,76 +1405,38 @@ int mgp_must_abort(struct mgp_graph *graph);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Stream Source message API
|
||||
/// API for accessing specific data contained in a mgp_message
|
||||
/// used for defining transformation procedures.
|
||||
/// Not all methods are available for all stream sources
|
||||
/// so make sure that your transformation procedure can be used
|
||||
/// for a specific source, i.e. only valid methods are used.
|
||||
/// @name Kafka message API
|
||||
/// Currently the API below is for kafka only but in the future
|
||||
/// mgp_message and mgp_messages might be generic to support
|
||||
/// other streaming systems.
|
||||
///@{
|
||||
|
||||
/// A single Stream source message
|
||||
/// A single Kafka message
|
||||
struct mgp_message;
|
||||
|
||||
/// A list of Stream source messages
|
||||
/// A list of Kafka messages
|
||||
struct mgp_messages;
|
||||
|
||||
/// Stream source type.
|
||||
enum mgp_source_type {
|
||||
KAFKA,
|
||||
PULSAR,
|
||||
};
|
||||
|
||||
/// Get the type of the stream source that produced the message.
|
||||
enum mgp_error mgp_message_source_type(struct mgp_message *message, enum mgp_source_type *result);
|
||||
|
||||
/// Payload is not null terminated and not a string but rather a byte array.
|
||||
/// You need to call mgp_message_payload_size() first, to read the size of
|
||||
/// the payload.
|
||||
/// Supported stream sources:
|
||||
/// - Kafka
|
||||
/// - Pulsar
|
||||
/// Return MGP_ERROR_INVALID_ARGUMENT if the message is from an unsupported stream source.
|
||||
enum mgp_error mgp_message_payload(struct mgp_message *message, const char **result);
|
||||
|
||||
/// Get the payload size
|
||||
/// Supported stream sources:
|
||||
/// - Kafka
|
||||
/// - Pulsar
|
||||
/// Return MGP_ERROR_INVALID_ARGUMENT if the message is from an unsupported stream source.
|
||||
enum mgp_error mgp_message_payload_size(struct mgp_message *message, size_t *result);
|
||||
|
||||
/// Get the name of topic
|
||||
/// Supported stream sources:
|
||||
/// - Kafka
|
||||
/// - Pulsar
|
||||
/// Return MGP_ERROR_INVALID_ARGUMENT if the message is from an unsupported stream source.
|
||||
enum mgp_error mgp_message_topic_name(struct mgp_message *message, const char **result);
|
||||
|
||||
/// Get the key of mgp_message as a byte array
|
||||
/// Supported stream sources:
|
||||
/// - Kafka
|
||||
/// Return MGP_ERROR_INVALID_ARGUMENT if the message is from an unsupported stream source.
|
||||
enum mgp_error mgp_message_key(struct mgp_message *message, const char **result);
|
||||
|
||||
/// Get the key size of mgp_message
|
||||
/// Supported stream sources:
|
||||
/// - Kafka
|
||||
/// Return MGP_ERROR_INVALID_ARGUMENT if the message is from an unsupported stream source.
|
||||
enum mgp_error mgp_message_key_size(struct mgp_message *message, size_t *result);
|
||||
|
||||
/// Get the timestamp of mgp_message as a byte array
|
||||
/// Supported stream sources:
|
||||
/// - Kafka
|
||||
/// Return MGP_ERROR_INVALID_ARGUMENT if the message is from an unsupported stream source.
|
||||
enum mgp_error mgp_message_timestamp(struct mgp_message *message, int64_t *result);
|
||||
|
||||
/// Get the message offset from a message.
|
||||
/// Supported stream sources:
|
||||
/// - Kafka
|
||||
/// Return MGP_ERROR_INVALID_ARGUMENT if the message is from an unsupported stream source.
|
||||
enum mgp_error mgp_message_offset(struct mgp_message *message, int64_t *result);
|
||||
|
||||
/// Get the number of messages contained in the mgp_messages list
|
||||
/// Current implementation always returns without errors.
|
||||
enum mgp_error mgp_messages_size(struct mgp_messages *message, size_t *result);
|
||||
|
||||
@@ -1260,9 +1260,6 @@ class InvalidMessageError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
SOURCE_TYPE_KAFKA = _mgp.SOURCE_TYPE_KAFKA
|
||||
SOURCE_TYPE_PULSAR = _mgp.SOURCE_TYPE_PULSAR
|
||||
|
||||
class Message:
|
||||
"""Represents a message from a stream."""
|
||||
__slots__ = ('_message',)
|
||||
@@ -1283,73 +1280,26 @@ class Message:
|
||||
"""Return True if `self` is in valid context and may be used."""
|
||||
return self._message.is_valid()
|
||||
|
||||
def source_type(self) -> str:
|
||||
"""
|
||||
Supported in all stream sources
|
||||
|
||||
Raise InvalidArgumentError if the message is from an unsupported stream source.
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidMessageError()
|
||||
return self._message.source_type()
|
||||
|
||||
def payload(self) -> bytes:
|
||||
"""
|
||||
Supported stream sources:
|
||||
- Kafka
|
||||
- Pulsar
|
||||
|
||||
Raise InvalidArgumentError if the message is from an unsupported stream source.
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidMessageError()
|
||||
return self._message.payload()
|
||||
|
||||
def topic_name(self) -> str:
|
||||
"""
|
||||
Supported stream sources:
|
||||
- Kafka
|
||||
- Pulsar
|
||||
|
||||
Raise InvalidArgumentError if the message is from an unsupported stream source.
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidMessageError()
|
||||
return self._message.topic_name()
|
||||
|
||||
def key(self) -> bytes:
|
||||
"""
|
||||
Supported stream sources:
|
||||
- Kafka
|
||||
|
||||
Raise InvalidArgumentError if the message is from an unsupported stream source.
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidMessageError()
|
||||
return self._message.key()
|
||||
|
||||
def timestamp(self) -> int:
|
||||
"""
|
||||
Supported stream sources:
|
||||
- Kafka
|
||||
|
||||
Raise InvalidArgumentError if the message is from an unsupported stream source.
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidMessageError()
|
||||
return self._message.timestamp()
|
||||
|
||||
def offset(self) -> int:
|
||||
"""
|
||||
Supported stream sources:
|
||||
- Kafka
|
||||
|
||||
Raise InvalidArgumentError if the message is from an unsupported stream source.
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidMessageError()
|
||||
return self._message.offset()
|
||||
|
||||
|
||||
class InvalidMessagesError(Exception):
|
||||
"""Signals using a messages instance outside of the registered transformation."""
|
||||
|
||||
59
init
59
init
@@ -10,8 +10,6 @@ function print_help () {
|
||||
echo -e "Check for missing packages and setup the project.\n"
|
||||
echo "Optional arguments:"
|
||||
echo -e " -h\tdisplay this help and exit"
|
||||
echo -e " --without-libs-setup\tskip the step for setting up libs"
|
||||
echo -e " --wsl-quicklisp-proxy \"host:port\"\tquicklist HTTP proxy (this flag + HTTP proxy are required on WSL)"
|
||||
}
|
||||
|
||||
function setup_virtualenv () {
|
||||
@@ -32,36 +30,21 @@ function setup_virtualenv () {
|
||||
popd > /dev/null
|
||||
}
|
||||
|
||||
wsl_quicklisp_proxy=""
|
||||
setup_libs=true
|
||||
if [[ $# -eq 1 && "$1" == "-h" ]]; then
|
||||
if [[ $# -gt 1 ]]; then
|
||||
print_help
|
||||
exit 0
|
||||
else
|
||||
while(($#)); do
|
||||
case "$1" in
|
||||
--wsl-quicklisp-proxy)
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "Missing proxy URL"
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
wsl_quicklisp_proxy=":proxy \"http://$1/\""
|
||||
shift
|
||||
;;
|
||||
--without-libs-setup)
|
||||
shift
|
||||
setup_libs=false
|
||||
;;
|
||||
*)
|
||||
# unknown option
|
||||
echo "Invalid argument provided: $1"
|
||||
print_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
exit 1
|
||||
elif [[ $# -eq 1 ]]; then
|
||||
case "$1" in
|
||||
-h)
|
||||
print_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
# unknown option
|
||||
print_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
DISTRO=$(operating_system)
|
||||
@@ -83,7 +66,7 @@ if [[ ! -f "${quicklisp_install_dir}/setup.lisp" ]]; then
|
||||
echo \
|
||||
"
|
||||
(load \"${DIR}/quicklisp.lisp\")
|
||||
(quicklisp-quickstart:install $wsl_quicklisp_proxy :path \"${quicklisp_install_dir}\")
|
||||
(quicklisp-quickstart:install :path \"${quicklisp_install_dir}\")
|
||||
" | sbcl --script || exit 1
|
||||
rm -rf quicklisp.lisp || exit 1
|
||||
fi
|
||||
@@ -97,13 +80,11 @@ echo \
|
||||
(ql:quickload '(:lcp :lcp/test) :silent t)
|
||||
" | sbcl --script
|
||||
|
||||
if [[ "$setup_libs" == "true" ]]; then
|
||||
# Setup libs (download).
|
||||
cd libs
|
||||
./cleanup.sh
|
||||
./setup.sh
|
||||
cd ..
|
||||
fi
|
||||
# Setup libs (download).
|
||||
cd libs
|
||||
./cleanup.sh
|
||||
./setup.sh
|
||||
cd ..
|
||||
|
||||
# setup gql_behave dependencies
|
||||
setup_virtualenv tests/gql_behave
|
||||
|
||||
@@ -8,15 +8,6 @@ if (NPROC EQUAL 0)
|
||||
set(NPROC 1)
|
||||
endif()
|
||||
|
||||
find_package(Boost 1.78 REQUIRED)
|
||||
find_package(BZip2 1.0.6 REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
set(GFLAGS_NOTHREADS OFF)
|
||||
find_package(gflags REQUIRED)
|
||||
find_package(fmt 8.0.1)
|
||||
find_package(Jemalloc REQUIRED)
|
||||
find_package(ZLIB 1.2.11 REQUIRED)
|
||||
|
||||
set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# convenience functions
|
||||
@@ -111,12 +102,17 @@ import_external_library(antlr4 STATIC
|
||||
|
||||
# Setup google benchmark.
|
||||
import_external_library(benchmark STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark/${CMAKE_INSTALL_LIBDIR}/libbenchmark.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark/lib/libbenchmark.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark/include
|
||||
# Skip testing. The tests don't compile with Clang 8.
|
||||
CMAKE_ARGS -DBENCHMARK_ENABLE_TESTING=OFF)
|
||||
|
||||
include(FetchContent)
|
||||
# setup fmt format
|
||||
import_external_library(fmt STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fmt/${CMAKE_INSTALL_LIBDIR}/libfmt.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fmt/include
|
||||
# Skip testing.
|
||||
CMAKE_ARGS -DFMT_TEST=OFF)
|
||||
|
||||
# setup rapidcheck (it cannot be external, since it doesn't have install
|
||||
# target)
|
||||
@@ -143,19 +139,58 @@ import_library(gtest_main STATIC ${GTEST_MAIN_LIBRARY} ${GTEST_INCLUDE_DIR} gtes
|
||||
import_library(gmock STATIC ${GMOCK_LIBRARY} ${GTEST_INCLUDE_DIR} gtest-proj)
|
||||
import_library(gmock_main STATIC ${GMOCK_MAIN_LIBRARY} ${GTEST_INCLUDE_DIR} gtest-proj)
|
||||
|
||||
# setup google flags
|
||||
set(GFLAGS_NO_FILENAMES "0")
|
||||
if ("${CMAKE_BUILD_TYPE}" MATCHES "^(R|r)(E|e)(L|l).+")
|
||||
set(GFLAGS_NO_FILENAMES "1")
|
||||
endif()
|
||||
|
||||
# setup google flags
|
||||
import_external_library(gflags STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/gflags/lib/libgflags.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/gflags/include
|
||||
# Not needed, since gflags is C++ only.
|
||||
NO_C_COMPILER
|
||||
# Don't register installation in ~/.cmake
|
||||
CMAKE_ARGS -DREGISTER_INSTALL_PREFIX=OFF
|
||||
-DBUILD_gflags_nothreads_LIB=OFF
|
||||
-DGFLAGS_NO_FILENAMES=${GFLAGS_NO_FILENAMES})
|
||||
|
||||
# Setup cppitertools
|
||||
import_header_library(cppitertools ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# Setup json
|
||||
import_header_library(json ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# Setup bzip2
|
||||
import_external_library(bzip2 STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bzip2/libbz2.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bzip2
|
||||
# bzip2's Makefile has -g CFLAG which is redundant
|
||||
CONFIGURE_COMMAND sed -i "s/-Wall -Winline -O2 -g/-Wall -Winline -O2/g" ${CMAKE_CURRENT_SOURCE_DIR}/bzip2/Makefile
|
||||
BUILD_COMMAND make -C ${CMAKE_CURRENT_SOURCE_DIR}/bzip2
|
||||
CC=${CMAKE_C_COMPILER}
|
||||
CXX=${CMAKE_CXX_COMPILER}
|
||||
INSTALL_COMMAND true)
|
||||
|
||||
# Setup zlib
|
||||
set(ZLIB_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/zlib)
|
||||
set(ZLIB_LIBRARIES ${ZLIB_ROOT}/lib/libz.a)
|
||||
set(ZLIB_INCLUDE_DIRS ${ZLIB_ROOT}/include)
|
||||
import_external_library(zlib STATIC
|
||||
${ZLIB_LIBRARIES}
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
CMAKE_ARGS -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=true
|
||||
BUILD_COMMAND $(MAKE) zlibstatic)
|
||||
|
||||
# Setup RocksDB
|
||||
import_external_library(rocksdb STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rocksdb/lib/librocksdb.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rocksdb/include
|
||||
CMAKE_ARGS -DUSE_RTTI=ON
|
||||
DEPENDS gflags-proj
|
||||
CMAKE_ARGS -Dgflags_DIR=${CMAKE_CURRENT_SOURCE_DIR}/gflags/lib/cmake/gflags
|
||||
-DUSE_RTTI=ON
|
||||
-DWITH_TESTS=OFF
|
||||
-DGFLAGS_NOTHREADS=OFF
|
||||
-DCMAKE_INSTALL_LIBDIR=lib
|
||||
-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=true
|
||||
BUILD_COMMAND $(MAKE) rocksdb)
|
||||
@@ -190,11 +225,12 @@ add_external_project(mgconsole
|
||||
add_custom_target(mgconsole DEPENDS mgconsole-proj)
|
||||
|
||||
# Setup spdlog
|
||||
set(SPDLOG_FMT_EXTERNAL ON)
|
||||
FetchContent_Declare(spdlog
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spdlog)
|
||||
import_external_library(spdlog STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spdlog/${CMAKE_INSTALL_LIBDIR}/libspdlog.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spdlog/include
|
||||
BUILD_COMMAND $(MAKE) spdlog)
|
||||
|
||||
FetchContent_MakeAvailable(spdlog)
|
||||
include(jemalloc.cmake)
|
||||
|
||||
# Setup librdkafka.
|
||||
import_external_library(librdkafka STATIC
|
||||
@@ -203,13 +239,12 @@ import_external_library(librdkafka STATIC
|
||||
CMAKE_ARGS -DRDKAFKA_BUILD_STATIC=ON
|
||||
-DRDKAFKA_BUILD_EXAMPLES=OFF
|
||||
-DRDKAFKA_BUILD_TESTS=OFF
|
||||
-DWITH_ZSTD=OFF
|
||||
-DENABLE_LZ4_EXT=OFF
|
||||
-DCMAKE_INSTALL_LIBDIR=lib
|
||||
-DWITH_SSL=ON
|
||||
# If we want SASL, we need to install it on build machines
|
||||
-DWITH_SASL=OFF)
|
||||
target_link_libraries(librdkafka INTERFACE ${OPENSSL_LIBRARIES} ZLIB::ZLIB)
|
||||
target_link_libraries(librdkafka INTERFACE ${OPENSSL_LIBRARIES} zlib)
|
||||
|
||||
import_library(librdkafka++ STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/librdkafka/lib/librdkafka++.a
|
||||
@@ -224,6 +259,8 @@ import_external_library(protobuf STATIC
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND true)
|
||||
|
||||
set(BOOST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/boost/lib)
|
||||
|
||||
import_external_library(pulsar STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pulsar/pulsar-client-cpp/lib/libpulsarwithdeps.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pulsar/install/include
|
||||
@@ -240,18 +277,10 @@ import_external_library(pulsar STATIC
|
||||
-DBOOST_ROOT=${BOOST_ROOT}
|
||||
-DCMAKE_PREFIX_PATH=${PROTOBUF_ROOT}
|
||||
-DProtobuf_INCLUDE_DIRS=${PROTOBUF_ROOT}/include
|
||||
-DZLIB_LIBRARIES=${ZLIB_LIBRARIES}
|
||||
-DZLIB_INCLUDE_DIRS=${ZLIB_INCLUDE_DIRS}
|
||||
-DBUILD_PYTHON_WRAPPER=OFF
|
||||
-DBUILD_PERF_TOOLS=OFF
|
||||
-DUSE_LOG4CXX=OFF
|
||||
BUILD_COMMAND $(MAKE) pulsarStaticWithDeps)
|
||||
add_dependencies(pulsar-proj protobuf)
|
||||
|
||||
if (${MG_ARCH} STREQUAL "ARM64")
|
||||
set(MG_LIBRDTSC_CMAKE_ARGS -DLIBRDTSC_ARCH_x86=OFF -DLIBRDTSC_ARCH_ARM64=ON)
|
||||
endif()
|
||||
|
||||
import_external_library(librdtsc STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/librdtsc/lib/librdtsc.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/librdtsc/include
|
||||
CMAKE_ARGS ${MG_LIBRDTSC_CMAKE_ARGS}
|
||||
BUILD_COMMAND $(MAKE) rdtsc)
|
||||
add_dependencies(pulsar-proj protobuf zlib)
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
diff --git a/runtime/Cpp/runtime/CMakeLists.txt b/runtime/Cpp/runtime/CMakeLists.txt
|
||||
index a8503bb..11362cf 100644
|
||||
--- a/runtime/Cpp/runtime/CMakeLists.txt
|
||||
+++ b/runtime/Cpp/runtime/CMakeLists.txt
|
||||
@@ -5,8 +5,8 @@ set(THIRDPARTY_DIR ${CMAKE_BINARY_DIR}/runtime/thirdparty)
|
||||
set(UTFCPP_DIR ${THIRDPARTY_DIR}/utfcpp)
|
||||
ExternalProject_Add(
|
||||
utfcpp
|
||||
- GIT_REPOSITORY "git://github.com/nemtrif/utfcpp"
|
||||
- GIT_TAG "v3.1.1"
|
||||
+ GIT_REPOSITORY "https://github.com/nemtrif/utfcpp"
|
||||
+ GIT_TAG "v3.2.1"
|
||||
SOURCE_DIR ${UTFCPP_DIR}
|
||||
UPDATE_DISCONNECTED 1
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${UTFCPP_DIR}/install -Dgtest_force_shared_crt=ON
|
||||
@@ -118,7 +118,7 @@ set_target_properties(antlr4_static
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${LIB_OUTPUT_DIR}
|
||||
COMPILE_FLAGS "${disabled_compile_warnings} ${extra_static_compile_flags}")
|
||||
|
||||
-install(TARGETS antlr4_shared
|
||||
+install(TARGETS antlr4_shared OPTIONAL
|
||||
DESTINATION lib
|
||||
EXPORT antlr4-targets)
|
||||
install(TARGETS antlr4_static
|
||||
diff --git a/runtime/Cpp/runtime/src/support/Any.h b/runtime/Cpp/runtime/src/support/Any.h
|
||||
index 468db98..65a473b 100644
|
||||
--- a/runtime/Cpp/runtime/src/support/Any.h
|
||||
+++ b/runtime/Cpp/runtime/src/support/Any.h
|
||||
@@ -122,12 +122,12 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
- template<int N = 0, typename std::enable_if<N == N && std::is_nothrow_copy_constructible<T>::value, int>::type = 0>
|
||||
+ template<int N = 0, typename std::enable_if<N == N && std::is_copy_constructible<T>::value, int>::type = 0>
|
||||
Base* clone() const {
|
||||
return new Derived<T>(value);
|
||||
}
|
||||
|
||||
- template<int N = 0, typename std::enable_if<N == N && !std::is_nothrow_copy_constructible<T>::value, int>::type = 0>
|
||||
+ template<int N = 0, typename std::enable_if<N == N && !std::is_copy_constructible<T>::value, int>::type = 0>
|
||||
Base* clone() const {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index ee9b58c..31359a9 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -48,7 +48,7 @@ option(LIBRDTSC_USE_PMU "Enables PMU usage on ARM platforms" OFF)
|
||||
# | Library Build and Install Properties |
|
||||
# +--------------------------------------------------------+
|
||||
|
||||
-add_library(rdtsc SHARED
|
||||
+add_library(rdtsc
|
||||
src/cycles.c
|
||||
src/common_timer.c
|
||||
src/timer.c
|
||||
@@ -72,15 +72,6 @@ target_include_directories(rdtsc
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
-# Install directory changes depending on build mode
|
||||
-if (CMAKE_BUILD_TYPE MATCHES "^[Dd]ebug")
|
||||
- # During debug, the library will be installed into a local directory
|
||||
- set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/_install CACHE PATH "" FORCE)
|
||||
-else ()
|
||||
- # This will install in /usr/lib and /usr/include
|
||||
- set(CMAKE_INSTALL_PREFIX /usr CACHE PATH "" FORCE)
|
||||
-endif ()
|
||||
-
|
||||
# Specifying what to export when installing (GNUInstallDirs required)
|
||||
install(TARGETS rdtsc
|
||||
EXPORT librstsc-config
|
||||
@@ -60,7 +60,7 @@ index 508e4f4..87c5f2a 100644
|
||||
decompressed.bytesWritten(uncompressedSize);
|
||||
decoded = decompressed;
|
||||
diff --git a/pulsar-client-cpp/lib/lz4/lz4.c b/pulsar-client-cpp/lib/lz4/lz4.c
|
||||
index 08cf6b5..07d3e01 100644
|
||||
index 08cf6b5..d74c287 100644
|
||||
--- a/pulsar-client-cpp/lib/lz4/lz4.c
|
||||
+++ b/pulsar-client-cpp/lib/lz4/lz4.c
|
||||
@@ -45,7 +45,7 @@
|
||||
@@ -174,7 +174,7 @@ index 08cf6b5..07d3e01 100644
|
||||
|
||||
#define KB *(1 <<10)
|
||||
#define MB *(1 <<20)
|
||||
@@ -239,15 +239,15 @@ static const int LZ4_minLength = (MFLIMIT+1);
|
||||
@@ -239,7 +239,7 @@ static const int LZ4_minLength = (MFLIMIT+1);
|
||||
/**************************************
|
||||
* Common Utils
|
||||
**************************************/
|
||||
@@ -183,10 +183,9 @@ index 08cf6b5..07d3e01 100644
|
||||
|
||||
|
||||
/**************************************
|
||||
* Common functions
|
||||
@@ -247,7 +247,7 @@ static const int LZ4_minLength = (MFLIMIT+1);
|
||||
**************************************/
|
||||
-static unsigned LZ4_NbCommonBytes (register size_t val)
|
||||
+static unsigned PULSAR_LZ4_NbCommonBytes (register size_t val)
|
||||
static unsigned LZ4_NbCommonBytes (register size_t val)
|
||||
{
|
||||
- if (LZ4_isLittleEndian())
|
||||
+ if (PULSAR_LZ4_isLittleEndian())
|
||||
@@ -207,8 +206,7 @@ index 08cf6b5..07d3e01 100644
|
||||
- size_t diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
|
||||
+ size_t diff = PULSAR_LZ4_read_ARCH(pMatch) ^ PULSAR_LZ4_read_ARCH(pIn);
|
||||
if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
|
||||
- pIn += LZ4_NbCommonBytes(diff);
|
||||
+ pIn += PULSAR_LZ4_NbCommonBytes(diff);
|
||||
pIn += LZ4_NbCommonBytes(diff);
|
||||
return (unsigned)(pIn - pStart);
|
||||
}
|
||||
|
||||
@@ -219,13 +217,11 @@ index 08cf6b5..07d3e01 100644
|
||||
if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;
|
||||
return (unsigned)(pIn - pStart);
|
||||
}
|
||||
@@ -339,8 +339,8 @@ static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLi
|
||||
#define HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
|
||||
@@ -340,7 +340,7 @@ static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLi
|
||||
#define HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
|
||||
|
||||
-static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
|
||||
static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
|
||||
-static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */
|
||||
+static const int PULSAR_LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
|
||||
+static const U32 PULSAR_LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */
|
||||
|
||||
|
||||
@@ -335,13 +331,11 @@ index 08cf6b5..07d3e01 100644
|
||||
|
||||
const BYTE* ip = (const BYTE*) source;
|
||||
const BYTE* base;
|
||||
@@ -482,12 +482,12 @@ FORCE_INLINE int LZ4_compress_generic(
|
||||
lowLimit = (const BYTE*)source;
|
||||
@@ -483,11 +483,11 @@ FORCE_INLINE int LZ4_compress_generic(
|
||||
break;
|
||||
}
|
||||
- if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
|
||||
if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
|
||||
- if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
|
||||
+ if ((tableType == byU16) && (inputSize>=PULSAR_LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
|
||||
+ if (inputSize<PULSAR_LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
|
||||
|
||||
/* First Byte */
|
||||
@@ -473,9 +467,8 @@ index 08cf6b5..07d3e01 100644
|
||||
- if (maxOutputSize >= LZ4_compressBound(inputSize))
|
||||
+ if (maxOutputSize >= PULSAR_LZ4_compressBound(inputSize))
|
||||
{
|
||||
- if (inputSize < LZ4_64Klimit)
|
||||
if (inputSize < LZ4_64Klimit)
|
||||
- return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
|
||||
+ if (inputSize < PULSAR_LZ4_64Klimit)
|
||||
+ return PULSAR_LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
|
||||
else
|
||||
- return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
|
||||
@@ -483,9 +476,8 @@ index 08cf6b5..07d3e01 100644
|
||||
}
|
||||
else
|
||||
{
|
||||
- if (inputSize < LZ4_64Klimit)
|
||||
if (inputSize < LZ4_64Klimit)
|
||||
- return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
|
||||
+ if (inputSize < PULSAR_LZ4_64Klimit)
|
||||
+ return PULSAR_LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
|
||||
else
|
||||
- return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
|
||||
@@ -534,9 +526,8 @@ index 08cf6b5..07d3e01 100644
|
||||
- LZ4_resetStream(&ctx);
|
||||
+ PULSAR_LZ4_resetStream(&ctx);
|
||||
|
||||
- if (inputSize < LZ4_64Klimit)
|
||||
if (inputSize < LZ4_64Klimit)
|
||||
- return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
|
||||
+ if (inputSize < PULSAR_LZ4_64Klimit)
|
||||
+ return PULSAR_LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
|
||||
else
|
||||
- return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
|
||||
@@ -553,13 +544,11 @@ index 08cf6b5..07d3e01 100644
|
||||
void* const ctx,
|
||||
const char* const src,
|
||||
char* const dst,
|
||||
@@ -747,13 +747,13 @@ static int LZ4_compress_destSize_generic(
|
||||
/* Init conditions */
|
||||
@@ -748,12 +748,12 @@ static int LZ4_compress_destSize_generic(
|
||||
if (targetDstSize < 1) return 0; /* Impossible to store anything */
|
||||
if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
|
||||
- if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
|
||||
if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
|
||||
- if (*srcSizePtr<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
|
||||
+ if ((tableType == byU16) && (*srcSizePtr>=PULSAR_LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
|
||||
+ if (*srcSizePtr<PULSAR_LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
|
||||
|
||||
/* First Byte */
|
||||
@@ -666,9 +655,8 @@ index 08cf6b5..07d3e01 100644
|
||||
}
|
||||
else
|
||||
{
|
||||
- if (*srcSizePtr < LZ4_64Klimit)
|
||||
if (*srcSizePtr < LZ4_64Klimit)
|
||||
- return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16);
|
||||
+ if (*srcSizePtr < PULSAR_LZ4_64Klimit)
|
||||
+ return PULSAR_LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16);
|
||||
else
|
||||
- return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, LZ4_64bits() ? byU32 : byPtr);
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 6761929..6a369af 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -220,6 +220,7 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -momit-leaf-frame-pointer")
|
||||
endif()
|
||||
endif()
|
||||
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-copy -Wno-unused-but-set-variable")
|
||||
endif()
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
@@ -997,7 +998,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
|
||||
if(ROCKSDB_BUILD_SHARED)
|
||||
install(
|
||||
- TARGETS ${ROCKSDB_SHARED_LIB}
|
||||
+ TARGETS ${ROCKSDB_SHARED_LIB} OPTIONAL
|
||||
EXPORT RocksDBTargets
|
||||
COMPONENT runtime
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
@@ -107,21 +107,26 @@ declare -A primary_urls=(
|
||||
["antlr4-code"]="http://$local_cache_host/git/antlr4.git"
|
||||
["antlr4-generator"]="http://$local_cache_host/file/antlr-4.9.2-complete.jar"
|
||||
["cppitertools"]="http://$local_cache_host/git/cppitertools.git"
|
||||
["fmt"]="http://$local_cache_host/git/fmt.git"
|
||||
["rapidcheck"]="http://$local_cache_host/git/rapidcheck.git"
|
||||
["gbenchmark"]="http://$local_cache_host/git/benchmark.git"
|
||||
["gtest"]="http://$local_cache_host/git/googletest.git"
|
||||
["gflags"]="http://$local_cache_host/git/gflags.git"
|
||||
["libbcrypt"]="http://$local_cache_host/git/libbcrypt.git"
|
||||
["bzip2"]="http://$local_cache_host/git/bzip2.git"
|
||||
["zlib"]="http://$local_cache_host/git/zlib.git"
|
||||
["rocksdb"]="http://$local_cache_host/git/rocksdb.git"
|
||||
["mgclient"]="http://$local_cache_host/git/mgclient.git"
|
||||
["pymgclient"]="http://$local_cache_host/git/pymgclient.git"
|
||||
["mgconsole"]="http://$local_cache_host/git/mgconsole.git"
|
||||
["spdlog"]="http://$local_cache_host/git/spdlog"
|
||||
["nlohmann"]="http://$local_cache_host/file/nlohmann/json/4f8fba14066156b73f1189a2b8bd568bde5284c5/single_include/nlohmann/json.hpp"
|
||||
["jemalloc"]="http://$local_cache_host/git/jemalloc.git"
|
||||
["nlohmann"]="http://$local_cache_host/file/nlohmann/json/b3e5cb7f20dcc5c806e418df34324eca60d17d4e/single_include/nlohmann/json.hpp"
|
||||
["neo4j"]="http://$local_cache_host/file/neo4j-community-3.2.3-unix.tar.gz"
|
||||
["librdkafka"]="http://$local_cache_host/git/librdkafka.git"
|
||||
["protobuf"]="http://$local_cache_host/git/protobuf.git"
|
||||
["pulsar"]="http://$local_cache_host/git/pulsar.git"
|
||||
["librdtsc"]="http://$local_cache_host/git/librdtsc.git"
|
||||
["boost"]="https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.gz"
|
||||
["pulsar"]="https://github.com/apache/pulsar.git"
|
||||
)
|
||||
|
||||
# The goal of secondary urls is to have links to the "source of truth" of
|
||||
@@ -132,21 +137,26 @@ declare -A secondary_urls=(
|
||||
["antlr4-code"]="https://github.com/antlr/antlr4.git"
|
||||
["antlr4-generator"]="http://www.antlr.org/download/antlr-4.9.2-complete.jar"
|
||||
["cppitertools"]="https://github.com/ryanhaining/cppitertools.git"
|
||||
["fmt"]="https://github.com/fmtlib/fmt.git"
|
||||
["rapidcheck"]="https://github.com/emil-e/rapidcheck.git"
|
||||
["gbenchmark"]="https://github.com/google/benchmark.git"
|
||||
["gtest"]="https://github.com/google/googletest.git"
|
||||
["gflags"]="https://github.com/memgraph/gflags.git"
|
||||
["libbcrypt"]="https://github.com/rg3/libbcrypt"
|
||||
["bzip2"]="https://github.com/VFR-maniac/bzip2"
|
||||
["zlib"]="https://github.com/madler/zlib.git"
|
||||
["rocksdb"]="https://github.com/facebook/rocksdb.git"
|
||||
["mgclient"]="https://github.com/memgraph/mgclient.git"
|
||||
["pymgclient"]="https://github.com/memgraph/pymgclient.git"
|
||||
["mgconsole"]="http://github.com/memgraph/mgconsole.git"
|
||||
["spdlog"]="https://github.com/gabime/spdlog"
|
||||
["nlohmann"]="https://raw.githubusercontent.com/nlohmann/json/4f8fba14066156b73f1189a2b8bd568bde5284c5/single_include/nlohmann/json.hpp"
|
||||
["jemalloc"]="https://github.com/jemalloc/jemalloc.git"
|
||||
["nlohmann"]="https://raw.githubusercontent.com/nlohmann/json/b3e5cb7f20dcc5c806e418df34324eca60d17d4e/single_include/nlohmann/json.hpp"
|
||||
["neo4j"]="https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/neo4j-community-3.2.3-unix.tar.gz"
|
||||
["librdkafka"]="https://github.com/edenhill/librdkafka.git"
|
||||
["protobuf"]="https://github.com/protocolbuffers/protobuf.git"
|
||||
["boost"]="https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.gz"
|
||||
["pulsar"]="https://github.com/apache/pulsar.git"
|
||||
["librdtsc"]="https://github.com/gabrieleara/librdtsc.git"
|
||||
)
|
||||
|
||||
# antlr
|
||||
@@ -154,26 +164,39 @@ file_get_try_double "${primary_urls[antlr4-generator]}" "${secondary_urls[antlr4
|
||||
|
||||
antlr4_tag="4.9.2" # v4.9.2
|
||||
repo_clone_try_double "${primary_urls[antlr4-code]}" "${secondary_urls[antlr4-code]}" "antlr4" "$antlr4_tag" true
|
||||
pushd antlr4
|
||||
git apply ../antlr4.patch
|
||||
popd
|
||||
# remove shared library from install dependencies
|
||||
sed -i 's/install(TARGETS antlr4_shared/install(TARGETS antlr4_shared OPTIONAL/' antlr4/runtime/Cpp/runtime/CMakeLists.txt
|
||||
# fix issue https://github.com/antlr/antlr4/issues/3194 - should update Antlr commit once the PR related to the issue gets merged
|
||||
sed -i 's/std::is_nothrow_copy_constructible/std::is_copy_constructible/' antlr4/runtime/Cpp/runtime/src/support/Any.h
|
||||
# replace the utf8cpp version which is used because the older one uses gtest that doesn't
|
||||
# compile with the newer compilers because of uninitialized variable
|
||||
# the newer utf8cpp switched to ftest
|
||||
sed -i 's/v3.1.1/v3.2.1/' antlr4/runtime/Cpp/runtime/CMakeLists.txt
|
||||
|
||||
# cppitertools v2.0 2019-12-23
|
||||
cppitertools_ref="cb3635456bdb531121b82b4d2e3afc7ae1f56d47"
|
||||
repo_clone_try_double "${primary_urls[cppitertools]}" "${secondary_urls[cppitertools]}" "cppitertools" "$cppitertools_ref"
|
||||
|
||||
# fmt
|
||||
fmt_tag="7.1.3" # (2020-11-25)
|
||||
repo_clone_try_double "${primary_urls[fmt]}" "${secondary_urls[fmt]}" "fmt" "$fmt_tag" true
|
||||
|
||||
# rapidcheck
|
||||
rapidcheck_tag="7bc7d302191a4f3d0bf005692677126136e02f60" # (2020-05-04)
|
||||
repo_clone_try_double "${primary_urls[rapidcheck]}" "${secondary_urls[rapidcheck]}" "rapidcheck" "$rapidcheck_tag"
|
||||
|
||||
# google benchmark
|
||||
benchmark_tag="v1.6.0"
|
||||
benchmark_tag="v1.1.0"
|
||||
repo_clone_try_double "${primary_urls[gbenchmark]}" "${secondary_urls[gbenchmark]}" "benchmark" "$benchmark_tag" true
|
||||
|
||||
# google test
|
||||
googletest_tag="release-1.8.0"
|
||||
repo_clone_try_double "${primary_urls[gtest]}" "${secondary_urls[gtest]}" "googletest" "$googletest_tag" true
|
||||
|
||||
# google flags
|
||||
gflags_tag="b37ceb03a0e56c9f15ce80409438a555f8a67b7c" # custom version (May 6, 2017)
|
||||
repo_clone_try_double "${primary_urls[gflags]}" "${secondary_urls[gflags]}" "gflags" "$gflags_tag"
|
||||
|
||||
# libbcrypt
|
||||
libbcrypt_tag="8aa32ad94ebe06b76853b0767c910c9fbf7ccef4" # custom version (Dec 16, 2016)
|
||||
repo_clone_try_double "${primary_urls[libbcrypt]}" "${secondary_urls[libbcrypt]}" "libbcrypt" "$libbcrypt_tag"
|
||||
@@ -192,15 +215,22 @@ cd json
|
||||
file_get_try_double "${primary_urls[nlohmann]}" "${secondary_urls[nlohmann]}"
|
||||
cd ..
|
||||
|
||||
bzip2_tag="0405487e2b1de738e7f1c8afb50d19cf44e8d580" # v1.0.6 (May 26, 2011)
|
||||
repo_clone_try_double "${primary_urls[bzip2]}" "${secondary_urls[bzip2]}" "bzip2" "$bzip2_tag"
|
||||
|
||||
zlib_tag="v1.2.11" # v1.2.11.
|
||||
repo_clone_try_double "${primary_urls[zlib]}" "${secondary_urls[zlib]}" "zlib" "$zlib_tag" true
|
||||
# remove shared library from install dependencies
|
||||
sed -i 's/install(TARGETS zlib zlibstatic/install(TARGETS zlibstatic/g' zlib/CMakeLists.txt
|
||||
|
||||
rocksdb_tag="v6.14.6" # (2020-10-14)
|
||||
repo_clone_try_double "${primary_urls[rocksdb]}" "${secondary_urls[rocksdb]}" "rocksdb" "$rocksdb_tag" true
|
||||
pushd rocksdb
|
||||
git apply ../rocksdb.patch
|
||||
popd
|
||||
# remove shared library from install dependencies
|
||||
sed -i 's/TARGETS ${ROCKSDB_SHARED_LIB}/TARGETS ${ROCKSDB_SHARED_LIB} OPTIONAL/' rocksdb/CMakeLists.txt
|
||||
|
||||
# mgclient
|
||||
mgclient_tag="96e95c6845463cbe88948392be58d26da0d5ffd3" # (2022-02-08)
|
||||
repo_clone_try_double "${primary_urls[mgclient]}" "${secondary_urls[mgclient]}" "mgclient" "$mgclient_tag"
|
||||
mgclient_tag="v1.3.0" # (2021-09-23)
|
||||
repo_clone_try_double "${primary_urls[mgclient]}" "${secondary_urls[mgclient]}" "mgclient" "$mgclient_tag" true
|
||||
sed -i 's/\${CMAKE_INSTALL_LIBDIR}/lib/' mgclient/src/CMakeLists.txt
|
||||
|
||||
# pymgclient
|
||||
@@ -211,9 +241,28 @@ repo_clone_try_double "${primary_urls[pymgclient]}" "${secondary_urls[pymgclient
|
||||
mgconsole_tag="v1.1.0" # (2021-10-07)
|
||||
repo_clone_try_double "${primary_urls[mgconsole]}" "${secondary_urls[mgconsole]}" "mgconsole" "$mgconsole_tag" true
|
||||
|
||||
spdlog_tag="v1.9.2" # (2021-08-12)
|
||||
spdlog_tag="v1.8.2" # (2020-12-01)
|
||||
repo_clone_try_double "${primary_urls[spdlog]}" "${secondary_urls[spdlog]}" "spdlog" "$spdlog_tag" true
|
||||
|
||||
jemalloc_tag="ea6b3e973b477b8061e0076bb257dbd7f3faa756" # (2021-02-11)
|
||||
repo_clone_try_double "${primary_urls[jemalloc]}" "${secondary_urls[jemalloc]}" "jemalloc" "$jemalloc_tag"
|
||||
pushd jemalloc
|
||||
# ThreadPool select job randomly, and there can be some threads that had been
|
||||
# performed some memory heavy task before and will be inactive for some time,
|
||||
# but until it will became active again, the memory will not be freed since by
|
||||
# default each thread has it's own arena, but there should be not more then
|
||||
# 4*CPU arenas (see opt.nareans description).
|
||||
#
|
||||
# By enabling percpu_arena number of arenas limited to number of CPUs and hence
|
||||
# this problem should go away.
|
||||
#
|
||||
# muzzy_decay_ms -- use MADV_FREE when available on newer Linuxes, to
|
||||
# avoid spurious latencies and additional work associated with
|
||||
# MADV_DONTNEED. See
|
||||
# https://github.com/ClickHouse/ClickHouse/issues/11121 for motivation.
|
||||
./autogen.sh --with-malloc-conf="percpu_arena:percpu,oversize_threshold:0,muzzy_decay_ms:5000,dirty_decay_ms:5000"
|
||||
popd
|
||||
|
||||
# librdkafka
|
||||
librdkafka_tag="v1.7.0" # (2021-05-06)
|
||||
repo_clone_try_double "${primary_urls[librdkafka]}" "${secondary_urls[librdkafka]}" "librdkafka" "$librdkafka_tag" true
|
||||
@@ -222,7 +271,16 @@ repo_clone_try_double "${primary_urls[librdkafka]}" "${secondary_urls[librdkafka
|
||||
protobuf_tag="v3.12.4"
|
||||
repo_clone_try_double "${primary_urls[protobuf]}" "${secondary_urls[protobuf]}" "protobuf" "$protobuf_tag" true
|
||||
pushd protobuf
|
||||
./autogen.sh && ./configure CC=clang CXX=clang++ --prefix=$(pwd)/lib
|
||||
./autogen.sh && ./configure --prefix=$(pwd)/lib
|
||||
popd
|
||||
|
||||
# boost
|
||||
file_get_try_double "${primary_urls[boost]}" "${secondary_urls[boost]}"
|
||||
tar -xzf boost_1_77_0.tar.gz
|
||||
mv boost_1_77_0 boost
|
||||
pushd boost
|
||||
./bootstrap.sh --prefix=$(pwd)/lib --with-libraries="system,regex"
|
||||
./b2 -j$(nproc) install
|
||||
popd
|
||||
|
||||
#pulsar
|
||||
@@ -231,10 +289,3 @@ repo_clone_try_double "${primary_urls[pulsar]}" "${secondary_urls[pulsar]}" "pul
|
||||
pushd pulsar
|
||||
git apply ../pulsar.patch
|
||||
popd
|
||||
|
||||
#librdtsc
|
||||
librdtsc_tag="v0.3"
|
||||
repo_clone_try_double "${primary_urls[librdtsc]}" "${secondary_urls[librdtsc]}" "librdtsc" "$librdtsc_tag" true
|
||||
pushd librdtsc
|
||||
git apply ../librdtsc.patch
|
||||
popd
|
||||
|
||||
@@ -36,7 +36,7 @@ ADDITIONAL USE GRANT: You may use the Licensed Work in accordance with the
|
||||
3. using the Licensed Work to create a work or solution
|
||||
which competes (or might reasonably be expected to
|
||||
compete) with the Licensed Work.
|
||||
CHANGE DATE: 2026-18-02
|
||||
CHANGE DATE: 2025-10-12
|
||||
CHANGE LICENSE: Apache License, Version 2.0
|
||||
|
||||
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.
|
||||
|
||||
@@ -36,18 +36,9 @@ set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "openssl (>= 1.1.0), python3 (>= 3.5.0)")
|
||||
|
||||
# RPM specific
|
||||
|
||||
set(MG_ARCH_EXTENSION "noarch")
|
||||
|
||||
if (${MG_ARCH} STREQUAL "x86_64")
|
||||
set(MG_ARCH_EXTENSION "x86_64")
|
||||
elseif (${MG_ARCH} STREQUAL "ARM64")
|
||||
set(MG_ARCH_EXTENSION "aarch64")
|
||||
endif()
|
||||
|
||||
set(CPACK_RPM_PACKAGE_URL https://memgraph.com)
|
||||
set(CPACK_RPM_PACKAGE_VERSION "${MEMGRAPH_VERSION_RPM}")
|
||||
set(CPACK_RPM_FILE_NAME "memgraph-${MEMGRAPH_VERSION_RPM}-1.${MG_ARCH_EXTENSION}.rpm")
|
||||
set(CPACK_RPM_FILE_NAME "memgraph-${MEMGRAPH_VERSION_RPM}-1.x86_64.rpm")
|
||||
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
|
||||
/var /var/lib /var/log /etc/logrotate.d
|
||||
/lib /lib/systemd /lib/systemd/system /lib/systemd/system/memgraph.service)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
FROM dokken/centos-stream-9
|
||||
|
||||
ARG env_folder
|
||||
ARG toolchain_version
|
||||
|
||||
COPY ${env_folder} /env_folder
|
||||
|
||||
RUN yum update && yum install -y curl git
|
||||
|
||||
RUN /${env_folder}/os/centos-9.sh install MEMGRAPH_BUILD_DEPS
|
||||
RUN /${env_folder}/os/centos-9.sh install TOOLCHAIN_RUN_DEPS
|
||||
|
||||
RUN rm -rf /env_folder
|
||||
|
||||
RUN yum clean all
|
||||
|
||||
RUN curl https://s3.eu-west-1.amazonaws.com/deps.memgraph.io/${toolchain_version}/${toolchain_version}-binaries-centos-9-arm64.tar.gz -o /tmp/toolchain.tar.gz \
|
||||
&& tar xvzf /tmp/toolchain.tar.gz -C /opt \
|
||||
&& rm /tmp/toolchain.tar.gz
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
cp -r ../../environment env_folder
|
||||
docker build -f build_env.dockerfile --build-arg env_folder=env_folder --build-arg toolchain_version=toolchain-v4 -t mg_build_env .
|
||||
rm -rf env_folder
|
||||
@@ -1,19 +1,19 @@
|
||||
FROM debian:bullseye
|
||||
FROM debian:buster
|
||||
# NOTE: If you change the base distro update release/package as well.
|
||||
|
||||
ARG release
|
||||
ARG deb_release
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
openssl libcurl4 libssl1.1 libseccomp2 python3 libpython3.9 python3-pip \
|
||||
openssl libcurl4 libssl1.1 libseccomp2 python3 libpython3.7 python3-pip \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
RUN pip3 install networkx==2.4 numpy==1.21.4 scipy==1.7.3
|
||||
RUN pip3 install networkx==2.4 numpy==1.19.2 scipy==1.5.2
|
||||
|
||||
COPY ${release} /
|
||||
COPY ${deb_release} /
|
||||
|
||||
# Install memgraph package
|
||||
RUN dpkg -i ${release}
|
||||
RUN dpkg -i ${deb_release}
|
||||
|
||||
# Memgraph listens for Bolt Protocol on this port by default.
|
||||
EXPOSE 7687
|
||||
@@ -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 [""]
|
||||
46
release/docker/package_deb_docker
Executable file
46
release/docker/package_deb_docker
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# Build and package Docker image of Memgraph.
|
||||
|
||||
function print_help () {
|
||||
echo "Usage: $0 [--latest] MEMGRAPH_PACKAGE.deb"
|
||||
echo "Optional arguments:"
|
||||
echo -e "\t-h|--help\t\tPrint help."
|
||||
echo -e "\t--latest\t\tTag image as latest version."
|
||||
}
|
||||
|
||||
working_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
latest_image=""
|
||||
tag_latest=""
|
||||
if [[ $# -eq 2 && "$1" == "--latest" ]]; then
|
||||
latest_image="memgraph:latest"
|
||||
tag_latest="-t memgraph:latest"
|
||||
shift
|
||||
elif [[ $# -ne 1 || "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
deb_path="$1"
|
||||
if [[ ! -f "$deb_path" ]]; then
|
||||
echo "File '$deb_path' does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy the .deb to working directory.
|
||||
cp "$deb_path" "${working_dir}/"
|
||||
cd ${working_dir}
|
||||
|
||||
# Extract version and offering from deb name.
|
||||
deb_name=`echo $(basename "$deb_path") | sed 's/.deb$//'`
|
||||
version=`echo ${deb_name} | cut -d '_' -f 2 | rev | cut -d '-' -f 2- | rev | tr '+~' '__'`
|
||||
dockerfile_path="${working_dir}/memgraph.dockerfile"
|
||||
image_name="memgraph:${version}"
|
||||
package_name="memgraph-${version}-docker.tar.gz"
|
||||
|
||||
# Build docker image.
|
||||
docker build -t ${image_name} ${tag_latest} -f ${dockerfile_path} --build-arg deb_release=${deb_name}.deb .
|
||||
docker save ${image_name} ${latest_image} > ${package_name}
|
||||
rm "${deb_name}.deb"
|
||||
echo "Built Docker image at '${working_dir}/${package_name}'"
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# Build and package Docker image of Memgraph.
|
||||
|
||||
function print_help () {
|
||||
echo "Usage: $0 [--latest] MEMGRAPH_PACKAGE.(deb|rpm)"
|
||||
echo "Optional arguments:"
|
||||
echo -e "\t-h|--help\t\tPrint help."
|
||||
echo -e "\t--latest\t\tTag image as latest version."
|
||||
}
|
||||
|
||||
working_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
latest_image=""
|
||||
tag_latest=""
|
||||
if [[ $# -eq 2 && "$1" == "--latest" ]]; then
|
||||
latest_image="memgraph:latest"
|
||||
tag_latest="-t memgraph:latest"
|
||||
shift
|
||||
elif [[ $# -ne 1 || "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
package_path="$1"
|
||||
if [[ ! -f "$package_path" ]]; then
|
||||
echo "File '$package_path' does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy the .deb to working directory.
|
||||
cp "$package_path" "${working_dir}/"
|
||||
cd ${working_dir}
|
||||
|
||||
extension="${package_path##*.}"
|
||||
|
||||
if [[ "$extension" == "deb" ]]; then
|
||||
# Extract version and offering from deb name.
|
||||
package_name=`echo $(basename "$package_path") | sed 's/.deb$//'`
|
||||
version=`echo ${package_name} | cut -d '_' -f 2 | rev | cut -d '-' -f 2- | rev | tr '+~' '__'`
|
||||
dockerfile_path="${working_dir}/memgraph_deb.dockerfile"
|
||||
elif [[ "$extension" == "rpm" ]]; then
|
||||
# Extract version and offering from deb name.
|
||||
package_name=`echo $(basename "$package_path") | sed 's/.rpm$//'`
|
||||
version=`echo ${package_name} | cut -d '-' -f 2 | rev | cut -d '-' -f 2- | rev`
|
||||
version=${version%_1}
|
||||
dockerfile_path="${working_dir}/memgraph_rpm.dockerfile"
|
||||
else
|
||||
echo "Invalid file sent as the package"
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
image_name="memgraph:${version}"
|
||||
image_package_name="memgraph-${version}-docker.tar.gz"
|
||||
|
||||
# Build docker image.
|
||||
docker build -t ${image_name} ${tag_latest} -f ${dockerfile_path} --build-arg release=${package_name}.${extension} .
|
||||
docker save ${image_name} ${latest_image} | gzip > ${image_package_name}
|
||||
rm "${package_name}.${extension}"
|
||||
echo "Built Docker image at '${working_dir}/${image_package_name}'"
|
||||
17
release/package/debian-9/Dockerfile
Normal file
17
release/package/debian-9/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM debian:9
|
||||
|
||||
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-9.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-debian-9.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-9.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
@@ -8,6 +8,10 @@ services:
|
||||
build:
|
||||
context: centos-8
|
||||
container_name: "mgbuild_centos-8"
|
||||
mgbuild_debian-9:
|
||||
build:
|
||||
context: debian-9
|
||||
container_name: "mgbuild_debian-9"
|
||||
mgbuild_debian-10:
|
||||
build:
|
||||
context: debian-10
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
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-8 debian-9 debian-10 debian-11 ubuntu-18.04 ubuntu-20.04)
|
||||
PROJECT_ROOT="$SCRIPT_DIR/../.."
|
||||
TOOLCHAIN_VERSION="toolchain-v4"
|
||||
TOOLCHAIN_VERSION="toolchain-v3"
|
||||
ACTIVATE_TOOLCHAIN="source /opt/${TOOLCHAIN_VERSION}/activate"
|
||||
HOST_OUTPUT_DIR="$PROJECT_ROOT/build/output"
|
||||
|
||||
@@ -72,7 +72,7 @@ make_package () {
|
||||
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 /memgraph && ./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 .."
|
||||
# ' is used instead of " because we need to run make within the allowed
|
||||
@@ -100,13 +100,13 @@ case "$1" in
|
||||
;;
|
||||
|
||||
docker)
|
||||
# NOTE: Docker is build on top of Debian 11 package.
|
||||
based_on_os="debian-11"
|
||||
# NOTE: Docker is build on top of Debian 10 package.
|
||||
based_on_os="debian-10"
|
||||
# shellcheck disable=SC2012
|
||||
last_package_name=$(cd "$HOST_OUTPUT_DIR/$based_on_os" && ls -t memgraph* | head -1)
|
||||
docker_build_folder="$PROJECT_ROOT/release/docker"
|
||||
cd "$docker_build_folder"
|
||||
./package_docker --latest "$HOST_OUTPUT_DIR/$based_on_os/$last_package_name"
|
||||
./package_deb_docker --latest "$HOST_OUTPUT_DIR/$based_on_os/$last_package_name"
|
||||
# shellcheck disable=SC2012
|
||||
docker_image_name=$(cd "$docker_build_folder" && ls -t memgraph* | head -1)
|
||||
docker_host_folder="$HOST_OUTPUT_DIR/docker"
|
||||
|
||||
@@ -37,7 +37,7 @@ set(mg_single_node_v2_sources
|
||||
)
|
||||
|
||||
set(mg_single_node_v2_libs stdc++fs Threads::Threads
|
||||
telemetry_lib mg-query mg-communication mg-memory mg-utils mg-auth mg-license mg-settings)
|
||||
telemetry_lib mg-query mg-communication mg-memory mg-utils mg-auth)
|
||||
if (MG_ENTERPRISE)
|
||||
# These are enterprise subsystems
|
||||
set(mg_single_node_v2_libs ${mg_single_node_v2_libs} mg-audit)
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
set(audit_src_files log.cpp)
|
||||
|
||||
find_package(fmt REQUIRED)
|
||||
find_package(gflags REQUIRED)
|
||||
|
||||
add_library(mg-audit STATIC ${audit_src_files})
|
||||
target_link_libraries(mg-audit json gflags fmt::fmt)
|
||||
target_link_libraries(mg-audit json gflags fmt)
|
||||
target_link_libraries(mg-audit mg-utils mg-storage-v2)
|
||||
|
||||
@@ -5,13 +5,10 @@ set(auth_src_files
|
||||
module.cpp)
|
||||
|
||||
find_package(Seccomp REQUIRED)
|
||||
find_package(fmt REQUIRED)
|
||||
find_package(gflags REQUIRED)
|
||||
|
||||
|
||||
add_library(mg-auth STATIC ${auth_src_files})
|
||||
target_link_libraries(mg-auth json libbcrypt gflags fmt::fmt)
|
||||
target_link_libraries(mg-auth mg-utils mg-kvstore mg-license )
|
||||
target_link_libraries(mg-auth json libbcrypt gflags fmt)
|
||||
target_link_libraries(mg-auth mg-utils mg-kvstore)
|
||||
|
||||
target_link_libraries(mg-auth ${Seccomp_LIBRARIES})
|
||||
target_include_directories(mg-auth SYSTEM PRIVATE ${Seccomp_INCLUDE_DIRS})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -30,17 +30,6 @@ DEFINE_string(auth_password_strength_regex, default_password_regex.data(),
|
||||
|
||||
namespace auth {
|
||||
|
||||
namespace {
|
||||
// Constant list of all available permissions.
|
||||
const std::vector<Permission> kPermissionsAll = {
|
||||
Permission::MATCH, Permission::CREATE, Permission::MERGE, Permission::DELETE,
|
||||
Permission::SET, Permission::REMOVE, Permission::INDEX, Permission::STATS,
|
||||
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};
|
||||
} // namespace
|
||||
|
||||
std::string PermissionToString(Permission permission) {
|
||||
switch (permission) {
|
||||
case Permission::MATCH:
|
||||
@@ -79,12 +68,6 @@ std::string PermissionToString(Permission permission) {
|
||||
return "AUTH";
|
||||
case Permission::STREAM:
|
||||
return "STREAM";
|
||||
case Permission::MODULE_READ:
|
||||
return "MODULE_READ";
|
||||
case Permission::MODULE_WRITE:
|
||||
return "MODULE_WRITE";
|
||||
case Permission::WEBSOCKET:
|
||||
return "WEBSOCKET";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -19,30 +19,35 @@ namespace auth {
|
||||
// bitmask.
|
||||
// clang-format off
|
||||
enum class Permission : uint64_t {
|
||||
MATCH = 1,
|
||||
CREATE = 1U << 1U,
|
||||
MERGE = 1U << 2U,
|
||||
DELETE = 1U << 3U,
|
||||
SET = 1U << 4U,
|
||||
REMOVE = 1U << 5U,
|
||||
INDEX = 1U << 6U,
|
||||
STATS = 1U << 7U,
|
||||
CONSTRAINT = 1U << 8U,
|
||||
DUMP = 1U << 9U,
|
||||
REPLICATION = 1U << 10U,
|
||||
DURABILITY = 1U << 11U,
|
||||
READ_FILE = 1U << 12U,
|
||||
FREE_MEMORY = 1U << 13U,
|
||||
TRIGGER = 1U << 14U,
|
||||
CONFIG = 1U << 15U,
|
||||
AUTH = 1U << 16U,
|
||||
STREAM = 1U << 17U,
|
||||
MODULE_READ = 1U << 18U,
|
||||
MODULE_WRITE = 1U << 19U,
|
||||
WEBSOCKET = 1U << 20U
|
||||
MATCH = 1,
|
||||
CREATE = 1U << 1U,
|
||||
MERGE = 1U << 2U,
|
||||
DELETE = 1U << 3U,
|
||||
SET = 1U << 4U,
|
||||
REMOVE = 1U << 5U,
|
||||
INDEX = 1U << 6U,
|
||||
STATS = 1U << 7U,
|
||||
CONSTRAINT = 1U << 8U,
|
||||
DUMP = 1U << 9U,
|
||||
REPLICATION = 1U << 10U,
|
||||
DURABILITY = 1U << 11U,
|
||||
READ_FILE = 1U << 12U,
|
||||
FREE_MEMORY = 1U << 13U,
|
||||
TRIGGER = 1U << 14U,
|
||||
CONFIG = 1U << 15U,
|
||||
AUTH = 1U << 16U,
|
||||
STREAM = 1U << 17U
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Constant list of all available permissions.
|
||||
const std::vector<Permission> kPermissionsAll = {Permission::MATCH, Permission::CREATE, Permission::MERGE,
|
||||
Permission::DELETE, Permission::SET, Permission::REMOVE,
|
||||
Permission::INDEX, Permission::STATS, Permission::CONSTRAINT,
|
||||
Permission::DUMP, Permission::AUTH, Permission::REPLICATION,
|
||||
Permission::DURABILITY, Permission::READ_FILE, Permission::FREE_MEMORY,
|
||||
Permission::TRIGGER, Permission::CONFIG, Permission::STREAM};
|
||||
|
||||
// Function that converts a permission to its string representation.
|
||||
std::string PermissionToString(Permission permission);
|
||||
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
find_package(fmt REQUIRED)
|
||||
find_package(gflags REQUIRED)
|
||||
|
||||
set(communication_src_files
|
||||
websocket/auth.cpp
|
||||
websocket/server.cpp
|
||||
websocket/listener.cpp
|
||||
websocket/session.cpp
|
||||
bolt/v1/value.cpp
|
||||
buffer.cpp
|
||||
client.cpp
|
||||
@@ -13,10 +6,8 @@ set(communication_src_files
|
||||
helpers.cpp
|
||||
init.cpp)
|
||||
|
||||
find_package(Boost REQUIRED)
|
||||
|
||||
add_library(mg-communication STATIC ${communication_src_files})
|
||||
target_link_libraries(mg-communication Boost::headers Threads::Threads mg-utils mg-io mg-auth fmt::fmt gflags)
|
||||
target_link_libraries(mg-communication Threads::Threads mg-utils mg-io fmt gflags)
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(mg-communication ${OPENSSL_LIBRARIES})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -73,9 +73,11 @@ SSL_CTX *ClientContext::context() { return ctx_; }
|
||||
|
||||
bool ClientContext::use_ssl() { return use_ssl_; }
|
||||
|
||||
ServerContext::ServerContext() : use_ssl_(false), ctx_(nullptr) {}
|
||||
|
||||
ServerContext::ServerContext(const std::string &key_file, const std::string &cert_file, const std::string &ca_file,
|
||||
bool verify_peer)
|
||||
:
|
||||
: use_ssl_(true),
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
ctx_(SSL_CTX_new(SSLv23_server_method()))
|
||||
#else
|
||||
@@ -108,47 +110,43 @@ ServerContext::ServerContext(const std::string &key_file, const std::string &cer
|
||||
SSL_CTX_set_client_CA_list(ctx_, ca_names);
|
||||
|
||||
// Enable verification of the client certificate.
|
||||
// NOLINTNEXTLINE(hicpp-signed-bitwise)
|
||||
SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ServerContext::ServerContext(ServerContext &&other) noexcept { std::swap(ctx_, other.ctx_); }
|
||||
ServerContext::ServerContext(ServerContext &&other) noexcept : use_ssl_(other.use_ssl_), ctx_(other.ctx_) {
|
||||
other.use_ssl_ = false;
|
||||
other.ctx_ = nullptr;
|
||||
}
|
||||
|
||||
ServerContext &ServerContext::operator=(ServerContext &&other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
|
||||
// destroy my objects
|
||||
if (ctx_) {
|
||||
if (use_ssl_) {
|
||||
SSL_CTX_free(ctx_);
|
||||
}
|
||||
|
||||
// move other objects to self
|
||||
use_ssl_ = other.use_ssl_;
|
||||
ctx_ = other.ctx_;
|
||||
|
||||
// reset other objects
|
||||
other.use_ssl_ = false;
|
||||
other.ctx_ = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ServerContext::~ServerContext() {
|
||||
if (ctx_) {
|
||||
if (use_ssl_) {
|
||||
SSL_CTX_free(ctx_);
|
||||
}
|
||||
}
|
||||
|
||||
SSL_CTX *ServerContext::context() {
|
||||
MG_ASSERT(ctx_);
|
||||
return ctx_;
|
||||
}
|
||||
SSL_CTX *ServerContext::context_clone() {
|
||||
MG_ASSERT(ctx_);
|
||||
SSL_CTX_up_ref(ctx_);
|
||||
return ctx_;
|
||||
}
|
||||
SSL_CTX *ServerContext::context() { return ctx_; }
|
||||
|
||||
bool ServerContext::use_ssl() const { return ctx_ != nullptr; }
|
||||
bool ServerContext::use_ssl() { return use_ssl_; }
|
||||
|
||||
} // namespace communication
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -69,7 +69,11 @@ class ClientContext final {
|
||||
*/
|
||||
class ServerContext final {
|
||||
public:
|
||||
ServerContext() = default;
|
||||
/**
|
||||
* This constructor constructs a ServerContext that doesn't use SSL.
|
||||
*/
|
||||
ServerContext();
|
||||
|
||||
/**
|
||||
* This constructor constructs a ServerContext that uses SSL. The parameters
|
||||
* `key_file` and `cert_file` can't be "" because when setting up a server it
|
||||
@@ -91,15 +95,16 @@ class ServerContext final {
|
||||
ServerContext(ServerContext &&other) noexcept;
|
||||
ServerContext &operator=(ServerContext &&other) noexcept;
|
||||
|
||||
// Destructor that handles ownership of the SSL object.
|
||||
~ServerContext();
|
||||
|
||||
SSL_CTX *context();
|
||||
SSL_CTX *context_clone();
|
||||
|
||||
bool use_ssl() const;
|
||||
bool use_ssl();
|
||||
|
||||
private:
|
||||
SSL_CTX *ctx_{nullptr};
|
||||
bool use_ssl_;
|
||||
SSL_CTX *ctx_;
|
||||
};
|
||||
|
||||
} // namespace communication
|
||||
|
||||
@@ -89,8 +89,7 @@ class Server final {
|
||||
alive_.store(true);
|
||||
|
||||
if (!socket_.Bind(endpoint_)) {
|
||||
spdlog::error(
|
||||
utils::MessageWithLink("Cannot bind to socket on endpoint {}.", endpoint_, "https://memgr.ph/socket"));
|
||||
spdlog::error(utils::MessageWithLink("Cannot bind to socket on endpoint {}.", endpoint_, "https://memgr.ph/socket"));
|
||||
alive_.store(false);
|
||||
return false;
|
||||
}
|
||||
@@ -103,8 +102,9 @@ class Server final {
|
||||
|
||||
listener_.Start();
|
||||
|
||||
thread_ = std::thread([this]() {
|
||||
utils::ThreadSetName(fmt::format("{} server", service_name_));
|
||||
std::string service_name(service_name_);
|
||||
thread_ = std::thread([this, service_name]() {
|
||||
utils::ThreadSetName(fmt::format("{} server", service_name));
|
||||
|
||||
spdlog::info("{} server is fully armed and operational", service_name_);
|
||||
spdlog::info("{} listening on {}", service_name_, socket_.endpoint());
|
||||
|
||||
@@ -1,30 +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.
|
||||
|
||||
#include "communication/websocket/auth.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace communication::websocket {
|
||||
|
||||
bool SafeAuth::Authenticate(const std::string &username, const std::string &password) const {
|
||||
return auth_->Lock()->Authenticate(username, password).has_value();
|
||||
}
|
||||
|
||||
bool SafeAuth::HasUserPermission(const std::string &username, const auth::Permission permission) const {
|
||||
if (const auto user = auth_->ReadLock()->GetUser(username); user) {
|
||||
return user->GetPermissions().Has(permission) == auth::PermissionLevel::GRANT;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SafeAuth::HasAnyUsers() const { return auth_->ReadLock()->HasUsers(); }
|
||||
} // namespace communication::websocket
|
||||
@@ -1,44 +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 <string>
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
|
||||
namespace communication::websocket {
|
||||
|
||||
class AuthenticationInterface {
|
||||
public:
|
||||
virtual bool Authenticate(const std::string &username, const std::string &password) const = 0;
|
||||
|
||||
virtual bool HasUserPermission(const std::string &username, auth::Permission permission) const = 0;
|
||||
|
||||
virtual bool HasAnyUsers() const = 0;
|
||||
};
|
||||
|
||||
class SafeAuth : public AuthenticationInterface {
|
||||
public:
|
||||
explicit SafeAuth(utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock> *auth) : auth_{auth} {}
|
||||
|
||||
bool Authenticate(const std::string &username, const std::string &password) const override;
|
||||
|
||||
bool HasUserPermission(const std::string &username, auth::Permission permission) const override;
|
||||
|
||||
bool HasAnyUsers() const override;
|
||||
|
||||
private:
|
||||
utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock> *auth_;
|
||||
};
|
||||
} // namespace communication::websocket
|
||||
@@ -1,90 +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.
|
||||
|
||||
#include "communication/websocket/listener.hpp"
|
||||
|
||||
namespace communication::websocket {
|
||||
namespace {
|
||||
void LogError(boost::beast::error_code ec, const std::string_view what) {
|
||||
spdlog::warn("Websocket listener failed on {}: {}", what, ec.message());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Listener::Run() { DoAccept(); }
|
||||
|
||||
void Listener::WriteToAll(std::shared_ptr<std::string> message) {
|
||||
auto sessions_ptr = sessions_.Lock();
|
||||
for (auto &session : *sessions_ptr) {
|
||||
session->Write(message);
|
||||
}
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::endpoint Listener::GetEndpoint() const { return acceptor_.local_endpoint(); };
|
||||
|
||||
Listener::Listener(boost::asio::io_context &ioc, ServerContext *context, tcp::endpoint endpoint,
|
||||
AuthenticationInterface &auth)
|
||||
: ioc_(ioc), context_(context), acceptor_(ioc), auth_(auth) {
|
||||
boost::beast::error_code ec;
|
||||
|
||||
// Open the acceptor
|
||||
acceptor_.open(endpoint.protocol(), ec);
|
||||
if (ec) {
|
||||
LogError(ec, "open");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow address reuse
|
||||
acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec);
|
||||
if (ec) {
|
||||
LogError(ec, "set_option");
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind to the server address
|
||||
acceptor_.bind(endpoint, ec);
|
||||
if (ec) {
|
||||
LogError(ec, "bind");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec);
|
||||
if (ec) {
|
||||
LogError(ec, "listen");
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::info("WebSocket server is listening on {}:{}", endpoint.address(), endpoint.port());
|
||||
}
|
||||
|
||||
void Listener::DoAccept() {
|
||||
acceptor_.async_accept(
|
||||
ioc_, [shared_this = shared_from_this()](auto ec, auto socket) { shared_this->OnAccept(ec, std::move(socket)); });
|
||||
}
|
||||
|
||||
void Listener::OnAccept(boost::beast::error_code ec, tcp::socket socket) {
|
||||
if (ec) {
|
||||
return LogError(ec, "accept");
|
||||
}
|
||||
|
||||
auto session = Session::Create(std::move(socket), *context_, auth_);
|
||||
|
||||
if (session->Run()) {
|
||||
auto sessions_ptr = sessions_.Lock();
|
||||
|
||||
// Clean disconnected clients
|
||||
std::erase_if(*sessions_ptr, [](const auto &elem) { return !elem->IsConnected(); });
|
||||
|
||||
sessions_ptr->emplace_back(std::move(session));
|
||||
}
|
||||
|
||||
DoAccept();
|
||||
}
|
||||
} // namespace communication::websocket
|
||||
@@ -1,57 +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
|
||||
|
||||
#define BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/websocket/session.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
|
||||
namespace communication::websocket {
|
||||
class Listener : public std::enable_shared_from_this<Listener> {
|
||||
using tcp = boost::asio::ip::tcp;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
static std::shared_ptr<Listener> Create(Args &&...args) {
|
||||
return std::shared_ptr<Listener>{new Listener(std::forward<Args>(args)...)};
|
||||
}
|
||||
|
||||
// Start accepting incoming connections
|
||||
void Run();
|
||||
void WriteToAll(std::shared_ptr<std::string> message);
|
||||
tcp::endpoint GetEndpoint() const;
|
||||
|
||||
private:
|
||||
Listener(boost::asio::io_context &ioc, ServerContext *context, tcp::endpoint endpoint, AuthenticationInterface &auth);
|
||||
|
||||
void DoAccept();
|
||||
void OnAccept(boost::beast::error_code ec, tcp::socket socket);
|
||||
|
||||
boost::asio::io_context &ioc_;
|
||||
ServerContext *context_;
|
||||
tcp::acceptor acceptor_;
|
||||
utils::Synchronized<std::list<std::shared_ptr<Session>>, utils::SpinLock> sessions_;
|
||||
AuthenticationInterface &auth_;
|
||||
};
|
||||
} // namespace communication::websocket
|
||||
@@ -1,84 +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.
|
||||
|
||||
#include "communication/websocket/server.hpp"
|
||||
|
||||
#include <spdlog/pattern_formatter.h>
|
||||
|
||||
namespace communication::websocket {
|
||||
|
||||
Server::~Server() {
|
||||
MG_ASSERT(!background_thread_ || (ioc_.stopped() && !background_thread_->joinable()),
|
||||
"Server wasn't shutdown properly");
|
||||
}
|
||||
|
||||
void Server::Start() {
|
||||
MG_ASSERT(!background_thread_, "The server was already started!");
|
||||
listener_->Run();
|
||||
background_thread_.emplace([this] { ioc_.run(); });
|
||||
}
|
||||
|
||||
void Server::Shutdown() { ioc_.stop(); }
|
||||
|
||||
void Server::AwaitShutdown() {
|
||||
if (background_thread_ && background_thread_->joinable()) {
|
||||
background_thread_->join();
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::IsRunning() const { return background_thread_ && !ioc_.stopped(); }
|
||||
|
||||
boost::asio::ip::tcp::endpoint Server::GetEndpoint() const { return listener_->GetEndpoint(); };
|
||||
|
||||
namespace {
|
||||
class QuoteEscapeFormatter : public spdlog::custom_flag_formatter {
|
||||
public:
|
||||
void format(const spdlog::details::log_msg &msg, const std::tm & /*time*/, spdlog::memory_buf_t &dest) override {
|
||||
for (const auto c : msg.payload) {
|
||||
if (c == '"') {
|
||||
constexpr std::string_view escaped_quote = "\\\"";
|
||||
dest.append(escaped_quote.data(), escaped_quote.data() + escaped_quote.size());
|
||||
} else if (c == '\n') {
|
||||
constexpr std::string_view escaped_newline = "\\n";
|
||||
dest.append(escaped_newline.data(), escaped_newline.data() + escaped_newline.size());
|
||||
} else {
|
||||
dest.push_back(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::unique_ptr<custom_flag_formatter> clone() const override {
|
||||
return spdlog::details::make_unique<QuoteEscapeFormatter>();
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace
|
||||
|
||||
void Server::LoggingSink::sink_it_(const spdlog::details::log_msg &msg) {
|
||||
const auto listener = listener_.lock();
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
||||
memory_buf_t formatted;
|
||||
base_sink<std::mutex>::formatter_->format(msg, formatted);
|
||||
listener->WriteToAll(std::make_shared<std::string>(formatted.data(), formatted.size()));
|
||||
}
|
||||
|
||||
std::shared_ptr<Server::LoggingSink> Server::GetLoggingSink() {
|
||||
auto formatter = std::make_unique<spdlog::pattern_formatter>();
|
||||
formatter->add_flag<QuoteEscapeFormatter>('*').set_pattern(
|
||||
R"json({"event": "log", "level": "%l", "message": "%*"})json");
|
||||
auto sink = std::make_shared<LoggingSink>(listener_);
|
||||
sink->set_formatter(std::move(formatter));
|
||||
return sink;
|
||||
}
|
||||
|
||||
} // namespace communication::websocket
|
||||
@@ -1,68 +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
|
||||
|
||||
#define BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
#include "communication/websocket/listener.hpp"
|
||||
#include "io/network/endpoint.hpp"
|
||||
|
||||
namespace communication::websocket {
|
||||
|
||||
class Server final {
|
||||
using tcp = boost::asio::ip::tcp;
|
||||
|
||||
public:
|
||||
explicit Server(io::network::Endpoint endpoint, ServerContext *context, AuthenticationInterface &auth)
|
||||
: listener_{Listener::Create(
|
||||
ioc_, context, tcp::endpoint{boost::asio::ip::make_address(endpoint.address), endpoint.port}, auth)} {}
|
||||
|
||||
Server(const Server &) = delete;
|
||||
Server(Server &&) = delete;
|
||||
Server &operator=(const Server &) = delete;
|
||||
Server &operator=(Server &&) = delete;
|
||||
|
||||
~Server();
|
||||
|
||||
void Start();
|
||||
void Shutdown();
|
||||
void AwaitShutdown();
|
||||
bool IsRunning() const;
|
||||
tcp::endpoint GetEndpoint() const;
|
||||
|
||||
class LoggingSink : public spdlog::sinks::base_sink<std::mutex> {
|
||||
public:
|
||||
explicit LoggingSink(std::weak_ptr<Listener> listener) : listener_(listener) {}
|
||||
|
||||
private:
|
||||
void sink_it_(const spdlog::details::log_msg &msg) override;
|
||||
|
||||
void flush_() override {}
|
||||
|
||||
std::weak_ptr<Listener> listener_;
|
||||
};
|
||||
|
||||
std::shared_ptr<LoggingSink> GetLoggingSink();
|
||||
|
||||
private:
|
||||
boost::asio::io_context ioc_;
|
||||
|
||||
std::shared_ptr<Listener> listener_;
|
||||
std::optional<std::thread> background_thread_;
|
||||
};
|
||||
} // namespace communication::websocket
|
||||
@@ -1,235 +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.
|
||||
|
||||
#include "communication/websocket/session.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/beast/core/buffers_to_string.hpp>
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
#include <json/json.hpp>
|
||||
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/websocket/auth.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace communication::websocket {
|
||||
namespace {
|
||||
void LogError(const boost::beast::error_code ec, const std::string_view what) {
|
||||
spdlog::warn("Websocket session failed on {}: {}", what, ec.message());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::variant<Session::PlainWebSocket, Session::SSLWebSocket> Session::CreateWebSocket(tcp::socket &&socket,
|
||||
ServerContext &context) {
|
||||
if (context.use_ssl()) {
|
||||
ssl_context_.emplace(context.context_clone());
|
||||
return Session::SSLWebSocket{std::move(socket), *ssl_context_};
|
||||
}
|
||||
|
||||
return Session::PlainWebSocket{std::move(socket)};
|
||||
}
|
||||
|
||||
Session::Session(tcp::socket &&socket, ServerContext &context, AuthenticationInterface &auth)
|
||||
: ws_(CreateWebSocket(std::move(socket), context)), strand_{boost::asio::make_strand(GetExecutor())}, auth_{auth} {}
|
||||
|
||||
bool Session::Run() {
|
||||
ExecuteForWebsocket([](auto &&ws) {
|
||||
ws.set_option(boost::beast::websocket::stream_base::timeout::suggested(boost::beast::role_type::server));
|
||||
|
||||
ws.set_option(boost::beast::websocket::stream_base::decorator([](boost::beast::websocket::response_type &res) {
|
||||
res.set(boost::beast::http::field::server, "Memgraph WS");
|
||||
}));
|
||||
});
|
||||
|
||||
if (auto *ssl_ws = std::get_if<SSLWebSocket>(&ws_); ssl_ws != nullptr) {
|
||||
try {
|
||||
boost::beast::get_lowest_layer(*ssl_ws).expires_after(std::chrono::seconds(30));
|
||||
ssl_ws->next_layer().handshake(boost::asio::ssl::stream_base::server);
|
||||
} catch (const boost::system::system_error &e) {
|
||||
spdlog::warn("Failed on SSL handshake: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto result = ExecuteForWebsocket([](auto &&ws) -> bool {
|
||||
// Accept the websocket handshake
|
||||
boost::beast::error_code ec;
|
||||
ws.accept(ec);
|
||||
if (ec) {
|
||||
LogError(ec, "accept");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
authenticated_ = !auth_.HasAnyUsers();
|
||||
connected_.store(true, std::memory_order_relaxed);
|
||||
|
||||
// run on the strand
|
||||
boost::asio::dispatch(strand_, [shared_this = shared_from_this()] { shared_this->DoRead(); });
|
||||
return true;
|
||||
}
|
||||
|
||||
void Session::Write(std::shared_ptr<std::string> message) {
|
||||
boost::asio::dispatch(strand_, [message = std::move(message), shared_this = shared_from_this()]() mutable {
|
||||
if (!shared_this->connected_.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
if (!shared_this->IsAuthenticated()) {
|
||||
return;
|
||||
}
|
||||
shared_this->messages_.push_back(std::move(message));
|
||||
if (shared_this->messages_.size() > 1) {
|
||||
return;
|
||||
}
|
||||
shared_this->DoWrite();
|
||||
});
|
||||
}
|
||||
|
||||
bool Session::IsConnected() const { return connected_.load(std::memory_order_relaxed); }
|
||||
|
||||
void Session::DoWrite() {
|
||||
ExecuteForWebsocket([this](auto &&ws) {
|
||||
auto next_message = messages_.front();
|
||||
ws.async_write(boost::asio::buffer(*next_message),
|
||||
boost::asio::bind_executor(
|
||||
strand_, [message_string = std::move(next_message), shared_this = shared_from_this()](
|
||||
boost::beast::error_code ec, const size_t bytes_transferred) {
|
||||
shared_this->OnWrite(ec, bytes_transferred);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
void Session::OnWrite(boost::beast::error_code ec, size_t /*bytes_transferred*/) {
|
||||
messages_.pop_front();
|
||||
|
||||
if (close_) {
|
||||
DoShutdown();
|
||||
return;
|
||||
}
|
||||
if (ec) {
|
||||
close_ = true;
|
||||
return LogError(ec, "write");
|
||||
}
|
||||
if (!messages_.empty()) {
|
||||
DoWrite();
|
||||
}
|
||||
}
|
||||
|
||||
void Session::DoRead() {
|
||||
ExecuteForWebsocket([this](auto &&ws) {
|
||||
ws.async_read(buffer_, boost::asio::bind_executor(strand_, std::bind_front(&Session::OnRead, shared_from_this())));
|
||||
});
|
||||
;
|
||||
}
|
||||
|
||||
void Session::DoClose() {
|
||||
ExecuteForWebsocket([this](auto &&ws) mutable {
|
||||
ws.async_close(boost::beast::websocket::close_code::normal,
|
||||
boost::asio::bind_executor(strand_, [shared_this = shared_from_this()](boost::beast::error_code ec) {
|
||||
shared_this->OnClose(ec);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
void Session::OnClose(boost::beast::error_code ec) {
|
||||
connected_.store(false, std::memory_order_relaxed);
|
||||
if (ec) {
|
||||
return LogError(ec, "close");
|
||||
}
|
||||
}
|
||||
|
||||
utils::BasicResult<std::string> Session::Authorize(const nlohmann::json &creds) {
|
||||
if (!auth_.Authenticate(creds.at("username").get<std::string>(), creds.at("password").get<std::string>())) {
|
||||
return {"Authentication failed!"};
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (!auth_.HasUserPermission(creds.at("username").get<std::string>(), auth::Permission::WEBSOCKET)) {
|
||||
return {"Authorization failed!"};
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
void Session::OnRead(const boost::beast::error_code ec, const size_t /*bytes_transferred*/) {
|
||||
if (ec == boost::beast::websocket::error::closed) {
|
||||
DoShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsAuthenticated()) {
|
||||
auto response = nlohmann::json();
|
||||
auto auth_failed = [this, &response](const std::string &message) {
|
||||
response["success"] = false;
|
||||
response["message"] = message;
|
||||
MG_ASSERT(messages_.empty());
|
||||
messages_.push_back(std::make_shared<std::string>(response.dump()));
|
||||
close_ = true;
|
||||
DoWrite();
|
||||
};
|
||||
try {
|
||||
const auto creds = nlohmann::json::parse(boost::beast::buffers_to_string(buffer_.data()));
|
||||
buffer_.consume(buffer_.size());
|
||||
|
||||
if (const auto result = Authorize(creds); result.HasError()) {
|
||||
std::invoke(auth_failed, result.GetError());
|
||||
return;
|
||||
}
|
||||
response["success"] = true;
|
||||
response["message"] = "User has been successfully authenticated!";
|
||||
MG_ASSERT(messages_.empty());
|
||||
authenticated_ = true;
|
||||
messages_.push_back(std::make_shared<std::string>(response.dump()));
|
||||
DoWrite();
|
||||
} catch (const nlohmann::json::out_of_range &out_of_range) {
|
||||
const auto err_msg = fmt::format("Invalid JSON for authentication received: {}!", out_of_range.what());
|
||||
spdlog::error(err_msg);
|
||||
std::invoke(auth_failed, err_msg);
|
||||
return;
|
||||
} catch (const nlohmann::json::parse_error &parse_error) {
|
||||
const auto err_msg = fmt::format("Cannot parse JSON for WebSocket authentication: {}!", parse_error.what());
|
||||
spdlog::error(err_msg);
|
||||
std::invoke(auth_failed, err_msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DoRead();
|
||||
}
|
||||
|
||||
bool Session::IsAuthenticated() const { return authenticated_; }
|
||||
|
||||
void Session::DoShutdown() {
|
||||
std::visit(utils::Overloaded{[this](SSLWebSocket &ssl_ws) {
|
||||
boost::beast::get_lowest_layer(ssl_ws).expires_after(std::chrono::seconds(30));
|
||||
ssl_ws.next_layer().async_shutdown(
|
||||
[shared_this = shared_from_this()](boost::beast::error_code ec) {
|
||||
if (ec) {
|
||||
LogError(ec, "shutdown");
|
||||
}
|
||||
shared_this->DoClose();
|
||||
});
|
||||
},
|
||||
[this](auto && /* plain_ws */) { DoClose(); }},
|
||||
ws_);
|
||||
}
|
||||
|
||||
} // namespace communication::websocket
|
||||
@@ -1,91 +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
|
||||
|
||||
#define BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/beast/core/tcp_stream.hpp>
|
||||
#include <boost/beast/ssl.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
#include <json/json.hpp>
|
||||
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/websocket/auth.hpp"
|
||||
#include "utils/result.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
namespace communication::websocket {
|
||||
class Session : public std::enable_shared_from_this<Session> {
|
||||
using tcp = boost::asio::ip::tcp;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
static std::shared_ptr<Session> Create(Args &&...args) {
|
||||
return std::shared_ptr<Session>{new Session{std::forward<Args>(args)...}};
|
||||
}
|
||||
|
||||
bool Run();
|
||||
void Write(std::shared_ptr<std::string> message);
|
||||
bool IsConnected() const;
|
||||
|
||||
private:
|
||||
using PlainWebSocket = boost::beast::websocket::stream<boost::beast::tcp_stream>;
|
||||
using SSLWebSocket = boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>>;
|
||||
|
||||
explicit Session(tcp::socket &&socket, ServerContext &context, AuthenticationInterface &auth);
|
||||
|
||||
void DoWrite();
|
||||
void OnWrite(boost::beast::error_code ec, size_t bytes_transferred);
|
||||
|
||||
void DoRead();
|
||||
void OnRead(boost::beast::error_code ec, size_t bytes_transferred);
|
||||
|
||||
void DoClose();
|
||||
void OnClose(boost::beast::error_code ec);
|
||||
|
||||
bool IsAuthenticated() const;
|
||||
|
||||
utils::BasicResult<std::string> Authorize(const nlohmann::json &creds);
|
||||
|
||||
void DoShutdown();
|
||||
|
||||
auto GetExecutor() {
|
||||
return std::visit(utils::Overloaded{[](auto &&ws) { return ws.get_executor(); }}, ws_);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
decltype(auto) ExecuteForWebsocket(F &&fn) {
|
||||
return std::visit(utils::Overloaded{std::forward<F>(fn)}, ws_);
|
||||
}
|
||||
|
||||
std::variant<PlainWebSocket, SSLWebSocket> CreateWebSocket(tcp::socket &&socket, ServerContext &context);
|
||||
|
||||
std::optional<boost::asio::ssl::context> ssl_context_;
|
||||
std::variant<PlainWebSocket, SSLWebSocket> ws_;
|
||||
boost::beast::flat_buffer buffer_;
|
||||
std::deque<std::shared_ptr<std::string>> messages_;
|
||||
boost::asio::strand<PlainWebSocket::executor_type> strand_;
|
||||
std::atomic<bool> connected_{false};
|
||||
bool authenticated_{false};
|
||||
bool close_{false};
|
||||
AuthenticationInterface &auth_;
|
||||
};
|
||||
} // namespace communication::websocket
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -51,12 +51,6 @@ auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege) {
|
||||
return auth::Permission::AUTH;
|
||||
case query::AuthQuery::Privilege::STREAM:
|
||||
return auth::Permission::STREAM;
|
||||
case query::AuthQuery::Privilege::MODULE_READ:
|
||||
return auth::Permission::MODULE_READ;
|
||||
case query::AuthQuery::Privilege::MODULE_WRITE:
|
||||
return auth::Permission::MODULE_WRITE;
|
||||
case query::AuthQuery::Privilege::WEBSOCKET:
|
||||
return auth::Permission::WEBSOCKET;
|
||||
}
|
||||
}
|
||||
} // namespace glue
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -10,14 +10,13 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace integrations {
|
||||
constexpr int64_t kDefaultCheckBatchLimit{1};
|
||||
constexpr std::chrono::milliseconds kDefaultBatchInterval{100};
|
||||
constexpr int64_t kDefaultBatchSize = 1000;
|
||||
constexpr int64_t kDefaultCheckBatchLimit = 1;
|
||||
constexpr std::chrono::milliseconds kDefaultCheckTimeout{30000};
|
||||
constexpr std::chrono::milliseconds kMinimumInterval{1};
|
||||
constexpr int64_t kMinimumSize{1};
|
||||
const std::string kReducted{"<REDUCTED>"};
|
||||
} // namespace integrations
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -35,13 +35,14 @@ utils::BasicResult<std::string, std::vector<Message>> GetBatch(RdKafka::KafkaCon
|
||||
std::atomic<bool> &is_running) {
|
||||
std::vector<Message> batch{};
|
||||
|
||||
batch.reserve(info.batch_size);
|
||||
int64_t batch_size = info.batch_size.value_or(kDefaultBatchSize);
|
||||
batch.reserve(batch_size);
|
||||
|
||||
auto remaining_timeout_in_ms = info.batch_interval.count();
|
||||
auto remaining_timeout_in_ms = info.batch_interval.value_or(kDefaultBatchInterval).count();
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
bool run_batch = true;
|
||||
for (int64_t i = 0; remaining_timeout_in_ms > 0 && i < info.batch_size && is_running.load(); ++i) {
|
||||
for (int64_t i = 0; remaining_timeout_in_ms > 0 && i < batch_size && is_running.load(); ++i) {
|
||||
std::unique_ptr<RdKafka::Message> msg(consumer.consume(remaining_timeout_in_ms));
|
||||
switch (msg->err()) {
|
||||
case RdKafka::ERR__TIMED_OUT:
|
||||
@@ -72,7 +73,7 @@ utils::BasicResult<std::string, std::vector<Message>> GetBatch(RdKafka::KafkaCon
|
||||
start = now;
|
||||
}
|
||||
|
||||
return std::move(batch);
|
||||
return {std::move(batch)};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -103,19 +104,14 @@ int64_t Message::Timestamp() const {
|
||||
return rd_kafka_message_timestamp(c_message, nullptr);
|
||||
}
|
||||
|
||||
int64_t Message::Offset() const {
|
||||
const auto *c_message = message_->c_ptr();
|
||||
return c_message->offset;
|
||||
}
|
||||
|
||||
Consumer::Consumer(ConsumerInfo info, ConsumerFunction consumer_function)
|
||||
: info_{std::move(info)}, consumer_function_(std::move(consumer_function)), cb_(info_.consumer_name) {
|
||||
: info_{std::move(info)}, consumer_function_(std::move(consumer_function)) {
|
||||
MG_ASSERT(consumer_function_, "Empty consumer function for Kafka consumer");
|
||||
// NOLINTNEXTLINE (modernize-use-nullptr)
|
||||
if (info_.batch_interval < kMinimumInterval) {
|
||||
if (info_.batch_interval.value_or(kMinimumInterval) < kMinimumInterval) {
|
||||
throw ConsumerFailedToInitializeException(info_.consumer_name, "Batch interval has to be positive!");
|
||||
}
|
||||
if (info_.batch_size < kMinimumSize) {
|
||||
if (info_.batch_size.value_or(kMinimumSize) < kMinimumSize) {
|
||||
throw ConsumerFailedToInitializeException(info_.consumer_name, "Batch size has to be positive!");
|
||||
}
|
||||
|
||||
@@ -126,26 +122,10 @@ Consumer::Consumer(ConsumerInfo info, ConsumerFunction consumer_function)
|
||||
|
||||
std::string error;
|
||||
|
||||
for (const auto &[key, value] : info_.public_configs) {
|
||||
if (conf->set(key, value, error) != RdKafka::Conf::CONF_OK) {
|
||||
throw SettingCustomConfigFailed(info_.consumer_name, error, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[key, value] : info_.private_configs) {
|
||||
if (conf->set(key, value, error) != RdKafka::Conf::CONF_OK) {
|
||||
throw SettingCustomConfigFailed(info_.consumer_name, error, key, kReducted);
|
||||
}
|
||||
}
|
||||
|
||||
if (conf->set("event_cb", this, error) != RdKafka::Conf::CONF_OK) {
|
||||
throw ConsumerFailedToInitializeException(info_.consumer_name, error);
|
||||
}
|
||||
|
||||
if (conf->set("rebalance_cb", &cb_, error) != RdKafka::Conf::CONF_OK) {
|
||||
throw ConsumerFailedToInitializeException(info_.consumer_name, error);
|
||||
}
|
||||
|
||||
if (conf->set("enable.partition.eof", "false", error) != RdKafka::Conf::CONF_OK) {
|
||||
throw ConsumerFailedToInitializeException(info_.consumer_name, error);
|
||||
}
|
||||
@@ -185,16 +165,7 @@ Consumer::Consumer(ConsumerInfo info, ConsumerFunction consumer_function)
|
||||
std::inserter(topic_names_from_metadata, topic_names_from_metadata.begin()),
|
||||
[](const auto topic_metadata) { return topic_metadata->topic(); });
|
||||
|
||||
constexpr size_t max_topic_name_length = 249;
|
||||
constexpr auto is_valid_topic_name = [](const auto c) { return std::isalnum(c) || c == '.' || c == '_' || c == '-'; };
|
||||
|
||||
for (const auto &topic_name : info_.topics) {
|
||||
if (topic_name.size() > max_topic_name_length ||
|
||||
std::any_of(topic_name.begin(), topic_name.end(), [&](const auto c) { return !is_valid_topic_name(c); })) {
|
||||
throw ConsumerFailedToInitializeException(info_.consumer_name,
|
||||
fmt::format("'{}' is an invalid topic name", topic_name));
|
||||
}
|
||||
|
||||
if (!topic_names_from_metadata.contains(topic_name)) {
|
||||
throw TopicNotFoundException(info_.consumer_name, topic_name);
|
||||
}
|
||||
@@ -264,18 +235,10 @@ void Consumer::Check(std::optional<std::chrono::milliseconds> timeout, std::opti
|
||||
utils::OnScopeExit restore_is_running([this] { is_running_.store(false); });
|
||||
|
||||
if (last_assignment_.empty()) {
|
||||
auto throw_consumer_check_failed = [this](const auto err) {
|
||||
if (const auto err = consumer_->assignment(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
|
||||
spdlog::warn("Saving the commited offset of consumer {} failed: {}", info_.consumer_name, RdKafka::err2str(err));
|
||||
throw ConsumerCheckFailedException(info_.consumer_name,
|
||||
fmt::format("Couldn't save commited offsets: '{}'", RdKafka::err2str(err)));
|
||||
};
|
||||
if (const auto err = consumer_->assignment(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
|
||||
spdlog::warn("Saving the assignment of consumer {} failed: {}", info_.consumer_name, RdKafka::err2str(err));
|
||||
throw_consumer_check_failed(err);
|
||||
}
|
||||
if (const auto err = consumer_->position(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
|
||||
spdlog::warn("Saving the position offset assignment of consumer {} failed: {}", info_.consumer_name,
|
||||
RdKafka::err2str(err));
|
||||
throw_consumer_check_failed(err);
|
||||
}
|
||||
} else {
|
||||
if (const auto err = consumer_->assign(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
|
||||
@@ -371,18 +334,7 @@ void Consumer::StartConsuming() {
|
||||
|
||||
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) {
|
||||
if (const auto err = consumer_->commitSync(); err != RdKafka::ERR_NO_ERROR) {
|
||||
spdlog::warn("Committing offset of consumer {} failed: {}", info_.consumer_name, RdKafka::err2str(err));
|
||||
break;
|
||||
}
|
||||
@@ -401,51 +353,4 @@ void Consumer::StopConsuming() {
|
||||
if (thread_.joinable()) thread_.join();
|
||||
}
|
||||
|
||||
utils::BasicResult<std::string> Consumer::SetConsumerOffsets(int64_t offset) {
|
||||
if (is_running_) {
|
||||
throw ConsumerRunningException(info_.consumer_name);
|
||||
}
|
||||
|
||||
if (offset == -1) {
|
||||
offset = RD_KAFKA_OFFSET_BEGINNING;
|
||||
} else if (offset == -2) {
|
||||
offset = RD_KAFKA_OFFSET_END;
|
||||
}
|
||||
|
||||
cb_.set_offset(offset);
|
||||
if (const auto err = consumer_->subscribe(info_.topics); err != RdKafka::ERR_NO_ERROR) {
|
||||
return fmt::format("Could not set offset of consumer: {}. Error: {}", info_.consumer_name, RdKafka::err2str(err));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Consumer::ConsumerRebalanceCb::ConsumerRebalanceCb(std::string consumer_name)
|
||||
: consumer_name_(std::move(consumer_name)) {}
|
||||
|
||||
void Consumer::ConsumerRebalanceCb::rebalance_cb(RdKafka::KafkaConsumer *consumer, RdKafka::ErrorCode err,
|
||||
std::vector<RdKafka::TopicPartition *> &partitions) {
|
||||
if (err == RdKafka::ERR__REVOKE_PARTITIONS) {
|
||||
consumer->unassign();
|
||||
return;
|
||||
}
|
||||
if (err != RdKafka::ERR__ASSIGN_PARTITIONS) {
|
||||
spdlog::critical("Consumer {} received an unexpected error {}", consumer_name_, RdKafka::err2str(err));
|
||||
return;
|
||||
}
|
||||
if (offset_) {
|
||||
for (auto &partition : partitions) {
|
||||
partition->set_offset(*offset_);
|
||||
}
|
||||
offset_.reset();
|
||||
}
|
||||
auto maybe_error = consumer->assign(partitions);
|
||||
if (maybe_error != RdKafka::ErrorCode::ERR_NO_ERROR) {
|
||||
spdlog::warn("Assigning offset of consumer {} failed: {}", consumer_name_, RdKafka::err2str(maybe_error));
|
||||
}
|
||||
maybe_error = consumer->commitSync(partitions);
|
||||
if (maybe_error != RdKafka::ErrorCode::ERR_NO_ERROR) {
|
||||
spdlog::warn("Commiting offsets of consumer {} failed: {}", consumer_name_, RdKafka::err2str(maybe_error));
|
||||
}
|
||||
}
|
||||
void Consumer::ConsumerRebalanceCb::set_offset(int64_t offset) { offset_ = offset; }
|
||||
} // namespace integrations::kafka
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -69,9 +68,6 @@ class Message final {
|
||||
/// can be implemented knowing that.
|
||||
int64_t Timestamp() const;
|
||||
|
||||
/// Returns the offset of the message
|
||||
int64_t Offset() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<RdKafka::Message> message_;
|
||||
};
|
||||
@@ -84,10 +80,8 @@ struct ConsumerInfo {
|
||||
std::vector<std::string> topics;
|
||||
std::string consumer_group;
|
||||
std::string bootstrap_servers;
|
||||
std::chrono::milliseconds batch_interval;
|
||||
int64_t batch_size;
|
||||
std::unordered_map<std::string, std::string> public_configs;
|
||||
std::unordered_map<std::string, std::string> private_configs;
|
||||
std::optional<std::chrono::milliseconds> batch_interval;
|
||||
std::optional<int64_t> batch_size;
|
||||
};
|
||||
|
||||
/// Memgraphs Kafka consumer wrapper.
|
||||
@@ -144,13 +138,6 @@ class Consumer final : public RdKafka::EventCb {
|
||||
/// Returns true if the consumer is actively consuming messages.
|
||||
bool IsRunning() const;
|
||||
|
||||
/// Sets the consumer's offset.
|
||||
///
|
||||
/// This function returns the empty string on success or an error message otherwise.
|
||||
///
|
||||
/// @param offset: the offset to set.
|
||||
[[nodiscard]] utils::BasicResult<std::string> SetConsumerOffsets(int64_t offset);
|
||||
|
||||
const ConsumerInfo &Info() const;
|
||||
|
||||
private:
|
||||
@@ -160,20 +147,6 @@ class Consumer final : public RdKafka::EventCb {
|
||||
|
||||
void StopConsuming();
|
||||
|
||||
class ConsumerRebalanceCb : public RdKafka::RebalanceCb {
|
||||
public:
|
||||
ConsumerRebalanceCb(std::string consumer_name);
|
||||
|
||||
void rebalance_cb(RdKafka::KafkaConsumer *consumer, RdKafka::ErrorCode err,
|
||||
std::vector<RdKafka::TopicPartition *> &partitions) override final;
|
||||
|
||||
void set_offset(int64_t offset);
|
||||
|
||||
private:
|
||||
std::optional<int64_t> offset_;
|
||||
std::string consumer_name_;
|
||||
};
|
||||
|
||||
ConsumerInfo info_;
|
||||
ConsumerFunction consumer_function_;
|
||||
mutable std::atomic<bool> is_running_{false};
|
||||
@@ -181,6 +154,5 @@ class Consumer final : public RdKafka::EventCb {
|
||||
std::optional<int64_t> limit_batches_{std::nullopt};
|
||||
std::unique_ptr<RdKafka::KafkaConsumer, std::function<void(RdKafka::KafkaConsumer *)>> consumer_;
|
||||
std::thread thread_;
|
||||
ConsumerRebalanceCb cb_;
|
||||
};
|
||||
} // namespace integrations::kafka
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
@@ -22,46 +22,37 @@ class KafkaStreamException : public utils::BasicException {
|
||||
|
||||
class ConsumerFailedToInitializeException : public KafkaStreamException {
|
||||
public:
|
||||
ConsumerFailedToInitializeException(const std::string_view consumer_name, const std::string_view error)
|
||||
ConsumerFailedToInitializeException(const std::string &consumer_name, const std::string &error)
|
||||
: KafkaStreamException("Failed to initialize Kafka consumer {} : {}", consumer_name, error) {}
|
||||
};
|
||||
|
||||
class SettingCustomConfigFailed : public ConsumerFailedToInitializeException {
|
||||
public:
|
||||
SettingCustomConfigFailed(const std::string_view consumer_name, const std::string_view error,
|
||||
const std::string_view key, const std::string_view value)
|
||||
: ConsumerFailedToInitializeException(
|
||||
consumer_name,
|
||||
fmt::format(R"(failed to set custom config ("{}": "{}"), because of error {})", key, value, error)) {}
|
||||
};
|
||||
|
||||
class ConsumerRunningException : public KafkaStreamException {
|
||||
public:
|
||||
explicit ConsumerRunningException(const std::string_view consumer_name)
|
||||
explicit ConsumerRunningException(const std::string &consumer_name)
|
||||
: KafkaStreamException("Kafka consumer {} is already running", consumer_name) {}
|
||||
};
|
||||
|
||||
class ConsumerStoppedException : public KafkaStreamException {
|
||||
public:
|
||||
explicit ConsumerStoppedException(const std::string_view consumer_name)
|
||||
explicit ConsumerStoppedException(const std::string &consumer_name)
|
||||
: KafkaStreamException("Kafka consumer {} is already stopped", consumer_name) {}
|
||||
};
|
||||
|
||||
class ConsumerCheckFailedException : public KafkaStreamException {
|
||||
public:
|
||||
explicit ConsumerCheckFailedException(const std::string_view consumer_name, const std::string_view error)
|
||||
explicit ConsumerCheckFailedException(const std::string &consumer_name, const std::string &error)
|
||||
: KafkaStreamException("Kafka consumer {} check failed: {}", consumer_name, error) {}
|
||||
};
|
||||
|
||||
class ConsumerStartFailedException : public KafkaStreamException {
|
||||
public:
|
||||
explicit ConsumerStartFailedException(const std::string_view consumer_name, const std::string_view error)
|
||||
explicit ConsumerStartFailedException(const std::string &consumer_name, const std::string &error)
|
||||
: KafkaStreamException("Starting Kafka consumer {} failed: {}", consumer_name, error) {}
|
||||
};
|
||||
|
||||
class TopicNotFoundException : public KafkaStreamException {
|
||||
public:
|
||||
TopicNotFoundException(const std::string_view consumer_name, const std::string_view topic_name)
|
||||
TopicNotFoundException(const std::string &consumer_name, const std::string &topic_name)
|
||||
: KafkaStreamException("Kafka consumer {} cannot find topic {}", consumer_name, topic_name) {}
|
||||
};
|
||||
} // namespace integrations::kafka
|
||||
|
||||
@@ -45,25 +45,23 @@ pulsar_client::Result ConsumeMessage(pulsar_client::Reader &reader, pulsar_clien
|
||||
|
||||
template <PulsarConsumer TConsumer>
|
||||
utils::BasicResult<std::string, std::vector<Message>> GetBatch(TConsumer &consumer, const ConsumerInfo &info,
|
||||
std::atomic<bool> &is_running,
|
||||
const pulsar_client::MessageId &last_message_id) {
|
||||
std::atomic<bool> &is_running) {
|
||||
std::vector<Message> batch{};
|
||||
|
||||
batch.reserve(info.batch_size);
|
||||
const auto batch_size = info.batch_size.value_or(kDefaultBatchSize);
|
||||
batch.reserve(batch_size);
|
||||
|
||||
auto remaining_timeout_in_ms = info.batch_interval.count();
|
||||
auto remaining_timeout_in_ms = info.batch_interval.value_or(kDefaultBatchInterval).count();
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
while (remaining_timeout_in_ms > 0 && batch.size() < info.batch_size && is_running) {
|
||||
for (int64_t i = 0; remaining_timeout_in_ms > 0 && i < batch_size && is_running.load(); ++i) {
|
||||
pulsar_client::Message message;
|
||||
const auto result = ConsumeMessage(consumer, message, remaining_timeout_in_ms);
|
||||
switch (result) {
|
||||
case pulsar_client::Result::ResultTimeout:
|
||||
return std::move(batch);
|
||||
case pulsar_client::Result::ResultOk:
|
||||
if (message.getMessageId() != last_message_id) {
|
||||
batch.emplace_back(Message{std::move(message)});
|
||||
}
|
||||
batch.emplace_back(Message{std::move(message)});
|
||||
break;
|
||||
default:
|
||||
spdlog::warn(fmt::format("Unexpected error while consuming message from consumer {}, error: {}",
|
||||
@@ -89,7 +87,15 @@ class SpdlogLogger : public pulsar_client::Logger {
|
||||
};
|
||||
|
||||
class SpdlogLoggerFactory : public pulsar_client::LoggerFactory {
|
||||
pulsar_client::Logger *getLogger(const std::string & /*file_name*/) override { return new SpdlogLogger; }
|
||||
pulsar_client::Logger *getLogger(const std::string & /*file_name*/) override {
|
||||
if (!logger_) {
|
||||
logger_ = std::make_unique<SpdlogLogger>();
|
||||
}
|
||||
return logger_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<SpdlogLogger> logger_;
|
||||
};
|
||||
|
||||
pulsar_client::Client CreateClient(const std::string &service_url) {
|
||||
@@ -105,8 +111,6 @@ std::span<const char> Message::Payload() const {
|
||||
return {static_cast<const char *>(message_.getData()), message_.getLength()};
|
||||
}
|
||||
|
||||
std::string_view Message::TopicName() const { return message_.getTopicName(); }
|
||||
|
||||
Consumer::Consumer(ConsumerInfo info, ConsumerFunction consumer_function)
|
||||
: info_{std::move(info)},
|
||||
client_{CreateClient(info_.service_url)},
|
||||
@@ -181,7 +185,7 @@ void Consumer::Check(std::optional<std::chrono::milliseconds> timeout, std::opti
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
|
||||
if (info_.topics.size() != 1) {
|
||||
throw ConsumerCheckFailedException(info_.consumer_name, "Check cannot be used for consumers with multiple topics.");
|
||||
throw ConsumerCheckFailedException(info_.consumer_name, "Check cannot be used for multiple topics");
|
||||
}
|
||||
|
||||
std::vector<std::string> partitions;
|
||||
@@ -199,7 +203,7 @@ void Consumer::Check(std::optional<std::chrono::milliseconds> timeout, std::opti
|
||||
throw ConsumerCheckFailedException(info_.consumer_name, "Timeout reached");
|
||||
}
|
||||
|
||||
auto maybe_batch = GetBatch(reader, info_, is_running_, last_message_id_);
|
||||
auto maybe_batch = GetBatch(reader, info_, is_running_);
|
||||
|
||||
if (maybe_batch.HasError()) {
|
||||
throw ConsumerCheckFailedException(info_.consumer_name, maybe_batch.GetError());
|
||||
@@ -237,7 +241,7 @@ void Consumer::StartConsuming() {
|
||||
utils::ThreadSetName(full_thread_name.substr(0, kMaxThreadNameSize));
|
||||
|
||||
while (is_running_) {
|
||||
auto maybe_batch = GetBatch(consumer_, info_, is_running_, last_message_id_);
|
||||
auto maybe_batch = GetBatch(consumer_, info_, is_running_);
|
||||
|
||||
if (maybe_batch.HasError()) {
|
||||
spdlog::warn("Error happened in consumer {} while fetching messages: {}!", info_.consumer_name,
|
||||
|
||||
@@ -28,7 +28,6 @@ class Message final {
|
||||
explicit Message(pulsar_client::Message &&message);
|
||||
|
||||
std::span<const char> Payload() const;
|
||||
std::string_view TopicName() const;
|
||||
|
||||
private:
|
||||
pulsar_client::Message message_;
|
||||
@@ -39,8 +38,8 @@ class Message final {
|
||||
using ConsumerFunction = std::function<void(const std::vector<Message> &)>;
|
||||
|
||||
struct ConsumerInfo {
|
||||
int64_t batch_size;
|
||||
std::chrono::milliseconds batch_interval;
|
||||
std::optional<int64_t> batch_size;
|
||||
std::optional<std::chrono::milliseconds> batch_interval;
|
||||
std::vector<std::string> topics;
|
||||
std::string consumer_name;
|
||||
std::string service_url;
|
||||
|
||||
@@ -4,8 +4,5 @@ set(io_src_files
|
||||
network/socket.cpp
|
||||
network/utils.cpp)
|
||||
|
||||
find_package(fmt REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_library(mg-io STATIC ${io_src_files})
|
||||
target_link_libraries(mg-io stdc++fs Threads::Threads fmt::fmt mg-utils)
|
||||
target_link_libraries(mg-io stdc++fs Threads::Threads fmt mg-utils)
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
find_package(gflags REQUIRED)
|
||||
find_package(BZip2 REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
# STATIC library used to store key-value pairs
|
||||
add_library(mg-kvstore STATIC kvstore.cpp)
|
||||
target_link_libraries(mg-kvstore stdc++fs mg-utils rocksdb BZip2::BZip2 ZLIB::ZLIB gflags)
|
||||
target_link_libraries(mg-kvstore stdc++fs mg-utils rocksdb bzip2 zlib gflags)
|
||||
|
||||
# STATIC library for dummy key-value storage
|
||||
# add_library(mg-kvstore-dummy STATIC kvstore_dummy.cpp)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -29,12 +29,9 @@
|
||||
#include <gflags/gflags.h>
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/sinks/daily_file_sink.h>
|
||||
#include <spdlog/sinks/dist_sink.h>
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
|
||||
#include "communication/bolt/v1/constants.hpp"
|
||||
#include "communication/websocket/auth.hpp"
|
||||
#include "communication/websocket/server.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "py/py.hpp"
|
||||
#include "query/auth_checker.hpp"
|
||||
@@ -130,15 +127,8 @@ std::optional<Enum> StringToEnum(const auto &value, const auto &mappings) {
|
||||
|
||||
// Bolt server flags.
|
||||
DEFINE_string(bolt_address, "0.0.0.0", "IP address on which the Bolt server should listen.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(monitoring_address, "0.0.0.0",
|
||||
"IP address on which the websocket server for Memgraph monitoring should listen.");
|
||||
DEFINE_VALIDATED_int32(bolt_port, 7687, "Port on which the Bolt server should listen.",
|
||||
FLAG_IN_RANGE(0, std::numeric_limits<uint16_t>::max()));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_int32(monitoring_port, 7444,
|
||||
"Port on which the websocket server for Memgraph monitoring should listen.",
|
||||
FLAG_IN_RANGE(0, std::numeric_limits<uint16_t>::max()));
|
||||
DEFINE_VALIDATED_int32(bolt_num_workers, std::max(std::thread::hardware_concurrency(), 1U),
|
||||
"Number of workers used by the Bolt server. By default, this will be the "
|
||||
"number of processing units available on the machine.",
|
||||
@@ -196,22 +186,10 @@ DEFINE_bool(telemetry_enabled, false,
|
||||
"the database runtime (vertex and edge counts and resource usage) "
|
||||
"to allow for easier improvement of the product.");
|
||||
|
||||
// Streams flags
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint32(
|
||||
stream_transaction_conflict_retries, 30,
|
||||
"Number of times to retry when a stream transformation fails to commit because of conflicting transactions");
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint32(
|
||||
stream_transaction_retry_interval, 500,
|
||||
"Retry interval in milliseconds when a stream transformation fails to commit because of conflicting transactions");
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(kafka_bootstrap_servers, "",
|
||||
"List of default Kafka brokers as a comma separated list of broker host or host:port.");
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(pulsar_service_url, "", "Default URL used while connecting to Pulsar brokers.");
|
||||
|
||||
// Audit logging flags.
|
||||
#ifdef MG_ENTERPRISE
|
||||
DEFINE_bool(audit_enabled, false, "Set to true to enable audit logging.");
|
||||
@@ -354,49 +332,39 @@ DEFINE_VALIDATED_string(log_level, "WARNING", log_level_help_string.c_str(), {
|
||||
});
|
||||
|
||||
namespace {
|
||||
spdlog::level::level_enum ParseLogLevel() {
|
||||
void ParseLogLevel() {
|
||||
const auto log_level = StringToEnum<spdlog::level::level_enum>(FLAGS_log_level, log_level_mappings);
|
||||
MG_ASSERT(log_level, "Invalid log level");
|
||||
return *log_level;
|
||||
spdlog::set_level(*log_level);
|
||||
}
|
||||
|
||||
// 5 weeks * 7 days
|
||||
constexpr auto log_retention_count = 35;
|
||||
void CreateLoggerFromSink(const auto &sinks, const auto log_level) {
|
||||
auto logger = std::make_shared<spdlog::logger>("memgraph_log", sinks.begin(), sinks.end());
|
||||
logger->set_level(log_level);
|
||||
logger->flush_on(spdlog::level::trace);
|
||||
spdlog::set_default_logger(std::move(logger));
|
||||
}
|
||||
|
||||
void InitializeLogger() {
|
||||
std::vector<spdlog::sink_ptr> sinks;
|
||||
void ConfigureLogging() {
|
||||
std::vector<spdlog::sink_ptr> loggers;
|
||||
|
||||
if (FLAGS_also_log_to_stderr) {
|
||||
sinks.emplace_back(std::make_shared<spdlog::sinks::stderr_color_sink_mt>());
|
||||
loggers.emplace_back(std::make_shared<spdlog::sinks::stderr_color_sink_mt>());
|
||||
}
|
||||
|
||||
if (!FLAGS_log_file.empty()) {
|
||||
// get local time
|
||||
time_t current_time{0};
|
||||
time_t current_time;
|
||||
struct tm *local_time{nullptr};
|
||||
|
||||
time(¤t_time);
|
||||
local_time = localtime(¤t_time);
|
||||
|
||||
sinks.emplace_back(std::make_shared<spdlog::sinks::daily_file_sink_mt>(
|
||||
loggers.emplace_back(std::make_shared<spdlog::sinks::daily_file_sink_mt>(
|
||||
FLAGS_log_file, local_time->tm_hour, local_time->tm_min, false, log_retention_count));
|
||||
}
|
||||
CreateLoggerFromSink(sinks, ParseLogLevel());
|
||||
}
|
||||
|
||||
void AddLoggerSink(spdlog::sink_ptr new_sink) {
|
||||
auto default_logger = spdlog::default_logger();
|
||||
auto sinks = default_logger->sinks();
|
||||
sinks.push_back(new_sink);
|
||||
CreateLoggerFromSink(sinks, default_logger->level());
|
||||
}
|
||||
spdlog::set_default_logger(std::make_shared<spdlog::logger>("memgraph_log", loggers.begin(), loggers.end()));
|
||||
|
||||
spdlog::flush_on(spdlog::level::trace);
|
||||
ParseLogLevel();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -1007,7 +975,7 @@ int main(int argc, char **argv) {
|
||||
LoadConfig("memgraph");
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
InitializeLogger();
|
||||
ConfigureLogging();
|
||||
|
||||
// Unhandled exception handler init.
|
||||
std::set_terminate(&utils::TerminateHandler);
|
||||
@@ -1151,22 +1119,18 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
storage::Storage db(db_config);
|
||||
|
||||
query::InterpreterContext interpreter_context{
|
||||
&db,
|
||||
{.query = {.allow_load_csv = FLAGS_allow_load_csv},
|
||||
.execution_timeout_sec = FLAGS_query_execution_timeout_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,
|
||||
.stream_transaction_retry_interval = std::chrono::milliseconds(FLAGS_stream_transaction_retry_interval)},
|
||||
FLAGS_data_directory};
|
||||
query::InterpreterContext interpreter_context{&db,
|
||||
{.query = {.allow_load_csv = FLAGS_allow_load_csv},
|
||||
.execution_timeout_sec = FLAGS_query_execution_timeout_sec,
|
||||
.default_kafka_bootstrap_servers = FLAGS_kafka_bootstrap_servers},
|
||||
FLAGS_data_directory};
|
||||
#ifdef MG_ENTERPRISE
|
||||
SessionData session_data{&db, &interpreter_context, &auth, &audit_log};
|
||||
#else
|
||||
SessionData session_data{&db, &interpreter_context, &auth};
|
||||
#endif
|
||||
|
||||
query::procedure::gModuleRegistry.SetModulesDirectory(query_modules_directories, FLAGS_data_directory);
|
||||
query::procedure::gModuleRegistry.SetModulesDirectory(query_modules_directories);
|
||||
query::procedure::gModuleRegistry.UnloadAndLoadModulesFromDirectories();
|
||||
|
||||
AuthQueryHandler auth_handler(&auth, FLAGS_auth_user_or_role_name_regex);
|
||||
@@ -1220,13 +1184,8 @@ int main(int argc, char **argv) {
|
||||
[]() -> nlohmann::json { return query::plan::CallProcedure::GetAndResetCounters(); });
|
||||
}
|
||||
|
||||
communication::websocket::SafeAuth websocket_auth{&auth};
|
||||
communication::websocket::Server websocket_server{
|
||||
{FLAGS_monitoring_address, static_cast<uint16_t>(FLAGS_monitoring_port)}, &context, websocket_auth};
|
||||
AddLoggerSink(websocket_server.GetLoggingSink());
|
||||
|
||||
// Handler for regular termination signals
|
||||
auto shutdown = [&websocket_server, &server, &interpreter_context] {
|
||||
auto shutdown = [&server, &interpreter_context] {
|
||||
// Server needs to be shutdown first and then the database. This prevents
|
||||
// a race condition when a transaction is accepted during server shutdown.
|
||||
server.Shutdown();
|
||||
@@ -1234,17 +1193,11 @@ int main(int argc, char **argv) {
|
||||
// connections we tell the execution engine to stop processing all pending
|
||||
// queries.
|
||||
query::Shutdown(&interpreter_context);
|
||||
websocket_server.Shutdown();
|
||||
};
|
||||
|
||||
InitSignalHandlers(shutdown);
|
||||
|
||||
MG_ASSERT(server.Start(), "Couldn't start the Bolt server!");
|
||||
websocket_server.Start();
|
||||
|
||||
server.AwaitShutdown();
|
||||
websocket_server.AwaitShutdown();
|
||||
|
||||
query::procedure::gModuleRegistry.UnloadAllModules();
|
||||
|
||||
Py_END_ALLOW_THREADS;
|
||||
|
||||
@@ -2,11 +2,9 @@ set(memory_src_files
|
||||
new_delete.cpp
|
||||
memory_control.cpp)
|
||||
|
||||
find_package(Jemalloc REQUIRED)
|
||||
|
||||
add_library(mg-memory STATIC ${memory_src_files})
|
||||
target_link_libraries(mg-memory mg-utils fmt)
|
||||
|
||||
if (ENABLE_JEMALLOC)
|
||||
target_link_libraries(mg-memory Jemalloc::Jemalloc)
|
||||
target_link_libraries(mg-memory jemalloc)
|
||||
endif()
|
||||
|
||||
@@ -20,7 +20,6 @@ set(mg_query_sources
|
||||
interpret/awesome_memgraph_functions.cpp
|
||||
interpret/eval.cpp
|
||||
interpreter.cpp
|
||||
metadata.cpp
|
||||
plan/operator.cpp
|
||||
plan/preprocess.cpp
|
||||
plan/pretty_print.cpp
|
||||
@@ -30,7 +29,6 @@ set(mg_query_sources
|
||||
plan/rule_based_planner.cpp
|
||||
plan/variable_start_planner.cpp
|
||||
procedure/mg_procedure_impl.cpp
|
||||
procedure/mg_procedure_helpers.cpp
|
||||
procedure/module.cpp
|
||||
procedure/py_module.cpp
|
||||
serialization/property_value.cpp
|
||||
@@ -41,16 +39,11 @@ set(mg_query_sources
|
||||
trigger_context.cpp
|
||||
typed_value.cpp)
|
||||
|
||||
find_package(Boost REQUIRED)
|
||||
|
||||
add_library(mg-query STATIC ${mg_query_sources})
|
||||
add_dependencies(mg-query generate_lcp_query)
|
||||
target_include_directories(mg-query PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(mg-query dl cppitertools Boost::headers)
|
||||
target_link_libraries(mg-query mg-integrations-pulsar mg-integrations-kafka mg-storage-v2 mg-license mg-utils mg-kvstore mg-memory)
|
||||
if(NOT "${MG_PYTHON_PATH}" STREQUAL "")
|
||||
set(Python3_ROOT_DIR "${MG_PYTHON_PATH}")
|
||||
endif()
|
||||
target_link_libraries(mg-query dl cppitertools)
|
||||
target_link_libraries(mg-query mg-integrations-pulsar mg-integrations-kafka mg-storage-v2 mg-utils mg-kvstore mg-memory)
|
||||
if("${MG_PYTHON_VERSION}" STREQUAL "")
|
||||
find_package(Python3 3.5 REQUIRED COMPONENTS Development)
|
||||
else()
|
||||
|
||||
@@ -42,28 +42,15 @@ bool TypedValueCompare(const TypedValue &a, const TypedValue &b) {
|
||||
else
|
||||
return a.ValueDouble() < b.ValueDouble();
|
||||
case TypedValue::Type::String:
|
||||
// NOLINTNEXTLINE(modernize-use-nullptr)
|
||||
return a.ValueString() < b.ValueString();
|
||||
case TypedValue::Type::Date:
|
||||
// NOLINTNEXTLINE(modernize-use-nullptr)
|
||||
return a.ValueDate() < b.ValueDate();
|
||||
case TypedValue::Type::LocalTime:
|
||||
// NOLINTNEXTLINE(modernize-use-nullptr)
|
||||
return a.ValueLocalTime() < b.ValueLocalTime();
|
||||
case TypedValue::Type::LocalDateTime:
|
||||
// NOLINTNEXTLINE(modernize-use-nullptr)
|
||||
return a.ValueLocalDateTime() < b.ValueLocalDateTime();
|
||||
case TypedValue::Type::Duration:
|
||||
// NOLINTNEXTLINE(modernize-use-nullptr)
|
||||
return a.ValueDuration() < b.ValueDuration();
|
||||
case TypedValue::Type::List:
|
||||
case TypedValue::Type::Map:
|
||||
case TypedValue::Type::Vertex:
|
||||
case TypedValue::Type::Edge:
|
||||
case TypedValue::Type::Path:
|
||||
throw QueryRuntimeException("Comparison is not defined for values of type {}.", a.type());
|
||||
case TypedValue::Type::Null:
|
||||
LOG_FATAL("Invalid type");
|
||||
default:
|
||||
LOG_FATAL("Unhandled comparison for types");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ namespace impl {
|
||||
bool TypedValueCompare(const TypedValue &a, const TypedValue &b);
|
||||
} // namespace impl
|
||||
|
||||
constexpr inline std::string_view kSerializationErrorMessage{
|
||||
"Cannot resolve conflicting transactions. You can retry this transaction when the conflicting transaction is "
|
||||
"finished."};
|
||||
|
||||
/// Custom Comparator type for comparing vectors of TypedValues.
|
||||
///
|
||||
/// Does lexicographical ordering of elements based on the above
|
||||
@@ -91,7 +95,7 @@ storage::PropertyValue PropsSetChecked(T *record, const storage::PropertyId &key
|
||||
if (maybe_old_value.HasError()) {
|
||||
switch (maybe_old_value.GetError()) {
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to set properties on a deleted object.");
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace query {
|
||||
@@ -23,8 +22,5 @@ struct InterpreterConfig {
|
||||
double execution_timeout_sec{600.0};
|
||||
|
||||
std::string default_kafka_bootstrap_servers;
|
||||
std::string default_pulsar_service_url;
|
||||
uint32_t stream_transaction_conflict_retries;
|
||||
std::chrono::milliseconds stream_transaction_retry_interval;
|
||||
};
|
||||
} // namespace query
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
#include "query/common.hpp"
|
||||
#include "query/frontend/semantic/symbol_table.hpp"
|
||||
#include "query/metadata.hpp"
|
||||
#include "query/parameters.hpp"
|
||||
#include "query/plan/profile.hpp"
|
||||
#include "query/trigger.hpp"
|
||||
@@ -69,7 +68,6 @@ struct ExecutionContext {
|
||||
std::chrono::duration<double> profile_execution_time;
|
||||
plan::ProfilingStats stats;
|
||||
plan::ProfilingStats *stats_root{nullptr};
|
||||
ExecutionStats execution_stats;
|
||||
TriggerContextCollector *trigger_context_collector{nullptr};
|
||||
utils::AsyncTimer timer;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -11,13 +11,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// THIS INCLUDE SHOULD ALWAYS COME BEFORE THE
|
||||
// "cypher_main_visitor.hpp"
|
||||
// "planner.hpp" includes json.hpp which uses libc's
|
||||
// EOF macro while "cypher_main_visitor.hpp" includes
|
||||
// "antlr4-runtime.h" which contains a static variable
|
||||
// of the same name, EOF.
|
||||
// This hides the definition of the macro which causes
|
||||
// the compilation to fail.
|
||||
#include "query/plan/planner.hpp"
|
||||
//////////////////////////////////////////////////////
|
||||
#include "query/config.hpp"
|
||||
#include "query/frontend/ast/cypher_main_visitor.hpp"
|
||||
#include "query/frontend/opencypher/parser.hpp"
|
||||
#include "query/frontend/semantic/required_privileges.hpp"
|
||||
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||
#include "query/frontend/stripped.hpp"
|
||||
#include "query/plan/planner.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
|
||||
|
||||
@@ -130,18 +130,6 @@ class ExplicitTransactionUsageException : public QueryRuntimeException {
|
||||
using QueryRuntimeException::QueryRuntimeException;
|
||||
};
|
||||
|
||||
/**
|
||||
* An exception for serialization error
|
||||
*/
|
||||
class TransactionSerializationException : public QueryException {
|
||||
public:
|
||||
using QueryException::QueryException;
|
||||
TransactionSerializationException()
|
||||
: QueryException(
|
||||
"Cannot resolve conflicting transactions. You can retry this transaction when the conflicting transaction "
|
||||
"is finished") {}
|
||||
};
|
||||
|
||||
class ReconstructionException : public QueryException {
|
||||
public:
|
||||
ReconstructionException()
|
||||
@@ -217,11 +205,4 @@ class SettingConfigInMulticommandTxException final : public QueryException {
|
||||
SettingConfigInMulticommandTxException()
|
||||
: QueryException("Settings cannot be changed or fetched in multicommand transactions.") {}
|
||||
};
|
||||
|
||||
class VersionInfoInMulticommandTxException : public QueryException {
|
||||
public:
|
||||
VersionInfoInMulticommandTxException()
|
||||
: QueryException("Version info query not allowed in multicommand transactions.") {}
|
||||
};
|
||||
|
||||
} // namespace query
|
||||
|
||||
@@ -54,7 +54,9 @@ cpp<#
|
||||
size_t size = 0;
|
||||
slk::Load(&size, reader);
|
||||
self->${member}.resize(size);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
for (size_t i = 0;
|
||||
i < size;
|
||||
++i) {
|
||||
self->${member}[i] = query::LoadAstPointer<query::${type}>(storage, reader);
|
||||
}
|
||||
cpp<#))
|
||||
@@ -73,7 +75,9 @@ cpp<#
|
||||
#>cpp
|
||||
size_t size = 0;
|
||||
slk::Load(&size, reader);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
for (size_t i = 0;
|
||||
i < size;
|
||||
++i) {
|
||||
query::PropertyIx key;
|
||||
slk::Load(&key, reader, storage);
|
||||
auto *value = query::LoadAstPointer<query::Expression>(storage, reader);
|
||||
@@ -89,34 +93,6 @@ cpp<#
|
||||
}
|
||||
cpp<#)
|
||||
|
||||
(defun slk-save-expression-map (member)
|
||||
#>cpp
|
||||
size_t size = self.${member}.size();
|
||||
slk::Save(size, builder);
|
||||
for (const auto &entry : self.${member}) {
|
||||
query::SaveAstPointer(entry.first, builder);
|
||||
query::SaveAstPointer(entry.second, builder);
|
||||
}
|
||||
cpp<#)
|
||||
|
||||
(defun slk-load-expression-map (member)
|
||||
#>cpp
|
||||
size_t size = 0;
|
||||
slk::Load(&size, reader);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
auto *key = query::LoadAstPointer<query::Expression>(storage, reader);
|
||||
auto *value = query::LoadAstPointer<query::Expression>(storage, reader);
|
||||
self->${member}.emplace(key, value);
|
||||
}
|
||||
cpp<#)
|
||||
|
||||
(defun clone-expression-map (source dest)
|
||||
#>cpp
|
||||
for (const auto &[key, value] : ${source}) {
|
||||
${dest}[key->Clone(storage)] = value->Clone(storage);
|
||||
}
|
||||
cpp<#)
|
||||
|
||||
(defun slk-load-name-ix (name-type)
|
||||
(lambda (member)
|
||||
#>cpp
|
||||
@@ -1843,7 +1819,9 @@ cpp<#
|
||||
size_t size = 0;
|
||||
slk::Load(&size, reader);
|
||||
self->${member}.resize(size);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
for (size_t i = 0;
|
||||
i < size;
|
||||
++i) {
|
||||
slk::Load(&self->${member}[i], reader, storage);
|
||||
}
|
||||
cpp<#)
|
||||
@@ -2247,8 +2225,7 @@ cpp<#
|
||||
(:serialize))
|
||||
(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)
|
||||
dump replication durability read_file free_memory trigger config stream)
|
||||
(:serialize))
|
||||
#>cpp
|
||||
AuthQuery() = default;
|
||||
@@ -2288,9 +2265,7 @@ const std::vector<AuthQuery::Privilege> kPrivilegesAll = {
|
||||
AuthQuery::Privilege::READ_FILE,
|
||||
AuthQuery::Privilege::DURABILITY,
|
||||
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::CONFIG, AuthQuery::Privilege::STREAM};
|
||||
cpp<#
|
||||
|
||||
(lcp:define-class info-query (query)
|
||||
@@ -2556,7 +2531,7 @@ cpp<#
|
||||
:slk-save #'slk-save-ast-pointer
|
||||
:slk-load (slk-load-ast-pointer "Expression"))
|
||||
|
||||
(topic_names "std::variant<Expression*, std::vector<std::string>>" :initval "nullptr"
|
||||
(topic_names "std::variant<Expression*, std::vector<std::string>>" :initval "nullptr"
|
||||
:clone #'clone-variant-topic-names
|
||||
:scope :public)
|
||||
(consumer_group "std::string" :scope :public)
|
||||
@@ -2566,17 +2541,7 @@ cpp<#
|
||||
|
||||
(service_url "Expression *" :initval "nullptr" :scope :public
|
||||
:slk-save #'slk-save-ast-pointer
|
||||
:slk-load (slk-load-ast-pointer "Expression"))
|
||||
|
||||
(configs "std::unordered_map<Expression *, Expression *>" :scope :public
|
||||
:slk-save #'slk-save-expression-map
|
||||
:slk-load #'slk-load-expression-map
|
||||
:clone #'clone-expression-map)
|
||||
|
||||
(credentials "std::unordered_map<Expression *, Expression *>" :scope :public
|
||||
:slk-save #'slk-save-expression-map
|
||||
:slk-load #'slk-load-expression-map
|
||||
:clone #'clone-expression-map))
|
||||
:slk-load (slk-load-ast-pointer "Expression")))
|
||||
|
||||
(:public
|
||||
(lcp:define-enum action
|
||||
@@ -2618,12 +2583,4 @@ cpp<#
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
(lcp:define-class version-query (query) ()
|
||||
(:public
|
||||
#>cpp
|
||||
DEFVISITABLE(QueryVisitor<void>);
|
||||
cpp<#)
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
(lcp:pop-namespace) ;; namespace query
|
||||
|
||||
@@ -92,7 +92,6 @@ class IsolationLevelQuery;
|
||||
class CreateSnapshotQuery;
|
||||
class StreamQuery;
|
||||
class SettingQuery;
|
||||
class VersionQuery;
|
||||
|
||||
using TreeCompositeVisitor = ::utils::CompositeVisitor<
|
||||
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
|
||||
@@ -124,9 +123,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> {};
|
||||
class QueryVisitor
|
||||
: public ::utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery, InfoQuery,
|
||||
ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery, FreeMemoryQuery,
|
||||
TriggerQuery, IsolationLevelQuery, CreateSnapshotQuery, StreamQuery, SettingQuery> {};
|
||||
|
||||
} // namespace query
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -9,6 +9,19 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// THIS INCLUDE SHOULD ALWAYS COME BEFORE THE
|
||||
// "cypher_main_visitor.hpp"
|
||||
// "module.hpp" includes json.hpp which uses libc's
|
||||
// EOF macro while "cypher_main_visitor.hpp" includes
|
||||
// "antlr4-runtime.h" which contains a static variable
|
||||
// of the same name, EOF.
|
||||
// This hides the definition of the macro which causes
|
||||
// the compilation to fail.
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/frontend/ast/ast_visitor.hpp"
|
||||
#include "query/procedure/module.hpp"
|
||||
//////////////////////////////////////////////////////
|
||||
#include "query/frontend/ast/cypher_main_visitor.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -19,20 +32,14 @@
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
|
||||
#include "query/exceptions.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/frontend/ast/ast_visitor.hpp"
|
||||
#include "query/frontend/parsing.hpp"
|
||||
#include "query/interpret/awesome_memgraph_functions.hpp"
|
||||
#include "query/procedure/module.hpp"
|
||||
#include "query/stream/common.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
@@ -493,23 +500,19 @@ antlrcpp::Any CypherMainVisitor::visitCreateStream(MemgraphCypher::CreateStreamC
|
||||
return stream_query;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::vector<std::string> TopicNamesFromSymbols(
|
||||
antlr4::tree::ParseTreeVisitor &visitor,
|
||||
const std::vector<MemgraphCypher::SymbolicNameWithDotsAndMinusContext *> &topic_name_symbols) {
|
||||
MG_ASSERT(!topic_name_symbols.empty());
|
||||
std::vector<std::string> topic_names;
|
||||
topic_names.reserve(topic_name_symbols.size());
|
||||
topic_names.reserve(topic_names.size());
|
||||
std::transform(topic_name_symbols.begin(), topic_name_symbols.end(), std::back_inserter(topic_names),
|
||||
[&visitor](auto *topic_name) { return JoinSymbolicNamesWithDotsAndMinus(visitor, *topic_name); });
|
||||
return topic_names;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
concept EnumUint8 = std::is_enum_v<T> && std::same_as<uint8_t, std::underlying_type_t<T>>;
|
||||
|
||||
template <bool required, typename... ValueTypes>
|
||||
void MapConfig(auto &memory, const EnumUint8 auto &enum_key, auto &destination) {
|
||||
void MapConfig(auto &memory, const auto &enum_key, auto &destination) {
|
||||
const auto key = static_cast<uint8_t>(enum_key);
|
||||
if (!memory.contains(key)) {
|
||||
if constexpr (required) {
|
||||
@@ -529,11 +532,13 @@ void MapConfig(auto &memory, const EnumUint8 auto &enum_key, auto &destination)
|
||||
}
|
||||
},
|
||||
std::move(memory[key]));
|
||||
memory.erase(key);
|
||||
}
|
||||
|
||||
enum class CommonStreamConfigKey : uint8_t { TRANSFORM, BATCH_INTERVAL, BATCH_SIZE, END };
|
||||
|
||||
constexpr std::array all_common_stream_config_keys{
|
||||
CommonStreamConfigKey::TRANSFORM, CommonStreamConfigKey::BATCH_INTERVAL, CommonStreamConfigKey::BATCH_SIZE};
|
||||
|
||||
std::string_view ToString(const CommonStreamConfigKey key) {
|
||||
switch (key) {
|
||||
case CommonStreamConfigKey::TRANSFORM:
|
||||
@@ -547,14 +552,19 @@ std::string_view ToString(const CommonStreamConfigKey key) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define CONCAT_HELPER(a, b) a##b
|
||||
#define CONCAT(a, b) CONCAT_HELPER(a, b)
|
||||
|
||||
#define GENERATE_STREAM_CONFIG_KEY_ENUM(stream, first_config, ...) \
|
||||
enum class BOOST_PP_CAT(stream, ConfigKey) : uint8_t { \
|
||||
enum class CONCAT(stream, ConfigKey) : uint8_t { \
|
||||
first_config = static_cast<uint8_t>(CommonStreamConfigKey::END), \
|
||||
__VA_ARGS__ \
|
||||
};
|
||||
|
||||
GENERATE_STREAM_CONFIG_KEY_ENUM(Kafka, TOPICS, CONSUMER_GROUP, BOOTSTRAP_SERVERS, CONFIGS, CREDENTIALS);
|
||||
GENERATE_STREAM_CONFIG_KEY_ENUM(Kafka, TOPICS, CONSUMER_GROUP, BOOTSTRAP_SERVERS);
|
||||
|
||||
constexpr std::array all_kafka_config_keys{KafkaConfigKey::TOPICS, KafkaConfigKey::CONSUMER_GROUP,
|
||||
KafkaConfigKey::BOOTSTRAP_SERVERS};
|
||||
|
||||
std::string_view ToString(const KafkaConfigKey key) {
|
||||
switch (key) {
|
||||
@@ -564,33 +574,25 @@ std::string_view ToString(const KafkaConfigKey key) {
|
||||
return "CONSUMER_GROUP";
|
||||
case KafkaConfigKey::BOOTSTRAP_SERVERS:
|
||||
return "BOOTSTRAP_SERVERS";
|
||||
case KafkaConfigKey::CONFIGS:
|
||||
return "CONFIGS";
|
||||
case KafkaConfigKey::CREDENTIALS:
|
||||
return "CREDENTIALS";
|
||||
}
|
||||
}
|
||||
|
||||
void MapCommonStreamConfigs(auto &memory, StreamQuery &stream_query) {
|
||||
MapConfig<true, std::string>(memory, CommonStreamConfigKey::TRANSFORM, stream_query.transform_name_);
|
||||
MapConfig<false, Expression *>(memory, CommonStreamConfigKey::BATCH_INTERVAL, stream_query.batch_interval_);
|
||||
MapConfig<false, Expression *>(memory, CommonStreamConfigKey::BATCH_SIZE, stream_query.batch_size_);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitConfigKeyValuePair(MemgraphCypher::ConfigKeyValuePairContext *ctx) {
|
||||
MG_ASSERT(ctx->literal().size() == 2);
|
||||
return std::pair{ctx->literal(0)->accept(this).as<Expression *>(), ctx->literal(1)->accept(this).as<Expression *>()};
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitConfigMap(MemgraphCypher::ConfigMapContext *ctx) {
|
||||
std::unordered_map<Expression *, Expression *> map;
|
||||
for (auto *key_value_pair : ctx->configKeyValuePair()) {
|
||||
// If the queries are cached, then only the stripped query is parsed, so the actual keys cannot be determined
|
||||
// here. That means duplicates cannot be checked.
|
||||
map.insert(key_value_pair->accept(this).as<std::pair<Expression *, Expression *>>());
|
||||
for (const auto key : all_common_stream_config_keys) {
|
||||
switch (key) {
|
||||
case CommonStreamConfigKey::TRANSFORM:
|
||||
MapConfig<true, std::string>(memory, CommonStreamConfigKey::TRANSFORM, stream_query.transform_name_);
|
||||
break;
|
||||
case CommonStreamConfigKey::BATCH_INTERVAL:
|
||||
MapConfig<false, Expression *>(memory, CommonStreamConfigKey::BATCH_INTERVAL, stream_query.batch_interval_);
|
||||
break;
|
||||
case CommonStreamConfigKey::BATCH_SIZE:
|
||||
MapConfig<false, Expression *>(memory, CommonStreamConfigKey::BATCH_SIZE, stream_query.batch_size_);
|
||||
break;
|
||||
case CommonStreamConfigKey::END:
|
||||
LOG_FATAL("Invalid config key used");
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitKafkaCreateStream(MemgraphCypher::KafkaCreateStreamContext *ctx) {
|
||||
@@ -603,41 +605,32 @@ antlrcpp::Any CypherMainVisitor::visitKafkaCreateStream(MemgraphCypher::KafkaCre
|
||||
create_config_ctx->accept(this);
|
||||
}
|
||||
|
||||
MapConfig<true, std::vector<std::string>, Expression *>(memory_, KafkaConfigKey::TOPICS, stream_query->topic_names_);
|
||||
MapConfig<false, std::string>(memory_, KafkaConfigKey::CONSUMER_GROUP, stream_query->consumer_group_);
|
||||
MapConfig<false, Expression *>(memory_, KafkaConfigKey::BOOTSTRAP_SERVERS, stream_query->bootstrap_servers_);
|
||||
MapConfig<false, std::unordered_map<Expression *, Expression *>>(memory_, KafkaConfigKey::CONFIGS,
|
||||
stream_query->configs_);
|
||||
MapConfig<false, std::unordered_map<Expression *, Expression *>>(memory_, KafkaConfigKey::CREDENTIALS,
|
||||
stream_query->credentials_);
|
||||
for (const auto key : all_kafka_config_keys) {
|
||||
switch (key) {
|
||||
case KafkaConfigKey::TOPICS:
|
||||
MapConfig<true, std::vector<std::string>>(memory_, KafkaConfigKey::TOPICS, stream_query->topic_names_);
|
||||
break;
|
||||
case KafkaConfigKey::CONSUMER_GROUP:
|
||||
MapConfig<false, std::string>(memory_, KafkaConfigKey::CONSUMER_GROUP, stream_query->consumer_group_);
|
||||
break;
|
||||
case KafkaConfigKey::BOOTSTRAP_SERVERS:
|
||||
MapConfig<false, Expression *>(memory_, KafkaConfigKey::BOOTSTRAP_SERVERS, stream_query->bootstrap_servers_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MapCommonStreamConfigs(memory_, *stream_query);
|
||||
|
||||
return stream_query;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void ThrowIfExists(const auto &map, const EnumUint8 auto &enum_key) {
|
||||
void ThrowIfExists(auto &map, const auto &enum_key) {
|
||||
const auto key = static_cast<uint8_t>(enum_key);
|
||||
if (map.contains(key)) {
|
||||
throw SemanticException("{} defined multiple times in the query", ToString(enum_key));
|
||||
}
|
||||
}
|
||||
|
||||
void GetTopicNames(auto &destination, MemgraphCypher::TopicNamesContext *topic_names_ctx,
|
||||
antlr4::tree::ParseTreeVisitor &visitor) {
|
||||
MG_ASSERT(topic_names_ctx != nullptr);
|
||||
if (auto *symbolic_topic_names_ctx = topic_names_ctx->symbolicTopicNames()) {
|
||||
destination = TopicNamesFromSymbols(visitor, symbolic_topic_names_ctx->symbolicNameWithDotsAndMinus());
|
||||
} else {
|
||||
if (!topic_names_ctx->literal()->StringLiteral()) {
|
||||
throw SemanticException("Topic names should be defined as a string literal or as symbolic names");
|
||||
}
|
||||
destination = topic_names_ctx->accept(&visitor).as<Expression *>();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitKafkaCreateStreamConfig(MemgraphCypher::KafkaCreateStreamConfigContext *ctx) {
|
||||
if (ctx->commonCreateStreamConfig()) {
|
||||
return ctx->commonCreateStreamConfig()->accept(this);
|
||||
@@ -645,33 +638,20 @@ antlrcpp::Any CypherMainVisitor::visitKafkaCreateStreamConfig(MemgraphCypher::Ka
|
||||
|
||||
if (ctx->TOPICS()) {
|
||||
ThrowIfExists(memory_, KafkaConfigKey::TOPICS);
|
||||
constexpr auto topics_key = static_cast<uint8_t>(KafkaConfigKey::TOPICS);
|
||||
GetTopicNames(memory_[topics_key], ctx->topicNames(), *this);
|
||||
auto *topic_names_ctx = ctx->topicNames();
|
||||
MG_ASSERT(topic_names_ctx != nullptr);
|
||||
const auto topic_key = static_cast<uint8_t>(KafkaConfigKey::TOPICS);
|
||||
memory_[topic_key] = TopicNamesFromSymbols(*this, topic_names_ctx->symbolicNameWithDotsAndMinus());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ctx->CONSUMER_GROUP()) {
|
||||
ThrowIfExists(memory_, KafkaConfigKey::CONSUMER_GROUP);
|
||||
constexpr auto consumer_group_key = static_cast<uint8_t>(KafkaConfigKey::CONSUMER_GROUP);
|
||||
const auto consumer_group_key = static_cast<uint8_t>(KafkaConfigKey::CONSUMER_GROUP);
|
||||
memory_[consumer_group_key] = JoinSymbolicNamesWithDotsAndMinus(*this, *ctx->consumerGroup);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ctx->CONFIGS()) {
|
||||
ThrowIfExists(memory_, KafkaConfigKey::CONFIGS);
|
||||
constexpr auto configs_key = static_cast<uint8_t>(KafkaConfigKey::CONFIGS);
|
||||
memory_.emplace(configs_key, ctx->configsMap->accept(this).as<std::unordered_map<Expression *, Expression *>>());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ctx->CREDENTIALS()) {
|
||||
ThrowIfExists(memory_, KafkaConfigKey::CREDENTIALS);
|
||||
constexpr auto credentials_key = static_cast<uint8_t>(KafkaConfigKey::CREDENTIALS);
|
||||
memory_.emplace(credentials_key,
|
||||
ctx->credentialsMap->accept(this).as<std::unordered_map<Expression *, Expression *>>());
|
||||
return {};
|
||||
}
|
||||
|
||||
MG_ASSERT(ctx->BOOTSTRAP_SERVERS());
|
||||
ThrowIfExists(memory_, KafkaConfigKey::BOOTSTRAP_SERVERS);
|
||||
if (!ctx->bootstrapServers->StringLiteral()) {
|
||||
@@ -681,11 +661,12 @@ antlrcpp::Any CypherMainVisitor::visitKafkaCreateStreamConfig(MemgraphCypher::Ka
|
||||
const auto bootstrap_servers_key = static_cast<uint8_t>(KafkaConfigKey::BOOTSTRAP_SERVERS);
|
||||
memory_[bootstrap_servers_key] = ctx->bootstrapServers->accept(this).as<Expression *>();
|
||||
return {};
|
||||
}
|
||||
} // namespace query::frontend
|
||||
|
||||
namespace {
|
||||
GENERATE_STREAM_CONFIG_KEY_ENUM(Pulsar, TOPICS, SERVICE_URL);
|
||||
|
||||
constexpr std::array all_pulsar_config_keys{PulsarConfigKey::TOPICS, PulsarConfigKey::SERVICE_URL};
|
||||
|
||||
std::string_view ToString(const PulsarConfigKey key) {
|
||||
switch (key) {
|
||||
case PulsarConfigKey::TOPICS:
|
||||
@@ -694,7 +675,6 @@ std::string_view ToString(const PulsarConfigKey key) {
|
||||
return "SERVICE_URL";
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitPulsarCreateStream(MemgraphCypher::PulsarCreateStreamContext *ctx) {
|
||||
auto *stream_query = storage_->Create<StreamQuery>();
|
||||
@@ -706,8 +686,17 @@ antlrcpp::Any CypherMainVisitor::visitPulsarCreateStream(MemgraphCypher::PulsarC
|
||||
create_config_ctx->accept(this);
|
||||
}
|
||||
|
||||
MapConfig<true, std::vector<std::string>, Expression *>(memory_, PulsarConfigKey::TOPICS, stream_query->topic_names_);
|
||||
MapConfig<false, Expression *>(memory_, PulsarConfigKey::SERVICE_URL, stream_query->service_url_);
|
||||
for (const auto key : all_pulsar_config_keys) {
|
||||
switch (key) {
|
||||
case PulsarConfigKey::TOPICS:
|
||||
MapConfig<true, std::vector<std::string>, Expression *>(memory_, PulsarConfigKey::TOPICS,
|
||||
stream_query->topic_names_);
|
||||
break;
|
||||
case PulsarConfigKey::SERVICE_URL:
|
||||
MapConfig<true, Expression *>(memory_, PulsarConfigKey::SERVICE_URL, stream_query->service_url_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MapCommonStreamConfigs(memory_, *stream_query);
|
||||
|
||||
@@ -721,15 +710,24 @@ antlrcpp::Any CypherMainVisitor::visitPulsarCreateStreamConfig(MemgraphCypher::P
|
||||
|
||||
if (ctx->TOPICS()) {
|
||||
ThrowIfExists(memory_, PulsarConfigKey::TOPICS);
|
||||
auto *pulsar_topic_names_ctx = ctx->pulsarTopicNames();
|
||||
MG_ASSERT(pulsar_topic_names_ctx != nullptr);
|
||||
const auto topics_key = static_cast<uint8_t>(PulsarConfigKey::TOPICS);
|
||||
GetTopicNames(memory_[topics_key], ctx->topicNames(), *this);
|
||||
if (auto *topic_names_ctx = pulsar_topic_names_ctx->topicNames()) {
|
||||
memory_[topics_key] = TopicNamesFromSymbols(*this, topic_names_ctx->symbolicNameWithDotsAndMinus());
|
||||
} else {
|
||||
if (!pulsar_topic_names_ctx->literal()->StringLiteral()) {
|
||||
throw SemanticException("Topic names should be defined in a string");
|
||||
}
|
||||
memory_[topics_key] = pulsar_topic_names_ctx->accept(this).as<Expression *>();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
MG_ASSERT(ctx->SERVICE_URL());
|
||||
ThrowIfExists(memory_, PulsarConfigKey::SERVICE_URL);
|
||||
if (!ctx->serviceUrl->StringLiteral()) {
|
||||
throw SemanticException("Service URL must be a string!");
|
||||
throw SemanticException("Service url should be a string!");
|
||||
}
|
||||
const auto service_url_key = static_cast<uint8_t>(PulsarConfigKey::SERVICE_URL);
|
||||
memory_[service_url_key] = ctx->serviceUrl->accept(this).as<Expression *>();
|
||||
@@ -747,7 +745,7 @@ antlrcpp::Any CypherMainVisitor::visitCommonCreateStreamConfig(MemgraphCypher::C
|
||||
if (ctx->BATCH_INTERVAL()) {
|
||||
ThrowIfExists(memory_, CommonStreamConfigKey::BATCH_INTERVAL);
|
||||
if (!ctx->batchInterval->numberLiteral() || !ctx->batchInterval->numberLiteral()->integerLiteral()) {
|
||||
throw SemanticException("Batch interval must be an integer literal!");
|
||||
throw SemanticException("Batch interval should be an integer literal!");
|
||||
}
|
||||
const auto batch_interval_key = static_cast<uint8_t>(CommonStreamConfigKey::BATCH_INTERVAL);
|
||||
memory_[batch_interval_key] = ctx->batchInterval->accept(this).as<Expression *>();
|
||||
@@ -757,7 +755,7 @@ antlrcpp::Any CypherMainVisitor::visitCommonCreateStreamConfig(MemgraphCypher::C
|
||||
MG_ASSERT(ctx->BATCH_SIZE());
|
||||
ThrowIfExists(memory_, CommonStreamConfigKey::BATCH_SIZE);
|
||||
if (!ctx->batchSize->numberLiteral() || !ctx->batchSize->numberLiteral()->integerLiteral()) {
|
||||
throw SemanticException("Batch size must be an integer literal!");
|
||||
throw SemanticException("Batch size should be an integer literal!");
|
||||
}
|
||||
const auto batch_size_key = static_cast<uint8_t>(CommonStreamConfigKey::BATCH_SIZE);
|
||||
memory_[batch_size_key] = ctx->batchSize->accept(this).as<Expression *>();
|
||||
@@ -870,12 +868,6 @@ antlrcpp::Any CypherMainVisitor::visitShowSettings(MemgraphCypher::ShowSettingsC
|
||||
return setting_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitVersionQuery(MemgraphCypher::VersionQueryContext * /*ctx*/) {
|
||||
auto *version_query = storage_->Create<VersionQuery>();
|
||||
query_ = version_query;
|
||||
return version_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitCypherUnion(MemgraphCypher::CypherUnionContext *ctx) {
|
||||
bool distinct = !ctx->ALL();
|
||||
auto *cypher_union = storage_->Create<CypherUnion>(distinct);
|
||||
@@ -1091,7 +1083,7 @@ antlrcpp::Any CypherMainVisitor::visitCallProcedure(MemgraphCypher::CallProcedur
|
||||
if (!maybe_found) {
|
||||
throw SemanticException("There is no procedure named '{}'.", call_proc->procedure_name_);
|
||||
}
|
||||
call_proc->is_write_ = maybe_found->second->info.is_write;
|
||||
call_proc->is_write_ = maybe_found->second->is_write_procedure;
|
||||
|
||||
auto *yield_ctx = ctx->yieldProcedureResults();
|
||||
if (!yield_ctx) {
|
||||
@@ -1330,9 +1322,6 @@ antlrcpp::Any CypherMainVisitor::visitPrivilege(MemgraphCypher::PrivilegeContext
|
||||
if (ctx->CONFIG()) return AuthQuery::Privilege::CONFIG;
|
||||
if (ctx->DURABILITY()) return AuthQuery::Privilege::DURABILITY;
|
||||
if (ctx->STREAM()) return AuthQuery::Privilege::STREAM;
|
||||
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;
|
||||
LOG_FATAL("Should not get here - unknown privilege!");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -269,16 +269,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
*/
|
||||
antlrcpp::Any visitCreateStream(MemgraphCypher::CreateStreamContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return StreamQuery*
|
||||
*/
|
||||
antlrcpp::Any visitConfigKeyValuePair(MemgraphCypher::ConfigKeyValuePairContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return StreamQuery*
|
||||
*/
|
||||
antlrcpp::Any visitConfigMap(MemgraphCypher::ConfigMapContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return StreamQuery*
|
||||
*/
|
||||
@@ -359,11 +349,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
*/
|
||||
antlrcpp::Any visitShowSettings(MemgraphCypher::ShowSettingsContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return VersionQuery*
|
||||
*/
|
||||
antlrcpp::Any visitVersionQuery(MemgraphCypher::VersionQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return CypherUnion*
|
||||
*/
|
||||
@@ -864,9 +849,7 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
ParsingContext context_;
|
||||
AstStorage *storage_;
|
||||
|
||||
std::unordered_map<uint8_t, std::variant<Expression *, std::string, std::vector<std::string>,
|
||||
std::unordered_map<Expression *, Expression *>>>
|
||||
memory_;
|
||||
std::unordered_map<uint8_t, std::variant<Expression *, std::string, std::vector<std::string>>> memory_;
|
||||
// Set of identifiers from queries.
|
||||
std::unordered_set<std::string> users_identifiers;
|
||||
// Identifiers that user didn't name.
|
||||
|
||||
@@ -35,9 +35,7 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| COMMIT
|
||||
| COMMITTED
|
||||
| CONFIG
|
||||
| CONFIGS
|
||||
| CONSUMER_GROUP
|
||||
| CREDENTIALS
|
||||
| CSV
|
||||
| DATA
|
||||
| DELIMITER
|
||||
@@ -54,7 +52,6 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| HEADER
|
||||
| IDENTIFIED
|
||||
| ISOLATION
|
||||
| KAFKA
|
||||
| LEVEL
|
||||
| LOAD
|
||||
| LOCK
|
||||
@@ -63,7 +60,6 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| NEXT
|
||||
| NO
|
||||
| PASSWORD
|
||||
| PULSAR
|
||||
| PORT
|
||||
| PRIVILEGES
|
||||
| READ
|
||||
@@ -96,7 +92,6 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| UPDATE
|
||||
| USER
|
||||
| USERS
|
||||
| VERSION
|
||||
;
|
||||
|
||||
symbolicName : UnescapedSymbolicName
|
||||
@@ -120,7 +115,6 @@ query : cypherQuery
|
||||
| createSnapshotQuery
|
||||
| streamQuery
|
||||
| settingQuery
|
||||
| versionQuery
|
||||
;
|
||||
|
||||
authQuery : createRole
|
||||
@@ -239,9 +233,6 @@ privilege : CREATE
|
||||
| CONFIG
|
||||
| DURABILITY
|
||||
| STREAM
|
||||
| MODULE_READ
|
||||
| MODULE_WRITE
|
||||
| WEBSOCKET
|
||||
;
|
||||
|
||||
privilegeList : privilege ( ',' privilege )* ;
|
||||
@@ -304,9 +295,7 @@ symbolicNameWithMinus : symbolicName ( MINUS symbolicName )* ;
|
||||
|
||||
symbolicNameWithDotsAndMinus: symbolicNameWithMinus ( DOT symbolicNameWithMinus )* ;
|
||||
|
||||
symbolicTopicNames : symbolicNameWithDotsAndMinus ( COMMA symbolicNameWithDotsAndMinus )* ;
|
||||
|
||||
topicNames : symbolicTopicNames | literal ;
|
||||
topicNames : symbolicNameWithDotsAndMinus ( COMMA symbolicNameWithDotsAndMinus )* ;
|
||||
|
||||
commonCreateStreamConfig : TRANSFORM transformationName=procedureName
|
||||
| BATCH_INTERVAL batchInterval=literal
|
||||
@@ -315,22 +304,17 @@ commonCreateStreamConfig : TRANSFORM transformationName=procedureName
|
||||
|
||||
createStream : kafkaCreateStream | pulsarCreateStream ;
|
||||
|
||||
configKeyValuePair : literal ':' literal ;
|
||||
|
||||
configMap : '{' ( configKeyValuePair ( ',' configKeyValuePair )* )? '}' ;
|
||||
|
||||
kafkaCreateStreamConfig : TOPICS topicNames
|
||||
| CONSUMER_GROUP consumerGroup=symbolicNameWithDotsAndMinus
|
||||
| BOOTSTRAP_SERVERS bootstrapServers=literal
|
||||
| CONFIGS configsMap=configMap
|
||||
| CREDENTIALS credentialsMap=configMap
|
||||
| commonCreateStreamConfig
|
||||
;
|
||||
|
||||
kafkaCreateStream : CREATE KAFKA STREAM streamName ( kafkaCreateStreamConfig ) * ;
|
||||
|
||||
pulsarTopicNames : topicNames | literal ;
|
||||
|
||||
pulsarCreateStreamConfig : TOPICS topicNames
|
||||
pulsarCreateStreamConfig : TOPICS pulsarTopicNames
|
||||
| SERVICE_URL serviceUrl=literal
|
||||
| commonCreateStreamConfig
|
||||
;
|
||||
@@ -360,5 +344,3 @@ setSetting : SET DATABASE SETTING settingName TO settingValue ;
|
||||
showSetting : SHOW DATABASE SETTING settingName ;
|
||||
|
||||
showSettings : SHOW DATABASE SETTINGS ;
|
||||
|
||||
versionQuery : SHOW VERSION ;
|
||||
|
||||
@@ -40,9 +40,7 @@ CLEAR : C L E A R ;
|
||||
COMMIT : C O M M I T ;
|
||||
COMMITTED : C O M M I T T E D ;
|
||||
CONFIG : C O N F I G ;
|
||||
CONFIGS : C O N F I G S;
|
||||
CONSUMER_GROUP : C O N S U M E R UNDERSCORE G R O U P ;
|
||||
CREDENTIALS : C R E D E N T I A L S ;
|
||||
CSV : C S V ;
|
||||
DATA : D A T A ;
|
||||
DELIMITER : D E L I M I T E R ;
|
||||
@@ -70,8 +68,6 @@ LOAD : L O A D ;
|
||||
LOCK : L O C K ;
|
||||
MAIN : M A I N ;
|
||||
MODE : M O D E ;
|
||||
MODULE_READ : M O D U L E UNDERSCORE R E A D ;
|
||||
MODULE_WRITE : M O D U L E UNDERSCORE W R I T E ;
|
||||
NEXT : N E X T ;
|
||||
NO : N O ;
|
||||
PASSWORD : P A S S W O R D ;
|
||||
@@ -111,5 +107,3 @@ UNLOCK : U N L O C K ;
|
||||
UPDATE : U P D A T E ;
|
||||
USER : U S E R ;
|
||||
USERS : U S E R S ;
|
||||
VERSION : V E R S I O N ;
|
||||
WEBSOCKET : W E B S O C K E T ;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -11,8 +11,6 @@
|
||||
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/frontend/ast/ast_visitor.hpp"
|
||||
#include "query/procedure/module.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
|
||||
namespace query {
|
||||
|
||||
@@ -78,18 +76,12 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
|
||||
|
||||
void Visit(SettingQuery & /*setting_query*/) override { AddPrivilege(AuthQuery::Privilege::CONFIG); }
|
||||
|
||||
void Visit(VersionQuery & /*version_query*/) override { AddPrivilege(AuthQuery::Privilege::STATS); }
|
||||
|
||||
bool PreVisit(Create & /*unused*/) override {
|
||||
AddPrivilege(AuthQuery::Privilege::CREATE);
|
||||
return false;
|
||||
}
|
||||
bool PreVisit(CallProcedure &procedure) override {
|
||||
const auto maybe_proc =
|
||||
procedure::FindProcedure(procedure::gModuleRegistry, procedure.procedure_name_, utils::NewDeleteResource());
|
||||
if (maybe_proc && maybe_proc->second->info.required_privilege) {
|
||||
AddPrivilege(*maybe_proc->second->info.required_privilege);
|
||||
}
|
||||
bool PreVisit(CallProcedure & /*unused*/) override {
|
||||
// TODO: Corresponding privilege
|
||||
return false;
|
||||
}
|
||||
bool PreVisit(Delete & /*unused*/) override {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -202,9 +202,7 @@ const trie::Trie kKeywords = {"union",
|
||||
"bootstrap_servers",
|
||||
"kafka",
|
||||
"pulsar",
|
||||
"service_url",
|
||||
"version",
|
||||
"websocket"};
|
||||
"service_url"};
|
||||
|
||||
// Unicode codepoints that are allowed at the start of the unescaped name.
|
||||
const std::bitset<kBitsetSize> kUnescapedNameAllowedStarts(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -1080,7 +1080,7 @@ void MapNumericParameters(auto ¶meter_mappings, const auto &input_parameters
|
||||
TypedValue Date(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) {
|
||||
FType<Optional<Or<String, Map>>>("date", args, nargs);
|
||||
if (nargs == 0) {
|
||||
return TypedValue(utils::LocalDateTime(ctx.timestamp).date, ctx.memory);
|
||||
return TypedValue(utils::UtcToday(), ctx.memory);
|
||||
}
|
||||
|
||||
if (args[0].IsString()) {
|
||||
@@ -1103,7 +1103,7 @@ TypedValue LocalTime(const TypedValue *args, int64_t nargs, const FunctionContex
|
||||
FType<Optional<Or<String, Map>>>("localtime", args, nargs);
|
||||
|
||||
if (nargs == 0) {
|
||||
return TypedValue(utils::LocalDateTime(ctx.timestamp).local_time, ctx.memory);
|
||||
return TypedValue(utils::UtcLocalTime(), ctx.memory);
|
||||
}
|
||||
|
||||
if (args[0].IsString()) {
|
||||
@@ -1130,7 +1130,7 @@ TypedValue LocalDateTime(const TypedValue *args, int64_t nargs, const FunctionCo
|
||||
FType<Optional<Or<String, Map>>>("localdatetime", args, nargs);
|
||||
|
||||
if (nargs == 0) {
|
||||
return TypedValue(utils::LocalDateTime(ctx.timestamp), ctx.memory);
|
||||
return TypedValue(utils::UtcLocalDateTime(), ctx.memory);
|
||||
}
|
||||
|
||||
if (args[0].IsString()) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -10,14 +10,9 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "query/interpreter.hpp"
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
@@ -36,7 +31,6 @@
|
||||
#include "query/frontend/semantic/required_privileges.hpp"
|
||||
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||
#include "query/interpret/eval.hpp"
|
||||
#include "query/metadata.hpp"
|
||||
#include "query/plan/planner.hpp"
|
||||
#include "query/plan/profile.hpp"
|
||||
#include "query/plan/vertex_count_cache.hpp"
|
||||
@@ -58,7 +52,7 @@
|
||||
#include "utils/settings.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/tsc.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
#include "utils/variant.hpp"
|
||||
|
||||
namespace EventCounter {
|
||||
extern Event ReadQuery;
|
||||
@@ -413,8 +407,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa
|
||||
}
|
||||
|
||||
Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters ¶meters,
|
||||
InterpreterContext *interpreter_context, DbAccessor *db_accessor,
|
||||
std::vector<Notification> *notifications) {
|
||||
InterpreterContext *interpreter_context, DbAccessor *db_accessor) {
|
||||
Frame frame(0);
|
||||
SymbolTable symbol_table;
|
||||
EvaluationContext evaluation_context;
|
||||
@@ -432,19 +425,11 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
if (port.IsInt()) {
|
||||
maybe_port = port.ValueInt();
|
||||
}
|
||||
if (maybe_port == 7687 && repl_query->role_ == ReplicationQuery::ReplicationRole::REPLICA) {
|
||||
notifications->emplace_back(SeverityLevel::WARNING, NotificationCode::REPLICA_PORT_WARNING,
|
||||
"Be careful the replication port must be different from the memgraph port!");
|
||||
}
|
||||
callback.fn = [handler = ReplQueryHandler{interpreter_context->db}, role = repl_query->role_,
|
||||
maybe_port]() mutable {
|
||||
handler.SetReplicationRole(role, maybe_port);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
notifications->emplace_back(
|
||||
SeverityLevel::INFO, NotificationCode::SET_REPLICA,
|
||||
fmt::format("Replica role set to {}.",
|
||||
repl_query->role_ == ReplicationQuery::ReplicationRole::MAIN ? "MAIN" : "REPLICA"));
|
||||
return callback;
|
||||
}
|
||||
case ReplicationQuery::Action::SHOW_REPLICATION_ROLE: {
|
||||
@@ -478,8 +463,6 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
handler.RegisterReplica(name, std::string(socket_address.ValueString()), sync_mode, maybe_timeout);
|
||||
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: {
|
||||
@@ -488,8 +471,6 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
handler.DropReplica(name);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::DROP_REPLICA,
|
||||
fmt::format("Replica {} is dropped.", repl_query->replica_name_));
|
||||
return callback;
|
||||
}
|
||||
case ReplicationQuery::Action::SHOW_REPLICAS: {
|
||||
@@ -532,12 +513,10 @@ std::optional<std::string> StringPointerToOptional(const std::string *str) {
|
||||
return str == nullptr ? std::nullopt : std::make_optional(*str);
|
||||
}
|
||||
|
||||
stream::CommonStreamInfo GetCommonStreamInfo(StreamQuery *stream_query, ExpressionEvaluator &evaluator) {
|
||||
return {
|
||||
.batch_interval = GetOptionalValue<std::chrono::milliseconds>(stream_query->batch_interval_, evaluator)
|
||||
.value_or(stream::kDefaultBatchInterval),
|
||||
.batch_size = GetOptionalValue<int64_t>(stream_query->batch_size_, evaluator).value_or(stream::kDefaultBatchSize),
|
||||
.transformation_name = stream_query->transform_name_};
|
||||
CommonStreamInfo GetCommonStreamInfo(StreamQuery *stream_query, ExpressionEvaluator &evaluator) {
|
||||
return {.batch_interval = GetOptionalValue<std::chrono::milliseconds>(stream_query->batch_interval_, evaluator),
|
||||
.batch_size = GetOptionalValue<int64_t>(stream_query->batch_size_, evaluator),
|
||||
.transformation_name = stream_query->transform_name_};
|
||||
}
|
||||
|
||||
std::vector<std::string> EvaluateTopicNames(ExpressionEvaluator &evaluator,
|
||||
@@ -564,37 +543,19 @@ Callback::CallbackFunction GetKafkaCreateCallback(StreamQuery *stream_query, Exp
|
||||
}
|
||||
auto common_stream_info = GetCommonStreamInfo(stream_query, evaluator);
|
||||
|
||||
const auto get_config_map = [&evaluator](std::unordered_map<Expression *, Expression *> map,
|
||||
std::string_view map_name) -> std::unordered_map<std::string, std::string> {
|
||||
std::unordered_map<std::string, std::string> config_map;
|
||||
for (const auto [key_expr, value_expr] : map) {
|
||||
const auto key = key_expr->Accept(evaluator);
|
||||
const auto value = value_expr->Accept(evaluator);
|
||||
if (!key.IsString() || !value.IsString()) {
|
||||
throw SemanticException("{} must contain only string keys and values!", map_name);
|
||||
}
|
||||
config_map.emplace(key.ValueString(), value.ValueString());
|
||||
}
|
||||
return config_map;
|
||||
};
|
||||
|
||||
return [interpreter_context, stream_name = stream_query->stream_name_,
|
||||
topic_names = EvaluateTopicNames(evaluator, stream_query->topic_names_),
|
||||
consumer_group = std::move(consumer_group), common_stream_info = std::move(common_stream_info),
|
||||
bootstrap_servers = std::move(bootstrap), owner = StringPointerToOptional(username),
|
||||
configs = get_config_map(stream_query->configs_, "Configs"),
|
||||
credentials = get_config_map(stream_query->credentials_, "Credentials")]() mutable {
|
||||
bootstrap_servers = std::move(bootstrap), owner = StringPointerToOptional(username)]() mutable {
|
||||
std::string bootstrap = bootstrap_servers
|
||||
? std::move(*bootstrap_servers)
|
||||
: std::string{interpreter_context->config.default_kafka_bootstrap_servers};
|
||||
interpreter_context->streams.Create<query::stream::KafkaStream>(stream_name,
|
||||
{.common_info = std::move(common_stream_info),
|
||||
.topics = std::move(topic_names),
|
||||
.consumer_group = std::move(consumer_group),
|
||||
.bootstrap_servers = std::move(bootstrap),
|
||||
.configs = std::move(configs),
|
||||
.credentials = std::move(credentials)},
|
||||
std::move(owner));
|
||||
interpreter_context->streams.Create<query::KafkaStream>(stream_name,
|
||||
{.common_info = std::move(common_stream_info),
|
||||
.topics = std::move(topic_names),
|
||||
.consumer_group = std::move(consumer_group),
|
||||
.bootstrap_servers = std::move(bootstrap)},
|
||||
std::move(owner));
|
||||
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
@@ -612,12 +573,11 @@ Callback::CallbackFunction GetPulsarCreateCallback(StreamQuery *stream_query, Ex
|
||||
topic_names = EvaluateTopicNames(evaluator, stream_query->topic_names_),
|
||||
common_stream_info = std::move(common_stream_info), service_url = std::move(service_url),
|
||||
owner = StringPointerToOptional(username)]() mutable {
|
||||
std::string url =
|
||||
service_url ? std::move(*service_url) : std::string{interpreter_context->config.default_pulsar_service_url};
|
||||
interpreter_context->streams.Create<query::stream::PulsarStream>(
|
||||
stream_name,
|
||||
{.common_info = std::move(common_stream_info), .topics = std::move(topic_names), .service_url = std::move(url)},
|
||||
std::move(owner));
|
||||
interpreter_context->streams.Create<query::PulsarStream>(stream_name,
|
||||
{.common_info = std::move(common_stream_info),
|
||||
.topics = std::move(topic_names),
|
||||
.service_url = std::move(*service_url)},
|
||||
std::move(owner));
|
||||
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
@@ -625,7 +585,7 @@ Callback::CallbackFunction GetPulsarCreateCallback(StreamQuery *stream_query, Ex
|
||||
|
||||
Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶meters,
|
||||
InterpreterContext *interpreter_context, DbAccessor *db_accessor,
|
||||
const std::string *username, std::vector<Notification> *notifications) {
|
||||
const std::string *username) {
|
||||
Frame frame(0);
|
||||
SymbolTable symbol_table;
|
||||
EvaluationContext evaluation_context;
|
||||
@@ -647,8 +607,7 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
callback.fn = GetPulsarCreateCallback(stream_query, evaluator, interpreter_context, username);
|
||||
break;
|
||||
}
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::CREATE_STREAM,
|
||||
fmt::format("Created stream {}.", stream_query->stream_name_));
|
||||
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::START_STREAM: {
|
||||
@@ -656,8 +615,6 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
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: {
|
||||
@@ -665,7 +622,6 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
interpreter_context->streams.StartAll();
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::START_ALL_STREAMS, "Started all streams.");
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::STOP_STREAM: {
|
||||
@@ -673,8 +629,6 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
interpreter_context->streams.Stop(stream_name);
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::STOP_STREAM,
|
||||
fmt::format("Stopped stream {}.", stream_query->stream_name_));
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::STOP_ALL_STREAMS: {
|
||||
@@ -682,7 +636,6 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
interpreter_context->streams.StopAll();
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::STOP_ALL_STREAMS, "Stopped all streams.");
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::DROP_STREAM: {
|
||||
@@ -690,27 +643,32 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
interpreter_context->streams.Drop(stream_name);
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::DROP_STREAM,
|
||||
fmt::format("Dropped stream {}.", stream_query->stream_name_));
|
||||
return callback;
|
||||
}
|
||||
case StreamQuery::Action::SHOW_STREAMS: {
|
||||
callback.header = {"name", "type", "batch_interval", "batch_size", "transformation_name", "owner", "is running"};
|
||||
callback.header = {"name", "batch_interval", "batch_size", "transformation_name", "owner", "is running"};
|
||||
callback.fn = [interpreter_context]() {
|
||||
auto streams_status = interpreter_context->streams.GetStreamInfo();
|
||||
std::vector<std::vector<TypedValue>> results;
|
||||
results.reserve(streams_status.size());
|
||||
auto stream_info_as_typed_stream_info_emplace_in = [](auto &typed_status, const auto &stream_info) {
|
||||
typed_status.emplace_back(stream_info.batch_interval.count());
|
||||
typed_status.emplace_back(stream_info.batch_size);
|
||||
if (stream_info.batch_interval.has_value()) {
|
||||
typed_status.emplace_back(stream_info.batch_interval->count());
|
||||
} else {
|
||||
typed_status.emplace_back();
|
||||
}
|
||||
if (stream_info.batch_size.has_value()) {
|
||||
typed_status.emplace_back(*stream_info.batch_size);
|
||||
} else {
|
||||
typed_status.emplace_back();
|
||||
}
|
||||
typed_status.emplace_back(stream_info.transformation_name);
|
||||
};
|
||||
|
||||
for (const auto &status : streams_status) {
|
||||
std::vector<TypedValue> typed_status;
|
||||
typed_status.reserve(7);
|
||||
typed_status.reserve(8);
|
||||
typed_status.emplace_back(status.name);
|
||||
typed_status.emplace_back(StreamSourceTypeToString(status.type));
|
||||
stream_info_as_typed_stream_info_emplace_in(typed_status, status.info);
|
||||
if (status.owner.has_value()) {
|
||||
typed_status.emplace_back(*status.owner);
|
||||
@@ -732,8 +690,6 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
batch_limit = GetOptionalValue<int64_t>(stream_query->batch_limit_, evaluator)]() mutable {
|
||||
return interpreter_context->streams.Check(stream_name, timeout, batch_limit);
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::CHECK_STREAM,
|
||||
fmt::format("Checked stream {}.", stream_query->stream_name_));
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
@@ -964,19 +920,8 @@ std::optional<plan::ProfilingStatsWithTotalTime> PullPlan::Pull(AnyStream *strea
|
||||
if (has_unsent_results_) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
summary->insert_or_assign("plan_execution_time", execution_time_.count());
|
||||
// We are finished with pulling all the data, therefore we can send any
|
||||
// metadata about the results i.e. notifications and statistics
|
||||
const bool is_any_counter_set =
|
||||
std::any_of(ctx_.execution_stats.counters.begin(), ctx_.execution_stats.counters.end(),
|
||||
[](const auto &counter) { return counter > 0; });
|
||||
if (is_any_counter_set) {
|
||||
std::map<std::string, TypedValue> stats;
|
||||
for (size_t i = 0; i < ctx_.execution_stats.counters.size(); ++i) {
|
||||
stats.emplace(ExecutionStatsKeyToString(ExecutionStats::Key(i)), ctx_.execution_stats.counters[i]);
|
||||
}
|
||||
summary->insert_or_assign("stats", std::move(stats));
|
||||
}
|
||||
cursor_->Shutdown();
|
||||
ctx_.profile_execution_time = execution_time_;
|
||||
return GetStatsWithTotalTime(ctx_);
|
||||
@@ -1057,7 +1002,7 @@ PreparedQuery Interpreter::PrepareTransactionQuery(std::string_view query_upper)
|
||||
|
||||
PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string, TypedValue> *summary,
|
||||
InterpreterContext *interpreter_context, DbAccessor *dba,
|
||||
utils::MemoryResource *execution_memory, std::vector<Notification> *notifications,
|
||||
utils::MemoryResource *execution_memory,
|
||||
TriggerContextCollector *trigger_context_collector = nullptr) {
|
||||
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_query.query);
|
||||
|
||||
@@ -1072,15 +1017,6 @@ PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string,
|
||||
spdlog::info("Running query with memory limit of {}", utils::GetReadableSize(*memory_limit));
|
||||
}
|
||||
|
||||
if (const auto &clauses = cypher_query->single_query_->clauses_; std::any_of(
|
||||
clauses.begin(), clauses.end(), [](const auto *clause) { return clause->GetTypeInfo() == LoadCsv::kType; })) {
|
||||
notifications->emplace_back(
|
||||
SeverityLevel::INFO, NotificationCode::LOAD_CSV_TIP,
|
||||
"It's important to note that the parser parses the values as strings. It's up to the user to "
|
||||
"convert the parsed row values to the appropriate type. This can be done using the built-in "
|
||||
"conversion functions such as ToInteger, ToFloat, ToBoolean etc.");
|
||||
}
|
||||
|
||||
auto plan = CypherQueryToPlan(parsed_query.stripped_query.hash(), std::move(parsed_query.ast_storage), cypher_query,
|
||||
parsed_query.parameters,
|
||||
parsed_query.is_cacheable ? &interpreter_context->plan_cache : nullptr, dba);
|
||||
@@ -1101,6 +1037,7 @@ PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string,
|
||||
header.push_back(
|
||||
utils::FindOr(parsed_query.stripped_query.named_expressions(), symbol.token_position(), symbol.name()).first);
|
||||
}
|
||||
|
||||
auto pull_plan = std::make_shared<PullPlan>(plan, parsed_query.parameters, false, dba, interpreter_context,
|
||||
execution_memory, trigger_context_collector, memory_limit);
|
||||
return PreparedQuery{std::move(header), std::move(parsed_query.required_privileges),
|
||||
@@ -1258,13 +1195,14 @@ PreparedQuery PrepareDumpQuery(ParsedQuery parsed_query, std::map<std::string, T
|
||||
}
|
||||
|
||||
PreparedQuery PrepareIndexQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications, InterpreterContext *interpreter_context) {
|
||||
std::map<std::string, TypedValue> *summary, InterpreterContext *interpreter_context,
|
||||
utils::MemoryResource *execution_memory) {
|
||||
if (in_explicit_transaction) {
|
||||
throw IndexInMulticommandTxException();
|
||||
}
|
||||
|
||||
auto *index_query = utils::Downcast<IndexQuery>(parsed_query.query);
|
||||
std::function<void(Notification &)> handler;
|
||||
std::function<void()> handler;
|
||||
|
||||
// Creating an index influences computed plan costs.
|
||||
auto invalidate_plan_cache = [plan_cache = &interpreter_context->plan_cache] {
|
||||
@@ -1275,45 +1213,26 @@ PreparedQuery PrepareIndexQuery(ParsedQuery parsed_query, bool in_explicit_trans
|
||||
};
|
||||
|
||||
auto label = interpreter_context->db->NameToLabel(index_query->label_.name);
|
||||
|
||||
std::vector<storage::PropertyId> properties;
|
||||
std::vector<std::string> properties_string;
|
||||
properties.reserve(index_query->properties_.size());
|
||||
properties_string.reserve(index_query->properties_.size());
|
||||
for (const auto &prop : index_query->properties_) {
|
||||
properties.push_back(interpreter_context->db->NameToProperty(prop.name));
|
||||
properties_string.push_back(prop.name);
|
||||
}
|
||||
auto properties_stringified = utils::Join(properties_string, ", ");
|
||||
|
||||
if (properties.size() > 1) {
|
||||
throw utils::NotYetImplemented("index on multiple properties");
|
||||
}
|
||||
|
||||
Notification index_notification(SeverityLevel::INFO);
|
||||
switch (index_query->action_) {
|
||||
case IndexQuery::Action::CREATE: {
|
||||
index_notification.code = NotificationCode::CREATE_INDEX;
|
||||
index_notification.title =
|
||||
fmt::format("Created index on label {} on properties {}.", index_query->label_.name, properties_stringified);
|
||||
|
||||
handler = [interpreter_context, label, properties_stringified = std::move(properties_stringified),
|
||||
label_name = index_query->label_.name, properties = std::move(properties),
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)](Notification &index_notification) {
|
||||
handler = [interpreter_context, label, properties = std::move(properties),
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)] {
|
||||
if (properties.empty()) {
|
||||
if (!interpreter_context->db->CreateIndex(label)) {
|
||||
index_notification.code = NotificationCode::EXISTANT_INDEX;
|
||||
index_notification.title =
|
||||
fmt::format("Index on label {} on properties {} already exists.", label_name, properties_stringified);
|
||||
}
|
||||
interpreter_context->db->CreateIndex(label);
|
||||
EventCounter::IncrementCounter(EventCounter::LabelIndexCreated);
|
||||
} else {
|
||||
MG_ASSERT(properties.size() == 1U);
|
||||
if (!interpreter_context->db->CreateIndex(label, properties[0])) {
|
||||
index_notification.code = NotificationCode::EXISTANT_INDEX;
|
||||
index_notification.title =
|
||||
fmt::format("Index on label {} on properties {} already exists.", label_name, properties_stringified);
|
||||
}
|
||||
interpreter_context->db->CreateIndex(label, properties[0]);
|
||||
EventCounter::IncrementCounter(EventCounter::LabelPropertyIndexCreated);
|
||||
}
|
||||
invalidate_plan_cache();
|
||||
@@ -1321,25 +1240,13 @@ PreparedQuery PrepareIndexQuery(ParsedQuery parsed_query, bool in_explicit_trans
|
||||
break;
|
||||
}
|
||||
case IndexQuery::Action::DROP: {
|
||||
index_notification.code = NotificationCode::DROP_INDEX;
|
||||
index_notification.title = fmt::format("Dropped index on label {} on properties {}.", index_query->label_.name,
|
||||
utils::Join(properties_string, ", "));
|
||||
handler = [interpreter_context, label, properties_stringified = std::move(properties_stringified),
|
||||
label_name = index_query->label_.name, properties = std::move(properties),
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)](Notification &index_notification) {
|
||||
handler = [interpreter_context, label, properties = std::move(properties),
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)] {
|
||||
if (properties.empty()) {
|
||||
if (!interpreter_context->db->DropIndex(label)) {
|
||||
index_notification.code = NotificationCode::NONEXISTANT_INDEX;
|
||||
index_notification.title =
|
||||
fmt::format("Index on label {} on properties {} doesn't exist.", label_name, properties_stringified);
|
||||
}
|
||||
interpreter_context->db->DropIndex(label);
|
||||
} else {
|
||||
MG_ASSERT(properties.size() == 1U);
|
||||
if (!interpreter_context->db->DropIndex(label, properties[0])) {
|
||||
index_notification.code = NotificationCode::NONEXISTANT_INDEX;
|
||||
index_notification.title =
|
||||
fmt::format("Index on label {} on properties {} doesn't exist.", label_name, properties_stringified);
|
||||
}
|
||||
interpreter_context->db->DropIndex(label, properties[0]);
|
||||
}
|
||||
invalidate_plan_cache();
|
||||
};
|
||||
@@ -1347,16 +1254,13 @@ PreparedQuery PrepareIndexQuery(ParsedQuery parsed_query, bool in_explicit_trans
|
||||
}
|
||||
}
|
||||
|
||||
return PreparedQuery{
|
||||
{},
|
||||
std::move(parsed_query.required_privileges),
|
||||
[handler = std::move(handler), notifications, index_notification = std::move(index_notification)](
|
||||
AnyStream * /*stream*/, std::optional<int> /*unused*/) mutable {
|
||||
handler(index_notification);
|
||||
notifications->push_back(index_notification);
|
||||
return QueryHandlerResult::NOTHING;
|
||||
},
|
||||
RWType::W};
|
||||
return PreparedQuery{{},
|
||||
std::move(parsed_query.required_privileges),
|
||||
[handler = std::move(handler)](AnyStream *stream, std::optional<int>) {
|
||||
handler();
|
||||
return QueryHandlerResult::NOTHING;
|
||||
},
|
||||
RWType::W};
|
||||
}
|
||||
|
||||
PreparedQuery PrepareAuthQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
@@ -1396,15 +1300,13 @@ PreparedQuery PrepareAuthQuery(ParsedQuery parsed_query, bool in_explicit_transa
|
||||
}
|
||||
|
||||
PreparedQuery PrepareReplicationQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications, InterpreterContext *interpreter_context,
|
||||
DbAccessor *dba) {
|
||||
InterpreterContext *interpreter_context, DbAccessor *dba) {
|
||||
if (in_explicit_transaction) {
|
||||
throw ReplicationModificationInMulticommandTxException();
|
||||
}
|
||||
|
||||
auto *replication_query = utils::Downcast<ReplicationQuery>(parsed_query.query);
|
||||
auto callback =
|
||||
HandleReplicationQuery(replication_query, parsed_query.parameters, interpreter_context, dba, notifications);
|
||||
auto callback = HandleReplicationQuery(replication_query, parsed_query.parameters, interpreter_context, dba);
|
||||
|
||||
return PreparedQuery{callback.header, std::move(parsed_query.required_privileges),
|
||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
||||
@@ -1553,8 +1455,8 @@ Callback ShowTriggers(InterpreterContext *interpreter_context) {
|
||||
}
|
||||
|
||||
PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications, InterpreterContext *interpreter_context,
|
||||
DbAccessor *dba, const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
InterpreterContext *interpreter_context, DbAccessor *dba,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
const std::string *username) {
|
||||
if (in_explicit_transaction) {
|
||||
throw TriggerModificationInMulticommandTxException();
|
||||
@@ -1563,36 +1465,27 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, const bool in_explic
|
||||
auto *trigger_query = utils::Downcast<TriggerQuery>(parsed_query.query);
|
||||
MG_ASSERT(trigger_query);
|
||||
|
||||
std::optional<Notification> trigger_notification;
|
||||
auto callback = std::invoke([trigger_query, interpreter_context, dba, &user_parameters,
|
||||
owner = StringPointerToOptional(username), &trigger_notification]() mutable {
|
||||
auto callback = [trigger_query, interpreter_context, dba, &user_parameters,
|
||||
owner = StringPointerToOptional(username)]() mutable {
|
||||
switch (trigger_query->action_) {
|
||||
case TriggerQuery::Action::CREATE_TRIGGER:
|
||||
trigger_notification.emplace(SeverityLevel::INFO, NotificationCode::CREATE_TRIGGER,
|
||||
fmt::format("Created trigger {}.", trigger_query->trigger_name_));
|
||||
EventCounter::IncrementCounter(EventCounter::TriggersCreated);
|
||||
return CreateTrigger(trigger_query, user_parameters, interpreter_context, dba, std::move(owner));
|
||||
case TriggerQuery::Action::DROP_TRIGGER:
|
||||
trigger_notification.emplace(SeverityLevel::INFO, NotificationCode::DROP_TRIGGER,
|
||||
fmt::format("Dropped trigger {}.", trigger_query->trigger_name_));
|
||||
return DropTrigger(trigger_query, interpreter_context);
|
||||
case TriggerQuery::Action::SHOW_TRIGGERS:
|
||||
return ShowTriggers(interpreter_context);
|
||||
}
|
||||
});
|
||||
}();
|
||||
|
||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr},
|
||||
trigger_notification = std::move(trigger_notification), notifications](
|
||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
||||
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
||||
if (UNLIKELY(!pull_plan)) {
|
||||
pull_plan = std::make_shared<PullPlanVector>(callback_fn());
|
||||
}
|
||||
|
||||
if (pull_plan->Pull(stream, n)) {
|
||||
if (trigger_notification) {
|
||||
notifications->push_back(std::move(*trigger_notification));
|
||||
}
|
||||
return QueryHandlerResult::COMMIT;
|
||||
}
|
||||
return std::nullopt;
|
||||
@@ -1603,9 +1496,8 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, const bool in_explic
|
||||
}
|
||||
|
||||
PreparedQuery PrepareStreamQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications, InterpreterContext *interpreter_context,
|
||||
DbAccessor *dba,
|
||||
const std::map<std::string, storage::PropertyValue> & /*user_parameters*/,
|
||||
InterpreterContext *interpreter_context, DbAccessor *dba,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
const std::string *username) {
|
||||
if (in_explicit_transaction) {
|
||||
throw StreamQueryInMulticommandTxException();
|
||||
@@ -1613,8 +1505,7 @@ PreparedQuery PrepareStreamQuery(ParsedQuery parsed_query, const bool in_explici
|
||||
|
||||
auto *stream_query = utils::Downcast<StreamQuery>(parsed_query.query);
|
||||
MG_ASSERT(stream_query);
|
||||
auto callback =
|
||||
HandleStreamQuery(stream_query, parsed_query.parameters, interpreter_context, dba, username, notifications);
|
||||
auto callback = HandleStreamQuery(stream_query, parsed_query.parameters, interpreter_context, dba, username);
|
||||
|
||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
||||
@@ -1725,24 +1616,6 @@ PreparedQuery PrepareSettingQuery(ParsedQuery parsed_query, const bool in_explic
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
}
|
||||
|
||||
PreparedQuery PrepareVersionQuery(ParsedQuery parsed_query, const bool in_explicit_transaction) {
|
||||
if (in_explicit_transaction) {
|
||||
throw VersionInfoInMulticommandTxException();
|
||||
}
|
||||
|
||||
return PreparedQuery{{"version"},
|
||||
std::move(parsed_query.required_privileges),
|
||||
[](AnyStream *stream, std::optional<int> /*n*/) {
|
||||
std::vector<TypedValue> version_value;
|
||||
version_value.reserve(1);
|
||||
|
||||
version_value.emplace_back(gflags::VersionString());
|
||||
stream->Result(version_value);
|
||||
return QueryHandlerResult::COMMIT;
|
||||
},
|
||||
RWType::NONE};
|
||||
}
|
||||
|
||||
PreparedQuery PrepareInfoQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
std::map<std::string, TypedValue> *summary, InterpreterContext *interpreter_context,
|
||||
storage::Storage *db, utils::MemoryResource *execution_memory) {
|
||||
@@ -1832,31 +1705,24 @@ PreparedQuery PrepareInfoQuery(ParsedQuery parsed_query, bool in_explicit_transa
|
||||
}
|
||||
|
||||
PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications,
|
||||
InterpreterContext *interpreter_context) {
|
||||
std::map<std::string, TypedValue> *summary,
|
||||
InterpreterContext *interpreter_context, utils::MemoryResource *execution_memory) {
|
||||
if (in_explicit_transaction) {
|
||||
throw ConstraintInMulticommandTxException();
|
||||
}
|
||||
|
||||
auto *constraint_query = utils::Downcast<ConstraintQuery>(parsed_query.query);
|
||||
std::function<void(Notification &)> handler;
|
||||
std::function<void()> handler;
|
||||
|
||||
auto label = interpreter_context->db->NameToLabel(constraint_query->constraint_.label.name);
|
||||
std::vector<storage::PropertyId> properties;
|
||||
std::vector<std::string> properties_string;
|
||||
properties.reserve(constraint_query->constraint_.properties.size());
|
||||
properties_string.reserve(constraint_query->constraint_.properties.size());
|
||||
for (const auto &prop : constraint_query->constraint_.properties) {
|
||||
properties.push_back(interpreter_context->db->NameToProperty(prop.name));
|
||||
properties_string.push_back(prop.name);
|
||||
}
|
||||
auto properties_stringified = utils::Join(properties_string, ", ");
|
||||
|
||||
Notification constraint_notification(SeverityLevel::INFO);
|
||||
switch (constraint_query->action_type_) {
|
||||
case ConstraintQuery::ActionType::CREATE: {
|
||||
constraint_notification.code = NotificationCode::CREATE_CONSTRAINT;
|
||||
|
||||
switch (constraint_query->constraint_.type) {
|
||||
case Constraint::Type::NODE_KEY:
|
||||
throw utils::NotYetImplemented("Node key constraints");
|
||||
@@ -1864,11 +1730,7 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
|
||||
if (properties.empty() || properties.size() > 1) {
|
||||
throw SyntaxException("Exactly one property must be used for existence constraints.");
|
||||
}
|
||||
constraint_notification.title = fmt::format("Created EXISTS constraint on label {} on properties {}.",
|
||||
constraint_query->constraint_.label.name, properties_stringified);
|
||||
handler = [interpreter_context, label, label_name = constraint_query->constraint_.label.name,
|
||||
properties_stringified = std::move(properties_stringified),
|
||||
properties = std::move(properties)](Notification &constraint_notification) {
|
||||
handler = [interpreter_context, label, properties = std::move(properties)] {
|
||||
auto res = interpreter_context->db->CreateExistenceConstraint(label, properties[0]);
|
||||
if (res.HasError()) {
|
||||
auto violation = res.GetError();
|
||||
@@ -1880,11 +1742,6 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
|
||||
"existing node violates it.",
|
||||
label_name, property_name);
|
||||
}
|
||||
if (res.HasValue() && !res.GetValue()) {
|
||||
constraint_notification.code = NotificationCode::EXISTANT_CONSTRAINT;
|
||||
constraint_notification.title = fmt::format(
|
||||
"Constraint EXISTS on label {} on properties {} already exists.", label_name, properties_stringified);
|
||||
}
|
||||
};
|
||||
break;
|
||||
case Constraint::Type::UNIQUE:
|
||||
@@ -1895,12 +1752,7 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
|
||||
if (property_set.size() != properties.size()) {
|
||||
throw SyntaxException("The given set of properties contains duplicates.");
|
||||
}
|
||||
constraint_notification.title =
|
||||
fmt::format("Created UNIQUE constraint on label {} on properties {}.",
|
||||
constraint_query->constraint_.label.name, utils::Join(properties_string, ", "));
|
||||
handler = [interpreter_context, label, label_name = constraint_query->constraint_.label.name,
|
||||
properties_stringified = std::move(properties_stringified),
|
||||
property_set = std::move(property_set)](Notification &constraint_notification) {
|
||||
handler = [interpreter_context, label, property_set = std::move(property_set)] {
|
||||
auto res = interpreter_context->db->CreateUniqueConstraint(label, property_set);
|
||||
if (res.HasError()) {
|
||||
auto violation = res.GetError();
|
||||
@@ -1914,33 +1766,29 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
|
||||
"Unable to create unique constraint :{}({}), because an "
|
||||
"existing node violates it.",
|
||||
label_name, property_names_stream.str());
|
||||
}
|
||||
switch (res.GetValue()) {
|
||||
case storage::UniqueConstraints::CreationStatus::EMPTY_PROPERTIES:
|
||||
throw SyntaxException(
|
||||
"At least one property must be used for unique "
|
||||
"constraints.");
|
||||
case storage::UniqueConstraints::CreationStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED:
|
||||
throw SyntaxException(
|
||||
"Too many properties specified. Limit of {} properties "
|
||||
"for unique constraints is exceeded.",
|
||||
storage::kUniqueConstraintsMaxProperties);
|
||||
case storage::UniqueConstraints::CreationStatus::ALREADY_EXISTS:
|
||||
constraint_notification.code = NotificationCode::EXISTANT_CONSTRAINT;
|
||||
constraint_notification.title =
|
||||
fmt::format("Constraint UNIQUE on label {} on properties {} already exists.", label_name,
|
||||
properties_stringified);
|
||||
break;
|
||||
case storage::UniqueConstraints::CreationStatus::SUCCESS:
|
||||
break;
|
||||
} else {
|
||||
switch (res.GetValue()) {
|
||||
case storage::UniqueConstraints::CreationStatus::EMPTY_PROPERTIES:
|
||||
throw SyntaxException(
|
||||
"At least one property must be used for unique "
|
||||
"constraints.");
|
||||
break;
|
||||
case storage::UniqueConstraints::CreationStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED:
|
||||
throw SyntaxException(
|
||||
"Too many properties specified. Limit of {} properties "
|
||||
"for unique constraints is exceeded.",
|
||||
storage::kUniqueConstraintsMaxProperties);
|
||||
break;
|
||||
case storage::UniqueConstraints::CreationStatus::ALREADY_EXISTS:
|
||||
case storage::UniqueConstraints::CreationStatus::SUCCESS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case ConstraintQuery::ActionType::DROP: {
|
||||
constraint_notification.code = NotificationCode::DROP_CONSTRAINT;
|
||||
|
||||
switch (constraint_query->constraint_.type) {
|
||||
case Constraint::Type::NODE_KEY:
|
||||
throw utils::NotYetImplemented("Node key constraints");
|
||||
@@ -1948,17 +1796,8 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
|
||||
if (properties.empty() || properties.size() > 1) {
|
||||
throw SyntaxException("Exactly one property must be used for existence constraints.");
|
||||
}
|
||||
constraint_notification.title =
|
||||
fmt::format("Dropped EXISTS constraint on label {} on properties {}.",
|
||||
constraint_query->constraint_.label.name, utils::Join(properties_string, ", "));
|
||||
handler = [interpreter_context, label, label_name = constraint_query->constraint_.label.name,
|
||||
properties_stringified = std::move(properties_stringified),
|
||||
properties = std::move(properties)](Notification &constraint_notification) {
|
||||
if (!interpreter_context->db->DropExistenceConstraint(label, properties[0])) {
|
||||
constraint_notification.code = NotificationCode::NONEXISTANT_CONSTRAINT;
|
||||
constraint_notification.title = fmt::format(
|
||||
"Constraint EXISTS on label {} on properties {} doesn't exist.", label_name, properties_stringified);
|
||||
}
|
||||
handler = [interpreter_context, label, properties = std::move(properties)] {
|
||||
interpreter_context->db->DropExistenceConstraint(label, properties[0]);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
break;
|
||||
@@ -1970,12 +1809,7 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
|
||||
if (property_set.size() != properties.size()) {
|
||||
throw SyntaxException("The given set of properties contains duplicates.");
|
||||
}
|
||||
constraint_notification.title =
|
||||
fmt::format("Dropped UNIQUE constraint on label {} on properties {}.",
|
||||
constraint_query->constraint_.label.name, utils::Join(properties_string, ", "));
|
||||
handler = [interpreter_context, label, label_name = constraint_query->constraint_.label.name,
|
||||
properties_stringified = std::move(properties_stringified),
|
||||
property_set = std::move(property_set)](Notification &constraint_notification) {
|
||||
handler = [interpreter_context, label, property_set = std::move(property_set)] {
|
||||
auto res = interpreter_context->db->DropUniqueConstraint(label, property_set);
|
||||
switch (res) {
|
||||
case storage::UniqueConstraints::DeletionStatus::EMPTY_PROPERTIES:
|
||||
@@ -1990,11 +1824,6 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
|
||||
storage::kUniqueConstraintsMaxProperties);
|
||||
break;
|
||||
case storage::UniqueConstraints::DeletionStatus::NOT_FOUND:
|
||||
constraint_notification.code = NotificationCode::NONEXISTANT_CONSTRAINT;
|
||||
constraint_notification.title =
|
||||
fmt::format("Constraint UNIQUE on label {} on properties {} doesn't exist.", label_name,
|
||||
properties_stringified);
|
||||
break;
|
||||
case storage::UniqueConstraints::DeletionStatus::SUCCESS:
|
||||
break;
|
||||
}
|
||||
@@ -2006,10 +1835,8 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
|
||||
|
||||
return PreparedQuery{{},
|
||||
std::move(parsed_query.required_privileges),
|
||||
[handler = std::move(handler), constraint_notification = std::move(constraint_notification),
|
||||
notifications](AnyStream * /*stream*/, std::optional<int> /*n*/) mutable {
|
||||
handler(constraint_notification);
|
||||
notifications->push_back(constraint_notification);
|
||||
[handler = std::move(handler)](AnyStream *stream, std::optional<int> n) {
|
||||
handler();
|
||||
return QueryHandlerResult::COMMIT;
|
||||
},
|
||||
RWType::NONE};
|
||||
@@ -2095,7 +1922,6 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
if (utils::Downcast<CypherQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareCypherQuery(std::move(parsed_query), &query_execution->summary, interpreter_context_,
|
||||
&*execution_db_accessor_, &query_execution->execution_memory,
|
||||
&query_execution->notifications,
|
||||
trigger_context_collector_ ? &*trigger_context_collector_ : nullptr);
|
||||
} else if (utils::Downcast<ExplainQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareExplainQuery(std::move(parsed_query), &query_execution->summary, interpreter_context_,
|
||||
@@ -2108,8 +1934,8 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
prepared_query = PrepareDumpQuery(std::move(parsed_query), &query_execution->summary, &*execution_db_accessor_,
|
||||
&query_execution->execution_memory);
|
||||
} else if (utils::Downcast<IndexQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareIndexQuery(std::move(parsed_query), in_explicit_transaction_,
|
||||
&query_execution->notifications, interpreter_context_);
|
||||
prepared_query = PrepareIndexQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
|
||||
interpreter_context_, &query_execution->execution_memory_with_exception);
|
||||
} else if (utils::Downcast<AuthQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareAuthQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
|
||||
interpreter_context_, &*execution_db_accessor_,
|
||||
@@ -2119,25 +1945,23 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
interpreter_context_, interpreter_context_->db,
|
||||
&query_execution->execution_memory_with_exception);
|
||||
} else if (utils::Downcast<ConstraintQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareConstraintQuery(std::move(parsed_query), in_explicit_transaction_,
|
||||
&query_execution->notifications, interpreter_context_);
|
||||
} else if (utils::Downcast<ReplicationQuery>(parsed_query.query)) {
|
||||
prepared_query =
|
||||
PrepareReplicationQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
||||
interpreter_context_, &*execution_db_accessor_);
|
||||
PrepareConstraintQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
|
||||
interpreter_context_, &query_execution->execution_memory_with_exception);
|
||||
} else if (utils::Downcast<ReplicationQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareReplicationQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
|
||||
&*execution_db_accessor_);
|
||||
} else if (utils::Downcast<LockPathQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareLockPathQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
|
||||
&*execution_db_accessor_);
|
||||
} else if (utils::Downcast<FreeMemoryQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareFreeMemoryQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_);
|
||||
} else if (utils::Downcast<TriggerQuery>(parsed_query.query)) {
|
||||
prepared_query =
|
||||
PrepareTriggerQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
||||
interpreter_context_, &*execution_db_accessor_, params, username);
|
||||
prepared_query = PrepareTriggerQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
|
||||
&*execution_db_accessor_, params, username);
|
||||
} else if (utils::Downcast<StreamQuery>(parsed_query.query)) {
|
||||
prepared_query =
|
||||
PrepareStreamQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
||||
interpreter_context_, &*execution_db_accessor_, params, username);
|
||||
prepared_query = PrepareStreamQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
|
||||
&*execution_db_accessor_, params, username);
|
||||
} else if (utils::Downcast<IsolationLevelQuery>(parsed_query.query)) {
|
||||
prepared_query =
|
||||
PrepareIsolationLevelQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_, this);
|
||||
@@ -2146,8 +1970,6 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
PrepareCreateSnapshotQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_);
|
||||
} else if (utils::Downcast<SettingQuery>(parsed_query.query)) {
|
||||
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 {
|
||||
LOG_FATAL("Should not get here -- unknown query type!");
|
||||
}
|
||||
@@ -2314,7 +2136,7 @@ void Interpreter::Commit() {
|
||||
|
||||
reset_necessary_members();
|
||||
|
||||
SPDLOG_DEBUG("Finished committing the transaction");
|
||||
SPDLOG_DEBUG("Finished comitting the transaction");
|
||||
}
|
||||
|
||||
void Interpreter::AdvanceCommand() {
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "query/frontend/ast/cypher_main_visitor.hpp"
|
||||
#include "query/frontend/stripped.hpp"
|
||||
#include "query/interpret/frame.hpp"
|
||||
#include "query/metadata.hpp"
|
||||
#include "query/plan/operator.hpp"
|
||||
#include "query/plan/read_write_type_checker.hpp"
|
||||
#include "query/stream.hpp"
|
||||
@@ -190,7 +189,7 @@ struct InterpreterContext {
|
||||
|
||||
const InterpreterConfig config;
|
||||
|
||||
query::stream::Streams streams;
|
||||
query::Streams streams;
|
||||
};
|
||||
|
||||
/// Function that is used to tell all active interpreters that they should stop
|
||||
@@ -286,7 +285,6 @@ class Interpreter final {
|
||||
utils::ResourceWithOutOfMemoryException execution_memory_with_exception{&execution_memory};
|
||||
|
||||
std::map<std::string, TypedValue> summary;
|
||||
std::vector<Notification> notifications;
|
||||
|
||||
explicit QueryExecution() = default;
|
||||
QueryExecution(const QueryExecution &) = delete;
|
||||
@@ -379,14 +377,6 @@ std::map<std::string, TypedValue> Interpreter::Pull(TStream *result_stream, std:
|
||||
if (maybe_res) {
|
||||
// Save its summary
|
||||
maybe_summary.emplace(std::move(query_execution->summary));
|
||||
if (!query_execution->notifications.empty()) {
|
||||
std::vector<TypedValue> notifications;
|
||||
notifications.reserve(query_execution->notifications.size());
|
||||
for (const auto ¬ification : query_execution->notifications) {
|
||||
notifications.emplace_back(notification.ConvertToMap());
|
||||
}
|
||||
maybe_summary->insert_or_assign("notifications", std::move(notifications));
|
||||
}
|
||||
if (!in_explicit_transaction_) {
|
||||
switch (*maybe_res) {
|
||||
case QueryHandlerResult::COMMIT:
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
// Copyright 2021 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/metadata.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <compare>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace query {
|
||||
|
||||
namespace {
|
||||
using namespace std::literals;
|
||||
|
||||
constexpr std::string_view GetSeverityLevelString(const SeverityLevel level) {
|
||||
switch (level) {
|
||||
case SeverityLevel::INFO:
|
||||
return "INFO"sv;
|
||||
case SeverityLevel::WARNING:
|
||||
return "WARNING"sv;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr std::string_view GetCodeString(const NotificationCode code) {
|
||||
switch (code) {
|
||||
case NotificationCode::CREATE_CONSTRAINT:
|
||||
return "CreateConstraint"sv;
|
||||
case NotificationCode::CREATE_INDEX:
|
||||
return "CreateIndex"sv;
|
||||
case NotificationCode::CREATE_STREAM:
|
||||
return "CreateStream"sv;
|
||||
case NotificationCode::CHECK_STREAM:
|
||||
return "CheckStream"sv;
|
||||
case NotificationCode::CREATE_TRIGGER:
|
||||
return "CreateTrigger"sv;
|
||||
case NotificationCode::DROP_CONSTRAINT:
|
||||
return "DropConstraint"sv;
|
||||
case NotificationCode::DROP_REPLICA:
|
||||
return "DropReplica"sv;
|
||||
case NotificationCode::DROP_INDEX:
|
||||
return "DropIndex"sv;
|
||||
case NotificationCode::DROP_STREAM:
|
||||
return "DropStream"sv;
|
||||
case NotificationCode::DROP_TRIGGER:
|
||||
return "DropTrigger"sv;
|
||||
case NotificationCode::EXISTANT_CONSTRAINT:
|
||||
return "ConstraintAlreadyExists"sv;
|
||||
case NotificationCode::EXISTANT_INDEX:
|
||||
return "IndexAlreadyExists"sv;
|
||||
case NotificationCode::LOAD_CSV_TIP:
|
||||
return "LoadCSVTip"sv;
|
||||
case NotificationCode::NONEXISTANT_INDEX:
|
||||
return "IndexDoesNotExist"sv;
|
||||
case NotificationCode::NONEXISTANT_CONSTRAINT:
|
||||
return "ConstraintDoesNotExist"sv;
|
||||
case NotificationCode::REGISTER_REPLICA:
|
||||
return "RegisterReplica"sv;
|
||||
case NotificationCode::REPLICA_PORT_WARNING:
|
||||
return "ReplicaPortWarning"sv;
|
||||
case NotificationCode::SET_REPLICA:
|
||||
return "SetReplica"sv;
|
||||
case NotificationCode::START_STREAM:
|
||||
return "StartStream"sv;
|
||||
case NotificationCode::START_ALL_STREAMS:
|
||||
return "StartAllStreams"sv;
|
||||
case NotificationCode::STOP_STREAM:
|
||||
return "StopStream"sv;
|
||||
case NotificationCode::STOP_ALL_STREAMS:
|
||||
return "StopAllStreams"sv;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Notification::Notification(SeverityLevel level) : level{level} {};
|
||||
|
||||
Notification::Notification(SeverityLevel level, NotificationCode code, std::string title, std::string description)
|
||||
: level{level}, code{code}, title(std::move(title)), description(std::move(description)){};
|
||||
|
||||
Notification::Notification(SeverityLevel level, NotificationCode code, std::string title)
|
||||
: level{level}, code{code}, title(std::move(title)){};
|
||||
|
||||
std::map<std::string, TypedValue> Notification::ConvertToMap() const {
|
||||
return std::map<std::string, TypedValue>{{"severity", TypedValue(GetSeverityLevelString(level))},
|
||||
{"code", TypedValue(GetCodeString(code))},
|
||||
{"title", TypedValue(title)},
|
||||
{"description", TypedValue(description)}};
|
||||
}
|
||||
|
||||
std::string ExecutionStatsKeyToString(const ExecutionStats::Key key) {
|
||||
switch (key) {
|
||||
case ExecutionStats::Key::CREATED_NODES:
|
||||
return std::string("nodes-created");
|
||||
case ExecutionStats::Key::DELETED_NODES:
|
||||
return std::string("nodes-deleted");
|
||||
case ExecutionStats::Key::CREATED_EDGES:
|
||||
return std::string("relationships-created");
|
||||
case ExecutionStats::Key::DELETED_EDGES:
|
||||
return std::string("relationships-deleted");
|
||||
case ExecutionStats::Key::CREATED_LABELS:
|
||||
return std::string("labels-added");
|
||||
case ExecutionStats::Key::DELETED_LABELS:
|
||||
return std::string("labels-removed");
|
||||
case ExecutionStats::Key::UPDATED_PROPERTIES:
|
||||
return std::string("properties-set");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace query
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright 2021 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>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include "query/typed_value.hpp"
|
||||
|
||||
namespace query {
|
||||
|
||||
enum class SeverityLevel : uint8_t { INFO, WARNING };
|
||||
|
||||
enum class NotificationCode : uint8_t {
|
||||
CREATE_CONSTRAINT,
|
||||
CREATE_INDEX,
|
||||
CHECK_STREAM,
|
||||
CREATE_STREAM,
|
||||
CREATE_TRIGGER,
|
||||
DROP_CONSTRAINT,
|
||||
DROP_INDEX,
|
||||
DROP_REPLICA,
|
||||
DROP_STREAM,
|
||||
DROP_TRIGGER,
|
||||
EXISTANT_INDEX,
|
||||
EXISTANT_CONSTRAINT,
|
||||
LOAD_CSV_TIP,
|
||||
NONEXISTANT_INDEX,
|
||||
NONEXISTANT_CONSTRAINT,
|
||||
REPLICA_PORT_WARNING,
|
||||
REGISTER_REPLICA,
|
||||
SET_REPLICA,
|
||||
START_STREAM,
|
||||
START_ALL_STREAMS,
|
||||
STOP_STREAM,
|
||||
STOP_ALL_STREAMS,
|
||||
};
|
||||
|
||||
struct Notification {
|
||||
SeverityLevel level;
|
||||
NotificationCode code;
|
||||
std::string title;
|
||||
std::string description;
|
||||
|
||||
explicit Notification(SeverityLevel level);
|
||||
|
||||
Notification(SeverityLevel level, NotificationCode code, std::string title, std::string description);
|
||||
|
||||
Notification(SeverityLevel level, NotificationCode code, std::string title);
|
||||
|
||||
std::map<std::string, TypedValue> ConvertToMap() const;
|
||||
};
|
||||
|
||||
struct ExecutionStats {
|
||||
public:
|
||||
// All the stats have specific key to be compatible with neo4j
|
||||
enum class Key : uint8_t {
|
||||
CREATED_NODES,
|
||||
DELETED_NODES,
|
||||
CREATED_EDGES,
|
||||
DELETED_EDGES,
|
||||
CREATED_LABELS,
|
||||
DELETED_LABELS,
|
||||
UPDATED_PROPERTIES,
|
||||
};
|
||||
|
||||
int64_t &operator[](Key key) { return counters[static_cast<size_t>(key)]; }
|
||||
|
||||
private:
|
||||
static constexpr auto kExecutionStatsCountersSize = std::underlying_type_t<Key>(Key::UPDATED_PROPERTIES) + 1;
|
||||
|
||||
public:
|
||||
std::array<int64_t, kExecutionStatsCountersSize> counters{0};
|
||||
};
|
||||
|
||||
std::string ExecutionStatsKeyToString(ExecutionStats::Key key);
|
||||
|
||||
} // namespace query
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "query/plan/operator.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <random>
|
||||
@@ -172,16 +171,15 @@ CreateNode::CreateNode(const std::shared_ptr<LogicalOperator> &input, const Node
|
||||
|
||||
// Creates a vertex on this GraphDb. Returns a reference to vertex placed on the
|
||||
// frame.
|
||||
VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *frame, ExecutionContext &context) {
|
||||
VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *frame, const ExecutionContext &context) {
|
||||
auto &dba = *context.db_accessor;
|
||||
auto new_node = dba.InsertVertex();
|
||||
context.execution_stats[ExecutionStats::Key::CREATED_NODES] += 1;
|
||||
for (auto label : node_info.labels) {
|
||||
auto maybe_error = new_node.AddLabel(label);
|
||||
if (maybe_error.HasError()) {
|
||||
switch (maybe_error.GetError()) {
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to set a label on a deleted node.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@@ -190,7 +188,6 @@ VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *fram
|
||||
throw QueryRuntimeException("Unexpected error when setting a label.");
|
||||
}
|
||||
}
|
||||
context.execution_stats[ExecutionStats::Key::CREATED_LABELS] += 1;
|
||||
}
|
||||
// Evaluator should use the latest accessors, as modified in this query, when
|
||||
// setting properties on new nodes.
|
||||
@@ -298,7 +295,7 @@ EdgeAccessor CreateEdge(const EdgeCreationInfo &edge_info, DbAccessor *dba, Vert
|
||||
} else {
|
||||
switch (maybe_edge.GetError()) {
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to create an edge on a deleted node.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@@ -349,7 +346,6 @@ bool CreateExpand::CreateExpandCursor::Pull(Frame &frame, ExecutionContext &cont
|
||||
}
|
||||
}();
|
||||
|
||||
context.execution_stats[ExecutionStats::Key::CREATED_EDGES] += 1;
|
||||
if (context.trigger_context_collector) {
|
||||
context.trigger_context_collector->RegisterCreatedObject(created_edge);
|
||||
}
|
||||
@@ -1921,7 +1917,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
if (maybe_value.HasError()) {
|
||||
switch (maybe_value.GetError()) {
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@@ -1929,7 +1925,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
throw QueryRuntimeException("Unexpected error when deleting an edge.");
|
||||
}
|
||||
}
|
||||
context.execution_stats[ExecutionStats::Key::DELETED_EDGES] += 1;
|
||||
|
||||
if (context.trigger_context_collector && maybe_value.GetValue()) {
|
||||
context.trigger_context_collector->RegisterDeletedObject(*maybe_value.GetValue());
|
||||
}
|
||||
@@ -1947,7 +1943,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
if (res.HasError()) {
|
||||
switch (res.GetError()) {
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
@@ -1956,10 +1952,6 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
}
|
||||
}
|
||||
|
||||
context.execution_stats[ExecutionStats::Key::DELETED_NODES] += 1;
|
||||
if (*res) {
|
||||
context.execution_stats[ExecutionStats::Key::DELETED_EDGES] += static_cast<int64_t>((*res)->second.size());
|
||||
}
|
||||
std::invoke([&] {
|
||||
if (!context.trigger_context_collector || !*res) {
|
||||
return;
|
||||
@@ -1978,7 +1970,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
if (res.HasError()) {
|
||||
switch (res.GetError()) {
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
throw RemoveAttachedVertexException();
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
@@ -1987,7 +1979,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
throw QueryRuntimeException("Unexpected error when deleting a node.");
|
||||
}
|
||||
}
|
||||
context.execution_stats[ExecutionStats::Key::DELETED_NODES] += 1;
|
||||
|
||||
if (context.trigger_context_collector && res.GetValue()) {
|
||||
context.trigger_context_collector->RegisterDeletedObject(*res.GetValue());
|
||||
}
|
||||
@@ -2046,7 +2038,7 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
|
||||
switch (lhs.type()) {
|
||||
case TypedValue::Type::Vertex: {
|
||||
auto old_value = PropsSetChecked(&lhs.ValueVertex(), self_.property_, rhs);
|
||||
context.execution_stats[ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
|
||||
|
||||
if (context.trigger_context_collector) {
|
||||
// rhs cannot be moved because it was created with the allocator that is only valid during current pull
|
||||
context.trigger_context_collector->RegisterSetObjectProperty(lhs.ValueVertex(), self_.property_,
|
||||
@@ -2056,7 +2048,7 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
|
||||
}
|
||||
case TypedValue::Type::Edge: {
|
||||
auto old_value = PropsSetChecked(&lhs.ValueEdge(), self_.property_, rhs);
|
||||
context.execution_stats[ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
|
||||
|
||||
if (context.trigger_context_collector) {
|
||||
// rhs cannot be moved because it was created with the allocator that is only valid during current pull
|
||||
context.trigger_context_collector->RegisterSetObjectProperty(lhs.ValueEdge(), self_.property_,
|
||||
@@ -2107,7 +2099,7 @@ template <typename T>
|
||||
concept AccessorWithProperties = requires(T value, storage::PropertyId property_id,
|
||||
storage::PropertyValue property_value) {
|
||||
{ value.ClearProperties() } -> std::same_as<storage::Result<std::map<storage::PropertyId, storage::PropertyValue>>>;
|
||||
{value.SetProperty(property_id, property_value)};
|
||||
{ value.SetProperty(property_id, property_value) };
|
||||
};
|
||||
|
||||
/// Helper function that sets the given values on either a Vertex or an Edge.
|
||||
@@ -2128,7 +2120,7 @@ void SetPropertiesOnRecord(TRecordAccessor *record, const TypedValue &rhs, SetPr
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to set properties on a deleted graph element.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
throw QueryRuntimeException("Can't set property because properties on edges are disabled.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@@ -2184,7 +2176,7 @@ void SetPropertiesOnRecord(TRecordAccessor *record, const TypedValue &rhs, SetPr
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to set properties on a deleted graph element.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
throw QueryRuntimeException("Can't set property because properties on edges are disabled.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@@ -2299,7 +2291,7 @@ bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
if (maybe_value.HasError()) {
|
||||
switch (maybe_value.GetError()) {
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to set a label on a deleted node.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@@ -2357,7 +2349,7 @@ bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame, ExecutionContext &
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to remove a property on a deleted graph element.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
throw QueryRuntimeException(
|
||||
"Can't remove property because properties on edges are "
|
||||
@@ -2428,7 +2420,7 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &cont
|
||||
if (maybe_value.HasError()) {
|
||||
switch (maybe_value.GetError()) {
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
throw QueryRuntimeException(kSerializationErrorMessage);
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to remove labels from a deleted node.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
@@ -2438,7 +2430,6 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &cont
|
||||
}
|
||||
}
|
||||
|
||||
context.execution_stats[ExecutionStats::Key::DELETED_LABELS] += 1;
|
||||
if (context.trigger_context_collector && *maybe_value) {
|
||||
context.trigger_context_collector->RegisterRemovedVertexLabel(vertex, label);
|
||||
}
|
||||
@@ -2599,8 +2590,8 @@ namespace {
|
||||
TypedValue DefaultAggregationOpValue(const Aggregate::Element &element, utils::MemoryResource *memory) {
|
||||
switch (element.op) {
|
||||
case Aggregation::Op::COUNT:
|
||||
case Aggregation::Op::SUM:
|
||||
return TypedValue(0, memory);
|
||||
case Aggregation::Op::SUM:
|
||||
case Aggregation::Op::MIN:
|
||||
case Aggregation::Op::MAX:
|
||||
case Aggregation::Op::AVG:
|
||||
@@ -3813,13 +3804,13 @@ class CallProcedureCursor : public Cursor {
|
||||
throw QueryRuntimeException("There is no procedure named '{}'.", self_->procedure_name_);
|
||||
}
|
||||
const auto &[module, proc] = *maybe_found;
|
||||
if (proc->info.is_write != self_->is_write_) {
|
||||
if (proc->is_write_procedure != self_->is_write_) {
|
||||
auto get_proc_type_str = [](bool is_write) { return is_write ? "write" : "read"; };
|
||||
throw QueryRuntimeException("The procedure named '{}' was a {} procedure, but changed to be a {} procedure.",
|
||||
self_->procedure_name_, get_proc_type_str(self_->is_write_),
|
||||
get_proc_type_str(proc->info.is_write));
|
||||
get_proc_type_str(proc->is_write_procedure));
|
||||
}
|
||||
const auto graph_view = proc->info.is_write ? storage::View::NEW : storage::View::OLD;
|
||||
const auto graph_view = proc->is_write_procedure ? storage::View::NEW : storage::View::OLD;
|
||||
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
|
||||
graph_view);
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright 2021 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/procedure/mg_procedure_helpers.hpp"
|
||||
|
||||
namespace query::procedure {
|
||||
MgpUniquePtr<mgp_value> GetStringValueOrSetError(const char *string, mgp_memory *memory, mgp_result *result) {
|
||||
procedure::MgpUniquePtr<mgp_value> value{nullptr, mgp_value_destroy};
|
||||
const auto success =
|
||||
TryOrSetError([&] { return procedure::CreateMgpObject(value, mgp_value_make_string, string, memory); }, result);
|
||||
if (!success) {
|
||||
value.reset();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool InsertResultOrSetError(mgp_result *result, mgp_result_record *record, const char *result_name, mgp_value *value) {
|
||||
if (const auto err = mgp_result_record_insert(record, result_name, value); err != MGP_ERROR_NO_ERROR) {
|
||||
const auto error_msg = fmt::format("Unable to set the result for {}, error = {}", result_name, err);
|
||||
static_cast<void>(mgp_result_set_error_msg(result, error_msg.c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace query::procedure
|
||||
@@ -15,8 +15,6 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "mg_procedure.h"
|
||||
|
||||
namespace query::procedure {
|
||||
@@ -47,23 +45,4 @@ mgp_error CreateMgpObject(MgpUniquePtr<TObj> &obj, TFunc func, TArgs &&...args)
|
||||
obj.reset(raw_obj);
|
||||
return err;
|
||||
}
|
||||
|
||||
template <typename Fun>
|
||||
[[nodiscard]] bool TryOrSetError(Fun &&func, mgp_result *result) {
|
||||
if (const auto err = func(); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
|
||||
return false;
|
||||
} else if (err != MGP_ERROR_NO_ERROR) {
|
||||
const auto error_msg = fmt::format("Unexpected error ({})!", err);
|
||||
static_cast<void>(mgp_result_set_error_msg(result, error_msg.c_str()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] MgpUniquePtr<mgp_value> GetStringValueOrSetError(const char *string, mgp_memory *memory,
|
||||
mgp_result *result);
|
||||
|
||||
[[nodiscard]] bool InsertResultOrSetError(mgp_result *result, mgp_result_record *record, const char *result_name,
|
||||
mgp_value *value);
|
||||
} // namespace query::procedure
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "module.hpp"
|
||||
#include "query/procedure/cypher_types.hpp"
|
||||
#include "query/procedure/mg_procedure_helpers.hpp"
|
||||
#include "query/stream/common.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/view.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
@@ -36,7 +35,6 @@
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/temporal.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
// This file contains implementation of top level C API functions, but this is
|
||||
// all actually part of query::procedure. So use that namespace for simplicity.
|
||||
@@ -1178,7 +1176,7 @@ mgp_error mgp_date_timestamp(mgp_date *date, int64_t *timestamp) {
|
||||
}
|
||||
|
||||
mgp_error mgp_date_now(mgp_memory *memory, mgp_date **date) {
|
||||
return WrapExceptions([memory] { return NewRawMgpObject<mgp_date>(memory, utils::CurrentDate()); }, date);
|
||||
return WrapExceptions([memory] { return NewRawMgpObject<mgp_date>(memory, utils::UtcToday()); }, date);
|
||||
}
|
||||
|
||||
mgp_error mgp_date_add_duration(mgp_date *date, mgp_duration *dur, mgp_memory *memory, mgp_date **result) {
|
||||
@@ -1241,7 +1239,7 @@ mgp_error mgp_local_time_timestamp(mgp_local_time *local_time, int64_t *timestam
|
||||
}
|
||||
|
||||
mgp_error mgp_local_time_now(mgp_memory *memory, mgp_local_time **local_time) {
|
||||
return WrapExceptions([memory] { return NewRawMgpObject<mgp_local_time>(memory, utils::CurrentLocalTime()); },
|
||||
return WrapExceptions([memory] { return NewRawMgpObject<mgp_local_time>(memory, utils::UtcLocalTime()); },
|
||||
local_time);
|
||||
}
|
||||
|
||||
@@ -1334,9 +1332,8 @@ mgp_error mgp_local_date_time_timestamp(mgp_local_date_time *local_date_time, in
|
||||
}
|
||||
|
||||
mgp_error mgp_local_date_time_now(mgp_memory *memory, mgp_local_date_time **local_date_time) {
|
||||
return WrapExceptions(
|
||||
[memory] { return NewRawMgpObject<mgp_local_date_time>(memory, utils::CurrentLocalDateTime()); },
|
||||
local_date_time);
|
||||
return WrapExceptions([memory] { return NewRawMgpObject<mgp_local_date_time>(memory, utils::UtcLocalDateTime()); },
|
||||
local_date_time);
|
||||
}
|
||||
|
||||
mgp_error mgp_local_date_time_add_duration(mgp_local_date_time *local_date_time, mgp_duration *dur, mgp_memory *memory,
|
||||
@@ -1584,11 +1581,7 @@ mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_nam
|
||||
}
|
||||
}
|
||||
|
||||
auto &ctx = v->graph->ctx;
|
||||
|
||||
ctx->execution_stats[query::ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
|
||||
|
||||
auto *trigger_ctx_collector = ctx->trigger_context_collector;
|
||||
auto *trigger_ctx_collector = v->graph->ctx->trigger_context_collector;
|
||||
if (!trigger_ctx_collector || !trigger_ctx_collector->ShouldRegisterObjectPropertyChange<query::VertexAccessor>()) {
|
||||
return;
|
||||
}
|
||||
@@ -1624,12 +1617,8 @@ mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) {
|
||||
}
|
||||
}
|
||||
|
||||
auto &ctx = v->graph->ctx;
|
||||
|
||||
ctx->execution_stats[query::ExecutionStats::Key::CREATED_LABELS] += 1;
|
||||
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterSetVertexLabel(v->impl, label_id);
|
||||
if (v->graph->ctx->trigger_context_collector) {
|
||||
v->graph->ctx->trigger_context_collector->RegisterSetVertexLabel(v->impl, label_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1655,13 +1644,8 @@ mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, mgp_label label) {
|
||||
throw SerializationException{"Cannot serialize removing a label from a vertex."};
|
||||
}
|
||||
}
|
||||
|
||||
auto &ctx = v->graph->ctx;
|
||||
|
||||
ctx->execution_stats[query::ExecutionStats::Key::DELETED_LABELS] += 1;
|
||||
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterRemovedVertexLabel(v->impl, label_id);
|
||||
if (v->graph->ctx->trigger_context_collector) {
|
||||
v->graph->ctx->trigger_context_collector->RegisterRemovedVertexLabel(v->impl, label_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2008,10 +1992,6 @@ mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, m
|
||||
}
|
||||
}
|
||||
|
||||
auto &ctx = e->from.graph->ctx;
|
||||
|
||||
ctx->execution_stats[query::ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
|
||||
|
||||
auto *trigger_ctx_collector = e->from.graph->ctx->trigger_context_collector;
|
||||
if (!trigger_ctx_collector || !trigger_ctx_collector->ShouldRegisterObjectPropertyChange<query::EdgeAccessor>()) {
|
||||
return;
|
||||
@@ -2077,12 +2057,8 @@ mgp_error mgp_graph_create_vertex(struct mgp_graph *graph, mgp_memory *memory, m
|
||||
throw ImmutableObjectException{"Cannot create a vertex in an immutable graph!"};
|
||||
}
|
||||
auto vertex = graph->impl->InsertVertex();
|
||||
|
||||
auto &ctx = graph->ctx;
|
||||
ctx->execution_stats[query::ExecutionStats::Key::CREATED_NODES] += 1;
|
||||
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterCreatedObject(vertex);
|
||||
if (graph->ctx->trigger_context_collector) {
|
||||
graph->ctx->trigger_context_collector->RegisterCreatedObject(vertex);
|
||||
}
|
||||
return NewRawMgpObject<mgp_vertex>(memory, vertex, graph);
|
||||
},
|
||||
@@ -2109,17 +2085,8 @@ mgp_error mgp_graph_delete_vertex(struct mgp_graph *graph, mgp_vertex *vertex) {
|
||||
throw SerializationException{"Cannot serialize removing a vertex."};
|
||||
}
|
||||
}
|
||||
|
||||
if (!*result) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &ctx = graph->ctx;
|
||||
|
||||
ctx->execution_stats[query::ExecutionStats::Key::DELETED_NODES] += 1;
|
||||
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterDeletedObject(**result);
|
||||
if (graph->ctx->trigger_context_collector && *result) {
|
||||
graph->ctx->trigger_context_collector->RegisterDeletedObject(**result);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2144,20 +2111,10 @@ mgp_error mgp_graph_detach_delete_vertex(struct mgp_graph *graph, mgp_vertex *ve
|
||||
}
|
||||
}
|
||||
|
||||
if (!*result) {
|
||||
auto *trigger_ctx_collector = graph->ctx->trigger_context_collector;
|
||||
if (!trigger_ctx_collector || !*result) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &ctx = graph->ctx;
|
||||
|
||||
ctx->execution_stats[query::ExecutionStats::Key::DELETED_NODES] += 1;
|
||||
ctx->execution_stats[query::ExecutionStats::Key::DELETED_EDGES] += static_cast<int64_t>((*result)->second.size());
|
||||
|
||||
auto *trigger_ctx_collector = ctx->trigger_context_collector;
|
||||
if (!trigger_ctx_collector) {
|
||||
return;
|
||||
}
|
||||
|
||||
trigger_ctx_collector->RegisterDeletedObject((*result)->first);
|
||||
if (!trigger_ctx_collector->ShouldRegisterDeletedObject<query::EdgeAccessor>()) {
|
||||
return;
|
||||
@@ -2190,12 +2147,8 @@ mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *
|
||||
throw SerializationException{"Cannot serialize creating an edge."};
|
||||
}
|
||||
}
|
||||
auto &ctx = graph->ctx;
|
||||
|
||||
ctx->execution_stats[query::ExecutionStats::Key::CREATED_EDGES] += 1;
|
||||
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterCreatedObject(*edge);
|
||||
if (graph->ctx->trigger_context_collector) {
|
||||
graph->ctx->trigger_context_collector->RegisterCreatedObject(*edge);
|
||||
}
|
||||
return NewRawMgpObject<mgp_edge>(memory, edge.GetValue(), from->graph);
|
||||
},
|
||||
@@ -2221,15 +2174,8 @@ mgp_error mgp_graph_delete_edge(struct mgp_graph *graph, mgp_edge *edge) {
|
||||
throw SerializationException{"Cannot serialize removing an edge."};
|
||||
}
|
||||
}
|
||||
|
||||
if (!*result) {
|
||||
return;
|
||||
}
|
||||
auto &ctx = graph->ctx;
|
||||
|
||||
ctx->execution_stats[query::ExecutionStats::Key::DELETED_EDGES] += 1;
|
||||
if (ctx->trigger_context_collector) {
|
||||
ctx->trigger_context_collector->RegisterDeletedObject(**result);
|
||||
if (graph->ctx->trigger_context_collector && *result) {
|
||||
graph->ctx->trigger_context_collector->RegisterDeletedObject(**result);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2353,8 +2299,7 @@ mgp_error mgp_type_nullable(mgp_type *type, mgp_type **result) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
mgp_proc *mgp_module_add_procedure(mgp_module *module, const char *name, mgp_proc_cb cb,
|
||||
const ProcedureInfo &procedure_info) {
|
||||
mgp_proc *mgp_module_add_procedure(mgp_module *module, const char *name, mgp_proc_cb cb, bool is_write_procedure) {
|
||||
if (!IsValidIdentifierName(name)) {
|
||||
throw std::invalid_argument{fmt::format("Invalid procedure name: {}", name)};
|
||||
}
|
||||
@@ -2364,16 +2309,16 @@ mgp_proc *mgp_module_add_procedure(mgp_module *module, const char *name, mgp_pro
|
||||
|
||||
auto *memory = module->procedures.get_allocator().GetMemoryResource();
|
||||
// May throw std::bad_alloc, std::length_error
|
||||
return &module->procedures.emplace(name, mgp_proc(name, cb, memory, procedure_info)).first->second;
|
||||
return &module->procedures.emplace(name, mgp_proc(name, cb, memory, is_write_procedure)).first->second;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
mgp_error mgp_module_add_read_procedure(mgp_module *module, const char *name, mgp_proc_cb cb, mgp_proc **result) {
|
||||
return WrapExceptions([=] { return mgp_module_add_procedure(module, name, cb, {.is_write = false}); }, result);
|
||||
return WrapExceptions([=] { return mgp_module_add_procedure(module, name, cb, false); }, result);
|
||||
}
|
||||
|
||||
mgp_error mgp_module_add_write_procedure(mgp_module *module, const char *name, mgp_proc_cb cb, mgp_proc **result) {
|
||||
return WrapExceptions([=] { return mgp_module_add_procedure(module, name, cb, {.is_write = true}); }, result);
|
||||
return WrapExceptions([=] { return mgp_module_add_procedure(module, name, cb, true); }, result);
|
||||
}
|
||||
|
||||
mgp_error mgp_proc_add_arg(mgp_proc *proc, const char *name, mgp_type *type) {
|
||||
@@ -2547,55 +2492,21 @@ bool IsValidIdentifierName(const char *name) {
|
||||
|
||||
} // namespace query::procedure
|
||||
|
||||
namespace {
|
||||
using StreamSourceType = query::stream::StreamSourceType;
|
||||
|
||||
class InvalidMessageFunction : public std::invalid_argument {
|
||||
public:
|
||||
InvalidMessageFunction(const StreamSourceType type, const std::string_view function_name)
|
||||
: std::invalid_argument{fmt::format("'{}' is not defined for a message from a stream of type '{}'", function_name,
|
||||
StreamSourceTypeToString(type))} {}
|
||||
};
|
||||
|
||||
StreamSourceType MessageToStreamSourceType(const mgp_message::KafkaMessage & /*msg*/) {
|
||||
return StreamSourceType::KAFKA;
|
||||
}
|
||||
|
||||
StreamSourceType MessageToStreamSourceType(const mgp_message::PulsarMessage & /*msg*/) {
|
||||
return StreamSourceType::PULSAR;
|
||||
}
|
||||
|
||||
mgp_source_type StreamSourceTypeToMgpSourceType(const StreamSourceType type) {
|
||||
switch (type) {
|
||||
case StreamSourceType::KAFKA:
|
||||
return mgp_source_type::KAFKA;
|
||||
case StreamSourceType::PULSAR:
|
||||
return mgp_source_type::PULSAR;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
mgp_error mgp_message_source_type(mgp_message *message, mgp_source_type *result) {
|
||||
return WrapExceptions(
|
||||
[message] {
|
||||
return std::visit(utils::Overloaded{[](const auto &message) {
|
||||
return StreamSourceTypeToMgpSourceType(MessageToStreamSourceType(message));
|
||||
}},
|
||||
message->msg);
|
||||
},
|
||||
result);
|
||||
}
|
||||
|
||||
mgp_error mgp_message_payload(mgp_message *message, const char **result) {
|
||||
return WrapExceptions(
|
||||
[message] {
|
||||
return std::visit(utils::Overloaded{[](const mgp_message::KafkaMessage &msg) { return msg->Payload().data(); },
|
||||
[](const mgp_message::PulsarMessage &msg) { return msg.Payload().data(); },
|
||||
[](const auto &msg) -> const char * {
|
||||
throw InvalidMessageFunction(MessageToStreamSourceType(msg), "payload");
|
||||
}},
|
||||
message->msg);
|
||||
return std::visit(
|
||||
[]<typename T>(T &&msg) -> const char * {
|
||||
using MessageType = std::decay_t<T>;
|
||||
if constexpr (std::same_as<MessageType, mgp_message::KafkaMessage>) {
|
||||
return msg->Payload().data();
|
||||
} else if constexpr (std::same_as<MessageType, mgp_message::PulsarMessage>) {
|
||||
return msg.Payload().data();
|
||||
} else {
|
||||
throw std::invalid_argument("Invalid source type");
|
||||
}
|
||||
},
|
||||
message->msg);
|
||||
},
|
||||
result);
|
||||
}
|
||||
@@ -2603,13 +2514,18 @@ mgp_error mgp_message_payload(mgp_message *message, const char **result) {
|
||||
mgp_error mgp_message_payload_size(mgp_message *message, size_t *result) {
|
||||
return WrapExceptions(
|
||||
[message] {
|
||||
return std::visit(utils::Overloaded{[](const mgp_message::KafkaMessage &msg) { return msg->Payload().size(); },
|
||||
[](const mgp_message::PulsarMessage &msg) { return msg.Payload().size(); },
|
||||
[](const auto &msg) -> size_t {
|
||||
throw InvalidMessageFunction(MessageToStreamSourceType(msg),
|
||||
"payload_size");
|
||||
}},
|
||||
message->msg);
|
||||
return std::visit(
|
||||
[]<typename T>(T &&msg) -> size_t {
|
||||
using MessageType = std::decay_t<T>;
|
||||
if constexpr (std::same_as<MessageType, mgp_message::KafkaMessage>) {
|
||||
return msg->Payload().size();
|
||||
} else if constexpr (std::same_as<MessageType, mgp_message::PulsarMessage>) {
|
||||
return msg.Payload().size();
|
||||
} else {
|
||||
throw std::invalid_argument("Invalid source type");
|
||||
}
|
||||
},
|
||||
message->msg);
|
||||
},
|
||||
result);
|
||||
}
|
||||
@@ -2618,11 +2534,14 @@ mgp_error mgp_message_topic_name(mgp_message *message, const char **result) {
|
||||
return WrapExceptions(
|
||||
[message] {
|
||||
return std::visit(
|
||||
utils::Overloaded{[](const mgp_message::KafkaMessage &msg) { return msg->TopicName().data(); },
|
||||
[](const mgp_message::PulsarMessage &msg) { return msg.TopicName().data(); },
|
||||
[](const auto &msg) -> const char * {
|
||||
throw InvalidMessageFunction(MessageToStreamSourceType(msg), "topic_name");
|
||||
}},
|
||||
[]<typename T>(T &&msg) -> const char * {
|
||||
using MessageType = std::decay_t<T>;
|
||||
if constexpr (std::same_as<MessageType, mgp_message::KafkaMessage>) {
|
||||
return msg->TopicName().data();
|
||||
} else {
|
||||
throw std::invalid_argument("Invalid source type");
|
||||
}
|
||||
},
|
||||
message->msg);
|
||||
},
|
||||
result);
|
||||
@@ -2631,11 +2550,16 @@ mgp_error mgp_message_topic_name(mgp_message *message, const char **result) {
|
||||
mgp_error mgp_message_key(mgp_message *message, const char **result) {
|
||||
return WrapExceptions(
|
||||
[message] {
|
||||
return std::visit(utils::Overloaded{[](const mgp_message::KafkaMessage &msg) { return msg->Key().data(); },
|
||||
[](const auto &msg) -> const char * {
|
||||
throw InvalidMessageFunction(MessageToStreamSourceType(msg), "key");
|
||||
}},
|
||||
message->msg);
|
||||
return std::visit(
|
||||
[]<typename T>(T &&msg) -> const char * {
|
||||
using MessageType = std::decay_t<T>;
|
||||
if constexpr (std::same_as<MessageType, mgp_message::KafkaMessage>) {
|
||||
return msg->Key().data();
|
||||
} else {
|
||||
throw std::invalid_argument("Invalid source type");
|
||||
}
|
||||
},
|
||||
message->msg);
|
||||
},
|
||||
result);
|
||||
}
|
||||
@@ -2643,11 +2567,16 @@ mgp_error mgp_message_key(mgp_message *message, const char **result) {
|
||||
mgp_error mgp_message_key_size(mgp_message *message, size_t *result) {
|
||||
return WrapExceptions(
|
||||
[message] {
|
||||
return std::visit(utils::Overloaded{[](const mgp_message::KafkaMessage &msg) { return msg->Key().size(); },
|
||||
[](const auto &msg) -> size_t {
|
||||
throw InvalidMessageFunction(MessageToStreamSourceType(msg), "key_size");
|
||||
}},
|
||||
message->msg);
|
||||
return std::visit(
|
||||
[]<typename T>(T &&msg) -> size_t {
|
||||
using MessageType = std::decay_t<T>;
|
||||
if constexpr (std::same_as<MessageType, mgp_message::KafkaMessage>) {
|
||||
return msg->Key().size();
|
||||
} else {
|
||||
throw std::invalid_argument("Invalid source type");
|
||||
}
|
||||
},
|
||||
message->msg);
|
||||
},
|
||||
result);
|
||||
}
|
||||
@@ -2655,23 +2584,16 @@ mgp_error mgp_message_key_size(mgp_message *message, size_t *result) {
|
||||
mgp_error mgp_message_timestamp(mgp_message *message, int64_t *result) {
|
||||
return WrapExceptions(
|
||||
[message] {
|
||||
return std::visit(utils::Overloaded{[](const mgp_message::KafkaMessage &msg) { return msg->Timestamp(); },
|
||||
[](const auto &msg) -> int64_t {
|
||||
throw InvalidMessageFunction(MessageToStreamSourceType(msg), "timestamp");
|
||||
}},
|
||||
message->msg);
|
||||
},
|
||||
result);
|
||||
}
|
||||
|
||||
mgp_error mgp_message_offset(struct mgp_message *message, int64_t *result) {
|
||||
return WrapExceptions(
|
||||
[message] {
|
||||
return std::visit(utils::Overloaded{[](const mgp_message::KafkaMessage &msg) { return msg->Offset(); },
|
||||
[](const auto &msg) -> int64_t {
|
||||
throw InvalidMessageFunction(MessageToStreamSourceType(msg), "offset");
|
||||
}},
|
||||
message->msg);
|
||||
return std::visit(
|
||||
[]<typename T>(T &&msg) -> int64_t {
|
||||
using MessageType = std::decay_t<T>;
|
||||
if constexpr (std::same_as<MessageType, mgp_message::KafkaMessage>) {
|
||||
return msg->Timestamp();
|
||||
} else {
|
||||
throw std::invalid_argument("Invalid source type");
|
||||
}
|
||||
},
|
||||
message->msg);
|
||||
},
|
||||
result);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "integrations/pulsar/consumer.hpp"
|
||||
#include "query/context.hpp"
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/procedure/cypher_type_ptr.hpp"
|
||||
#include "query/typed_value.hpp"
|
||||
#include "storage/v2/view.hpp"
|
||||
@@ -654,29 +653,29 @@ struct mgp_type {
|
||||
query::procedure::CypherTypePtr impl;
|
||||
};
|
||||
|
||||
struct ProcedureInfo {
|
||||
bool is_write = false;
|
||||
std::optional<query::AuthQuery::Privilege> required_privilege = std::nullopt;
|
||||
};
|
||||
struct mgp_proc {
|
||||
using allocator_type = utils::Allocator<mgp_proc>;
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error
|
||||
mgp_proc(const char *name, mgp_proc_cb cb, utils::MemoryResource *memory, const ProcedureInfo &info = {})
|
||||
: name(name, memory), cb(cb), args(memory), opt_args(memory), results(memory), info(info) {}
|
||||
mgp_proc(const char *name, mgp_proc_cb cb, utils::MemoryResource *memory, bool is_write_procedure)
|
||||
: name(name, memory),
|
||||
cb(cb),
|
||||
args(memory),
|
||||
opt_args(memory),
|
||||
results(memory),
|
||||
is_write_procedure(is_write_procedure) {}
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error
|
||||
mgp_proc(const char *name, std::function<void(mgp_list *, mgp_graph *, mgp_result *, mgp_memory *)> cb,
|
||||
utils::MemoryResource *memory, const ProcedureInfo &info = {})
|
||||
: name(name, memory), cb(cb), args(memory), opt_args(memory), results(memory), info(info) {}
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error
|
||||
mgp_proc(const std::string_view name, std::function<void(mgp_list *, mgp_graph *, mgp_result *, mgp_memory *)> cb,
|
||||
utils::MemoryResource *memory, const ProcedureInfo &info = {})
|
||||
: name(name, memory), cb(cb), args(memory), opt_args(memory), results(memory), info(info) {}
|
||||
utils::MemoryResource *memory, bool is_write_procedure)
|
||||
: name(name, memory),
|
||||
cb(cb),
|
||||
args(memory),
|
||||
opt_args(memory),
|
||||
results(memory),
|
||||
is_write_procedure(is_write_procedure) {}
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error
|
||||
@@ -686,7 +685,7 @@ struct mgp_proc {
|
||||
args(other.args, memory),
|
||||
opt_args(other.opt_args, memory),
|
||||
results(other.results, memory),
|
||||
info(other.info) {}
|
||||
is_write_procedure(other.is_write_procedure) {}
|
||||
|
||||
mgp_proc(mgp_proc &&other, utils::MemoryResource *memory)
|
||||
: name(std::move(other.name), memory),
|
||||
@@ -694,7 +693,7 @@ struct mgp_proc {
|
||||
args(std::move(other.args), memory),
|
||||
opt_args(std::move(other.opt_args), memory),
|
||||
results(std::move(other.results), memory),
|
||||
info(other.info) {}
|
||||
is_write_procedure(other.is_write_procedure) {}
|
||||
|
||||
mgp_proc(const mgp_proc &other) = default;
|
||||
mgp_proc(mgp_proc &&other) = default;
|
||||
@@ -714,7 +713,7 @@ struct mgp_proc {
|
||||
utils::pmr::vector<std::tuple<utils::pmr::string, const query::procedure::CypherType *, query::TypedValue>> opt_args;
|
||||
/// Fields this procedure returns, as a (name -> (type, is_deprecated)) map.
|
||||
utils::pmr::map<utils::pmr::string, std::pair<const query::procedure::CypherType *, bool>> results;
|
||||
ProcedureInfo info;
|
||||
bool is_write_procedure{false};
|
||||
};
|
||||
|
||||
struct mgp_trans {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -10,23 +10,20 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "query/procedure/module.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include "utils/memory.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <dlfcn.h>
|
||||
}
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <unistd.h>
|
||||
#include <optional>
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "py/py.hpp"
|
||||
#include "query/procedure/mg_procedure_helpers.hpp"
|
||||
#include "query/procedure/py_module.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/pmr/vector.hpp"
|
||||
#include "utils/string.hpp"
|
||||
@@ -56,8 +53,6 @@ class BuiltinModule final : public Module {
|
||||
|
||||
void AddTransformation(std::string_view name, mgp_trans trans);
|
||||
|
||||
std::optional<std::filesystem::path> Path() const override { return std::nullopt; }
|
||||
|
||||
private:
|
||||
/// Registered procedures
|
||||
std::map<std::string, mgp_proc, std::less<>> procedures_;
|
||||
@@ -84,12 +79,6 @@ void BuiltinModule::AddTransformation(std::string_view name, mgp_trans trans) {
|
||||
|
||||
namespace {
|
||||
|
||||
auto WithUpgradedLock(auto *lock, const auto &function) {
|
||||
lock->unlock_shared();
|
||||
utils::OnScopeExit shared_lock{[&] { lock->lock_shared(); }};
|
||||
function();
|
||||
};
|
||||
|
||||
void RegisterMgLoad(ModuleRegistry *module_registry, utils::RWLock *lock, BuiltinModule *module) {
|
||||
// Loading relies on the fact that regular procedure invocation through
|
||||
// CallProcedureCursor::Pull takes ModuleRegistry::lock_ with READ access. To
|
||||
@@ -103,19 +92,31 @@ void RegisterMgLoad(ModuleRegistry *module_registry, utils::RWLock *lock, Builti
|
||||
// single thread may only take either a READ or a WRITE lock, it's not
|
||||
// possible for a thread to hold both. If a thread tries to do that, it will
|
||||
// deadlock immediately (no other thread needs to do anything).
|
||||
auto load_all_cb = [module_registry, lock](mgp_list * /*args*/, mgp_graph * /*graph*/, mgp_result * /*result*/,
|
||||
mgp_memory * /*memory*/) {
|
||||
WithUpgradedLock(lock, [&]() { module_registry->UnloadAndLoadModulesFromDirectories(); });
|
||||
auto with_unlock_shared = [lock](const auto &load_function) {
|
||||
lock->unlock_shared();
|
||||
try {
|
||||
load_function();
|
||||
// There's no finally in C++, but we have to return our original READ lock
|
||||
// state in any possible case.
|
||||
} catch (...) {
|
||||
lock->lock_shared();
|
||||
throw;
|
||||
}
|
||||
lock->lock_shared();
|
||||
};
|
||||
mgp_proc load_all("load_all", load_all_cb, utils::NewDeleteResource());
|
||||
auto load_all_cb = [module_registry, with_unlock_shared](mgp_list * /*args*/, mgp_graph * /*graph*/,
|
||||
mgp_result * /*result*/, mgp_memory * /*memory*/) {
|
||||
with_unlock_shared([&]() { module_registry->UnloadAndLoadModulesFromDirectories(); });
|
||||
};
|
||||
mgp_proc load_all("load_all", load_all_cb, utils::NewDeleteResource(), false);
|
||||
module->AddProcedure("load_all", std::move(load_all));
|
||||
auto load_cb = [module_registry, lock](mgp_list *args, mgp_graph * /*graph*/, mgp_result *result,
|
||||
mgp_memory * /*memory*/) {
|
||||
auto load_cb = [module_registry, with_unlock_shared](mgp_list *args, mgp_graph * /*graph*/, mgp_result *result,
|
||||
mgp_memory * /*memory*/) {
|
||||
MG_ASSERT(Call<size_t>(mgp_list_size, args) == 1U, "Should have been type checked already");
|
||||
auto *arg = Call<mgp_value *>(mgp_list_at, args, 0);
|
||||
MG_ASSERT(CallBool(mgp_value_is_string, arg), "Should have been type checked already");
|
||||
bool succ = false;
|
||||
WithUpgradedLock(lock, [&]() {
|
||||
with_unlock_shared([&]() {
|
||||
const char *arg_as_string{nullptr};
|
||||
if (const auto err = mgp_value_get_string(arg, &arg_as_string); err != MGP_ERROR_NO_ERROR) {
|
||||
succ = false;
|
||||
@@ -127,25 +128,11 @@ void RegisterMgLoad(ModuleRegistry *module_registry, utils::RWLock *lock, Builti
|
||||
MG_ASSERT(mgp_result_set_error_msg(result, "Failed to (re)load the module.") == MGP_ERROR_NO_ERROR);
|
||||
}
|
||||
};
|
||||
mgp_proc load("load", load_cb, utils::NewDeleteResource());
|
||||
mgp_proc load("load", load_cb, utils::NewDeleteResource(), false);
|
||||
MG_ASSERT(mgp_proc_add_arg(&load, "module_name", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
module->AddProcedure("load", std::move(load));
|
||||
}
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] bool IsFileEditable(const std::optional<std::filesystem::path> &path) {
|
||||
return path && access(path->c_str(), W_OK) == 0;
|
||||
}
|
||||
|
||||
std::string GetPathString(const std::optional<std::filesystem::path> &path) {
|
||||
if (!path) {
|
||||
return "builtin";
|
||||
}
|
||||
|
||||
return std::filesystem::canonical(*path).generic_string();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void RegisterMgProcedures(
|
||||
// We expect modules to be sorted by name.
|
||||
const std::map<std::string, std::unique_ptr<Module>, std::less<>> *all_modules, BuiltinModule *module) {
|
||||
@@ -160,82 +147,66 @@ void RegisterMgProcedures(
|
||||
static_assert(
|
||||
std::is_same_v<decltype(module->Procedures()), const std::map<std::string, mgp_proc, std::less<>> *>,
|
||||
"Expected module procedures to be sorted by name");
|
||||
|
||||
const auto path = module->Path();
|
||||
const auto path_string = GetPathString(path);
|
||||
const auto is_editable = IsFileEditable(path);
|
||||
|
||||
for (const auto &[proc_name, proc] : *module->Procedures()) {
|
||||
mgp_result_record *record{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_result_new_record(result, &record); }, result)) {
|
||||
if (const auto err = mgp_result_new_record(result, &record); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto path_value = GetStringValueOrSetError(path_string.c_str(), memory, result);
|
||||
if (!path_value) {
|
||||
return;
|
||||
}
|
||||
|
||||
MgpUniquePtr<mgp_value> is_editable_value{nullptr, mgp_value_destroy};
|
||||
if (!TryOrSetError([&] { return CreateMgpObject(is_editable_value, mgp_value_make_bool, is_editable, memory); },
|
||||
result)) {
|
||||
} else if (err != MGP_ERROR_NO_ERROR) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
|
||||
return;
|
||||
}
|
||||
|
||||
utils::pmr::string full_name(module_name, memory->impl);
|
||||
full_name.append(1, '.');
|
||||
full_name.append(proc_name);
|
||||
const auto name_value = GetStringValueOrSetError(full_name.c_str(), memory, result);
|
||||
if (!name_value) {
|
||||
MgpUniquePtr<mgp_value> name_value{nullptr, mgp_value_destroy};
|
||||
if (const auto err = CreateMgpObject(name_value, mgp_value_make_string, full_name.c_str(), memory);
|
||||
err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
|
||||
return;
|
||||
} else if (err != MGP_ERROR_NO_ERROR) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << module_name << ".";
|
||||
PrintProcSignature(proc, &ss);
|
||||
const auto signature = ss.str();
|
||||
const auto signature_value = GetStringValueOrSetError(signature.c_str(), memory, result);
|
||||
if (!signature_value) {
|
||||
MgpUniquePtr<mgp_value> signature_value{nullptr, mgp_value_destroy};
|
||||
if (const auto err = CreateMgpObject(signature_value, mgp_value_make_string, signature.c_str(), memory);
|
||||
err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
|
||||
return;
|
||||
} else if (err != MGP_ERROR_NO_ERROR) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
|
||||
return;
|
||||
}
|
||||
|
||||
MgpUniquePtr<mgp_value> is_write_value{nullptr, mgp_value_destroy};
|
||||
if (!TryOrSetError(
|
||||
[&, &proc = proc] {
|
||||
return CreateMgpObject(is_write_value, mgp_value_make_bool, proc.info.is_write ? 1 : 0, memory);
|
||||
},
|
||||
result)) {
|
||||
|
||||
if (const auto err =
|
||||
CreateMgpObject(is_write_value, mgp_value_make_bool, proc.is_write_procedure ? 1 : 0, memory);
|
||||
err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
|
||||
return;
|
||||
} else if (err != MGP_ERROR_NO_ERROR) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "name", name_value.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "signature", signature_value.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "is_write", is_write_value.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "path", path_value.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "is_editable", is_editable_value.get())) {
|
||||
const auto err1 = mgp_result_record_insert(record, "name", name_value.get());
|
||||
const auto err2 = mgp_result_record_insert(record, "signature", signature_value.get());
|
||||
const auto err3 = mgp_result_record_insert(record, "is_write", is_write_value.get());
|
||||
if (err1 != MGP_ERROR_NO_ERROR || err2 != MGP_ERROR_NO_ERROR || err3 != MGP_ERROR_NO_ERROR) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Unable to set the result!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
mgp_proc procedures("procedures", procedures_cb, utils::NewDeleteResource());
|
||||
mgp_proc procedures("procedures", procedures_cb, utils::NewDeleteResource(), false);
|
||||
MG_ASSERT(mgp_proc_add_result(&procedures, "name", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_result(&procedures, "signature", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_result(&procedures, "is_write", Call<mgp_type *>(mgp_type_bool)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_result(&procedures, "path", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_result(&procedures, "is_editable", Call<mgp_type *>(mgp_type_bool)) == MGP_ERROR_NO_ERROR);
|
||||
module->AddProcedure("procedures", std::move(procedures));
|
||||
}
|
||||
|
||||
@@ -248,25 +219,13 @@ void RegisterMgTransformations(const std::map<std::string, std::unique_ptr<Modul
|
||||
static_assert(
|
||||
std::is_same_v<decltype(module->Transformations()), const std::map<std::string, mgp_trans, std::less<>> *>,
|
||||
"Expected module transformations to be sorted by name");
|
||||
|
||||
const auto path = module->Path();
|
||||
const auto path_string = GetPathString(path);
|
||||
const auto is_editable = IsFileEditable(path);
|
||||
|
||||
for (const auto &[trans_name, proc] : *module->Transformations()) {
|
||||
mgp_result_record *record{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_result_new_record(result, &record); }, result)) {
|
||||
if (const auto err = mgp_result_new_record(result, &record); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto path_value = GetStringValueOrSetError(path_string.c_str(), memory, result);
|
||||
if (!path_value) {
|
||||
return;
|
||||
}
|
||||
|
||||
MgpUniquePtr<mgp_value> is_editable_value{nullptr, mgp_value_destroy};
|
||||
if (!TryOrSetError([&] { return CreateMgpObject(is_editable_value, mgp_value_make_bool, is_editable, memory); },
|
||||
result)) {
|
||||
} else if (err != MGP_ERROR_NO_ERROR) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -274,377 +233,28 @@ void RegisterMgTransformations(const std::map<std::string, std::unique_ptr<Modul
|
||||
full_name.append(1, '.');
|
||||
full_name.append(trans_name);
|
||||
|
||||
const auto name_value = GetStringValueOrSetError(full_name.c_str(), memory, result);
|
||||
if (!name_value) {
|
||||
MgpUniquePtr<mgp_value> name_value{nullptr, mgp_value_destroy};
|
||||
if (const auto err = CreateMgpObject(name_value, mgp_value_make_string, full_name.c_str(), memory);
|
||||
err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
|
||||
return;
|
||||
} else if (err != MGP_ERROR_NO_ERROR) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "name", name_value.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "path", path_value.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "is_editable", is_editable_value.get())) {
|
||||
if (const auto err = mgp_result_record_insert(record, "name", name_value.get()); err != MGP_ERROR_NO_ERROR) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Unable to set the result!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
mgp_proc procedures("transformations", transformations_cb, utils::NewDeleteResource());
|
||||
mgp_proc procedures("transformations", transformations_cb, utils::NewDeleteResource(), false);
|
||||
MG_ASSERT(mgp_proc_add_result(&procedures, "name", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_result(&procedures, "path", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_result(&procedures, "is_editable", Call<mgp_type *>(mgp_type_bool)) == MGP_ERROR_NO_ERROR);
|
||||
module->AddProcedure("transformations", std::move(procedures));
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool IsAllowedExtension(const auto &extension) {
|
||||
constexpr std::array<std::string_view, 1> allowed_extensions{".py"};
|
||||
return std::any_of(allowed_extensions.begin(), allowed_extensions.end(),
|
||||
[&](const auto allowed_extension) { return allowed_extension == extension; });
|
||||
}
|
||||
|
||||
bool IsSubPath(const auto &base, const auto &destination) {
|
||||
const auto relative = std::filesystem::relative(destination, base);
|
||||
return !relative.empty() && *relative.begin() != "..";
|
||||
}
|
||||
|
||||
std::optional<std::string> ReadFile(const auto &path) {
|
||||
std::ifstream file(path);
|
||||
if (!file.is_open()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto size = std::filesystem::file_size(path);
|
||||
std::string content(size, '\0');
|
||||
file.read(content.data(), static_cast<std::streamsize>(size));
|
||||
return std::move(content);
|
||||
}
|
||||
|
||||
// Return the module directory that contains the `path`
|
||||
utils::BasicResult<const char *, std::filesystem::path> ParentModuleDirectory(const ModuleRegistry &module_registry,
|
||||
const std::filesystem::path &path) {
|
||||
const auto &module_directories = module_registry.GetModulesDirectory();
|
||||
|
||||
auto longest_parent_directory = module_directories.end();
|
||||
auto max_length = std::numeric_limits<uint64_t>::min();
|
||||
for (auto it = module_directories.begin(); it != module_directories.end(); ++it) {
|
||||
if (IsSubPath(*it, path)) {
|
||||
const auto length = std::filesystem::canonical(*it).string().size();
|
||||
if (length > max_length) {
|
||||
longest_parent_directory = it;
|
||||
max_length = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (longest_parent_directory == module_directories.end()) {
|
||||
return "The specified file isn't contained in any of the module directories.";
|
||||
}
|
||||
|
||||
return *longest_parent_directory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void RegisterMgGetModuleFiles(ModuleRegistry *module_registry, BuiltinModule *module) {
|
||||
auto get_module_files_cb = [module_registry](mgp_list * /*args*/, mgp_graph * /*unused*/, mgp_result *result,
|
||||
mgp_memory *memory) {
|
||||
for (const auto &module_directory : module_registry->GetModulesDirectory()) {
|
||||
for (const auto &dir_entry : std::filesystem::recursive_directory_iterator(module_directory)) {
|
||||
if (dir_entry.is_regular_file() && IsAllowedExtension(dir_entry.path().extension())) {
|
||||
mgp_result_record *record{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_result_new_record(result, &record); }, result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto path_string = GetPathString(dir_entry);
|
||||
const auto is_editable = IsFileEditable(dir_entry);
|
||||
|
||||
const auto path_value = GetStringValueOrSetError(path_string.c_str(), memory, result);
|
||||
if (!path_value) {
|
||||
return;
|
||||
}
|
||||
|
||||
MgpUniquePtr<mgp_value> is_editable_value{nullptr, mgp_value_destroy};
|
||||
if (!TryOrSetError(
|
||||
[&] { return CreateMgpObject(is_editable_value, mgp_value_make_bool, is_editable, memory); },
|
||||
result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "path", path_value.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "is_editable", is_editable_value.get())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mgp_proc get_module_files("get_module_files", get_module_files_cb, utils::NewDeleteResource(),
|
||||
{.required_privilege = AuthQuery::Privilege::MODULE_READ});
|
||||
MG_ASSERT(mgp_proc_add_result(&get_module_files, "path", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_result(&get_module_files, "is_editable", Call<mgp_type *>(mgp_type_bool)) ==
|
||||
MGP_ERROR_NO_ERROR);
|
||||
module->AddProcedure("get_module_files", std::move(get_module_files));
|
||||
}
|
||||
|
||||
void RegisterMgGetModuleFile(ModuleRegistry *module_registry, BuiltinModule *module) {
|
||||
auto get_module_file_cb = [module_registry](mgp_list *args, mgp_graph * /*unused*/, mgp_result *result,
|
||||
mgp_memory *memory) {
|
||||
MG_ASSERT(Call<size_t>(mgp_list_size, args) == 1U, "Should have been type checked already");
|
||||
auto *arg = Call<mgp_value *>(mgp_list_at, args, 0);
|
||||
MG_ASSERT(CallBool(mgp_value_is_string, arg), "Should have been type checked already");
|
||||
const char *path_str{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_value_get_string(arg, &path_str); }, result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path path{path_str};
|
||||
|
||||
if (!path.is_absolute()) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The path should be an absolute path."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsAllowedExtension(path.extension())) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The specified file isn't in the supported format."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(path)) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The specified file doesn't exist."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto maybe_error_msg = ParentModuleDirectory(*module_registry, path); maybe_error_msg.HasError()) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, maybe_error_msg.GetError()));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto maybe_content = ReadFile(path);
|
||||
if (!maybe_content) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "Couldn't read the content of the file."));
|
||||
return;
|
||||
}
|
||||
|
||||
mgp_result_record *record{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_result_new_record(result, &record); }, result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto content_value = GetStringValueOrSetError(maybe_content->c_str(), memory, result);
|
||||
if (!content_value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "content", content_value.get())) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
mgp_proc get_module_file("get_module_file", std::move(get_module_file_cb), utils::NewDeleteResource(),
|
||||
{.required_privilege = AuthQuery::Privilege::MODULE_READ});
|
||||
MG_ASSERT(mgp_proc_add_arg(&get_module_file, "path", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_result(&get_module_file, "content", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
module->AddProcedure("get_module_file", std::move(get_module_file));
|
||||
}
|
||||
|
||||
namespace {
|
||||
utils::BasicResult<std::string> WriteToFile(const std::filesystem::path &file, const std::string_view content) {
|
||||
std::ofstream output_file{file};
|
||||
if (!output_file.is_open()) {
|
||||
return fmt::format("Failed to open the file at location {}", file);
|
||||
}
|
||||
output_file.write(content.data(), static_cast<std::streamsize>(content.size()));
|
||||
output_file.flush();
|
||||
return {};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void RegisterMgCreateModuleFile(ModuleRegistry *module_registry, utils::RWLock *lock, BuiltinModule *module) {
|
||||
auto create_module_file_cb = [module_registry, lock](mgp_list *args, mgp_graph * /*unused*/, mgp_result *result,
|
||||
mgp_memory *memory) {
|
||||
MG_ASSERT(Call<size_t>(mgp_list_size, args) == 2U, "Should have been type checked already");
|
||||
auto *filename_arg = Call<mgp_value *>(mgp_list_at, args, 0);
|
||||
MG_ASSERT(CallBool(mgp_value_is_string, filename_arg), "Should have been type checked already");
|
||||
const char *filename_str{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_value_get_string(filename_arg, &filename_str); }, result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file_path = module_registry->InternalModuleDir() / filename_str;
|
||||
|
||||
if (!IsSubPath(module_registry->InternalModuleDir(), file_path)) {
|
||||
static_cast<void>(mgp_result_set_error_msg(
|
||||
result,
|
||||
"Invalid relative path defined. The module file cannot be define outside the internal modules directory."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsAllowedExtension(file_path.extension())) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The specified file isn't in the supported format."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::filesystem::exists(file_path)) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "File with the same name already exists!"));
|
||||
return;
|
||||
}
|
||||
|
||||
utils::EnsureDir(file_path.parent_path());
|
||||
|
||||
auto *content_arg = Call<mgp_value *>(mgp_list_at, args, 1);
|
||||
MG_ASSERT(CallBool(mgp_value_is_string, content_arg), "Should have been type checked already");
|
||||
const char *content_str{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_value_get_string(content_arg, &content_str); }, result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto maybe_error = WriteToFile(file_path, {content_str, std::strlen(content_str)}); maybe_error.HasError()) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, maybe_error.GetError().c_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
mgp_result_record *record{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_result_new_record(result, &record); }, result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto path_value = GetStringValueOrSetError(std::filesystem::canonical(file_path).c_str(), memory, result);
|
||||
if (!path_value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertResultOrSetError(result, record, "path", path_value.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
WithUpgradedLock(lock, [&]() { module_registry->UnloadAndLoadModulesFromDirectories(); });
|
||||
};
|
||||
mgp_proc create_module_file("create_module_file", std::move(create_module_file_cb), utils::NewDeleteResource(),
|
||||
{.required_privilege = AuthQuery::Privilege::MODULE_WRITE});
|
||||
MG_ASSERT(mgp_proc_add_arg(&create_module_file, "filename", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_arg(&create_module_file, "content", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_result(&create_module_file, "path", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
module->AddProcedure("create_module_file", std::move(create_module_file));
|
||||
}
|
||||
|
||||
void RegisterMgUpdateModuleFile(ModuleRegistry *module_registry, utils::RWLock *lock, BuiltinModule *module) {
|
||||
auto update_module_file_cb = [module_registry, lock](mgp_list *args, mgp_graph * /*unused*/, mgp_result *result,
|
||||
mgp_memory * /*memory*/) {
|
||||
MG_ASSERT(Call<size_t>(mgp_list_size, args) == 2U, "Should have been type checked already");
|
||||
auto *path_arg = Call<mgp_value *>(mgp_list_at, args, 0);
|
||||
MG_ASSERT(CallBool(mgp_value_is_string, path_arg), "Should have been type checked already");
|
||||
const char *path_str{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_value_get_string(path_arg, &path_str); }, result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path path{path_str};
|
||||
|
||||
if (!path.is_absolute()) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The path should be an absolute path."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsAllowedExtension(path.extension())) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The specified file isn't in the supported format."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(path)) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The specified file doesn't exist."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto maybe_error_msg = ParentModuleDirectory(*module_registry, path); maybe_error_msg.HasError()) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, maybe_error_msg.GetError()));
|
||||
return;
|
||||
}
|
||||
|
||||
auto *content_arg = Call<mgp_value *>(mgp_list_at, args, 1);
|
||||
MG_ASSERT(CallBool(mgp_value_is_string, content_arg), "Should have been type checked already");
|
||||
const char *content_str{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_value_get_string(content_arg, &content_str); }, result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto maybe_error = WriteToFile(path, {content_str, std::strlen(content_str)}); maybe_error.HasError()) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, maybe_error.GetError().c_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
WithUpgradedLock(lock, [&]() { module_registry->UnloadAndLoadModulesFromDirectories(); });
|
||||
};
|
||||
mgp_proc update_module_file("update_module_file", std::move(update_module_file_cb), utils::NewDeleteResource(),
|
||||
{.required_privilege = AuthQuery::Privilege::MODULE_WRITE});
|
||||
MG_ASSERT(mgp_proc_add_arg(&update_module_file, "path", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
MG_ASSERT(mgp_proc_add_arg(&update_module_file, "content", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
module->AddProcedure("update_module_file", std::move(update_module_file));
|
||||
}
|
||||
|
||||
void RegisterMgDeleteModuleFile(ModuleRegistry *module_registry, utils::RWLock *lock, BuiltinModule *module) {
|
||||
auto delete_module_file_cb = [module_registry, lock](mgp_list *args, mgp_graph * /*unused*/, mgp_result *result,
|
||||
mgp_memory * /*memory*/) {
|
||||
MG_ASSERT(Call<size_t>(mgp_list_size, args) == 1U, "Should have been type checked already");
|
||||
auto *path_arg = Call<mgp_value *>(mgp_list_at, args, 0);
|
||||
MG_ASSERT(CallBool(mgp_value_is_string, path_arg), "Should have been type checked already");
|
||||
const char *path_str{nullptr};
|
||||
if (!TryOrSetError([&] { return mgp_value_get_string(path_arg, &path_str); }, result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path path{path_str};
|
||||
|
||||
if (!path.is_absolute()) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The path should be an absolute path."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsAllowedExtension(path.extension())) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The specified file isn't in the supported format."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(path)) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, "The specified file doesn't exist."));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto parent_module_directory = ParentModuleDirectory(*module_registry, path);
|
||||
if (parent_module_directory.HasError()) {
|
||||
static_cast<void>(mgp_result_set_error_msg(result, parent_module_directory.GetError()));
|
||||
return;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::remove(path, ec)) {
|
||||
static_cast<void>(
|
||||
mgp_result_set_error_msg(result, fmt::format("Failed to delete the module: {}", ec.message()).c_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
auto parent_path = path.parent_path();
|
||||
while (!std::filesystem::is_symlink(parent_path) && std::filesystem::is_empty(parent_path) &&
|
||||
!std::filesystem::equivalent(*parent_module_directory, parent_path)) {
|
||||
std::filesystem::remove(parent_path);
|
||||
parent_path = parent_path.parent_path();
|
||||
}
|
||||
|
||||
WithUpgradedLock(lock, [&]() { module_registry->UnloadAndLoadModulesFromDirectories(); });
|
||||
};
|
||||
mgp_proc delete_module_file("delete_module_file", std::move(delete_module_file_cb), utils::NewDeleteResource(),
|
||||
{.required_privilege = AuthQuery::Privilege::MODULE_WRITE});
|
||||
MG_ASSERT(mgp_proc_add_arg(&delete_module_file, "path", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
|
||||
module->AddProcedure("delete_module_file", std::move(delete_module_file));
|
||||
}
|
||||
|
||||
// Run `fun` with `mgp_module *` and `mgp_memory *` arguments. If `fun` returned
|
||||
// a `true` value, store the `mgp_module::procedures` and
|
||||
// `mgp_module::transformations into `proc_map`. The return value of WithModuleRegistration
|
||||
@@ -687,8 +297,6 @@ class SharedLibraryModule final : public Module {
|
||||
|
||||
const std::map<std::string, mgp_trans, std::less<>> *Transformations() const override;
|
||||
|
||||
std::optional<std::filesystem::path> Path() const override { return file_path_; }
|
||||
|
||||
private:
|
||||
/// Path as requested for loading the module from a library.
|
||||
std::filesystem::path file_path_;
|
||||
@@ -816,7 +424,6 @@ class PythonModule final : public Module {
|
||||
|
||||
const std::map<std::string, mgp_proc, std::less<>> *Procedures() const override;
|
||||
const std::map<std::string, mgp_trans, std::less<>> *Transformations() const override;
|
||||
std::optional<std::filesystem::path> Path() const override { return file_path_; }
|
||||
|
||||
private:
|
||||
std::filesystem::path file_path_;
|
||||
@@ -885,9 +492,6 @@ bool PythonModule::Close() {
|
||||
py_module_ = py::Object(nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove the cached bytecode if it's present
|
||||
std::filesystem::remove_all(file_path_.parent_path() / "__pycache__");
|
||||
py_module_ = py::Object(nullptr);
|
||||
spdlog::info("Closed module {}", file_path_);
|
||||
return true;
|
||||
@@ -955,24 +559,13 @@ ModuleRegistry::ModuleRegistry() {
|
||||
RegisterMgProcedures(&modules_, module.get());
|
||||
RegisterMgTransformations(&modules_, module.get());
|
||||
RegisterMgLoad(this, &lock_, module.get());
|
||||
RegisterMgGetModuleFiles(this, module.get());
|
||||
RegisterMgGetModuleFile(this, module.get());
|
||||
RegisterMgCreateModuleFile(this, &lock_, module.get());
|
||||
RegisterMgUpdateModuleFile(this, &lock_, module.get());
|
||||
RegisterMgDeleteModuleFile(this, &lock_, module.get());
|
||||
modules_.emplace("mg", std::move(module));
|
||||
}
|
||||
|
||||
void ModuleRegistry::SetModulesDirectory(std::vector<std::filesystem::path> modules_dirs,
|
||||
const std::filesystem::path &data_directory) {
|
||||
internal_module_dir_ = data_directory / "internal_modules";
|
||||
utils::EnsureDirOrDie(internal_module_dir_);
|
||||
void ModuleRegistry::SetModulesDirectory(std::vector<std::filesystem::path> modules_dirs) {
|
||||
modules_dirs_ = std::move(modules_dirs);
|
||||
modules_dirs_.push_back(internal_module_dir_);
|
||||
}
|
||||
|
||||
const std::vector<std::filesystem::path> &ModuleRegistry::GetModulesDirectory() const { return modules_dirs_; }
|
||||
|
||||
bool ModuleRegistry::LoadModuleIfFound(const std::filesystem::path &modules_dir, const std::string_view name) {
|
||||
if (!utils::DirExists(modules_dir)) {
|
||||
spdlog::error(
|
||||
@@ -1051,18 +644,6 @@ void ModuleRegistry::UnloadAllModules() {
|
||||
|
||||
utils::MemoryResource &ModuleRegistry::GetSharedMemoryResource() noexcept { return *shared_; }
|
||||
|
||||
bool ModuleRegistry::RegisterMgProcedure(const std::string_view name, mgp_proc proc) {
|
||||
std::unique_lock<utils::RWLock> guard(lock_);
|
||||
if (auto module = modules_.find("mg"); module != modules_.end()) {
|
||||
auto *builtin_module = dynamic_cast<BuiltinModule *>(module->second.get());
|
||||
builtin_module->AddProcedure(name, std::move(proc));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path &ModuleRegistry::InternalModuleDir() const noexcept { return internal_module_dir_; }
|
||||
|
||||
namespace {
|
||||
|
||||
/// This function returns a pair of either
|
||||
@@ -1086,9 +667,9 @@ template <typename T>
|
||||
concept ModuleProperties = utils::SameAsAnyOf<T, mgp_proc, mgp_trans>;
|
||||
|
||||
template <ModuleProperties T>
|
||||
std::optional<std::pair<ModulePtr, const T *>> MakePairIfPropFound(const ModuleRegistry &module_registry,
|
||||
std::string_view fully_qualified_name,
|
||||
utils::MemoryResource *memory) {
|
||||
std::optional<std::pair<procedure::ModulePtr, const T *>> MakePairIfPropFound(const ModuleRegistry &module_registry,
|
||||
std::string_view fully_qualified_name,
|
||||
utils::MemoryResource *memory) {
|
||||
auto prop_fun = [](auto &module) {
|
||||
if constexpr (std::is_same_v<T, mgp_proc>) {
|
||||
return module->Procedures();
|
||||
@@ -1109,13 +690,13 @@ std::optional<std::pair<ModulePtr, const T *>> MakePairIfPropFound(const ModuleR
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<std::pair<ModulePtr, const mgp_proc *>> FindProcedure(const ModuleRegistry &module_registry,
|
||||
std::string_view fully_qualified_procedure_name,
|
||||
utils::MemoryResource *memory) {
|
||||
std::optional<std::pair<procedure::ModulePtr, const mgp_proc *>> FindProcedure(
|
||||
const ModuleRegistry &module_registry, std::string_view fully_qualified_procedure_name,
|
||||
utils::MemoryResource *memory) {
|
||||
return MakePairIfPropFound<mgp_proc>(module_registry, fully_qualified_procedure_name, memory);
|
||||
}
|
||||
|
||||
std::optional<std::pair<ModulePtr, const mgp_trans *>> FindTransformation(
|
||||
std::optional<std::pair<procedure::ModulePtr, const mgp_trans *>> FindTransformation(
|
||||
const ModuleRegistry &module_registry, std::string_view fully_qualified_transformation_name,
|
||||
utils::MemoryResource *memory) {
|
||||
return MakePairIfPropFound<mgp_trans>(module_registry, fully_qualified_transformation_name, memory);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2021 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
|
||||
@@ -45,8 +45,6 @@ class Module {
|
||||
virtual const std::map<std::string, mgp_proc, std::less<>> *Procedures() const = 0;
|
||||
/// Returns registered transformations of this module
|
||||
virtual const std::map<std::string, mgp_trans, std::less<>> *Transformations() const = 0;
|
||||
|
||||
virtual std::optional<std::filesystem::path> Path() const = 0;
|
||||
};
|
||||
|
||||
/// Proxy for a registered Module, acquires a read lock from ModuleRegistry.
|
||||
@@ -87,8 +85,7 @@ class ModuleRegistry final {
|
||||
ModuleRegistry();
|
||||
|
||||
/// Set the modules directories that will be used when (re)loading modules.
|
||||
void SetModulesDirectory(std::vector<std::filesystem::path> modules_dir, const std::filesystem::path &data_directory);
|
||||
const std::vector<std::filesystem::path> &GetModulesDirectory() const;
|
||||
void SetModulesDirectory(std::vector<std::filesystem::path> modules_dir);
|
||||
|
||||
/// Atomically load or reload a module with a particular name from the given
|
||||
/// directory.
|
||||
@@ -120,13 +117,8 @@ class ModuleRegistry final {
|
||||
/// Returns the shared memory allocator used by modules
|
||||
utils::MemoryResource &GetSharedMemoryResource() noexcept;
|
||||
|
||||
bool RegisterMgProcedure(std::string_view name, mgp_proc proc);
|
||||
|
||||
const std::filesystem::path &InternalModuleDir() const noexcept;
|
||||
|
||||
private:
|
||||
std::vector<std::filesystem::path> modules_dirs_;
|
||||
std::filesystem::path internal_module_dir_;
|
||||
};
|
||||
|
||||
/// Single, global module registry.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user