Compare commits

..

2 Commits

Author SHA1 Message Date
imilinovic
3bf6cbf9b9 Merge branch 'master' of github.com:memgraph/memgraph into gc-proactive-deletion 2023-11-14 11:47:41 +01:00
Gareth Lloyd
cab59feb50 More proactive deletion 2023-11-02 21:57:34 +00:00
351 changed files with 3588 additions and 9112 deletions

View File

@@ -32,7 +32,7 @@ jobs:
# Build only memgraph release binaries.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=release ..
cmake -DCMAKE_BUILD_TYPE=release ..
make -j$THREADS
- name: Run macro benchmarks

View File

@@ -43,7 +43,7 @@ jobs:
# Build community binaries.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMG_ENTERPRISE=OFF ..
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMG_ENTERPRISE=OFF ..
make -j$THREADS
- name: Run unit tests
@@ -111,7 +111,7 @@ jobs:
source /opt/toolchain-v4/activate
cd build
CC=clang CXX=clang++ cmake -DTEST_COVERAGE=ON -DASAN=ON -DUBSAN=ON ..
cmake -DTEST_COVERAGE=ON -DASAN=ON -DUBSAN=ON ..
make -j$THREADS memgraph__unit
- name: Run unit tests
@@ -178,7 +178,7 @@ jobs:
# Build debug binaries.
cd build
CC=clang CXX=clang++ cmake ..
cmake ..
make -j$THREADS
- name: Run leftover CTest tests
@@ -229,6 +229,10 @@ jobs:
# branches and tags. (default: 1)
fetch-depth: 0
- name: Check e2e service dependencies
run: |
cd tests/e2e
./dependency_check.sh
- name: Build release binaries
run: |
@@ -240,7 +244,7 @@ jobs:
# Build release binaries.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Release ..
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j$THREADS
- name: Run GQL Behave tests
@@ -267,13 +271,6 @@ jobs:
cd build
ctest -R memgraph__unit --output-on-failure -j$THREADS
- name: Ensure Kafka and Pulsar are up
run: |
cd tests/e2e/streams/kafka
docker-compose up -d
cd ../pulsar
docker-compose up -d
- name: Run e2e tests
run: |
cd tests
@@ -282,14 +279,6 @@ jobs:
cd e2e
./run.sh
- name: Ensure Kafka and Pulsar are down
if: always()
run: |
cd tests/e2e/streams/kafka
docker-compose down
cd ../pulsar
docker-compose down
- name: Run stress test (plain)
run: |
cd tests/stress
@@ -362,7 +351,7 @@ jobs:
./init
# Build only memgraph release binarie.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
make -j$THREADS memgraph
- name: Run Jepsen tests
@@ -403,7 +392,7 @@ jobs:
# Build only memgraph release binaries.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=release ..
cmake -DCMAKE_BUILD_TYPE=release ..
make -j$THREADS
- name: Run macro benchmarks

View File

@@ -31,7 +31,7 @@ jobs:
# Build debug binaries.
cd build
CC=clang CXX=clang++ cmake ..
cmake ..
make -j$THREADS
- name: Run clang-tidy

263
.github/workflows/package_all.yaml vendored Normal file
View File

@@ -0,0 +1,263 @@
name: Package All
# TODO(gitbuda): Cleanup docker container if GHA job was canceled.
on:
workflow_dispatch:
inputs:
memgraph_version:
description: "Memgraph version to upload as. If empty upload is skipped. Format: 'X.Y.Z'"
required: false
build_type:
type: choice
description: "Memgraph Build type. Default value is Release."
default: 'Release'
options:
- Release
- RelWithDebInfo
jobs:
centos-7:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package centos-7 ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: centos-7
path: build/output/centos-7/memgraph*.rpm
centos-9:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package centos-9 ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: centos-9
path: build/output/centos-9/memgraph*.rpm
debian-10:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package debian-10 ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: debian-10
path: build/output/debian-10/memgraph*.deb
debian-11:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package debian-11 ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: debian-11
path: build/output/debian-11/memgraph*.deb
docker:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
cd release/package
./run.sh package debian-11 ${{ github.event.inputs.build_type }} --for-docker
./run.sh docker
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: docker
path: build/output/docker/memgraph*.tar.gz
ubuntu-1804:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package ubuntu-18.04 ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: ubuntu-18.04
path: build/output/ubuntu-18.04/memgraph*.deb
ubuntu-2004:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package ubuntu-20.04 ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: ubuntu-20.04
path: build/output/ubuntu-20.04/memgraph*.deb
ubuntu-2204:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package ubuntu-22.04 ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: ubuntu-22.04
path: build/output/ubuntu-22.04/memgraph*.deb
debian-11-platform:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package debian-11 ${{ github.event.inputs.build_type }} --for-platform
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: debian-11-platform
path: build/output/debian-11/memgraph*.deb
fedora-36:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package fedora-36 ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: fedora-36
path: build/output/fedora-36/memgraph*.rpm
amzn-2:
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package amzn-2 ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: amzn-2
path: build/output/amzn-2/memgraph*.rpm
debian-11-arm:
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
timeout-minutes: 120
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package debian-11-arm ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: debian-11-aarch64
path: build/output/debian-11-arm/memgraph*.deb
ubuntu-2204-arm:
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
timeout-minutes: 120
steps:
- name: "Set up repository"
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package ubuntu-22.04-arm ${{ github.event.inputs.build_type }}
- name: "Upload package"
uses: actions/upload-artifact@v3
with:
name: ubuntu-22.04-aarch64
path: build/output/ubuntu-22.04-arm/memgraph*.deb
upload-to-s3:
# only run upload if we specified version. Allows for runs without upload
if: "${{ github.event.inputs.memgraph_version != '' }}"
needs: [centos-7, centos-9, debian-10, debian-11, docker, ubuntu-1804, ubuntu-2004, ubuntu-2204, debian-11-platform, fedora-36, amzn-2, debian-11-arm, ubuntu-2204-arm]
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v3
with:
# name: # if name input parameter is not provided, all artifacts are downloaded
# and put in directories named after each one.
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 }}/"

View File

@@ -1,4 +1,4 @@
name: Package memgraph
name: Package Specific
# TODO(gitbuda): Cleanup docker container if GHA job was canceled.
@@ -10,17 +10,16 @@ on:
required: false
build_type:
type: choice
description: "Memgraph Build type. Default value is Release"
description: "Memgraph Build type. Default value is Release."
default: 'Release'
options:
- Release
- RelWithDebInfo
target_os:
type: choice
description: "Target OS for which memgraph will be packaged. Select 'all' if you want to package for every listed OS. Default is Ubuntu 22.04"
description: "Target OS for which memgraph will be packaged. Default is Ubuntu 22.04"
default: 'ubuntu-22_04'
options:
- all
- amzn-2
- centos-7
- centos-9
@@ -37,7 +36,7 @@ on:
jobs:
amzn-2:
if: ${{ github.event.inputs.target_os == 'amzn-2' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'amzn-2' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -55,7 +54,7 @@ jobs:
path: build/output/amzn-2/memgraph*.rpm
centos-7:
if: ${{ github.event.inputs.target_os == 'centos-7' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'centos-7' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -73,7 +72,7 @@ jobs:
path: build/output/centos-7/memgraph*.rpm
centos-9:
if: ${{ github.event.inputs.target_os == 'centos-9' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'centos-9' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -91,7 +90,7 @@ jobs:
path: build/output/centos-9/memgraph*.rpm
debian-10:
if: ${{ github.event.inputs.target_os == 'debian-10' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'debian-10' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -109,7 +108,7 @@ jobs:
path: build/output/debian-10/memgraph*.deb
debian-11:
if: ${{ github.event.inputs.target_os == 'debian-11' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'debian-11' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -127,7 +126,7 @@ jobs:
path: build/output/debian-11/memgraph*.deb
debian-11-arm:
if: ${{ github.event.inputs.target_os == 'debian-11-arm' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'debian-11-arm' }}
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
timeout-minutes: 120
steps:
@@ -145,7 +144,7 @@ jobs:
path: build/output/debian-11-arm/memgraph*.deb
debian-11-platform:
if: ${{ github.event.inputs.target_os == 'debian-11-platform' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'debian-11-platform' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -163,7 +162,7 @@ jobs:
path: build/output/debian-11/memgraph*.deb
docker:
if: ${{ github.event.inputs.target_os == 'docker' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'docker' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -183,7 +182,7 @@ jobs:
path: build/output/docker/memgraph*.tar.gz
fedora-36:
if: ${{ github.event.inputs.target_os == 'fedora-36' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'fedora-36' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -201,7 +200,7 @@ jobs:
path: build/output/fedora-36/memgraph*.rpm
ubuntu-18_04:
if: ${{ github.event.inputs.target_os == 'ubuntu-18_04' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'ubuntu-18_04' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -219,7 +218,7 @@ jobs:
path: build/output/ubuntu-18.04/memgraph*.deb
ubuntu-20_04:
if: ${{ github.event.inputs.target_os == 'ubuntu-20_04' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'ubuntu-20_04' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -237,7 +236,7 @@ jobs:
path: build/output/ubuntu-20.04/memgraph*.deb
ubuntu-22_04:
if: ${{ github.event.inputs.target_os == 'ubuntu-22_04' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'ubuntu-22_04' }}
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
@@ -255,7 +254,7 @@ jobs:
path: build/output/ubuntu-22.04/memgraph*.deb
ubuntu-22_04-arm:
if: ${{ github.event.inputs.target_os == 'ubuntu-22_04-arm' || github.event.inputs.target_os == 'all' }}
if: ${{ github.event.inputs.target_os == 'ubuntu-22_04-arm' }}
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
timeout-minutes: 120
steps:

View File

@@ -30,7 +30,7 @@ jobs:
# Build only memgraph release binaries.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=release ..
cmake -DCMAKE_BUILD_TYPE=release ..
make -j$THREADS
- name: Get branch name (merge)

318
.github/workflows/release_centos8.yaml vendored Normal file
View File

@@ -0,0 +1,318 @@
name: Release CentOS 8
on:
workflow_dispatch:
inputs:
build_type:
type: choice
description: "Memgraph Build type. Default value is Release."
default: 'Release'
options:
- Release
- RelWithDebInfo
schedule:
- cron: "0 22 * * *"
jobs:
community_build:
name: "Community build"
runs-on: [self-hosted, Linux, X64, CentOS8]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 960
steps:
- name: Set up repository
uses: actions/checkout@v3
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build community binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Set default build_type to Release
INPUT_BUILD_TYPE=${{ github.event.inputs.build_type }}
BUILD_TYPE=${INPUT_BUILD_TYPE:-"Release"}
# Build community binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DMG_ENTERPRISE=OFF ..
make -j$THREADS
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
coverage_build:
name: "Coverage build"
runs-on: [self-hosted, Linux, X64, CentOS8]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
steps:
- name: Set up repository
uses: actions/checkout@v3
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build coverage binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build coverage binaries.
cd build
cmake -DTEST_COVERAGE=ON ..
make -j$THREADS memgraph__unit
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
- name: Compute code coverage
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Compute code coverage.
cd tools/github
./coverage_convert
# Package code coverage.
cd generated
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
- name: Save code coverage
uses: actions/upload-artifact@v3
with:
name: "Code coverage"
path: tools/github/generated/code_coverage.tar.gz
debug_build:
name: "Debug build"
runs-on: [self-hosted, Linux, X64, CentOS8]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
steps:
- name: Set up repository
uses: actions/checkout@v3
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build debug binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build debug binaries.
cd build
cmake ..
make -j$THREADS
- name: Run leftover CTest tests
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Run leftover CTest tests (all except unit and benchmark tests).
cd build
ctest -E "(memgraph__unit|memgraph__benchmark)" --output-on-failure
- name: Run drivers tests
run: |
./tests/drivers/run.sh
- name: Run integration tests
run: |
tests/integration/run.sh
- name: Run cppcheck and clang-format
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Run cppcheck and clang-format.
cd tools/github
./cppcheck_and_clang_format diff
- name: Save cppcheck and clang-format errors
uses: actions/upload-artifact@v3
with:
name: "Code coverage"
path: tools/github/cppcheck_and_clang_format.txt
release_build:
name: "Release build"
runs-on: [self-hosted, Linux, X64, CentOS8]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 960
steps:
- name: Set up repository
uses: actions/checkout@v3
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Set default build_type to Release
INPUT_BUILD_TYPE=${{ github.event.inputs.build_type }}
BUILD_TYPE=${INPUT_BUILD_TYPE:-"Release"}
# Build release binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Create enterprise RPM package
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
cd build
# create mgconsole
# we use the -B to force the build
make -j$THREADS -B mgconsole
# Create enterprise RPM package.
mkdir output && cd output
cpack -G RPM --config ../CPackConfig.cmake
rpmlint memgraph*.rpm
- name: Save enterprise RPM package
uses: actions/upload-artifact@v3
with:
name: "Enterprise RPM package"
path: build/output/memgraph*.rpm
- name: Run micro benchmark tests
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Run micro benchmark tests.
cd build
# The `eval` benchmark needs a large stack limit.
ulimit -s 262144
ctest -R memgraph__benchmark -V
- name: Run macro benchmark tests
run: |
cd tests/macro_benchmark
./harness QuerySuite MemgraphRunner \
--groups aggregation 1000_create unwind_create dense_expand match \
--no-strict
- name: Run parallel macro benchmark tests
run: |
cd tests/macro_benchmark
./harness QueryParallelSuite MemgraphRunner \
--groups aggregation_parallel create_parallel bfs_parallel \
--num-database-workers 9 --num-clients-workers 30 \
--no-strict
- name: Run GQL Behave tests
run: |
cd tests
./setup.sh /opt/toolchain-v4/activate
cd gql_behave
./continuous_integration
- name: Save quality assurance status
uses: actions/upload-artifact@v3
with:
name: "GQL Behave Status"
path: |
tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
- name: Run e2e tests
run: |
cd tests
./setup.sh /opt/toolchain-v4/activate
source ve3/bin/activate_e2e
cd e2e
./run.sh
- name: Run stress test (plain)
run: |
cd tests/stress
./continuous_integration
- name: Run stress test (SSL)
run: |
cd tests/stress
./continuous_integration --use-ssl
- name: Run stress test (large)
run: |
cd tests/stress
./continuous_integration --large-dataset
- name: Run durability test (plain)
run: |
cd tests/stress
source ve3/bin/activate
python3 durability --num-steps 5
- name: Run durability test (large)
run: |
cd tests/stress
source ve3/bin/activate
python3 durability --num-steps 20

View File

@@ -46,7 +46,7 @@ jobs:
# Build community binaries.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DMG_ENTERPRISE=OFF ..
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DMG_ENTERPRISE=OFF ..
make -j$THREADS
- name: Run unit tests
@@ -84,7 +84,7 @@ jobs:
# Build coverage binaries.
cd build
CC=clang CXX=clang++ cmake -DTEST_COVERAGE=ON ..
cmake -DTEST_COVERAGE=ON ..
make -j$THREADS memgraph__unit
- name: Run unit tests
@@ -141,7 +141,7 @@ jobs:
# Build debug binaries.
cd build
CC=clang CXX=clang++ cmake ..
cmake ..
make -j$THREADS
- name: Run leftover CTest tests
@@ -178,7 +178,7 @@ jobs:
release_build:
name: "Release build"
runs-on: [self-hosted, Linux, X64, Debian10, BigMemory]
runs-on: [self-hosted, Linux, X64, Debian10]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
@@ -207,7 +207,7 @@ jobs:
# Build release binaries.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Create enterprise DEB package
@@ -346,7 +346,7 @@ jobs:
# Build only memgraph release binary.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS memgraph
- name: Run Jepsen tests

View File

@@ -46,7 +46,7 @@ jobs:
# Build community binaries.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DMG_ENTERPRISE=OFF ..
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DMG_ENTERPRISE=OFF ..
make -j$THREADS
- name: Run unit tests
@@ -84,7 +84,7 @@ jobs:
# Build coverage binaries.
cd build
CC=clang CXX=clang++ cmake -DTEST_COVERAGE=ON ..
cmake -DTEST_COVERAGE=ON ..
make -j$THREADS memgraph__unit
- name: Run unit tests
@@ -141,7 +141,7 @@ jobs:
# Build debug binaries.
cd build
CC=clang CXX=clang++ cmake ..
cmake ..
make -j$THREADS
- name: Run leftover CTest tests
@@ -207,7 +207,7 @@ jobs:
# Build release binaries.
cd build
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Create enterprise DEB package

View File

@@ -1,22 +0,0 @@
# Path to sources
sonar.sources = .
# sonar.exclusions=
sonar.inclusions=src,include,query_modules
# Path to tests
sonar.tests = tests/
# sonar.test.exclusions=
# sonar.test.inclusions=
# Source encoding
# sonar.sourceEncoding=
# Exclusions for copy-paste detection
# sonar.cpd.exclusions=
# Python version (for python projects only)
# sonar.python.version=
# C++ standard version (for C++ projects only)
# If not specified, it defaults to the latest supported standard
# sonar.cfamily.reportingCppStandardOverride=c++98|c++11|c++14|c++17|c++20

View File

@@ -27,14 +27,21 @@ else ()
message(STATUS "CCache: Not used")
endif(CCACHE_FOUND AND USE_CCACHE)
# choose a compiler
# NOTE: must be choosen before use of project() or enable_language()
find_program(CLANG_FOUND clang)
find_program(CLANGXX_FOUND clang++)
if (CLANG_FOUND AND CLANGXX_FOUND)
set(CMAKE_C_COMPILER ${CLANG_FOUND})
set(CMAKE_CXX_COMPILER ${CLANGXX_FOUND})
else()
message(FATAL_ERROR "Couldn't find clang and/or clang++!")
endif()
# -----------------------------------------------------------------------------
project(memgraph LANGUAGES C CXX)
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(FATAL_ERROR "Clang compiler is required")
endif()
#TODO: upgrade to cmake 3.24 + CheckIPOSupported
#cmake_policy(SET CMP0138 NEW)
#include(CheckIPOSupported)

View File

@@ -111,14 +111,6 @@ modifications:
value: "false"
override: true
- name: "storage_parallel_schema_recovery"
value: "false"
override: true
- name: "storage_enable_schema_metadata"
value: "false"
override: true
- name: "query_callable_mappings_path"
value: "/etc/memgraph/apoc_compatibility_mappings.json"
override: true

View File

@@ -234,57 +234,7 @@ inline mgp_type *type_duration() { return MgInvoke<mgp_type *>(mgp_type_duration
inline mgp_type *type_nullable(mgp_type *type) { return MgInvoke<mgp_type *>(mgp_type_nullable, type); }
inline bool create_label_index(mgp_graph *graph, const char *label) {
return MgInvoke<int>(mgp_create_label_index, graph, label);
}
inline bool drop_label_index(mgp_graph *graph, const char *label) {
return MgInvoke<int>(mgp_drop_label_index, graph, label);
}
inline mgp_list *list_all_label_indices(mgp_graph *graph, mgp_memory *memory) {
return MgInvoke<mgp_list *>(mgp_list_all_label_indices, graph, memory);
}
inline bool create_label_property_index(mgp_graph *graph, const char *label, const char *property) {
return MgInvoke<int>(mgp_create_label_property_index, graph, label, property);
}
inline bool drop_label_property_index(mgp_graph *graph, const char *label, const char *property) {
return MgInvoke<int>(mgp_drop_label_property_index, graph, label, property);
}
inline mgp_list *list_all_label_property_indices(mgp_graph *graph, mgp_memory *memory) {
return MgInvoke<mgp_list *>(mgp_list_all_label_property_indices, graph, memory);
}
inline bool create_existence_constraint(mgp_graph *graph, const char *label, const char *property) {
return MgInvoke<int>(mgp_create_existence_constraint, graph, label, property);
}
inline bool drop_existence_constraint(mgp_graph *graph, const char *label, const char *property) {
return MgInvoke<int>(mgp_drop_existence_constraint, graph, label, property);
}
inline mgp_list *list_all_existence_constraints(mgp_graph *graph, mgp_memory *memory) {
return MgInvoke<mgp_list *>(mgp_list_all_existence_constraints, graph, memory);
}
inline bool create_unique_constraint(mgp_graph *memgraph_graph, const char *label, mgp_value *properties) {
return MgInvoke<int>(mgp_create_unique_constraint, memgraph_graph, label, properties);
}
inline bool drop_unique_constraint(mgp_graph *memgraph_graph, const char *label, mgp_value *properties) {
return MgInvoke<int>(mgp_drop_unique_constraint, memgraph_graph, label, properties);
}
inline mgp_list *list_all_unique_constraints(mgp_graph *graph, mgp_memory *memory) {
return MgInvoke<mgp_list *>(mgp_list_all_unique_constraints, graph, memory);
}
// mgp_graph
inline bool graph_is_transactional(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_transactional, graph); }
inline bool graph_is_mutable(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_mutable, graph); }
@@ -378,8 +328,6 @@ inline mgp_list *list_copy(mgp_list *list, mgp_memory *memory) {
inline void list_destroy(mgp_list *list) { mgp_list_destroy(list); }
inline bool list_contains_deleted(mgp_list *list) { return MgInvoke<int>(mgp_list_contains_deleted, list); }
inline void list_append(mgp_list *list, mgp_value *val) { MgInvokeVoid(mgp_list_append, list, val); }
inline void list_append_extend(mgp_list *list, mgp_value *val) { MgInvokeVoid(mgp_list_append_extend, list, val); }
@@ -398,8 +346,6 @@ inline mgp_map *map_copy(mgp_map *map, mgp_memory *memory) { return MgInvoke<mgp
inline void map_destroy(mgp_map *map) { mgp_map_destroy(map); }
inline bool map_contains_deleted(mgp_map *map) { return MgInvoke<int>(mgp_map_contains_deleted, map); }
inline void map_insert(mgp_map *map, const char *key, mgp_value *value) {
MgInvokeVoid(mgp_map_insert, map, key, value);
}
@@ -448,8 +394,6 @@ inline mgp_vertex *vertex_copy(mgp_vertex *v, mgp_memory *memory) {
inline void vertex_destroy(mgp_vertex *v) { mgp_vertex_destroy(v); }
inline bool vertex_is_deleted(mgp_vertex *v) { return MgInvoke<int>(mgp_vertex_is_deleted, v); }
inline bool vertex_equal(mgp_vertex *v1, mgp_vertex *v2) { return MgInvoke<int>(mgp_vertex_equal, v1, v2); }
inline size_t vertex_labels_count(mgp_vertex *v) { return MgInvoke<size_t>(mgp_vertex_labels_count, v); }
@@ -502,8 +446,6 @@ inline mgp_edge *edge_copy(mgp_edge *e, mgp_memory *memory) { return MgInvoke<mg
inline void edge_destroy(mgp_edge *e) { mgp_edge_destroy(e); }
inline bool edge_is_deleted(mgp_edge *e) { return MgInvoke<int>(mgp_edge_is_deleted, e); }
inline bool edge_equal(mgp_edge *e1, mgp_edge *e2) { return MgInvoke<int>(mgp_edge_equal, e1, e2); }
inline mgp_edge_type edge_get_type(mgp_edge *e) { return MgInvoke<mgp_edge_type>(mgp_edge_get_type, e); }
@@ -540,8 +482,6 @@ inline mgp_path *path_copy(mgp_path *path, mgp_memory *memory) {
inline void path_destroy(mgp_path *path) { mgp_path_destroy(path); }
inline bool path_contains_deleted(mgp_path *path) { return MgInvoke<int>(mgp_path_contains_deleted, path); }
inline void path_expand(mgp_path *path, mgp_edge *edge) { MgInvokeVoid(mgp_path_expand, path, edge); }
inline void path_pop(mgp_path *path) { MgInvokeVoid(mgp_path_pop, path); }

View File

@@ -429,9 +429,6 @@ enum mgp_error mgp_list_copy(struct mgp_list *list, struct mgp_memory *memory, s
/// Free the memory used by the given mgp_list and contained elements.
void mgp_list_destroy(struct mgp_list *list);
/// Return whether the given mgp_list contains any deleted values.
enum mgp_error mgp_list_contains_deleted(struct mgp_list *list, int *result);
/// Append a copy of mgp_value to mgp_list if capacity allows.
/// The list copies the given value and therefore does not take ownership of the
/// original value. You still need to call mgp_value_destroy to free the
@@ -472,9 +469,6 @@ enum mgp_error mgp_map_copy(struct mgp_map *map, struct mgp_memory *memory, stru
/// Free the memory used by the given mgp_map and contained items.
void mgp_map_destroy(struct mgp_map *map);
/// Return whether the given mgp_map contains any deleted values.
enum mgp_error mgp_map_contains_deleted(struct mgp_map *map, int *result);
/// Insert a new mapping from a NULL terminated character string to a value.
/// If a mapping with the same key already exists, it is *not* replaced.
/// In case of insertion, both the string and the value are copied into the map.
@@ -558,9 +552,6 @@ enum mgp_error mgp_path_copy(struct mgp_path *path, struct mgp_memory *memory, s
/// Free the memory used by the given mgp_path and contained vertices and edges.
void mgp_path_destroy(struct mgp_path *path);
/// Return whether the given mgp_path contains any deleted values.
enum mgp_error mgp_path_contains_deleted(struct mgp_path *path, int *result);
/// Append an edge continuing from the last vertex on the path.
/// The edge is copied into the path. Therefore, the path does not take
/// ownership of the original edge, so you still need to free the edge memory
@@ -734,9 +725,6 @@ enum mgp_error mgp_vertex_copy(struct mgp_vertex *v, struct mgp_memory *memory,
/// Free the memory used by a mgp_vertex.
void mgp_vertex_destroy(struct mgp_vertex *v);
/// Return whether the given mgp_vertex is deleted.
enum mgp_error mgp_vertex_is_deleted(struct mgp_vertex *v, int *result);
/// Result is non-zero if given vertices are equal, otherwise 0.
enum mgp_error mgp_vertex_equal(struct mgp_vertex *v1, struct mgp_vertex *v2, int *result);
@@ -831,9 +819,6 @@ enum mgp_error mgp_edge_copy(struct mgp_edge *e, struct mgp_memory *memory, stru
/// Free the memory used by a mgp_edge.
void mgp_edge_destroy(struct mgp_edge *e);
/// Return whether the given mgp_edge is deleted.
enum mgp_error mgp_edge_is_deleted(struct mgp_edge *e, int *result);
/// Result is non-zero if given edges are equal, otherwise 0.
enum mgp_error mgp_edge_equal(struct mgp_edge *e1, struct mgp_edge *e2, int *result);
@@ -891,77 +876,12 @@ enum mgp_error mgp_edge_iter_properties(struct mgp_edge *e, struct mgp_memory *m
enum mgp_error mgp_graph_get_vertex_by_id(struct mgp_graph *g, struct mgp_vertex_id id, struct mgp_memory *memory,
struct mgp_vertex **result);
/// Creates label index for given label.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if label index already exists, result will be 0, otherwise 1.
enum mgp_error mgp_create_label_index(struct mgp_graph *graph, const char *label, int *result);
/// Drop label index.
enum mgp_error mgp_drop_label_index(struct mgp_graph *graph, const char *label, int *result);
/// List all label indices.
enum mgp_error mgp_list_all_label_indices(struct mgp_graph *graph, struct mgp_memory *memory, struct mgp_list **result);
/// Creates label-property index for given label and propery.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if label property index already exists, result will be 0, otherwise 1.
enum mgp_error mgp_create_label_property_index(struct mgp_graph *graph, const char *label, const char *property,
int *result);
/// Drops label-property index for given label and propery.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if dropping label property index failed, result will be 0, otherwise 1.
enum mgp_error mgp_drop_label_property_index(struct mgp_graph *graph, const char *label, const char *property,
int *result);
/// List all label+property indices.
enum mgp_error mgp_list_all_label_property_indices(struct mgp_graph *graph, struct mgp_memory *memory,
struct mgp_list **result);
/// Creates existence constraint for given label and property.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if creating existence constraint failed, result will be 0, otherwise 1.
enum mgp_error mgp_create_existence_constraint(struct mgp_graph *graph, const char *label, const char *property,
int *result);
/// Drops existence constraint for given label and property.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if dropping existence constraint failed, result will be 0, otherwise 1.
enum mgp_error mgp_drop_existence_constraint(struct mgp_graph *graph, const char *label, const char *property,
int *result);
/// List all existence constraints.
enum mgp_error mgp_list_all_existence_constraints(struct mgp_graph *graph, struct mgp_memory *memory,
struct mgp_list **result);
/// Creates unique constraint for given label and properties.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if creating unique constraint failed, result will be 0, otherwise 1.
enum mgp_error mgp_create_unique_constraint(struct mgp_graph *graph, const char *label, struct mgp_value *properties,
int *result);
/// Drops unique constraint for given label and properties.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if dropping unique constraint failed, result will be 0, otherwise 1.
enum mgp_error mgp_drop_unique_constraint(struct mgp_graph *graph, const char *label, struct mgp_value *properties,
int *result);
/// List all unique constraints
enum mgp_error mgp_list_all_unique_constraints(struct mgp_graph *graph, struct mgp_memory *memory,
struct mgp_list **result);
/// Result is non-zero if the graph can be modified.
/// If a graph is immutable, then vertices cannot be created or deleted, and all of the returned vertices will be
/// immutable also. The same applies for edges.
/// Current implementation always returns without errors.
enum mgp_error mgp_graph_is_mutable(struct mgp_graph *graph, int *result);
/// Result is non-zero if the graph is in transactional storage mode.
/// If a graph is not in transactional mode (i.e. analytical mode), then vertices and edges can be missing
/// because changes from other transactions are visible.
/// Current implementation always returns without errors.
enum mgp_error mgp_graph_is_transactional(struct mgp_graph *graph, int *result);
/// Add a new vertex to the graph.
/// Resulting vertex must be freed using mgp_vertex_destroy.
/// Return mgp_error::MGP_ERROR_IMMUTABLE_OBJECT if `graph` is immutable.

File diff suppressed because it is too large Load Diff

View File

@@ -259,7 +259,7 @@ repo_clone_try_double "${primary_urls[absl]}" "${secondary_urls[absl]}" "absl" "
# jemalloc ea6b3e973b477b8061e0076bb257dbd7f3faa756
JEMALLOC_COMMIT_VERSION="5.2.1"
repo_clone_try_double "${primary_urls[jemalloc]}" "${secondary_urls[jemalloc]}" "jemalloc" "$JEMALLOC_COMMIT_VERSION"
repo_clone_try_double "${secondary_urls[jemalloc]}" "${secondary_urls[jemalloc]}" "jemalloc" "$JEMALLOC_COMMIT_VERSION"
# this is hack for cmake in libs to set path, and for FindJemalloc to use Jemalloc_INCLUDE_DIR
pushd jemalloc

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-08-12
CHANGE DATE: 2027-30-10
CHANGE LICENSE: Apache License, Version 2.0
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.

View File

@@ -10,33 +10,18 @@
// licenses/APL.txt.
#include <mgp.hpp>
#include "utils/string.hpp"
#include <optional>
namespace Schema {
constexpr std::string_view kStatusKept = "Kept";
constexpr std::string_view kStatusCreated = "Created";
constexpr std::string_view kStatusDropped = "Dropped";
/*NodeTypeProperties and RelTypeProperties constants*/
constexpr std::string_view kReturnNodeType = "nodeType";
constexpr std::string_view kProcedureNodeType = "node_type_properties";
constexpr std::string_view kProcedureRelType = "rel_type_properties";
constexpr std::string_view kProcedureAssert = "assert";
constexpr std::string_view kReturnLabels = "nodeLabels";
constexpr std::string_view kReturnRelType = "relType";
constexpr std::string_view kReturnPropertyName = "propertyName";
constexpr std::string_view kReturnPropertyType = "propertyTypes";
constexpr std::string_view kReturnMandatory = "mandatory";
constexpr std::string_view kReturnLabel = "label";
constexpr std::string_view kReturnKey = "key";
constexpr std::string_view kReturnKeys = "keys";
constexpr std::string_view kReturnUnique = "unique";
constexpr std::string_view kReturnAction = "action";
constexpr std::string_view kParameterIndices = "indices";
constexpr std::string_view kParameterUniqueConstraints = "unique_constraints";
constexpr std::string_view kParameterExistenceConstraints = "existence_constraints";
constexpr std::string_view kParameterDropExisting = "drop_existing";
std::string TypeOf(const mgp::Type &type);
@@ -50,7 +35,6 @@ void ProcessPropertiesRel(mgp::Record &record, const std::string_view &type, con
void NodeTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory);
void RelTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory);
void Assert(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory);
} // namespace Schema
/*we have << operator for type in Cpp API, but in it we return somewhat different strings than I would like in this
@@ -108,129 +92,31 @@ void Schema::ProcessPropertiesRel(mgp::Record &record, const std::string_view &t
record.Insert(std::string(kReturnMandatory).c_str(), mandatory);
}
struct Property {
std::string name;
mgp::Value value;
Property(const std::string &name, mgp::Value &&value) : name(name), value(std::move(value)) {}
};
struct LabelsHash {
std::size_t operator()(const std::set<std::string> &set) const {
std::size_t seed = set.size();
for (const auto &i : set) {
seed ^= std::hash<std::string>{}(i) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
return seed;
}
};
struct LabelsComparator {
bool operator()(const std::set<std::string> &lhs, const std::set<std::string> &rhs) const { return lhs == rhs; }
};
struct PropertyComparator {
bool operator()(const Property &lhs, const Property &rhs) const { return lhs.name < rhs.name; }
};
struct PropertyInfo {
std::set<Property, PropertyComparator> properties;
bool mandatory;
};
void Schema::NodeTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, mgp_result *result,
mgp_memory *memory) {
void Schema::NodeTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
mgp::MemoryDispatcherGuard guard{memory};
const auto record_factory = mgp::RecordFactory(result);
try {
std::unordered_map<std::set<std::string>, PropertyInfo, LabelsHash, LabelsComparator> node_types_properties;
for (auto node : mgp::Graph(memgraph_graph).Nodes()) {
std::set<std::string> labels_set = {};
for (auto label : node.Labels()) {
labels_set.emplace(label);
}
if (node_types_properties.find(labels_set) == node_types_properties.end()) {
node_types_properties[labels_set] = PropertyInfo{std::set<Property, PropertyComparator>(), true};
}
if (node.Properties().empty()) {
node_types_properties[labels_set].mandatory = false; // if there is node with no property, it is not mandatory
continue;
}
auto &property_info = node_types_properties.at(labels_set);
for (auto &[key, prop] : node.Properties()) {
property_info.properties.emplace(key, std::move(prop));
if (property_info.mandatory) {
property_info.mandatory =
property_info.properties.size() == 1; // if there is only one property, it is mandatory
}
}
}
for (auto &[labels, property_info] : node_types_properties) {
std::string label_type;
mgp::List labels_list = mgp::List();
for (auto const &label : labels) {
label_type += ":`" + std::string(label) + "`";
labels_list.AppendExtend(mgp::Value(label));
}
for (auto const &prop : property_info.properties) {
auto record = record_factory.NewRecord();
ProcessPropertiesNode(record, label_type, labels_list, prop.name, TypeOf(prop.value.Type()),
property_info.mandatory);
}
if (property_info.properties.empty()) {
auto record = record_factory.NewRecord();
ProcessPropertiesNode<std::string>(record, label_type, labels_list, "", "", false);
}
}
} catch (const std::exception &e) {
record_factory.SetErrorMessage(e.what());
return;
}
}
void Schema::RelTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
mgp::MemoryDispatcherGuard guard{memory};
std::unordered_map<std::string, PropertyInfo> rel_types_properties;
;
const auto record_factory = mgp::RecordFactory(result);
try {
const mgp::Graph graph = mgp::Graph(memgraph_graph);
for (auto rel : graph.Relationships()) {
std::string rel_type = std::string(rel.Type());
if (rel_types_properties.find(rel_type) == rel_types_properties.end()) {
rel_types_properties[rel_type] = PropertyInfo{std::set<Property, PropertyComparator>(), true};
for (auto node : graph.Nodes()) {
std::string type = "";
mgp::List labels = mgp::List();
for (auto label : node.Labels()) {
labels.AppendExtend(mgp::Value(label));
type += ":`" + std::string(label) + "`";
}
if (rel.Properties().empty()) {
rel_types_properties[rel_type].mandatory = false; // if there is rel with no property, it is not mandatory
if (node.Properties().size() == 0) {
auto record = record_factory.NewRecord();
ProcessPropertiesNode<std::string>(record, type, labels, "", "", false);
continue;
}
auto &property_info = rel_types_properties.at(rel_type);
for (auto &[key, prop] : rel.Properties()) {
property_info.properties.emplace(key, std::move(prop));
if (property_info.mandatory) {
property_info.mandatory =
property_info.properties.size() == 1; // if there is only one property, it is mandatory
}
}
}
for (auto &[type, property_info] : rel_types_properties) {
std::string type_str = ":`" + std::string(type) + "`";
for (auto const &prop : property_info.properties) {
for (auto &[key, prop] : node.Properties()) {
auto property_type = mgp::List();
auto record = record_factory.NewRecord();
ProcessPropertiesRel(record, type_str, prop.name, TypeOf(prop.value.Type()), property_info.mandatory);
}
if (property_info.properties.empty()) {
auto record = record_factory.NewRecord();
ProcessPropertiesRel<std::string>(record, type_str, "", "", false);
property_type.AppendExtend(mgp::Value(TypeOf(prop.Type())));
ProcessPropertiesNode<mgp::List>(record, type, labels, key, property_type, true);
}
}
@@ -240,435 +126,58 @@ void Schema::RelTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, m
}
}
void InsertRecordForLabelIndex(const auto &record_factory, const std::string_view label,
const std::string_view status) {
auto record = record_factory.NewRecord();
record.Insert(std::string(Schema::kReturnLabel).c_str(), label);
record.Insert(std::string(Schema::kReturnKey).c_str(), "");
record.Insert(std::string(Schema::kReturnKeys).c_str(), mgp::List());
record.Insert(std::string(Schema::kReturnUnique).c_str(), false);
record.Insert(std::string(Schema::kReturnAction).c_str(), status);
}
void InsertRecordForUniqueConstraint(const auto &record_factory, const std::string_view label,
const mgp::List &properties, const std::string_view status) {
auto record = record_factory.NewRecord();
record.Insert(std::string(Schema::kReturnLabel).c_str(), label);
record.Insert(std::string(Schema::kReturnKey).c_str(), properties.ToString());
record.Insert(std::string(Schema::kReturnKeys).c_str(), properties);
record.Insert(std::string(Schema::kReturnUnique).c_str(), true);
record.Insert(std::string(Schema::kReturnAction).c_str(), status);
}
void InsertRecordForLabelPropertyIndexAndExistenceConstraint(const auto &record_factory, const std::string_view label,
const std::string_view property,
const std::string_view status) {
auto record = record_factory.NewRecord();
record.Insert(std::string(Schema::kReturnLabel).c_str(), label);
record.Insert(std::string(Schema::kReturnKey).c_str(), property);
record.Insert(std::string(Schema::kReturnKeys).c_str(), mgp::List({mgp::Value(property)}));
record.Insert(std::string(Schema::kReturnUnique).c_str(), false);
record.Insert(std::string(Schema::kReturnAction).c_str(), status);
}
void ProcessCreatingLabelIndex(const std::string_view label, const std::set<std::string_view> &existing_label_indices,
mgp_graph *memgraph_graph, const auto &record_factory) {
if (existing_label_indices.contains(label)) {
InsertRecordForLabelIndex(record_factory, label, Schema::kStatusKept);
} else if (mgp::CreateLabelIndex(memgraph_graph, label)) {
InsertRecordForLabelIndex(record_factory, label, Schema::kStatusCreated);
}
}
template <typename TFunc>
void ProcessCreatingLabelPropertyIndexAndExistenceConstraint(const std::string_view label,
const std::string_view property,
const std::set<std::string_view> &existing_collection,
const TFunc &func_creation, mgp_graph *memgraph_graph,
const auto &record_factory) {
const auto label_property_search_key = std::string(label) + ":" + std::string(property);
if (existing_collection.contains(label_property_search_key)) {
InsertRecordForLabelPropertyIndexAndExistenceConstraint(record_factory, label, property, Schema::kStatusKept);
} else if (func_creation(memgraph_graph, label, property)) {
InsertRecordForLabelPropertyIndexAndExistenceConstraint(record_factory, label, property, Schema::kStatusCreated);
}
}
/// We collect properties for which index was created.
using AssertedIndices = std::set<std::string, std::less<>>;
AssertedIndices CreateIndicesForLabel(const std::string_view label, const mgp::Value &properties_val,
mgp_graph *memgraph_graph, const auto &record_factory,
const std::set<std::string_view> &existing_label_indices,
const std::set<std::string_view> &existing_label_property_indices) {
AssertedIndices asserted_indices;
if (!properties_val.IsList()) {
return {};
}
if (const auto properties = properties_val.ValueList();
properties.Empty() && mgp::CreateLabelIndex(memgraph_graph, label)) {
InsertRecordForLabelIndex(record_factory, label, Schema::kStatusCreated);
asserted_indices.emplace("");
} else {
std::for_each(properties.begin(), properties.end(),
[&label, &existing_label_indices, &existing_label_property_indices, &memgraph_graph, &record_factory,
&asserted_indices](const mgp::Value &property) {
if (!property.IsString()) {
return;
}
const auto property_str = property.ValueString();
if (property_str.empty()) {
ProcessCreatingLabelIndex(label, existing_label_indices, memgraph_graph, record_factory);
asserted_indices.emplace("");
} else {
ProcessCreatingLabelPropertyIndexAndExistenceConstraint(
label, property_str, existing_label_property_indices, mgp::CreateLabelPropertyIndex,
memgraph_graph, record_factory);
asserted_indices.emplace(property_str);
}
});
}
return asserted_indices;
}
void ProcessIndices(const mgp::Map &indices_map, mgp_graph *memgraph_graph, const auto &record_factory,
bool drop_existing) {
auto mgp_existing_label_indices = mgp::ListAllLabelIndices(memgraph_graph);
auto mgp_existing_label_property_indices = mgp::ListAllLabelPropertyIndices(memgraph_graph);
std::set<std::string_view> existing_label_indices;
std::transform(mgp_existing_label_indices.begin(), mgp_existing_label_indices.end(),
std::inserter(existing_label_indices, existing_label_indices.begin()),
[](const mgp::Value &index) { return index.ValueString(); });
std::set<std::string_view> existing_label_property_indices;
std::transform(mgp_existing_label_property_indices.begin(), mgp_existing_label_property_indices.end(),
std::inserter(existing_label_property_indices, existing_label_property_indices.begin()),
[](const mgp::Value &index) { return index.ValueString(); });
std::set<std::string> asserted_label_indices;
std::set<std::string> asserted_label_property_indices;
auto merge_label_property = [](const std::string &label, const std::string &property) {
return label + ":" + property;
};
for (const auto &index : indices_map) {
const std::string_view label = index.key;
const mgp::Value &properties_val = index.value;
AssertedIndices asserted_indices_new = CreateIndicesForLabel(
label, properties_val, memgraph_graph, record_factory, existing_label_indices, existing_label_property_indices);
if (!drop_existing) {
continue;
}
std::ranges::for_each(asserted_indices_new, [&asserted_label_indices, &asserted_label_property_indices, label,
&merge_label_property](const std::string &property) {
if (property.empty()) {
asserted_label_indices.emplace(label);
} else {
asserted_label_property_indices.emplace(merge_label_property(std::string(label), property));
}
});
}
if (!drop_existing) {
return;
}
std::set<std::string_view> label_indices_to_drop;
std::ranges::set_difference(existing_label_indices, asserted_label_indices,
std::inserter(label_indices_to_drop, label_indices_to_drop.begin()));
std::ranges::for_each(label_indices_to_drop, [memgraph_graph, &record_factory](const std::string_view label) {
if (mgp::DropLabelIndex(memgraph_graph, label)) {
InsertRecordForLabelIndex(record_factory, label, Schema::kStatusDropped);
}
});
std::set<std::string_view> label_property_indices_to_drop;
std::ranges::set_difference(existing_label_property_indices, asserted_label_property_indices,
std::inserter(label_property_indices_to_drop, label_property_indices_to_drop.begin()));
auto decouple_label_property = [](const std::string_view label_property) {
const auto label_size = label_property.find(':');
const auto label = std::string(label_property.substr(0, label_size));
const auto property = std::string(label_property.substr(label_size + 1));
return std::make_pair(label, property);
};
std::ranges::for_each(label_property_indices_to_drop, [memgraph_graph, &record_factory, decouple_label_property](
const std::string_view label_property) {
const auto [label, property] = decouple_label_property(label_property);
if (mgp::DropLabelPropertyIndex(memgraph_graph, label, property)) {
InsertRecordForLabelPropertyIndexAndExistenceConstraint(record_factory, label, property, Schema::kStatusDropped);
}
});
}
using ExistenceConstraintsStorage = std::set<std::string_view>;
ExistenceConstraintsStorage CreateExistenceConstraintsForLabel(
const std::string_view label, const mgp::Value &properties_val, mgp_graph *memgraph_graph,
const auto &record_factory, const std::set<std::string_view> &existing_existence_constraints) {
ExistenceConstraintsStorage asserted_existence_constraints;
if (!properties_val.IsList()) {
return asserted_existence_constraints;
}
auto validate_property = [](const mgp::Value &property) -> bool {
return property.IsString() && !property.ValueString().empty();
};
const auto &properties = properties_val.ValueList();
std::for_each(properties.begin(), properties.end(),
[&label, &existing_existence_constraints, &asserted_existence_constraints, &memgraph_graph,
&record_factory, &validate_property](const mgp::Value &property) {
if (!validate_property(property)) {
return;
}
const std::string_view property_str = property.ValueString();
asserted_existence_constraints.emplace(property_str);
ProcessCreatingLabelPropertyIndexAndExistenceConstraint(
label, property_str, existing_existence_constraints, mgp::CreateExistenceConstraint,
memgraph_graph, record_factory);
});
return asserted_existence_constraints;
}
void ProcessExistenceConstraints(const mgp::Map &existence_constraints_map, mgp_graph *memgraph_graph,
const auto &record_factory, bool drop_existing) {
auto mgp_existing_existence_constraints = mgp::ListAllExistenceConstraints(memgraph_graph);
std::set<std::string_view> existing_existence_constraints;
std::transform(mgp_existing_existence_constraints.begin(), mgp_existing_existence_constraints.end(),
std::inserter(existing_existence_constraints, existing_existence_constraints.begin()),
[](const mgp::Value &constraint) { return constraint.ValueString(); });
auto merge_label_property = [](const std::string_view label, const std::string_view property) {
auto str = std::string(label) + ":";
str += property;
return str;
};
ExistenceConstraintsStorage asserted_existence_constraints;
for (const auto &existing_constraint : existence_constraints_map) {
const std::string_view label = existing_constraint.key;
const mgp::Value &properties_val = existing_constraint.value;
auto asserted_existence_constraints_new = CreateExistenceConstraintsForLabel(
label, properties_val, memgraph_graph, record_factory, existing_existence_constraints);
if (!drop_existing) {
continue;
}
std::ranges::for_each(asserted_existence_constraints_new, [&asserted_existence_constraints, &merge_label_property,
label](const std::string_view property) {
asserted_existence_constraints.emplace(merge_label_property(label, property));
});
}
if (!drop_existing) {
return;
}
std::set<std::string_view> existence_constraints_to_drop;
std::ranges::set_difference(existing_existence_constraints, asserted_existence_constraints,
std::inserter(existence_constraints_to_drop, existence_constraints_to_drop.begin()));
auto decouple_label_property = [](const std::string_view label_property) {
const auto label_size = label_property.find(':');
const auto label = std::string(label_property.substr(0, label_size));
const auto property = std::string(label_property.substr(label_size + 1));
return std::make_pair(label, property);
};
std::ranges::for_each(existence_constraints_to_drop, [&](const std::string_view label_property) {
const auto [label, property] = decouple_label_property(label_property);
if (mgp::DropExistenceConstraint(memgraph_graph, label, property)) {
InsertRecordForLabelPropertyIndexAndExistenceConstraint(record_factory, label, property, Schema::kStatusDropped);
}
});
}
using AssertedUniqueConstraintsStorage = std::set<std::set<std::string_view>>;
AssertedUniqueConstraintsStorage CreateUniqueConstraintsForLabel(
const std::string_view label, const mgp::Value &unique_props_nested,
const std::map<std::string_view, AssertedUniqueConstraintsStorage> &existing_unique_constraints,
mgp_graph *memgraph_graph, const auto &record_factory) {
AssertedUniqueConstraintsStorage asserted_unique_constraints;
if (!unique_props_nested.IsList()) {
return asserted_unique_constraints;
}
auto validate_unique_constraint_props = [](const mgp::Value &properties) -> bool {
if (!properties.IsList()) {
return false;
}
const auto &properties_list = properties.ValueList();
if (properties_list.Empty()) {
return false;
}
return std::all_of(properties_list.begin(), properties_list.end(), [](const mgp::Value &property) {
return property.IsString() && !property.ValueString().empty();
});
};
auto unique_constraint_exists =
[](const std::string_view label, const std::set<std::string_view> &properties,
const std::map<std::string_view, AssertedUniqueConstraintsStorage> &existing_unique_constraints) -> bool {
auto iter = existing_unique_constraints.find(label);
if (iter == existing_unique_constraints.end()) {
return false;
}
return iter->second.find(properties) != iter->second.end();
};
for (const auto unique_props_nested_list = unique_props_nested.ValueList();
const auto &properties : unique_props_nested_list) {
if (!validate_unique_constraint_props(properties)) {
continue;
}
const auto properties_list = properties.ValueList();
std::set<std::string_view> properties_coll;
std::transform(properties_list.begin(), properties_list.end(),
std::inserter(properties_coll, properties_coll.begin()),
[](const mgp::Value &property) { return property.ValueString(); });
if (unique_constraint_exists(label, properties_coll, existing_unique_constraints)) {
InsertRecordForUniqueConstraint(record_factory, label, properties_list, Schema::kStatusKept);
} else if (mgp::CreateUniqueConstraint(memgraph_graph, label, properties.ptr())) {
InsertRecordForUniqueConstraint(record_factory, label, properties_list, Schema::kStatusCreated);
}
asserted_unique_constraints.emplace(std::move(properties_coll));
}
return asserted_unique_constraints;
}
void ProcessUniqueConstraints(const mgp::Map &unique_constraints_map, mgp_graph *memgraph_graph,
const auto &record_factory, bool drop_existing) {
auto mgp_existing_unique_constraints = mgp::ListAllUniqueConstraints(memgraph_graph);
// label-unique_constraints pair
std::map<std::string_view, AssertedUniqueConstraintsStorage> existing_unique_constraints;
for (const auto &constraint : mgp_existing_unique_constraints) {
auto constraint_list = constraint.ValueList();
std::set<std::string_view> properties;
for (int i = 1; i < constraint_list.Size(); i++) {
properties.emplace(constraint_list[i].ValueString());
}
const std::string_view label = constraint_list[0].ValueString();
auto [it, inserted] = existing_unique_constraints.try_emplace(label, AssertedUniqueConstraintsStorage{properties});
if (!inserted) {
it->second.emplace(std::move(properties));
}
}
std::map<std::string_view, AssertedUniqueConstraintsStorage> asserted_unique_constraints;
for (const auto &[label, unique_props_nested] : unique_constraints_map) {
auto asserted_unique_constraints_new = CreateUniqueConstraintsForLabel(
label, unique_props_nested, existing_unique_constraints, memgraph_graph, record_factory);
if (drop_existing) {
asserted_unique_constraints.emplace(label, std::move(asserted_unique_constraints_new));
}
}
if (!drop_existing) {
return;
}
std::vector<std::pair<std::string_view, std::set<std::string_view>>> unique_constraints_to_drop;
// Check for each label for we found existing constraint in the DB whether it was asserted.
// If no unique constraint was found with label, we can drop all unique constraints for this label. (if branch)
// If some unique constraint was found with label, we can drop only those unique constraints that were not asserted.
// (else branch.)
std::ranges::for_each(existing_unique_constraints, [&asserted_unique_constraints, &unique_constraints_to_drop](
const auto &existing_label_unique_constraints) {
const auto &label = existing_label_unique_constraints.first;
const auto &existing_unique_constraints_for_label = existing_label_unique_constraints.second;
const auto &asserted_unique_constraints_for_label = asserted_unique_constraints.find(label);
if (asserted_unique_constraints_for_label == asserted_unique_constraints.end()) {
std::ranges::for_each(
std::make_move_iterator(existing_unique_constraints_for_label.begin()),
std::make_move_iterator(existing_unique_constraints_for_label.end()),
[&unique_constraints_to_drop, &label](std::set<std::string_view> existing_unique_constraint_for_label) {
unique_constraints_to_drop.emplace_back(label, std::move(existing_unique_constraint_for_label));
});
} else {
const auto &asserted_unique_constraints_for_label_coll = asserted_unique_constraints_for_label->second;
std::ranges::for_each(
std::make_move_iterator(existing_unique_constraints_for_label.begin()),
std::make_move_iterator(existing_unique_constraints_for_label.end()),
[&unique_constraints_to_drop, &label, &asserted_unique_constraints_for_label_coll](
std::set<std::string_view> existing_unique_constraint_for_label) {
if (!asserted_unique_constraints_for_label_coll.contains(existing_unique_constraint_for_label)) {
unique_constraints_to_drop.emplace_back(label, std::move(existing_unique_constraint_for_label));
}
});
}
});
std::ranges::for_each(
unique_constraints_to_drop, [memgraph_graph, &record_factory](const auto &label_unique_constraint) {
const auto &[label, unique_constraint] = label_unique_constraint;
auto unique_constraint_list = mgp::List();
std::ranges::for_each(unique_constraint, [&unique_constraint_list](const std::string_view &property) {
unique_constraint_list.AppendExtend(mgp::Value(property));
});
if (mgp::DropUniqueConstraint(memgraph_graph, label, mgp::Value(unique_constraint_list).ptr())) {
InsertRecordForUniqueConstraint(record_factory, label, unique_constraint_list, Schema::kStatusDropped);
}
});
}
void Schema::Assert(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
void Schema::RelTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
mgp::MemoryDispatcherGuard guard{memory};
;
const auto record_factory = mgp::RecordFactory(result);
auto arguments = mgp::List(args);
auto indices_map = arguments[0].ValueMap();
auto unique_constraints_map = arguments[1].ValueMap();
auto existence_constraints_map = arguments[2].ValueMap();
auto drop_existing = arguments[3].ValueBool();
try {
const mgp::Graph graph = mgp::Graph(memgraph_graph);
ProcessIndices(indices_map, memgraph_graph, record_factory, drop_existing);
ProcessExistenceConstraints(existence_constraints_map, memgraph_graph, record_factory, drop_existing);
ProcessUniqueConstraints(unique_constraints_map, memgraph_graph, record_factory, drop_existing);
for (auto rel : graph.Relationships()) {
std::string type = ":`" + std::string(rel.Type()) + "`";
if (rel.Properties().size() == 0) {
auto record = record_factory.NewRecord();
ProcessPropertiesRel<std::string>(record, type, "", "", false);
continue;
}
for (auto &[key, prop] : rel.Properties()) {
auto property_type = mgp::List();
auto record = record_factory.NewRecord();
property_type.AppendExtend(mgp::Value(TypeOf(prop.Type())));
ProcessPropertiesRel<mgp::List>(record, type, key, property_type, true);
}
}
} catch (const std::exception &e) {
record_factory.SetErrorMessage(e.what());
return;
}
}
extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
try {
mgp::MemoryDispatcherGuard guard{memory};
;
AddProcedure(Schema::NodeTypeProperties, Schema::kProcedureNodeType, mgp::ProcedureType::Read, {},
{mgp::Return(Schema::kReturnNodeType, mgp::Type::String),
mgp::Return(Schema::kReturnLabels, {mgp::Type::List, mgp::Type::String}),
mgp::Return(Schema::kReturnPropertyName, mgp::Type::String),
mgp::Return(Schema::kReturnPropertyType, mgp::Type::Any),
mgp::Return(Schema::kReturnMandatory, mgp::Type::Bool)},
AddProcedure(Schema::NodeTypeProperties, std::string(Schema::kProcedureNodeType).c_str(), mgp::ProcedureType::Read,
{},
{mgp::Return(std::string(Schema::kReturnNodeType).c_str(), mgp::Type::String),
mgp::Return(std::string(Schema::kReturnLabels).c_str(), {mgp::Type::List, mgp::Type::String}),
mgp::Return(std::string(Schema::kReturnPropertyName).c_str(), mgp::Type::String),
mgp::Return(std::string(Schema::kReturnPropertyType).c_str(), mgp::Type::Any),
mgp::Return(std::string(Schema::kReturnMandatory).c_str(), mgp::Type::Bool)},
module, memory);
AddProcedure(Schema::RelTypeProperties, Schema::kProcedureRelType, mgp::ProcedureType::Read, {},
{mgp::Return(Schema::kReturnRelType, mgp::Type::String),
mgp::Return(Schema::kReturnPropertyName, mgp::Type::String),
mgp::Return(Schema::kReturnPropertyType, mgp::Type::Any),
mgp::Return(Schema::kReturnMandatory, mgp::Type::Bool)},
AddProcedure(Schema::RelTypeProperties, std::string(Schema::kProcedureRelType).c_str(), mgp::ProcedureType::Read,
{},
{mgp::Return(std::string(Schema::kReturnRelType).c_str(), mgp::Type::String),
mgp::Return(std::string(Schema::kReturnPropertyName).c_str(), mgp::Type::String),
mgp::Return(std::string(Schema::kReturnPropertyType).c_str(), mgp::Type::Any),
mgp::Return(std::string(Schema::kReturnMandatory).c_str(), mgp::Type::Bool)},
module, memory);
AddProcedure(
Schema::Assert, Schema::kProcedureAssert, mgp::ProcedureType::Read,
{
mgp::Parameter(Schema::kParameterIndices, {mgp::Type::Map, mgp::Type::Any}),
mgp::Parameter(Schema::kParameterUniqueConstraints, {mgp::Type::Map, mgp::Type::Any}),
mgp::Parameter(Schema::kParameterExistenceConstraints, {mgp::Type::Map, mgp::Type::Any},
mgp::Value(mgp::Map{})),
mgp::Parameter(Schema::kParameterDropExisting, mgp::Type::Bool, mgp::Value(true)),
},
{mgp::Return(Schema::kReturnLabel, mgp::Type::String), mgp::Return(Schema::kReturnKey, mgp::Type::String),
mgp::Return(Schema::kReturnKeys, {mgp::Type::List, mgp::Type::String}),
mgp::Return(Schema::kReturnUnique, mgp::Type::Bool), mgp::Return(Schema::kReturnAction, mgp::Type::String)},
module, memory);
} catch (const std::exception &e) {
std::cerr << "Error while initializing query module: " << e.what() << std::endl;
return 1;
}

View File

@@ -104,9 +104,7 @@ def retry(retry_limit, timeout=100):
except Exception:
time.sleep(timeout)
return func(*args, **kwargs)
return wrapper
return inner_func
@@ -202,19 +200,19 @@ if args.version:
try:
current_branch = get_output("git", "rev-parse", "--abbrev-ref", "HEAD")
if current_branch != "master":
branches = get_output("git", "branch", "-r", "--list", "origin/master")
if "origin/master" in branches:
branches = get_output("git", "branch")
if "master" in branches:
# If master is present locally, the fetch is allowed to fail
# because this script will still be able to compare against the
# master branch.
try:
get_output("git", "fetch", "origin", "master")
get_output("git", "fetch", "origin", "master:master")
except Exception:
pass
else:
# If master is not present locally, the fetch command has to
# succeed because something else will fail otherwise.
get_output("git", "fetch", "origin", "master")
get_output("git", "fetch", "origin", "master:master")
except Exception:
print("Fatal error while ensuring local master branch.")
sys.exit(1)
@@ -234,7 +232,7 @@ for branch in branches:
match = branch_regex.match(branch)
if match is not None:
version = tuple(map(int, match.group(1).split(".")))
master_branch_merge = get_output("git", "merge-base", "origin/master", branch)
master_branch_merge = get_output("git", "merge-base", "master", branch)
versions.append((version, branch, master_branch_merge))
versions.sort(reverse=True)
@@ -245,7 +243,7 @@ current_version = None
for version in versions:
version_tuple, branch, master_branch_merge = version
current_branch_merge = get_output("git", "merge-base", current_hash, branch)
master_current_merge = get_output("git", "merge-base", current_hash, "origin/master")
master_current_merge = get_output("git", "merge-base", current_hash, "master")
# The first check checks whether this commit is a child of `master` and
# the version branch was created before us.
# The second check checks whether this commit is a child of the version

View File

@@ -53,10 +53,7 @@ set_target_properties(memgraph PROPERTIES
OUTPUT_NAME "memgraph-${MEMGRAPH_VERSION}_${CMAKE_BUILD_TYPE}"
# Output the executable in main binary dir.
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
POSITION_INDEPENDENT_CODE ON
)
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
# Create symlink to the built executable.
add_custom_command(TARGET memgraph POST_BUILD

View File

@@ -13,7 +13,6 @@
#include <fmt/format.h>
#include <json/json.hpp>
#include <utility>
#include "storage/v2/temporal.hpp"
#include "utils/logging.hpp"
@@ -88,8 +87,8 @@ inline nlohmann::json PropertyValueToJson(const storage::PropertyValue &pv) {
return ret;
}
Log::Log(std::filesystem::path storage_directory, int32_t buffer_size, int32_t buffer_flush_interval_millis)
: storage_directory_(std::move(storage_directory)),
Log::Log(const std::filesystem::path &storage_directory, int32_t buffer_size, int32_t buffer_flush_interval_millis)
: storage_directory_(storage_directory),
buffer_size_(buffer_size),
buffer_flush_interval_millis_(buffer_flush_interval_millis),
started_(false) {}

View File

@@ -36,7 +36,7 @@ class Log {
};
public:
Log(std::filesystem::path storage_directory, int32_t buffer_size, int32_t buffer_flush_interval_millis);
Log(const std::filesystem::path &storage_directory, int32_t buffer_size, int32_t buffer_flush_interval_millis);
~Log();

View File

@@ -10,7 +10,6 @@
#include <cstdint>
#include <regex>
#include <utility>
#include <gflags/gflags.h>
@@ -561,20 +560,20 @@ Databases Databases::Deserialize(const nlohmann::json &data) {
}
#endif
User::User() = default;
User::User() {}
User::User(const std::string &username) : username_(utils::ToLowerCase(username)) {}
User::User(const std::string &username, std::string password_hash, const Permissions &permissions)
: username_(utils::ToLowerCase(username)), password_hash_(std::move(password_hash)), permissions_(permissions) {}
User::User(const std::string &username, const std::string &password_hash, const Permissions &permissions)
: username_(utils::ToLowerCase(username)), password_hash_(password_hash), permissions_(permissions) {}
#ifdef MG_ENTERPRISE
User::User(const std::string &username, std::string password_hash, const Permissions &permissions,
User::User(const std::string &username, const std::string &password_hash, const Permissions &permissions,
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access)
: username_(utils::ToLowerCase(username)),
password_hash_(std::move(password_hash)),
password_hash_(password_hash),
permissions_(permissions),
fine_grained_access_handler_(std::move(fine_grained_access_handler)),
database_access_(std::move(db_access)) {}
database_access_(db_access) {}
#endif
bool User::CheckPassword(const std::string &password) {

View File

@@ -14,7 +14,6 @@
#include <unordered_map>
#include <json/json.hpp>
#include <utility>
#include "dbms/constants.hpp"
#include "utils/logging.hpp"
@@ -302,8 +301,8 @@ class Databases final {
bool Contains(const std::string &db) const;
bool GetAllowAll() const { return allow_all_; }
const std::set<std::string, std::less<>> &GetGrants() const { return grants_dbs_; }
const std::set<std::string, std::less<>> &GetDenies() const { return denies_dbs_; }
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;
@@ -311,17 +310,14 @@ class Databases final {
static Databases Deserialize(const nlohmann::json &data);
private:
Databases(bool allow_all, std::set<std::string, std::less<>> grant, std::set<std::string, std::less<>> deny,
std::string default_db = dbms::kDefaultDB)
: grants_dbs_(std::move(grant)),
denies_dbs_(std::move(deny)),
allow_all_(allow_all),
default_db_(std::move(default_db)) {}
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, std::less<>> grants_dbs_; //!< set of databases with granted access
std::set<std::string, std::less<>> 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
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
@@ -331,9 +327,9 @@ class User final {
User();
explicit User(const std::string &username);
User(const std::string &username, std::string password_hash, const Permissions &permissions);
User(const std::string &username, const std::string &password_hash, const Permissions &permissions);
#ifdef MG_ENTERPRISE
User(const std::string &username, std::string password_hash, const Permissions &permissions,
User(const std::string &username, const std::string &password_hash, const Permissions &permissions,
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access = {});
#endif
User(const User &) = default;

View File

@@ -14,7 +14,6 @@
#include <map>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "communication/bolt/v1/codes.hpp"
@@ -35,8 +34,8 @@ class FailureResponseException : public utils::BasicException {
explicit FailureResponseException(const std::string &message) : utils::BasicException{message} {}
FailureResponseException(std::string code, const std::string &message)
: utils::BasicException{message}, code_{std::move(code)} {}
FailureResponseException(const std::string &code, const std::string &message)
: utils::BasicException{message}, code_{code} {}
const std::string &code() const { return code_; }
SPECIALIZE_GET_EXCEPTION_NAME(FailureResponseException)

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
@@ -51,7 +51,7 @@ enum class ChunkState : uint8_t {
template <typename TBuffer>
class ChunkedDecoderBuffer {
public:
explicit ChunkedDecoderBuffer(TBuffer &buffer) : buffer_(buffer) { data_.reserve(kChunkMaxDataSize); }
ChunkedDecoderBuffer(TBuffer &buffer) : buffer_(buffer) { data_.reserve(kChunkMaxDataSize); }
/**
* Reads data from the internal buffer.

View File

@@ -401,11 +401,11 @@ class Decoder {
}
auto &labels = dv.ValueList();
vertex.labels.reserve(labels.size());
for (auto &label : labels) {
if (label.type() != Value::Type::String) {
for (size_t i = 0; i < labels.size(); ++i) {
if (labels[i].type() != Value::Type::String) {
return false;
}
vertex.labels.emplace_back(std::move(label.ValueString()));
vertex.labels.emplace_back(std::move(labels[i].ValueString()));
}
// read properties

View File

@@ -111,12 +111,12 @@ class BaseEncoder {
void WriteList(const std::vector<Value> &value) {
WriteTypeSize(value.size(), MarkerList);
for (const auto &x : value) WriteValue(x);
for (auto &x : value) WriteValue(x);
}
void WriteMap(const std::map<std::string, Value> &value) {
WriteTypeSize(value.size(), MarkerMap);
for (const auto &x : value) {
for (auto &x : value) {
WriteString(x.first);
WriteValue(x.second);
}
@@ -205,11 +205,11 @@ class BaseEncoder {
WriteRAW(utils::UnderlyingCast(Marker::TinyStruct) + 3);
WriteRAW(utils::UnderlyingCast(Signature::Path));
WriteTypeSize(path.vertices.size(), MarkerList);
for (const auto &v : path.vertices) WriteVertex(v);
for (auto &v : path.vertices) WriteVertex(v);
WriteTypeSize(path.edges.size(), MarkerList);
for (const auto &e : path.edges) WriteEdge(e);
for (auto &e : path.edges) WriteEdge(e);
WriteTypeSize(path.indices.size(), MarkerList);
for (const auto &i : path.indices) WriteInt(i);
for (auto &i : path.indices) WriteInt(i);
}
void WriteDate(const utils::Date &date) {

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
@@ -48,7 +48,7 @@ namespace memgraph::communication::bolt {
template <class TOutputStream>
class ChunkedEncoderBuffer {
public:
explicit ChunkedEncoderBuffer(TOutputStream &output_stream) : output_stream_(output_stream) {}
ChunkedEncoderBuffer(TOutputStream &output_stream) : output_stream_(output_stream) {}
/**
* Writes n values into the buffer. If n is bigger than whole chunk size

View File

@@ -39,7 +39,7 @@ class ClientEncoder : private BaseEncoder<Buffer> {
using BaseEncoder<Buffer>::buffer_;
public:
explicit ClientEncoder(Buffer &buffer) : BaseEncoder<Buffer>(buffer) {}
ClientEncoder(Buffer &buffer) : BaseEncoder<Buffer>(buffer) {}
using BaseEncoder<Buffer>::UpdateVersion;

View File

@@ -32,7 +32,7 @@ class Encoder : private BaseEncoder<Buffer> {
using BaseEncoder<Buffer>::buffer_;
public:
explicit Encoder(Buffer &buffer) : BaseEncoder<Buffer>(buffer) {}
Encoder(Buffer &buffer) : BaseEncoder<Buffer>(buffer) {}
using BaseEncoder<Buffer>::UpdateVersion;

View File

@@ -93,7 +93,7 @@ State HandlePullDiscard(TSession &session, std::optional<int> n, std::optional<i
return State::Close;
}
if (summary.contains("has_more") && summary.at("has_more").ValueBool()) {
if (summary.count("has_more") && summary.at("has_more").ValueBool()) {
return State::Result;
}
@@ -148,13 +148,13 @@ State HandlePullDiscardV4(TSession &session, const State state, const Marker mar
spdlog::trace("Couldn't read extra field!");
}
const auto &extra_map = extra.ValueMap();
if (extra_map.contains("n")) {
if (extra_map.count("n")) {
if (const auto n_value = extra_map.at("n").ValueInt(); n_value != kPullAll) {
n = n_value;
}
}
if (extra_map.contains("qid")) {
if (extra_map.count("qid")) {
if (const auto qid_value = extra_map.at("qid").ValueInt(); qid_value != kPullLast) {
qid = qid_value;
}
@@ -367,16 +367,14 @@ State HandleReset(TSession &session, const Marker marker) {
return State::Close;
}
try {
session.Abort();
if (!session.encoder_.MessageSuccess({})) {
spdlog::trace("Couldn't send success message!");
return State::Close;
}
return State::Idle;
} catch (const std::exception &e) {
return HandleFailure(session, e);
if (!session.encoder_.MessageSuccess()) {
spdlog::trace("Couldn't send success message!");
return State::Close;
}
session.Abort();
return State::Idle;
}
template <typename TSession>
@@ -399,17 +397,19 @@ State HandleBegin(TSession &session, const State state, const Marker marker) {
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
if (!session.encoder_.MessageSuccess({})) {
spdlog::trace("Couldn't send success message!");
return State::Close;
}
try {
session.Configure(extra.ValueMap());
session.BeginTransaction(extra.ValueMap());
if (!session.encoder_.MessageSuccess({})) {
spdlog::trace("Couldn't send success message!");
return State::Close;
}
return State::Idle;
} catch (const std::exception &e) {
return HandleFailure(session, e);
}
return State::Idle;
}
template <typename TSession>
@@ -427,11 +427,11 @@ State HandleCommit(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.CommitTransaction();
if (!session.encoder_.MessageSuccess({})) {
spdlog::trace("Couldn't send success message!");
return State::Close;
}
session.CommitTransaction();
return State::Idle;
} catch (const std::exception &e) {
return HandleFailure(session, e);
@@ -453,11 +453,11 @@ State HandleRollback(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.RollbackTransaction();
if (!session.encoder_.MessageSuccess({})) {
spdlog::trace("Couldn't send success message!");
return State::Close;
}
session.RollbackTransaction();
return State::Idle;
} catch (const std::exception &e) {
return HandleFailure(session, e);

View File

@@ -42,11 +42,11 @@ std::optional<State> AuthenticateUser(TSession &session, Value &metadata) {
std::string username;
std::string password;
if (data["scheme"].ValueString() == "basic") {
if (!data.contains("principal")) { // Special case principal = ""
if (!data.count("principal")) { // Special case principal = ""
spdlog::warn("The client didn't supply the principal field! Trying with \"\"...");
data["principal"] = "";
}
if (!data.contains("credentials")) { // Special case credentials = ""
if (!data.count("credentials")) { // Special case credentials = ""
spdlog::warn("The client didn't supply the credentials field! Trying with \"\"...");
data["credentials"] = "";
}
@@ -118,7 +118,7 @@ std::optional<Value> GetMetadataV4(TSession &session, const Marker marker) {
}
auto &data = metadata.ValueMap();
if (!data.contains("user_agent")) {
if (!data.count("user_agent")) {
spdlog::warn("The client didn't supply the user agent!");
return std::nullopt;
}
@@ -142,7 +142,7 @@ std::optional<Value> GetInitDataV5(TSession &session, const Marker marker) {
}
const auto &data = metadata.ValueMap();
if (!data.contains("user_agent")) {
if (!data.count("user_agent")) {
spdlog::warn("The client didn't supply the user agent!");
return std::nullopt;
}

View File

@@ -91,7 +91,7 @@ struct UnboundedEdge {
* The decoder writes data into this structure.
*/
struct Path {
Path() = default;
Path() {}
Path(const std::vector<Vertex> &vertices, const std::vector<Edge> &edges) {
// Helper function. Looks for the given element in the collection. If found,

View File

@@ -132,7 +132,7 @@ class Client final {
*/
class ClientInputStream final {
public:
explicit ClientInputStream(Client &client);
ClientInputStream(Client &client);
ClientInputStream(const ClientInputStream &) = delete;
ClientInputStream(ClientInputStream &&) = delete;
@@ -156,7 +156,7 @@ class ClientInputStream final {
*/
class ClientOutputStream final {
public:
explicit ClientOutputStream(Client &client);
ClientOutputStream(Client &client);
ClientOutputStream(const ClientOutputStream &) = delete;
ClientOutputStream(ClientOutputStream &&) = delete;

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
@@ -34,7 +34,7 @@ ClientContext::ClientContext(bool use_ssl) : use_ssl_(use_ssl), ctx_(nullptr) {
}
ClientContext::ClientContext(const std::string &key_file, const std::string &cert_file) : ClientContext(true) {
if (!key_file.empty() && !cert_file.empty()) {
if (key_file != "" && cert_file != "") {
MG_ASSERT(SSL_CTX_use_certificate_file(ctx_, cert_file.c_str(), SSL_FILETYPE_PEM) == 1,
"Couldn't load client certificate from file: {}", cert_file);
MG_ASSERT(SSL_CTX_use_PrivateKey_file(ctx_, key_file.c_str(), SSL_FILETYPE_PEM) == 1,
@@ -124,7 +124,7 @@ ServerContext &ServerContext::operator=(ServerContext &&other) noexcept {
return *this;
}
ServerContext::~ServerContext() = default;
ServerContext::~ServerContext() {}
SSL_CTX *ServerContext::context() {
MG_ASSERT(ctx_);

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
@@ -15,7 +15,7 @@
namespace memgraph::communication {
std::string SslGetLastError() {
const std::string SslGetLastError() {
char buff[2048];
auto err = ERR_get_error();
ERR_error_string_n(err, buff, sizeof(buff));

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
@@ -18,6 +18,6 @@ namespace memgraph::communication {
/**
* This function reads and returns a string describing the last OpenSSL error.
*/
std::string SslGetLastError();
const std::string SslGetLastError();
} // namespace memgraph::communication

View File

@@ -38,7 +38,7 @@ class Listener final : public std::enable_shared_from_this<Listener<TRequestHand
Listener(Listener &&) = delete;
Listener &operator=(const Listener &) = delete;
Listener &operator=(Listener &&) = delete;
~Listener() = default;
~Listener() {}
template <typename... Args>
static std::shared_ptr<Listener> Create(Args &&...args) {

View File

@@ -17,7 +17,6 @@
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <gflags/gflags.h>
@@ -52,13 +51,13 @@ class Listener final {
using SessionHandler = Session<TSession, TSessionContext>;
public:
Listener(TSessionContext *data, ServerContext *context, int inactivity_timeout_sec, std::string service_name,
Listener(TSessionContext *data, ServerContext *context, int inactivity_timeout_sec, const std::string &service_name,
size_t workers_count)
: data_(data),
alive_(false),
context_(context),
inactivity_timeout_sec_(inactivity_timeout_sec),
service_name_(std::move(service_name)),
service_name_(service_name),
workers_count_(workers_count) {}
~Listener() {

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
@@ -80,7 +80,7 @@ class ResultStreamFaker {
std::transform(header.begin(), header.end(), column_widths.begin(), [](const auto &s) { return s.size(); });
// convert all the results into strings, and track max column width
const auto &results_data = results.GetResults();
auto &results_data = results.GetResults();
std::vector<std::vector<std::string>> result_strings(results_data.size(),
std::vector<std::string>(column_widths.size()));
for (int row_ind = 0; row_ind < static_cast<int>(results_data.size()); ++row_ind) {

View File

@@ -17,7 +17,6 @@
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <openssl/bio.h>
#include <openssl/err.h>
@@ -52,8 +51,7 @@ using InputStream = Buffer::ReadEnd;
*/
class OutputStream final {
public:
explicit OutputStream(std::function<bool(const uint8_t *, size_t, bool)> write_function)
: write_function_(std::move(write_function)) {}
OutputStream(std::function<bool(const uint8_t *, size_t, bool)> write_function) : write_function_(write_function) {}
OutputStream(const OutputStream &) = delete;
OutputStream(OutputStream &&) = delete;

View File

@@ -47,7 +47,7 @@ class Listener final : public std::enable_shared_from_this<Listener<TSession, TS
Listener(Listener &&) = delete;
Listener &operator=(const Listener &) = delete;
Listener &operator=(Listener &&) = delete;
~Listener() = default;
~Listener() {}
template <typename... Args>
static std::shared_ptr<Listener> Create(Args &&...args) {

View File

@@ -76,7 +76,7 @@ using tcp = boost::asio::ip::tcp;
class OutputStream final {
public:
explicit OutputStream(std::function<bool(const uint8_t *, size_t, bool)> write_function)
: write_function_(std::move(write_function)) {}
: write_function_(write_function) {}
OutputStream(const OutputStream &) = delete;
OutputStream(OutputStream &&) = delete;

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
@@ -16,7 +16,6 @@
#include <spdlog/sinks/base_sink.h>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <utility>
#include "communication/websocket/listener.hpp"
#include "io/network/endpoint.hpp"
@@ -46,7 +45,7 @@ class Server final {
class LoggingSink : public spdlog::sinks::base_sink<std::mutex> {
public:
explicit LoggingSink(std::weak_ptr<Listener> listener) : listener_(std::move(listener)) {}
explicit LoggingSink(std::weak_ptr<Listener> listener) : listener_(listener) {}
private:
void sink_it_(const spdlog::details::log_msg &msg) override;

View File

@@ -1,3 +1,3 @@
add_library(mg-dbms STATIC dbms_handler.cpp database.cpp replication_handler.cpp replication_client.cpp inmemory/replication_handlers.cpp)
add_library(mg-dbms STATIC database.cpp replication_handler.cpp inmemory/replication_handlers.cpp)
target_link_libraries(mg-dbms mg-utils mg-storage-v2 mg-query)

View File

@@ -15,10 +15,4 @@ namespace memgraph::dbms {
constexpr static const char *kDefaultDB = "memgraph"; //!< Name of the default database
#ifdef MG_EXPERIMENTAL_REPLICATION_MULTITENANCY
constexpr bool allow_mt_repl = true;
#else
constexpr bool allow_mt_repl = false;
#endif
} // namespace memgraph::dbms

View File

@@ -21,7 +21,7 @@ template struct memgraph::utils::Gatekeeper<memgraph::dbms::Database>;
namespace memgraph::dbms {
Database::Database(storage::Config config, replication::ReplicationState &repl_state)
Database::Database(storage::Config config, const replication::ReplicationState &repl_state)
: trigger_store_(config.durability.storage_directory / "triggers"),
streams_{config.durability.storage_directory / "streams"},
plan_cache_{FLAGS_query_plan_cache_max_size},

View File

@@ -48,7 +48,7 @@ class Database {
*
* @param config storage configuration
*/
explicit Database(storage::Config config, replication::ReplicationState &repl_state);
explicit Database(storage::Config config, const replication::ReplicationState &repl_state);
/**
* @brief Returns the raw storage pointer.
@@ -95,7 +95,7 @@ class Database {
*
* @return storage::StorageMode
*/
storage::StorageMode GetStorageMode() const noexcept { return storage_->GetStorageMode(); }
storage::StorageMode GetStorageMode() const { return storage_->GetStorageMode(); }
/**
* @brief Get the storage info

View File

@@ -51,7 +51,8 @@ class DatabaseHandler : public Handler<Database> {
* @param config Storage configuration
* @return HandlerT::NewResult
*/
HandlerT::NewResult New(std::string_view name, storage::Config config, replication::ReplicationState &repl_state) {
HandlerT::NewResult New(std::string_view name, storage::Config config,
const replication::ReplicationState &repl_state) {
// Control that no one is using the same data directory
if (std::any_of(begin(), end(), [&](auto &elem) {
auto db_acc = elem.second.access();

View File

@@ -1,75 +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.
#include "dbms/dbms_handler.hpp"
namespace memgraph::dbms {
#ifdef MG_ENTERPRISE
DbmsHandler::DbmsHandler(
storage::Config config,
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth,
bool recovery_on_startup, bool delete_on_drop)
: default_config_{std::move(config)},
delete_on_drop_(delete_on_drop),
repl_state_{ReplicationStateRootPath(default_config_)} {
// TODO: Decouple storage config from dbms config
// TODO: Save individual db configs inside the kvstore and restore from there
storage::UpdatePaths(default_config_, default_config_.durability.storage_directory / "databases");
const auto &db_dir = default_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);
}
}
// Startup replication state (if recovered at startup)
auto replica = [this](replication::RoleReplicaData const &data) {
// Register handlers
InMemoryReplicationHandlers::Register(this, *data.server);
if (!data.server->Start()) {
spdlog::error("Unable to start the replication server.");
return false;
}
return true;
};
// Replication frequent check start
auto main = [this](replication::RoleMainData &data) {
for (auto &client : data.registered_replicas_) {
StartReplicaClient(*this, client);
}
return true;
};
// Startup proccess for main/replica
MG_ASSERT(std::visit(memgraph::utils::Overloaded{replica, main}, repl_state_.ReplicationData()),
"Replica recovery failure!");
}
#endif
} // namespace memgraph::dbms

View File

@@ -26,11 +26,9 @@
#include "auth/auth.hpp"
#include "constants.hpp"
#include "dbms/database.hpp"
#include "dbms/inmemory/replication_handlers.hpp"
#ifdef MG_ENTERPRISE
#include "dbms/database_handler.hpp"
#endif
#include "dbms/replication_client.hpp"
#include "global.hpp"
#include "query/config.hpp"
#include "query/interpreter_context.hpp"
@@ -104,22 +102,52 @@ class DbmsHandler {
* @param recovery_on_startup restore databases (and its content) and authentication data
* @param delete_on_drop when dropping delete any associated directories on disk
*/
DbmsHandler(storage::Config config,
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth,
bool recovery_on_startup, bool delete_on_drop); // TODO If more arguments are added use a config strut
DbmsHandler(storage::Config config, const replication::ReplicationState &repl_state, auto *auth,
bool recovery_on_startup, bool delete_on_drop)
: lock_{utils::RWLock::Priority::READ},
default_config_{std::move(config)},
repl_state_(repl_state),
delete_on_drop_(delete_on_drop) {
// TODO: Decouple storage config from dbms config
// TODO: Save individual db configs inside the kvstore and restore from there
storage::UpdatePaths(default_config_, default_config_.durability.storage_directory / "databases");
const auto &db_dir = default_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);
}
}
}
#else
/**
* @brief Initialize the handler. A single database is supported in community edition.
*
* @param configs storage configuration
*/
DbmsHandler(storage::Config config)
: repl_state_{ReplicationStateRootPath(config)},
db_gatekeeper_{[&] {
DbmsHandler(storage::Config config, const replication::ReplicationState &repl_state)
: db_gatekeeper_{[&] {
config.name = kDefaultDB;
return std::move(config);
}(),
repl_state_} {}
repl_state} {}
#endif
#ifdef MG_ENTERPRISE
@@ -220,12 +248,6 @@ class DbmsHandler {
#endif
}
replication::ReplicationState &ReplicationState() { return repl_state_; }
replication::ReplicationState const &ReplicationState() const { return repl_state_; }
bool IsMain() const { return repl_state_.IsMain(); }
bool IsReplica() const { return repl_state_.IsReplica(); }
/**
* @brief Return the statistics all databases.
*
@@ -514,15 +536,14 @@ class DbmsHandler {
throw UnknownDatabaseException("Tried to retrieve an unknown database \"{}\".", name);
}
mutable LockT lock_{utils::RWLock::Priority::READ}; //!< protective lock
storage::Config default_config_; //!< Storage configuration used when creating new databases
DatabaseHandler db_handler_; //!< multi-tenancy storage handler
std::unique_ptr<kvstore::KVStore> durability_; //!< list of active dbs (pointer so we can postpone its creation)
bool delete_on_drop_; //!< Flag defining if dropping storage also deletes its directory
std::set<std::string> defunct_dbs_; //!< Databases that are in an unknown state due to various failures
#endif
replication::ReplicationState repl_state_; //!< Global replication state
#ifndef MG_ENTERPRISE
mutable LockT lock_; //!< protective lock
storage::Config default_config_; //!< Storage configuration used when creating new databases
const replication::ReplicationState &repl_state_; //!< Global replication state
DatabaseHandler db_handler_; //!< multi-tenancy storage handler
std::unique_ptr<kvstore::KVStore> durability_; //!< list of active dbs (pointer so we can postpone its creation)
bool delete_on_drop_; //!< Flag defining if dropping storage also deletes its directory
std::set<std::string> defunct_dbs_; //!< Databases that are in an unknown state due to various failures
#else
mutable utils::Gatekeeper<Database> db_gatekeeper_; //!< Single databases gatekeeper
#endif
};

View File

@@ -38,7 +38,7 @@ class Handler {
* @brief Empty Handler constructor.
*
*/
Handler() = default;
Handler() {}
/**
* @brief Generate a new context and corresponding configuration.

View File

@@ -10,7 +10,6 @@
// licenses/APL.txt.
#include "dbms/inmemory/replication_handlers.hpp"
#include <optional>
#include "dbms/constants.hpp"
#include "dbms/dbms_handler.hpp"
#include "replication/replication_server.hpp"
@@ -188,9 +187,9 @@ void InMemoryReplicationHandlers::SnapshotHandler(dbms::DbmsHandler *dbms_handle
storage::replication::Decoder decoder(req_reader);
auto *storage = static_cast<storage::InMemoryStorage *>(db_acc->get()->storage());
utils::EnsureDirOrDie(storage->recovery_.snapshot_directory_);
utils::EnsureDirOrDie(storage->snapshot_directory_);
const auto maybe_snapshot_path = decoder.ReadFile(storage->recovery_.snapshot_directory_);
const auto maybe_snapshot_path = decoder.ReadFile(storage->snapshot_directory_);
MG_ASSERT(maybe_snapshot_path, "Failed to load snapshot!");
spdlog::info("Received snapshot saved to {}", *maybe_snapshot_path);
@@ -220,12 +219,8 @@ void InMemoryReplicationHandlers::SnapshotHandler(dbms::DbmsHandler *dbms_handle
storage->timestamp_ = std::max(storage->timestamp_, recovery_info.next_timestamp);
spdlog::trace("Recovering indices and constraints from snapshot.");
memgraph::storage::durability::RecoverIndicesAndStats(recovered_snapshot.indices_constraints.indices,
&storage->indices_, &storage->vertices_,
storage->name_id_mapper_.get());
memgraph::storage::durability::RecoverConstraints(recovered_snapshot.indices_constraints.constraints,
&storage->constraints_, &storage->vertices_,
storage->name_id_mapper_.get());
storage::durability::RecoverIndicesAndConstraints(recovered_snapshot.indices_constraints, &storage->indices_,
&storage->constraints_, &storage->vertices_);
} catch (const storage::durability::RecoveryFailure &e) {
LOG_FATAL("Couldn't load the snapshot because of: {}", e.what());
}
@@ -237,7 +232,7 @@ void InMemoryReplicationHandlers::SnapshotHandler(dbms::DbmsHandler *dbms_handle
spdlog::trace("Deleting old snapshot files due to snapshot recovery.");
// Delete other durability files
auto snapshot_files = storage::durability::GetSnapshotFiles(storage->recovery_.snapshot_directory_, storage->uuid_);
auto snapshot_files = storage::durability::GetSnapshotFiles(storage->snapshot_directory_, storage->uuid_);
for (const auto &[path, uuid, _] : snapshot_files) {
if (path != *maybe_snapshot_path) {
spdlog::trace("Deleting snapshot file {}", path);
@@ -246,7 +241,7 @@ void InMemoryReplicationHandlers::SnapshotHandler(dbms::DbmsHandler *dbms_handle
}
spdlog::trace("Deleting old WAL files due to snapshot recovery.");
auto wal_files = storage::durability::GetWalFiles(storage->recovery_.wal_directory_, storage->uuid_);
auto wal_files = storage::durability::GetWalFiles(storage->wal_directory_, storage->uuid_);
if (wal_files) {
for (const auto &wal_file : *wal_files) {
spdlog::trace("Deleting WAL file {}", wal_file.path);
@@ -271,7 +266,7 @@ void InMemoryReplicationHandlers::WalFilesHandler(dbms::DbmsHandler *dbms_handle
storage::replication::Decoder decoder(req_reader);
auto *storage = static_cast<storage::InMemoryStorage *>(db_acc->get()->storage());
utils::EnsureDirOrDie(storage->recovery_.wal_directory_);
utils::EnsureDirOrDie(storage->wal_directory_);
for (auto i = 0; i < wal_file_number; ++i) {
LoadWal(storage, &decoder);
@@ -293,7 +288,7 @@ void InMemoryReplicationHandlers::CurrentWalHandler(dbms::DbmsHandler *dbms_hand
storage::replication::Decoder decoder(req_reader);
auto *storage = static_cast<storage::InMemoryStorage *>(db_acc->get()->storage());
utils::EnsureDirOrDie(storage->recovery_.wal_directory_);
utils::EnsureDirOrDie(storage->wal_directory_);
LoadWal(storage, &decoder);
@@ -374,9 +369,8 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
constexpr bool kSharedAccess = false;
std::optional<std::pair<uint64_t, storage::InMemoryStorage::ReplicationAccessor>> commit_timestamp_and_accessor;
auto const get_transaction = [storage, &commit_timestamp_and_accessor](
uint64_t commit_timestamp,
bool unique = kSharedAccess) -> storage::InMemoryStorage::ReplicationAccessor * {
auto get_transaction = [storage, &commit_timestamp_and_accessor](uint64_t commit_timestamp,
bool unique = kSharedAccess) {
if (!commit_timestamp_and_accessor) {
std::unique_ptr<storage::Storage::Accessor> acc = nullptr;
if (unique) {
@@ -420,11 +414,9 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
spdlog::trace(" Delete vertex {}", delta.vertex_create_delete.gid.AsUint());
auto *transaction = get_transaction(timestamp);
auto vertex = transaction->FindVertex(delta.vertex_create_delete.gid, View::NEW);
if (!vertex)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (!vertex) throw utils::BasicException("Invalid transaction!");
auto ret = transaction->DeleteVertex(&*vertex);
if (ret.HasError() || !ret.GetValue())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (ret.HasError() || !ret.GetValue()) throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::VERTEX_ADD_LABEL: {
@@ -432,11 +424,9 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
delta.vertex_add_remove_label.label);
auto *transaction = get_transaction(timestamp);
auto vertex = transaction->FindVertex(delta.vertex_add_remove_label.gid, View::NEW);
if (!vertex)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (!vertex) throw utils::BasicException("Invalid transaction!");
auto ret = vertex->AddLabel(transaction->NameToLabel(delta.vertex_add_remove_label.label));
if (ret.HasError() || !ret.GetValue())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (ret.HasError() || !ret.GetValue()) throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::VERTEX_REMOVE_LABEL: {
@@ -444,11 +434,9 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
delta.vertex_add_remove_label.label);
auto *transaction = get_transaction(timestamp);
auto vertex = transaction->FindVertex(delta.vertex_add_remove_label.gid, View::NEW);
if (!vertex)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (!vertex) throw utils::BasicException("Invalid transaction!");
auto ret = vertex->RemoveLabel(transaction->NameToLabel(delta.vertex_add_remove_label.label));
if (ret.HasError() || !ret.GetValue())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (ret.HasError() || !ret.GetValue()) throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::VERTEX_SET_PROPERTY: {
@@ -456,12 +444,10 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
delta.vertex_edge_set_property.property, delta.vertex_edge_set_property.value);
auto *transaction = get_transaction(timestamp);
auto vertex = transaction->FindVertex(delta.vertex_edge_set_property.gid, View::NEW);
if (!vertex)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (!vertex) throw utils::BasicException("Invalid transaction!");
auto ret = vertex->SetProperty(transaction->NameToProperty(delta.vertex_edge_set_property.property),
delta.vertex_edge_set_property.value);
if (ret.HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (ret.HasError()) throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::EDGE_CREATE: {
@@ -470,16 +456,13 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
delta.edge_create_delete.from_vertex.AsUint(), delta.edge_create_delete.to_vertex.AsUint());
auto *transaction = get_transaction(timestamp);
auto from_vertex = transaction->FindVertex(delta.edge_create_delete.from_vertex, View::NEW);
if (!from_vertex)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (!from_vertex) throw utils::BasicException("Invalid transaction!");
auto to_vertex = transaction->FindVertex(delta.edge_create_delete.to_vertex, View::NEW);
if (!to_vertex)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (!to_vertex) throw utils::BasicException("Invalid transaction!");
auto edge = transaction->CreateEdgeEx(&*from_vertex, &*to_vertex,
transaction->NameToEdgeType(delta.edge_create_delete.edge_type),
delta.edge_create_delete.gid);
if (edge.HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (edge.HasError()) throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::EDGE_DELETE: {
@@ -488,17 +471,16 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
delta.edge_create_delete.from_vertex.AsUint(), delta.edge_create_delete.to_vertex.AsUint());
auto *transaction = get_transaction(timestamp);
auto from_vertex = transaction->FindVertex(delta.edge_create_delete.from_vertex, View::NEW);
if (!from_vertex)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (!from_vertex) throw utils::BasicException("Invalid transaction!");
auto to_vertex = transaction->FindVertex(delta.edge_create_delete.to_vertex, View::NEW);
if (!to_vertex)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
auto edgeType = transaction->NameToEdgeType(delta.edge_create_delete.edge_type);
auto edge =
transaction->FindEdge(delta.edge_create_delete.gid, View::NEW, edgeType, &*from_vertex, &*to_vertex);
if (!edge) throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (auto ret = transaction->DeleteEdge(&*edge); ret.HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (!to_vertex) throw utils::BasicException("Invalid transaction!");
auto edges = from_vertex->OutEdges(View::NEW, {transaction->NameToEdgeType(delta.edge_create_delete.edge_type)},
&*to_vertex);
if (edges.HasError()) throw utils::BasicException("Invalid transaction!");
if (edges->edges.size() != 1) throw utils::BasicException("Invalid transaction!");
auto &edge = (*edges).edges[0];
auto ret = transaction->DeleteEdge(&edge);
if (ret.HasError()) throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::EDGE_SET_PROPERTY: {
@@ -515,8 +497,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
// yields an accessor that is only valid for managing the edge's
// properties.
auto edge = edge_acc.find(delta.vertex_edge_set_property.gid);
if (edge == edge_acc.end())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (edge == edge_acc.end()) throw utils::BasicException("Invalid transaction!");
// The edge visibility check must be done here manually because we
// don't allow direct access to the edges through the public API.
{
@@ -548,8 +529,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
}
}
});
if (!is_visible)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (!is_visible) throw utils::BasicException("Invalid transaction!");
}
EdgeRef edge_ref(&*edge);
// Here we create an edge accessor that we will use to get the
@@ -562,8 +542,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
auto ret = ea.SetProperty(transaction->NameToProperty(delta.vertex_edge_set_property.property),
delta.vertex_edge_set_property.value);
if (ret.HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (ret.HasError()) throw utils::BasicException("Invalid transaction!");
break;
}
@@ -573,8 +552,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
throw utils::BasicException("Invalid commit data!");
auto ret =
commit_timestamp_and_accessor->second.Commit(commit_timestamp_and_accessor->first, false /* not main */);
if (ret.HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (ret.HasError()) throw utils::BasicException("Invalid transaction!");
commit_timestamp_and_accessor = std::nullopt;
break;
}
@@ -584,14 +562,14 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
// Need to send the timestamp
auto *transaction = get_transaction(timestamp, kUniqueAccess);
if (transaction->CreateIndex(storage->NameToLabel(delta.operation_label.label)).HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::LABEL_INDEX_DROP: {
spdlog::trace(" Drop label index on :{}", delta.operation_label.label);
auto *transaction = get_transaction(timestamp, kUniqueAccess);
if (transaction->DropIndex(storage->NameToLabel(delta.operation_label.label)).HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::LABEL_INDEX_STATS_SET: {
@@ -622,7 +600,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
->CreateIndex(storage->NameToLabel(delta.operation_label_property.label),
storage->NameToProperty(delta.operation_label_property.property))
.HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::LABEL_PROPERTY_INDEX_DROP: {
@@ -633,7 +611,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
->DropIndex(storage->NameToLabel(delta.operation_label_property.label),
storage->NameToProperty(delta.operation_label_property.property))
.HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::LABEL_PROPERTY_INDEX_STATS_SET: {
@@ -665,8 +643,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
auto ret =
transaction->CreateExistenceConstraint(storage->NameToLabel(delta.operation_label_property.label),
storage->NameToProperty(delta.operation_label_property.property));
if (ret.HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
if (ret.HasError()) throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::EXISTENCE_CONSTRAINT_DROP: {
@@ -677,7 +654,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
->DropExistenceConstraint(storage->NameToLabel(delta.operation_label_property.label),
storage->NameToProperty(delta.operation_label_property.property))
.HasError())
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::UNIQUE_CONSTRAINT_CREATE: {
@@ -692,7 +669,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
auto ret = transaction->CreateUniqueConstraint(storage->NameToLabel(delta.operation_label_properties.label),
properties);
if (!ret.HasValue() || ret.GetValue() != UniqueConstraints::CreationStatus::SUCCESS)
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
throw utils::BasicException("Invalid transaction!");
break;
}
case WalDeltaData::Type::UNIQUE_CONSTRAINT_DROP: {
@@ -707,7 +684,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
auto ret =
transaction->DropUniqueConstraint(storage->NameToLabel(delta.operation_label_properties.label), properties);
if (ret != UniqueConstraints::DeletionStatus::SUCCESS) {
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
throw utils::BasicException("Invalid transaction!");
}
break;
}

View File

@@ -22,8 +22,14 @@
namespace memgraph::dbms {
inline std::unique_ptr<storage::Storage> CreateInMemoryStorage(storage::Config config,
::memgraph::replication::ReplicationState &repl_state) {
#ifdef MG_EXPERIMENTAL_REPLICATION_MULTITENANCY
constexpr bool allow_mt_repl = true;
#else
constexpr bool allow_mt_repl = false;
#endif
inline std::unique_ptr<storage::Storage> CreateInMemoryStorage(
storage::Config config, const ::memgraph::replication::ReplicationState &repl_state) {
const auto wal_mode = config.durability.snapshot_wal_mode;
const auto name = config.name;
auto storage = std::make_unique<storage::InMemoryStorage>(std::move(config));

View File

@@ -1,34 +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.
#include "dbms/replication_client.hpp"
namespace memgraph::dbms {
void StartReplicaClient(DbmsHandler &dbms_handler, replication::ReplicationClient &client) {
// No client error, start instance level client
auto const &endpoint = client.rpc_client_.Endpoint();
spdlog::trace("Replication client started at: {}:{}", endpoint.address, endpoint.port);
client.StartFrequentCheck([&dbms_handler](std::string_view name) {
// Working connection, check if any database has been left behind
dbms_handler.ForEach([name](dbms::Database *db) {
// Specific database <-> replica client
db->storage()->repl_storage_state_.WithClient(name, [&](storage::ReplicationStorageClient *client) {
if (client->State() == storage::replication::ReplicaState::MAYBE_BEHIND) {
// Database <-> replica might be behind, check and recover
client->TryCheckReplicaStateAsync(db->storage());
}
});
});
});
}
} // namespace memgraph::dbms

View File

@@ -1,21 +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 "dbms/dbms_handler.hpp"
#include "replication/replication_client.hpp"
namespace memgraph::dbms {
void StartReplicaClient(DbmsHandler &dbms_handler, replication::ReplicationClient &client);
} // namespace memgraph::dbms

View File

@@ -15,7 +15,6 @@
#include "dbms/dbms_handler.hpp"
#include "dbms/inmemory/replication_handlers.hpp"
#include "dbms/inmemory/storage_helper.hpp"
#include "dbms/replication_client.hpp"
#include "replication/state.hpp"
using memgraph::replication::ReplicationClientConfig;
@@ -42,8 +41,6 @@ std::string RegisterReplicaErrorToString(RegisterReplicaError error) {
}
} // namespace
ReplicationHandler::ReplicationHandler(DbmsHandler &dbms_handler) : dbms_handler_(dbms_handler) {}
bool ReplicationHandler::SetReplicationRoleMain() {
auto const main_handler = [](RoleMainData const &) {
// If we are already MAIN, we don't want to change anything
@@ -59,49 +56,42 @@ bool ReplicationHandler::SetReplicationRoleMain() {
// STEP 2) Change to MAIN
// TODO: restore replication servers if false?
if (!dbms_handler_.ReplicationState().SetReplicationRoleMain()) {
if (!repl_state_.SetReplicationRoleMain()) {
// TODO: Handle recovery on failure???
return false;
}
// STEP 3) We are now MAIN, update storage local epoch
const auto &epoch =
std::get<RoleMainData>(std::as_const(dbms_handler_.ReplicationState()).ReplicationData()).epoch_;
dbms_handler_.ForEach([&](Database *db) {
auto *storage = db->storage();
storage->repl_storage_state_.epoch_ = epoch;
storage->repl_storage_state_.epoch_ = std::get<RoleMainData>(std::as_const(repl_state_).ReplicationData()).epoch_;
});
return true;
};
// TODO: under lock
return std::visit(utils::Overloaded{main_handler, replica_handler},
dbms_handler_.ReplicationState().ReplicationData());
return std::visit(utils::Overloaded{main_handler, replica_handler}, repl_state_.ReplicationData());
}
bool ReplicationHandler::SetReplicationRoleReplica(const memgraph::replication::ReplicationServerConfig &config) {
// We don't want to restart the server if we're already a REPLICA
if (dbms_handler_.ReplicationState().IsReplica()) {
if (repl_state_.IsReplica()) {
return false;
}
// TODO StorageState needs to be synched. Could have a dangling reference if someone adds a database as we are
// deleting the replica.
// Remove database specific clients
// Remove registered replicas
dbms_handler_.ForEach([&](Database *db) {
auto *storage = db->storage();
storage->repl_storage_state_.replication_clients_.WithLock([](auto &clients) { clients.clear(); });
});
// Remove instance level clients
std::get<RoleMainData>(dbms_handler_.ReplicationState().ReplicationData()).registered_replicas_.clear();
// Creates the server
dbms_handler_.ReplicationState().SetReplicationRoleReplica(config);
repl_state_.SetReplicationRoleReplica(config);
// Start
const auto success =
std::visit(utils::Overloaded{[](RoleMainData const &) {
std::visit(utils::Overloaded{[](auto) {
// ASSERT
return false;
},
@@ -114,37 +104,36 @@ bool ReplicationHandler::SetReplicationRoleReplica(const memgraph::replication::
}
return true;
}},
dbms_handler_.ReplicationState().ReplicationData());
repl_state_.ReplicationData());
// TODO Handle error (restore to main?)
return success;
}
auto ReplicationHandler::RegisterReplica(const memgraph::replication::ReplicationClientConfig &config)
-> memgraph::utils::BasicResult<RegisterReplicaError> {
MG_ASSERT(dbms_handler_.ReplicationState().IsMain(), "Only main instance can register a replica!");
MG_ASSERT(repl_state_.IsMain(), "Only main instance can register a replica!");
auto instance_client = dbms_handler_.ReplicationState().RegisterReplica(config);
if (instance_client.HasError()) switch (instance_client.GetError()) {
case memgraph::replication::RegisterReplicaError::NOT_MAIN:
MG_ASSERT(false, "Only main instance can register a replica!");
return {};
case memgraph::replication::RegisterReplicaError::NAME_EXISTS:
return memgraph::dbms::RegisterReplicaError::NAME_EXISTS;
case memgraph::replication::RegisterReplicaError::END_POINT_EXISTS:
return memgraph::dbms::RegisterReplicaError::END_POINT_EXISTS;
case memgraph::replication::RegisterReplicaError::COULD_NOT_BE_PERSISTED:
return memgraph::dbms::RegisterReplicaError::COULD_NOT_BE_PERSISTED;
case memgraph::replication::RegisterReplicaError::SUCCESS:
break;
}
auto res = repl_state_.RegisterReplica(config);
switch (res) {
case memgraph::replication::RegisterReplicaError::NOT_MAIN:
MG_ASSERT(false, "Only main instance can register a replica!");
return {};
case memgraph::replication::RegisterReplicaError::NAME_EXISTS:
return memgraph::dbms::RegisterReplicaError::NAME_EXISTS;
case memgraph::replication::RegisterReplicaError::END_POINT_EXISTS:
return memgraph::dbms::RegisterReplicaError::END_POINT_EXISTS;
case memgraph::replication::RegisterReplicaError::COULD_NOT_BE_PERSISTED:
return memgraph::dbms::RegisterReplicaError::COULD_NOT_BE_PERSISTED;
case memgraph::replication::RegisterReplicaError::SUCCESS:
break;
}
bool all_clients_good = true;
if (!allow_mt_repl && dbms_handler_.All().size() > 1) {
spdlog::warn("Multi-tenant replication is currently not supported!");
}
bool all_clients_good = true;
// Add database specific clients (NOTE Currently all databases are connected to each replica)
dbms_handler_.ForEach([&](Database *db) {
auto *storage = db->storage();
if (!allow_mt_repl && storage->id() != kDefaultDB) {
@@ -154,29 +143,18 @@ auto ReplicationHandler::RegisterReplica(const memgraph::replication::Replicatio
if (storage->storage_mode_ != storage::StorageMode::IN_MEMORY_TRANSACTIONAL) return;
all_clients_good &=
storage->repl_storage_state_.replication_clients_.WithLock([storage, &instance_client](auto &storage_clients) {
auto client = std::make_unique<storage::ReplicationStorageClient>(*instance_client.GetValue());
client->Start(storage);
// After start the storage <-> replica state should be READY or RECOVERING (if correctly started)
// MAYBE_BEHIND isn't a statement of the current state, this is the default value
// Failed to start due to branching of MAIN and REPLICA
if (client->State() == storage::replication::ReplicaState::MAYBE_BEHIND) {
storage->repl_storage_state_.replication_clients_.WithLock([storage, &config](auto &clients) -> bool {
auto client = storage->CreateReplicationClient(config, &storage->repl_storage_state_.epoch_);
client->Start();
if (client->State() == storage::replication::ReplicaState::INVALID) {
return false;
}
storage_clients.push_back(std::move(client));
clients.push_back(std::move(client));
return true;
});
});
// NOTE Currently if any databases fails, we revert back
if (!all_clients_good) {
spdlog::error("Failed to register all databases to the REPLICA \"{}\"", config.name);
UnregisterReplica(config.name);
return RegisterReplicaError::CONNECTION_FAILED;
}
// No client error, start instance level client
StartReplicaClient(dbms_handler_, *instance_client.GetValue());
if (!all_clients_good) return RegisterReplicaError::CONNECTION_FAILED; // TODO: this happen to 1 or many...what to do
return {};
}
@@ -185,66 +163,60 @@ auto ReplicationHandler::UnregisterReplica(std::string_view name) -> UnregisterR
return UnregisterReplicaResult::NOT_MAIN;
};
auto const main_handler = [this, name](RoleMainData &mainData) -> UnregisterReplicaResult {
if (!dbms_handler_.ReplicationState().TryPersistUnregisterReplica(name)) {
if (!repl_state_.TryPersistUnregisterReplica(name)) {
return UnregisterReplicaResult::COULD_NOT_BE_PERSISTED;
}
// Remove database specific clients
dbms_handler_.ForEach([name](Database *db) {
db->storage()->repl_storage_state_.replication_clients_.WithLock([&name](auto &clients) {
std::erase_if(clients, [name](const auto &client) { return client->Name() == name; });
});
});
// Remove instance level clients
auto const n_unregistered =
std::erase_if(mainData.registered_replicas_, [name](auto const &client) { return client.name_ == name; });
std::erase_if(mainData.registered_replicas_,
[&](ReplicationClientConfig const &registered_config) { return registered_config.name == name; });
dbms_handler_.ForEach([&](Database *db) {
db->storage()->repl_storage_state_.replication_clients_.WithLock(
[&](auto &clients) { std::erase_if(clients, [&](const auto &client) { return client->Name() == name; }); });
});
return n_unregistered != 0 ? UnregisterReplicaResult::SUCCESS : UnregisterReplicaResult::CAN_NOT_UNREGISTER;
};
return std::visit(utils::Overloaded{main_handler, replica_handler},
dbms_handler_.ReplicationState().ReplicationData());
return std::visit(utils::Overloaded{main_handler, replica_handler}, repl_state_.ReplicationData());
}
auto ReplicationHandler::GetRole() const -> memgraph::replication::ReplicationRole {
return dbms_handler_.ReplicationState().GetRole();
}
auto ReplicationHandler::GetRole() const -> memgraph::replication::ReplicationRole { return repl_state_.GetRole(); }
bool ReplicationHandler::IsMain() const { return dbms_handler_.ReplicationState().IsMain(); }
bool ReplicationHandler::IsMain() const { return repl_state_.IsMain(); }
bool ReplicationHandler::IsReplica() const { return dbms_handler_.ReplicationState().IsReplica(); }
bool ReplicationHandler::IsReplica() const { return repl_state_.IsReplica(); }
// Per storage
// NOTE Storage will connect to all replicas. Future work might change this
void RestoreReplication(replication::ReplicationState &repl_state, storage::Storage &storage) {
void RestoreReplication(const replication::ReplicationState &repl_state, storage::Storage &storage) {
spdlog::info("Restoring replication role.");
/// MAIN
auto const recover_main = [&storage](RoleMainData &mainData) {
// Each individual client has already been restored and started. Here we just go through each database and start its
// client
for (auto &instance_client : mainData.registered_replicas_) {
spdlog::info("Replica {} restoration started for {}.", instance_client.name_, storage.id());
auto const recover_main = [&storage](RoleMainData const &mainData) {
for (const auto &config : mainData.registered_replicas_) {
spdlog::info("Replica {} restoration started for {}.", config.name, storage.id());
const auto &ret = storage.repl_storage_state_.replication_clients_.WithLock(
[&](auto &storage_clients) -> utils::BasicResult<RegisterReplicaError> {
auto client = std::make_unique<storage::ReplicationStorageClient>(instance_client);
client->Start(&storage);
// After start the storage <-> replica state should be READY or RECOVERING (if correctly started)
// MAYBE_BEHIND isn't a statement of the current state, this is the default value
// Failed to start due to branching of MAIN and REPLICA
if (client->State() == storage::replication::ReplicaState::MAYBE_BEHIND) {
spdlog::warn("Connection failed when registering replica {}. Replica will still be registered.",
instance_client.name_);
}
storage_clients.push_back(std::move(client));
return {};
});
auto register_replica = [&storage](const memgraph::replication::ReplicationClientConfig &config)
-> memgraph::utils::BasicResult<RegisterReplicaError> {
return storage.repl_storage_state_.replication_clients_.WithLock(
[&storage, &config](auto &clients) -> utils::BasicResult<RegisterReplicaError> {
auto client = storage.CreateReplicationClient(config, &storage.repl_storage_state_.epoch_);
client->Start();
if (client->State() == storage::replication::ReplicaState::INVALID) {
spdlog::warn("Connection failed when registering replica {}. Replica will still be registered.",
client->Name());
}
clients.push_back(std::move(client));
return {};
});
};
auto ret = register_replica(config);
if (ret.HasError()) {
MG_ASSERT(RegisterReplicaError::CONNECTION_FAILED != ret.GetError());
LOG_FATAL("Failure when restoring replica {}: {}.", instance_client.name_,
RegisterReplicaErrorToString(ret.GetError()));
LOG_FATAL("Failure when restoring replica {}: {}.", config.name, RegisterReplicaErrorToString(ret.GetError()));
}
spdlog::info("Replica {} restored for {}.", instance_client.name_, storage.id());
spdlog::info("Replica {} restored for {}.", config.name, storage.id());
}
spdlog::info("Replication role restored to MAIN.");
};
@@ -257,6 +229,6 @@ void RestoreReplication(replication::ReplicationState &repl_state, storage::Stor
recover_main,
recover_replica,
},
repl_state.ReplicationData());
std::as_const(repl_state).ReplicationData());
}
} // namespace memgraph::dbms

View File

@@ -36,7 +36,8 @@ enum class UnregisterReplicaResult : uint8_t {
/// A handler type that keep in sync current ReplicationState and the MAIN/REPLICA-ness of Storage
/// TODO: extend to do multiple storages
struct ReplicationHandler {
explicit ReplicationHandler(DbmsHandler &dbms_handler);
ReplicationHandler(memgraph::replication::ReplicationState &replState, DbmsHandler &dbms_handler)
: repl_state_(replState), dbms_handler_(dbms_handler) {}
// as REPLICA, become MAIN
bool SetReplicationRoleMain();
@@ -57,11 +58,12 @@ struct ReplicationHandler {
bool IsReplica() const;
private:
memgraph::replication::ReplicationState &repl_state_;
DbmsHandler &dbms_handler_;
};
/// A handler type that keep in sync current ReplicationState and the MAIN/REPLICA-ness of Storage
/// TODO: extend to do multiple storages
void RestoreReplication(replication::ReplicationState &repl_state, storage::Storage &storage);
void RestoreReplication(const replication::ReplicationState &repl_state, storage::Storage &storage);
} // namespace memgraph::dbms

View File

@@ -65,10 +65,7 @@ DEFINE_bool(allow_load_csv, true, "Controls whether LOAD CSV clause is allowed i
// Storage flags.
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_VALIDATED_uint64(storage_gc_cycle_sec, 30, "Storage garbage collector interval (in seconds).",
FLAG_IN_RANGE(1, 24UL * 3600));
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_VALIDATED_uint64(storage_python_gc_cycle_sec, 180,
"Storage python full garbage collection interval (in seconds).", FLAG_IN_RANGE(1, 24UL * 3600));
FLAG_IN_RANGE(1, 24 * 3600));
// NOTE: The `storage_properties_on_edges` flag must be the same here and in
// `mg_import_csv`. If you change it, make sure to change it there as well.
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
@@ -107,19 +104,9 @@ DEFINE_bool(storage_snapshot_on_exit, false, "Controls whether the storage creat
DEFINE_uint64(storage_items_per_batch, memgraph::storage::Config::Durability().items_per_batch,
"The number of edges and vertices stored in a batch in a snapshot file.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables,misc-unused-parameters)
DEFINE_VALIDATED_bool(
storage_parallel_index_recovery, false,
"Controls whether the index creation can be done in a multithreaded fashion.", {
spdlog::warn(
"storage_parallel_index_recovery flag is deprecated. Check storage_mode_parallel_schema_recovery for more "
"details.");
return true;
});
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_bool(storage_parallel_schema_recovery, false,
"Controls whether the indices and constraints creation can be done in a multithreaded fashion.");
DEFINE_bool(storage_parallel_index_recovery, false,
"Controls whether the index creation can be done in a multithreaded fashion.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_uint64(storage_recovery_thread_count,
@@ -127,10 +114,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.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_bool(storage_enable_schema_metadata, false,
"Controls whether metadata should be collected about the resident labels and edge types.");
#ifdef MG_ENTERPRISE
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_bool(storage_delete_on_drop, true,

View File

@@ -52,8 +52,6 @@ DECLARE_bool(allow_load_csv);
// Storage flags.
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_uint64(storage_gc_cycle_sec);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_uint64(storage_python_gc_cycle_sec);
// NOTE: The `storage_properties_on_edges` flag must be the same here and in
// `mg_import_csv`. If you change it, make sure to change it there as well.
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
@@ -75,15 +73,10 @@ DECLARE_uint64(storage_wal_file_flush_every_n_tx);
DECLARE_bool(storage_snapshot_on_exit);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_uint64(storage_items_per_batch);
// storage_parallel_index_recovery deprecated; use storage_parallel_schema_recovery instead
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_bool(storage_parallel_index_recovery);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_bool(storage_parallel_schema_recovery);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_uint64(storage_recovery_thread_count);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_bool(storage_enable_schema_metadata);
#ifdef MG_ENTERPRISE
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_bool(storage_delete_on_drop);

View File

@@ -10,7 +10,6 @@
// licenses/APL.txt.
#include <optional>
#include <utility>
#include "gflags/gflags.h"
#include "audit/log.hpp"
@@ -234,55 +233,11 @@ std::pair<std::vector<std::string>, std::optional<int>> SessionHL::Interpret(
throw memgraph::communication::bolt::ClientError(e.what());
}
}
void SessionHL::RollbackTransaction() {
try {
interpreter_.RollbackTransaction();
} catch (const memgraph::query::QueryException &e) {
// Count the number of specific exceptions thrown
metrics::IncrementCounter(GetExceptionName(e));
// Wrap QueryException into ClientError, because we want to allow the
// client to fix their query.
throw memgraph::communication::bolt::ClientError(e.what());
} catch (const memgraph::query::ReplicationException &e) {
// Count the number of specific exceptions thrown
metrics::IncrementCounter(GetExceptionName(e));
throw memgraph::communication::bolt::ClientError(e.what());
}
}
void SessionHL::CommitTransaction() {
try {
interpreter_.CommitTransaction();
} catch (const memgraph::query::QueryException &e) {
// Count the number of specific exceptions thrown
metrics::IncrementCounter(GetExceptionName(e));
// Wrap QueryException into ClientError, because we want to allow the
// client to fix their query.
throw memgraph::communication::bolt::ClientError(e.what());
} catch (const memgraph::query::ReplicationException &e) {
// Count the number of specific exceptions thrown
metrics::IncrementCounter(GetExceptionName(e));
throw memgraph::communication::bolt::ClientError(e.what());
}
}
void SessionHL::RollbackTransaction() { interpreter_.RollbackTransaction(); }
void SessionHL::CommitTransaction() { interpreter_.CommitTransaction(); }
void SessionHL::BeginTransaction(const std::map<std::string, memgraph::communication::bolt::Value> &extra) {
try {
interpreter_.BeginTransaction(ToQueryExtras(extra));
} catch (const memgraph::query::QueryException &e) {
// Count the number of specific exceptions thrown
metrics::IncrementCounter(GetExceptionName(e));
// Wrap QueryException into ClientError, because we want to allow the
// client to fix their query.
throw memgraph::communication::bolt::ClientError(e.what());
} catch (const memgraph::query::ReplicationException &e) {
// Count the number of specific exceptions thrown
metrics::IncrementCounter(GetExceptionName(e));
throw memgraph::communication::bolt::ClientError(e.what());
}
interpreter_.BeginTransaction(ToQueryExtras(extra));
}
void SessionHL::Configure(const std::map<std::string, memgraph::communication::bolt::Value> &run_time_info) {
#ifdef MG_ENTERPRISE
std::string db;
@@ -317,7 +272,7 @@ void SessionHL::Configure(const std::map<std::string, memgraph::communication::b
#endif
}
SessionHL::SessionHL(memgraph::query::InterpreterContext *interpreter_context,
memgraph::communication::v2::ServerEndpoint endpoint,
const memgraph::communication::v2::ServerEndpoint &endpoint,
memgraph::communication::v2::InputStream *input_stream,
memgraph::communication::v2::OutputStream *output_stream,
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth
@@ -334,7 +289,7 @@ SessionHL::SessionHL(memgraph::query::InterpreterContext *interpreter_context,
audit_log_(audit_log),
#endif
auth_(auth),
endpoint_(std::move(endpoint)),
endpoint_(endpoint),
implicit_db_(dbms::kDefaultDB) {
// Metrics update
memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions);

View File

@@ -23,7 +23,7 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
memgraph::communication::v2::OutputStream> {
public:
SessionHL(memgraph::query::InterpreterContext *interpreter_context,
memgraph::communication::v2::ServerEndpoint endpoint,
const memgraph::communication::v2::ServerEndpoint &endpoint,
memgraph::communication::v2::InputStream *input_stream,
memgraph::communication::v2::OutputStream *output_stream,
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth

View File

@@ -127,8 +127,6 @@ storage::Result<Value> ToBoltValue(const query::TypedValue &value, const storage
return Value(value.ValueLocalDateTime());
case query::TypedValue::Type::Duration:
return Value(value.ValueDuration());
case query::TypedValue::Type::Function:
throw communication::bolt::ValueException("Unsupported conversion from TypedValue::Function to Value");
case query::TypedValue::Type::Graph:
auto maybe_graph = ToBoltGraph(value.ValueGraph(), db, view);
if (maybe_graph.HasError()) return maybe_graph.GetError();
@@ -204,7 +202,7 @@ storage::Result<std::map<std::string, Value>> ToBoltGraph(const query::Graph &gr
for (const auto &v : graph.vertices()) {
auto maybe_vertex = ToBoltVertex(v, db, view);
if (maybe_vertex.HasError()) return maybe_vertex.GetError();
vertices.emplace_back(std::move(*maybe_vertex));
vertices.emplace_back(Value(std::move(*maybe_vertex)));
}
map.emplace("nodes", Value(vertices));
@@ -213,7 +211,7 @@ storage::Result<std::map<std::string, Value>> ToBoltGraph(const query::Graph &gr
for (const auto &e : graph.edges()) {
auto maybe_edge = ToBoltEdge(e, db, view);
if (maybe_edge.HasError()) return maybe_edge.GetError();
edges.emplace_back(std::move(*maybe_edge));
edges.emplace_back(Value(std::move(*maybe_edge)));
}
map.emplace("edges", Value(edges));

View File

@@ -31,7 +31,7 @@ inline void LoadConfig(const std::string &product_name) {
std::vector<fs::path> configs = {fs::path("/etc/memgraph/memgraph.conf")};
if (getenv("HOME") != nullptr) configs.emplace_back(fs::path(getenv("HOME")) / fs::path(".memgraph/config"));
{
auto *memgraph_config = getenv("MEMGRAPH_CONFIG");
auto memgraph_config = getenv("MEMGRAPH_CONFIG");
if (memgraph_config != nullptr) {
auto path = fs::path(memgraph_config);
MG_ASSERT(fs::exists(path), "MEMGRAPH_CONFIG environment variable set to nonexisting path: {}",

View File

@@ -23,6 +23,7 @@
#include <utils/event_counter.hpp>
#include <utils/event_gauge.hpp>
#include "storage/v2/storage.hpp"
#include "utils/event_gauge.hpp"
#include "utils/event_histogram.hpp"
namespace memgraph::http {

View File

@@ -171,10 +171,10 @@ class Consumer final : public RdKafka::EventCb {
class ConsumerRebalanceCb : public RdKafka::RebalanceCb {
public:
explicit ConsumerRebalanceCb(std::string consumer_name);
ConsumerRebalanceCb(std::string consumer_name);
void rebalance_cb(RdKafka::KafkaConsumer *consumer, RdKafka::ErrorCode err,
std::vector<RdKafka::TopicPartition *> &partitions) final;
std::vector<RdKafka::TopicPartition *> &partitions) override final;
void set_offset(int64_t offset);

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
@@ -67,7 +67,7 @@ utils::BasicResult<std::string, std::vector<Message>> GetBatch(TConsumer &consum
return std::move(batch);
case pulsar_client::Result::ResultOk:
if (message.getMessageId() != last_message_id) {
batch.emplace_back(std::move(message));
batch.emplace_back(Message{std::move(message)});
}
break;
default:

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
@@ -10,7 +10,6 @@
// licenses/APL.txt.
#pragma once
#include <atomic>
#include <optional>
#include <span>

View File

@@ -48,8 +48,8 @@ struct Endpoint {
uint16_t port{0};
IpFamily family{IpFamily::NONE};
static std::optional<std::pair<std::string, uint16_t>> ParseSocketOrAddress(const std::string &address,
std::optional<uint16_t> default_port);
static std::optional<std::pair<std::string, uint16_t>> ParseSocketOrAddress(
const std::string &address, const std::optional<uint16_t> default_port);
/**
* Tries to parse the given string as either a socket address or ip address.
@@ -61,8 +61,8 @@ struct Endpoint {
* it into an ip address and a port number; even if a default port is given,
* it won't be used, as we expect that it is given in the address string.
*/
static std::optional<std::pair<std::string, uint16_t>> ParseSocketOrIpAddress(const std::string &address,
std::optional<uint16_t> default_port);
static std::optional<std::pair<std::string, uint16_t>> ParseSocketOrIpAddress(
const std::string &address, const std::optional<uint16_t> default_port);
/**
* Tries to parse given string as either socket address or hostname.
@@ -72,7 +72,7 @@ struct Endpoint {
* After we parse hostname and port we try to resolve the hostname into an ip_address.
*/
static std::optional<std::pair<std::string, uint16_t>> ParseHostname(const std::string &address,
std::optional<uint16_t> default_port);
const std::optional<uint16_t> default_port);
static IpFamily GetIpFamily(const std::string &address);

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
@@ -32,7 +32,7 @@ class Epoll {
public:
using Event = struct epoll_event;
explicit Epoll(bool set_cloexec = false) : epoll_fd_(epoll_create1(set_cloexec ? EPOLL_CLOEXEC : 0)) {
Epoll(bool set_cloexec = false) : epoll_fd_(epoll_create1(set_cloexec ? EPOLL_CLOEXEC : 0)) {
// epoll_create1 returns an error if there is a logical error in our code
// (for example invalid flags) or if there is irrecoverable error. In both
// cases it is best to terminate.

View File

@@ -14,7 +14,6 @@
#include <functional>
#include <iostream>
#include <optional>
#include <utility>
#include "io/network/endpoint.hpp"
@@ -202,7 +201,7 @@ class Socket {
bool WaitForReadyWrite();
private:
Socket(int fd, Endpoint endpoint) : socket_(fd), endpoint_(std::move(endpoint)) {}
Socket(int fd, const Endpoint &endpoint) : socket_(fd), endpoint_(endpoint) {}
int socket_ = -1;
Endpoint endpoint_;

View File

@@ -128,7 +128,7 @@ KVStore::iterator::iterator(const KVStore *kvstore, const std::string &prefix, b
KVStore::iterator::iterator(KVStore::iterator &&other) { pimpl_ = std::move(other.pimpl_); }
KVStore::iterator::~iterator() = default;
KVStore::iterator::~iterator() {}
KVStore::iterator &KVStore::iterator::operator=(KVStore::iterator &&other) {
pimpl_ = std::move(other.pimpl_);

View File

@@ -65,13 +65,10 @@ void InitFromCypherlFile(memgraph::query::InterpreterContext &ctx, memgraph::dbm
std::string line;
while (std::getline(file, line)) {
if (!line.empty()) {
try {
auto results = interpreter.Prepare(line, {}, {});
memgraph::query::DiscardValueResultStream stream;
interpreter.Pull(&stream, {}, results.qid);
} catch (const memgraph::query::UserAlreadyExistsException &e) {
spdlog::warn("{} The rest of the init-file will be run.", e.what());
}
auto results = interpreter.Prepare(line, {}, {});
memgraph::query::DiscardValueResultStream stream;
interpreter.Pull(&stream, {}, results.qid);
if (audit_log) {
audit_log->Record("", "", line, {}, memgraph::dbms::kDefaultDB);
}
@@ -163,7 +160,7 @@ int main(int argc, char **argv) {
// libstd.
auto gil = memgraph::py::EnsureGIL();
// NOLINTNEXTLINE(hicpp-signed-bitwise)
auto *flag = PyLong_FromLong(RTLD_NOW);
auto *flag = PyLong_FromLong(RTLD_NOW | RTLD_DEEPBIND);
auto *setdl = PySys_GetObject("setdlopenflags");
MG_ASSERT(setdl);
auto *arg = PyTuple_New(1);
@@ -184,10 +181,6 @@ int main(int argc, char **argv) {
"https://memgr.ph/python"));
}
memgraph::utils::Scheduler python_gc_scheduler;
python_gc_scheduler.Run("Python GC", std::chrono::seconds(FLAGS_storage_python_gc_cycle_sec),
[] { memgraph::query::procedure::PyCollectGarbage(); });
// Initialize the communication library.
memgraph::communication::SSLInit sslInit;
@@ -298,8 +291,7 @@ int main(int argc, char **argv) {
memgraph::storage::Config db_config{
.gc = {.type = memgraph::storage::Config::Gc::Type::PERIODIC,
.interval = std::chrono::seconds(FLAGS_storage_gc_cycle_sec)},
.items = {.properties_on_edges = FLAGS_storage_properties_on_edges,
.enable_schema_metadata = FLAGS_storage_enable_schema_metadata},
.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,
.snapshot_retention_count = FLAGS_storage_snapshot_retention_count,
@@ -309,9 +301,7 @@ int main(int argc, char **argv) {
.restore_replication_state_on_startup = FLAGS_replication_restore_state_on_startup,
.items_per_batch = FLAGS_storage_items_per_batch,
.recovery_thread_count = FLAGS_storage_recovery_thread_count,
// deprecated
.allow_parallel_index_creation = FLAGS_storage_parallel_index_recovery,
.allow_parallel_schema_creation = FLAGS_storage_parallel_schema_recovery},
.allow_parallel_index_creation = FLAGS_storage_parallel_index_recovery},
.transaction = {.isolation_level = memgraph::flags::ParseIsolationLevel()},
.disk = {.main_storage_directory = FLAGS_data_directory + "/rocksdb_main_storage",
.label_index_directory = FLAGS_data_directory + "/rocksdb_label_index",
@@ -322,11 +312,6 @@ int main(int argc, char **argv) {
.durability_directory = FLAGS_data_directory + "/rocksdb_durability",
.wal_directory = FLAGS_data_directory + "/rocksdb_wal"},
.storage_mode = memgraph::flags::ParseStorageMode()};
memgraph::utils::Scheduler jemalloc_purge_scheduler;
jemalloc_purge_scheduler.Run("Jemalloc purge", std::chrono::seconds(FLAGS_storage_gc_cycle_sec),
[] { memgraph::memory::PurgeUnusedMemory(); });
if (FLAGS_storage_snapshot_interval_sec == 0) {
if (FLAGS_storage_wal_enabled) {
LOG_FATAL(
@@ -383,18 +368,35 @@ int main(int argc, char **argv) {
std::unique_ptr<memgraph::query::AuthChecker> auth_checker;
auth_glue(&auth_, auth_handler, auth_checker);
memgraph::dbms::DbmsHandler dbms_handler(db_config
memgraph::replication::ReplicationState repl_state(ReplicationStateRootPath(db_config));
memgraph::dbms::DbmsHandler dbms_handler(db_config, repl_state
#ifdef MG_ENTERPRISE
,
&auth_, FLAGS_data_recovery_on_startup, FLAGS_storage_delete_on_drop
#endif
);
auto db_acc = dbms_handler.Get();
memgraph::query::InterpreterContext interpreter_context_(
interp_config, &dbms_handler, &dbms_handler.ReplicationState(), auth_handler.get(), auth_checker.get());
memgraph::query::InterpreterContext interpreter_context_(interp_config, &dbms_handler, &repl_state,
auth_handler.get(), auth_checker.get());
MG_ASSERT(db_acc, "Failed to access the main database");
// TODO: Move it somewhere better
// Startup replication state (if recovered at startup)
MG_ASSERT(std::visit(memgraph::utils::Overloaded{[](memgraph::replication::RoleMainData const &) { return true; },
[&](memgraph::replication::RoleReplicaData const &data) {
// Register handlers
memgraph::dbms::InMemoryReplicationHandlers::Register(
&dbms_handler, *data.server);
if (!data.server->Start()) {
spdlog::error("Unable to start the replication server.");
return false;
}
return true;
}},
repl_state.ReplicationData()),
"Replica recovery failure!");
memgraph::query::procedure::gModuleRegistry.SetModulesDirectory(memgraph::flags::ParseQueryModulesDirectory(),
FLAGS_data_directory);
memgraph::query::procedure::gModuleRegistry.UnloadAndLoadModulesFromDirectories();

View File

@@ -119,7 +119,7 @@ static bool my_commit(extent_hooks_t *extent_hooks, void *addr, size_t size, siz
memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(length));
if (GetQueriesMemoryControl().IsThreadTracked()) [[unlikely]] {
GetQueriesMemoryControl().TrackAllocOnCurrentThread(size);
GetQueriesMemoryControl().TrackFreeOnCurrentThread(size);
}
return false;

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
@@ -62,7 +62,6 @@ bool TypedValueCompare(const TypedValue &a, const TypedValue &b) {
case TypedValue::Type::Edge:
case TypedValue::Type::Path:
case TypedValue::Type::Graph:
case TypedValue::Type::Function:
throw QueryRuntimeException("Comparison is not defined for values of type {}.", a.type());
case TypedValue::Type::Null:
LOG_FATAL("Invalid type");

View File

@@ -41,7 +41,7 @@ bool TypedValueCompare(const TypedValue &a, const TypedValue &b);
/// the define how respective elements compare.
class TypedValueVectorCompare final {
public:
TypedValueVectorCompare() = default;
TypedValueVectorCompare() {}
explicit TypedValueVectorCompare(const std::vector<Ordering> &ordering) : ordering_(ordering) {}
template <class TAllocator>
@@ -147,8 +147,8 @@ concept AccessorWithUpdateProperties = requires(T accessor,
///
/// @throw QueryRuntimeException if value cannot be set as a property value
template <AccessorWithUpdateProperties T>
auto UpdatePropertiesChecked(T *record, std::map<storage::PropertyId, storage::PropertyValue> &properties)
-> std::remove_reference_t<decltype(record->UpdateProperties(properties).GetValue())> {
auto UpdatePropertiesChecked(T *record, std::map<storage::PropertyId, storage::PropertyValue> &properties) ->
typename std::remove_reference<decltype(record->UpdateProperties(properties).GetValue())>::type {
try {
auto maybe_values = record->UpdateProperties(properties);
if (maybe_values.HasError()) {

View File

@@ -11,8 +11,6 @@
#pragma once
#include <utility>
#include "query/config.hpp"
#include "query/frontend/semantic/required_privileges.hpp"
#include "query/frontend/semantic/symbol_generator.hpp"
@@ -100,8 +98,8 @@ ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::stri
class SingleNodeLogicalPlan final : public LogicalPlan {
public:
SingleNodeLogicalPlan(std::unique_ptr<plan::LogicalOperator> root, double cost, AstStorage storage,
SymbolTable symbol_table)
: root_(std::move(root)), cost_(cost), storage_(std::move(storage)), symbol_table_(std::move(symbol_table)) {}
const SymbolTable &symbol_table)
: root_(std::move(root)), cost_(cost), storage_(std::move(storage)), symbol_table_(symbol_table) {}
const plan::LogicalOperator &GetRoot() const override { return *root_; }
double GetCost() const override { return cost_; }

View File

@@ -15,7 +15,6 @@
#include <cppitertools/filter.hpp>
#include <cppitertools/imap.hpp>
#include "storage/v2/storage_mode.hpp"
#include "utils/pmr/unordered_set.hpp"
namespace memgraph::query {
@@ -140,10 +139,6 @@ std::optional<VertexAccessor> SubgraphDbAccessor::FindVertex(storage::Gid gid, s
query::Graph *SubgraphDbAccessor::getGraph() { return graph_; }
storage::StorageMode SubgraphDbAccessor::GetStorageMode() const noexcept { return db_accessor_.GetStorageMode(); }
DbAccessor *SubgraphDbAccessor::GetAccessor() { return &db_accessor_; }
VertexAccessor SubgraphVertexAccessor::GetVertexAccessor() const { return impl_; }
storage::Result<EdgeVertexAccessorResult> SubgraphVertexAccessor::OutEdges(storage::View view) const {

View File

@@ -40,10 +40,9 @@ class EdgeAccessor final {
public:
storage::EdgeAccessor impl_;
public:
explicit EdgeAccessor(storage::EdgeAccessor impl) : impl_(std::move(impl)) {}
bool IsDeleted() const { return impl_.IsDeleted(); }
bool IsVisible(storage::View view) const { return impl_.IsVisible(view); }
storage::EdgeTypeId EdgeType() const { return impl_.EdgeType(); }
@@ -109,6 +108,7 @@ class VertexAccessor final {
static EdgeAccessor MakeEdgeAccessor(const storage::EdgeAccessor impl) { return EdgeAccessor(impl); }
public:
explicit VertexAccessor(storage::VertexAccessor impl) : impl_(impl) {}
bool IsVisible(storage::View view) const { return impl_.IsVisible(view); }
@@ -545,7 +545,7 @@ class DbAccessor final {
void Abort() { accessor_->Abort(); }
storage::StorageMode GetStorageMode() const noexcept { return accessor_->GetCreationStorageMode(); }
storage::StorageMode GetStorageMode() const { return accessor_->GetCreationStorageMode(); }
bool LabelIndexExists(storage::LabelId label) const { return accessor_->LabelIndexExists(label); }
@@ -597,13 +597,6 @@ class DbAccessor final {
return accessor_->ApproximateVertexCount(label, property, lower, upper);
}
std::vector<storage::LabelId> ListAllPossiblyPresentVertexLabels() const {
return accessor_->ListAllPossiblyPresentVertexLabels();
}
std::vector<storage::EdgeTypeId> ListAllPossiblyPresentEdgeTypes() const {
return accessor_->ListAllPossiblyPresentEdgeTypes();
}
storage::IndicesInfo ListAllIndices() const { return accessor_->ListAllIndices(); }
storage::ConstraintsInfo ListAllConstraints() const { return accessor_->ListAllConstraints(); }
@@ -701,10 +694,6 @@ class SubgraphDbAccessor final {
std::optional<VertexAccessor> FindVertex(storage::Gid gid, storage::View view);
Graph *getGraph();
storage::StorageMode GetStorageMode() const noexcept;
DbAccessor *GetAccessor();
};
} // namespace memgraph::query

View File

@@ -159,7 +159,7 @@ void DumpProperties(std::ostream *os, query::DbAccessor *dba,
*os << "{";
if (property_id) {
*os << kInternalPropertyId << ": " << *property_id;
if (!store.empty()) *os << ", ";
if (store.size() > 0) *os << ", ";
}
utils::PrintIterable(*os, store, ", ", [&dba](auto &os, const auto &kv) {
os << EscapeName(dba->PropertyToName(kv.first)) << ": ";
@@ -228,7 +228,7 @@ void DumpEdge(std::ostream *os, query::DbAccessor *dba, const query::EdgeAccesso
throw query::QueryRuntimeException("Unexpected error when getting properties.");
}
}
if (!maybe_props->empty()) {
if (maybe_props->size() > 0) {
*os << " ";
DumpProperties(os, dba, *maybe_props);
}

View File

@@ -126,12 +126,6 @@ class InfoInMulticommandTxException : public QueryException {
SPECIALIZE_GET_EXCEPTION_NAME(InfoInMulticommandTxException)
};
class UserAlreadyExistsException : public QueryException {
public:
using QueryException::QueryException;
SPECIALIZE_GET_EXCEPTION_NAME(UserAlreadyExistsException)
};
/**
* An exception for an illegal operation that can not be detected
* before the query starts executing over data.

View File

@@ -8,49 +8,48 @@
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include <tuple>
#include <utility>
#include "query/typed_value.hpp"
#include "utils/fnv.hpp"
#include "utils/memory.hpp"
#include "utils/pmr/unordered_map.hpp"
#include "utils/pmr/vector.hpp"
namespace memgraph::query {
// Key is hash output, value is vector of unique elements
using CachedType = utils::pmr::unordered_map<size_t, utils::pmr::vector<TypedValue>>;
using CachedType = utils::pmr::unordered_map<size_t, std::vector<TypedValue>>;
struct CachedValue {
using allocator_type = utils::Allocator<CachedValue>;
// Cached value, this can be probably templateized
CachedType cache_;
explicit CachedValue(utils::MemoryResource *mem) : cache_{mem} {};
CachedValue(const CachedValue &other, utils::MemoryResource *mem) : cache_(other.cache_, mem) {}
CachedValue(CachedValue &&other, utils::MemoryResource *mem) : cache_(std::move(other.cache_), mem){};
explicit CachedValue(utils::MemoryResource *mem) : cache_(mem) {}
CachedValue(CachedValue &&other) noexcept : CachedValue(std::move(other), other.GetMemoryResource()) {}
CachedValue(CachedType &&cache, memgraph::utils::MemoryResource *memory) : cache_(std::move(cache), memory) {}
CachedValue(const CachedValue &other)
: CachedValue(other, std::allocator_traits<allocator_type>::select_on_container_copy_construction(
other.GetMemoryResource())
.GetMemoryResource()) {}
CachedValue(const CachedValue &other, memgraph::utils::MemoryResource *memory) : cache_(other.cache_, memory) {}
utils::MemoryResource *GetMemoryResource() const { return cache_.get_allocator().GetMemoryResource(); }
CachedValue(CachedValue &&other, memgraph::utils::MemoryResource *memory) : cache_(std::move(other.cache_), memory) {}
CachedValue(CachedValue &&other) noexcept = delete;
/// Copy construction without memgraph::utils::MemoryResource is not allowed.
CachedValue(const CachedValue &) = delete;
CachedValue &operator=(const CachedValue &) = delete;
CachedValue &operator=(CachedValue &&) = delete;
~CachedValue() = default;
memgraph::utils::MemoryResource *GetMemoryResource() const noexcept {
return cache_.get_allocator().GetMemoryResource();
}
bool CacheValue(const TypedValue &maybe_list) {
if (!maybe_list.IsList()) {
return false;
}
const auto &list = maybe_list.ValueList();
auto &list = maybe_list.ValueList();
TypedValue::Hash hash{};
for (const auto &element : list) {
for (auto &element : list) {
const auto key = hash(element);
auto &vector_values = cache_[key];
if (!IsValueInVec(vector_values, element)) {
@@ -71,7 +70,7 @@ struct CachedValue {
}
private:
static bool IsValueInVec(const utils::pmr::vector<TypedValue> &vec_values, const TypedValue &value) {
static bool IsValueInVec(const std::vector<TypedValue> &vec_values, const TypedValue &value) {
return std::any_of(vec_values.begin(), vec_values.end(), [&value](auto &vec_value) {
const auto is_value_equal = vec_value == value;
if (is_value_equal.IsNull()) return false;
@@ -83,70 +82,35 @@ struct CachedValue {
// Class tracks keys for which user can cache values which help with faster search or faster retrieval
// in the future. Used for IN LIST operator.
class FrameChangeCollector {
/** Allocator type so that STL containers are aware that we need one */
using allocator_type = utils::Allocator<FrameChangeCollector>;
public:
explicit FrameChangeCollector(utils::MemoryResource *mem = utils::NewDeleteResource()) : tracked_values_{mem} {}
FrameChangeCollector(FrameChangeCollector &&other, utils::MemoryResource *mem)
: tracked_values_(std::move(other.tracked_values_), mem) {}
FrameChangeCollector(const FrameChangeCollector &other, utils::MemoryResource *mem)
: tracked_values_(other.tracked_values_, mem) {}
FrameChangeCollector(const FrameChangeCollector &other)
: FrameChangeCollector(other, std::allocator_traits<allocator_type>::select_on_container_copy_construction(
other.GetMemoryResource())
.GetMemoryResource()){};
FrameChangeCollector(FrameChangeCollector &&other) noexcept
: FrameChangeCollector(std::move(other), other.GetMemoryResource()) {}
/** Copy assign other, utils::MemoryResource of `this` is used */
FrameChangeCollector &operator=(const FrameChangeCollector &) = default;
/** Move assign other, utils::MemoryResource of `this` is used. */
FrameChangeCollector &operator=(FrameChangeCollector &&) noexcept = default;
utils::MemoryResource *GetMemoryResource() const { return tracked_values_.get_allocator().GetMemoryResource(); }
explicit FrameChangeCollector() : tracked_values_(&memory_resource_){};
CachedValue &AddTrackingKey(const std::string &key) {
const auto &[it, _] = tracked_values_.emplace(
std::piecewise_construct, std::forward_as_tuple(utils::pmr::string(key, utils::NewDeleteResource())),
std::forward_as_tuple());
const auto &[it, _] = tracked_values_.emplace(key, tracked_values_.get_allocator().GetMemoryResource());
return it->second;
}
bool IsKeyTracked(const std::string &key) const {
return tracked_values_.contains(utils::pmr::string(key, utils::NewDeleteResource()));
}
bool IsKeyTracked(const std::string &key) const { return tracked_values_.contains(key); }
bool IsKeyValueCached(const std::string &key) const {
return IsKeyTracked(key) && !tracked_values_.at(utils::pmr::string(key, utils::NewDeleteResource())).cache_.empty();
return IsKeyTracked(key) && !tracked_values_.at(key).cache_.empty();
}
bool ResetTrackingValue(const std::string &key) {
if (!tracked_values_.contains(utils::pmr::string(key, utils::NewDeleteResource()))) {
if (!tracked_values_.contains(key)) {
return false;
}
tracked_values_.erase(utils::pmr::string(key, utils::NewDeleteResource()));
tracked_values_.erase(key);
AddTrackingKey(key);
return true;
}
CachedValue &GetCachedValue(const std::string &key) {
return tracked_values_.at(utils::pmr::string(key, utils::NewDeleteResource()));
}
CachedValue &GetCachedValue(const std::string &key) { return tracked_values_.at(key); }
bool IsTrackingValues() const { return !tracked_values_.empty(); }
~FrameChangeCollector() = default;
private:
struct PmrStringHash {
size_t operator()(const utils::pmr::string &key) const { return utils::Fnv(key); }
};
utils::pmr::unordered_map<utils::pmr::string, CachedValue, PmrStringHash> tracked_values_;
utils::MonotonicBufferResource memory_resource_{0};
memgraph::utils::pmr::unordered_map<std::string, CachedValue> tracked_values_;
};
} // namespace memgraph::query

View File

@@ -23,7 +23,9 @@
#include "storage/v2/property_value.hpp"
#include "utils/typeinfo.hpp"
namespace memgraph::query {
namespace memgraph {
namespace query {
struct LabelIx {
static const utils::TypeInfo kType;
@@ -60,8 +62,8 @@ inline bool operator!=(const PropertyIx &a, const PropertyIx &b) { return !(a ==
inline bool operator==(const EdgeTypeIx &a, const EdgeTypeIx &b) { return a.ix == b.ix && a.name == b.name; }
inline bool operator!=(const EdgeTypeIx &a, const EdgeTypeIx &b) { return !(a == b); }
} // namespace memgraph::query
} // namespace query
} // namespace memgraph
namespace std {
template <>
@@ -81,7 +83,9 @@ struct hash<memgraph::query::EdgeTypeIx> {
} // namespace std
namespace memgraph::query {
namespace memgraph {
namespace query {
class Tree;
@@ -1818,10 +1822,6 @@ class EdgeAtom : public memgraph::query::PatternAtom {
memgraph::query::Identifier *inner_edge{nullptr};
/// Argument identifier for the destination node of the edge.
memgraph::query::Identifier *inner_node{nullptr};
/// Argument identifier for the currently-accumulated path.
memgraph::query::Identifier *accumulated_path{nullptr};
/// Argument identifier for the weight of the currently-accumulated path.
memgraph::query::Identifier *accumulated_weight{nullptr};
/// Evaluates the result of the lambda.
memgraph::query::Expression *expression{nullptr};
@@ -1829,8 +1829,6 @@ class EdgeAtom : public memgraph::query::PatternAtom {
Lambda object;
object.inner_edge = inner_edge ? inner_edge->Clone(storage) : nullptr;
object.inner_node = inner_node ? inner_node->Clone(storage) : nullptr;
object.accumulated_path = accumulated_path ? accumulated_path->Clone(storage) : nullptr;
object.accumulated_weight = accumulated_weight ? accumulated_weight->Clone(storage) : nullptr;
object.expression = expression ? expression->Clone(storage) : nullptr;
return object;
}
@@ -2934,7 +2932,7 @@ class DatabaseInfoQuery : public memgraph::query::Query {
static const utils::TypeInfo kType;
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
enum class InfoType { INDEX, CONSTRAINT, EDGE_TYPES, NODE_LABELS };
enum class InfoType { INDEX, CONSTRAINT };
DEFVISITABLE(QueryVisitor<void>);
@@ -3031,7 +3029,7 @@ class ReplicationQuery : public memgraph::query::Query {
enum class SyncMode { SYNC, ASYNC };
enum class ReplicaState { READY, REPLICATING, RECOVERY, MAYBE_BEHIND };
enum class ReplicaState { READY, REPLICATING, RECOVERY, INVALID };
ReplicationQuery() = default;
@@ -3579,4 +3577,5 @@ class ShowDatabasesQuery : public memgraph::query::Query {
}
};
} // namespace memgraph::query
} // namespace query
} // namespace memgraph

View File

@@ -124,14 +124,6 @@ antlrcpp::Any CypherMainVisitor::visitDatabaseInfoQuery(MemgraphCypher::Database
info_query->info_type_ = DatabaseInfoQuery::InfoType::CONSTRAINT;
return info_query;
}
if (ctx->edgetypeInfo()) {
info_query->info_type_ = DatabaseInfoQuery::InfoType::EDGE_TYPES;
return info_query;
}
if (ctx->nodelabelInfo()) {
info_query->info_type_ = DatabaseInfoQuery::InfoType::NODE_LABELS;
return info_query;
}
// Should never get here
throw utils::NotYetImplemented("Database info query: '{}'", ctx->getText());
}
@@ -1224,6 +1216,10 @@ antlrcpp::Any CypherMainVisitor::visitCallProcedure(MemgraphCypher::CallProcedur
call_proc->memory_limit_ = memory_limit_info->first;
call_proc->memory_scale_ = memory_limit_info->second;
}
} else {
// Default to 100 MB
call_proc->memory_limit_ = storage_->Create<PrimitiveLiteral>(TypedValue(100));
call_proc->memory_scale_ = 1024U * 1024U;
}
const auto &maybe_found =
@@ -1244,13 +1240,11 @@ antlrcpp::Any CypherMainVisitor::visitCallProcedure(MemgraphCypher::CallProcedur
throw SemanticException("There is no procedure named '{}'.", call_proc->procedure_name_);
}
}
if (maybe_found) {
call_proc->is_write_ = maybe_found->second->info.is_write;
}
call_proc->is_write_ = maybe_found->second->info.is_write;
auto *yield_ctx = ctx->yieldProcedureResults();
if (!yield_ctx) {
if ((maybe_found && !maybe_found->second->results.empty()) && !call_proc->void_procedure_) {
if (!maybe_found->second->results.empty() && !call_proc->void_procedure_) {
throw SemanticException(
"CALL without YIELD may only be used on procedures which do not "
"return any result fields.");
@@ -1276,59 +1270,28 @@ antlrcpp::Any CypherMainVisitor::visitCallProcedure(MemgraphCypher::CallProcedur
call_proc->result_identifiers_.push_back(storage_->Create<Identifier>(result_alias));
}
} else {
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_) {
throw SemanticException(
"CALL without YIELD may only be used on procedures which do not "
"return any result fields.");
}
// When we return, we will release the lock on modules. This means that
// someone may reload the procedure and change the result signature. But to
// keep the implementation simple, we ignore the case as the rest of the
// code doesn't really care whether we yield or not, so it should not break.
return call_proc;
const auto &maybe_found =
procedure::FindProcedure(procedure::gModuleRegistry, call_proc->procedure_name_, utils::NewDeleteResource());
if (!maybe_found) {
throw SemanticException("There is no procedure named '{}'.", call_proc->procedure_name_);
}
if (yield_ctx->getTokens(MemgraphCypher::ASTERISK).empty()) {
call_proc->result_fields_.reserve(yield_ctx->procedureResult().size());
call_proc->result_identifiers_.reserve(yield_ctx->procedureResult().size());
for (auto *result : yield_ctx->procedureResult()) {
MG_ASSERT(result->variable().size() == 1 || result->variable().size() == 2);
call_proc->result_fields_.push_back(std::any_cast<std::string>(result->variable()[0]->accept(this)));
std::string result_alias;
if (result->variable().size() == 2) {
result_alias = std::any_cast<std::string>(result->variable()[1]->accept(this));
} else {
result_alias = std::any_cast<std::string>(result->variable()[0]->accept(this));
}
call_proc->result_identifiers_.push_back(storage_->Create<Identifier>(result_alias));
}
} else {
const auto &maybe_found =
procedure::FindProcedure(procedure::gModuleRegistry, call_proc->procedure_name_, utils::NewDeleteResource());
if (!maybe_found) {
throw SemanticException("There is no procedure named '{}'.", call_proc->procedure_name_);
}
const auto &[module, proc] = *maybe_found;
call_proc->result_fields_.reserve(proc->results.size());
call_proc->result_identifiers_.reserve(proc->results.size());
for (const auto &[result_name, desc] : proc->results) {
bool is_deprecated = desc.second;
if (is_deprecated) continue;
call_proc->result_fields_.emplace_back(result_name);
call_proc->result_identifiers_.push_back(storage_->Create<Identifier>(std::string(result_name)));
}
// When we leave the scope, we will release the lock on modules. This means
// that someone may reload the procedure and change its result signature. We
// are fine with this, because if new result fields were added then we yield
// the subset of those and that will appear to a user as if they used the
// procedure before reload. Any subsequent `CALL ... YIELD *` will fetch the
// new fields as well. In case the result signature has had some result
// fields removed, then the query execution will report an error that we are
// yielding missing fields. The user can then just retry the query.
const auto &[module, proc] = *maybe_found;
call_proc->result_fields_.reserve(proc->results.size());
call_proc->result_identifiers_.reserve(proc->results.size());
for (const auto &[result_name, desc] : proc->results) {
bool is_deprecated = desc.second;
if (is_deprecated) continue;
call_proc->result_fields_.emplace_back(result_name);
call_proc->result_identifiers_.push_back(storage_->Create<Identifier>(std::string(result_name)));
}
// When we leave the scope, we will release the lock on modules. This means
// that someone may reload the procedure and change its result signature. We
// are fine with this, because if new result fields were added then we yield
// the subset of those and that will appear to a user as if they used the
// procedure before reload. Any subsequent `CALL ... YIELD *` will fetch the
// new fields as well. In case the result signature has had some result
// fields removed, then the query execution will report an error that we are
// yielding missing fields. The user can then just retry the query.
}
return call_proc;
@@ -2017,15 +1980,6 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(MemgraphCypher::Relati
edge_lambda.inner_edge = storage_->Create<Identifier>(traversed_edge_variable);
auto traversed_node_variable = std::any_cast<std::string>(lambda->traversed_node->accept(this));
edge_lambda.inner_node = storage_->Create<Identifier>(traversed_node_variable);
if (lambda->accumulated_path) {
auto accumulated_path_variable = std::any_cast<std::string>(lambda->accumulated_path->accept(this));
edge_lambda.accumulated_path = storage_->Create<Identifier>(accumulated_path_variable);
if (lambda->accumulated_weight) {
auto accumulated_weight_variable = std::any_cast<std::string>(lambda->accumulated_weight->accept(this));
edge_lambda.accumulated_weight = storage_->Create<Identifier>(accumulated_weight_variable);
}
}
edge_lambda.expression = std::any_cast<Expression *>(lambda->expression()->accept(this));
return edge_lambda;
};
@@ -2050,15 +2004,6 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(MemgraphCypher::Relati
// In variable expansion inner variables are mandatory.
anonymous_identifiers.push_back(&edge->filter_lambda_.inner_edge);
anonymous_identifiers.push_back(&edge->filter_lambda_.inner_node);
// TODO: In what use case do we need accumulated path and weight here?
if (edge->filter_lambda_.accumulated_path) {
anonymous_identifiers.push_back(&edge->filter_lambda_.accumulated_path);
if (edge->filter_lambda_.accumulated_weight) {
anonymous_identifiers.push_back(&edge->filter_lambda_.accumulated_weight);
}
}
break;
case 1:
if (edge->type_ == EdgeAtom::Type::WEIGHTED_SHORTEST_PATH ||
@@ -2070,21 +2015,9 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(MemgraphCypher::Relati
// Add mandatory inner variables for filter lambda.
anonymous_identifiers.push_back(&edge->filter_lambda_.inner_edge);
anonymous_identifiers.push_back(&edge->filter_lambda_.inner_node);
if (edge->filter_lambda_.accumulated_path) {
anonymous_identifiers.push_back(&edge->filter_lambda_.accumulated_path);
if (edge->filter_lambda_.accumulated_weight) {
anonymous_identifiers.push_back(&edge->filter_lambda_.accumulated_weight);
}
}
} else {
// Other variable expands only have the filter lambda.
edge->filter_lambda_ = visit_lambda(relationshipLambdas[0]);
if (edge->filter_lambda_.accumulated_weight) {
throw SemanticException(
"Accumulated weight in filter lambda can be used only with "
"shortest paths expansion.");
}
}
break;
case 2:

View File

@@ -105,7 +105,7 @@ void PrintObject(std::ostream *out, const std::map<K, V> &map);
template <typename T>
void PrintObject(std::ostream *out, const T &arg) {
static_assert(!std::is_convertible_v<T, Expression *>,
static_assert(!std::is_convertible<T, Expression *>::value,
"This overload shouldn't be called with pointers convertible "
"to Expression *. This means your other PrintObject overloads aren't "
"being called for certain AST nodes when they should (or perhaps such "

View File

@@ -47,13 +47,9 @@ indexInfo : INDEX INFO ;
constraintInfo : CONSTRAINT INFO ;
edgetypeInfo : EDGE_TYPES INFO ;
nodelabelInfo : NODE_LABELS INFO ;
buildInfo : BUILD INFO ;
databaseInfoQuery : SHOW ( indexInfo | constraintInfo | edgetypeInfo | nodelabelInfo ) ;
databaseInfoQuery : SHOW ( indexInfo | constraintInfo ) ;
systemInfoQuery : SHOW ( storageInfo | buildInfo ) ;
@@ -179,7 +175,7 @@ relationshipDetail : '[' ( name=variable )? ( relationshipTypes )? ( variableExp
| '[' ( name=variable )? ( relationshipTypes )? ( variableExpansion )? relationshipLambda ( total_weight=variable )? (relationshipLambda )? ']'
| '[' ( name=variable )? ( relationshipTypes )? ( variableExpansion )? (properties )* ( relationshipLambda total_weight=variable )? (relationshipLambda )? ']';
relationshipLambda: '(' traversed_edge=variable ',' traversed_node=variable ( ',' accumulated_path=variable )? ( ',' accumulated_weight=variable )? '|' expression ')';
relationshipLambda: '(' traversed_edge=variable ',' traversed_node=variable '|' expression ')';
variableExpansion : '*' (BFS | WSHORTEST | ALLSHORTEST)? ( expression )? ( '..' ( expression )? )? ;

View File

@@ -61,7 +61,6 @@ memgraphCypherKeyword : cypherKeyword
| GRANT
| HEADER
| IDENTIFIED
| NODE_LABELS
| NULLIF
| IMPORT
| INACTIVE

View File

@@ -89,7 +89,6 @@ MULTI_DATABASE_EDIT : M U L T I UNDERSCORE D A T A B A S E UNDERSCORE E D I
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 ;
NODE_LABELS : N O D E UNDERSCORE L A B E L S ;
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 ;

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
@@ -31,7 +31,7 @@ class Parser {
* @param query incoming query that has to be compiled into query plan
* the first step is to generate AST
*/
explicit Parser(const std::string query) : query_(std::move(query)) {
Parser(const std::string query) : query_(std::move(query)) {
parser_.removeErrorListeners();
parser_.addErrorListener(&error_listener_);
tree_ = parser_.cypher();

View File

@@ -38,9 +38,6 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
void Visit(DatabaseInfoQuery &info_query) override {
switch (info_query.info_type_) {
case DatabaseInfoQuery::InfoType::INDEX:
// TODO: Reconsider priviliges, this 4 should have the same.
case DatabaseInfoQuery::InfoType::EDGE_TYPES:
case DatabaseInfoQuery::InfoType::NODE_LABELS:
// TODO: This should be INDEX | STATS, but we don't have support for
// *or* with privileges.
AddPrivilege(AuthQuery::Privilege::INDEX);

View File

@@ -12,11 +12,12 @@
#pragma once
#include <string>
#include <utility>
#include "utils/typeinfo.hpp"
namespace memgraph::query {
namespace memgraph {
namespace query {
class Symbol {
public:
@@ -33,13 +34,9 @@ class Symbol {
return enum_string[static_cast<int>(type)];
}
Symbol() = default;
Symbol(std::string name, int position, bool user_declared, Type type = Type::ANY, int token_position = -1)
: name_(std::move(name)),
position_(position),
user_declared_(user_declared),
type_(type),
token_position_(token_position) {}
Symbol() {}
Symbol(const std::string &name, int position, bool user_declared, Type type = Type::ANY, int token_position = -1)
: name_(name), position_(position), user_declared_(user_declared), type_(type), token_position_(token_position) {}
bool operator==(const Symbol &other) const {
return position_ == other.position_ && name_ == other.name_ && type_ == other.type_;
@@ -60,8 +57,8 @@ class Symbol {
int64_t token_position_{-1};
};
} // namespace memgraph::query
} // namespace query
} // namespace memgraph
namespace std {
template <>

View File

@@ -658,16 +658,8 @@ bool SymbolGenerator::PreVisit(EdgeAtom &edge_atom) {
scope.in_edge_range = false;
scope.in_pattern = false;
if (edge_atom.filter_lambda_.expression) {
std::vector<Identifier *> filter_lambda_identifiers{edge_atom.filter_lambda_.inner_edge,
edge_atom.filter_lambda_.inner_node};
if (edge_atom.filter_lambda_.accumulated_path) {
filter_lambda_identifiers.emplace_back(edge_atom.filter_lambda_.accumulated_path);
if (edge_atom.filter_lambda_.accumulated_weight) {
filter_lambda_identifiers.emplace_back(edge_atom.filter_lambda_.accumulated_weight);
}
}
VisitWithIdentifiers(edge_atom.filter_lambda_.expression, filter_lambda_identifiers);
VisitWithIdentifiers(edge_atom.filter_lambda_.expression,
{edge_atom.filter_lambda_.inner_edge, edge_atom.filter_lambda_.inner_node});
} else {
// Create inner symbols, but don't bind them in scope, since they are to
// be used in the missing filter expression.
@@ -676,17 +668,6 @@ bool SymbolGenerator::PreVisit(EdgeAtom &edge_atom) {
auto *inner_node = edge_atom.filter_lambda_.inner_node;
inner_node->MapTo(
symbol_table_->CreateSymbol(inner_node->name_, inner_node->user_declared_, Symbol::Type::VERTEX));
if (edge_atom.filter_lambda_.accumulated_path) {
auto *accumulated_path = edge_atom.filter_lambda_.accumulated_path;
accumulated_path->MapTo(
symbol_table_->CreateSymbol(accumulated_path->name_, accumulated_path->user_declared_, Symbol::Type::PATH));
if (edge_atom.filter_lambda_.accumulated_weight) {
auto *accumulated_weight = edge_atom.filter_lambda_.accumulated_weight;
accumulated_weight->MapTo(symbol_table_->CreateSymbol(
accumulated_weight->name_, accumulated_weight->user_declared_, Symbol::Type::NUMBER));
}
}
}
if (edge_atom.weight_lambda_.expression) {
VisitWithIdentifiers(edge_atom.weight_lambda_.expression,

View File

@@ -183,7 +183,7 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
/// If property lookup for one symbol is visited more times, it is better to fetch all properties
class PropertyLookupEvaluationModeVisitor : public ExpressionVisitor<void> {
public:
explicit PropertyLookupEvaluationModeVisitor() = default;
explicit PropertyLookupEvaluationModeVisitor() {}
using ExpressionVisitor<void>::Visit;

View File

@@ -22,7 +22,7 @@ namespace memgraph::query {
class SymbolTable final {
public:
SymbolTable() = default;
SymbolTable() {}
const Symbol &CreateSymbol(const std::string &name, bool user_declared, Symbol::Type type = Symbol::Type::ANY,
int32_t token_position = -1) {
MG_ASSERT(table_.size() <= std::numeric_limits<int32_t>::max(),

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
@@ -16,7 +16,6 @@
#include <iostream>
#include <span>
#include <string>
#include <utility>
#include <vector>
#include "query/exceptions.hpp"
@@ -33,7 +32,7 @@ namespace memgraph::query::frontend {
using namespace lexer_constants;
StrippedQuery::StrippedQuery(std::string query) : original_(std::move(query)) {
StrippedQuery::StrippedQuery(const std::string &query) : original_(query) {
enum class Token {
UNMATCHED,
KEYWORD, // Including true, false and null.
@@ -256,29 +255,29 @@ std::string GetFirstUtf8Symbol(const char *_s) {
// According to
// https://stackoverflow.com/questions/16260033/reinterpret-cast-between-char-and-stduint8-t-safe
// this checks if casting from const char * to uint8_t is undefined behaviour.
static_assert(std::is_same_v<std::uint8_t, unsigned char>,
static_assert(std::is_same<std::uint8_t, unsigned char>::value,
"This library requires std::uint8_t to be implemented as "
"unsigned char.");
const uint8_t *s = reinterpret_cast<const uint8_t *>(_s);
if ((*s >> 7) == 0x00) return std::string(_s, _s + 1);
if ((*s >> 5) == 0x06) {
const auto *s1 = s + 1;
auto *s1 = s + 1;
if ((*s1 >> 6) != 0x02) throw LexingException("Invalid character.");
return std::string(_s, _s + 2);
}
if ((*s >> 4) == 0x0e) {
const auto *s1 = s + 1;
auto *s1 = s + 1;
if ((*s1 >> 6) != 0x02) throw LexingException("Invalid character.");
const auto *s2 = s + 2;
auto *s2 = s + 2;
if ((*s2 >> 6) != 0x02) throw LexingException("Invalid character.");
return std::string(_s, _s + 3);
}
if ((*s >> 3) == 0x1e) {
const auto *s1 = s + 1;
auto *s1 = s + 1;
if ((*s1 >> 6) != 0x02) throw LexingException("Invalid character.");
const auto *s2 = s + 2;
auto *s2 = s + 2;
if ((*s2 >> 6) != 0x02) throw LexingException("Invalid character.");
const auto *s3 = s + 3;
auto *s3 = s + 3;
if ((*s3 >> 6) != 0x02) throw LexingException("Invalid character.");
return std::string(_s, _s + 4);
}
@@ -287,29 +286,29 @@ std::string GetFirstUtf8Symbol(const char *_s) {
// Return codepoint of first utf8 symbol and its encoded length.
std::pair<int, int> GetFirstUtf8SymbolCodepoint(const char *_s) {
static_assert(std::is_same_v<std::uint8_t, unsigned char>,
static_assert(std::is_same<std::uint8_t, unsigned char>::value,
"This library requires std::uint8_t to be implemented as "
"unsigned char.");
const uint8_t *s = reinterpret_cast<const uint8_t *>(_s);
if ((*s >> 7) == 0x00) return {*s & 0x7f, 1};
if ((*s >> 5) == 0x06) {
const auto *s1 = s + 1;
auto *s1 = s + 1;
if ((*s1 >> 6) != 0x02) throw LexingException("Invalid character.");
return {((*s & 0x1f) << 6) | (*s1 & 0x3f), 2};
}
if ((*s >> 4) == 0x0e) {
const auto *s1 = s + 1;
auto *s1 = s + 1;
if ((*s1 >> 6) != 0x02) throw LexingException("Invalid character.");
const auto *s2 = s + 2;
auto *s2 = s + 2;
if ((*s2 >> 6) != 0x02) throw LexingException("Invalid character.");
return {((*s & 0x0f) << 12) | ((*s1 & 0x3f) << 6) | (*s2 & 0x3f), 3};
}
if ((*s >> 3) == 0x1e) {
const auto *s1 = s + 1;
auto *s1 = s + 1;
if ((*s1 >> 6) != 0x02) throw LexingException("Invalid character.");
const auto *s2 = s + 2;
auto *s2 = s + 2;
if ((*s2 >> 6) != 0x02) throw LexingException("Invalid character.");
const auto *s3 = s + 3;
auto *s3 = s + 3;
if ((*s3 >> 6) != 0x02) throw LexingException("Invalid character.");
return {((*s & 0x07) << 18) | ((*s1 & 0x3f) << 12) | ((*s2 & 0x3f) << 6) | (*s3 & 0x3f), 4};
}
@@ -337,7 +336,7 @@ int StrippedQuery::MatchSpecial(int start) const { return kSpecialTokens.Match(o
int StrippedQuery::MatchString(int start) const {
if (original_[start] != '"' && original_[start] != '\'') return 0;
char start_char = original_[start];
for (const auto *p = original_.data() + start + 1; *p; ++p) {
for (auto *p = original_.data() + start + 1; *p; ++p) {
if (*p == start_char) return p - (original_.data() + start) + 1;
if (*p == '\\') {
++p;
@@ -347,7 +346,7 @@ int StrippedQuery::MatchString(int start) const {
continue;
} else if (*p == 'U' || *p == 'u') {
int cnt = 0;
const auto *r = p + 1;
auto *r = p + 1;
while (isxdigit(*r) && cnt < 8) {
++cnt;
++r;

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
@@ -40,7 +40,7 @@ class StrippedQuery {
*
* @param query Input query.
*/
explicit StrippedQuery(std::string query);
explicit StrippedQuery(const std::string &query);
/**
* Copy constructor is deleted because we don't want to make unnecessary

View File

@@ -17,7 +17,8 @@
#include <unordered_set>
#include <vector>
namespace memgraph::query::lexer_constants {
namespace memgraph::query {
namespace lexer_constants {
namespace trie {
@@ -32,7 +33,7 @@ inline int Noop(int x) { return x; }
class Trie {
public:
Trie() = default;
Trie() {}
Trie(std::initializer_list<std::string> l) {
for (const auto &s : l) {
Insert(s);
@@ -2933,4 +2934,5 @@ const trie::Trie kSpecialTokens = {";",
"\xEF\xB9\x98", // u8"\ufe58"
"\xEF\xB9\xA3", // u8"\ufe63"
"\xEF\xBC\x8D"}; // u8"\uff0d"
} // namespace memgraph::query::lexer_constants
} // namespace lexer_constants
} // namespace memgraph::query

View File

@@ -593,7 +593,6 @@ TypedValue ValueType(const TypedValue *args, int64_t nargs, const FunctionContex
case TypedValue::Type::Duration:
return TypedValue("DURATION", ctx.memory);
case TypedValue::Type::Graph:
case TypedValue::Type::Function:
throw QueryRuntimeException("Cannot fetch graph as it is not standardized openCypher type name");
}
}

View File

@@ -18,7 +18,6 @@
#include <map>
#include <optional>
#include <regex>
#include <stdexcept>
#include <string>
#include <vector>
@@ -29,10 +28,8 @@
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/interpret/frame.hpp"
#include "query/procedure/mg_procedure_impl.hpp"
#include "query/typed_value.hpp"
#include "spdlog/spdlog.h"
#include "storage/v2/storage_mode.hpp"
#include "utils/exceptions.hpp"
#include "utils/frame_change_id.hpp"
#include "utils/logging.hpp"
@@ -190,8 +187,6 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
utils::MemoryResource *GetMemoryResource() const { return ctx_->memory; }
void ResetPropertyLookupCache() { property_lookup_cache_.clear(); }
TypedValue Visit(NamedExpression &named_expression) override {
const auto &symbol = symbol_table_->at(named_expression);
auto value = named_expression.expression_->Accept(*this);
@@ -830,8 +825,8 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
throw QueryRuntimeException("'coalesce' requires at least one argument.");
}
for (auto &expr : exprs) {
TypedValue val(expr->Accept(*this), ctx_->memory);
for (int64_t i = 0; i < exprs.size(); ++i) {
TypedValue val(exprs[i]->Accept(*this), ctx_->memory);
if (!val.IsNull()) {
return val;
}
@@ -842,8 +837,6 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
TypedValue Visit(Function &function) override {
FunctionContext function_ctx{dba_, ctx_->memory, ctx_->timestamp, &ctx_->counters, view_};
bool is_transactional = storage::IsTransactional(dba_->GetStorageMode());
TypedValue res(ctx_->memory);
// Stack allocate evaluated arguments when there's a small number of them.
if (function.arguments_.size() <= 8) {
TypedValue arguments[8] = {TypedValue(ctx_->memory), TypedValue(ctx_->memory), TypedValue(ctx_->memory),
@@ -852,20 +845,19 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
for (size_t i = 0; i < function.arguments_.size(); ++i) {
arguments[i] = function.arguments_[i]->Accept(*this);
}
res = function.function_(arguments, function.arguments_.size(), function_ctx);
auto res = function.function_(arguments, function.arguments_.size(), function_ctx);
MG_ASSERT(res.GetMemoryResource() == ctx_->memory);
return res;
} else {
TypedValue::TVector arguments(ctx_->memory);
arguments.reserve(function.arguments_.size());
for (const auto &argument : function.arguments_) {
arguments.emplace_back(argument->Accept(*this));
}
res = function.function_(arguments.data(), arguments.size(), function_ctx);
auto res = function.function_(arguments.data(), arguments.size(), function_ctx);
MG_ASSERT(res.GetMemoryResource() == ctx_->memory);
return res;
}
MG_ASSERT(res.GetMemoryResource() == ctx_->memory);
if (!is_transactional && res.ContainsDeleted()) [[unlikely]] {
return TypedValue(ctx_->memory);
}
return res;
}
TypedValue Visit(Reduce &reduce) override {
@@ -911,17 +903,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
return TypedValue(std::move(result), ctx_->memory);
}
TypedValue Visit(Exists &exists) override {
TypedValue &frame_exists_value = frame_->at(symbol_table_->at(exists));
if (!frame_exists_value.IsFunction()) [[unlikely]] {
throw QueryRuntimeException(
"Unexpected behavior: Exists expected a function, got {}. Please report the problem on GitHub issues",
frame_exists_value.type());
}
TypedValue result{ctx_->memory};
frame_exists_value.ValueFunction()(&result);
return result;
}
TypedValue Visit(Exists &exists) override { return TypedValue{frame_->at(symbol_table_->at(exists)), ctx_->memory}; }
TypedValue Visit(All &all) override {
auto list_value = all.list_expression_->Accept(*this);

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