Compare commits

..

35 Commits

Author SHA1 Message Date
Marko Budiselić
3cef394b8c Merge branch 'master' into T635-add-vertex-degree-to-query-planner 2023-06-27 17:59:26 +02:00
Josip Mrden
409788067e Merge branch 'T635-add-vertex-degree-to-query-planner' of github.com:memgraph/memgraph into T635-add-vertex-degree-to-query-planner 2023-06-27 17:14:28 +02:00
Josip Mrden
00818fb718 Address PR comments 2023-06-27 17:11:52 +02:00
Josipmrden
c39c90bbbe Merge branch 'master' into T635-add-vertex-degree-to-query-planner 2023-06-27 15:04:36 +02:00
Josip Mrden
8f4767a9e6 Merge branch 'master' into T635-add-vertex-degree-to-query-planner 2023-06-27 13:31:56 +02:00
Josip Mrden
931cc35817 Address PR comments 2023-06-27 13:31:30 +02:00
Josip Mrden
1fdf0b9bec Add empty graph test on analyze 2023-06-26 13:02:45 +02:00
Josip Mrden
2f4f2896e2 Merge branch 'master' into T635-add-vertex-degree-to-query-planner 2023-06-26 12:30:22 +02:00
Josip Mrden
da84498428 Merge branch 'master' into T635-add-vertex-degree-to-query-planner 2023-06-26 12:26:25 +02:00
Josip Mrden
7042387976 Add test for subqueries and unions 2023-06-26 11:38:40 +02:00
Josip Mrden
edd902cd18 Fix cost estimator test 2023-06-23 14:47:41 +02:00
Josip Mrden
80a0d12e08 Add template to cost estimator 2023-06-23 13:33:18 +02:00
Josip Mrden
2f335f2507 Revert configuration to old one 2023-06-23 13:22:04 +02:00
Josip Mrden
5efe9857d4 Merge branch 'master' into T635-add-vertex-degree-to-query-planner 2023-06-23 13:19:47 +02:00
Josip Mrden
be936e46ae Add symbol table in front of some arguments 2023-06-23 13:19:22 +02:00
Josip Mrden
2e0884661c Solve clang tidy issues 2023-06-23 12:54:13 +02:00
Josip Mrden
6574c220b7 Merge branch 'master' into T635-add-vertex-degree-to-query-planner 2023-06-23 12:25:25 +02:00
Josip Mrden
79d91b68e2 Add e2e test for subqueries 2023-06-21 17:27:38 +02:00
Josip Mrden
7b7c5b3fc0 Add cost estimator to subqueries 2023-06-21 16:35:59 +02:00
Josip Mrden
fe91f44234 Introduce scopes in cost estimator 2023-06-21 15:38:43 +02:00
Josip Mrden
a772a5414b Checkout index lookup as optional match can not be reproduced 2023-06-21 13:32:07 +02:00
Josip Mrden
42bd5637dc Added unit test for merge expansion 2023-06-21 13:04:18 +02:00
Josip Mrden
c6be4508d5 Merge branch 'master' into T635-add-vertex-degree-to-query-planner 2023-06-21 11:26:06 +02:00
Josip Mrden
aef90a28ea Add fixes to e2e 2023-06-20 18:03:47 +02:00
Josip Mrden
c66cc56d5f Add E2E test for optimizing merge on supernodes 2023-06-20 17:20:09 +02:00
Josip Mrden
114f00d64c Add index stats for merge expands 2023-06-20 15:13:53 +02:00
Josip Mrden
e0e0b19e44 Delete unnecessary scanall index variables 2023-06-20 14:14:10 +02:00
Josip Mrden
24ade79579 Add delete statistics for label indexes 2023-06-20 13:54:05 +02:00
Josip Mrden
8b849575c1 Add typed value results for label indexes 2023-06-20 13:11:19 +02:00
Josip Mrden
48138bb52f Rewrite code to have IndexStats in methods instead of having it separated 2023-06-20 11:35:39 +02:00
Josip Mrden
58867362d6 Fix build problems 2023-06-20 10:50:58 +02:00
Josip Mrden
00fb8149b5 Merge branch 'master' into T635-add-vertex-degree-to-query-planner 2023-06-20 10:10:52 +02:00
Josip Mrden
54bb5ed50d Introduce degree in cost estimator and index lookup expansions 2023-05-11 14:35:37 +02:00
Josip Mrden
b60aebaf56 Add LabelIndexStats 2023-05-11 09:52:25 +02:00
Josip Mrden
b2477663b7 Add average degree to label property indices 2023-05-10 17:51:26 +02:00
370 changed files with 14992 additions and 35966 deletions

View File

@@ -266,11 +266,12 @@ jobs:
- name: Run e2e tests
run: |
# TODO(gitbuda): Setup mgclient and pymgclient properly.
cd tests
./setup.sh /opt/toolchain-v4/activate
source ve3/bin/activate_e2e
./setup.sh
source ve3/bin/activate
cd e2e
./run.sh
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../libs/mgclient/lib python runner.py --workloads-root-directory .
- name: Run stress test (plain)
run: |
@@ -292,6 +293,7 @@ jobs:
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
cd build
# create mgconsole
@@ -338,8 +340,10 @@ jobs:
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build only memgraph release binarie.
cd build
cmake -DCMAKE_BUILD_TYPE=release ..
@@ -348,7 +352,7 @@ jobs:
- name: Run Jepsen tests
run: |
cd tests/jepsen
./run.sh test-all-individually --binary ../../build/memgraph --ignore-run-stdout-logs --ignore-run-stderr-logs
./run.sh test --binary ../../build/memgraph --run-args "test-all --node-configs resources/node-config.edn" --ignore-run-stdout-logs --ignore-run-stderr-logs
- name: Save Jepsen report
uses: actions/upload-artifact@v3

View File

@@ -6,7 +6,7 @@ on:
workflow_dispatch:
inputs:
memgraph_version:
description: "Memgraph version to upload as. If empty upload is skipped. Format: 'X.Y.Z'"
description: "Memgraph version to upload as. If empty upload is skipped. Format: 'vX.Y.Z'"
required: false
jobs:
@@ -201,7 +201,7 @@ jobs:
debian-11-arm:
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
timeout-minutes: 120
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
@@ -218,7 +218,7 @@ jobs:
ubuntu-2204-arm:
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
timeout-minutes: 120
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
@@ -253,4 +253,4 @@ jobs:
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: "eu-west-1"
SOURCE_DIR: "build/output/release"
DEST_DIR: "memgraph/v${{ github.event.inputs.memgraph_version }}/"
DEST_DIR: "memgraph/${{ github.event.inputs.memgraph_version }}/"

View File

@@ -265,11 +265,12 @@ jobs:
- name: Run e2e tests
run: |
# TODO(gitbuda): Setup mgclient and pymgclient properly.
cd tests
./setup.sh /opt/toolchain-v4/activate
source ve3/bin/activate_e2e
./setup.sh
source ve3/bin/activate
cd e2e
./run.sh
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../libs/mgclient/lib python runner.py --workloads-root-directory .
- name: Run stress test (plain)
run: |

View File

@@ -264,11 +264,12 @@ jobs:
- name: Run e2e tests
run: |
# TODO(gitbuda): Setup mgclient and pymgclient properly.
cd tests
./setup.sh /opt/toolchain-v4/activate
source ve3/bin/activate_e2e
./setup.sh
source ve3/bin/activate
cd e2e
./run.sh
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../libs/mgclient/lib python runner.py --workloads-root-directory .
- name: Run stress test (plain)
run: |
@@ -318,8 +319,10 @@ jobs:
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build only memgraph release binary.
cd build
cmake -DCMAKE_BUILD_TYPE=release ..
@@ -328,7 +331,7 @@ jobs:
- name: Run Jepsen tests
run: |
cd tests/jepsen
./run.sh test-all-individually --binary ../../build/memgraph --ignore-run-stdout-logs --ignore-run-stderr-logs
./run.sh test --binary ../../build/memgraph --run-args "test-all --node-configs resources/node-config.edn" --ignore-run-stdout-logs --ignore-run-stderr-logs
- name: Save Jepsen report
uses: actions/upload-artifact@v3

View File

@@ -264,11 +264,12 @@ jobs:
- name: Run e2e tests
run: |
# TODO(gitbuda): Setup mgclient and pymgclient properly.
cd tests
./setup.sh /opt/toolchain-v4/activate
source ve3/bin/activate_e2e
./setup.sh
source ve3/bin/activate
cd e2e
./run.sh
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../libs/mgclient/lib python runner.py --workloads-root-directory .
- name: Run stress test (plain)
run: |

View File

@@ -1,32 +0,0 @@
name: Upload Package All artifacts to S3
on:
workflow_dispatch:
inputs:
memgraph_version:
description: "Memgraph version to upload as. Format: 'X.Y.Z'"
required: true
run_number:
description: "# of the package_all workflow run to upload artifacts from. Format: '#XYZ'"
required: true
jobs:
upload-to-s3:
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: dawidd6/action-download-artifact@v2
with:
workflow: package_all.yaml
workflow_conclusion: success
run_number: "${{ github.event.inputs.run_number }}"
path: build/output/release
- name: Upload to S3
uses: jakejarvis/s3-sync-action@v0.5.1
env:
AWS_S3_BUCKET: "download.memgraph.com"
AWS_ACCESS_KEY_ID: ${{ secrets.S3_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: "eu-west-1"
SOURCE_DIR: "build/output/release"
DEST_DIR: "memgraph/v${{ github.event.inputs.memgraph_version }}/"

2
.gitignore vendored
View File

@@ -59,5 +59,3 @@ src/raft/storage_info_rpc_messages.hpp
src/stats/stats_rpc_messages.hpp
src/storage/distributed/rpc/concurrent_id_mapper_rpc_messages.hpp
src/transactions/distributed/engine_rpc_messages.hpp
/tests/manual/js/transaction_timeout/package-lock.json
/tests/manual/js/transaction_timeout/node_modules/

View File

@@ -196,7 +196,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \
-Werror=switch -Werror=switch-bool -Werror=return-type \
-Werror=return-stack-address \
-Wno-c99-designator -Wmissing-field-initializers \
-Wno-c99-designator \
-DBOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT")
# Don't omit frame pointer in RelWithDebInfo, for additional callchain debug.
@@ -298,7 +298,7 @@ set(ENABLE_JEMALLOC ON)
if (ASAN)
message(WARNING "Disabling jemalloc as it doesn't work well with ASAN")
set(ENABLE_JEMALLOC OFF)
# Enable Address sanitizer and get nicer stack traces in error messages.
# Enable Addres sanitizer and get nicer stack traces in error messages.
# NOTE: AddressSanitizer uses llvm-symbolizer binary from the Clang
# distribution to symbolize the stack traces (note that ideally the
# llvm-symbolizer version must match the version of ASan runtime library).

View File

@@ -175,6 +175,7 @@ license](./licenses/BSL.txt).</br> Memgraph Enterprise is available under the
- :purple_heart: [**Discord**](https://discord.gg/memgraph)
- :ocean: [**Stack Overflow**](https://stackoverflow.com/questions/tagged/memgraphdb)
- :busts_in_silhouette: [**Discourse forum**](https://discourse.memgraph.com/)
- :bird: [**Twitter**](https://twitter.com/memgraphdb)
- :movie_camera:
[**YouTube**](https://www.youtube.com/channel/UCZ3HOJvHGxtQ_JHxOselBYg)

View File

@@ -57,7 +57,7 @@ MEMGRAPH_BUILD_DEPS=(
libcurl-devel # mg-requests
rpm-build rpmlint # for RPM package building
doxygen graphviz # source documentation generators
which nodejs golang custom-golang1.18.9 zip unzip java-11-openjdk-devel jdk-17 custom-maven3.9.3 # for driver tests
which nodejs golang custom-golang1.18.9 zip unzip java-11-openjdk-devel jdk-17 custom-maven3.9.2 # for driver tests
autoconf # for jemalloc code generation
libtool # for protobuf code generation
cyrus-sasl-devel
@@ -82,8 +82,8 @@ check() {
local OLD_LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-""}
LD_LIBRARY_PATH=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -127,8 +127,8 @@ install() {
yum update -y
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -57,7 +57,7 @@ MEMGRAPH_BUILD_DEPS=(
rpm-build rpmlint # for RPM package building
doxygen graphviz # source documentation generators
which mono-complete dotnet-sdk-3.1 golang custom-golang1.18.9 # for driver tests
nodejs zip unzip java-11-openjdk-devel jdk-17 custom-maven3.9.3 # for driver tests
nodejs zip unzip java-11-openjdk-devel jdk-17 custom-maven3.9.2 # for driver tests
autoconf # for jemalloc code generation
libtool # for protobuf code generation
cyrus-sasl-devel
@@ -78,8 +78,8 @@ list() {
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -134,8 +134,8 @@ install() {
yum install -y git
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -57,7 +57,7 @@ MEMGRAPH_BUILD_DEPS=(
rpm-build rpmlint # for RPM package building
doxygen graphviz # source documentation generators
which nodejs golang custom-golang1.18.9 # for driver tests
zip unzip java-11-openjdk-devel java-17-openjdk java-17-openjdk-devel custom-maven3.9.3 # for driver tests
zip unzip java-11-openjdk-devel java-17-openjdk java-17-openjdk-devel custom-maven3.9.2 # for driver tests
sbcl # for custom Lisp C++ preprocessing
autoconf # for jemalloc code generation
libtool # for protobuf code generation
@@ -79,8 +79,8 @@ list() {
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -127,8 +127,8 @@ install() {
yum install -y wget git python3 python3-pip
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -53,7 +53,7 @@ MEMGRAPH_BUILD_DEPS=(
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless oracle-java17-installer custom-maven3.9.3 # for driver tests
mono-runtime mono-mcs zip unzip default-jdk-headless oracle-java17-installer custom-maven3.9.2 # for driver tests
dotnet-sdk-3.1 golang custom-golang1.18.9 nodejs npm # for driver tests
autoconf # for jemalloc code generation
libtool # for protobuf code generation
@@ -75,8 +75,8 @@ list() {
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -124,8 +124,8 @@ EOF
fi
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -54,7 +54,7 @@ MEMGRAPH_BUILD_DEPS=(
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk custom-maven3.9.3 # for driver tests
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk custom-maven3.9.2 # for driver tests
golang custom-golang1.18.9 nodejs npm
autoconf # for jemalloc code generation
libtool # for protobuf code generation
@@ -76,8 +76,8 @@ list() {
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -121,8 +121,8 @@ EOF
apt install -y wget
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -54,7 +54,7 @@ MEMGRAPH_BUILD_DEPS=(
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk custom-maven3.9.3 # for driver tests
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk custom-maven3.9.2 # for driver tests
dotnet-sdk-3.1 golang custom-golang1.18.9 nodejs npm
autoconf # for jemalloc code generation
libtool # for protobuf code generation
@@ -76,8 +76,8 @@ list() {
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -121,8 +121,8 @@ EOF
apt install -y wget
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -52,7 +52,7 @@ MEMGRAPH_BUILD_DEPS=(
libcurl-devel # mg-requests
rpm-build rpmlint # for RPM package building
doxygen graphviz # source documentation generators
java-11-openjdk-devel java-17-openjdk-devel custom-maven3.9.3 # for driver tests
java-11-openjdk-devel java-17-openjdk-devel custom-maven3.9.2 # for driver tests
which zip unzip
nodejs golang custom-golang1.18.9 # for driver tests
sbcl # for custom Lisp C++ preprocessing
@@ -82,8 +82,8 @@ check() {
LD_LIBRARY_PATH=""
fi
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -124,8 +124,8 @@ install() {
dnf update -y
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -53,7 +53,7 @@ MEMGRAPH_BUILD_DEPS=(
libcurl4-openssl-dev # mg-requests
sbcl # custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs nodejs zip unzip default-jdk-headless openjdk-17-jdk-headless custom-maven3.9.3 # driver tests
mono-runtime mono-mcs nodejs zip unzip default-jdk-headless openjdk-17-jdk-headless custom-maven3.9.2 # driver tests
custom-golang1.18.9 # for driver tests
autoconf # for jemalloc code generation
libtool # for protobuf code generation
@@ -75,8 +75,8 @@ list() {
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -101,8 +101,8 @@ install() {
apt update -y
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -53,7 +53,7 @@ MEMGRAPH_BUILD_DEPS=(
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk-headless custom-maven3.9.3 # for driver tests
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk-headless custom-maven3.9.2 # for driver tests
dotnet-sdk-3.1 golang custom-golang1.18.9 nodejs npm # for driver tests
autoconf # for jemalloc code generation
libtool # for protobuf code generation
@@ -75,8 +75,8 @@ list() {
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -111,8 +111,8 @@ install() {
fi
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -53,7 +53,7 @@ MEMGRAPH_BUILD_DEPS=(
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk-headless custom-maven3.9.3 # for driver tests
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk-headless custom-maven3.9.2 # for driver tests
dotnet-sdk-6.0 golang custom-golang1.18.9 nodejs npm
autoconf # for jemalloc code generation
libtool # for protobuf code generation
@@ -75,8 +75,8 @@ list() {
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -110,8 +110,8 @@ install() {
apt install -y wget
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -53,7 +53,7 @@ MEMGRAPH_BUILD_DEPS=(
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk-headless custom-maven3.9.3 # for driver tests
mono-runtime mono-mcs zip unzip default-jdk-headless openjdk-17-jdk-headless custom-maven3.9.2 # for driver tests
dotnet-sdk-6.0 golang custom-golang1.18.9 nodejs npm # for driver tests
autoconf # for jemalloc code generation
libtool # for protobuf code generation
@@ -75,8 +75,8 @@ list() {
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
if [ "$pkg" == custom-maven3.9.2 ]; then
if [ ! -f "/opt/apache-maven-3.9.2/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
@@ -110,8 +110,8 @@ install() {
fi
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
if [ "$pkg" == custom-maven3.9.2 ]; then
install_custom_maven "3.9.2"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then

View File

@@ -1,15 +1,8 @@
#!/bin/bash
function operating_system() {
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
grep -E '^(VERSION_)?ID=' /etc/os-release | \
sort | cut -d '=' -f 2- | sed 's/"//g' | paste -s -d '-'
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "$(sw_vers -productName)-$(sw_vers -productVersion | cut -d '.' -f 1)"
else
echo "operating_system called on an unknown OS"
exit 1
fi
grep -E '^(VERSION_)?ID=' /etc/os-release | \
sort | cut -d '=' -f 2- | sed 's/"//g' | paste -s -d '-'
}
function check_operating_system() {
@@ -100,10 +93,8 @@ function install_custom_golang() {
function install_custom_maven() {
MVNVERSION="$1"
MVNINSTALLDIR="/opt/apache-maven-$MVNVERSION"
MVNURL="https://s3.eu-west-1.amazonaws.com/deps.memgraph.io/maven/apache-maven-$MVNVERSION-bin.tar.gz"
if [ ! -f "$MVNINSTALLDIR/bin/mvn" ]; then
echo "Downloading maven from $MVNURL"
curl -LO "$MVNURL"
curl -LO "https://dlcdn.apache.org/maven/maven-3/$MVNVERSION/binaries/apache-maven-$MVNVERSION-bin.tar.gz"
tar -C "/opt" -xzf "apache-maven-$MVNVERSION-bin.tar.gz"
fi
echo "maven $MVNVERSION installed under $MVNINSTALLDIR"

View File

@@ -135,13 +135,6 @@ inline int64_t value_get_int(mgp_value *val) { return MgInvoke<int64_t>(mgp_valu
inline double value_get_double(mgp_value *val) { return MgInvoke<double>(mgp_value_get_double, val); }
inline double value_get_numeric(mgp_value *val) {
if (MgInvoke<int>(mgp_value_is_int, val)) {
return static_cast<double>(value_get_int(val));
}
return value_get_double(val);
}
inline const char *value_get_string(mgp_value *val) { return MgInvoke<const char *>(mgp_value_get_string, val); }
inline mgp_list *value_get_list(mgp_value *val) { return MgInvoke<mgp_list *>(mgp_value_get_list, val); }
@@ -178,8 +171,6 @@ inline bool value_is_int(mgp_value *val) { return MgInvoke<int>(mgp_value_is_int
inline bool value_is_double(mgp_value *val) { return MgInvoke<int>(mgp_value_is_double, val); }
inline bool value_is_numeric(mgp_value *val) { return value_is_int(val) || value_is_double(val); }
inline bool value_is_string(mgp_value *val) { return MgInvoke<int>(mgp_value_is_string, val); }
inline bool value_is_list(mgp_value *val) { return MgInvoke<int>(mgp_value_is_list, val); }
@@ -335,12 +326,6 @@ inline void map_insert(mgp_map *map, const char *key, mgp_value *value) {
MgInvokeVoid(mgp_map_insert, map, key, value);
}
inline void map_update(mgp_map *map, const char *key, mgp_value *value) {
MgInvokeVoid(mgp_map_update, map, key, value);
}
inline void map_erase(mgp_map *map, const char *key) { MgInvokeVoid(mgp_map_erase, map, key); }
inline size_t map_size(mgp_map *map) { return MgInvoke<size_t>(mgp_map_size, map); }
inline mgp_value *map_at(mgp_map *map, const char *key) { return MgInvoke<mgp_value *>(mgp_map_at, map, key); }
@@ -389,10 +374,6 @@ inline bool vertex_has_label_named(mgp_vertex *v, const char *label_name) {
inline void vertex_add_label(mgp_vertex *vertex, mgp_label label) { MgInvokeVoid(mgp_vertex_add_label, vertex, label); }
inline void vertex_remove_label(mgp_vertex *vertex, mgp_label label) {
MgInvokeVoid(mgp_vertex_remove_label, vertex, label);
}
inline mgp_value *vertex_get_property(mgp_vertex *v, const char *property_name, mgp_memory *memory) {
return MgInvoke<mgp_value *>(mgp_vertex_get_property, v, property_name, memory);
}
@@ -722,7 +703,7 @@ inline void proc_add_deprecated_result(mgp_proc *proc, const char *name, mgp_typ
MgInvokeVoid(mgp_proc_add_deprecated_result, proc, name, type);
}
inline int must_abort(mgp_graph *graph) { return mgp_must_abort(graph); }
inline bool must_abort(mgp_graph *graph) { return mgp_must_abort(graph); }
// mgp_result

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,96 +13,45 @@
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
namespace mg_exception {
// Instead of writing this utility function, we could have used `fmt::format`, but that's not an ideal option here
// because that would introduce dependency that would be propagated to the client code (if exceptions here would be
// used). Since the functionality here is not complex + the code is not on a critical path, we opted for a pure C++
// solution.
template <typename FirstArg, typename... Args>
std::string StringSerialize(FirstArg &&firstArg, Args &&...args) {
std::stringstream stream;
stream << firstArg;
((stream << " " << args), ...);
return stream.str();
}
struct UnknownException : public std::exception {
const char *what() const noexcept override { return "Unknown exception!"; }
};
struct NotEnoughMemoryException : public std::exception {
NotEnoughMemoryException()
: message_{
StringSerialize("Not enough memory! For more details please visit", "https://memgr.ph/memory-control")} {}
const char *what() const noexcept override { return message_.c_str(); }
private:
std::string message_;
const char *what() const throw() { return "Not enough memory!"; }
};
struct UnknownException : public std::exception {
const char *what() const throw() { return "Unknown exception!"; }
};
struct AllocationException : public std::exception {
AllocationException()
: message_{StringSerialize("Could not allocate memory. For more details please visit",
"https://memgr.ph/memory-control")} {}
const char *what() const noexcept override { return message_.c_str(); }
private:
std::string message_;
const char *what() const throw() { return "Could not allocate memory!"; }
};
struct InsufficientBufferException : public std::exception {
const char *what() const noexcept override { return "Buffer is not sufficient to process procedure!"; }
const char *what() const throw() { return "Buffer is not sufficient to process procedure!"; }
};
struct OutOfRangeException : public std::exception {
const char *what() const noexcept override { return "Index out of range!"; }
const char *what() const throw() { return "Index out of range!"; }
};
struct LogicException : public std::exception {
const char *what() const noexcept override { return "Logic exception, check the procedure signature!"; }
const char *what() const throw() { return "Logic exception, check the procedure signature!"; }
};
struct DeletedObjectException : public std::exception {
const char *what() const noexcept override { return "Object is deleted!"; }
const char *what() const throw() { return "Object is deleted!"; }
};
struct InvalidArgumentException : public std::exception {
const char *what() const noexcept override { return "Invalid argument!"; }
const char *what() const throw() { return "Invalid argument!"; }
};
struct InvalidIDException : public std::exception {
InvalidIDException() : message_{"Invalid ID!"} {}
explicit InvalidIDException(std::uint64_t identifier) : message_{StringSerialize("Invalid ID =", identifier)} {}
const char *what() const noexcept override { return message_.c_str(); }
private:
std::string message_;
const char *what() const throw() { return "Invalid ID!"; }
};
struct KeyAlreadyExistsException : public std::exception {
KeyAlreadyExistsException() : message_{"Key you are trying to set already exists!"} {}
explicit KeyAlreadyExistsException(const std::string &key)
: message_{StringSerialize("Key you are trying to set already exists! KEY = ", key)} {}
const char *what() const noexcept override { return message_.c_str(); }
private:
std::string message_;
const char *what() const throw() { return "Key you are trying to set already exists!"; }
};
struct ImmutableObjectException : public std::exception {
const char *what() const noexcept override { return "Object you are trying to change is immutable!"; }
const char *what() const throw() { return "Object you are trying to change is immutable!"; }
};
struct ValueConversionException : public std::exception {
const char *what() const noexcept override { return "Error in value conversion!"; }
const char *what() const throw() { return "Error in value conversion!"; }
};
struct SerializationException : public std::exception {
const char *what() const noexcept override { return "Error in serialization!"; }
const char *what() const throw() { return "Error in serialization!"; }
};
} // namespace mg_exception

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -462,18 +462,6 @@ void mgp_map_destroy(struct mgp_map *map);
/// Return mgp_error::MGP_ERROR_KEY_ALREADY_EXISTS if a previous mapping already exists.
enum mgp_error mgp_map_insert(struct mgp_map *map, const char *key, struct mgp_value *value);
/// Insert a mapping from a NULL terminated character string to a value.
/// If a mapping with the same key already exists, it is replaced.
/// In case of update, both the string and the value are copied into the map.
/// Therefore, the map does not take ownership of the original key nor value, so
/// you still need to free their memory explicitly.
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate for insertion.
enum mgp_error mgp_map_update(struct mgp_map *map, const char *key, struct mgp_value *value);
// Erase a mapping by key.
// If the key doesn't exist in the map nothing happens
enum mgp_error mgp_map_erase(struct mgp_map *map, const char *key);
/// Get the number of items stored in mgp_map.
/// Current implementation always returns without errors.
enum mgp_error mgp_map_size(struct mgp_map *map, size_t *result);
@@ -1460,10 +1448,7 @@ enum mgp_error mgp_log(enum mgp_log_level log_level, const char *output);
/// @{
/// Return non-zero if the currently executing procedure should abort as soon as
/// possible. If non-zero the reasons are:
/// (1) The transaction was requested to be terminated
/// (2) The server is gracefully shutting down
/// (3) The transaction has hit its timeout threshold
/// possible.
///
/// Procedures which perform heavyweight processing run the risk of running too
/// long and going over the query execution time limit. To prevent this, such

View File

@@ -11,17 +11,14 @@
#pragma once
#include <cstdlib>
#include <cstring>
#include <functional>
#include <map>
#include <set>
#include <string>
#include <string_view>
#include <vector>
#include "_mgp.hpp"
#include "mg_exceptions.hpp"
#include "mg_procedure.h"
namespace mgp {
@@ -53,6 +50,11 @@ class NotFoundException : public std::exception {
std::string message_;
};
class NotEnoughMemoryException : public std::exception {
public:
const char *what() const throw() { return "Not enough memory!"; }
};
class MustAbortException : public std::exception {
public:
explicit MustAbortException(const std::string &message) : message_(message) {}
@@ -62,21 +64,6 @@ class MustAbortException : public std::exception {
std::string message_;
};
class TerminatedMustAbortException : public MustAbortException {
public:
explicit TerminatedMustAbortException() : MustAbortException("Query was asked to terminate directly.") {}
};
class ShutdownMustAbortException : public MustAbortException {
public:
explicit ShutdownMustAbortException() : MustAbortException("Query was asked to because of server shutdown.") {}
};
class TimeoutMustAbortException : public MustAbortException {
public:
explicit TimeoutMustAbortException() : MustAbortException("Query was asked to because of timeout was hit.") {}
};
// Forward declarations
class Nodes;
using GraphNodes = Nodes;
@@ -119,19 +106,6 @@ class Id {
int64_t id_;
};
enum class AbortReason : uint8_t {
NO_ABORT = 0,
// transaction has been requested to terminate, ie. "TERMINATE TRANSACTIONS ..."
TERMINATED = 1,
// server is gracefully shutting down
SHUTDOWN = 2,
// the transaction timeout has been reached. Either via "--query-execution-timeout-sec", or a per-transaction timeout
TIMEOUT = 3,
};
/// @brief Wrapper class for @ref mgp_graph.
class Graph {
private:
@@ -176,13 +150,8 @@ class Graph {
/// @brief Deletes a relationship from the graph.
void DeleteRelationship(const Relationship &relationship);
/// @brief Checks if process must abort
/// @return AbortReason the reason to abort, if no need to abort then AbortReason::NO_ABORT is returned
AbortReason MustAbort() const;
bool MustAbort() const;
/// @brief Checks if process must abort
/// @throws MustAbortException If process must abort for any reason
/// @note For the reason why the process must abort consider using MustAbort method instead
void CheckMustAbort() const;
private:
@@ -427,9 +396,6 @@ class List {
/// @brief Returns the value at the given `index`.
const Value operator[](size_t index) const;
///@brief Same as above, but non const value
Value operator[](size_t index);
class Iterator {
private:
friend class List;
@@ -492,7 +458,6 @@ class Map {
public:
/// @brief Creates a Map from the copy of the given @ref mgp_map.
explicit Map(mgp_map *ptr);
/// @brief Creates a Map from the copy of the given @ref mgp_map.
explicit Map(const mgp_map *const_ptr);
@@ -501,7 +466,6 @@ class Map {
/// @brief Creates a Map from the given vector.
explicit Map(const std::map<std::string_view, Value> &items);
/// @brief Creates a Map from the given vector.
explicit Map(std::map<std::string_view, Value> &&items);
@@ -518,13 +482,11 @@ class Map {
/// @brief Returns the size of the map.
size_t Size() const;
/// @brief Returns whether the map is empty.
bool Empty() const;
/// @brief Returns the value at the given `key`.
Value const operator[](std::string_view key) const;
/// @brief Returns the value at the given `key`.
Value const At(std::string_view key) const;
@@ -565,30 +527,16 @@ class Map {
/// @brief Inserts the given `key`-`value` pair into the map. The `value` is copied.
void Insert(std::string_view key, const Value &value);
/// @brief Inserts the given `key`-`value` pair into the map.
/// @note Takes the ownership of `value` by moving it. The behavior of accessing `value` after performing this
/// operation is undefined.
void Insert(std::string_view key, Value &&value);
/// @brief Updates the `key`-`value` pair in the map. If the key doesn't exist, the value gets inserted. The `value`
/// is copied.
void Update(std::string_view key, const Value &value);
/// @brief Updates the `key`-`value` pair in the map. If the key doesn't exist, the value gets inserted. The `value`
/// is copied.
/// @note Takes the ownership of `value` by moving it. The behavior of accessing `value` after performing this
/// operation is undefined.
void Update(std::string_view key, Value &&value);
/// @brief Erases the element associated with the key from the map, if it doesn't exist does nothing.
void Erase(std::string_view key);
// void Erase(std::string_view key); // not implemented (requires mgp_map_erase in the MGP API)
// void Clear(); // not implemented (requires mgp_map_clear in the MGP API)
/// @exception std::runtime_error Map contains value of unknown type.
bool operator==(const Map &other) const;
/// @exception std::runtime_error Map contains value of unknown type.
bool operator!=(const Map &other) const;
@@ -610,7 +558,6 @@ class Node {
/// @brief Creates a Node from the copy of the given @ref mgp_vertex.
explicit Node(mgp_vertex *ptr);
/// @brief Creates a Node from the copy of the given @ref mgp_vertex.
explicit Node(const mgp_vertex *const_ptr);
@@ -637,29 +584,20 @@ class Node {
/// @brief Sets the chosen property to the given value.
void SetProperty(std::string property, Value value);
/// @brief Removes the chosen property.
void RemoveProperty(std::string property);
/// @brief Retrieves the value of the chosen property.
Value GetProperty(const std::string &property) const;
/// @brief Returns an iterable structure of the nodes inbound relationships.
Relationships InRelationships() const;
/// @brief Returns an iterable structure of the nodes outbound relationships.
Relationships OutRelationships() const;
/// @brief Adds a label to the node.
void AddLabel(const std::string_view label);
/// @brief Removes a label from the node.
void RemoveLabel(const std::string_view label);
bool operator<(const Node &other) const;
/// @exception std::runtime_error Node properties contain value(s) of unknown type.
bool operator==(const Node &other) const;
/// @exception std::runtime_error Node properties contain value(s) of unknown type.
bool operator!=(const Node &other) const;
@@ -1127,46 +1065,32 @@ class Value {
/// @pre Value type needs to be Type::Bool.
bool ValueBool() const;
bool ValueBool();
/// @pre Value type needs to be Type::Int.
int64_t ValueInt() const;
int64_t ValueInt();
/// @pre Value type needs to be Type::Double.
double ValueDouble() const;
double ValueDouble();
/// @pre Value type needs to be Type::Numeric.
double ValueNumeric() const;
double ValueNumeric();
/// @pre Value type needs to be Type::String.
std::string_view ValueString() const;
std::string_view ValueString();
/// @pre Value type needs to be Type::List.
const List ValueList() const;
List ValueList();
/// @pre Value type needs to be Type::Map.
const Map ValueMap() const;
Map ValueMap();
/// @pre Value type needs to be Type::Node.
const Node ValueNode() const;
Node ValueNode();
/// @pre Value type needs to be Type::Relationship.
const Relationship ValueRelationship() const;
Relationship ValueRelationship();
/// @pre Value type needs to be Type::Path.
const Path ValuePath() const;
Path ValuePath();
/// @pre Value type needs to be Type::Date.
const Date ValueDate() const;
Date ValueDate();
/// @pre Value type needs to be Type::LocalTime.
const LocalTime ValueLocalTime() const;
LocalTime ValueLocalTime();
/// @pre Value type needs to be Type::LocalDateTime.
const LocalDateTime ValueLocalDateTime() const;
LocalDateTime ValueLocalDateTime();
/// @pre Value type needs to be Type::Duration.
const Duration ValueDuration() const;
Duration ValueDuration();
/// @brief Returns whether the value is null.
bool IsNull() const;
@@ -1204,10 +1128,6 @@ class Value {
/// @exception std::runtime_error Unknown value type.
bool operator!=(const Value &other) const;
bool operator<(const Value &other) const;
friend std::ostream &operator<<(std::ostream &os, const mgp::Value &value);
private:
mgp_value *ptr_;
};
@@ -1260,8 +1180,6 @@ class Record {
void Insert(const char *field_name, const LocalDateTime &local_date_time);
/// @brief Inserts a @ref Duration value under field `field_name`.
void Insert(const char *field_name, const Duration &duration);
/// @brief Inserts a @ref Value value under field `field_name`, and then call appropriate insert.
void Insert(const char *field_name, const Value &value);
private:
mgp_result_record *record_;
@@ -1427,67 +1345,6 @@ inline void AddFunction(mgp_func_cb callback, std::string_view name, std::vector
/* #endregion */
namespace util {
inline uint64_t Fnv(const std::string_view s) {
// fnv1a is recommended so use it as the default implementation.
uint64_t hash = 14695981039346656037UL;
for (const auto &ch : s) {
hash = (hash ^ (uint64_t)ch) * 1099511628211UL;
}
return hash;
}
/**
* Does FNV-like hashing on a collection. Not truly FNV
* because it operates on 8-bit elements, while this
* implementation uses size_t elements (collection item
* hash).
*
* https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
*
*
* @tparam TIterable A collection type that has begin() and end().
* @tparam TElement Type of element in the collection.
* @tparam THash Hash type (has operator() that accepts a 'const TEelement &'
* and returns size_t. Defaults to std::hash<TElement>.
* @param iterable A collection of elements.
* @param element_hash Function for hashing a single element.
* @return The hash of the whole collection.
*/
template <typename TIterable, typename TElement, typename THash = std::hash<TElement>>
struct FnvCollection {
size_t operator()(const TIterable &iterable) const {
uint64_t hash = 14695981039346656037u;
THash element_hash;
for (const TElement &element : iterable) {
hash *= fnv_prime;
hash ^= element_hash(element);
}
return hash;
}
private:
static const uint64_t fnv_prime = 1099511628211u;
};
/**
* Like FNV hashing for a collection, just specialized for two elements to avoid
* iteration overhead.
*/
template <typename TA, typename TB, typename TAHash = std::hash<TA>, typename TBHash = std::hash<TB>>
struct HashCombine {
size_t operator()(const TA &a, const TB &b) const {
static constexpr size_t fnv_prime = 1099511628211UL;
static constexpr size_t fnv_offset = 14695981039346656037UL;
size_t ret = fnv_offset;
ret ^= TAHash()(a);
ret *= fnv_prime;
ret ^= TBHash()(b);
return ret;
}
};
// uint to int conversion in C++ is a bit tricky. Take a look here
// https://stackoverflow.com/questions/14623266/why-cant-i-reinterpret-cast-uint-to-int
// for more details.
@@ -1610,10 +1467,6 @@ inline bool ValuesEqual(mgp_value *value1, mgp_value *value2) {
if (value1 == value2) {
return true;
}
// Make int and double comparable, (ex. this is true -> 1.0 == 1)
if (mgp::value_is_numeric(value1) && mgp::value_is_numeric(value2)) {
return mgp::value_get_numeric(value1) == mgp::value_get_numeric(value2);
}
if (mgp::value_get_type(value1) != mgp::value_get_type(value2)) {
return false;
}
@@ -1749,31 +1602,11 @@ inline Id::Id(int64_t id) : id_(id) {}
inline Graph::Graph(mgp_graph *graph) : graph_(graph) {}
inline AbortReason Graph::MustAbort() const {
const auto reason = must_abort(graph_);
switch (reason) {
case 1:
return AbortReason::TERMINATED;
case 2:
return AbortReason::SHUTDOWN;
case 3:
return AbortReason::TIMEOUT;
default:
break;
}
return AbortReason::NO_ABORT;
}
inline bool Graph::MustAbort() const { return must_abort(graph_); }
inline void Graph::CheckMustAbort() const {
switch (MustAbort()) {
case AbortReason::TERMINATED:
throw TerminatedMustAbortException();
case AbortReason::SHUTDOWN:
throw ShutdownMustAbortException();
case AbortReason::TIMEOUT:
throw TimeoutMustAbortException();
case AbortReason::NO_ABORT:
break;
if (MustAbort()) {
throw MustAbortException("Query was asked to abort.");
}
}
@@ -2277,8 +2110,6 @@ inline bool List::Empty() const { return Size() == 0; }
inline const Value List::operator[](size_t index) const { return Value(mgp::list_at(ptr_, index)); }
inline Value List::operator[](size_t index) { return Value(mgp::list_at(ptr_, index)); }
inline bool List::Iterator::operator==(const Iterator &other) const {
return iterable_ == other.iterable_ && index_ == other.index_;
}
@@ -2468,20 +2299,9 @@ inline void Map::Insert(std::string_view key, const Value &value) { mgp::map_ins
inline void Map::Insert(std::string_view key, Value &&value) {
mgp::map_insert(ptr_, key.data(), value.ptr_);
value.~Value();
value.ptr_ = nullptr;
}
inline void Map::Update(std::string_view key, const Value &value) { mgp::map_update(ptr_, key.data(), value.ptr_); }
inline void Map::Update(std::string_view key, Value &&value) {
mgp::map_update(ptr_, key.data(), value.ptr_);
value.~Value();
value.ptr_ = nullptr;
}
inline void Map::Erase(std::string_view key) { mgp::map_erase(ptr_, key.data()); }
inline bool Map::operator==(const Map &other) const { return util::MapsEqual(ptr_, other.ptr_); }
inline bool Map::operator!=(const Map &other) const { return !(*this == other); }
@@ -2541,7 +2361,7 @@ inline bool Node::HasLabel(std::string_view label) const {
inline Relationships Node::InRelationships() const {
auto relationship_iterator = mgp::vertex_iter_in_edges(ptr_, memory);
if (relationship_iterator == nullptr) {
throw mg_exception::NotEnoughMemoryException();
throw NotEnoughMemoryException();
}
return Relationships(relationship_iterator);
}
@@ -2549,7 +2369,7 @@ inline Relationships Node::InRelationships() const {
inline Relationships Node::OutRelationships() const {
auto relationship_iterator = mgp::vertex_iter_out_edges(ptr_, memory);
if (relationship_iterator == nullptr) {
throw mg_exception::NotEnoughMemoryException();
throw NotEnoughMemoryException();
}
return Relationships(relationship_iterator);
}
@@ -2558,10 +2378,6 @@ inline void Node::AddLabel(const std::string_view label) {
mgp::vertex_add_label(this->ptr_, mgp_label{.name = label.data()});
}
inline void Node::RemoveLabel(const std::string_view label) {
mgp::vertex_remove_label(this->ptr_, mgp_label{.name = label.data()});
}
inline std::map<std::string, Value> Node::Properties() const {
mgp_properties_iterator *properties_iterator = mgp::vertex_iter_properties(ptr_, memory);
std::map<std::string, Value> property_map;
@@ -2577,8 +2393,6 @@ inline void Node::SetProperty(std::string property, Value value) {
mgp::vertex_set_property(ptr_, property.data(), value.ptr());
}
inline void Node::RemoveProperty(std::string property) { SetProperty(property, Value()); }
inline Value Node::GetProperty(const std::string &property) const {
mgp_value *vertex_prop = mgp::vertex_get_property(ptr_, property.data(), memory);
return Value(steal, vertex_prop);
@@ -2722,7 +2536,6 @@ inline void Path::Expand(const Relationship &relationship) { mgp::path_expand(pt
inline bool Path::operator==(const Path &other) const { return util::PathsEqual(ptr_, other.ptr_); }
inline bool Path::operator!=(const Path &other) const { return !(*this == other); }
/* #endregion */
/* #region Temporal types (Date, LocalTime, LocalDateTime, Duration) */
@@ -3244,12 +3057,6 @@ inline bool Value::ValueBool() const {
}
return mgp::value_get_bool(ptr_);
}
inline bool Value::ValueBool() {
if (Type() != Type::Bool) {
throw ValueException("Type of value is wrong: expected Bool.");
}
return mgp::value_get_bool(ptr_);
}
inline std::int64_t Value::ValueInt() const {
if (Type() != Type::Int) {
@@ -3257,12 +3064,6 @@ inline std::int64_t Value::ValueInt() const {
}
return mgp::value_get_int(ptr_);
}
inline std::int64_t Value::ValueInt() {
if (Type() != Type::Int) {
throw ValueException("Type of value is wrong: expected Int.");
}
return mgp::value_get_int(ptr_);
}
inline double Value::ValueDouble() const {
if (Type() != Type::Double) {
@@ -3270,12 +3071,6 @@ inline double Value::ValueDouble() const {
}
return mgp::value_get_double(ptr_);
}
inline double Value::ValueDouble() {
if (Type() != Type::Double) {
throw ValueException("Type of value is wrong: expected Double.");
}
return mgp::value_get_double(ptr_);
}
inline double Value::ValueNumeric() const {
if (Type() != Type::Int && Type() != Type::Double) {
@@ -3286,15 +3081,6 @@ inline double Value::ValueNumeric() const {
}
return mgp::value_get_double(ptr_);
}
inline double Value::ValueNumeric() {
if (Type() != Type::Int && Type() != Type::Double) {
throw ValueException("Type of value is wrong: expected Int or Double.");
}
if (Type() == Type::Int) {
return static_cast<double>(mgp::value_get_int(ptr_));
}
return mgp::value_get_double(ptr_);
}
inline std::string_view Value::ValueString() const {
if (Type() != Type::String) {
@@ -3302,12 +3088,6 @@ inline std::string_view Value::ValueString() const {
}
return mgp::value_get_string(ptr_);
}
inline std::string_view Value::ValueString() {
if (Type() != Type::String) {
throw ValueException("Type of value is wrong: expected String.");
}
return mgp::value_get_string(ptr_);
}
inline const List Value::ValueList() const {
if (Type() != Type::List) {
@@ -3315,12 +3095,6 @@ inline const List Value::ValueList() const {
}
return List(mgp::value_get_list(ptr_));
}
inline List Value::ValueList() {
if (Type() != Type::List) {
throw ValueException("Type of value is wrong: expected List.");
}
return List(mgp::value_get_list(ptr_));
}
inline const Map Value::ValueMap() const {
if (Type() != Type::Map) {
@@ -3328,12 +3102,6 @@ inline const Map Value::ValueMap() const {
}
return Map(mgp::value_get_map(ptr_));
}
inline Map Value::ValueMap() {
if (Type() != Type::Map) {
throw ValueException("Type of value is wrong: expected Map.");
}
return Map(mgp::value_get_map(ptr_));
}
inline const Node Value::ValueNode() const {
if (Type() != Type::Node) {
@@ -3341,12 +3109,6 @@ inline const Node Value::ValueNode() const {
}
return Node(mgp::value_get_vertex(ptr_));
}
inline Node Value::ValueNode() {
if (Type() != Type::Node) {
throw ValueException("Type of value is wrong: expected Node.");
}
return Node(mgp::value_get_vertex(ptr_));
}
inline const Relationship Value::ValueRelationship() const {
if (Type() != Type::Relationship) {
@@ -3354,12 +3116,6 @@ inline const Relationship Value::ValueRelationship() const {
}
return Relationship(mgp::value_get_edge(ptr_));
}
inline Relationship Value::ValueRelationship() {
if (Type() != Type::Relationship) {
throw ValueException("Type of value is wrong: expected Relationship.");
}
return Relationship(mgp::value_get_edge(ptr_));
}
inline const Path Value::ValuePath() const {
if (Type() != Type::Path) {
@@ -3367,12 +3123,6 @@ inline const Path Value::ValuePath() const {
}
return Path(mgp::value_get_path(ptr_));
}
inline Path Value::ValuePath() {
if (Type() != Type::Path) {
throw ValueException("Type of value is wrong: expected Path.");
}
return Path(mgp::value_get_path(ptr_));
}
inline const Date Value::ValueDate() const {
if (Type() != Type::Date) {
@@ -3380,12 +3130,6 @@ inline const Date Value::ValueDate() const {
}
return Date(mgp::value_get_date(ptr_));
}
inline Date Value::ValueDate() {
if (Type() != Type::Date) {
throw ValueException("Type of value is wrong: expected Date.");
}
return Date(mgp::value_get_date(ptr_));
}
inline const LocalTime Value::ValueLocalTime() const {
if (Type() != Type::LocalTime) {
@@ -3393,12 +3137,6 @@ inline const LocalTime Value::ValueLocalTime() const {
}
return LocalTime(mgp::value_get_local_time(ptr_));
}
inline LocalTime Value::ValueLocalTime() {
if (Type() != Type::LocalTime) {
throw ValueException("Type of value is wrong: expected LocalTime.");
}
return LocalTime(mgp::value_get_local_time(ptr_));
}
inline const LocalDateTime Value::ValueLocalDateTime() const {
if (Type() != Type::LocalDateTime) {
@@ -3406,12 +3144,6 @@ inline const LocalDateTime Value::ValueLocalDateTime() const {
}
return LocalDateTime(mgp::value_get_local_date_time(ptr_));
}
inline LocalDateTime Value::ValueLocalDateTime() {
if (Type() != Type::LocalDateTime) {
throw ValueException("Type of value is wrong: expected LocalDateTime.");
}
return LocalDateTime(mgp::value_get_local_date_time(ptr_));
}
inline const Duration Value::ValueDuration() const {
if (Type() != Type::Duration) {
@@ -3419,12 +3151,6 @@ inline const Duration Value::ValueDuration() const {
}
return Duration(mgp::value_get_duration(ptr_));
}
inline Duration Value::ValueDuration() {
if (Type() != Type::Duration) {
throw ValueException("Type of value is wrong: expected Duration.");
}
return Duration(mgp::value_get_duration(ptr_));
}
inline bool Value::IsNull() const { return mgp::value_is_null(ptr_); }
@@ -3459,128 +3185,6 @@ inline bool Value::IsDuration() const { return mgp::value_is_duration(ptr_); }
inline bool Value::operator==(const Value &other) const { return util::ValuesEqual(ptr_, other.ptr_); }
inline bool Value::operator!=(const Value &other) const { return !(*this == other); }
inline bool Value::operator<(const Value &other) const {
const mgp::Type &type = Type();
if (type != other.Type() && !(IsNumeric() && other.IsNumeric())) {
throw ValueException("Values have to be of the same type");
}
switch (type) {
case Type::Null:
throw ValueException("Cannot compare Null types");
case Type::Bool:
return ValueBool() < other.ValueBool();
case Type::Int:
return ValueNumeric() < other.ValueNumeric();
case Type::Double:
return ValueNumeric() < other.ValueNumeric();
case Type::String:
return ValueString() < other.ValueString();
case Type::Node:
return ValueNode() < other.ValueNode();
case Type::Relationship:
return ValueRelationship() < other.ValueRelationship();
case Type::Date:
return ValueDate() < other.ValueDate();
case Type::LocalTime:
return ValueLocalTime() < other.ValueLocalTime();
case Type::LocalDateTime:
return ValueLocalDateTime() < other.ValueLocalDateTime();
case Type::Duration:
return ValueDuration() < other.ValueDuration();
case Type::Path:
case Type::List:
case Type::Map:
throw ValueException("Operator < is not defined for this Path, List or Map data type");
default:
throw ValueException("Undefined behaviour");
}
}
inline std::ostream &operator<<(std::ostream &os, const mgp::Value &value) {
switch (value.Type()) {
case mgp::Type::Null:
return os << "null";
case mgp::Type::Any:
return os << "any";
case mgp::Type::Bool:
return os << (value.ValueBool() ? "true" : "false");
case mgp::Type::Int:
return os << std::to_string(value.ValueInt());
case mgp::Type::Double:
return os << std::to_string(value.ValueDouble());
case mgp::Type::String:
return os << std::string(value.ValueString());
case mgp::Type::List:
throw mgp::ValueException("Printing mgp::List type currently not supported.");
case mgp::Type::Map:
throw mgp::ValueException("Printing mgp::Map type currently not supported.");
case mgp::Type::Node:
return os << "Node[" + std::to_string(value.ValueNode().Id().AsInt()) + "]";
case mgp::Type::Relationship:
return os << "Relationship[" + std::to_string(value.ValueRelationship().Id().AsInt()) + "]";
case mgp::Type::Path:
throw mgp::ValueException("Printing mgp::Path type currently not supported.");
case mgp::Type::Date: {
const auto date{value.ValueDate()};
return os << std::to_string(date.Year()) + "-" + std::to_string(date.Month()) + "-" + std::to_string(date.Day());
}
case mgp::Type::LocalTime: {
const auto localTime{value.ValueLocalTime()};
return os << std::to_string(localTime.Hour()) + ":" + std::to_string(localTime.Minute()) + ":" +
std::to_string(localTime.Second()) + "," + std::to_string(localTime.Millisecond()) +
std::to_string(localTime.Microsecond());
}
case mgp::Type::LocalDateTime: {
const auto localDateTime = value.ValueLocalDateTime();
return os << std::to_string(localDateTime.Year()) + "-" + std::to_string(localDateTime.Month()) + "-" +
std::to_string(localDateTime.Day()) + "T" + std::to_string(localDateTime.Hour()) + ":" +
std::to_string(localDateTime.Minute()) + ":" + std::to_string(localDateTime.Second()) + "," +
std::to_string(localDateTime.Millisecond()) + std::to_string(localDateTime.Microsecond());
}
case mgp::Type::Duration:
return os << std::to_string(value.ValueDuration().Microseconds()) + "ms";
default:
throw mgp::ValueException("Unknown value type");
}
}
inline std::ostream &operator<<(std::ostream &os, const mgp::Type &type) {
switch (type) {
case mgp::Type::Null:
return os << "null";
case mgp::Type::Bool:
return os << "bool";
case mgp::Type::Int:
return os << "int";
case mgp::Type::Double:
return os << "double";
case mgp::Type::String:
return os << "string";
case mgp::Type::List:
return os << "list";
case mgp::Type::Map:
return os << "map";
case mgp::Type::Node:
return os << "vertex";
case mgp::Type::Relationship:
return os << "edge";
case mgp::Type::Path:
return os << "path";
case mgp::Type::Date:
return os << "date";
case mgp::Type::LocalTime:
return os << "local_time";
case mgp::Type::LocalDateTime:
return os << "local_date_time";
case mgp::Type::Duration:
return os << "duration";
default:
throw ValueException("Unknown type");
}
}
/* #endregion */
/* #region Record */
@@ -3672,40 +3276,6 @@ inline void Record::Insert(const char *field_name, const Duration &duration) {
mgp::value_destroy(mgp_val);
}
inline void Record::Insert(const char *field_name, const Value &value) {
switch (value.Type()) {
case Type::Bool:
return Insert(field_name, value.ValueBool());
case Type::Int:
return Insert(field_name, value.ValueInt());
case Type::Double:
return Insert(field_name, value.ValueDouble());
case Type::String:
return Insert(field_name, value.ValueString());
case Type::List:
return Insert(field_name, value.ValueList());
case Type::Map:
return Insert(field_name, value.ValueMap());
case Type::Node:
return Insert(field_name, value.ValueNode());
case Type::Relationship:
return Insert(field_name, value.ValueRelationship());
case Type::Path:
return Insert(field_name, value.ValuePath());
case Type::Date:
return Insert(field_name, value.ValueDate());
case Type::LocalTime:
return Insert(field_name, value.ValueLocalTime());
case Type::LocalDateTime:
return Insert(field_name, value.ValueLocalDateTime());
case Type::Duration:
return Insert(field_name, value.ValueDuration());
default:
throw ValueException("No Record.Insert for this datatype");
}
}
// RecordFactory:
inline RecordFactory::RecordFactory(mgp_result *result) : result_(result) {}
@@ -3713,7 +3283,7 @@ inline RecordFactory::RecordFactory(mgp_result *result) : result_(result) {}
inline const Record RecordFactory::NewRecord() const {
auto record = mgp::result_new_record(result_);
if (record == nullptr) {
throw mg_exception::NotEnoughMemoryException();
throw NotEnoughMemoryException();
}
return Record(record);
}
@@ -3953,28 +3523,6 @@ struct hash<mgp::Relationship> {
size_t operator()(const mgp::Relationship &x) const { return hash<int64_t>()(x.Id().AsInt()); };
};
template <>
struct hash<mgp::Path> {
size_t operator()(const mgp::Path &x) const {
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
// See mgp::util::FnvCollection
constexpr const uint64_t fnv_prime = 1099511628211U;
uint64_t hash = 14695981039346656037U;
auto multiply_and_xor = [](uint64_t &hash, size_t element_hash) {
hash *= fnv_prime;
hash ^= element_hash;
};
for (size_t i = 0; i < x.Length() - 1; ++i) {
multiply_and_xor(hash, std::hash<mgp::Node>{}(x.GetNodeAt(i)));
multiply_and_xor(hash, std::hash<mgp::Relationship>{}(x.GetRelationshipAt(i)));
}
multiply_and_xor(hash, std::hash<mgp::Node>{}(x.GetNodeAt(x.Length())));
return hash;
}
};
template <>
struct hash<mgp::Date> {
size_t operator()(const mgp::Date &x) const { return hash<int64_t>()(x.Timestamp()); };
@@ -3999,59 +3547,4 @@ template <>
struct hash<mgp::MapItem> {
size_t operator()(const mgp::MapItem &x) const { return hash<std::string_view>()(x.key); };
};
template <>
struct hash<mgp::Map> {
size_t operator()(const mgp::Map &x) const {
return mgp::util::FnvCollection<mgp::Map, mgp::MapItem, std::hash<mgp::MapItem>>{}(x);
}
};
template <>
struct hash<mgp::Value> {
size_t operator()(const mgp::Value &x) const {
switch (x.Type()) {
case mgp::Type::Null:
return 31;
case mgp::Type::Any:
throw mg_exception::InvalidArgumentException();
case mgp::Type::Bool:
return std::hash<bool>{}(x.ValueBool());
case mgp::Type::Int:
// we cast int to double for hashing purposes
// to be consistent with equality (2.0 == 2) == true
return std::hash<double>{}((double)x.ValueInt());
case mgp::Type::Double:
return std::hash<double>{}(x.ValueDouble());
case mgp::Type::String:
return std::hash<std::string_view>{}(x.ValueString());
case mgp::Type::List:
return mgp::util::FnvCollection<mgp::List, mgp::Value, std::hash<mgp::Value>>{}(x.ValueList());
case mgp::Type::Map:
return std::hash<mgp::Map>{}(x.ValueMap());
case mgp::Type::Node:
return std::hash<mgp::Node>{}(x.ValueNode());
case mgp::Type::Relationship:
return std::hash<mgp::Relationship>{}(x.ValueRelationship());
case mgp::Type::Path:
return std::hash<mgp::Path>{}(x.ValuePath());
case mgp::Type::Date:
return std::hash<mgp::Date>{}(x.ValueDate());
case mgp::Type::LocalTime:
return std::hash<mgp::LocalTime>{}(x.ValueLocalTime());
case mgp::Type::LocalDateTime:
return std::hash<mgp::LocalDateTime>{}(x.ValueLocalDateTime());
case mgp::Type::Duration:
return std::hash<mgp::Duration>{}(x.ValueDuration());
}
throw mg_exception::InvalidArgumentException();
}
};
template <>
struct hash<mgp::List> {
size_t operator()(const mgp::List &x) {
return mgp::util::FnvCollection<mgp::List, mgp::Value, std::hash<mgp::Value>>{}(x);
}
};
} // namespace std

4
init
View File

@@ -93,10 +93,6 @@ setup_virtualenv tests/stress
setup_virtualenv tests/integration/ldap
# Setup tests dependencies.
# NOTE: This is commented out because of the build order (at the time of
# execution mgclient is not built yet) which makes this setup to fail. mgclient
# is built during the make phase. The tests/setup.sh is called under GHA CI
# jobs.
# cd tests
# ./setup.sh
# cd ..

View File

@@ -71,8 +71,8 @@ file_get_try_double () {
if [ -z "$primary_url" ]; then echo "Primary should not be empty." && exit 1; fi
if [ -z "$secondary_url" ]; then echo "Secondary should not be empty." && exit 1; fi
filename="$(basename "$secondary_url")"
# Redirect primary/cache to /dev/null to make it less confusing for a new contributor because only CI has access to the cache.
wget -nv "$primary_url" -O "$filename" >/dev/null 2>&1 || wget -nv "$secondary_url" -O "$filename" || exit 1
wget -nv "$primary_url" -O "$filename" || wget -nv "$secondary_url" -O "$filename" || exit 1
echo ""
}
repo_clone_try_double () {
@@ -86,8 +86,8 @@ repo_clone_try_double () {
if [ -z "$secondary_url" ]; then echo "Secondary should not be empty." && exit 1; fi
if [ -z "$folder_name" ]; then echo "Clone folder should not be empty." && exit 1; fi
if [ -z "$ref" ]; then echo "Git clone ref should not be empty." && exit 1; fi
# Redirect primary/cache to /dev/null to make it less confusing for a new contributor because only CI has access to the cache.
clone "$primary_url" "$folder_name" "$ref" "$shallow" >/dev/null 2>&1 || clone "$secondary_url" "$folder_name" "$ref" "$shallow" || exit 1
clone "$primary_url" "$folder_name" "$ref" "$shallow" || clone "$secondary_url" "$folder_name" "$ref" "$shallow" || exit 1
echo ""
}
# List all dependencies.
@@ -210,7 +210,7 @@ pymgclient_tag="4f85c179e56302d46a1e3e2cf43509db65f062b3" # (2021-01-15)
repo_clone_try_double "${primary_urls[pymgclient]}" "${secondary_urls[pymgclient]}" "pymgclient" "$pymgclient_tag"
# mgconsole
mgconsole_tag="v1.4.0" # (2023-05-21)
mgconsole_tag="v1.3.0" # (2022-11-20)
repo_clone_try_double "${primary_urls[mgconsole]}" "${secondary_urls[mgconsole]}" "mgconsole" "$mgconsole_tag" true
spdlog_tag="v1.9.2" # (2021-08-12)

View File

@@ -36,7 +36,7 @@ ADDITIONAL USE GRANT: You may use the Licensed Work in accordance with the
3. using the Licensed Work to create a work or solution
which competes (or might reasonably be expected to
compete) with the Licensed Work.
CHANGE DATE: 2027-02-08
CHANGE DATE: 2027-18-05
CHANGE LICENSE: Apache License, Version 2.0
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.

View File

@@ -14,7 +14,7 @@ set(CPACK_PACKAGE_VENDOR "Memgraph Ltd.")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
"High performance, in-memory, transactional graph database")
# Setting architecture extension for deb packages
# Setting arhitecture extension for deb packages
set(MG_ARCH_EXTENSION_DEB "all")
if(${MG_ARCH} STREQUAL "x86_64")
@@ -50,7 +50,7 @@ set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}
# We also depend on `python3` because we embed it in Memgraph.
set(CPACK_DEBIAN_PACKAGE_DEPENDS "openssl (>= 1.1.0), python3 (>= 3.5.0)")
# Setting architecture extension for rpm packages
# Setting arhitecture extension for rpm packages
set(MG_ARCH_EXTENSION_RPM "noarch")
if(${MG_ARCH} STREQUAL "x86_64")

View File

@@ -9,7 +9,6 @@ RUN yum -y update \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-amzn-2-x86_64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-amzn-2-x86_64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-amzn-2-x86_64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-amzn-2-x86_64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-amzn-2-x86_64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -9,7 +9,6 @@ RUN yum -y update \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-centos-7-x86_64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-centos-7-x86_64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-centos-7-x86_64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-centos-7-x86_64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-centos-7-x86_64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -9,7 +9,6 @@ RUN yum -y update \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-centos-9-x86_64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-centos-9-x86_64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-centos-9-x86_64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-centos-9-x86_64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-centos-9-x86_64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -12,7 +12,6 @@ RUN apt update && apt install -y \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-debian-10-amd64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-debian-10-amd64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-10-amd64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-debian-10-amd64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-10-amd64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -12,7 +12,6 @@ RUN apt update && apt install -y \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-debian-11-arm64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-debian-11-arm64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-11-arm64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-debian-11-arm64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-11-arm64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -12,7 +12,6 @@ RUN apt update && apt install -y \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-debian-11-amd64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-debian-11-amd64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-11-amd64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-debian-11-amd64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-11-amd64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -10,7 +10,6 @@ RUN yum -y update \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -159,19 +159,6 @@ case "$1" in
fi
;;
build)
shift 1
if [[ "$#" -ne 2 ]]; then
print_help
fi
# in the vX format, e.g. v5
toolchain_version="$1"
# a name of the os folder, e.g. ubuntu-22.04-arm
os="$2"
cd "$SCRIPT_DIR/$os"
docker build -f Dockerfile --build-arg TOOLCHAIN_VERSION="toolchain-$toolchain_version" -t "memgraph/memgraph-builder:${toolchain_version}_$os" .
;;
test)
echo "TODO(gitbuda): Test all packages on mgtest containers."
;;

View File

@@ -12,7 +12,6 @@ RUN apt update && apt install -y \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04-amd64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04-amd64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04-amd64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04-amd64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-18.04-amd64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -12,7 +12,6 @@ RUN apt update && apt install -y \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04-amd64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04-amd64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04-amd64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04-amd64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-20.04-amd64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -12,7 +12,6 @@ RUN apt update && apt install -y \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -12,7 +12,6 @@ RUN apt update && apt install -y \
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-amd64.tar.gz \
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-amd64.tar.gz \
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-amd64.tar.gz -C /opt \
&& rm ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-amd64.tar.gz
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-amd64.tar.gz -C /opt
ENTRYPOINT ["sleep", "infinity"]

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 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
@@ -116,12 +116,12 @@ Log::~Log() {
}
void Log::Record(const std::string &address, const std::string &username, const std::string &query,
const storage::PropertyValue &params, const std::string &db) {
const storage::PropertyValue &params) {
if (!started_.load(std::memory_order_relaxed)) return;
auto timestamp =
std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
.count();
buffer_->emplace(Item{timestamp, address, username, query, params, db});
buffer_->emplace(Item{timestamp, address, username, query, params});
}
void Log::ReopenLog() {
@@ -136,8 +136,8 @@ void Log::Flush() {
for (uint64_t i = 0; i < buffer_size_; ++i) {
auto item = buffer_->pop();
if (!item) break;
log_.Write(fmt::format("{}.{:06d},{},{},{},{},{}\n", item->timestamp / 1000000, item->timestamp % 1000000,
item->address, item->username, item->db, utils::Escape(item->query),
log_.Write(fmt::format("{}.{:06d},{},{},{},{}\n", item->timestamp / 1000000, item->timestamp % 1000000,
item->address, item->username, utils::Escape(item->query),
utils::Escape(PropertyValueToJson(item->params).dump())));
}
log_.Sync();

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 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
@@ -32,7 +32,6 @@ class Log {
std::string username;
std::string query;
storage::PropertyValue params;
std::string db;
};
public:
@@ -52,7 +51,7 @@ class Log {
/// Adds an entry to the audit log. Thread-safe.
void Record(const std::string &address, const std::string &username, const std::string &query,
const storage::PropertyValue &params, const std::string &db);
const storage::PropertyValue &params);
/// Reopens the log file. Used for log file rotation. Thread-safe.
void ReopenLog();

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 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
@@ -314,57 +314,4 @@ std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig)
return ret;
}
#ifdef MG_ENTERPRISE
bool Auth::GrantDatabaseToUser(const std::string &db, const std::string &name) {
auto user = GetUser(name);
if (user) {
if (db == kAllDatabases) {
user->db_access().GrantAll();
} else {
user->db_access().Add(db);
}
SaveUser(*user);
return true;
}
return false;
}
bool Auth::RevokeDatabaseFromUser(const std::string &db, const std::string &name) {
auto user = GetUser(name);
if (user) {
if (db == kAllDatabases) {
user->db_access().DenyAll();
} else {
user->db_access().Remove(db);
}
SaveUser(*user);
return true;
}
return false;
}
void Auth::DeleteDatabase(const std::string &db) {
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
auto username = it->first.substr(kUserPrefix.size());
auto user = GetUser(username);
if (user) {
user->db_access().Delete(db);
SaveUser(*user);
}
}
}
bool Auth::SetMainDatabase(const std::string &db, const std::string &name) {
auto user = GetUser(name);
if (user) {
if (!user->db_access().SetDefault(db)) {
throw AuthException("Couldn't set default database '{}' for user '{}'!", db, name);
}
SaveUser(*user);
return true;
}
return false;
}
#endif
} // namespace memgraph::auth

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 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,9 +19,6 @@
#include "utils/settings.hpp"
namespace memgraph::auth {
static const constexpr char *const kAllDatabases = "*";
/**
* This class serves as the main Authentication/Authorization storage.
* It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions.
@@ -158,46 +155,6 @@ class Auth final {
*/
std::vector<User> AllUsersForRole(const std::string &rolename) const;
#ifdef MG_ENTERPRISE
/**
* @brief Revoke access to individual database for a user.
*
* @param db name of the database to revoke
* @param name user's username
* @return true on success
* @throw AuthException if unable to find or update the user
*/
bool RevokeDatabaseFromUser(const std::string &db, const std::string &name);
/**
* @brief Grant access to individual database for a user.
*
* @param db name of the database to revoke
* @param name user's username
* @return true on success
* @throw AuthException if unable to find or update the user
*/
bool GrantDatabaseToUser(const std::string &db, const std::string &name);
/**
* @brief Delete a database from all users.
*
* @param db name of the database to delete
* @throw AuthException if unable to read data
*/
void DeleteDatabase(const std::string &db);
/**
* @brief Set main database for an individual user.
*
* @param db name of the database to revoke
* @param name user's username
* @return true on success
* @throw AuthException if unable to find or update the user
*/
bool SetMainDatabase(const std::string &db, const std::string &name);
#endif
private:
// Even though the `kvstore::KVStore` class is guaranteed to be thread-safe,
// Auth is not thread-safe because modifying users and roles might require

View File

@@ -15,10 +15,8 @@
#include "auth/crypto.hpp"
#include "auth/exceptions.hpp"
#include "dbms/constants.hpp"
#include "license/license.hpp"
#include "query/constants.hpp"
#include "spdlog/spdlog.h"
#include "utils/cast.hpp"
#include "utils/logging.hpp"
#include "utils/settings.hpp"
@@ -37,31 +35,18 @@ namespace memgraph::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,
Permission::TRANSACTION_MANAGEMENT,
Permission::STORAGE_MODE,
Permission::MULTI_DATABASE_EDIT,
Permission::MULTI_DATABASE_USE};
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, Permission::TRANSACTION_MANAGEMENT,
Permission::STORAGE_MODE};
} // namespace
@@ -113,10 +98,6 @@ std::string PermissionToString(Permission permission) {
return "TRANSACTION_MANAGEMENT";
case Permission::STORAGE_MODE:
return "STORAGE_MODE";
case Permission::MULTI_DATABASE_EDIT:
return "MULTI_DATABASE_EDIT";
case Permission::MULTI_DATABASE_USE:
return "MULTI_DATABASE_USE";
}
}
@@ -463,11 +444,10 @@ Role Role::Deserialize(const nlohmann::json &data) {
auto permissions = Permissions::Deserialize(data["permissions"]);
#ifdef MG_ENTERPRISE
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
FineGrainedAccessHandler fine_grained_access_handler;
// We can have an empty fine_grained if the user was created without a valid license
if (data["fine_grained_access_handler"].is_object()) {
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
if (!data["fine_grained_access_handler"].is_object()) {
throw AuthException("Couldn't load user data!");
}
auto fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
return {data["rolename"], permissions, std::move(fine_grained_access_handler)};
}
#endif
@@ -484,82 +464,6 @@ bool operator==(const Role &first, const Role &second) {
return first.rolename_ == second.rolename_ && first.permissions_ == second.permissions_;
}
#ifdef MG_ENTERPRISE
void Databases::Add(const std::string &db) {
if (allow_all_) {
grants_dbs_.clear();
allow_all_ = false;
}
grants_dbs_.emplace(db);
denies_dbs_.erase(db);
}
void Databases::Remove(const std::string &db) {
denies_dbs_.emplace(db);
grants_dbs_.erase(db);
}
void Databases::Delete(const std::string &db) {
denies_dbs_.erase(db);
if (!allow_all_) {
grants_dbs_.erase(db);
}
// Reset if default deleted
if (default_db_ == db) {
default_db_ = "";
}
}
void Databases::GrantAll() {
allow_all_ = true;
grants_dbs_.clear();
denies_dbs_.clear();
}
void Databases::DenyAll() {
allow_all_ = false;
grants_dbs_.clear();
denies_dbs_.clear();
}
bool Databases::SetDefault(const std::string &db) {
if (!Contains(db)) return false;
default_db_ = db;
return true;
}
[[nodiscard]] bool Databases::Contains(const std::string &db) const {
return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db));
}
const std::string &Databases::GetDefault() const {
if (!Contains(default_db_)) {
throw AuthException("No access to the set default database \"{}\".", default_db_);
}
return default_db_;
}
nlohmann::json Databases::Serialize() const {
nlohmann::json data = nlohmann::json::object();
data["grants"] = grants_dbs_;
data["denies"] = denies_dbs_;
data["allow_all"] = allow_all_;
data["default"] = default_db_;
return data;
}
Databases Databases::Deserialize(const nlohmann::json &data) {
if (!data.is_object()) {
throw AuthException("Couldn't load database data!");
}
if (!data["grants"].is_structured() || !data["denies"].is_structured() || !data["allow_all"].is_boolean() ||
!data["default"].is_string()) {
throw AuthException("Couldn't load database data!");
}
return {data["allow_all"], data["grants"], data["denies"], data["default"]};
}
#endif
User::User() {}
User::User(const std::string &username) : username_(utils::ToLowerCase(username)) {}
@@ -568,12 +472,11 @@ User::User(const std::string &username, const std::string &password_hash, const
#ifdef MG_ENTERPRISE
User::User(const std::string &username, const std::string &password_hash, const Permissions &permissions,
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access)
FineGrainedAccessHandler fine_grained_access_handler)
: username_(utils::ToLowerCase(username)),
password_hash_(password_hash),
permissions_(permissions),
fine_grained_access_handler_(std::move(fine_grained_access_handler)),
database_access_(db_access) {}
fine_grained_access_handler_(std::move(fine_grained_access_handler)) {}
#endif
bool User::CheckPassword(const std::string &password) {
@@ -673,10 +576,8 @@ nlohmann::json User::Serialize() const {
#ifdef MG_ENTERPRISE
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
data["fine_grained_access_handler"] = fine_grained_access_handler_.Serialize();
data["databases"] = database_access_.Serialize();
} else {
data["fine_grained_access_handler"] = {};
data["databases"] = {};
}
#endif
// The role shouldn't be serialized here, it is stored as a foreign key.
@@ -693,21 +594,11 @@ User User::Deserialize(const nlohmann::json &data) {
auto permissions = Permissions::Deserialize(data["permissions"]);
#ifdef MG_ENTERPRISE
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
Databases db_access;
if (data["databases"].is_structured()) {
db_access = Databases::Deserialize(data["databases"]);
} else {
// Back-compatibility
spdlog::warn("User without specified database access. Given access to the default database.");
db_access.Add(dbms::kDefaultDB);
db_access.SetDefault(dbms::kDefaultDB);
if (!data["fine_grained_access_handler"].is_object()) {
throw AuthException("Couldn't load user data!");
}
FineGrainedAccessHandler fine_grained_access_handler;
// We can have an empty fine_grained if the user was created without a valid license
if (data["fine_grained_access_handler"].is_object()) {
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
}
return {data["username"], data["password_hash"], permissions, std::move(fine_grained_access_handler), db_access};
auto fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
return {data["username"], data["password_hash"], permissions, fine_grained_access_handler};
}
#endif
return {data["username"], data["password_hash"], permissions};

View File

@@ -9,13 +9,10 @@
#pragma once
#include <optional>
#include <set>
#include <string>
#include <unordered_map>
#include <json/json.hpp>
#include "dbms/constants.hpp"
#include "utils/logging.hpp"
namespace memgraph::auth {
// These permissions must have values that are applicable for usage in a
@@ -44,9 +41,7 @@ enum class Permission : uint64_t {
MODULE_WRITE = 1U << 19U,
WEBSOCKET = 1U << 20U,
TRANSACTION_MANAGEMENT = 1U << 21U,
STORAGE_MODE = 1U << 22U,
MULTI_DATABASE_EDIT = 1U << 23U,
MULTI_DATABASE_USE = 1U << 24U,
STORAGE_MODE = 1U << 22U
};
// clang-format on
@@ -242,85 +237,6 @@ class Role final {
bool operator==(const Role &first, const Role &second);
#ifdef MG_ENTERPRISE
class Databases final {
public:
Databases() : grants_dbs_({dbms::kDefaultDB}), allow_all_(false), default_db_(dbms::kDefaultDB) {}
Databases(const Databases &) = default;
Databases &operator=(const Databases &) = default;
Databases(Databases &&) noexcept = default;
Databases &operator=(Databases &&) noexcept = default;
~Databases() = default;
/**
* @brief Add database to the list of granted access. @note allow_all_ will be false after execution
*
* @param db name of the database to grant access to
*/
void Add(const std::string &db);
/**
* @brief Remove database to the list of granted access.
* @note if allow_all_ is set, the flag will remain set and the
* database will be added to the set of denied databases.
*
* @param db name of the database to grant access to
*/
void Remove(const std::string &db);
/**
* @brief Called when database is dropped. Removes it from granted (if allow_all is false) and denied set.
* @note allow_all_ is not changed
*
* @param db name of the database to grant access to
*/
void Delete(const std::string &db);
/**
* @brief Set allow_all_ to true and clears grants and denied sets.
*/
void GrantAll();
/**
* @brief Set allow_all_ to false and clears grants and denied sets.
*/
void DenyAll();
/**
* @brief Set the default database.
*/
bool SetDefault(const std::string &db);
/**
* @brief Checks if access is grated to the database.
*
* @param db name of the database
* @return true if allow_all and not denied or granted
*/
bool Contains(const std::string &db) const;
bool GetAllowAll() const { return allow_all_; }
const std::set<std::string> &GetGrants() const { return grants_dbs_; }
const std::set<std::string> &GetDenies() const { return denies_dbs_; }
const std::string &GetDefault() const;
nlohmann::json Serialize() const;
/// @throw AuthException if unable to deserialize.
static Databases Deserialize(const nlohmann::json &data);
private:
Databases(bool allow_all, std::set<std::string> grant, std::set<std::string> deny,
const std::string &default_db = dbms::kDefaultDB)
: grants_dbs_(grant), denies_dbs_(deny), allow_all_(allow_all), default_db_(default_db) {}
std::set<std::string> grants_dbs_; //!< set of databases with granted access
std::set<std::string> denies_dbs_; //!< set of databases with denied access
bool allow_all_; //!< flag to allow access to everything (denied overrides this)
std::string default_db_; //!< user's default database
};
#endif
// TODO (mferencevic): Implement password expiry.
class User final {
public:
@@ -330,7 +246,7 @@ class User final {
User(const std::string &username, const std::string &password_hash, const Permissions &permissions);
#ifdef MG_ENTERPRISE
User(const std::string &username, const std::string &password_hash, const Permissions &permissions,
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access = {});
FineGrainedAccessHandler fine_grained_access_handler);
#endif
User(const User &) = default;
User &operator=(const User &) = default;
@@ -363,11 +279,6 @@ class User final {
const Role *role() const;
#ifdef MG_ENTERPRISE
Databases &db_access() { return database_access_; }
const Databases &db_access() const { return database_access_; }
#endif
nlohmann::json Serialize() const;
/// @throw AuthException if unable to deserialize.
@@ -381,7 +292,6 @@ class User final {
Permissions permissions_;
#ifdef MG_ENTERPRISE
FineGrainedAccessHandler fine_grained_access_handler_;
Databases database_access_;
#endif
std::optional<Role> role_;
};

View File

@@ -19,7 +19,7 @@ inline constexpr uint8_t kPreamble[4] = {0x60, 0x60, 0xB0, 0x17};
enum class Signature : uint8_t {
Noop = 0x00,
Init = 0x01, // v3+ now HELLO
Init = 0x01,
LogOn = 0x6A,
LogOff = 0x6B,
AckFailure = 0x0E, // only v1

View File

@@ -11,8 +11,6 @@
#pragma once
#include <concepts>
#include <cstddef>
#include <optional>
#include <thread>
@@ -26,12 +24,8 @@
#include "communication/bolt/v1/states/executing.hpp"
#include "communication/bolt/v1/states/handshake.hpp"
#include "communication/bolt/v1/states/init.hpp"
#include "communication/bolt/v1/value.hpp"
#include "dbms/constants.hpp"
#include "dbms/global.hpp"
#include "utils/exceptions.hpp"
#include "utils/logging.hpp"
#include "utils/uuid.hpp"
namespace memgraph::communication::bolt {
@@ -54,26 +48,14 @@ class SessionException : public utils::BasicException {
* @tparam TOutputStream type of output stream that will be used
*/
template <typename TInputStream, typename TOutputStream>
class Session : public dbms::SessionInterface {
class Session {
public:
using TEncoder = Encoder<ChunkedEncoderBuffer<TOutputStream>>;
/**
* @brief Construct a new Session object
*
* @param input_stream stream to read from
* @param output_stream stream to write to
* @param impl a default high-level implementation to use (has to be defined)
*/
Session(TInputStream *input_stream, TOutputStream *output_stream)
: input_stream_(*input_stream), output_stream_(*output_stream), session_uuid_(utils::GenerateUUID()) {}
: input_stream_(*input_stream), output_stream_(*output_stream) {}
virtual ~Session() = default;
Session(const Session &) = delete;
Session &operator=(const Session &) = delete;
Session(Session &&) noexcept = delete;
Session &operator=(Session &&) noexcept = delete;
virtual ~Session() {}
/**
* Process the given `query` with `params`.
@@ -82,9 +64,7 @@ class Session : public dbms::SessionInterface {
*/
virtual std::pair<std::vector<std::string>, std::optional<int>> Interpret(
const std::string &query, const std::map<std::string, Value> &params,
const std::map<std::string, memgraph::communication::bolt::Value> &extra) = 0;
virtual void Configure(const std::map<std::string, memgraph::communication::bolt::Value> &run_time_info) = 0;
const std::map<std::string, memgraph::communication::bolt::Value> &metadata) = 0;
/**
* Put results of the processed query in the `encoder`.
@@ -106,7 +86,7 @@ class Session : public dbms::SessionInterface {
*/
virtual std::map<std::string, Value> Discard(std::optional<int> n, std::optional<int> qid) = 0;
virtual void BeginTransaction(const std::map<std::string, memgraph::communication::bolt::Value> &params) = 0;
virtual void BeginTransaction(const std::map<std::string, memgraph::communication::bolt::Value> &) = 0;
virtual void CommitTransaction() = 0;
virtual void RollbackTransaction() = 0;
@@ -119,6 +99,7 @@ class Session : public dbms::SessionInterface {
/** Return the name of the server that should be used for the Bolt INIT
* message. */
virtual std::optional<std::string> GetServerNameForInit() = 0;
/**
* Executes the session after data has been read into the buffer.
* Goes through the bolt states in order to execute commands from the client.
@@ -180,7 +161,8 @@ class Session : public dbms::SessionInterface {
}
}
// TODO: Rethink if there is a way to hide some members. At the momement all of them are public.
// TODO: Rethink if there is a way to hide some members. At the momement all
// of them are public.
TInputStream &input_stream_;
TOutputStream &output_stream_;
@@ -200,9 +182,6 @@ class Session : public dbms::SessionInterface {
Version version_;
std::string GetDatabaseName() const override = 0;
std::string UUID() const final { return session_uuid_; }
private:
void ClientFailureInvalidData() {
// Set the state to Close.
@@ -218,8 +197,6 @@ class Session : public dbms::SessionInterface {
// of the session to trigger session cleanup and socket close.
throw SessionException("Something went wrong during session execution!");
}
const std::string session_uuid_; //!< unique identifier of the session (auto generated)
};
} // namespace memgraph::communication::bolt

View File

@@ -152,7 +152,6 @@ State StateExecutingRun(TSession &session, State state) {
return RunHandlerV4<TSession>(signature, session, state, marker);
}
case 5:
memgraph::metrics::IncrementCounter(memgraph::metrics::BoltMessages);
return RunHandlerV5<TSession>(signature, session, state, marker);
default:
spdlog::trace("Unsupported bolt version:{}.{})!", session.version_.major, session.version_.minor);

View File

@@ -11,7 +11,6 @@
#pragma once
#include <exception>
#include <map>
#include <optional>
#include <string>
@@ -74,6 +73,23 @@ inline std::pair<std::string, std::string> ExceptionToErrorMessage(const std::ex
"should be in database logs."};
}
namespace helpers {
/** Extracts metadata from the extras field.
* NOTE: In order to avoid a copy, the metadata in moved.
* TODO: Update if extra field is used for anything else.
*/
inline std::map<std::string, Value> ConsumeMetadata(Value &extra) {
std::map<std::string, Value> md;
auto &md_tv = extra.ValueMap()["tx_metadata"];
if (md_tv.IsMap()) {
md = std::move(md_tv.ValueMap());
}
return md;
}
} // namespace helpers
namespace details {
template <bool is_pull, typename TSession>
@@ -208,7 +224,7 @@ State HandleRunV1(TSession &session, const State state, const Marker marker) {
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
spdlog::debug("[Run - {}] '{}'", session.GetDatabaseName(), query.ValueString());
spdlog::debug("[Run] '{}'", query.ValueString());
try {
// Interpret can throw.
@@ -266,17 +282,12 @@ State HandleRunV4(TSession &session, const State state, const Marker marker) {
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
try {
session.Configure(extra.ValueMap());
} catch (const std::exception &e) {
return HandleFailure(session, e);
}
spdlog::debug("[Run - {}] '{}'", session.GetDatabaseName(), query.ValueString());
spdlog::debug("[Run] '{}'", query.ValueString());
try {
// Interpret can throw.
const auto [header, qid] = session.Interpret(query.ValueString(), params.ValueMap(), extra.ValueMap());
const auto [header, qid] =
session.Interpret(query.ValueString(), params.ValueMap(), helpers::ConsumeMetadata(extra));
// Convert std::string to Value
std::vector<Value> vec;
std::map<std::string, Value> data;
@@ -388,8 +399,7 @@ State HandleBegin(TSession &session, const State state, const Marker marker) {
}
try {
session.Configure(extra.ValueMap());
session.BeginTransaction(extra.ValueMap());
session.BeginTransaction(helpers::ConsumeMetadata(extra));
} catch (const std::exception &e) {
return HandleFailure(session, e);
}
@@ -497,7 +507,7 @@ State HandleRoute(TSession &session, const Marker marker) {
template <typename TSession>
State HandleLogOff() {
// No arguments sent, the user just needs to reauthenticate
// Not arguments sent, the user just needs to reauthenticate
return State::Init;
}
} // namespace memgraph::communication::bolt

View File

@@ -18,7 +18,6 @@
#include "communication/bolt/v1/state.hpp"
#include "communication/bolt/v1/value.hpp"
#include "communication/exceptions.hpp"
#include "spdlog/spdlog.h"
#include "utils/likely.hpp"
#include "utils/logging.hpp"
@@ -249,9 +248,8 @@ State StateInitRunV5(TSession &session, Marker marker, Signature signature) {
}
// Stay in Init
return State::Init;
}
if (signature == Signature::LogOn) {
} else if (signature == Signature::LogOn) {
if (marker != Marker::TinyStruct1) [[unlikely]] {
spdlog::trace("Expected TinyStruct1 marker, but received 0x{:02X}!", utils::UnderlyingCast(marker));
spdlog::trace(
@@ -275,10 +273,11 @@ State StateInitRunV5(TSession &session, Marker marker, Signature signature) {
return State::Close;
}
return State::Idle;
}
spdlog::trace("Expected Init signature, but received 0x{:02X}!", utils::UnderlyingCast(signature));
return State::Close;
} else [[unlikely]] {
spdlog::trace("Expected Init signature, but received 0x{:02X}!", utils::UnderlyingCast(signature));
return State::Close;
}
}
} // namespace details

View File

@@ -112,7 +112,7 @@ struct Path {
for (uint i = 0; i < edges.size(); i++) {
const auto &e = edges[i];
const auto &v = vertices[i + 1];
UnboundedEdge unbounded_edge{e.id, e.type, e.properties, e.element_id};
UnboundedEdge unbounded_edge{e.id, e.type, e.properties};
add_element(this->edges, unbounded_edge, e.to == v.id ? 1 : -1, 1);
add_element(this->vertices, v, 1, 0);
}

View File

@@ -27,11 +27,11 @@
namespace memgraph::communication::http {
template <class TRequestHandler, typename TSessionContext>
class Listener final : public std::enable_shared_from_this<Listener<TRequestHandler, TSessionContext>> {
template <class TRequestHandler, typename TSessionData>
class Listener final : public std::enable_shared_from_this<Listener<TRequestHandler, TSessionData>> {
using tcp = boost::asio::ip::tcp;
using SessionHandler = Session<TRequestHandler, TSessionContext>;
using std::enable_shared_from_this<Listener<TRequestHandler, TSessionContext>>::shared_from_this;
using SessionHandler = Session<TRequestHandler, TSessionData>;
using std::enable_shared_from_this<Listener<TRequestHandler, TSessionData>>::shared_from_this;
public:
Listener(const Listener &) = delete;
@@ -50,9 +50,8 @@ class Listener final : public std::enable_shared_from_this<Listener<TRequestHand
tcp::endpoint GetEndpoint() const { return acceptor_.local_endpoint(); }
private:
Listener(boost::asio::io_context &ioc, TSessionContext *session_context, ServerContext *context,
tcp::endpoint endpoint)
: ioc_(ioc), session_context_(session_context), context_(context), acceptor_(ioc) {
Listener(boost::asio::io_context &ioc, TSessionData *data, ServerContext *context, tcp::endpoint endpoint)
: ioc_(ioc), data_(data), context_(context), acceptor_(ioc) {
boost::beast::error_code ec;
// Open the acceptor
@@ -96,13 +95,13 @@ class Listener final : public std::enable_shared_from_this<Listener<TRequestHand
return LogError(ec, "accept");
}
SessionHandler::Create(std::move(socket), session_context_, *context_)->Run();
SessionHandler::Create(std::move(socket), data_, *context_)->Run();
DoAccept();
}
boost::asio::io_context &ioc_;
TSessionContext *session_context_;
TSessionData *data_;
ServerContext *context_;
tcp::acceptor acceptor_;
};

View File

@@ -21,15 +21,14 @@
namespace memgraph::communication::http {
template <class TRequestHandler, typename TSessionContext>
template <class TRequestHandler, typename TSessionData>
class Server final {
using tcp = boost::asio::ip::tcp;
public:
explicit Server(io::network::Endpoint endpoint, TSessionContext *session_context, ServerContext *context)
: listener_{Listener<TRequestHandler, TSessionContext>::Create(
ioc_, session_context, context,
tcp::endpoint{boost::asio::ip::make_address(endpoint.address), endpoint.port})} {}
explicit Server(io::network::Endpoint endpoint, TSessionData *data, ServerContext *context)
: listener_{Listener<TRequestHandler, TSessionData>::Create(
ioc_, data, context, tcp::endpoint{boost::asio::ip::make_address(endpoint.address), endpoint.port})} {}
Server(const Server &) = delete;
Server(Server &&) = delete;
@@ -60,7 +59,7 @@ class Server final {
private:
boost::asio::io_context ioc_;
std::shared_ptr<Listener<TRequestHandler, TSessionContext>> listener_;
std::shared_ptr<Listener<TRequestHandler, TSessionData>> listener_;
std::optional<std::thread> background_thread_;
};
} // namespace memgraph::communication::http

View File

@@ -42,10 +42,10 @@ inline void LogError(boost::beast::error_code ec, const std::string_view what) {
spdlog::warn("HTTP session failed on {}: {}", what, ec.message());
}
template <class TRequestHandler, typename TSessionContext>
class Session : public std::enable_shared_from_this<Session<TRequestHandler, TSessionContext>> {
template <class TRequestHandler, typename TSessionData>
class Session : public std::enable_shared_from_this<Session<TRequestHandler, TSessionData>> {
using tcp = boost::asio::ip::tcp;
using std::enable_shared_from_this<Session<TRequestHandler, TSessionContext>>::shared_from_this;
using std::enable_shared_from_this<Session<TRequestHandler, TSessionData>>::shared_from_this;
public:
template <typename... Args>
@@ -72,7 +72,7 @@ class Session : public std::enable_shared_from_this<Session<TRequestHandler, TSe
using PlainSocket = boost::beast::tcp_stream;
using SSLSocket = boost::beast::ssl_stream<boost::beast::tcp_stream>;
explicit Session(tcp::socket &&socket, TSessionContext *data, ServerContext &context)
explicit Session(tcp::socket &&socket, TSessionData *data, ServerContext &context)
: stream_(CreateSocket(std::move(socket), context)),
handler_(data),
strand_{boost::asio::make_strand(GetExecutor())} {}

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -39,7 +39,7 @@ namespace memgraph::communication {
* second, checks all sessions for expiration and shuts them down if they have
* expired.
*/
template <class TSession, class TSessionContext>
template <class TSession, class TSessionData>
class Listener final {
private:
// The maximum number of events handled per execution thread is 1. This is
@@ -48,10 +48,10 @@ class Listener final {
// can take a long time.
static const int kMaxEvents = 1;
using SessionHandler = Session<TSession, TSessionContext>;
using SessionHandler = Session<TSession, TSessionData>;
public:
Listener(TSessionContext *data, ServerContext *context, int inactivity_timeout_sec, const std::string &service_name,
Listener(TSessionData *data, ServerContext *context, int inactivity_timeout_sec, const std::string &service_name,
size_t workers_count)
: data_(data),
alive_(false),
@@ -259,7 +259,7 @@ class Listener final {
io::network::Epoll epoll_;
TSessionContext *data_;
TSessionData *data_;
utils::SpinLock lock_;
std::vector<std::unique_ptr<SessionHandler>> sessions_;

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -46,10 +46,10 @@ namespace memgraph::communication {
* @tparam TSession the server can handle different Sessions, each session
* represents a different protocol so the same network infrastructure
* can be used for handling different protocols
* @tparam TSessionContext the class with objects that will be forwarded to the
* @tparam TSessionData the class with objects that will be forwarded to the
* session
*/
template <typename TSession, typename TSessionContext>
template <typename TSession, typename TSessionData>
class Server final {
public:
using Socket = io::network::Socket;
@@ -58,12 +58,12 @@ class Server final {
* Constructs and binds server to endpoint, operates on session data and
* invokes workers_count workers
*/
Server(const io::network::Endpoint &endpoint, TSessionContext *session_context, ServerContext *context,
Server(const io::network::Endpoint &endpoint, TSessionData *session_data, ServerContext *context,
int inactivity_timeout_sec, const std::string &service_name,
size_t workers_count = std::thread::hardware_concurrency())
: alive_(false),
endpoint_(endpoint),
listener_(session_context, context, inactivity_timeout_sec, service_name, workers_count),
listener_(session_data, context, inactivity_timeout_sec, service_name, workers_count),
service_name_(service_name) {}
~Server() {
@@ -156,7 +156,7 @@ class Server final {
Socket socket_;
io::network::Endpoint endpoint_;
Listener<TSession, TSessionContext> listener_;
Listener<TSession, TSessionData> listener_;
const std::string service_name_;
};

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -69,10 +69,10 @@ class OutputStream final {
* sessions. It handles socket ownership, inactivity timeout and protocol
* wrapping.
*/
template <class TSession, class TSessionContext>
template <class TSession, class TSessionData>
class Session final {
public:
Session(io::network::Socket &&socket, TSessionContext *data, ServerContext *context, int inactivity_timeout_sec)
Session(io::network::Socket &&socket, TSessionData *data, ServerContext *context, int inactivity_timeout_sec)
: socket_(std::move(socket)),
output_stream_([this](const uint8_t *data, size_t len, bool have_more) { return Write(data, len, have_more); }),
session_(data, socket_.endpoint(), input_buffer_.read_end(), &output_stream_),

View File

@@ -36,11 +36,11 @@
namespace memgraph::communication::v2 {
template <class TSession, class TSessionContext>
class Listener final : public std::enable_shared_from_this<Listener<TSession, TSessionContext>> {
template <class TSession, class TSessionData>
class Listener final : public std::enable_shared_from_this<Listener<TSession, TSessionData>> {
using tcp = boost::asio::ip::tcp;
using SessionHandler = Session<TSession, TSessionContext>;
using std::enable_shared_from_this<Listener<TSession, TSessionContext>>::shared_from_this;
using SessionHandler = Session<TSession, TSessionData>;
using std::enable_shared_from_this<Listener<TSession, TSessionData>>::shared_from_this;
public:
Listener(const Listener &) = delete;
@@ -59,10 +59,10 @@ class Listener final : public std::enable_shared_from_this<Listener<TSession, TS
bool IsRunning() const noexcept { return alive_.load(std::memory_order_relaxed); }
private:
Listener(boost::asio::io_context &io_context, TSessionContext *session_context, ServerContext *server_context,
Listener(boost::asio::io_context &io_context, TSessionData *data, ServerContext *server_context,
tcp::endpoint &endpoint, const std::string_view service_name, const uint64_t inactivity_timeout_sec)
: io_context_(io_context),
session_context_(session_context),
data_(data),
server_context_(server_context),
acceptor_(io_context_),
endpoint_{endpoint},
@@ -111,8 +111,8 @@ class Listener final : public std::enable_shared_from_this<Listener<TSession, TS
return OnError(ec, "accept");
}
auto session = SessionHandler::Create(std::move(socket), session_context_, *server_context_, endpoint_,
inactivity_timeout_, service_name_);
auto session = SessionHandler::Create(std::move(socket), data_, *server_context_, endpoint_, inactivity_timeout_,
service_name_);
session->Start();
DoAccept();
}
@@ -123,7 +123,7 @@ class Listener final : public std::enable_shared_from_this<Listener<TSession, TS
}
boost::asio::io_context &io_context_;
TSessionContext *session_context_;
TSessionData *data_;
ServerContext *server_context_;
tcp::acceptor acceptor_;

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -60,27 +60,27 @@ using ServerEndpoint = boost::asio::ip::tcp::endpoint;
* @tparam TSession the server can handle different Sessions, each session
* represents a different protocol so the same network infrastructure
* can be used for handling different protocols
* @tparam TSessionContext the class with objects that will be forwarded to the
* @tparam TSessionData the class with objects that will be forwarded to the
* session
*/
template <typename TSession, typename TSessionContext>
template <typename TSession, typename TSessionData>
class Server final {
using ServerHandler = Server<TSession, TSessionContext>;
using ServerHandler = Server<TSession, TSessionData>;
public:
/**
* Constructs and binds server to endpoint, operates on session data and
* invokes workers_count workers
*/
Server(ServerEndpoint &endpoint, TSessionContext *session_context, ServerContext *server_context,
Server(ServerEndpoint &endpoint, TSessionData *session_data, ServerContext *server_context,
const int inactivity_timeout_sec, const std::string_view service_name,
size_t workers_count = std::thread::hardware_concurrency())
: endpoint_{endpoint},
service_name_{service_name},
context_thread_pool_{workers_count},
listener_{Listener<TSession, TSessionContext>::Create(context_thread_pool_.GetIOContext(), session_context,
server_context, endpoint_, service_name_,
inactivity_timeout_sec)} {}
listener_{Listener<TSession, TSessionData>::Create(context_thread_pool_.GetIOContext(), session_data,
server_context, endpoint_, service_name_,
inactivity_timeout_sec)} {}
~Server() { MG_ASSERT(!IsRunning(), "Server wasn't shutdown properly"); }
@@ -122,7 +122,7 @@ class Server final {
std::string service_name_;
IOContextThreadPool context_thread_pool_;
std::shared_ptr<Listener<TSession, TSessionContext>> listener_;
std::shared_ptr<Listener<TSession, TSessionData>> listener_;
};
} // namespace memgraph::communication::v2

View File

@@ -16,12 +16,10 @@
#include <cstdint>
#include <cstring>
#include <deque>
#include <exception>
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <variant>
@@ -43,11 +41,9 @@
#include <boost/beast/websocket/rfc6455.hpp>
#include <boost/system/detail/error_code.hpp>
#include "communication/bolt/v1/session.hpp"
#include "communication/buffer.hpp"
#include "communication/context.hpp"
#include "communication/exceptions.hpp"
#include "dbms/global.hpp"
#include "utils/event_counter.hpp"
#include "utils/logging.hpp"
#include "utils/on_scope_exit.hpp"
@@ -99,10 +95,10 @@ class OutputStream final {
* Websocket Sessions. It handles socket ownership, inactivity timeout and protocol
* wrapping.
*/
template <typename TSession, typename TSessionContext>
class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TSession, TSessionContext>> {
template <typename TSession, typename TSessionData>
class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TSession, TSessionData>> {
using WebSocket = boost::beast::websocket::stream<boost::beast::tcp_stream>;
using std::enable_shared_from_this<WebsocketSession<TSession, TSessionContext>>::shared_from_this;
using std::enable_shared_from_this<WebsocketSession<TSession, TSessionData>>::shared_from_this;
public:
template <typename... Args>
@@ -110,17 +106,6 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
return std::shared_ptr<WebsocketSession>(new WebsocketSession(std::forward<Args>(args)...));
}
#ifdef MG_ENTERPRISE
~WebsocketSession() { session_context_->Delete(session_); }
#else
~WebsocketSession() = default;
#endif
WebsocketSession(const WebsocketSession &) = delete;
WebsocketSession &operator=(const WebsocketSession &) = delete;
WebsocketSession(WebsocketSession &&) noexcept = delete;
WebsocketSession &operator=(WebsocketSession &&) noexcept = delete;
// Start the asynchronous accept operation
template <class Body, class Allocator>
void DoAccept(boost::beast::http::request<Body, boost::beast::http::basic_fields<Allocator>> req) {
@@ -166,20 +151,15 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
private:
// Take ownership of the socket
explicit WebsocketSession(tcp::socket &&socket, TSessionContext *session_context, tcp::endpoint endpoint,
explicit WebsocketSession(tcp::socket &&socket, TSessionData *data, tcp::endpoint endpoint,
std::string_view service_name)
: ws_(std::move(socket)),
strand_{boost::asio::make_strand(ws_.get_executor())},
output_stream_([this](const uint8_t *data, size_t len, bool /*have_more*/) { return Write(data, len); }),
session_{*session_context, endpoint, input_buffer_.read_end(), &output_stream_},
session_context_{session_context},
session_(data, endpoint, input_buffer_.read_end(), &output_stream_),
endpoint_{endpoint},
remote_endpoint_{ws_.next_layer().socket().remote_endpoint()},
service_name_{service_name} {
#ifdef MG_ENTERPRISE
session_context_->Register(session_);
#endif
}
service_name_{service_name} {}
void OnAccept(boost::beast::error_code ec) {
if (ec) {
@@ -262,7 +242,6 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
communication::Buffer input_buffer_;
OutputStream output_stream_;
TSession session_;
TSessionContext *session_context_;
tcp::endpoint endpoint_;
tcp::endpoint remote_endpoint_;
std::string_view service_name_;
@@ -274,11 +253,11 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
* Sessions. It handles socket ownership, inactivity timeout and protocol
* wrapping.
*/
template <typename TSession, typename TSessionContext>
class Session final : public std::enable_shared_from_this<Session<TSession, TSessionContext>> {
template <typename TSession, typename TSessionData>
class Session final : public std::enable_shared_from_this<Session<TSession, TSessionData>> {
using TCPSocket = tcp::socket;
using SSLSocket = boost::asio::ssl::stream<TCPSocket>;
using std::enable_shared_from_this<Session<TSession, TSessionContext>>::shared_from_this;
using std::enable_shared_from_this<Session<TSession, TSessionData>>::shared_from_this;
public:
template <typename... Args>
@@ -286,16 +265,11 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
return std::shared_ptr<Session>(new Session(std::forward<Args>(args)...));
}
#ifdef MG_ENTERPRISE
~Session() { session_context_->Delete(session_); }
#else
~Session() = default;
#endif
Session(const Session &) = delete;
Session(Session &&) = delete;
Session &operator=(const Session &) = delete;
Session &operator=(Session &&) = delete;
~Session() = default;
bool Start() {
if (execution_active_) {
@@ -360,23 +334,18 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
}
private:
explicit Session(tcp::socket &&socket, TSessionContext *session_context, ServerContext &server_context,
tcp::endpoint endpoint, const std::chrono::seconds inactivity_timeout_sec,
std::string_view service_name)
explicit Session(tcp::socket &&socket, TSessionData *data, ServerContext &server_context, tcp::endpoint endpoint,
const std::chrono::seconds inactivity_timeout_sec, std::string_view service_name)
: socket_(CreateSocket(std::move(socket), server_context)),
strand_{boost::asio::make_strand(GetExecutor())},
output_stream_([this](const uint8_t *data, size_t len, bool have_more) { return Write(data, len, have_more); }),
session_{*session_context, endpoint, input_buffer_.read_end(), &output_stream_},
session_context_{session_context},
session_(data, endpoint, input_buffer_.read_end(), &output_stream_),
data_{data},
endpoint_{endpoint},
remote_endpoint_{GetRemoteEndpoint()},
service_name_{service_name},
timeout_seconds_(inactivity_timeout_sec),
timeout_timer_(GetExecutor()) {
#ifdef MG_ENTERPRISE
// TODO Try to remove Register (see comment at SessionInterface declaration)
session_context_->Register(session_);
#endif
ExecuteForSocket([](auto &&socket) {
socket.lowest_layer().set_option(tcp::no_delay(true)); // enable PSH
socket.lowest_layer().set_option(boost::asio::socket_base::keep_alive(true)); // enable SO_KEEPALIVE
@@ -427,8 +396,7 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
spdlog::info("Switching {} to websocket connection", remote_endpoint_);
if (std::holds_alternative<TCPSocket>(socket_)) {
auto sock = std::get<TCPSocket>(std::move(socket_));
WebsocketSession<TSession, TSessionContext>::Create(std::move(sock), session_context_, endpoint_,
service_name_)
WebsocketSession<TSession, TSessionData>::Create(std::move(sock), data_, endpoint_, service_name_)
->DoAccept(parser.release());
execution_active_ = false;
return;
@@ -567,7 +535,7 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
communication::Buffer input_buffer_;
OutputStream output_stream_;
TSession session_;
TSessionContext *session_context_;
TSessionData *data_;
tcp::endpoint endpoint_;
tcp::endpoint remote_endpoint_;
std::string_view service_name_;

View File

@@ -1,18 +0,0 @@
// Copyright 2023 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
namespace memgraph::dbms {
constexpr static const char *kDefaultDB = "memgraph"; //!< Name of the default database
} // namespace memgraph::dbms

View File

@@ -1,110 +0,0 @@
// Copyright 2023 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 <concepts>
#include <cstdint>
#include <string>
#include "utils/exceptions.hpp"
namespace memgraph::dbms {
enum class DeleteError : uint8_t {
DEFAULT_DB,
USING,
NON_EXISTENT,
FAIL,
DISK_FAIL,
};
enum class NewError : uint8_t {
NO_CONFIGS,
EXISTS,
DEFUNCT,
GENERIC,
};
enum class SetForResult : uint8_t {
SUCCESS,
ALREADY_SET,
FAIL,
};
/**
* UnknownSession Exception
*
* Used to indicate that an unknown session was used.
*/
class UnknownSessionException : public utils::BasicException {
public:
using utils::BasicException::BasicException;
};
/**
* UnknownDatabase Exception
*
* Used to indicate that an unknown database was used.
*/
class UnknownDatabaseException : public utils::BasicException {
public:
using utils::BasicException::BasicException;
};
/**
* @brief Session interface used by the DBMS to handle the the active sessions.
* @todo Try to remove this dependency from SessionContextHandler. OnDelete could be removed, as it only does an assert.
* OnChange could be removed if SetFor returned the pointer and the called then handled the OnChange execution.
* However, the interface is very useful to decouple the interpreter's query execution and the sessions themselves.
*/
class SessionInterface {
public:
SessionInterface() = default;
virtual ~SessionInterface() = default;
SessionInterface(const SessionInterface &) = default;
SessionInterface &operator=(const SessionInterface &) = default;
SessionInterface(SessionInterface &&) noexcept = default;
SessionInterface &operator=(SessionInterface &&) noexcept = default;
/**
* @brief Return the unique string identifying the session.
*
* @return std::string
*/
virtual std::string UUID() const = 0;
/**
* @brief Return the currently active database.
*
* @return std::string
*/
virtual std::string GetDatabaseName() const = 0;
#ifdef MG_ENTERPRISE
/**
* @brief Gets called on database change.
*
* @return SetForResult enum (SUCCESS, ALREADY_SET or FAIL)
*/
virtual dbms::SetForResult OnChange(const std::string &) = 0;
/**
* @brief Callback that gets called on database delete (drop).
*
* @return true on success
*/
virtual bool OnDelete(const std::string &) = 0;
#endif
};
} // namespace memgraph::dbms

View File

@@ -1,142 +0,0 @@
// Copyright 2023 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 <filesystem>
#include <memory>
#include <optional>
#include <string_view>
#include <unordered_map>
#include "global.hpp"
#include "utils/result.hpp"
#include "utils/sync_ptr.hpp"
namespace memgraph::dbms {
/**
* @brief Generic multi-database content handler.
*
* @tparam TContext
* @tparam TConfig
*/
template <typename TContext, typename TConfig>
class Handler {
public:
using NewResult = utils::BasicResult<NewError, std::shared_ptr<TContext>>;
/**
* @brief Empty Handler constructor.
*
*/
Handler() {}
/**
* @brief Generate a new context and corresponding configuration.
*
* @tparam T1 Variadic template of context constructor arguments
* @tparam T2 Variadic template of config constructor arguments
* @param name Name associated with the new context/config pair
* @param args1 Arguments passed (as a tuple) to the context constructor
* @param args2 Arguments passed (as a tuple) to the config constructor
* @return NewResult
*/
template <typename... T1, typename... T2>
NewResult New(std::string name, std::tuple<T1...> args1, std::tuple<T2...> args2) {
return New_(name, args1, args2, std::make_index_sequence<sizeof...(T1)>{},
std::make_index_sequence<sizeof...(T2)>{});
}
/**
* @brief Get pointer to context.
*
* @param name Name associated with the wanted context
* @return std::optional<std::shared_ptr<TContext>>
*/
std::optional<std::shared_ptr<TContext>> Get(const std::string &name) {
if (auto search = items_.find(name); search != items_.end()) {
return search->second.get();
}
return {};
}
/**
* @brief Get the config.
*
* @param name Name associated with the wanted config
* @return std::optional<TConfig>
*/
std::optional<TConfig> GetConfig(const std::string &name) const {
if (auto search = items_.find(name); search != items_.end()) {
return search->second.config();
}
return {};
}
/**
* @brief Delete the context/config pair associated with the name.
*
* @param name Name associated with the context/config pair to delete
* @return true on success
*/
bool Delete(const std::string &name) {
if (auto itr = items_.find(name); itr != items_.end()) {
itr->second.DestroyAndSync();
items_.erase(itr);
return true;
}
return false;
}
/**
* @brief Check if a name is already used.
*
* @param name Name to check
* @return true if a context/config pair is already associated with the name
*/
bool Has(const std::string &name) const { return items_.find(name) != items_.end(); }
auto begin() { return items_.begin(); }
auto end() { return items_.end(); }
auto begin() const { return items_.begin(); }
auto end() const { return items_.end(); }
auto cbegin() const { return items_.cbegin(); }
auto cend() const { return items_.cend(); }
private:
/**
* @brief Lower level handler that hides some ugly code.
*
* @tparam T1 Variadic template of context constructor arguments
* @tparam T2 Variadic template of config constructor arguments
* @tparam I1 List of indexes associated with the first tuple
* @tparam I2 List of indexes associated with the second tuple
*/
template <typename... T1, typename... T2, std::size_t... I1, std::size_t... I2>
NewResult New_(std::string name, std::tuple<T1...> &args1, std::tuple<T2...> &args2,
std::integer_sequence<std::size_t, I1...> /*not-used*/,
std::integer_sequence<std::size_t, I2...> /*not-used*/) {
// Make sure the emplace will succeed, since we don't want to create temporary objects that could break something
if (!Has(name)) {
auto [itr, _] = items_.emplace(std::piecewise_construct, std::forward_as_tuple(name),
std::forward_as_tuple(TConfig{std::forward<T1>(std::get<I1>(args1))...},
std::forward<T2>(std::get<I2>(args2))...));
return itr->second.get();
}
spdlog::info("Item with name \"{}\" already exists.", name);
return NewError::EXISTS;
}
std::unordered_map<std::string, utils::SyncPtr<TContext, TConfig>> items_; //!< map to all active items
};
} // namespace memgraph::dbms

View File

@@ -1,106 +0,0 @@
// Copyright 2023 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
#ifdef MG_ENTERPRISE
#include "global.hpp"
#include "query/auth_checker.hpp"
#include "query/config.hpp"
#include "query/interpreter.hpp"
#include "storage/v2/storage.hpp"
#include "handler.hpp"
namespace memgraph::dbms {
/**
* @brief Simple class that adds useful information to the query's InterpreterContext
*
* @tparam T Multi-database handler type
*/
template <typename T>
class ExpandedInterpContext : public query::InterpreterContext {
public:
template <typename... TArgs>
explicit ExpandedInterpContext(T &ref, TArgs &&...args)
: query::InterpreterContext(std::forward<TArgs>(args)...), sc_handler_(ref) {}
T &sc_handler_; //!< Multi-database/SessionContext handler (used in some queries)
};
/**
* @brief Simple structure that expands on the query's InterpreterConfig
*
*/
struct ExpandedInterpConfig {
storage::Config storage_config; //!< Storage configuration
query::InterpreterConfig interp_config; //!< Interpreter configuration
};
/**
* @brief Multi-database interpreter context handler
*
* @tparam TSCHandler High-level multi-database/SessionContext handler type
*/
template <typename TSCHandler>
class InterpContextHandler : public Handler<ExpandedInterpContext<TSCHandler>, ExpandedInterpConfig> {
public:
using InterpContextT = ExpandedInterpContext<TSCHandler>;
using HandlerT = Handler<InterpContextT, ExpandedInterpConfig>;
/**
* @brief Generate a new interpreter context associated with the passed name.
*
* @param name Name associating the new interpreter context
* @param sc_handler Multi-database/SessionContext handler used (some queries might use it)
* @param db Storage associated with the interpreter context
* @param config Interpreter's configuration
* @param dir Directory used by the interpreter
* @param auth_handler AuthQueryHandler used
* @param auth_checker AuthChecker used
* @return HandlerT::NewResult
*/
typename HandlerT::NewResult New(const std::string &name, TSCHandler &sc_handler, storage::Config storage_config,
const query::InterpreterConfig &interpreter_config,
query::AuthQueryHandler &auth_handler, query::AuthChecker &auth_checker) {
// Check if compatible with the existing interpreters
if (std::any_of(HandlerT::cbegin(), HandlerT::cend(), [&](const auto &elem) {
const auto &config = elem.second.config().storage_config;
return config.durability.storage_directory == storage_config.durability.storage_directory;
})) {
spdlog::info("Tried to generate a new context using claimed directory and/or storage.");
return NewError::EXISTS;
}
const auto dir = storage_config.durability.storage_directory;
storage_config.name = name; // Set storage id via config
return HandlerT::New(
name, std::forward_as_tuple(storage_config, interpreter_config),
std::forward_as_tuple(sc_handler, storage_config, interpreter_config, dir, &auth_handler, &auth_checker));
}
/**
* @brief All currently active storage.
*
* @return std::vector<std::string>
*/
std::vector<std::string> All() const {
std::vector<std::string> res;
res.reserve(std::distance(HandlerT::cbegin(), HandlerT::cend()));
std::for_each(HandlerT::cbegin(), HandlerT::cend(), [&](const auto &elem) { res.push_back(elem.first); });
return res;
}
};
} // namespace memgraph::dbms
#endif

View File

@@ -1,61 +0,0 @@
// Copyright 2023 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 "auth/auth.hpp"
#include "query/interpreter.hpp"
#include "storage/v2/storage.hpp"
#include "utils/synchronized.hpp"
#if MG_ENTERPRISE
#include "audit/log.hpp"
#endif
namespace memgraph::dbms {
/**
* @brief Structure encapsulating storage and interpreter context.
*
* @note Each session contains a copy.
*/
struct SessionContext {
// Explicit constructor here to ensure that pointers to all objects are
// supplied.
SessionContext(std::shared_ptr<memgraph::query::InterpreterContext> interpreter_context, std::string run,
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth
#ifdef MG_ENTERPRISE
,
memgraph::audit::Log *audit_log
#endif
)
: interpreter_context(interpreter_context),
run_id(run),
auth(auth)
#ifdef MG_ENTERPRISE
,
audit_log(audit_log)
#endif
{
}
std::shared_ptr<memgraph::query::InterpreterContext> interpreter_context;
std::string run_id;
// std::shared_ptr<AuthContext> auth_context;
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth;
#ifdef MG_ENTERPRISE
memgraph::audit::Log *audit_log;
#endif
};
} // namespace memgraph::dbms

View File

@@ -1,603 +0,0 @@
// Copyright 2023 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 <algorithm>
#include <concepts>
#include <cstdint>
#include <filesystem>
#include <memory>
#include <mutex>
#include <optional>
#include <ostream>
#include <stdexcept>
#include <system_error>
#include <unordered_map>
#include "constants.hpp"
#include "global.hpp"
#include "interp_handler.hpp"
#include "query/auth_checker.hpp"
#include "query/config.hpp"
#include "query/interpreter.hpp"
#include "session_context.hpp"
#include "spdlog/spdlog.h"
#include "storage/v2/durability/durability.hpp"
#include "storage/v2/durability/paths.hpp"
#include "utils/exceptions.hpp"
#include "utils/file.hpp"
#include "utils/logging.hpp"
#include "utils/result.hpp"
#include "utils/rw_lock.hpp"
#include "utils/synchronized.hpp"
#include "utils/uuid.hpp"
#include "handler.hpp"
namespace memgraph::dbms {
#ifdef MG_ENTERPRISE
using DeleteResult = utils::BasicResult<DeleteError>;
/**
* @brief Multi-database session contexts handler.
*/
class SessionContextHandler {
public:
using StorageT = storage::Storage;
using StorageConfigT = storage::Config;
using LockT = utils::RWLock;
using NewResultT = utils::BasicResult<NewError, SessionContext>;
struct Config {
StorageConfigT storage_config; //!< Storage configuration
query::InterpreterConfig interp_config; //!< Interpreter context configuration
std::function<void(utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock> *,
std::unique_ptr<query::AuthQueryHandler> &, std::unique_ptr<query::AuthChecker> &)>
glue_auth;
};
struct Statistics {
uint64_t num_vertex; //!< Sum of vertexes in every database
uint64_t num_edges; //!< Sum of edges in every database
uint64_t num_databases; //! number of isolated databases
};
/**
* @brief Initialize the handler.
*
* @param audit_log pointer to the audit logger (ENTERPRISE only)
* @param configs storage and interpreter configurations
* @param recovery_on_startup restore databases (and its content) and authentication data
*/
SessionContextHandler(memgraph::audit::Log &audit_log, Config configs, bool recovery_on_startup, bool delete_on_drop)
: lock_{utils::RWLock::Priority::READ},
default_configs_(configs),
run_id_{utils::GenerateUUID()},
audit_log_(&audit_log),
delete_on_drop_(delete_on_drop) {
const auto &root = configs.storage_config.durability.storage_directory;
utils::EnsureDirOrDie(root);
// Verify that the user that started the process is the same user that is
// the owner of the storage directory.
storage::durability::VerifyStorageDirectoryOwnerAndProcessUserOrDie(root);
// Create the lock file and open a handle to it. This will crash the
// database if it can't open the file for writing or if any other process is
// holding the file opened.
lock_file_path_ = root / ".lock";
lock_file_handle_.Open(lock_file_path_, utils::OutputFile::Mode::OVERWRITE_EXISTING);
MG_ASSERT(lock_file_handle_.AcquireLock(),
"Couldn't acquire lock on the storage directory {}"
"!\nAnother Memgraph process is currently running with the same "
"storage directory, please stop it first before starting this "
"process!",
root);
// TODO: Figure out if this is needed/wanted
// Clear auth database since we are not recovering
// if (!recovery_on_startup) {
// const auto &auth_dir = root / "auth";
// // Backup if auth present
// if (utils::DirExists(auth_dir)) {
// auto backup_dir = root / storage::durability::kBackupDirectory;
// std::error_code error_code;
// utils::EnsureDirOrDie(backup_dir);
// std::error_code ec;
// const auto now = std::chrono::system_clock::now();
// std::ostringstream os;
// os << now.time_since_epoch().count();
// std::filesystem::rename(auth_dir, backup_dir / ("auth-" + os.str()), ec);
// MG_ASSERT(!ec, "Couldn't backup auth directory because of: {}", ec.message());
// spdlog::warn(
// "Since Memgraph was not supposed to recover on startup the authentication files will be "
// "overwritten. To prevent important data loss, Memgraph has stored those files into .backup directory "
// "inside the storage directory.");
// }
// // Clear
// if (std::filesystem::exists(auth_dir)) {
// std::filesystem::remove_all(auth_dir);
// }
// }
// Lazy initialization of auth_
auth_ = std::make_unique<utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock>>(root / "auth");
configs.glue_auth(auth_.get(), auth_handler_, auth_checker_);
// TODO: Decouple storage config from dbms config
// TODO: Save individual db configs inside the kvstore and restore from there
storage::UpdatePaths(default_configs_->storage_config,
default_configs_->storage_config.durability.storage_directory / "databases");
const auto &db_dir = default_configs_->storage_config.durability.storage_directory;
const auto durability_dir = db_dir / ".durability";
utils::EnsureDirOrDie(db_dir);
utils::EnsureDirOrDie(durability_dir);
durability_ = std::make_unique<kvstore::KVStore>(durability_dir);
// Generate the default database
MG_ASSERT(!NewDefault_().HasError(), "Failed while creating the default DB.");
// Recover previous databases
if (recovery_on_startup) {
for (const auto &[name, _] : *durability_) {
if (name == kDefaultDB) continue; // Already set
spdlog::info("Restoring database {}.", name);
MG_ASSERT(!New_(name).HasError(), "Failed while creating database {}.", name);
spdlog::info("Database {} restored.", name);
}
} else { // Clear databases from the durability list and auth
auto locked_auth = auth_->Lock();
for (const auto &[name, _] : *durability_) {
if (name == kDefaultDB) continue;
locked_auth->DeleteDatabase(name);
durability_->Delete(name);
}
}
}
void Shutdown() {
for (auto &ic : interp_handler_) memgraph::query::Shutdown(ic.second.get().get());
}
/**
* @brief Create a new SessionContext associated with the "name" database
*
* @param name name of the database
* @return NewResultT context on success, error on failure
*/
NewResultT New(const std::string &name) {
std::lock_guard<LockT> wr(lock_);
return New_(name, name);
}
/**
* @brief Get the context associated with the "name" database
*
* @param name
* @return SessionContext
* @throw UnknownDatabaseException if getting unknown database
*/
SessionContext Get(const std::string &name) {
std::shared_lock<LockT> rd(lock_);
return Get_(name);
}
/**
* @brief Set the undelying database for a particular session.
*
* @param uuid unique session identifier
* @param db_name unique database name
* @return SetForResult enum
* @throws UnknownDatabaseException, UnknownSessionException or anything OnChange throws
*/
SetForResult SetFor(const std::string &uuid, const std::string &db_name) {
std::shared_lock<LockT> rd(lock_);
(void)Get_(
db_name); // throws if db doesn't exist (TODO: Better to pass it via OnChange - but injecting dependency)
try {
auto &s = sessions_.at(uuid);
return s.OnChange(db_name);
} catch (std::out_of_range &) {
throw UnknownSessionException("Unknown session \"{}\"", uuid);
}
}
/**
* @brief Set the undelying database from a session itself. SessionContext handler.
*
* @param db_name unique database name
* @param handler function that gets called in place with the appropriate SessionContext
* @return SetForResult enum
*/
template <typename THandler>
requires std::invocable<THandler, SessionContext> SetForResult SetInPlace(const std::string &db_name,
THandler handler) {
std::shared_lock<LockT> rd(lock_);
return handler(Get_(db_name));
}
/**
* @brief Call void handler under a shared lock.
*
* @param handler function that gets called in place
*/
template <typename THandler>
requires std::invocable<THandler>
void CallInPlace(THandler handler) {
std::shared_lock<LockT> rd(lock_);
handler();
}
/**
* @brief Register an active session (used to handle callbacks).
*
* @param session
* @return true on success
*/
bool Register(SessionInterface &session) {
std::lock_guard<LockT> wr(lock_);
auto [_, success] = sessions_.emplace(session.UUID(), session);
return success;
}
/**
* @brief Delete a session.
*
* @param session
*/
bool Delete(const SessionInterface &session) {
std::lock_guard<LockT> wr(lock_);
return sessions_.erase(session.UUID()) > 0;
}
/**
* @brief Delete database.
*
* @param db_name database name
* @return DeleteResult error on failure
*/
DeleteResult Delete(const std::string &db_name) {
std::lock_guard<LockT> wr(lock_);
if (db_name == kDefaultDB) {
// MSG cannot delete the default db
return DeleteError::DEFAULT_DB;
}
// Check if db exists
try {
auto sc = Get_(db_name);
// Check if a session is using the db
if (!sc.interpreter_context->interpreters->empty()) {
return DeleteError::USING;
}
} catch (UnknownDatabaseException &) {
return DeleteError::NON_EXISTENT;
}
// High level handlers
for (auto &[_, s] : sessions_) {
if (!s.OnDelete(db_name)) {
spdlog::error("Partial failure while deleting database \"{}\".", db_name);
defunct_dbs_.emplace(db_name);
return DeleteError::FAIL;
}
}
// Low level handlers
const auto storage_path = StorageDir_(db_name);
MG_ASSERT(storage_path, "Missing storage for {}", db_name);
if (!interp_handler_.Delete(db_name)) {
spdlog::error("Partial failure while deleting database \"{}\".", db_name);
defunct_dbs_.emplace(db_name);
return DeleteError::FAIL;
}
// Remove from auth
auth_->Lock()->DeleteDatabase(db_name);
// Remove from durability list
if (durability_) durability_->Delete(db_name);
// Delete disk storage
if (delete_on_drop_) {
std::error_code ec;
(void)std::filesystem::remove_all(*storage_path, ec);
if (ec) {
spdlog::error("Failed to clean disk while deleting database \"{}\".", db_name);
defunct_dbs_.emplace(db_name);
return DeleteError::DISK_FAIL;
}
}
// Delete from defunct_dbs_ (in case a second delete call was successful)
defunct_dbs_.erase(db_name);
return {}; // Success
}
/**
* @brief Set the default configurations.
*
* @param configs storage, interpreter and authorization configurations
*/
void SetDefaultConfigs(Config configs) {
std::lock_guard<LockT> wr(lock_);
default_configs_ = configs;
}
/**
* @brief Get the default configurations.
*
* @return std::optional<Config>
*/
std::optional<Config> GetDefaultConfigs() const {
std::shared_lock<LockT> rd(lock_);
return default_configs_;
}
/**
* @brief Return all active databases.
*
* @return std::vector<std::string>
*/
std::vector<std::string> All() const {
std::shared_lock<LockT> rd(lock_);
return interp_handler_.All();
}
/**
* @brief Return the number of vertex across all databases.
*
* @return uint64_t
*/
Statistics Info() const {
// TODO: Handle overflow
uint64_t nv = 0;
uint64_t ne = 0;
std::shared_lock<LockT> rd(lock_);
const uint64_t ndb = std::distance(interp_handler_.cbegin(), interp_handler_.cend());
for (const auto &ic : interp_handler_) {
const auto &info = ic.second.get()->db->GetInfo();
nv += info.vertex_count;
ne += info.edge_count;
}
return {nv, ne, ndb};
}
/**
* @brief Return the currently active database for a particular session.
*
* @param uuid session's unique identifier
* @return std::string name of the database
* @throw
*/
std::string Current(const std::string &uuid) const {
std::shared_lock<LockT> rd(lock_);
return sessions_.at(uuid).GetDatabaseName();
}
/**
* @brief Restore triggers for all currently defined databases.
* @note: Triggers can execute query procedures, so we need to reload the modules first and then the triggers
*/
void RestoreTriggers() {
std::lock_guard<LockT> wr(lock_);
for (auto &ic_itr : interp_handler_) {
auto ic = ic_itr.second.get();
spdlog::debug("Restoring trigger for database \"{}\"", ic->db->id());
auto storage_accessor = ic->db->Access();
auto dba = memgraph::query::DbAccessor{storage_accessor.get()};
ic->trigger_store.RestoreTriggers(&ic->ast_cache, &dba, ic->config.query, ic->auth_checker);
}
}
/**
* @brief Restore streams of all currently defined databases.
* @note: Stream transformations are using modules, they have to be restored after the query modules are loaded.
*/
void RestoreStreams() {
std::lock_guard<LockT> wr(lock_);
for (auto &ic_itr : interp_handler_) {
auto ic = ic_itr.second.get();
spdlog::debug("Restoring streams for database \"{}\"", ic->db->id());
ic->streams.RestoreStreams();
}
}
private:
std::optional<std::filesystem::path> StorageDir_(const std::string &name) const {
const auto conf = interp_handler_.GetConfig(name);
if (conf) {
return conf->storage_config.durability.storage_directory;
}
spdlog::debug("Failed to find storage dir for database \"{}\"", name);
return {};
}
/**
* @brief Create a new SessionContext associated with the "name" database
*
* @param name name of the database
* @return NewResultT context on success, error on failure
*/
NewResultT New_(const std::string &name) { return New_(name, name); }
/**
* @brief Create a new SessionContext associated with the "name" database
*
* @param name name of the database
* @param storage_subdir undelying RocksDB directory
* @return NewResultT context on success, error on failure
*/
NewResultT New_(const std::string &name, std::filesystem::path storage_subdir) {
if (default_configs_) {
auto storage = default_configs_->storage_config;
storage::UpdatePaths(storage, storage.durability.storage_directory / storage_subdir);
return New_(name, storage, default_configs_->interp_config);
}
spdlog::info("Trying to generate session context without any configurations.");
return NewError::NO_CONFIGS;
}
/**
* @brief Create a new SessionContext associated with the "name" database
*
* @param name name of the database
* @param storage_config storage configuration
* @param inter_config interpreter configuration
* @return NewResultT context on success, error on failure
*/
NewResultT New_(const std::string &name, StorageConfigT &storage_config, query::InterpreterConfig &inter_config/*,
const std::string &ah_flags*/) {
MG_ASSERT(auth_handler_, "No high level AuthQueryHandler has been supplied.");
MG_ASSERT(auth_checker_, "No high level AuthChecker has been supplied.");
if (defunct_dbs_.contains(name)) {
spdlog::warn("Failed to generate database due to the unknown state of the previously defunct database \"{}\".",
name);
return NewError::DEFUNCT;
}
auto new_interp = interp_handler_.New(name, *this, storage_config, inter_config, *auth_handler_, *auth_checker_);
if (new_interp.HasValue()) {
// Success
if (durability_) durability_->Put(name, "ok");
return SessionContext{new_interp.GetValue(), run_id_, auth_.get(), audit_log_};
}
return new_interp.GetError();
}
/**
* @brief Create a new SessionContext associated with the default database
*
* @return NewResultT context on success, error on failure
*/
NewResultT NewDefault_() {
// Create the default DB in the root (this is how it was done pre multi-tenancy)
auto res = New_(kDefaultDB, "..");
if (res.HasValue()) {
// For back-compatibility...
// Recreate the dbms layout for the default db and symlink to the root
const auto dir = StorageDir_(kDefaultDB);
MG_ASSERT(dir, "Failed to find storage path.");
const auto main_dir = *dir / "databases" / kDefaultDB;
if (!std::filesystem::exists(main_dir)) {
std::filesystem::create_directory(main_dir);
}
// Force link on-disk directories
const auto conf = interp_handler_.GetConfig(kDefaultDB);
MG_ASSERT(conf, "No configuration for the default database.");
const auto &tmp_conf = conf->storage_config.disk;
std::vector<std::filesystem::path> to_link{
tmp_conf.main_storage_directory, tmp_conf.label_index_directory,
tmp_conf.label_property_index_directory, tmp_conf.unique_constraints_directory,
tmp_conf.name_id_mapper_directory, tmp_conf.id_name_mapper_directory,
tmp_conf.durability_directory, tmp_conf.wal_directory,
};
// Add in-memory paths
// Some directories are redundant (skip those)
const std::vector<std::string> skip{".lock", "audit_log", "auth", "databases", "internal_modules", "settings"};
for (auto const &item : std::filesystem::directory_iterator{*dir}) {
const auto dir_name = std::filesystem::relative(item.path(), item.path().parent_path());
if (std::find(skip.begin(), skip.end(), dir_name) != skip.end()) continue;
to_link.push_back(item.path());
}
// Symlink to root dir
for (auto const &item : to_link) {
const auto dir_name = std::filesystem::relative(item, item.parent_path());
const auto link = main_dir / dir_name;
const auto to = std::filesystem::relative(item, main_dir);
if (!std::filesystem::is_symlink(link) && !std::filesystem::exists(link)) {
std::filesystem::create_directory_symlink(to, link);
} else { // Check existing link
std::error_code ec;
const auto test_link = std::filesystem::read_symlink(link, ec);
if (ec || test_link != to) {
MG_ASSERT(false,
"Memgraph storage directory incompatible with new version.\n"
"Please use a clean directory or remove \"{}\" and try again.",
link.string());
}
}
}
}
return res;
}
/**
* @brief Get the context associated with the "name" database
*
* @param name
* @return SessionContext
* @throw UnknownDatabaseException if trying to get unknown database
*/
SessionContext Get_(const std::string &name) {
auto interp = interp_handler_.Get(name);
if (interp) {
return SessionContext{*interp, run_id_, auth_.get(), audit_log_};
}
throw UnknownDatabaseException("Tried to retrieve an unknown database \"{}\".", name);
}
// Should storage objects ever be deleted?
mutable LockT lock_; //!< protective lock
std::filesystem::path lock_file_path_; //!< Lock file protecting the main storage
utils::OutputFile lock_file_handle_; //!< Handler the lock (crash if already open)
InterpContextHandler<SessionContextHandler> interp_handler_; //!< multi-tenancy interpreter handler
// AuthContextHandler auth_handler_; //!< multi-tenancy authorization handler (currently we use a single global
// auth)
std::unique_ptr<utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock>> auth_;
std::unique_ptr<query::AuthQueryHandler> auth_handler_;
std::unique_ptr<query::AuthChecker> auth_checker_;
std::optional<Config> default_configs_; //!< default storage and interpreter configurations
const std::string run_id_; //!< run's unique identifier (auto generated)
memgraph::audit::Log *audit_log_; //!< pointer to the audit logger
std::unordered_map<std::string, SessionInterface &> sessions_; //!< map of active/registered sessions
std::unique_ptr<kvstore::KVStore> durability_; //!< list of active dbs (pointer so we can postpone its creation)
std::set<std::string> defunct_dbs_; //!< Databases that are in an unknown state due to various failures
bool delete_on_drop_; //!< Flag defining if dropping storage also deletes its directory
public:
static SessionContextHandler &ExtractSCH(query::InterpreterContext *interpreter_context) {
return static_cast<typename decltype(interp_handler_)::InterpContextT *>(interpreter_context)->sc_handler_;
}
};
#else
/**
* @brief Initialize the handler.
*
* @param auth pointer to the authenticator
* @param configs storage and interpreter configurations
*/
static inline SessionContext Init(storage::Config &storage_config, query::InterpreterConfig &interp_config,
utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock> *auth,
query::AuthQueryHandler *auth_handler, query::AuthChecker *auth_checker) {
MG_ASSERT(auth, "Passed a nullptr auth");
MG_ASSERT(auth_handler, "Passed a nullptr auth_handler");
MG_ASSERT(auth_checker, "Passed a nullptr auth_checker");
storage_config.name = kDefaultDB;
auto interp_context = std::make_shared<query::InterpreterContext>(
storage_config, interp_config, storage_config.durability.storage_directory, auth_handler, auth_checker);
MG_ASSERT(interp_context, "Failed to construct main interpret context.");
return SessionContext{interp_context, utils::GenerateUUID(), auth};
}
#endif
} // namespace memgraph::dbms

View File

@@ -62,10 +62,6 @@ auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege) {
return auth::Permission::STORAGE_MODE;
case query::AuthQuery::Privilege::TRANSACTION_MANAGEMENT:
return auth::Permission::TRANSACTION_MANAGEMENT;
case query::AuthQuery::Privilege::MULTI_DATABASE_EDIT:
return auth::Permission::MULTI_DATABASE_EDIT;
case query::AuthQuery::Privilege::MULTI_DATABASE_USE:
return auth::Permission::MULTI_DATABASE_USE;
}
}

View File

@@ -71,8 +71,7 @@ AuthChecker::AuthChecker(
: auth_(auth) {}
bool AuthChecker::IsUserAuthorized(const std::optional<std::string> &username,
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
const std::string &db_name) const {
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) const {
std::optional<memgraph::auth::User> maybe_user;
{
auto locked_auth = auth_->ReadLock();
@@ -84,7 +83,7 @@ bool AuthChecker::IsUserAuthorized(const std::optional<std::string> &username,
}
}
return maybe_user.has_value() && IsUserAuthorized(*maybe_user, privileges, db_name);
return maybe_user.has_value() && IsUserAuthorized(*maybe_user, privileges);
}
#ifdef MG_ENTERPRISE
@@ -95,14 +94,12 @@ std::unique_ptr<memgraph::query::FineGrainedAuthChecker> AuthChecker::GetFineGra
}
try {
auto locked_auth = auth_->Lock();
if (username != user_.username()) {
auto maybe_user = locked_auth->GetUser(username);
if (!maybe_user) {
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
}
user_ = std::move(*maybe_user);
auto user = locked_auth->GetUser(username);
if (!user) {
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
}
return std::make_unique<memgraph::glue::FineGrainedAuthChecker>(user_, dba);
return std::make_unique<memgraph::glue::FineGrainedAuthChecker>(std::move(*user), dba);
} catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what());
@@ -111,13 +108,7 @@ std::unique_ptr<memgraph::query::FineGrainedAuthChecker> AuthChecker::GetFineGra
#endif
bool AuthChecker::IsUserAuthorized(const memgraph::auth::User &user,
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
const std::string &db_name) { // NOLINT
#ifdef MG_ENTERPRISE
if (!db_name.empty() && !user.db_access().Contains(db_name)) {
return false;
}
#endif
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) {
const auto user_permissions = user.GetPermissions();
return std::all_of(privileges.begin(), privileges.end(), [&user_permissions](const auto privilege) {
return user_permissions.Has(memgraph::glue::PrivilegeToPermission(privilege)) ==

View File

@@ -25,8 +25,7 @@ class AuthChecker : public query::AuthChecker {
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth);
bool IsUserAuthorized(const std::optional<std::string> &username,
const std::vector<query::AuthQuery::Privilege> &privileges,
const std::string &db_name) const override;
const std::vector<query::AuthQuery::Privilege> &privileges) const override;
#ifdef MG_ENTERPRISE
std::unique_ptr<memgraph::query::FineGrainedAuthChecker> GetFineGrainedAuthChecker(
@@ -34,12 +33,10 @@ class AuthChecker : public query::AuthChecker {
#endif
[[nodiscard]] static bool IsUserAuthorized(const memgraph::auth::User &user,
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
const std::string &db_name = "");
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges);
private:
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
mutable auth::User user_;
};
#ifdef MG_ENTERPRISE
class FineGrainedAuthChecker : public query::FineGrainedAuthChecker {

View File

@@ -16,7 +16,6 @@
#include <fmt/format.h>
#include "auth/models.hpp"
#include "dbms/constants.hpp"
#include "glue/auth.hpp"
#include "license/license.hpp"
#include "query/constants.hpp"
@@ -123,29 +122,6 @@ std::vector<std::vector<memgraph::query::TypedValue>> ShowRolePrivileges(
}
#ifdef MG_ENTERPRISE
std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges(
const std::optional<memgraph::auth::User> &user) {
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast() || !user) {
return {};
}
const auto &db = user->db_access();
const auto &allows = db.GetAllowAll();
const auto &grants = db.GetGrants();
const auto &denies = db.GetDenies();
std::vector<memgraph::query::TypedValue> res; // First element is a list of granted databases, second of revoked ones
if (allows) {
res.emplace_back("*");
} else {
std::vector<memgraph::query::TypedValue> grants_vec(grants.cbegin(), grants.cend());
res.emplace_back(std::move(grants_vec));
}
std::vector<memgraph::query::TypedValue> denies_vec(denies.cbegin(), denies.cend());
res.emplace_back(std::move(denies_vec));
return {res};
}
std::vector<FineGrainedPermissionForPrivilegeResult> GetFineGrainedPermissionForPrivilegeForUserOrRole(
const memgraph::auth::FineGrainedAccessPermissions &permissions, const std::string &permission_type,
const std::string &user_or_role) {
@@ -292,10 +268,6 @@ bool AuthQueryHandler::CreateUser(const std::string &username, const std::option
}
#endif
);
#ifdef MG_ENTERPRISE
GrantDatabaseToUser(auth::kAllDatabases, username);
SetMainDatabase(username, dbms::kDefaultDB);
#endif
}
return user_added;
@@ -347,67 +319,6 @@ bool AuthQueryHandler::CreateRole(const std::string &rolename) {
}
}
#ifdef MG_ENTERPRISE
bool AuthQueryHandler::RevokeDatabaseFromUser(const std::string &db, const std::string &username) {
if (!std::regex_match(username, name_regex_)) {
throw memgraph::query::QueryRuntimeException("Invalid user name.");
}
try {
auto locked_auth = auth_->Lock();
auto user = locked_auth->GetUser(username);
if (!user) return false;
return locked_auth->RevokeDatabaseFromUser(db, username);
} catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what());
}
}
bool AuthQueryHandler::GrantDatabaseToUser(const std::string &db, const std::string &username) {
if (!std::regex_match(username, name_regex_)) {
throw memgraph::query::QueryRuntimeException("Invalid user name.");
}
try {
auto locked_auth = auth_->Lock();
auto user = locked_auth->GetUser(username);
if (!user) return false;
return locked_auth->GrantDatabaseToUser(db, username);
} catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what());
}
}
std::vector<std::vector<memgraph::query::TypedValue>> AuthQueryHandler::GetDatabasePrivileges(
const std::string &username) {
if (!std::regex_match(username, name_regex_)) {
throw memgraph::query::QueryRuntimeException("Invalid user or role name.");
}
try {
auto locked_auth = auth_->ReadLock();
auto user = locked_auth->GetUser(username);
if (!user) {
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist.", username);
}
return ShowDatabasePrivileges(user);
} catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what());
}
}
bool AuthQueryHandler::SetMainDatabase(const std::string &db, const std::string &username) {
if (!std::regex_match(username, name_regex_)) {
throw memgraph::query::QueryRuntimeException("Invalid user name.");
}
try {
auto locked_auth = auth_->Lock();
auto user = locked_auth->GetUser(username);
if (!user) return false;
return locked_auth->SetMainDatabase(db, username);
} catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what());
}
}
#endif
bool AuthQueryHandler::DropRole(const std::string &rolename) {
if (!std::regex_match(rolename, name_regex_)) {
throw memgraph::query::QueryRuntimeException("Invalid role name.");

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -38,16 +38,6 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
void SetPassword(const std::string &username, const std::optional<std::string> &password) override;
#ifdef MG_ENTERPRISE
bool RevokeDatabaseFromUser(const std::string &db, const std::string &username) override;
bool GrantDatabaseToUser(const std::string &db, const std::string &username) override;
std::vector<std::vector<memgraph::query::TypedValue>> GetDatabasePrivileges(const std::string &username) override;
bool SetMainDatabase(const std::string &db, const std::string &username) override;
#endif
bool CreateRole(const std::string &rolename) override;
bool DropRole(const std::string &rolename) override;

View File

@@ -47,10 +47,10 @@ struct MetricsResponse {
std::vector<std::tuple<std::string, std::string, uint64_t>> event_histograms{};
};
template <typename TSessionContext>
template <typename TSessionData>
class MetricsService {
public:
explicit MetricsService(TSessionContext *session_context) : db_(session_context->interpreter_context->db.get()) {}
explicit MetricsService(TSessionData *data) : db_(data->db) {}
nlohmann::json GetMetricsJSON() {
auto response = GetMetrics();
@@ -141,10 +141,10 @@ class MetricsService {
}
};
template <typename TSessionContext>
template <typename TSessionData>
class MetricsRequestHandler final {
public:
explicit MetricsRequestHandler(TSessionContext *session_context) : service_(session_context) {
explicit MetricsRequestHandler(TSessionData *data) : service_(data) {
spdlog::info("Basic request handler started!");
}
@@ -206,6 +206,6 @@ class MetricsRequestHandler final {
}
private:
MetricsService<TSessionContext> service_;
MetricsService<TSessionData> service_;
};
} // namespace memgraph::http

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -86,8 +86,8 @@ struct ConsumerInfo {
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::unordered_map<std::string, std::string> public_configs;
std::unordered_map<std::string, std::string> private_configs;
};
/// Memgraphs Kafka consumer wrapper.

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -36,13 +36,7 @@ KVStore::KVStore(std::filesystem::path storage) : pimpl_(std::make_unique<impl>(
pimpl_->db.reset(db);
}
KVStore::~KVStore() {
spdlog::debug("Destroying KVStore at {}", pimpl_->storage.string());
const auto sync = pimpl_->db->SyncWAL();
if (!sync.ok()) spdlog::error("KVStore sync failed!");
const auto close = pimpl_->db->Close();
if (!close.ok()) spdlog::error("KVStore close failed!");
}
KVStore::~KVStore() {}
KVStore::KVStore(KVStore &&other) { pimpl_ = std::move(other.pimpl_); }

View File

@@ -40,9 +40,6 @@
#include "communication/http/server.hpp"
#include "communication/websocket/auth.hpp"
#include "communication/websocket/server.hpp"
#include "dbms/constants.hpp"
#include "dbms/global.hpp"
#include "dbms/session_context.hpp"
#include "glue/auth_checker.hpp"
#include "glue/auth_handler.hpp"
#include "helpers.hpp"
@@ -56,13 +53,9 @@
#include "query/frontend/ast/ast.hpp"
#include "query/interpreter.hpp"
#include "query/plan/operator.hpp"
#include "query/procedure/callable_alias_mapper.hpp"
#include "query/procedure/module.hpp"
#include "query/procedure/py_module.hpp"
#include "requests/requests.hpp"
#include "storage/v2/config.hpp"
#include "storage/v2/disk/storage.hpp"
#include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/isolation_level.hpp"
#include "storage/v2/storage.hpp"
#include "storage/v2/view.hpp"
@@ -101,7 +94,6 @@
#include "communication/init.hpp"
#include "communication/v2/server.hpp"
#include "communication/v2/session.hpp"
#include "dbms/session_context_handler.hpp"
#include "glue/communication.hpp"
#include "auth/auth.hpp"
@@ -161,10 +153,6 @@ DEFINE_string(init_data_file, "", "Path to cypherl file that is used for creatin
// `mg_import_csv`. If you change it, make sure to change it there as well.
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_string(data_directory, "mg_data", "Path to directory in which to save all permanent data.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_bool(data_recovery_on_startup, false, "Controls whether the database recovers persisted data on startup.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_uint64(memory_warning_threshold, 1024,
"Memory warning threshold, in MB. If Memgraph detects there is "
@@ -182,11 +170,8 @@ DEFINE_VALIDATED_uint64(storage_gc_cycle_sec, 30, "Storage garbage collector int
// `mg_import_csv`. If you change it, make sure to change it there as well.
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_bool(storage_properties_on_edges, false, "Controls whether edges have properties.");
// storage_recover_on_startup deprecated; use data_recovery_on_startup instead
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_HIDDEN_bool(storage_recover_on_startup, false,
"Controls whether the storage recovers persisted data on startup.");
DEFINE_bool(storage_recover_on_startup, false, "Controls whether the storage recovers persisted data on startup.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_VALIDATED_uint64(storage_snapshot_interval_sec, 0,
"Storage snapshot creation interval (in seconds). Set "
@@ -226,12 +211,6 @@ DEFINE_uint64(storage_recovery_thread_count,
memgraph::storage::Config::Durability().recovery_thread_count),
"The number of threads used to recover persisted data from disk.");
#ifdef MG_ENTERPRISE
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_bool(storage_delete_on_drop, true,
"If set to true the query 'DROP DATABASE x' will delete the underlying storage as well.");
#endif
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_bool(telemetry_enabled, false,
"Set to true to enable telemetry. We collect information about the "
@@ -279,8 +258,6 @@ DEFINE_double(query_execution_timeout_sec, 600,
DEFINE_uint64(replication_replica_check_frequency_sec, 1,
"The time duration between two replica checks/pings. If < 1, replicas will NOT be checked at all. NOTE: "
"The MAIN instance allocates a new thread for each REPLICA.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_bool(replication_restore_state_on_startup, false, "Restore replication state on startup, e.g. recover replica");
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_uint64(
@@ -373,12 +350,6 @@ DEFINE_VALIDATED_string(query_modules_directory, "",
return true;
});
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_string(query_callable_mappings_path, "",
"The path to mappings that describes aliases to callables in cypher queries in the form of key-value "
"pairs in a json file. With this option query module procedures that do not exist in memgraph can be "
"mapped to ones that exist.");
// Logging flags
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_HIDDEN_bool(also_log_to_stderr, false, "Log messages go to stderr in addition to logfiles");
@@ -464,6 +435,37 @@ void AddLoggerSink(spdlog::sink_ptr new_sink) {
DEFINE_HIDDEN_string(license_key, "", "License key for Memgraph Enterprise.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_HIDDEN_string(organization_name, "", "Organization name.");
/// Encapsulates Dbms and Interpreter that are passed through the network server
/// and worker to the session.
struct SessionData {
// Explicit constructor here to ensure that pointers to all objects are
// supplied.
#if MG_ENTERPRISE
SessionData(memgraph::storage::Storage *db, memgraph::query::InterpreterContext *interpreter_context,
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth,
memgraph::audit::Log *audit_log)
: db(db), interpreter_context(interpreter_context), auth(auth), audit_log(audit_log) {}
memgraph::storage::Storage *db;
memgraph::query::InterpreterContext *interpreter_context;
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth;
memgraph::audit::Log *audit_log;
#else
SessionData(memgraph::storage::Storage *db, memgraph::query::InterpreterContext *interpreter_context,
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth)
: db(db), interpreter_context(interpreter_context), auth(auth) {}
memgraph::storage::Storage *db;
memgraph::query::InterpreterContext *interpreter_context;
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth;
#endif
// NOTE: run_id should be const but that complicates code a lot.
std::optional<std::string> run_id;
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_string(auth_user_or_role_name_regex, memgraph::glue::kDefaultUserRoleRegex.data(),
"Set to the regular expression that each user or role name must fulfill.");
@@ -486,7 +488,7 @@ void InitFromCypherlFile(memgraph::query::InterpreterContext &ctx, std::string c
interpreter.Pull(&stream, {}, results.qid);
if (audit_log) {
audit_log->Record("", "", line, {}, memgraph::dbms::kDefaultDB);
audit_log->Record("", "", line, {});
}
}
}
@@ -498,197 +500,75 @@ namespace memgraph::metrics {
extern const Event ActiveBoltSessions;
} // namespace memgraph::metrics
auto ToQueryExtras(memgraph::communication::bolt::Value const &extra) -> memgraph::query::QueryExtras {
auto const &as_map = extra.ValueMap();
auto metadata_pv = std::map<std::string, memgraph::storage::PropertyValue>{};
if (auto const it = as_map.find("tx_metadata"); it != as_map.cend() && it->second.IsMap()) {
for (const auto &[key, bolt_md] : it->second.ValueMap()) {
metadata_pv.emplace(key, memgraph::glue::ToPropertyValue(bolt_md));
}
}
auto tx_timeout = std::optional<int64_t>{};
if (auto const it = as_map.find("tx_timeout"); it != as_map.cend() && it->second.IsInt()) {
tx_timeout = it->second.ValueInt();
}
return memgraph::query::QueryExtras{std::move(metadata_pv), tx_timeout};
}
class SessionHL final : public memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
memgraph::communication::v2::OutputStream> {
class BoltSession final : public memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
memgraph::communication::v2::OutputStream> {
public:
struct ContextWrapper {
explicit ContextWrapper(memgraph::dbms::SessionContext sc)
: session_context(sc),
interpreter(std::make_unique<memgraph::query::Interpreter>(session_context.interpreter_context.get())),
defunct_(false) {
session_context.interpreter_context->interpreters.WithLock(
[this](auto &interpreters) { interpreters.insert(interpreter.get()); });
}
~ContextWrapper() { Defunct(); }
void Defunct() {
if (!defunct_) {
session_context.interpreter_context->interpreters.WithLock(
[this](auto &interpreters) { interpreters.erase(interpreter.get()); });
defunct_ = true;
}
}
ContextWrapper(const ContextWrapper &) = delete;
ContextWrapper &operator=(const ContextWrapper &) = delete;
ContextWrapper(ContextWrapper &&in) noexcept
: session_context(std::move(in.session_context)),
interpreter(std::move(in.interpreter)),
defunct_(in.defunct_) {
in.defunct_ = true;
}
ContextWrapper &operator=(ContextWrapper &&in) noexcept {
if (this != &in) {
Defunct();
session_context = std::move(in.session_context);
interpreter = std::move(in.interpreter);
defunct_ = in.defunct_;
in.defunct_ = true;
}
return *this;
}
memgraph::query::InterpreterContext *interpreter_context() { return session_context.interpreter_context.get(); }
memgraph::query::Interpreter *interp() { return interpreter.get(); }
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth() const {
return session_context.auth;
}
#ifdef MG_ENTERPRISE
memgraph::audit::Log *audit_log() const { return session_context.audit_log; }
#endif
std::string run_id() const { return session_context.run_id; }
bool defunct() const { return defunct_; }
private:
memgraph::dbms::SessionContext session_context;
std::unique_ptr<memgraph::query::Interpreter> interpreter;
bool defunct_;
};
SessionHL(
#ifdef MG_ENTERPRISE
memgraph::dbms::SessionContextHandler &sc_handler,
#else
memgraph::dbms::SessionContext sc,
#endif
const memgraph::communication::v2::ServerEndpoint &endpoint,
memgraph::communication::v2::InputStream *input_stream, memgraph::communication::v2::OutputStream *output_stream,
const std::string &default_db = memgraph::dbms::kDefaultDB) // NOLINT
BoltSession(SessionData *data, const memgraph::communication::v2::ServerEndpoint &endpoint,
memgraph::communication::v2::InputStream *input_stream,
memgraph::communication::v2::OutputStream *output_stream)
: memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
memgraph::communication::v2::OutputStream>(input_stream, output_stream),
#ifdef MG_ENTERPRISE
sc_handler_(sc_handler),
current_(sc_handler_.Get(default_db)),
#else
current_(sc),
#endif
interpreter_context_(current_.interpreter_context()),
interpreter_(current_.interp()),
auth_(current_.auth()),
#ifdef MG_ENTERPRISE
audit_log_(current_.audit_log()),
db_(data->db),
interpreter_context_(data->interpreter_context),
interpreter_(data->interpreter_context),
auth_(data->auth),
#if MG_ENTERPRISE
audit_log_(data->audit_log),
#endif
endpoint_(endpoint),
run_id_(current_.run_id()) {
run_id_(data->run_id) {
memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions);
interpreter_context_->interpreters.WithLock([this](auto &interpreters) { interpreters.insert(&interpreter_); });
}
~SessionHL() override { memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveBoltSessions); }
~BoltSession() override {
memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveBoltSessions);
interpreter_context_->interpreters.WithLock([this](auto &interpreters) { interpreters.erase(&interpreter_); });
}
SessionHL(const SessionHL &) = delete;
SessionHL &operator=(const SessionHL &) = delete;
SessionHL(SessionHL &&) = delete;
SessionHL &operator=(SessionHL &&) = delete;
using memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
memgraph::communication::v2::OutputStream>::TEncoder;
void Configure(const std::map<std::string, memgraph::communication::bolt::Value> &run_time_info) override {
#ifdef MG_ENTERPRISE
std::string db;
bool update = false;
// Check if user explicitly defined the database to use
if (run_time_info.contains("db")) {
const auto &db_info = run_time_info.at("db");
if (!db_info.IsString()) {
throw memgraph::communication::bolt::ClientError("Malformed database name.");
}
db = db_info.ValueString();
update = db != current_.interpreter_context()->db->id();
in_explicit_db_ = true;
// NOTE: Once in a transaction, the drivers stop explicitly sending the db and count on using it until commit
} else if (in_explicit_db_ && !interpreter_->in_explicit_transaction_) { // Just on a switch
db = GetDefaultDB();
update = db != current_.interpreter_context()->db->id();
in_explicit_db_ = false;
void BeginTransaction(const std::map<std::string, memgraph::communication::bolt::Value> &metadata) override {
std::map<std::string, memgraph::storage::PropertyValue> metadata_pv;
for (const auto &[key, bolt_value] : metadata) {
metadata_pv.emplace(key, memgraph::glue::ToPropertyValue(bolt_value));
}
// Check if the underlying database needs to be updated
if (update) {
sc_handler_.SetInPlace(db, [this](auto new_sc) mutable {
const auto &db_name = new_sc.interpreter_context->db->id();
MultiDatabaseAuth(db_name);
try {
Update(ContextWrapper(new_sc));
return memgraph::dbms::SetForResult::SUCCESS;
} catch (memgraph::dbms::UnknownDatabaseException &e) {
throw memgraph::communication::bolt::ClientError("No database named \"{}\" found!", db_name);
}
});
}
#endif
interpreter_.BeginTransaction(metadata_pv);
}
using TEncoder = memgraph::communication::bolt::Encoder<
memgraph::communication::bolt::ChunkedEncoderBuffer<memgraph::communication::v2::OutputStream>>;
void CommitTransaction() override { interpreter_.CommitTransaction(); }
void BeginTransaction(const std::map<std::string, memgraph::communication::bolt::Value> &extra) override {
interpreter_->BeginTransaction(ToQueryExtras(extra));
}
void CommitTransaction() override { interpreter_->CommitTransaction(); }
void RollbackTransaction() override { interpreter_->RollbackTransaction(); }
void RollbackTransaction() override { interpreter_.RollbackTransaction(); }
std::pair<std::vector<std::string>, std::optional<int>> Interpret(
const std::string &query, const std::map<std::string, memgraph::communication::bolt::Value> &params,
const std::map<std::string, memgraph::communication::bolt::Value> &extra) override {
const std::map<std::string, memgraph::communication::bolt::Value> &metadata) override {
std::map<std::string, memgraph::storage::PropertyValue> params_pv;
std::map<std::string, memgraph::storage::PropertyValue> metadata_pv;
for (const auto &[key, bolt_param] : params) {
params_pv.emplace(key, memgraph::glue::ToPropertyValue(bolt_param));
}
for (const auto &[key, bolt_md] : metadata) {
metadata_pv.emplace(key, memgraph::glue::ToPropertyValue(bolt_md));
}
const std::string *username{nullptr};
if (user_) {
username = &user_->username();
}
#ifdef MG_ENTERPRISE
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
audit_log_->Record(endpoint_.address().to_string(), user_ ? *username : "", query,
memgraph::storage::PropertyValue(params_pv), interpreter_context_->db->id());
memgraph::storage::PropertyValue(params_pv));
}
#endif
try {
auto result = interpreter_->Prepare(query, params_pv, username, ToQueryExtras(extra), UUID());
const std::string db_name = result.db ? *result.db : "";
if (user_ && !memgraph::glue::AuthChecker::IsUserAuthorized(*user_, result.privileges, db_name)) {
interpreter_->Abort();
if (db_name.empty()) {
throw memgraph::communication::bolt::ClientError(
"You are not authorized to execute this query! Please contact your database administrator.");
}
auto result = interpreter_.Prepare(query, params_pv, username, metadata_pv);
if (user_ && !memgraph::glue::AuthChecker::IsUserAuthorized(*user_, result.privileges)) {
interpreter_.Abort();
throw memgraph::communication::bolt::ClientError(
"You are not authorized to execute this query on database \"{}\"! Please contact your database "
"administrator.",
db_name);
"You are not authorized to execute this query! Please contact "
"your database administrator.");
}
return {result.headers, result.qid};
@@ -703,7 +583,7 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
std::map<std::string, memgraph::communication::bolt::Value> Pull(TEncoder *encoder, std::optional<int> n,
std::optional<int> qid) override {
TypedValueResultStream stream(encoder, interpreter_context_);
TypedValueResultStream stream(encoder, db_);
return PullResults(stream, n, qid);
}
@@ -713,26 +593,14 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
return PullResults(stream, n, qid);
}
void Abort() override { interpreter_->Abort(); }
void Abort() override { interpreter_.Abort(); }
// Called during Init
// During Init, the user cannot choose the landing DB (switch is done during query execution)
bool Authenticate(const std::string &username, const std::string &password) override {
auto locked_auth = auth_->Lock();
if (!locked_auth->HasUsers()) {
return true;
}
user_ = locked_auth->Authenticate(username, password);
#ifdef MG_ENTERPRISE
if (user_.has_value()) {
const auto &db = user_->db_access().GetDefault();
// Check if the underlying database needs to be updated
if (db != current_.interpreter_context()->db->id()) {
const auto &res = sc_handler_.SetFor(UUID(), db);
return res == memgraph::dbms::SetForResult::SUCCESS || res == memgraph::dbms::SetForResult::ALREADY_SET;
}
}
#endif
return user_.has_value();
}
@@ -741,35 +609,15 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
return FLAGS_bolt_server_name_for_init;
}
#ifdef MG_ENTERPRISE
memgraph::dbms::SetForResult OnChange(const std::string &db_name) override {
MultiDatabaseAuth(db_name);
if (db_name != current_.interpreter_context()->db->id()) {
UpdateAndDefunct(db_name); // Done during Pull, so we cannot just replace the current db
return memgraph::dbms::SetForResult::SUCCESS;
}
return memgraph::dbms::SetForResult::ALREADY_SET;
}
bool OnDelete(const std::string &db_name) override {
MG_ASSERT(current_.interpreter_context()->db->id() != db_name && (!defunct_ || defunct_->defunct()),
"Trying to delete a database while still in use.");
return true;
}
#endif
std::string GetDatabaseName() const override { return interpreter_context_->db->id(); }
private:
template <typename TStream>
std::map<std::string, memgraph::communication::bolt::Value> PullResults(TStream &stream, std::optional<int> n,
std::optional<int> qid) {
try {
const auto &summary = interpreter_->Pull(&stream, n, qid);
const auto &summary = interpreter_.Pull(&stream, n, qid);
std::map<std::string, memgraph::communication::bolt::Value> decoded_summary;
for (const auto &kv : summary) {
auto maybe_value =
memgraph::glue::ToBoltValue(kv.second, *interpreter_context_->db, memgraph::storage::View::NEW);
auto maybe_value = memgraph::glue::ToBoltValue(kv.second, *db_, memgraph::storage::View::NEW);
if (maybe_value.HasError()) {
switch (maybe_value.GetError()) {
case memgraph::storage::Error::DELETED_OBJECT:
@@ -790,11 +638,6 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
decoded_summary.emplace("run_id", *run_id);
}
// Clean up previous session (session gets defunct when switching between databases)
if (defunct_) {
defunct_.reset();
}
return decoded_summary;
} catch (const memgraph::query::QueryException &e) {
// Wrap QueryException into ClientError, because we want to allow the
@@ -803,71 +646,17 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
}
}
#ifdef MG_ENTERPRISE
/**
* @brief Update setup to the new database.
*
* @param db_name name of the target database
* @throws UnknownDatabaseException if handler cannot get it
*/
void UpdateAndDefunct(const std::string &db_name) { UpdateAndDefunct(ContextWrapper(sc_handler_.Get(db_name))); }
void UpdateAndDefunct(ContextWrapper &&cntxt) {
defunct_.emplace(std::move(current_));
Update(std::forward<ContextWrapper>(cntxt));
defunct_->Defunct();
}
void Update(const std::string &db_name) {
ContextWrapper tmp(sc_handler_.Get(db_name));
Update(std::move(tmp));
}
void Update(ContextWrapper &&cntxt) {
current_ = std::move(cntxt);
interpreter_ = current_.interp();
interpreter_->in_explicit_db_ = in_explicit_db_;
interpreter_context_ = current_.interpreter_context();
}
/**
* @brief Authenticate user on passed database.
*
* @param db database to check against
* @throws bolt::ClientError when user is not authorized
*/
void MultiDatabaseAuth(const std::string &db) {
if (user_ && !memgraph::glue::AuthChecker::IsUserAuthorized(*user_, {}, db)) {
throw memgraph::communication::bolt::ClientError(
"You are not authorized on the database \"{}\"! Please contact your database administrator.", db);
}
}
/**
* @brief Get the user's default database
*
* @return std::string
*/
std::string GetDefaultDB() {
if (user_.has_value()) {
return user_->db_access().GetDefault();
}
return memgraph::dbms::kDefaultDB;
}
#endif
/// Wrapper around TEncoder which converts TypedValue to Value
/// before forwarding the calls to original TEncoder.
class TypedValueResultStream {
public:
TypedValueResultStream(TEncoder *encoder, memgraph::query::InterpreterContext *ic)
: encoder_(encoder), interpreter_context_(ic) {}
TypedValueResultStream(TEncoder *encoder, const memgraph::storage::Storage *db) : encoder_(encoder), db_(db) {}
void Result(const std::vector<memgraph::query::TypedValue> &values) {
std::vector<memgraph::communication::bolt::Value> decoded_values;
decoded_values.reserve(values.size());
for (const auto &v : values) {
auto maybe_value = memgraph::glue::ToBoltValue(v, *interpreter_context_->db, memgraph::storage::View::NEW);
auto maybe_value = memgraph::glue::ToBoltValue(v, *db_, memgraph::storage::View::NEW);
if (maybe_value.HasError()) {
switch (maybe_value.GetError()) {
case memgraph::storage::Error::DELETED_OBJECT:
@@ -888,36 +677,26 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
private:
TEncoder *encoder_;
// NOTE: Needed only for ToBoltValue conversions
memgraph::query::InterpreterContext *interpreter_context_;
const memgraph::storage::Storage *db_;
};
#ifdef MG_ENTERPRISE
memgraph::dbms::SessionContextHandler &sc_handler_;
#endif
ContextWrapper current_;
std::optional<ContextWrapper> defunct_;
// NOTE: Needed only for ToBoltValue conversions
const memgraph::storage::Storage *db_;
memgraph::query::InterpreterContext *interpreter_context_;
memgraph::query::Interpreter *interpreter_;
memgraph::query::Interpreter interpreter_;
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
std::optional<memgraph::auth::User> user_;
#ifdef MG_ENTERPRISE
memgraph::audit::Log *audit_log_;
bool in_explicit_db_{false}; //!< If true, the user has defined the database to use via metadata
#endif
memgraph::communication::v2::ServerEndpoint endpoint_;
// NOTE: run_id should be const but that complicates code a lot.
std::optional<std::string> run_id_;
};
#ifdef MG_ENTERPRISE
using ServerT = memgraph::communication::v2::Server<SessionHL, memgraph::dbms::SessionContextHandler>;
#else
using ServerT = memgraph::communication::v2::Server<SessionHL, memgraph::dbms::SessionContext>;
#endif
using ServerT = memgraph::communication::v2::Server<BoltSession, SessionData>;
using MonitoringServerT =
memgraph::communication::http::Server<memgraph::http::MetricsRequestHandler<memgraph::dbms::SessionContext>,
memgraph::dbms::SessionContext>;
memgraph::communication::http::Server<memgraph::http::MetricsRequestHandler<SessionData>, SessionData>;
using memgraph::communication::ServerContext;
// Needed to correctly handle memgraph destruction from a signal handler.
@@ -1080,6 +859,10 @@ int main(int argc, char **argv) {
// Begin enterprise features initialization
// Auth
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> auth{data_directory /
"auth"};
#ifdef MG_ENTERPRISE
// Audit log
memgraph::audit::Log audit_log{data_directory / "audit", FLAGS_audit_buffer_size,
@@ -1103,24 +886,16 @@ int main(int argc, char **argv) {
.interval = std::chrono::seconds(FLAGS_storage_gc_cycle_sec)},
.items = {.properties_on_edges = FLAGS_storage_properties_on_edges},
.durability = {.storage_directory = FLAGS_data_directory,
.recover_on_startup = FLAGS_storage_recover_on_startup || FLAGS_data_recovery_on_startup,
.recover_on_startup = FLAGS_storage_recover_on_startup,
.snapshot_retention_count = FLAGS_storage_snapshot_retention_count,
.wal_file_size_kibibytes = FLAGS_storage_wal_file_size_kib,
.wal_file_flush_every_n_tx = FLAGS_storage_wal_file_flush_every_n_tx,
.snapshot_on_exit = FLAGS_storage_snapshot_on_exit,
.restore_replication_state_on_startup = FLAGS_replication_restore_state_on_startup,
.restore_replication_state_on_startup = true,
.items_per_batch = FLAGS_storage_items_per_batch,
.recovery_thread_count = FLAGS_storage_recovery_thread_count,
.allow_parallel_index_creation = FLAGS_storage_parallel_index_recovery},
.transaction = {.isolation_level = ParseIsolationLevel()},
.disk = {.main_storage_directory = FLAGS_data_directory + "/rocksdb_main_storage",
.label_index_directory = FLAGS_data_directory + "/rocksdb_label_index",
.label_property_index_directory = FLAGS_data_directory + "/rocksdb_label_property_index",
.unique_constraints_directory = FLAGS_data_directory + "/rocksdb_unique_constraints",
.name_id_mapper_directory = FLAGS_data_directory + "/rocksdb_name_id_mapper",
.id_name_mapper_directory = FLAGS_data_directory + "/rocksdb_id_name_mapper",
.durability_directory = FLAGS_data_directory + "/rocksdb_durability",
.wal_directory = FLAGS_data_directory + "/rocksdb_wal"}};
.transaction = {.isolation_level = ParseIsolationLevel()}};
if (FLAGS_storage_snapshot_interval_sec == 0) {
if (FLAGS_storage_wal_enabled) {
LOG_FATAL(
@@ -1139,62 +914,31 @@ int main(int argc, char **argv) {
}
db_config.durability.snapshot_interval = std::chrono::seconds(FLAGS_storage_snapshot_interval_sec);
}
memgraph::storage::Storage db(db_config);
// Default interpreter configuration
memgraph::query::InterpreterConfig interp_config{
.query = {.allow_load_csv = FLAGS_allow_load_csv},
.execution_timeout_sec = FLAGS_query_execution_timeout_sec,
.replication_replica_check_frequency = std::chrono::seconds(FLAGS_replication_replica_check_frequency_sec),
.default_kafka_bootstrap_servers = FLAGS_kafka_bootstrap_servers,
.default_pulsar_service_url = FLAGS_pulsar_service_url,
.stream_transaction_conflict_retries = FLAGS_stream_transaction_conflict_retries,
.stream_transaction_retry_interval = std::chrono::milliseconds(FLAGS_stream_transaction_retry_interval)};
auto auth_glue =
[flag = FLAGS_auth_user_or_role_name_regex](
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth,
std::unique_ptr<memgraph::query::AuthQueryHandler> &ah, std::unique_ptr<memgraph::query::AuthChecker> &ac) {
// Glue high level auth implementations to the query side
ah = std::make_unique<memgraph::glue::AuthQueryHandler>(auth, flag);
ac = std::make_unique<memgraph::glue::AuthChecker>(auth);
// Handle users passed via arguments
auto *maybe_username = std::getenv(kMgUser);
auto *maybe_password = std::getenv(kMgPassword);
auto *maybe_pass_file = std::getenv(kMgPassfile);
if (maybe_username && maybe_password) {
ah->CreateUser(maybe_username, maybe_password);
} else if (maybe_pass_file) {
const auto [username, password] = LoadUsernameAndPassword(maybe_pass_file);
if (!username.empty() && !password.empty()) {
ah->CreateUser(username, password);
}
}
};
memgraph::query::InterpreterContext interpreter_context{
&db,
{.query = {.allow_load_csv = FLAGS_allow_load_csv},
.execution_timeout_sec = FLAGS_query_execution_timeout_sec,
.replication_replica_check_frequency = std::chrono::seconds(FLAGS_replication_replica_check_frequency_sec),
.default_kafka_bootstrap_servers = FLAGS_kafka_bootstrap_servers,
.default_pulsar_service_url = FLAGS_pulsar_service_url,
.stream_transaction_conflict_retries = FLAGS_stream_transaction_conflict_retries,
.stream_transaction_retry_interval = std::chrono::milliseconds(FLAGS_stream_transaction_retry_interval)},
FLAGS_data_directory};
#ifdef MG_ENTERPRISE
// SessionContext handler (multi-tenancy)
memgraph::dbms::SessionContextHandler sc_handler(audit_log, {db_config, interp_config, auth_glue},
FLAGS_storage_recover_on_startup || FLAGS_data_recovery_on_startup,
FLAGS_storage_delete_on_drop);
// Just for current support... TODO remove
auto session_context = sc_handler.Get(memgraph::dbms::kDefaultDB);
SessionData session_data{&db, &interpreter_context, &auth, &audit_log};
#else
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> auth_{data_directory /
"auth"};
std::unique_ptr<memgraph::query::AuthQueryHandler> auth_handler;
std::unique_ptr<memgraph::query::AuthChecker> auth_checker;
auth_glue(&auth_, auth_handler, auth_checker);
auto session_context = memgraph::dbms::Init(db_config, interp_config, &auth_, auth_handler.get(), auth_checker.get());
SessionData session_data{&db, &interpreter_context, &auth};
#endif
auto *auth = session_context.auth;
auto &interpreter_context = *session_context.interpreter_context; // TODO remove
memgraph::query::procedure::gModuleRegistry.SetModulesDirectory(query_modules_directories, FLAGS_data_directory);
memgraph::query::procedure::gModuleRegistry.UnloadAndLoadModulesFromDirectories();
memgraph::query::procedure::gCallableAliasMapper.LoadMapping(FLAGS_query_callable_mappings_path);
memgraph::glue::AuthQueryHandler auth_handler(&auth, FLAGS_auth_user_or_role_name_regex);
memgraph::glue::AuthChecker auth_checker{&auth};
interpreter_context.auth = &auth_handler;
interpreter_context.auth_checker = &auth_checker;
if (!FLAGS_init_file.empty()) {
spdlog::info("Running init file...");
@@ -1209,22 +953,29 @@ int main(int argc, char **argv) {
#endif
}
#ifdef MG_ENTERPRISE
sc_handler.RestoreTriggers();
sc_handler.RestoreStreams();
#else
auto *maybe_username = std::getenv(kMgUser);
auto *maybe_password = std::getenv(kMgPassword);
auto *maybe_pass_file = std::getenv(kMgPassfile);
if (maybe_username && maybe_password) {
auth_handler.CreateUser(maybe_username, maybe_password);
} else if (maybe_pass_file) {
const auto [username, password] = LoadUsernameAndPassword(maybe_pass_file);
if (!username.empty() && !password.empty()) {
auth_handler.CreateUser(username, password);
}
}
{
// Triggers can execute query procedures, so we need to reload the modules first and then
// the triggers
auto storage_accessor = interpreter_context.db->Access();
auto dba = memgraph::query::DbAccessor{storage_accessor.get()};
auto dba = memgraph::query::DbAccessor{&storage_accessor};
interpreter_context.trigger_store.RestoreTriggers(
&interpreter_context.ast_cache, &dba, interpreter_context.config.query, interpreter_context.auth_checker);
}
// As the Stream transformations are using modules, they have to be restored after the query modules are loaded.
interpreter_context.streams.RestoreStreams();
#endif
ServerContext context;
std::string service_name = "Bolt";
@@ -1236,35 +987,25 @@ int main(int argc, char **argv) {
spdlog::warn(
memgraph::utils::MessageWithLink("Using non-secure Bolt connection (without SSL).", "https://memgr.ph/ssl"));
}
auto server_endpoint = memgraph::communication::v2::ServerEndpoint{
boost::asio::ip::address::from_string(FLAGS_bolt_address), static_cast<uint16_t>(FLAGS_bolt_port)};
#ifdef MG_ENTERPRISE
ServerT server(server_endpoint, &sc_handler, &context, FLAGS_bolt_session_inactivity_timeout, service_name,
ServerT server(server_endpoint, &session_data, &context, FLAGS_bolt_session_inactivity_timeout, service_name,
FLAGS_bolt_num_workers);
#else
ServerT server(server_endpoint, &session_context, &context, FLAGS_bolt_session_inactivity_timeout, service_name,
FLAGS_bolt_num_workers);
#endif
const auto run_id = memgraph::utils::GenerateUUID();
const auto machine_id = memgraph::utils::GetMachineId();
const auto run_id = session_context.run_id; // For current compatibility
session_data.run_id = run_id;
// Setup telemetry
static constexpr auto telemetry_server{"https://telemetry.memgraph.com/88b5e7e8-746a-11e8-9f85-538a9e9690cc/"};
std::optional<memgraph::telemetry::Telemetry> telemetry;
if (FLAGS_telemetry_enabled) {
telemetry.emplace(telemetry_server, data_directory / "telemetry", run_id, machine_id, std::chrono::minutes(10));
#ifdef MG_ENTERPRISE
telemetry->AddCollector("storage", [&sc_handler]() -> nlohmann::json {
const auto &info = sc_handler.Info();
return {{"vertices", info.num_vertex}, {"edges", info.num_edges}, {"databases", info.num_databases}};
});
#else
telemetry->AddCollector("storage", [&interpreter_context]() -> nlohmann::json {
auto info = interpreter_context.db->GetInfo();
telemetry->AddCollector("storage", [&db]() -> nlohmann::json {
auto info = db.GetInfo();
return {{"vertices", info.vertex_count}, {"edges", info.edge_count}};
});
#endif
telemetry->AddCollector("event_counters", []() -> nlohmann::json {
nlohmann::json ret;
for (size_t i = 0; i < memgraph::metrics::CounterEnd(); ++i) {
@@ -1280,25 +1021,25 @@ int main(int argc, char **argv) {
memgraph::license::LicenseInfoSender license_info_sender(telemetry_server, run_id, machine_id, memory_limit,
memgraph::license::global_license_checker.GetLicenseInfo());
memgraph::communication::websocket::SafeAuth websocket_auth{auth};
memgraph::communication::websocket::SafeAuth websocket_auth{&auth};
memgraph::communication::websocket::Server websocket_server{
{FLAGS_monitoring_address, static_cast<uint16_t>(FLAGS_monitoring_port)}, &context, websocket_auth};
AddLoggerSink(websocket_server.GetLoggingSink());
MonitoringServerT metrics_server{
{FLAGS_metrics_address, static_cast<uint16_t>(FLAGS_metrics_port)}, &session_context, &context};
{FLAGS_metrics_address, static_cast<uint16_t>(FLAGS_metrics_port)}, &session_data, &context};
#ifdef MG_ENTERPRISE
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
// Handler for regular termination signals
auto shutdown = [&metrics_server, &websocket_server, &server, &sc_handler] {
auto shutdown = [&metrics_server, &websocket_server, &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();
// After the server is notified to stop accepting and processing
// connections we tell the execution engine to stop processing all pending
// queries.
sc_handler.Shutdown();
memgraph::query::Shutdown(&interpreter_context);
websocket_server.Shutdown();
metrics_server.Shutdown();

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -20,8 +20,7 @@
#include <unordered_map>
#include "helpers.hpp"
#include "storage/v2/edge_accessor.hpp"
#include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/storage.hpp"
#include "utils/exceptions.hpp"
#include "utils/logging.hpp"
#include "utils/message.hpp"
@@ -422,7 +421,7 @@ void ProcessNodeRow(memgraph::storage::Storage *store, const std::vector<std::st
std::unordered_map<NodeId, memgraph::storage::Gid> *node_id_map) {
std::optional<NodeId> id;
auto acc = store->Access();
auto node = acc->CreateVertex();
auto node = acc.CreateVertex();
for (size_t i = 0; i < row.size(); ++i) {
const auto &field = fields[i];
const auto &value = row[i];
@@ -451,29 +450,29 @@ void ProcessNodeRow(memgraph::storage::Storage *store, const std::vector<std::st
} else {
pv_id = memgraph::storage::PropertyValue(node_id.id);
}
auto old_node_property = node.SetProperty(acc->NameToProperty(field.name), pv_id);
auto old_node_property = node.SetProperty(acc.NameToProperty(field.name), pv_id);
if (!old_node_property.HasValue()) throw LoadException("Couldn't add property '{}' to the node", field.name);
if (!old_node_property->IsNull()) throw LoadException("The property '{}' already exists", field.name);
}
id = node_id;
} else if (field.type == "LABEL") {
for (const auto &label : memgraph::utils::Split(value, FLAGS_array_delimiter)) {
auto node_label = node.AddLabel(acc->NameToLabel(label));
auto node_label = node.AddLabel(acc.NameToLabel(label));
if (!node_label.HasValue()) throw LoadException("Couldn't add label '{}' to the node", label);
if (!*node_label) throw LoadException("The label '{}' already exists", label);
}
} else if (field.type != "IGNORE") {
auto old_node_property = node.SetProperty(acc->NameToProperty(field.name), StringToValue(value, field.type));
auto old_node_property = node.SetProperty(acc.NameToProperty(field.name), StringToValue(value, field.type));
if (!old_node_property.HasValue()) throw LoadException("Couldn't add property '{}' to the node", field.name);
if (!old_node_property->IsNull()) throw LoadException("The property '{}' already exists", field.name);
}
}
for (const auto &label : additional_labels) {
auto node_label = node.AddLabel(acc->NameToLabel(label));
auto node_label = node.AddLabel(acc.NameToLabel(label));
if (!node_label.HasValue()) throw LoadException("Couldn't add label '{}' to the node", label);
if (!*node_label) throw LoadException("The label '{}' already exists", label);
}
if (acc->Commit().HasError()) throw LoadException("Couldn't store the node");
if (acc.Commit().HasError()) throw LoadException("Couldn't store the node");
}
void ProcessNodes(memgraph::storage::Storage *store, const std::string &nodes_path,
@@ -568,16 +567,16 @@ void ProcessRelationshipsRow(memgraph::storage::Storage *store, const std::vecto
if (!relationship_type) throw LoadException("Relationship TYPE must be set");
auto acc = store->Access();
auto from_node = acc->FindVertex(*start_id, memgraph::storage::View::NEW);
auto from_node = acc.FindVertex(*start_id, memgraph::storage::View::NEW);
if (!from_node) throw LoadException("From node must be in the storage");
auto to_node = acc->FindVertex(*end_id, memgraph::storage::View::NEW);
auto to_node = acc.FindVertex(*end_id, memgraph::storage::View::NEW);
if (!to_node) throw LoadException("To node must be in the storage");
auto relationship = acc->CreateEdge(&from_node.value(), &to_node.value(), acc->NameToEdgeType(*relationship_type));
auto relationship = acc.CreateEdge(&*from_node, &*to_node, acc.NameToEdgeType(*relationship_type));
if (!relationship.HasValue()) throw LoadException("Couldn't create the relationship");
for (const auto &property : properties) {
auto ret = relationship.GetValue().SetProperty(acc->NameToProperty(property.first), property.second);
auto ret = relationship->SetProperty(acc.NameToProperty(property.first), property.second);
if (!ret.HasValue()) {
if (ret.GetError() != memgraph::storage::Error::PROPERTIES_DISABLED) {
throw LoadException("Couldn't add property '{}' to the relationship", property.first);
@@ -590,7 +589,7 @@ void ProcessRelationshipsRow(memgraph::storage::Storage *store, const std::vecto
}
}
if (acc->Commit().HasError()) throw LoadException("Couldn't store the relationship");
if (acc.Commit().HasError()) throw LoadException("Couldn't store the relationship");
}
void ProcessRelationships(memgraph::storage::Storage *store, const std::string &relationships_path,
@@ -700,13 +699,13 @@ int main(int argc, char *argv[]) {
}
std::unordered_map<NodeId, memgraph::storage::Gid> node_id_map;
std::unique_ptr<memgraph::storage::Storage> store{new memgraph::storage::InMemoryStorage{{
memgraph::storage::Storage store{{
.items = {.properties_on_edges = FLAGS_storage_properties_on_edges},
.durability = {.storage_directory = FLAGS_data_directory,
.recover_on_startup = false,
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::DISABLED,
.snapshot_on_exit = true},
}}};
}};
memgraph::utils::Timer load_timer;
@@ -716,7 +715,7 @@ int main(int argc, char *argv[]) {
std::optional<std::vector<Field>> header;
for (const auto &nodes_file : files) {
spdlog::info("Loading {}", nodes_file);
ProcessNodes(store.get(), nodes_file, &header, &node_id_map, additional_labels);
ProcessNodes(&store, nodes_file, &header, &node_id_map, additional_labels);
}
}
@@ -726,7 +725,7 @@ int main(int argc, char *argv[]) {
std::optional<std::vector<Field>> header;
for (const auto &relationships_file : files) {
spdlog::info("Loading {}", relationships_file);
ProcessRelationships(store.get(), relationships_file, type, &header, node_id_map);
ProcessRelationships(&store, relationships_file, type, &header, node_id_map);
}
}

View File

@@ -27,7 +27,6 @@ set(mg_query_sources
procedure/mg_procedure_helpers.cpp
procedure/module.cpp
procedure/py_module.cpp
procedure/callable_alias_mapper.cpp
serialization/property_value.cpp
stream/streams.cpp
stream/sources.cpp

View File

@@ -24,8 +24,7 @@ class AuthChecker {
virtual ~AuthChecker() = default;
[[nodiscard]] virtual bool IsUserAuthorized(const std::optional<std::string> &username,
const std::vector<query::AuthQuery::Privilege> &privileges,
const std::string &db_name) const = 0;
const std::vector<query::AuthQuery::Privilege> &privileges) const = 0;
#ifdef MG_ENTERPRISE
[[nodiscard]] virtual std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(
@@ -93,8 +92,7 @@ class AllowEverythingFineGrainedAuthChecker final : public query::FineGrainedAut
class AllowEverythingAuthChecker final : public query::AuthChecker {
public:
bool IsUserAuthorized(const std::optional<std::string> & /*username*/,
const std::vector<query::AuthQuery::Privilege> & /*privileges*/,
const std::string & /*db*/) const override {
const std::vector<query::AuthQuery::Privilege> & /*privileges*/) const override {
return true;
}

View File

@@ -43,14 +43,14 @@ struct EvaluationContext {
/// it as if the lifetime is only valid during the Pull.
utils::MemoryResource *memory{utils::NewDeleteResource()};
int64_t timestamp{-1};
Parameters parameters{};
Parameters parameters;
/// All properties indexable via PropertyIx
std::vector<storage::PropertyId> properties{};
std::vector<storage::PropertyId> properties;
/// All labels indexable via LabelIx
std::vector<storage::LabelId> labels{};
std::vector<storage::LabelId> labels;
/// All counters generated by `counter` function, mutable because the function
/// modifies the values
mutable std::unordered_map<std::string, int64_t> counters{};
mutable std::unordered_map<std::string, int64_t> counters;
};
inline std::vector<storage::PropertyId> NamesToProperties(const std::vector<std::string> &property_names,
@@ -85,7 +85,7 @@ struct ExecutionContext {
ExecutionStats execution_stats;
TriggerContextCollector *trigger_context_collector{nullptr};
FrameChangeCollector *frame_change_collector{nullptr};
std::shared_ptr<utils::AsyncTimer> timer;
utils::AsyncTimer timer;
#ifdef MG_ENTERPRISE
std::unique_ptr<FineGrainedAuthChecker> auth_checker{nullptr};
#endif
@@ -94,18 +94,11 @@ struct ExecutionContext {
static_assert(std::is_move_assignable_v<ExecutionContext>, "ExecutionContext must be move assignable!");
static_assert(std::is_move_constructible_v<ExecutionContext>, "ExecutionContext must be move constructible!");
inline auto MustAbort(const ExecutionContext &context) noexcept -> AbortReason {
if (context.transaction_status != nullptr &&
context.transaction_status->load(std::memory_order_acquire) == TransactionStatus::TERMINATED) {
return AbortReason::TERMINATED;
}
if (context.is_shutting_down != nullptr && context.is_shutting_down->load(std::memory_order_acquire)) {
return AbortReason::SHUTDOWN;
}
if (context.timer && context.timer->IsExpired()) {
return AbortReason::TIMEOUT;
}
return AbortReason::NO_ABORT;
inline bool MustAbort(const ExecutionContext &context) noexcept {
return (context.transaction_status != nullptr &&
context.transaction_status->load(std::memory_order_acquire) == TransactionStatus::TERMINATED) ||
(context.is_shutting_down != nullptr && context.is_shutting_down->load(std::memory_order_acquire)) ||
context.timer.IsExpired();
}
inline plan::ProfilingStatsWithTotalTime GetStatsWithTotalTime(const ExecutionContext &context) {

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source

View File

@@ -17,17 +17,15 @@
#include <cppitertools/imap.hpp>
#include "query/exceptions.hpp"
#include "storage/v2/edge_accessor.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/result.hpp"
#include "storage/v2/storage_mode.hpp"
#include "utils/pmr/unordered_set.hpp"
#include "utils/variant_helpers.hpp"
///////////////////////////////////////////////////////////
// Our communication layer and query engine don't mix
// very well on Centos because OpenSSL version available
// very well on Centos because OpenSSL version avaialable
// on Centos 7 include libkrb5 which has brilliant macros
// called TRUE and FALSE. For more detailed explanation go
// to memgraph.cpp.
@@ -349,10 +347,6 @@ class DbAccessor final {
VertexAccessor InsertVertex() { return VertexAccessor(accessor_->CreateVertex()); }
void PrefetchOutEdges(const VertexAccessor &vertex) const { accessor_->PrefetchOutEdges(vertex.impl_); }
void PrefetchInEdges(const VertexAccessor &vertex) const { accessor_->PrefetchInEdges(vertex.impl_); }
storage::Result<EdgeAccessor> InsertEdge(VertexAccessor *from, VertexAccessor *to,
const storage::EdgeTypeId &edge_type) {
auto maybe_edge = accessor_->CreateEdge(&from->impl_, &to->impl_, edge_type);
@@ -378,8 +372,6 @@ class DbAccessor final {
VertexAccessor *vertex_accessor) {
using ReturnType = std::pair<VertexAccessor, std::vector<EdgeAccessor>>;
accessor_->PrefetchOutEdges(vertex_accessor->impl_);
accessor_->PrefetchInEdges(vertex_accessor->impl_);
auto res = accessor_->DetachDeleteVertex(&vertex_accessor->impl_);
if (res.HasError()) {
return res.GetError();
@@ -432,8 +424,6 @@ class DbAccessor final {
void Abort() { accessor_->Abort(); }
storage::StorageMode GetStorageMode() const { return accessor_->GetCreationStorageMode(); }
bool LabelIndexExists(storage::LabelId label) const { return accessor_->LabelIndexExists(label); }
bool LabelPropertyIndexExists(storage::LabelId label, storage::PropertyId prop) const {
@@ -495,8 +485,6 @@ class DbAccessor final {
storage::IndicesInfo ListAllIndices() const { return accessor_->ListAllIndices(); }
storage::ConstraintsInfo ListAllConstraints() const { return accessor_->ListAllConstraints(); }
const std::string &id() const { return accessor_->id(); }
};
class SubgraphDbAccessor final {
@@ -520,10 +508,6 @@ class SubgraphDbAccessor final {
const std::string &EdgeTypeToName(storage::EdgeTypeId type) const;
void PrefetchOutEdges(const SubgraphVertexAccessor &vertex) const { db_accessor_.PrefetchOutEdges(vertex.impl_); }
void PrefetchInEdges(const SubgraphVertexAccessor &vertex) const { db_accessor_.PrefetchInEdges(vertex.impl_); }
storage::Result<std::optional<EdgeAccessor>> RemoveEdge(EdgeAccessor *edge);
storage::Result<EdgeAccessor> InsertEdge(SubgraphVertexAccessor *from, SubgraphVertexAccessor *to,

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -482,7 +482,6 @@ PullPlanDump::PullChunk PullPlanDump::CreateEdgePullChunk() {
// If we have a saved iterable from a previous pull
// we need to use the same iterable
if (!maybe_edge_iterable) {
dba_->PrefetchOutEdges(vertex);
maybe_edge_iterable = std::make_shared<EdgeAccessorIterable>(vertex.OutEdges(storage::View::OLD));
}
auto &maybe_edges = *maybe_edge_iterable;

View File

@@ -112,45 +112,16 @@ class QueryRuntimeException : public QueryException {
using QueryException::QueryException;
};
enum class AbortReason : uint8_t {
NO_ABORT = 0,
// transaction has been requested to terminate, ie. "TERMINATE TRANSACTIONS ..."
TERMINATED = 1,
// server is gracefully shutting down
SHUTDOWN = 2,
// the transaction timeout has been reached. Either via "--query-execution-timeout-sec", or a per-transaction timeout
TIMEOUT = 3,
};
// This one is inherited from BasicException and will be treated as
// TransientError, i. e. client will be encouraged to retry execution because it
// could succeed if executed again.
class HintedAbortError : public utils::BasicException {
public:
using utils::BasicException::BasicException;
explicit HintedAbortError(AbortReason reason) : utils::BasicException(AsMsg(reason)), reason_{reason} {}
auto Reason() const -> AbortReason { return reason_; }
private:
static auto AsMsg(AbortReason reason) -> std::string_view {
using namespace std::string_view_literals;
switch (reason) {
case AbortReason::TERMINATED:
return "Transaction was asked to abort by another user."sv;
case AbortReason::SHUTDOWN:
return "Transaction was asked to abort because of database shutdown."sv;
case AbortReason::TIMEOUT:
return "Transaction was asked to abort because of transaction timeout."sv;
default:
// should never happen
return "Transaction was asked to abort for an unknown reason."sv;
}
}
AbortReason reason_;
HintedAbortError()
: utils::BasicException(
"Transaction was asked to abort either because it was executing longer than time specified or another user "
"asked it to abort.") {}
};
class ExplicitTransactionUsageException : public QueryRuntimeException {
@@ -204,34 +175,18 @@ class ReplicationModificationInMulticommandTxException : public QueryException {
: QueryException("Replication clause not allowed in multicommand transactions.") {}
};
class ReplicationDisabledOnDiskStorage : public QueryException {
public:
ReplicationDisabledOnDiskStorage() : QueryException("Replication is not supported while in on-disk storage mode.") {}
};
class LockPathModificationInMulticommandTxException : public QueryException {
public:
LockPathModificationInMulticommandTxException()
: QueryException("Lock path query not allowed in multicommand transactions.") {}
};
class LockPathDisabledOnDiskStorage : public QueryException {
public:
LockPathDisabledOnDiskStorage()
: QueryException("Lock path disabled on disk storage since all data is already persisted. ") {}
};
class FreeMemoryModificationInMulticommandTxException : public QueryException {
public:
FreeMemoryModificationInMulticommandTxException()
: QueryException("Free memory query not allowed in multicommand transactions.") {}
};
class FreeMemoryDisabledOnDiskStorage : public QueryException {
public:
FreeMemoryDisabledOnDiskStorage() : QueryException("Free memory does nothing when using disk storage. ") {}
};
class ShowConfigModificationInMulticommandTxException : public QueryException {
public:
ShowConfigModificationInMulticommandTxException()
@@ -277,11 +232,6 @@ class CreateSnapshotInMulticommandTxException final : public QueryException {
: QueryException("Snapshot cannot be created in multicommand transactions.") {}
};
class CreateSnapshotDisabledOnDiskStorage final : public QueryException {
public:
CreateSnapshotDisabledOnDiskStorage() : QueryException("In the on-disk storage mode data is already persistent.") {}
};
class SettingConfigInMulticommandTxException final : public QueryException {
public:
SettingConfigInMulticommandTxException()
@@ -314,20 +264,4 @@ class TransactionQueueInMulticommandTxException : public QueryException {
: QueryException("Transaction queue queries not allowed in multicommand transactions.") {}
};
class IndexPersistenceException : public QueryException {
public:
IndexPersistenceException() : QueryException("Persisting index on disk failed.") {}
};
class ConstraintsPersistenceException : public QueryException {
public:
ConstraintsPersistenceException() : QueryException("Persisting constraints on disk failed.") {}
};
class MultiDatabaseQueryInMulticommandTxException : public QueryException {
public:
MultiDatabaseQueryInMulticommandTxException()
: QueryException("Multi-database queries are not allowed in multicommand transactions.") {}
};
} // namespace memgraph::query

View File

@@ -279,10 +279,4 @@ constexpr utils::TypeInfo query::Exists::kType{utils::TypeId::AST_EXISTS, "Exist
constexpr utils::TypeInfo query::CallSubquery::kType{utils::TypeId::AST_CALL_SUBQUERY, "CallSubquery",
&query::Clause::kType};
constexpr utils::TypeInfo query::MultiDatabaseQuery::kType{utils::TypeId::AST_MULTI_DATABASE_QUERY,
"MultiDatabaseQuery", &query::Query::kType};
constexpr utils::TypeInfo query::ShowDatabasesQuery::kType{utils::TypeId::AST_SHOW_DATABASES, "ShowDatabasesQuery",
&query::Query::kType};
} // namespace memgraph

View File

@@ -2253,7 +2253,6 @@ class CallProcedure : public memgraph::query::Clause {
memgraph::query::Expression *memory_limit_{nullptr};
size_t memory_scale_{1024U};
bool is_write_;
bool void_procedure_{false};
CallProcedure *Clone(AstStorage *storage) const override {
CallProcedure *object = storage->Create<CallProcedure>();
@@ -2270,7 +2269,6 @@ class CallProcedure : public memgraph::query::Clause {
object->memory_limit_ = memory_limit_ ? memory_limit_->Clone(storage) : nullptr;
object->memory_scale_ = memory_scale_;
object->is_write_ = is_write_;
object->void_procedure_ = void_procedure_;
return object;
}
@@ -2778,11 +2776,7 @@ class AuthQuery : public memgraph::query::Query {
REVOKE_PRIVILEGE,
SHOW_PRIVILEGES,
SHOW_ROLE_FOR_USER,
SHOW_USERS_FOR_ROLE,
GRANT_DATABASE_TO_USER,
REVOKE_DATABASE_FROM_USER,
SHOW_DATABASE_PRIVILEGES,
SET_MAIN_DATABASE,
SHOW_USERS_FOR_ROLE
};
enum class Privilege {
@@ -2808,9 +2802,7 @@ class AuthQuery : public memgraph::query::Query {
MODULE_WRITE,
WEBSOCKET,
STORAGE_MODE,
TRANSACTION_MANAGEMENT,
MULTI_DATABASE_EDIT,
MULTI_DATABASE_USE,
TRANSACTION_MANAGEMENT
};
enum class FineGrainedPrivilege { NOTHING, READ, UPDATE, CREATE_DELETE };
@@ -2824,7 +2816,6 @@ class AuthQuery : public memgraph::query::Query {
std::string role_;
std::string user_or_role_;
memgraph::query::Expression *password_{nullptr};
std::string database_;
std::vector<memgraph::query::AuthQuery::Privilege> privileges_;
std::vector<std::unordered_map<memgraph::query::AuthQuery::FineGrainedPrivilege, std::vector<std::string>>>
label_privileges_;
@@ -2838,7 +2829,6 @@ class AuthQuery : public memgraph::query::Query {
object->role_ = role_;
object->user_or_role_ = user_or_role_;
object->password_ = password_ ? password_->Clone(storage) : nullptr;
object->database_ = database_;
object->privileges_ = privileges_;
object->label_privileges_ = label_privileges_;
object->edge_type_privileges_ = edge_type_privileges_;
@@ -2847,7 +2837,7 @@ class AuthQuery : public memgraph::query::Query {
protected:
AuthQuery(Action action, std::string user, std::string role, std::string user_or_role, Expression *password,
std::string database, std::vector<Privilege> privileges,
std::vector<Privilege> privileges,
std::vector<std::unordered_map<FineGrainedPrivilege, std::vector<std::string>>> label_privileges,
std::vector<std::unordered_map<FineGrainedPrivilege, std::vector<std::string>>> edge_type_privileges)
: action_(action),
@@ -2855,7 +2845,6 @@ class AuthQuery : public memgraph::query::Query {
role_(role),
user_or_role_(user_or_role),
password_(password),
database_(database),
privileges_(privileges),
label_privileges_(label_privileges),
edge_type_privileges_(edge_type_privileges) {}
@@ -2865,31 +2854,19 @@ class AuthQuery : public memgraph::query::Query {
};
/// Constant that holds all available privileges.
const std::vector<AuthQuery::Privilege> kPrivilegesAll = {AuthQuery::Privilege::CREATE,
AuthQuery::Privilege::DELETE,
AuthQuery::Privilege::MATCH,
AuthQuery::Privilege::MERGE,
AuthQuery::Privilege::SET,
AuthQuery::Privilege::REMOVE,
AuthQuery::Privilege::INDEX,
AuthQuery::Privilege::STATS,
AuthQuery::Privilege::AUTH,
AuthQuery::Privilege::CONSTRAINT,
AuthQuery::Privilege::DUMP,
AuthQuery::Privilege::REPLICATION,
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::TRANSACTION_MANAGEMENT,
AuthQuery::Privilege::STORAGE_MODE,
AuthQuery::Privilege::MULTI_DATABASE_EDIT,
AuthQuery::Privilege::MULTI_DATABASE_USE};
const std::vector<AuthQuery::Privilege> kPrivilegesAll = {
AuthQuery::Privilege::CREATE, AuthQuery::Privilege::DELETE,
AuthQuery::Privilege::MATCH, AuthQuery::Privilege::MERGE,
AuthQuery::Privilege::SET, AuthQuery::Privilege::REMOVE,
AuthQuery::Privilege::INDEX, AuthQuery::Privilege::STATS,
AuthQuery::Privilege::AUTH, AuthQuery::Privilege::CONSTRAINT,
AuthQuery::Privilege::DUMP, AuthQuery::Privilege::REPLICATION,
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::TRANSACTION_MANAGEMENT,
AuthQuery::Privilege::STORAGE_MODE};
class InfoQuery : public memgraph::query::Query {
public:
@@ -3166,7 +3143,7 @@ class StorageModeQuery : public memgraph::query::Query {
static const utils::TypeInfo kType;
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
enum class StorageMode { IN_MEMORY_TRANSACTIONAL, IN_MEMORY_ANALYTICAL, ON_DISK_TRANSACTIONAL };
enum class StorageMode { IN_MEMORY_TRANSACTIONAL, IN_MEMORY_ANALYTICAL };
StorageModeQuery() = default;
@@ -3467,38 +3444,5 @@ class CallSubquery : public memgraph::query::Clause {
friend class AstStorage;
};
class MultiDatabaseQuery : public memgraph::query::Query {
public:
static const utils::TypeInfo kType;
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
DEFVISITABLE(QueryVisitor<void>);
enum class Action { CREATE, USE, DROP };
memgraph::query::MultiDatabaseQuery::Action action_;
std::string db_name_;
MultiDatabaseQuery *Clone(AstStorage *storage) const override {
auto *object = storage->Create<MultiDatabaseQuery>();
object->action_ = action_;
object->db_name_ = db_name_;
return object;
}
};
class ShowDatabasesQuery : public memgraph::query::Query {
public:
static const utils::TypeInfo kType;
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
DEFVISITABLE(QueryVisitor<void>);
ShowDatabasesQuery *Clone(AstStorage *storage) const override {
auto *object = storage->Create<ShowDatabasesQuery>();
return object;
}
};
} // namespace query
} // namespace memgraph

View File

@@ -11,7 +11,6 @@
#pragma once
#include "query/frontend/ast/ast.hpp"
#include "utils/visitor.hpp"
namespace memgraph::query {
@@ -103,8 +102,6 @@ class CallSubquery;
class AnalyzeGraphQuery;
class TransactionQueueQuery;
class Exists;
class MultiDatabaseQuery;
class ShowDatabasesQuery;
using TreeCompositeVisitor = utils::CompositeVisitor<
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
@@ -142,7 +139,6 @@ class QueryVisitor
: public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery, InfoQuery,
ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery, FreeMemoryQuery, TriggerQuery,
IsolationLevelQuery, CreateSnapshotQuery, StreamQuery, SettingQuery, VersionQuery,
ShowConfigQuery, TransactionQueueQuery, StorageModeQuery, AnalyzeGraphQuery,
MultiDatabaseQuery, ShowDatabasesQuery> {};
ShowConfigQuery, TransactionQueueQuery, StorageModeQuery, AnalyzeGraphQuery> {};
} // namespace memgraph::query

View File

@@ -35,7 +35,6 @@
#include "query/frontend/ast/ast_visitor.hpp"
#include "query/frontend/parsing.hpp"
#include "query/interpret/awesome_memgraph_functions.hpp"
#include "query/procedure/callable_alias_mapper.hpp"
#include "query/procedure/module.hpp"
#include "query/stream/common.hpp"
#include "utils/exceptions.hpp"
@@ -505,10 +504,7 @@ antlrcpp::Any CypherMainVisitor::visitStorageModeQuery(MemgraphCypher::StorageMo
if (mode->IN_MEMORY_ANALYTICAL()) {
return StorageModeQuery::StorageMode::IN_MEMORY_ANALYTICAL;
}
if (mode->IN_MEMORY_TRANSACTIONAL()) {
return StorageModeQuery::StorageMode::IN_MEMORY_TRANSACTIONAL;
}
return StorageModeQuery::StorageMode::ON_DISK_TRANSACTIONAL;
return StorageModeQuery::StorageMode::IN_MEMORY_TRANSACTIONAL;
});
query_ = storage_mode_query;
@@ -1189,26 +1185,13 @@ antlrcpp::Any CypherMainVisitor::visitCallProcedure(MemgraphCypher::CallProcedur
const auto &maybe_found =
procedure::FindProcedure(procedure::gModuleRegistry, call_proc->procedure_name_, utils::NewDeleteResource());
if (!maybe_found) {
// TODO remove this once void procedures are supported,
// this will not be needed anymore.
const auto mg_specific_name = procedure::gCallableAliasMapper.FindAlias(call_proc->procedure_name_);
const bool void_procedure_required = (mg_specific_name && *mg_specific_name == "mgps.validate");
if (void_procedure_required) {
// This is a special case. Since void procedures currently are not supported,
// we have to make sure that the non-memgraph native, void procedures that are
// possibly used against a memgraph instance are handled correctly. As of now
// this is the only known such case. This should be more generic, but the most
// generic solution would be to implement void procedures.
call_proc->void_procedure_ = true;
} else {
throw SemanticException("There is no procedure named '{}'.", call_proc->procedure_name_);
}
throw SemanticException("There is no procedure named '{}'.", call_proc->procedure_name_);
}
call_proc->is_write_ = maybe_found->second->info.is_write;
auto *yield_ctx = ctx->yieldProcedureResults();
if (!yield_ctx) {
if (!maybe_found->second->results.empty() && !call_proc->void_procedure_) {
if (!maybe_found->second->results.empty()) {
throw SemanticException(
"CALL without YIELD may only be used on procedures which do not "
"return any result fields.");
@@ -1272,7 +1255,7 @@ antlrcpp::Any CypherMainVisitor::visitUserOrRoleName(MemgraphCypher::UserOrRoleN
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitCreateRole(MemgraphCypher::CreateRoleContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::CREATE_ROLE;
auth->role_ = std::any_cast<std::string>(ctx->role->accept(this));
return auth;
@@ -1282,7 +1265,7 @@ antlrcpp::Any CypherMainVisitor::visitCreateRole(MemgraphCypher::CreateRoleConte
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitDropRole(MemgraphCypher::DropRoleContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::DROP_ROLE;
auth->role_ = std::any_cast<std::string>(ctx->role->accept(this));
return auth;
@@ -1292,7 +1275,7 @@ antlrcpp::Any CypherMainVisitor::visitDropRole(MemgraphCypher::DropRoleContext *
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowRoles(MemgraphCypher::ShowRolesContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_ROLES;
return auth;
}
@@ -1301,7 +1284,7 @@ antlrcpp::Any CypherMainVisitor::visitShowRoles(MemgraphCypher::ShowRolesContext
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitCreateUser(MemgraphCypher::CreateUserContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::CREATE_USER;
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
if (ctx->password) {
@@ -1317,7 +1300,7 @@ antlrcpp::Any CypherMainVisitor::visitCreateUser(MemgraphCypher::CreateUserConte
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitSetPassword(MemgraphCypher::SetPasswordContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SET_PASSWORD;
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
if (!ctx->password->StringLiteral() && !ctx->literal()->CYPHERNULL()) {
@@ -1331,7 +1314,7 @@ antlrcpp::Any CypherMainVisitor::visitSetPassword(MemgraphCypher::SetPasswordCon
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitDropUser(MemgraphCypher::DropUserContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::DROP_USER;
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
return auth;
@@ -1341,7 +1324,7 @@ antlrcpp::Any CypherMainVisitor::visitDropUser(MemgraphCypher::DropUserContext *
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowUsers(MemgraphCypher::ShowUsersContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_USERS;
return auth;
}
@@ -1350,7 +1333,7 @@ antlrcpp::Any CypherMainVisitor::visitShowUsers(MemgraphCypher::ShowUsersContext
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitSetRole(MemgraphCypher::SetRoleContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SET_ROLE;
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
auth->role_ = std::any_cast<std::string>(ctx->role->accept(this));
@@ -1361,7 +1344,7 @@ antlrcpp::Any CypherMainVisitor::visitSetRole(MemgraphCypher::SetRoleContext *ct
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitClearRole(MemgraphCypher::ClearRoleContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::CLEAR_ROLE;
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
return auth;
@@ -1371,7 +1354,7 @@ antlrcpp::Any CypherMainVisitor::visitClearRole(MemgraphCypher::ClearRoleContext
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitGrantPrivilege(MemgraphCypher::GrantPrivilegeContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::GRANT_PRIVILEGE;
auth->user_or_role_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
if (ctx->grantPrivilegesList()) {
@@ -1393,7 +1376,7 @@ antlrcpp::Any CypherMainVisitor::visitGrantPrivilege(MemgraphCypher::GrantPrivil
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitDenyPrivilege(MemgraphCypher::DenyPrivilegeContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::DENY_PRIVILEGE;
auth->user_or_role_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
if (ctx->privilegesList()) {
@@ -1453,7 +1436,7 @@ antlrcpp::Any CypherMainVisitor::visitGrantPrivilegesList(MemgraphCypher::GrantP
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitRevokePrivilege(MemgraphCypher::RevokePrivilegeContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::REVOKE_PRIVILEGE;
auth->user_or_role_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
if (ctx->revokePrivilegesList()) {
@@ -1526,16 +1509,6 @@ antlrcpp::Any CypherMainVisitor::visitEntitiesList(MemgraphCypher::EntitiesListC
return entities;
}
/**
* @return std::string
*/
antlrcpp::Any CypherMainVisitor::visitWildcardName(MemgraphCypher::WildcardNameContext *ctx) {
if (ctx->symbolicName()) {
return ctx->symbolicName()->accept(this);
}
return std::string("*");
}
/**
* @return AuthQuery::Privilege
*/
@@ -1563,8 +1536,6 @@ antlrcpp::Any CypherMainVisitor::visitPrivilege(MemgraphCypher::PrivilegeContext
if (ctx->WEBSOCKET()) return AuthQuery::Privilege::WEBSOCKET;
if (ctx->TRANSACTION_MANAGEMENT()) return AuthQuery::Privilege::TRANSACTION_MANAGEMENT;
if (ctx->STORAGE_MODE()) return AuthQuery::Privilege::STORAGE_MODE;
if (ctx->MULTI_DATABASE_EDIT()) return AuthQuery::Privilege::MULTI_DATABASE_EDIT;
if (ctx->MULTI_DATABASE_USE()) return AuthQuery::Privilege::MULTI_DATABASE_USE;
LOG_FATAL("Should not get here - unknown privilege!");
}
@@ -1592,7 +1563,7 @@ antlrcpp::Any CypherMainVisitor::visitEntityType(MemgraphCypher::EntityTypeConte
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowPrivileges(MemgraphCypher::ShowPrivilegesContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_PRIVILEGES;
auth->user_or_role_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
return auth;
@@ -1602,7 +1573,7 @@ antlrcpp::Any CypherMainVisitor::visitShowPrivileges(MemgraphCypher::ShowPrivile
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowRoleForUser(MemgraphCypher::ShowRoleForUserContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_ROLE_FOR_USER;
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
return auth;
@@ -1612,55 +1583,12 @@ antlrcpp::Any CypherMainVisitor::visitShowRoleForUser(MemgraphCypher::ShowRoleFo
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowUsersForRole(MemgraphCypher::ShowUsersForRoleContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
AuthQuery *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_USERS_FOR_ROLE;
auth->role_ = std::any_cast<std::string>(ctx->role->accept(this));
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitGrantDatabaseToUser(MemgraphCypher::GrantDatabaseToUserContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::GRANT_DATABASE_TO_USER;
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitRevokeDatabaseFromUser(MemgraphCypher::RevokeDatabaseFromUserContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::REVOKE_DATABASE_FROM_USER;
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowDatabasePrivileges(MemgraphCypher::ShowDatabasePrivilegesContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_DATABASE_PRIVILEGES;
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitSetMainDatabase(MemgraphCypher::SetMainDatabaseContext *ctx) {
auto *auth = storage_->Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SET_MAIN_DATABASE;
auth->database_ = std::any_cast<std::string>(ctx->db->accept(this));
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
return auth;
}
antlrcpp::Any CypherMainVisitor::visitCypherReturn(MemgraphCypher::CypherReturnContext *ctx) {
auto *return_clause = storage_->Create<Return>();
return_clause->body_ = std::any_cast<ReturnBody>(ctx->returnBody()->accept(this));
@@ -2726,33 +2654,4 @@ PropertyIx CypherMainVisitor::AddProperty(const std::string &name) { return stor
EdgeTypeIx CypherMainVisitor::AddEdgeType(const std::string &name) { return storage_->GetEdgeTypeIx(name); }
antlrcpp::Any CypherMainVisitor::visitCreateDatabase(MemgraphCypher::CreateDatabaseContext *ctx) {
auto *mdb_query = storage_->Create<MultiDatabaseQuery>();
mdb_query->db_name_ = std::any_cast<std::string>(ctx->databaseName()->accept(this));
mdb_query->action_ = MultiDatabaseQuery::Action::CREATE;
query_ = mdb_query;
return mdb_query;
}
antlrcpp::Any CypherMainVisitor::visitUseDatabase(MemgraphCypher::UseDatabaseContext *ctx) {
auto *mdb_query = storage_->Create<MultiDatabaseQuery>();
mdb_query->db_name_ = std::any_cast<std::string>(ctx->databaseName()->accept(this));
mdb_query->action_ = MultiDatabaseQuery::Action::USE;
query_ = mdb_query;
return mdb_query;
}
antlrcpp::Any CypherMainVisitor::visitDropDatabase(MemgraphCypher::DropDatabaseContext *ctx) {
auto *mdb_query = storage_->Create<MultiDatabaseQuery>();
mdb_query->db_name_ = std::any_cast<std::string>(ctx->databaseName()->accept(this));
mdb_query->action_ = MultiDatabaseQuery::Action::DROP;
query_ = mdb_query;
return mdb_query;
}
antlrcpp::Any CypherMainVisitor::visitShowDatabases(MemgraphCypher::ShowDatabasesContext * /*ctx*/) {
query_ = storage_->Create<ShowDatabasesQuery>();
return query_;
}
} // namespace memgraph::query::frontend

View File

@@ -524,11 +524,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/
antlrcpp::Any visitEntitiesList(MemgraphCypher::EntitiesListContext *ctx) override;
/**
* @return std::string
*/
antlrcpp::Any visitWildcardName(MemgraphCypher::WildcardNameContext *ctx) override;
/**
* @return AuthQuery::FineGrainedPrivilege
*/
@@ -559,26 +554,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/
antlrcpp::Any visitShowUsersForRole(MemgraphCypher::ShowUsersForRoleContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitGrantDatabaseToUser(MemgraphCypher::GrantDatabaseToUserContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitRevokeDatabaseFromUser(MemgraphCypher::RevokeDatabaseFromUserContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitShowDatabasePrivileges(MemgraphCypher::ShowDatabasePrivilegesContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitSetMainDatabase(MemgraphCypher::SetMainDatabaseContext *ctx) override;
/**
* @return Return*
*/
@@ -960,26 +935,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/
antlrcpp::Any visitCallSubquery(MemgraphCypher::CallSubqueryContext *ctx) override;
/**
* @return MultiDatabaseQuery*
*/
antlrcpp::Any visitCreateDatabase(MemgraphCypher::CreateDatabaseContext *ctx) override;
/**
* @return MultiDatabaseQuery*
*/
antlrcpp::Any visitUseDatabase(MemgraphCypher::UseDatabaseContext *ctx) override;
/**
* @return MultiDatabaseQuery*
*/
antlrcpp::Any visitDropDatabase(MemgraphCypher::DropDatabaseContext *ctx) override;
/**
* @return ShowDatabasesQuery*
*/
antlrcpp::Any visitShowDatabases(MemgraphCypher::ShowDatabasesContext *ctx) override;
public:
Query *query() { return query_; }
const static std::string kAnonPrefix;

View File

@@ -407,5 +407,4 @@ cypherKeyword : ALL
symbolicName : UnescapedSymbolicName
| EscapedSymbolicName
| cypherKeyword
| UNDERSCORE
;

View File

@@ -179,7 +179,6 @@ EscapedSymbolicName : ( '`' ~[`]* '`' )+ ;
*/
IdentifierStart : ID_Start | Pc ;
IdentifierPart : ID_Continue | Sc ;
UNDERSCORE : '_' ;
/* Hack for case-insensitive reserved words */
fragment A : 'A' | 'a' ;

View File

@@ -107,7 +107,6 @@ memgraphCypherKeyword : cypherKeyword
| UNCOMMITTED
| UNLOCK
| UPDATE
| USE
| USER
| USERS
| VERSION
@@ -141,8 +140,6 @@ query : cypherQuery
| versionQuery
| showConfigQuery
| transactionQueueQuery
| multiDatabaseQuery
| showDatabases
;
authQuery : createRole
@@ -160,10 +157,6 @@ authQuery : createRole
| showPrivileges
| showRoleForUser
| showUsersForRole
| grantDatabaseToUser
| revokeDatabaseFromUser
| showDatabasePrivileges
| setMainDatabase
;
replicationQuery : setReplicationRole
@@ -215,10 +208,6 @@ streamQuery : checkStream
| showStreams
;
databaseName : symbolicName ;
wildcardName : ASTERISK | symbolicName ;
settingQuery : setSetting
| showSetting
| showSettings
@@ -276,14 +265,6 @@ denyPrivilege : DENY ( ALL PRIVILEGES | privileges=privilegesList ) TO userOrRol
revokePrivilege : REVOKE ( ALL PRIVILEGES | privileges=revokePrivilegesList ) FROM userOrRole=userOrRoleName ;
grantDatabaseToUser : GRANT DATABASE db=wildcardName TO user=symbolicName ;
revokeDatabaseFromUser : REVOKE DATABASE db=wildcardName FROM user=symbolicName ;
showDatabasePrivileges : SHOW DATABASE PRIVILEGES FOR user=symbolicName ;
setMainDatabase : SET MAIN DATABASE db=symbolicName FOR user=symbolicName ;
privilege : CREATE
| DELETE
| MATCH
@@ -307,8 +288,6 @@ privilege : CREATE
| WEBSOCKET
| TRANSACTION_MANAGEMENT
| STORAGE_MODE
| MULTI_DATABASE_EDIT
| MULTI_DATABASE_USE
;
granularPrivilege : NOTHING | READ | UPDATE | CREATE_DELETE ;
@@ -386,7 +365,7 @@ isolationLevelScope : GLOBAL | SESSION | NEXT ;
isolationLevelQuery : SET isolationLevelScope TRANSACTION ISOLATION LEVEL isolationLevel ;
storageMode : IN_MEMORY_ANALYTICAL | IN_MEMORY_TRANSACTIONAL | ON_DISK_TRANSACTIONAL ;
storageMode : IN_MEMORY_ANALYTICAL | IN_MEMORY_TRANSACTIONAL ;
storageModeQuery : STORAGE MODE storageMode ;
@@ -462,16 +441,3 @@ versionQuery : SHOW VERSION ;
transactionIdList : transactionId ( ',' transactionId )* ;
transactionId : literal ;
multiDatabaseQuery : createDatabase
| useDatabase
| dropDatabase
;
createDatabase : CREATE DATABASE databaseName ;
useDatabase : USE DATABASE databaseName ;
dropDatabase : DROP DATABASE databaseName ;
showDatabases: SHOW DATABASES ;

View File

@@ -23,6 +23,8 @@ lexer grammar MemgraphCypherLexer ;
import CypherLexer ;
UNDERSCORE : '_' ;
AFTER : A F T E R ;
ALTER : A L T E R ;
ANALYZE : A N A L Y Z E ;
@@ -49,7 +51,6 @@ CSV : C S V ;
DATA : D A T A ;
DELIMITER : D E L I M I T E R ;
DATABASE : D A T A B A S E ;
DATABASES : D A T A B A S E S ;
DENY : D E N Y ;
DIRECTORY : D I R E C T O R Y ;
DROP : D R O P ;
@@ -81,12 +82,9 @@ 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 ;
MULTI_DATABASE_EDIT : M U L T I UNDERSCORE D A T A B A S E UNDERSCORE E D I T ;
MULTI_DATABASE_USE : M U L T I UNDERSCORE D A T A B A S E UNDERSCORE U S E ;
NEXT : N E X T ;
NO : N O ;
NOTHING : N O T H I N G ;
ON_DISK_TRANSACTIONAL : O N UNDERSCORE D I S K UNDERSCORE T R A N S A C T I O N A L ;
NULLIF : N U L L I F ;
PASSWORD : P A S S W O R D ;
PORT : P O R T ;
@@ -130,7 +128,6 @@ TRIGGERS : T R I G G E R S ;
UNCOMMITTED : U N C O M M I T T E D ;
UNLOCK : U N L O C K ;
UPDATE : U P D A T E ;
USE : U S E ;
USER : U S E R ;
USERS : U S E R S ;
VERSION : V E R S I O N ;

View File

@@ -89,22 +89,6 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
void Visit(VersionQuery & /*version_query*/) override { AddPrivilege(AuthQuery::Privilege::STATS); }
void Visit(MultiDatabaseQuery &query) override {
switch (query.action_) {
case MultiDatabaseQuery::Action::CREATE:
case MultiDatabaseQuery::Action::DROP:
AddPrivilege(AuthQuery::Privilege::MULTI_DATABASE_EDIT);
break;
case MultiDatabaseQuery::Action::USE:
AddPrivilege(AuthQuery::Privilege::MULTI_DATABASE_USE);
break;
}
}
void Visit(ShowDatabasesQuery & /*unused*/) override {
AddPrivilege(AuthQuery::Privilege::MULTI_DATABASE_USE); /* OR EDIT */
}
bool PreVisit(Create & /*unused*/) override {
AddPrivilege(AuthQuery::Privilege::CREATE);
return false;

View File

@@ -139,7 +139,6 @@ const trie::Trie kKeywords = {"union",
"false",
"reduce",
"coalesce",
"use",
"user",
"password",
"alter",
@@ -160,7 +159,6 @@ const trie::Trie kKeywords = {"union",
"key",
"dump",
"database",
"databases",
"call",
"yield",
"memory",

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -28,7 +28,6 @@
#include "query/typed_value.hpp"
#include "utils/string.hpp"
#include "utils/temporal.hpp"
#include "utils/uuid.hpp"
namespace memgraph::query {
namespace {
@@ -420,10 +419,6 @@ TypedValue Properties(const TypedValue *args, int64_t nargs, const FunctionConte
}
}
TypedValue RandomUuid(const TypedValue * /*args*/, int64_t /*nargs*/, const FunctionContext &ctx) {
return TypedValue(utils::GenerateUUID(), ctx.memory);
}
TypedValue Size(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) {
FType<Or<Null, List, String, Map, Path>>("size", args, nargs);
const auto &value = args[0];
@@ -472,8 +467,6 @@ TypedValue Degree(const TypedValue *args, int64_t nargs, const FunctionContext &
FType<Or<Null, Vertex>>("degree", args, nargs);
if (args[0].IsNull()) return TypedValue(ctx.memory);
const auto &vertex = args[0].ValueVertex();
ctx.db_accessor->PrefetchInEdges(vertex);
ctx.db_accessor->PrefetchOutEdges(vertex);
size_t out_degree = UnwrapDegreeResult(vertex.OutDegree(ctx.view));
size_t in_degree = UnwrapDegreeResult(vertex.InDegree(ctx.view));
return TypedValue(static_cast<int64_t>(out_degree + in_degree), ctx.memory);
@@ -483,7 +476,6 @@ TypedValue InDegree(const TypedValue *args, int64_t nargs, const FunctionContext
FType<Or<Null, Vertex>>("inDegree", args, nargs);
if (args[0].IsNull()) return TypedValue(ctx.memory);
const auto &vertex = args[0].ValueVertex();
ctx.db_accessor->PrefetchInEdges(vertex);
size_t in_degree = UnwrapDegreeResult(vertex.InDegree(ctx.view));
return TypedValue(static_cast<int64_t>(in_degree), ctx.memory);
}
@@ -492,7 +484,6 @@ TypedValue OutDegree(const TypedValue *args, int64_t nargs, const FunctionContex
FType<Or<Null, Vertex>>("outDegree", args, nargs);
if (args[0].IsNull()) return TypedValue(ctx.memory);
const auto &vertex = args[0].ValueVertex();
ctx.db_accessor->PrefetchOutEdges(vertex);
size_t out_degree = UnwrapDegreeResult(vertex.OutDegree(ctx.view));
return TypedValue(static_cast<int64_t>(out_degree), ctx.memory);
}
@@ -1207,7 +1198,7 @@ TypedValue Duration(const TypedValue *args, int64_t nargs, const FunctionContext
std::function<TypedValue(const TypedValue *, const int64_t, const FunctionContext &)> UserFunction(
const mgp_func &func, const std::string &fully_qualified_name) {
return [func, fully_qualified_name](const TypedValue *args, int64_t nargs, const FunctionContext &ctx) -> TypedValue {
/// Find function is called to acquire the lock on Module pointer while user-defined function is executed
/// Find function is called to aquire the lock on Module pointer while user-defined function is executed
const auto &maybe_found =
procedure::FindFunction(procedure::gModuleRegistry, fully_qualified_name, utils::NewDeleteResource());
if (!maybe_found) {
@@ -1215,7 +1206,7 @@ std::function<TypedValue(const TypedValue *, const int64_t, const FunctionContex
"Function '{}' has been unloaded. Please check query modules to confirm that function is loaded in Memgraph.",
fully_qualified_name);
}
/// Explicit extraction of module pointer, to clearly state that the lock is acquired.
/// Explicit extraction of module pointer, to clearly state that the lock is aquired.
// NOLINTNEXTLINE(clang-diagnostic-unused-variable)
const auto &module_ptr = (*maybe_found).first;
@@ -1263,7 +1254,6 @@ std::function<TypedValue(const TypedValue *, int64_t, const FunctionContext &ctx
if (function_name == kId) return Id;
if (function_name == "LAST") return Last;
if (function_name == "PROPERTIES") return Properties;
if (function_name == "RANDOMUUID") return RandomUuid;
if (function_name == "SIZE") return Size;
if (function_name == "STARTNODE") return StartNode;
if (function_name == "TIMESTAMP") return Timestamp;

View File

@@ -793,15 +793,15 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
const auto &element_symbol = symbol_table_->at(*extract.identifier_);
TypedValue::TVector result(ctx_->memory);
result.reserve(list.size());
for (auto &element : list) {
for (const auto &element : list) {
if (element.IsNull()) {
result.emplace_back();
} else {
frame_->at(element_symbol) = std::move(element);
frame_->at(element_symbol) = element;
result.emplace_back(extract.expression_->Accept(*this));
}
}
return TypedValue(std::move(result), ctx_->memory);
return TypedValue(result, ctx_->memory);
}
TypedValue Visit(Exists &exists) override { return TypedValue{frame_->at(symbol_table_->at(exists)), ctx_->memory}; }

Some files were not shown because too many files have changed in this diff Show More