Compare commits
69 Commits
add-macos-
...
T595-notif
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4194ff393 | ||
|
|
6abd356d01 | ||
|
|
99a6c72bba | ||
|
|
173f5430aa | ||
|
|
362dc95e27 | ||
|
|
eead24a562 | ||
|
|
d79dd69607 | ||
|
|
024bf0c578 | ||
|
|
d7443a5558 | ||
|
|
c1316a6b75 | ||
|
|
2df357b012 | ||
|
|
b2b5a6e2a0 | ||
|
|
2604b643c7 | ||
|
|
3167808062 | ||
|
|
5e2ee6c817 | ||
|
|
862a1afdf1 | ||
|
|
bbce21e78f | ||
|
|
15c8662023 | ||
|
|
beaba0fc16 | ||
|
|
8f70c5f2a5 | ||
|
|
14c651d3ba | ||
|
|
04efc7a4a6 | ||
|
|
19832b5838 | ||
|
|
e63fae2d5b | ||
|
|
34dd47ef07 | ||
|
|
d3f275b231 | ||
|
|
aad4bcb7a0 | ||
|
|
034b54cb72 | ||
|
|
8cf51d9f68 | ||
|
|
1cd1da84fd | ||
|
|
128a6cd522 | ||
|
|
d9eeedb9ee | ||
|
|
156e2cd095 | ||
|
|
8b834c702c | ||
|
|
eda5213d95 | ||
|
|
1f2a15e7c8 | ||
|
|
d72e7fa38d | ||
|
|
e5e37bc14a | ||
|
|
3ee068bbf9 | ||
|
|
68e846b182 | ||
|
|
310e305cfb | ||
|
|
9d6a23b6bd | ||
|
|
f2d5ab61c4 | ||
|
|
0f77c85824 | ||
|
|
d6d4153fb7 | ||
|
|
7d6a5e5b9c | ||
|
|
c529d52664 | ||
|
|
45451bae3b | ||
|
|
3e11f38548 | ||
|
|
6e4047a847 | ||
|
|
8febdc12fb | ||
|
|
3f23a10f44 | ||
|
|
11300960de | ||
|
|
1d5f387ddd | ||
|
|
c4c3a254bf | ||
|
|
b4beb0fc86 | ||
|
|
58e6097664 | ||
|
|
ff21c0705c | ||
|
|
2a2b99b02a | ||
|
|
3daab6ce97 | ||
|
|
fbd7274c95 | ||
|
|
6efc84f022 | ||
|
|
287c2e94d1 | ||
|
|
417cf4b30b | ||
|
|
68e7fd3d36 | ||
|
|
5261d82063 | ||
|
|
9eb87bcf3e | ||
|
|
a9491d3e68 | ||
|
|
b42e47b0be |
7
.github/pull_request_template.md
vendored
7
.github/pull_request_template.md
vendored
@@ -1,11 +1,14 @@
|
||||
[master < Epic] PR
|
||||
- [ ] Check, and update documentation if necessary
|
||||
- [ ] Update [changelog](https://docs.memgraph.com/memgraph/changelog)
|
||||
- [ ] Write E2E tests
|
||||
- [ ] Compare the [benchmarking results](https://bench-graph.memgraph.com/) between the master branch and the Epic branch
|
||||
- [ ] Provide the full content or a guide for the final git message
|
||||
|
||||
[master < Task] PR
|
||||
- [ ] Check, and update documentation if necessary
|
||||
- [ ] Update [changelog](https://docs.memgraph.com/memgraph/changelog)
|
||||
- [ ] Provide the full content or a guide for the final git message
|
||||
|
||||
|
||||
To keep docs changelog up to date, one more thing to do:
|
||||
- [ ] Write a release note here
|
||||
- [ ] Tag someone from docs team in the comments
|
||||
|
||||
4
.github/workflows/daily_benchmark.yaml
vendored
4
.github/workflows/daily_benchmark.yaml
vendored
@@ -3,7 +3,7 @@ name: Daily Benchmark
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
- cron: "0 22 * * *"
|
||||
|
||||
jobs:
|
||||
release_benchmarks:
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
|
||||
63
.github/workflows/diff.yaml
vendored
63
.github/workflows/diff.yaml
vendored
@@ -14,6 +14,7 @@ on:
|
||||
- "**/*.md"
|
||||
- ".clang-format"
|
||||
- "CODEOWNERS"
|
||||
- "licenses/*"
|
||||
|
||||
jobs:
|
||||
community_build:
|
||||
@@ -26,7 +27,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -64,7 +65,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -75,7 +76,7 @@ jobs:
|
||||
- name: Fetch all history for all tags and branches
|
||||
run: git fetch
|
||||
|
||||
- name: Build combined ASAN, UBSAN and coverage binaries
|
||||
- name: Initialize deps
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
@@ -83,6 +84,32 @@ jobs:
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
|
||||
- name: Set base branch
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
echo "BASE_BRANCH=origin/${{ github.base_ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set base branch # if we manually dispatch or push to master
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
run: |
|
||||
echo "BASE_BRANCH=origin/master" >> $GITHUB_ENV
|
||||
|
||||
- name: Python code analysis
|
||||
run: |
|
||||
CHANGED_FILES=$(git diff -U0 ${{ env.BASE_BRANCH }}... --name-only)
|
||||
for file in ${CHANGED_FILES}; do
|
||||
echo ${file}
|
||||
if [[ ${file} == *.py ]]; then
|
||||
python3 -m black --check --diff ${file}
|
||||
python3 -m isort --check-only --diff ${file}
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Build combined ASAN, UBSAN and coverage binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
|
||||
cd build
|
||||
cmake -DTEST_COVERAGE=ON -DASAN=ON -DUBSAN=ON ..
|
||||
make -j$THREADS memgraph__unit
|
||||
@@ -110,21 +137,11 @@ jobs:
|
||||
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
|
||||
|
||||
- name: Save code coverage
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Code coverage"
|
||||
path: tools/github/generated/code_coverage.tar.gz
|
||||
|
||||
- name: Set base branch
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
echo "BASE_BRANCH=origin/${{ github.base_ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set base branch # if we manually dispatch or push to master
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
run: |
|
||||
echo "BASE_BRANCH=origin/master" >> $GITHUB_ENV
|
||||
|
||||
- name: Run clang-tidy
|
||||
run: |
|
||||
source /opt/toolchain-v4/activate
|
||||
@@ -145,7 +162,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -206,7 +223,7 @@ jobs:
|
||||
./cppcheck_and_clang_format diff
|
||||
|
||||
- name: Save cppcheck and clang-format errors
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Code coverage"
|
||||
path: tools/github/cppcheck_and_clang_format.txt
|
||||
@@ -221,7 +238,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -246,7 +263,7 @@ jobs:
|
||||
./continuous_integration
|
||||
|
||||
- name: Save quality assurance status
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "GQL Behave Status"
|
||||
path: |
|
||||
@@ -303,13 +320,13 @@ jobs:
|
||||
cpack -G DEB --config ../CPackConfig.cmake
|
||||
|
||||
- name: Save enterprise DEB package
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Enterprise DEB package"
|
||||
path: build/output/memgraph*.deb
|
||||
|
||||
- name: Save test data
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: "Test data"
|
||||
@@ -328,7 +345,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -353,7 +370,7 @@ jobs:
|
||||
./run.sh test --binary ../../build/memgraph --run-args "test-all --node-configs resources/node-config.edn" --ignore-run-stdout-logs --ignore-run-stderr-logs
|
||||
|
||||
- name: Save Jepsen report
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: "Jepsen Report"
|
||||
@@ -369,7 +386,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
|
||||
2
.github/workflows/full_clang_tidy.yaml
vendored
2
.github/workflows/full_clang_tidy.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
|
||||
54
.github/workflows/package_all.yaml
vendored
54
.github/workflows/package_all.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
run: |
|
||||
./release/package/run.sh package centos-7
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: centos-7
|
||||
path: build/output/centos-7/memgraph*.rpm
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
run: |
|
||||
./release/package/run.sh package centos-9
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: centos-9
|
||||
path: build/output/centos-9/memgraph*.rpm
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
run: |
|
||||
./release/package/run.sh package debian-10
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: debian-10
|
||||
path: build/output/debian-10/memgraph*.deb
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
run: |
|
||||
./release/package/run.sh package debian-11
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: debian-11
|
||||
path: build/output/debian-11/memgraph*.deb
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
./run.sh package debian-11 --for-docker
|
||||
./run.sh docker
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: docker
|
||||
path: build/output/docker/memgraph*.tar.gz
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
run: |
|
||||
./release/package/run.sh package ubuntu-18.04
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ubuntu-1804
|
||||
path: build/output/ubuntu-18.04/memgraph*.deb
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
run: |
|
||||
./release/package/run.sh package ubuntu-20.04
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ubuntu-2004
|
||||
path: build/output/ubuntu-20.04/memgraph*.deb
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
run: |
|
||||
./release/package/run.sh package ubuntu-22.04
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ubuntu-2204
|
||||
path: build/output/ubuntu-22.04/memgraph*.deb
|
||||
@@ -155,11 +155,28 @@ jobs:
|
||||
run: |
|
||||
./release/package/run.sh package debian-11 --for-platform
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
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
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: fedora-36
|
||||
path: build/output/fedora-36/memgraph*.rpm
|
||||
|
||||
debian-11-arm:
|
||||
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
|
||||
timeout-minutes: 60
|
||||
@@ -172,7 +189,24 @@ jobs:
|
||||
run: |
|
||||
./release/package/run.sh package debian-11-arm
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: debian-11-arm
|
||||
path: build/output/debian-11-arm/memgraph*.deb
|
||||
|
||||
ubuntu-2204-arm:
|
||||
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package ubuntu-22.04-arm
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ubuntu-22.04-arm
|
||||
path: build/output/ubuntu-22.04-arm/memgraph*.deb
|
||||
|
||||
18
.github/workflows/release_centos8.yaml
vendored
18
.github/workflows/release_centos8.yaml
vendored
@@ -3,7 +3,7 @@ name: Release CentOS 8
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
- cron: "0 22 * * *"
|
||||
|
||||
jobs:
|
||||
community_build:
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
|
||||
|
||||
- name: Save code coverage
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Code coverage"
|
||||
path: tools/github/generated/code_coverage.tar.gz
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
./cppcheck_and_clang_format diff
|
||||
|
||||
- name: Save cppcheck and clang-format errors
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Code coverage"
|
||||
path: tools/github/cppcheck_and_clang_format.txt
|
||||
@@ -189,7 +189,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -225,7 +225,7 @@ jobs:
|
||||
rpmlint memgraph*.rpm
|
||||
|
||||
- name: Save enterprise RPM package
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Enterprise RPM package"
|
||||
path: build/output/memgraph*.rpm
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
./continuous_integration
|
||||
|
||||
- name: Save quality assurance status
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "GQL Behave Status"
|
||||
path: |
|
||||
|
||||
22
.github/workflows/release_debian10.yaml
vendored
22
.github/workflows/release_debian10.yaml
vendored
@@ -3,7 +3,7 @@ name: Release Debian 10
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
- cron: "0 22 * * *"
|
||||
|
||||
jobs:
|
||||
community_build:
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
|
||||
|
||||
- name: Save code coverage
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Code coverage"
|
||||
path: tools/github/generated/code_coverage.tar.gz
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
./cppcheck_and_clang_format diff
|
||||
|
||||
- name: Save cppcheck and clang-format errors
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Code coverage"
|
||||
path: tools/github/cppcheck_and_clang_format.txt
|
||||
@@ -189,7 +189,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -224,7 +224,7 @@ jobs:
|
||||
cpack -G DEB --config ../CPackConfig.cmake
|
||||
|
||||
- name: Save enterprise DEB package
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Enterprise DEB package"
|
||||
path: build/output/memgraph*.deb
|
||||
@@ -261,7 +261,7 @@ jobs:
|
||||
./continuous_integration
|
||||
|
||||
- name: Save quality assurance status
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "GQL Behave Status"
|
||||
path: |
|
||||
@@ -324,7 +324,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -349,7 +349,7 @@ jobs:
|
||||
./run.sh test --binary ../../build/memgraph --run-args "test-all --node-configs resources/node-config.edn" --ignore-run-stdout-logs --ignore-run-stderr-logs
|
||||
|
||||
- name: Save Jepsen report
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: "Jepsen Report"
|
||||
|
||||
8
.github/workflows/release_docker.yaml
vendored
8
.github/workflows/release_docker.yaml
vendored
@@ -19,17 +19,17 @@ jobs:
|
||||
DOCKER_REPOSITORY_NAME: memgraph
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
18
.github/workflows/release_ubuntu2004.yaml
vendored
18
.github/workflows/release_ubuntu2004.yaml
vendored
@@ -3,7 +3,7 @@ name: Release Ubuntu 20.04
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
- cron: "0 22 * * *"
|
||||
|
||||
jobs:
|
||||
community_build:
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
|
||||
|
||||
- name: Save code coverage
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Code coverage"
|
||||
path: tools/github/generated/code_coverage.tar.gz
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
./cppcheck_and_clang_format diff
|
||||
|
||||
- name: Save cppcheck and clang-format errors
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Code coverage"
|
||||
path: tools/github/cppcheck_and_clang_format.txt
|
||||
@@ -189,7 +189,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
@@ -224,7 +224,7 @@ jobs:
|
||||
cpack -G DEB --config ../CPackConfig.cmake
|
||||
|
||||
- name: Save enterprise DEB package
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "Enterprise DEB package"
|
||||
path: build/output/memgraph*.deb
|
||||
@@ -261,7 +261,7 @@ jobs:
|
||||
./continuous_integration
|
||||
|
||||
- name: Save quality assurance status
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "GQL Behave Status"
|
||||
path: |
|
||||
|
||||
@@ -6,18 +6,14 @@ repos:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
rev: 22.8.0
|
||||
hooks:
|
||||
- id: black
|
||||
args: # arguments to configure black
|
||||
- --line-length=120
|
||||
- --include='\.pyi?$'
|
||||
# these folders wont be formatted by black
|
||||
- --exclude="""\.git |
|
||||
\.__pycache__|
|
||||
build|
|
||||
libs|
|
||||
.cache"""
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.10.1
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v13.0.0
|
||||
hooks:
|
||||
|
||||
81
README.md
81
README.md
@@ -4,10 +4,6 @@
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
Build modern, graph-based applications on top of your streaming data in minutes.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/memgraph/memgraph/blob/master/licenses/APL.txt">
|
||||
<img src="https://img.shields.io/badge/license-APL-green" alt="license" title="license"/>
|
||||
@@ -22,7 +18,7 @@ Build modern, graph-based applications on top of your streaming data in minutes.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/memgraph/memgraph">
|
||||
<img src="https://img.shields.io/github/workflow/status/memgraph/memgraph/Release%20Ubuntu%2020.04/master" alt="build" title="build"/>
|
||||
<img src="https://img.shields.io/github/actions/workflow/status/memgraph/memgraph/release_debian10.yaml?branch=master&label=build%20and%20test&logo=github"/>
|
||||
</a>
|
||||
<a href="https://memgraph.com/docs/" alt="Documentation">
|
||||
<img src="https://img.shields.io/badge/documentation-Memgraph-orange" />
|
||||
@@ -37,9 +33,10 @@ Build modern, graph-based applications on top of your streaming data in minutes.
|
||||
|
||||
## :clipboard: Description
|
||||
|
||||
Memgraph is a streaming graph application platform that helps you wrangle your
|
||||
streaming data, build sophisticated models that you can query in real-time, and
|
||||
develop graph applications.
|
||||
Memgraph is an open source graph database built for real-time streaming and
|
||||
compatible with Neo4j. Whether you're a developer or a data scientist with
|
||||
interconnected data, Memgraph will get you the immediate actionable insights
|
||||
fast.
|
||||
|
||||
Memgraph directly connects to your streaming infrastructure. You can ingest data
|
||||
from sources like Kafka, SQL, or plain CSV files. Memgraph provides a standard
|
||||
@@ -51,8 +48,20 @@ natural and effective way to model many real-world problems without relying on
|
||||
complex SQL schemas.
|
||||
|
||||
Memgraph is implemented in C/C++ and leverages an in-memory first architecture
|
||||
to ensure that you’re getting the best possible performance consistently and
|
||||
without surprises. It’s also ACID-compliant and highly available.
|
||||
to ensure that you’re getting the [best possible
|
||||
performance](http://memgraph.com/benchgraph) consistently and without surprises.
|
||||
It’s also ACID-compliant and highly available.
|
||||
|
||||
## :zap: Features
|
||||
|
||||
- Run Python, Rust, and C/C++ code natively, check out the
|
||||
[MAGE](https://github.com/memgraph/mage) graph algorithm library
|
||||
- Native support for machine learning
|
||||
- Streaming support
|
||||
- Replication
|
||||
- Authentication and authorization
|
||||
- ACID compliance
|
||||
|
||||
|
||||
## :video_game: Memgraph Playground
|
||||
|
||||
@@ -76,28 +85,49 @@ your browser.
|
||||
### macOS
|
||||
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-on-macos-docker)
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-on-ubuntu)
|
||||
|
||||
### Linux
|
||||
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-on-linux-docker)
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-on-debian)
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-on-ubuntu)
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-from-rpm)
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-from-rpm)
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-from-rpm)
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-from-rpm)
|
||||
|
||||
You can find the binaries and Docker images on the [Download
|
||||
Hub](https://memgraph.com/download) and the installation instructions in the
|
||||
[official documentation](https://memgraph.com/docs/memgraph/installation).
|
||||
|
||||
## :zap: Features
|
||||
|
||||
- Run Python, Rust, and C/C++ code natively, check out the
|
||||
[MAGE](https://github.com/memgraph/mage) graph algorithm library
|
||||
- Native support for machine learning
|
||||
- Streaming support
|
||||
- Replication
|
||||
- Authentication and authorization
|
||||
- ACID compliance
|
||||
## :cloud: Memgraph Cloud
|
||||
|
||||
Check out [Memgraph Cloud](https://memgraph.com/docs/memgraph-cloud) - a cloud service fully managed on AWS and available in 6 geographic regions around the world. Memgraph Cloud allows you to create projects with Enterprise instances of MemgraphDB from your browser.
|
||||
|
||||
<p align="left">
|
||||
<a href="https://memgraph.com/docs/memgraph-cloud">
|
||||
<img width="450px" alt="Memgraph Cloud" src="https://public-assets.memgraph.com/memgraph-gifs%2Fcloud.gif">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## :link: Connect to Memgraph
|
||||
|
||||
[Connect to the database](https://memgraph.com/docs/memgraph/connect-to-memgraph) using Memgraph Lab, mgconsole, various drivers (Python, C/C++ and others) and WebSocket.
|
||||
|
||||
### :microscope: Memgraph Lab
|
||||
|
||||
Visualize graphs and play with queries to understand your data. [Memgraph Lab](https://memgraph.com/docs/memgraph-lab) is a user interface that helps you explore and manipulate the data stored in Memgraph. Visualize graphs, execute ad hoc queries, and optimize their performance.
|
||||
|
||||
<p align="left">
|
||||
<a href="https://memgraph.com/docs/memgraph-lab">
|
||||
<img width="450px" alt="Memgraph Cloud" src="https://public-assets.memgraph.com/memgraph-gifs%2Flab.gif">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## :file_folder: Import data
|
||||
|
||||
[Import data](https://memgraph.com/docs/memgraph/import-data) into Memgraph using Kafka, RedPanda or Pulsar streams, CSV and JSON files, or Cypher commands.
|
||||
|
||||
## :bookmark_tabs: Documentation
|
||||
|
||||
@@ -141,8 +171,17 @@ Memgraph Community is available under the [BSL
|
||||
license](./licenses/BSL.txt).</br> Memgraph Enterprise is available under the
|
||||
[MEL license](./licenses/MEL.txt).
|
||||
|
||||
## :busts_in_silhouette: Community
|
||||
|
||||
- :purple_heart: [**Discord**](https://discord.gg/memgraph)
|
||||
- :ocean: [**Stack Overflow**](https://stackoverflow.com/questions/tagged/memgraphdb)
|
||||
- :busts_in_silhouette: [**Discourse forum**](https://discourse.memgraph.com/)
|
||||
- :bird: [**Twitter**](https://twitter.com/memgraphdb)
|
||||
- :movie_camera:
|
||||
[**YouTube**](https://www.youtube.com/channel/UCZ3HOJvHGxtQ_JHxOselBYg)
|
||||
|
||||
<p align="center">
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/⬆️back_to_top_⬆️-white" alt="Back to top" title="Back to top"/>
|
||||
<img src="https://img.shields.io/badge/⬆️ back_to_top_⬆️-white" alt="Back to top" title="Back to top"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -5,12 +5,10 @@ import os
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
CONFIG_FILE = os.path.join(SCRIPT_DIR, "flags.yaml")
|
||||
WIDTH = 80
|
||||
@@ -18,14 +16,13 @@ WIDTH = 80
|
||||
|
||||
def wrap_text(s, initial_indent="# "):
|
||||
return "\n#\n".join(
|
||||
map(lambda x: textwrap.fill(x, WIDTH, initial_indent=initial_indent,
|
||||
subsequent_indent="# "), s.split("\n")))
|
||||
map(lambda x: textwrap.fill(x, WIDTH, initial_indent=initial_indent, subsequent_indent="# "), s.split("\n"))
|
||||
)
|
||||
|
||||
|
||||
def extract_flags(binary_path):
|
||||
ret = {}
|
||||
data = subprocess.run([binary_path, "--help-xml"],
|
||||
stdout=subprocess.PIPE).stdout.decode("utf-8")
|
||||
data = subprocess.run([binary_path, "--help-xml"], stdout=subprocess.PIPE).stdout.decode("utf-8")
|
||||
root = ET.fromstring(data)
|
||||
for child in root:
|
||||
if child.tag == "usage" and child.text.lower().count("warning"):
|
||||
@@ -46,8 +43,7 @@ def apply_config_to_flags(config, flags):
|
||||
for modification in config["modifications"]:
|
||||
name = modification["name"]
|
||||
if name not in flags:
|
||||
print("WARNING: Flag '" + name + "' missing from binary!",
|
||||
file=sys.stderr)
|
||||
print("WARNING: Flag '" + name + "' missing from binary!", file=sys.stderr)
|
||||
continue
|
||||
flags[name]["default"] = modification["value"]
|
||||
flags[name]["override"] = modification["override"]
|
||||
@@ -75,8 +71,9 @@ def extract_sections(flags):
|
||||
else:
|
||||
sections.append((current_section, current_flags))
|
||||
sections.append(("other", other))
|
||||
assert set(sum(map(lambda x: x[1], sections), [])) == set(flags.keys()), \
|
||||
"The section extraction algorithm lost some flags!"
|
||||
assert set(sum(map(lambda x: x[1], sections), [])) == set(
|
||||
flags.keys()
|
||||
), "The section extraction algorithm lost some flags!"
|
||||
return sections
|
||||
|
||||
|
||||
@@ -89,8 +86,7 @@ def generate_config_file(sections, flags):
|
||||
helpstr = flag["meaning"] + " [" + flag["type"] + "]"
|
||||
ret += wrap_text(helpstr) + "\n"
|
||||
prefix = "# " if not flag["override"] else ""
|
||||
ret += prefix + "--" + flag["name"].replace("_", "-") + \
|
||||
"=" + flag["default"] + "\n\n"
|
||||
ret += prefix + "--" + flag["name"].replace("_", "-") + "=" + flag["default"] + "\n\n"
|
||||
ret += "\n"
|
||||
ret += wrap_text(config["footer"])
|
||||
return ret.strip() + "\n"
|
||||
@@ -98,13 +94,9 @@ def generate_config_file(sections, flags):
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("memgraph_binary",
|
||||
help="path to Memgraph binary")
|
||||
parser.add_argument("output_file",
|
||||
help="path where to store the generated Memgraph "
|
||||
"configuration file")
|
||||
parser.add_argument("--config-file", default=CONFIG_FILE,
|
||||
help="path to generator configuration file")
|
||||
parser.add_argument("memgraph_binary", help="path to Memgraph binary")
|
||||
parser.add_argument("output_file", help="path where to store the generated Memgraph " "configuration file")
|
||||
parser.add_argument("--config-file", default=CONFIG_FILE, help="path to generator configuration file")
|
||||
|
||||
args = parser.parse_args()
|
||||
flags = extract_flags(args.memgraph_binary)
|
||||
|
||||
@@ -6,7 +6,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "debian-11"
|
||||
check_architecture "arm64"
|
||||
check_architecture "arm64" "aarch64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
|
||||
103
environment/os/fedora-36.sh
Executable file
103
environment/os/fedora-36.sh
Executable file
@@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "fedora-36"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils-common gcc gcc-c++ make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg2 # used for archive signature verification
|
||||
tar gzip bzip2 xz unzip # used for archive unpacking
|
||||
zlib-devel # zlib library used for all builds
|
||||
expat-devel xz-devel python3-devel texinfo libbabeltrace-devel # for gdb
|
||||
curl libcurl-devel # for cmake
|
||||
readline-devel # for cmake and llvm
|
||||
libffi-devel libxml2-devel # for llvm
|
||||
libedit-devel pcre-devel automake bison # for swig
|
||||
file
|
||||
openssl-devel
|
||||
gmp-devel
|
||||
gperf
|
||||
diffutils
|
||||
libipt libipt-devel # intel
|
||||
patch
|
||||
perl # for openssl
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz # used for archive unpacking
|
||||
zlib # zlib library used for all builds
|
||||
expat xz-libs python3 # for gdb
|
||||
readline # for cmake and llvm
|
||||
libffi libxml2 # for llvm
|
||||
openssl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make pkgconf-pkg-config # build system
|
||||
wget # for downloading libs
|
||||
libuuid-devel java-11-openjdk # required by antlr
|
||||
readline-devel # for memgraph console
|
||||
python3-devel # for query modules
|
||||
openssl-devel
|
||||
libseccomp-devel
|
||||
python3 python3-pip python3-virtualenv python3-virtualenvwrapper python3-pyyaml nmap-ncat # for tests
|
||||
libcurl-devel # mg-requests
|
||||
rpm-build rpmlint # for RPM package building
|
||||
doxygen graphviz # source documentation generators
|
||||
which nodejs golang zip unzip java-11-openjdk-devel # for driver tests
|
||||
sbcl # for custom Lisp C++ preprocessing
|
||||
autoconf # for jemalloc code generation
|
||||
libtool # for protobuf code generation
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
local missing=""
|
||||
# On Fedora yum/dnf and python10 use newer glibc which is not compatible
|
||||
# with ours, so we need to momentarely disable env
|
||||
local OLD_LD_LIBRARY_PATH=${LD_LIBRARY_PATH}
|
||||
LD_LIBRARY_PATH=""
|
||||
for pkg in $1; do
|
||||
if ! dnf list installed "$pkg" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
done
|
||||
if [ "$missing" != "" ]; then
|
||||
echo "MISSING PACKAGES: $missing"
|
||||
exit 1
|
||||
fi
|
||||
LD_LIBRARY_PATH=${OLD_LD_LIBRARY_PATH}
|
||||
}
|
||||
|
||||
install() {
|
||||
cd "$DIR"
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Please run as root."
|
||||
exit 1
|
||||
fi
|
||||
# If GitHub Actions runner is installed, append LANG to the environment.
|
||||
# Python related tests don't work without the LANG export.
|
||||
if [ -d "/home/gh/actions-runner" ]; then
|
||||
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
|
||||
else
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
fi
|
||||
dnf update -y
|
||||
for pkg in $1; do
|
||||
dnf install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
96
environment/os/ubuntu-22.04-arm.sh
Executable file
96
environment/os/ubuntu-22.04-arm.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "ubuntu-22.04"
|
||||
check_architecture "arm64" "aarch64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg # used for archive signature verification
|
||||
tar gzip bzip2 xz-utils unzip # used for archive unpacking
|
||||
zlib1g-dev # zlib library used for all builds
|
||||
libexpat1-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
|
||||
libcurl4-openssl-dev # for cmake
|
||||
libreadline-dev # for cmake and llvm
|
||||
libffi-dev libxml2-dev # for llvm
|
||||
curl # snappy
|
||||
file
|
||||
git # for thrift
|
||||
libgmp-dev # for gdb
|
||||
gperf # for proxygen
|
||||
libssl-dev
|
||||
libedit-dev libpcre3-dev automake bison # for swig
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz-utils # used for archive unpacking
|
||||
zlib1g # zlib library used for all builds
|
||||
libexpat1 libbabeltrace1 liblzma5 python3 # for gdb
|
||||
libcurl4 # for cmake
|
||||
libreadline8 # for cmake and llvm
|
||||
libffi7 libxml2 # for llvm
|
||||
libssl-dev # for libevent
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make pkg-config # build system
|
||||
curl wget # for downloading libs
|
||||
uuid-dev default-jre-headless # required by antlr
|
||||
libreadline-dev # for memgraph console
|
||||
libpython3-dev python3-dev # for query modules
|
||||
libssl-dev
|
||||
libseccomp-dev
|
||||
netcat # tests are using nc to wait for memgraph
|
||||
python3 python3-virtualenv python3-pip # for qa, macro_benchmark and stress tests
|
||||
python3-yaml # for the configuration generator
|
||||
libcurl4-openssl-dev # mg-requests
|
||||
sbcl # for custom Lisp C++ preprocessing
|
||||
doxygen graphviz # source documentation generators
|
||||
mono-runtime mono-mcs zip unzip default-jdk-headless # for driver tests
|
||||
dotnet-sdk-6.0 golang nodejs npm
|
||||
autoconf # for jemalloc code generation
|
||||
libtool # for protobuf code generation
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
check_all_dpkg "$1"
|
||||
}
|
||||
|
||||
install() {
|
||||
cd "$DIR"
|
||||
apt update
|
||||
# If GitHub Actions runner is installed, append LANG to the environment.
|
||||
# Python related tests doesn't work the LANG export.
|
||||
if [ -d "/home/gh/actions-runner" ]; then
|
||||
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
|
||||
else
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
fi
|
||||
apt install -y wget
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == dotnet-sdk-6.0 ]; then
|
||||
if ! dpkg -s dotnet-sdk-6.0 2>/dev/null >/dev/null; then
|
||||
wget -nv https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
|
||||
dpkg -i packages-microsoft-prod.deb
|
||||
apt-get update
|
||||
apt-get install -y apt-transport-https dotnet-sdk-6.0
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
apt install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
1
environment/toolchain/.gitignore
vendored
Normal file
1
environment/toolchain/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.tar.gz
|
||||
@@ -10,6 +10,14 @@ cd "$DIR"
|
||||
source "$DIR/../util.sh"
|
||||
DISTRO="$(operating_system)"
|
||||
|
||||
function log_tool_name () {
|
||||
echo ""
|
||||
echo ""
|
||||
echo "#### $1 ####"
|
||||
echo ""
|
||||
echo ""
|
||||
}
|
||||
|
||||
for_arm=false
|
||||
if [[ "$#" -eq 1 ]]; then
|
||||
if [[ "$1" == "--for-arm" ]]; then
|
||||
@@ -20,9 +28,11 @@ if [[ "$#" -eq 1 ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
os="$1"
|
||||
|
||||
# toolchain version
|
||||
TOOLCHAIN_STDCXX="${TOOLCHAIN_STDCXX:-libstdc++}"
|
||||
if [[ "$TOOLCHAIN_STDCXX" != "libstdc++" && "$TOOLCHAIN_STDCXX" != "libc++" ]]; then
|
||||
echo "Only GCC (libstdc++) or LLVM (libc++) C++ standard library implementations are supported."
|
||||
exit 1
|
||||
fi
|
||||
TOOLCHAIN_VERSION=4
|
||||
|
||||
# package versions used
|
||||
@@ -41,11 +51,19 @@ CPPCHECK_VERSION=2.6
|
||||
LLVM_VERSION=13.0.0
|
||||
SWIG_VERSION=4.0.2 # used only for LLVM compilation
|
||||
|
||||
# Check for the dependencies.
|
||||
echo "ALL BUILD PACKAGES: $($DIR/../os/$DISTRO.sh list TOOLCHAIN_BUILD_DEPS)"
|
||||
$DIR/../os/$DISTRO.sh check TOOLCHAIN_BUILD_DEPS
|
||||
echo "ALL RUN PACKAGES: $($DIR/../os/$DISTRO.sh list TOOLCHAIN_RUN_DEPS)"
|
||||
$DIR/../os/$DISTRO.sh check TOOLCHAIN_RUN_DEPS
|
||||
# Set the right env script
|
||||
ENV_SCRIPT="$DIR/../os/$DISTRO.sh"
|
||||
if [[ "$for_arm" = true ]]; then
|
||||
ENV_SCRIPT="$DIR/../os/$DISTRO-arm.sh"
|
||||
fi
|
||||
|
||||
# Check for the toolchain build dependencies.
|
||||
echo "ALL BUILD PACKAGES: $(${ENV_SCRIPT} list TOOLCHAIN_BUILD_DEPS)"
|
||||
${ENV_SCRIPT} check TOOLCHAIN_BUILD_DEPS
|
||||
|
||||
# Check for the toolchain run dependencies.
|
||||
echo "ALL RUN PACKAGES: $(${ENV_SCRIPT} list TOOLCHAIN_RUN_DEPS)"
|
||||
${ENV_SCRIPT} check TOOLCHAIN_RUN_DEPS
|
||||
|
||||
# check installation directory
|
||||
NAME=toolchain-v$TOOLCHAIN_VERSION
|
||||
@@ -99,6 +117,8 @@ if [ ! -f llvm-$LLVM_VERSION.src.tar.xz ]; then
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/clang-tools-extra-$LLVM_VERSION.src.tar.xz
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/compiler-rt-$LLVM_VERSION.src.tar.xz
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/libunwind-$LLVM_VERSION.src.tar.xz
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/libcxx-$LLVM_VERSION.src.tar.xz
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/libcxxabi-$LLVM_VERSION.src.tar.xz
|
||||
fi
|
||||
if [ ! -f pahole-gdb-master.zip ]; then
|
||||
wget https://github.com/PhilArmstrong/pahole-gdb/archive/master.zip -O pahole-gdb-master.zip
|
||||
@@ -156,6 +176,8 @@ if [ ! -f llvm-$LLVM_VERSION.src.tar.xz.sig ]; then
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/clang-tools-extra-$LLVM_VERSION.src.tar.xz.sig
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/compiler-rt-$LLVM_VERSION.src.tar.xz.sig
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/libunwind-$LLVM_VERSION.src.tar.xz.sig
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/libcxx-$LLVM_VERSION.src.tar.xz.sig
|
||||
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/libcxxabi-$LLVM_VERSION.src.tar.xz.sig
|
||||
fi
|
||||
# list of valid llvm gnupg keys: https://releases.llvm.org/download.html
|
||||
$GPG --keyserver $KEYSERVER --recv-keys 0x474E22316ABF4785A88C6E8EA2C794A986419D8A
|
||||
@@ -165,6 +187,8 @@ $GPG --verify lld-$LLVM_VERSION.src.tar.xz.sig lld-$LLVM_VERSION.src.tar.xz
|
||||
$GPG --verify clang-tools-extra-$LLVM_VERSION.src.tar.xz.sig clang-tools-extra-$LLVM_VERSION.src.tar.xz
|
||||
$GPG --verify compiler-rt-$LLVM_VERSION.src.tar.xz.sig compiler-rt-$LLVM_VERSION.src.tar.xz
|
||||
$GPG --verify libunwind-$LLVM_VERSION.src.tar.xz.sig libunwind-$LLVM_VERSION.src.tar.xz
|
||||
$GPG --verify libcxx-$LLVM_VERSION.src.tar.xz.sig libcxx-$LLVM_VERSION.src.tar.xz
|
||||
$GPG --verify libcxxabi-$LLVM_VERSION.src.tar.xz.sig libcxxabi-$LLVM_VERSION.src.tar.xz
|
||||
|
||||
popd
|
||||
|
||||
@@ -172,7 +196,7 @@ popd
|
||||
mkdir -p build
|
||||
pushd build
|
||||
|
||||
# compile gcc
|
||||
log_tool_name "GCC $GCC_VERSION"
|
||||
if [ ! -f $PREFIX/bin/gcc ]; then
|
||||
if [ -d gcc-$GCC_VERSION ]; then
|
||||
rm -rf gcc-$GCC_VERSION
|
||||
@@ -263,7 +287,7 @@ fi
|
||||
export PATH=$PREFIX/bin:$PATH
|
||||
export LD_LIBRARY_PATH=$PREFIX/lib64
|
||||
|
||||
# compile binutils
|
||||
log_tool_name "binutils $BINUTILS_VERSION"
|
||||
if [ ! -f $PREFIX/bin/ld.gold ]; then
|
||||
if [ -d binutils-$BINUTILS_VERSION ]; then
|
||||
rm -rf binutils-$BINUTILS_VERSION
|
||||
@@ -327,7 +351,7 @@ if [ ! -f $PREFIX/bin/ld.gold ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# compile gdb
|
||||
log_tool_name "GDB $GDB_VERSION"
|
||||
if [ ! -f $PREFIX/bin/gdb ]; then
|
||||
if [ -d gdb-$GDB_VERSION ]; then
|
||||
rm -rf gdb-$GDB_VERSION
|
||||
@@ -363,6 +387,34 @@ if [ ! -f $PREFIX/bin/gdb ]; then
|
||||
--without-babeltrace \
|
||||
--enable-tui \
|
||||
--with-python=python3
|
||||
elif [[ "${DISTRO}" == fedora* ]]; then
|
||||
# Remove readline, gdb does not compile
|
||||
env \
|
||||
CC=gcc \
|
||||
CXX=g++ \
|
||||
CFLAGS="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security" \
|
||||
CXXFLAGS="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security" \
|
||||
CPPFLAGS="-Wdate-time -D_FORTIFY_SOURCE=2 -fPIC" \
|
||||
LDFLAGS="-Wl,-z,relro" \
|
||||
PYTHON="" \
|
||||
../configure \
|
||||
--build=x86_64-linux-gnu \
|
||||
--host=x86_64-linux-gnu \
|
||||
--prefix=$PREFIX \
|
||||
--disable-maintainer-mode \
|
||||
--disable-dependency-tracking \
|
||||
--disable-silent-rules \
|
||||
--disable-gdbtk \
|
||||
--disable-shared \
|
||||
--without-guile \
|
||||
--with-system-gdbinit=$PREFIX/etc/gdb/gdbinit \
|
||||
--with-expat \
|
||||
--with-system-zlib \
|
||||
--with-lzma \
|
||||
--with-babeltrace \
|
||||
--with-intel-pt \
|
||||
--enable-tui \
|
||||
--with-python=python3
|
||||
else
|
||||
# https://buildd.debian.org/status/fetch.php?pkg=gdb&arch=amd64&ver=8.2.1-2&stamp=1550831554&raw=0
|
||||
env \
|
||||
@@ -398,13 +450,13 @@ if [ ! -f $PREFIX/bin/gdb ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install pahole
|
||||
log_tool_name "install pahole"
|
||||
if [ ! -d $PREFIX/share/pahole-gdb ]; then
|
||||
unzip ../archives/pahole-gdb-master.zip
|
||||
mv pahole-gdb-master $PREFIX/share/pahole-gdb
|
||||
fi
|
||||
|
||||
# setup system gdbinit
|
||||
log_tool_name "setup system gdbinit"
|
||||
if [ ! -f $PREFIX/etc/gdb/gdbinit ]; then
|
||||
mkdir -p $PREFIX/etc/gdb
|
||||
cat >$PREFIX/etc/gdb/gdbinit <<EOF
|
||||
@@ -430,7 +482,7 @@ end
|
||||
EOF
|
||||
fi
|
||||
|
||||
# compile cmake
|
||||
log_tool_name "cmake $CMAKE_VERSION"
|
||||
if [ ! -f $PREFIX/bin/cmake ]; then
|
||||
if [ -d cmake-$CMAKE_VERSION ]; then
|
||||
rm -rf cmake-$CMAKE_VERSION
|
||||
@@ -456,7 +508,7 @@ if [ ! -f $PREFIX/bin/cmake ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# compile cppcheck
|
||||
log_tool_name "cppcheck $CPPCHECK_VERSION"
|
||||
if [ ! -f $PREFIX/bin/cppcheck ]; then
|
||||
if [ -d cppcheck-$CPPCHECK_VERSION ]; then
|
||||
rm -rf cppcheck-$CPPCHECK_VERSION
|
||||
@@ -480,7 +532,7 @@ if [ ! -f $PREFIX/bin/cppcheck ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# compile swig
|
||||
log_tool_name "swig $SWIG_VERSION"
|
||||
if [ ! -d swig-$SWIG_VERSION/install ]; then
|
||||
if [ -d swig-$SWIG_VERSION ]; then
|
||||
rm -rf swig-$SWIG_VERSION
|
||||
@@ -496,7 +548,7 @@ if [ ! -d swig-$SWIG_VERSION/install ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# compile llvm
|
||||
log_tool_name "LLVM $LLVM_VERSION"
|
||||
if [ ! -f $PREFIX/bin/clang ]; then
|
||||
if [ -d llvm-$LLVM_VERSION ]; then
|
||||
rm -rf llvm-$LLVM_VERSION
|
||||
@@ -513,8 +565,19 @@ if [ ! -f $PREFIX/bin/clang ]; then
|
||||
mv compiler-rt-$LLVM_VERSION.src/ llvm-$LLVM_VERSION/projects/compiler-rt
|
||||
tar -xvf ../archives/libunwind-$LLVM_VERSION.src.tar.xz
|
||||
mv libunwind-$LLVM_VERSION.src/include/mach-o llvm-$LLVM_VERSION/tools/lld/include
|
||||
|
||||
# The following is required because of libc++
|
||||
tar -xvf ../archives/libcxx-$LLVM_VERSION.src.tar.xz
|
||||
mv libcxx-$LLVM_VERSION.src llvm-$LLVM_VERSION/projects/libcxx
|
||||
tar -xvf ../archives/libcxxabi-$LLVM_VERSION.src.tar.xz
|
||||
mv libcxxabi-$LLVM_VERSION.src llvm-$LLVM_VERSION/projects/libcxxabi
|
||||
# NOTE: We moved part of the libunwind in one of the previous step.
|
||||
rm -r libunwind-$LLVM_VERSION.src
|
||||
tar -xvf ../archives/libunwind-$LLVM_VERSION.src.tar.xz
|
||||
mv libunwind-$LLVM_VERSION.src llvm-$LLVM_VERSION/projects/libunwind
|
||||
|
||||
pushd llvm-$LLVM_VERSION
|
||||
mkdir build && pushd build
|
||||
mkdir -p build && pushd build
|
||||
# activate swig
|
||||
export PATH=$DIR/build/swig-$SWIG_VERSION/install/bin:$PATH
|
||||
# influenced by: https://buildd.debian.org/status/fetch.php?pkg=llvm-toolchain-7&arch=amd64&ver=1%3A7.0.1%7E%2Brc2-1%7Eexp1&stamp=1541506173&raw=0
|
||||
@@ -567,7 +630,7 @@ In order to be able to run all of these tools you should install the following
|
||||
packages:
|
||||
|
||||
\`\`\`
|
||||
$($DIR/../os/$DISTRO.sh list TOOLCHAIN_RUN_DEPS)
|
||||
$($DIR/../os/$ENV_SCRIPT.sh list TOOLCHAIN_RUN_DEPS)
|
||||
\`\`\`
|
||||
|
||||
## Usage
|
||||
@@ -624,6 +687,7 @@ export PS1="($NAME) \$PS1"
|
||||
export LD_LIBRARY_PATH=$PREFIX/lib:$PREFIX/lib64
|
||||
export CXXFLAGS=-isystem\ $PREFIX/include\ \$CXXFLAGS
|
||||
export CFLAGS=-isystem\ $PREFIX/include\ \$CFLAGS
|
||||
export VENV=$PREFIX
|
||||
|
||||
# disable root
|
||||
function su () {
|
||||
@@ -675,7 +739,7 @@ PROXYGEN_SHA256=5360a8ccdfb2f5a6c7b3eed331ec7ab0e2c792d579c6fff499c85c516c11fe14
|
||||
SNAPPY_SHA256=75c1fbb3d618dd3a0483bff0e26d0a92b495bbe5059c8b4f1c962b478b6e06e7
|
||||
SNAPPY_VERSION=1.1.9
|
||||
XZ_VERSION=5.2.5 # for LZMA
|
||||
ZLIB_VERSION=1.2.12
|
||||
ZLIB_VERSION=1.2.13
|
||||
ZSTD_VERSION=1.5.0
|
||||
WANGLE_SHA256=1002e9c32b6f4837f6a760016e3b3e22f3509880ef3eaad191c80dc92655f23f
|
||||
|
||||
@@ -820,7 +884,11 @@ source $PREFIX/activate
|
||||
export CC=$PREFIX/bin/clang
|
||||
export CXX=$PREFIX/bin/clang++
|
||||
export CFLAGS="$CFLAGS -fPIC"
|
||||
export CXXFLAGS="$CXXFLAGS -fPIC"
|
||||
if [ "$TOOLCHAIN_STDCXX" = "libstdc++" ]; then
|
||||
export CXXFLAGS="$CXXFLAGS -fPIC"
|
||||
else
|
||||
export CXXFLAGS="$CXXFLAGS -fPIC -stdlib=libc++"
|
||||
fi
|
||||
COMMON_CMAKE_FLAGS="-DCMAKE_INSTALL_PREFIX=$PREFIX
|
||||
-DCMAKE_PREFIX_PATH=$PREFIX
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
@@ -834,7 +902,7 @@ COMMON_CMAKE_FLAGS="-DCMAKE_INSTALL_PREFIX=$PREFIX
|
||||
COMMON_CONFIGURE_FLAGS="--enable-shared=no --prefix=$PREFIX"
|
||||
COMMON_MAKE_INSTALL_FLAGS="-j$CPUS BUILD_SHARED=no PREFIX=$PREFIX install"
|
||||
|
||||
# install bzip2
|
||||
log_tool_name "bzip2 $BZIP2_VERSION"
|
||||
if [ ! -f $PREFIX/include/bzlib.h ]; then
|
||||
if [ -d bzip2-$BZIP2_VERSION ]; then
|
||||
rm -rf bzip2-$BZIP2_VERSION
|
||||
@@ -845,7 +913,7 @@ if [ ! -f $PREFIX/include/bzlib.h ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# install fmt
|
||||
log_tool_name "fmt $FMT_VERSION"
|
||||
if [ ! -d $PREFIX/include/fmt ]; then
|
||||
if [ -d fmt-$FMT_VERSION ]; then
|
||||
rm -rf fmt-$FMT_VERSION
|
||||
@@ -858,7 +926,7 @@ if [ ! -d $PREFIX/include/fmt ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install lz4
|
||||
log_tool_name "lz4 $LZ4_VERSION"
|
||||
if [ ! -f $PREFIX/include/lz4.h ]; then
|
||||
if [ -d lz4-$LZ4_VERSION ]; then
|
||||
rm -rf lz4-$LZ4_VERSION
|
||||
@@ -869,7 +937,7 @@ if [ ! -f $PREFIX/include/lz4.h ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# install xz
|
||||
log_tool_name "xz $XZ_VERSION"
|
||||
if [ ! -f $PREFIX/include/lzma.h ]; then
|
||||
if [ -d xz-$XZ_VERSION ]; then
|
||||
rm -rf xz-$XZ_VERSION
|
||||
@@ -881,7 +949,7 @@ if [ ! -f $PREFIX/include/lzma.h ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# install zlib
|
||||
log_tool_name "zlib $ZLIB_VERSION"
|
||||
if [ ! -f $PREFIX/include/zlib.h ]; then
|
||||
if [ -d zlib-$ZLIB_VERSION ]; then
|
||||
rm -rf zlib-$ZLIB_VERSION
|
||||
@@ -895,7 +963,7 @@ if [ ! -f $PREFIX/include/zlib.h ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install zstd
|
||||
log_tool_name "zstd $ZSTD_VERSION"
|
||||
if [ ! -f $PREFIX/include/zstd.h ]; then
|
||||
if [ -d zstd-$ZSTD_VERSION ]; then
|
||||
rm -rf zstd-$ZSTD_VERSION
|
||||
@@ -910,7 +978,8 @@ if [ ! -f $PREFIX/include/zstd.h ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
#install jemalloc
|
||||
# TODO(gitbuda): Freeze jmalloc version.
|
||||
log_tool_name "jmalloc"
|
||||
if [ ! -d $PREFIX/include/jemalloc ]; then
|
||||
if [ -d jemalloc ]; then
|
||||
rm -rf jemalloc
|
||||
@@ -927,7 +996,7 @@ if [ ! -d $PREFIX/include/jemalloc ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# install boost
|
||||
log_tool_name "BOOST $BOOST_VERSION"
|
||||
if [ ! -d $PREFIX/include/boost ]; then
|
||||
if [ -d boost_$BOOST_VERSION_UNDERSCORES ]; then
|
||||
rm -rf boost_$BOOST_VERSION_UNDERSCORES
|
||||
@@ -935,15 +1004,24 @@ if [ ! -d $PREFIX/include/boost ]; then
|
||||
tar -xzf ../archives/boost_$BOOST_VERSION_UNDERSCORES.tar.gz
|
||||
pushd boost_$BOOST_VERSION_UNDERSCORES
|
||||
./bootstrap.sh --prefix=$PREFIX --with-toolset=clang --with-python=python3 --without-icu
|
||||
./b2 toolset=clang -j$CPUS install variant=release link=static cxxstd=20 --disable-icu \
|
||||
-sZLIB_SOURCE="$PREFIX" -sZLIB_INCLUDE="$PREFIX/include" -sZLIB_LIBPATH="$PREFIX/lib" \
|
||||
-sBZIP2_SOURCE="$PREFIX" -sBZIP2_INCLUDE="$PREFIX/include" -sBZIP2_LIBPATH="$PREFIX/lib" \
|
||||
-sLZMA_SOURCE="$PREFIX" -sLZMA_INCLUDE="$PREFIX/include" -sLZMA_LIBPATH="$PREFIX/lib" \
|
||||
-sZSTD_SOURCE="$PREFIX" -sZSTD_INCLUDE="$PREFIX/include" -sZSTD_LIBPATH="$PREFIX/lib"
|
||||
if [ "$TOOLCHAIN_STDCXX" = "libstdc++" ]; then
|
||||
./b2 toolset=clang -j$CPUS install variant=release link=static cxxstd=20 --disable-icu \
|
||||
-sZLIB_SOURCE="$PREFIX" -sZLIB_INCLUDE="$PREFIX/include" -sZLIB_LIBPATH="$PREFIX/lib" \
|
||||
-sBZIP2_SOURCE="$PREFIX" -sBZIP2_INCLUDE="$PREFIX/include" -sBZIP2_LIBPATH="$PREFIX/lib" \
|
||||
-sLZMA_SOURCE="$PREFIX" -sLZMA_INCLUDE="$PREFIX/include" -sLZMA_LIBPATH="$PREFIX/lib" \
|
||||
-sZSTD_SOURCE="$PREFIX" -sZSTD_INCLUDE="$PREFIX/include" -sZSTD_LIBPATH="$PREFIX/lib"
|
||||
else
|
||||
./b2 toolset=clang -j$CPUS install variant=release link=static cxxstd=20 --disable-icu \
|
||||
cxxflags="-stdlib=libc++" linkflags="-stdlib=libc++" \
|
||||
-sZLIB_SOURCE="$PREFIX" -sZLIB_INCLUDE="$PREFIX/include" -sZLIB_LIBPATH="$PREFIX/lib" \
|
||||
-sBZIP2_SOURCE="$PREFIX" -sBZIP2_INCLUDE="$PREFIX/include" -sBZIP2_LIBPATH="$PREFIX/lib" \
|
||||
-sLZMA_SOURCE="$PREFIX" -sLZMA_INCLUDE="$PREFIX/include" -sLZMA_LIBPATH="$PREFIX/lib" \
|
||||
-sZSTD_SOURCE="$PREFIX" -sZSTD_INCLUDE="$PREFIX/include" -sZSTD_LIBPATH="$PREFIX/lib"
|
||||
fi
|
||||
popd
|
||||
fi
|
||||
|
||||
# install double-conversion
|
||||
log_tool_name "double-conversion $DOUBLE_CONVERSION_VERSION"
|
||||
if [ ! -d $PREFIX/include/double-conversion ]; then
|
||||
if [ -d double-conversion-$DOUBLE_CONVERSION_VERSION ]; then
|
||||
rm -rf double-conversion-$DOUBLE_CONVERSION_VERSION
|
||||
@@ -958,7 +1036,8 @@ if [ ! -d $PREFIX/include/double-conversion ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install gflags
|
||||
# TODO(gitbuda): Freeze gflags version.
|
||||
log_tool_name "gflags"
|
||||
if [ ! -d $PREFIX/include/gflags ]; then
|
||||
if [ -d gflags ]; then
|
||||
rm -rf gflags
|
||||
@@ -977,7 +1056,7 @@ if [ ! -d $PREFIX/include/gflags ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install libunwind
|
||||
log_tool_name "libunwind $LIBUNWIND_VERSION"
|
||||
if [ ! -f $PREFIX/include/libunwind.h ]; then
|
||||
if [ -d libunwind-$LIBUNWIND_VERSION ]; then
|
||||
rm -rf libunwind-$LIBUNWIND_VERSION
|
||||
@@ -990,7 +1069,7 @@ if [ ! -f $PREFIX/include/libunwind.h ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# install glog
|
||||
log_tool_name "glog $GLOG_VERSION"
|
||||
if [ ! -d $PREFIX/include/glog ]; then
|
||||
if [ -d glog-$GLOG_VERSION ]; then
|
||||
rm -rf glog-$GLOG_VERSION
|
||||
@@ -1004,7 +1083,7 @@ if [ ! -d $PREFIX/include/glog ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install libevent
|
||||
log_tool_name "libevent $LIBEVENT_VERSION"
|
||||
if [ ! -d $PREFIX/include/event2 ]; then
|
||||
if [ -d libevent-$LIBEVENT_VERSION ]; then
|
||||
rm -rf libevent-$LIBEVENT_VERSION
|
||||
@@ -1023,7 +1102,7 @@ if [ ! -d $PREFIX/include/event2 ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install snappy
|
||||
log_tool_name "snappy $SNAPPY_VERSION"
|
||||
if [ ! -f $PREFIX/include/snappy.h ]; then
|
||||
if [ -d snappy-$SNAPPY_VERSION ]; then
|
||||
rm -rf snappy-$SNAPPY_VERSION
|
||||
@@ -1041,7 +1120,7 @@ if [ ! -f $PREFIX/include/snappy.h ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install libsodium
|
||||
log_tool_name "libsodium $LIBSODIUM_VERSION"
|
||||
if [ ! -f $PREFIX/include/sodium.h ]; then
|
||||
if [ -d libsodium-$LIBSODIUM_VERSION ]; then
|
||||
rm -rf libsodium-$LIBSODIUM_VERSION
|
||||
@@ -1053,7 +1132,7 @@ if [ ! -f $PREFIX/include/sodium.h ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# install libaio
|
||||
log_tool_name "libaio $LIBAIO_VERSION"
|
||||
if [ ! -f $PREFIX/include/libaio.h ]; then
|
||||
if [ -d libaio-$LIBAIO_VERSION ]; then
|
||||
rm -rf libaio-$LIBAIO_VERSION
|
||||
@@ -1064,7 +1143,7 @@ if [ ! -f $PREFIX/include/libaio.h ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# install folly
|
||||
log_tool_name "folly $FBLIBS_VERSION"
|
||||
if [ ! -d $PREFIX/include/folly ]; then
|
||||
if [ -d folly-$FBLIBS_VERSION ]; then
|
||||
rm -rf folly-$FBLIBS_VERSION
|
||||
@@ -1085,7 +1164,7 @@ if [ ! -d $PREFIX/include/folly ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install fizz
|
||||
log_tool_name "fizz $FBLIBS_VERSION"
|
||||
if [ ! -d $PREFIX/include/fizz ]; then
|
||||
if [ -d fizz-$FBLIBS_VERSION ]; then
|
||||
rm -rf fizz-$FBLIBS_VERSION
|
||||
@@ -1104,7 +1183,7 @@ if [ ! -d $PREFIX/include/fizz ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install wangle
|
||||
log_tool_name "wangle FBLIBS_VERSION"
|
||||
if [ ! -d $PREFIX/include/wangle ]; then
|
||||
if [ -d wangle-$FBLIBS_VERSION ]; then
|
||||
rm -rf wangle-$FBLIBS_VERSION
|
||||
@@ -1123,7 +1202,7 @@ if [ ! -d $PREFIX/include/wangle ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install proxygen
|
||||
log_tool_name "proxygen $FBLIBS_VERSION"
|
||||
if [ ! -d $PREFIX/include/proxygen ]; then
|
||||
if [ -d proxygen-$FBLIBS_VERSION ]; then
|
||||
rm -rf proxygen-$FBLIBS_VERSION
|
||||
@@ -1144,7 +1223,7 @@ if [ ! -d $PREFIX/include/proxygen ]; then
|
||||
popd && popd
|
||||
fi
|
||||
|
||||
# install flex
|
||||
log_tool_name "flex $FBLIBS_VERSION"
|
||||
if [ ! -f $PREFIX/include/FlexLexer.h ]; then
|
||||
if [ -d flex-$FLEX_VERSION ]; then
|
||||
rm -rf flex-$FLEX_VERSION
|
||||
@@ -1156,7 +1235,7 @@ if [ ! -f $PREFIX/include/FlexLexer.h ]; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# install fbthrift
|
||||
log_tool_name "fbthrift $FBLIBS_VERSION"
|
||||
if [ ! -d $PREFIX/include/thrift ]; then
|
||||
if [ -d fbthrift-$FBLIBS_VERSION ]; then
|
||||
rm -rf fbthrift-$FBLIBS_VERSION
|
||||
@@ -1166,10 +1245,15 @@ if [ ! -d $PREFIX/include/thrift ]; then
|
||||
# build is used by facebook builder
|
||||
mkdir _build
|
||||
pushd _build
|
||||
if [ "$TOOLCHAIN_STDCXX" = "libstdc++" ]; then
|
||||
CMAKE_CXX_FLAGS="-fsized-deallocation"
|
||||
else
|
||||
CMAKE_CXX_FLAGS="-fsized-deallocation -stdlib=libc++"
|
||||
fi
|
||||
cmake .. $COMMON_CMAKE_FLAGS \
|
||||
-Denable_tests=OFF \
|
||||
-DGFLAGS_NOTHREADS=OFF \
|
||||
-DCMAKE_CXX_FLAGS=-fsized-deallocation
|
||||
-DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS"
|
||||
make -j$CPUS install
|
||||
popd
|
||||
fi
|
||||
@@ -1179,7 +1263,7 @@ popd
|
||||
# create toolchain archive
|
||||
if [ ! -f $NAME-binaries-$DISTRO.tar.gz ]; then
|
||||
DISTRO_FULL_NAME=${DISTRO}
|
||||
if [[ "${DISTRO}" == centos* ]]; then
|
||||
if [[ "${DISTRO}" == centos* ]] || [[ "${DISTRO}" == fedora* ]]; then
|
||||
if [[ "$for_arm" = "true" ]]; then
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-aarch64"
|
||||
else
|
||||
@@ -1192,7 +1276,12 @@ if [ ! -f $NAME-binaries-$DISTRO.tar.gz ]; then
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-amd64"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$TOOLCHAIN_STDCXX" = "libstdc++" ]; then
|
||||
# Pass because infra scripts assume there is not C++ standard lib in the name.
|
||||
echo "NOTE: Not adding anything to the archive name that GCC C++ standard lib is used."
|
||||
else
|
||||
DISTRO_FULL_NAME="$DISTRO_FULL_NAME-libc++"
|
||||
fi
|
||||
tar --owner=root --group=root -cpvzf $NAME-binaries-$DISTRO_FULL_NAME.tar.gz -C /opt $NAME
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
operating_system() {
|
||||
function operating_system() {
|
||||
grep -E '^(VERSION_)?ID=' /etc/os-release | \
|
||||
sort | cut -d '=' -f 2- | sed 's/"//g' | paste -s -d '-'
|
||||
}
|
||||
|
||||
check_operating_system() {
|
||||
function check_operating_system() {
|
||||
if [ "$(operating_system)" != "$1" ]; then
|
||||
echo "Not the right operating system!"
|
||||
exit 1
|
||||
@@ -14,20 +14,25 @@ check_operating_system() {
|
||||
fi
|
||||
}
|
||||
|
||||
architecture() {
|
||||
function architecture() {
|
||||
uname -m
|
||||
}
|
||||
|
||||
check_architecture() {
|
||||
if [ "$(architecture)" != "$1" ]; then
|
||||
echo "Not the right architecture!"
|
||||
exit 1
|
||||
else
|
||||
echo "The right architecture."
|
||||
fi
|
||||
local ARCH=$(architecture)
|
||||
for arch in "$@"; do
|
||||
if [ "${ARCH}" = "$arch" ]; then
|
||||
echo "The right architecture!"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
echo "Not the right architecture!"
|
||||
echo "Expected: $@"
|
||||
echo "Actual: ${ARCH}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
check_all_yum() {
|
||||
function check_all_yum() {
|
||||
local missing=""
|
||||
for pkg in $1; do
|
||||
if ! yum list installed "$pkg" >/dev/null 2>/dev/null; then
|
||||
@@ -40,7 +45,7 @@ check_all_yum() {
|
||||
fi
|
||||
}
|
||||
|
||||
check_all_dpkg() {
|
||||
function check_all_dpkg() {
|
||||
local missing=""
|
||||
for pkg in $1; do
|
||||
if ! dpkg -s "$pkg" >/dev/null 2>/dev/null; then
|
||||
@@ -53,7 +58,7 @@ check_all_dpkg() {
|
||||
fi
|
||||
}
|
||||
|
||||
check_all_dnf() {
|
||||
function check_all_dnf() {
|
||||
local missing=""
|
||||
for pkg in $1; do
|
||||
if ! dnf list installed "$pkg" >/dev/null 2>/dev/null; then
|
||||
@@ -65,7 +70,8 @@ check_all_dnf() {
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
install_all_apt() {
|
||||
|
||||
function install_all_apt() {
|
||||
for pkg in $1; do
|
||||
apt install -y "$pkg"
|
||||
done
|
||||
|
||||
449
include/_mgp.hpp
449
include/_mgp.hpp
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -22,7 +22,7 @@
|
||||
namespace mgp {
|
||||
|
||||
namespace {
|
||||
void MgExceptionHandle(mgp_error result_code) {
|
||||
inline void MgExceptionHandle(mgp_error result_code) {
|
||||
switch (result_code) {
|
||||
case mgp_error::MGP_ERROR_UNKNOWN_ERROR:
|
||||
throw mg_exception::UnknownException();
|
||||
@@ -62,7 +62,7 @@ TResult MgInvoke(TFunc func, TArgs... args) {
|
||||
}
|
||||
|
||||
template <typename TFunc, typename... TArgs>
|
||||
void MgInvokeVoid(TFunc func, TArgs... args) {
|
||||
inline void MgInvokeVoid(TFunc func, TArgs... args) {
|
||||
auto result_code = func(args...);
|
||||
MgExceptionHandle(result_code);
|
||||
}
|
||||
@@ -72,211 +72,221 @@ void MgInvokeVoid(TFunc func, TArgs... args) {
|
||||
|
||||
// Make value
|
||||
|
||||
mgp_value *value_make_null(mgp_memory *memory) { return MgInvoke<mgp_value *>(mgp_value_make_null, memory); }
|
||||
inline mgp_value *value_make_null(mgp_memory *memory) { return MgInvoke<mgp_value *>(mgp_value_make_null, memory); }
|
||||
|
||||
mgp_value *value_make_bool(int val, mgp_memory *memory) {
|
||||
inline mgp_value *value_make_bool(int val, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_value *>(mgp_value_make_bool, val, memory);
|
||||
}
|
||||
|
||||
mgp_value *value_make_int(int64_t val, mgp_memory *memory) {
|
||||
inline mgp_value *value_make_int(int64_t val, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_value *>(mgp_value_make_int, val, memory);
|
||||
}
|
||||
|
||||
mgp_value *value_make_double(double val, mgp_memory *memory) {
|
||||
inline mgp_value *value_make_double(double val, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_value *>(mgp_value_make_double, val, memory);
|
||||
}
|
||||
|
||||
mgp_value *value_make_string(const char *val, mgp_memory *memory) {
|
||||
inline mgp_value *value_make_string(const char *val, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_value *>(mgp_value_make_string, val, memory);
|
||||
}
|
||||
|
||||
mgp_value *value_make_list(mgp_list *val) { return MgInvoke<mgp_value *>(mgp_value_make_list, val); }
|
||||
inline mgp_value *value_make_list(mgp_list *val) { return MgInvoke<mgp_value *>(mgp_value_make_list, val); }
|
||||
|
||||
mgp_value *value_make_map(mgp_map *val) { return MgInvoke<mgp_value *>(mgp_value_make_map, val); }
|
||||
inline mgp_value *value_make_map(mgp_map *val) { return MgInvoke<mgp_value *>(mgp_value_make_map, val); }
|
||||
|
||||
mgp_value *value_make_vertex(mgp_vertex *val) { return MgInvoke<mgp_value *>(mgp_value_make_vertex, val); }
|
||||
inline mgp_value *value_make_vertex(mgp_vertex *val) { return MgInvoke<mgp_value *>(mgp_value_make_vertex, val); }
|
||||
|
||||
mgp_value *value_make_edge(mgp_edge *val) { return MgInvoke<mgp_value *>(mgp_value_make_edge, val); }
|
||||
inline mgp_value *value_make_edge(mgp_edge *val) { return MgInvoke<mgp_value *>(mgp_value_make_edge, val); }
|
||||
|
||||
mgp_value *value_make_path(mgp_path *val) { return MgInvoke<mgp_value *>(mgp_value_make_path, val); }
|
||||
inline mgp_value *value_make_path(mgp_path *val) { return MgInvoke<mgp_value *>(mgp_value_make_path, val); }
|
||||
|
||||
mgp_value *value_make_date(mgp_date *val) { return MgInvoke<mgp_value *>(mgp_value_make_date, val); }
|
||||
inline mgp_value *value_make_date(mgp_date *val) { return MgInvoke<mgp_value *>(mgp_value_make_date, val); }
|
||||
|
||||
mgp_value *value_make_local_time(mgp_local_time *val) { return MgInvoke<mgp_value *>(mgp_value_make_local_time, val); }
|
||||
inline mgp_value *value_make_local_time(mgp_local_time *val) {
|
||||
return MgInvoke<mgp_value *>(mgp_value_make_local_time, val);
|
||||
}
|
||||
|
||||
mgp_value *value_make_local_date_time(mgp_local_date_time *val) {
|
||||
inline mgp_value *value_make_local_date_time(mgp_local_date_time *val) {
|
||||
return MgInvoke<mgp_value *>(mgp_value_make_local_date_time, val);
|
||||
}
|
||||
|
||||
mgp_value *value_make_duration(mgp_duration *val) { return MgInvoke<mgp_value *>(mgp_value_make_duration, val); }
|
||||
inline mgp_value *value_make_duration(mgp_duration *val) { return MgInvoke<mgp_value *>(mgp_value_make_duration, val); }
|
||||
|
||||
// Copy value
|
||||
|
||||
// TODO: implement within MGP API
|
||||
// with primitive types ({bool, int, double, string}), create a new identical value
|
||||
// otherwise call mgp_##TYPE_copy and convert tpye
|
||||
mgp_value *value_copy(mgp_value *val, mgp_memory *memory) { return MgInvoke<mgp_value *>(mgp_value_copy, val, memory); }
|
||||
inline mgp_value *value_copy(mgp_value *val, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_value *>(mgp_value_copy, val, memory);
|
||||
}
|
||||
|
||||
// Destroy value
|
||||
|
||||
void value_destroy(mgp_value *val) { mgp_value_destroy(val); }
|
||||
inline void value_destroy(mgp_value *val) { mgp_value_destroy(val); }
|
||||
|
||||
// Get value of type
|
||||
|
||||
mgp_value_type value_get_type(mgp_value *val) { return MgInvoke<mgp_value_type>(mgp_value_get_type, val); }
|
||||
inline mgp_value_type value_get_type(mgp_value *val) { return MgInvoke<mgp_value_type>(mgp_value_get_type, val); }
|
||||
|
||||
bool value_get_bool(mgp_value *val) { return MgInvoke<int>(mgp_value_get_bool, val); }
|
||||
inline bool value_get_bool(mgp_value *val) { return MgInvoke<int>(mgp_value_get_bool, val); }
|
||||
|
||||
int64_t value_get_int(mgp_value *val) { return MgInvoke<int64_t>(mgp_value_get_int, val); }
|
||||
inline int64_t value_get_int(mgp_value *val) { return MgInvoke<int64_t>(mgp_value_get_int, val); }
|
||||
|
||||
double value_get_double(mgp_value *val) { return MgInvoke<double>(mgp_value_get_double, val); }
|
||||
inline double value_get_double(mgp_value *val) { return MgInvoke<double>(mgp_value_get_double, val); }
|
||||
|
||||
const char *value_get_string(mgp_value *val) { return MgInvoke<const char *>(mgp_value_get_string, val); }
|
||||
inline const char *value_get_string(mgp_value *val) { return MgInvoke<const char *>(mgp_value_get_string, val); }
|
||||
|
||||
mgp_list *value_get_list(mgp_value *val) { return MgInvoke<mgp_list *>(mgp_value_get_list, val); }
|
||||
inline mgp_list *value_get_list(mgp_value *val) { return MgInvoke<mgp_list *>(mgp_value_get_list, val); }
|
||||
|
||||
mgp_map *value_get_map(mgp_value *val) { return MgInvoke<mgp_map *>(mgp_value_get_map, val); }
|
||||
inline mgp_map *value_get_map(mgp_value *val) { return MgInvoke<mgp_map *>(mgp_value_get_map, val); }
|
||||
|
||||
mgp_vertex *value_get_vertex(mgp_value *val) { return MgInvoke<mgp_vertex *>(mgp_value_get_vertex, val); }
|
||||
inline mgp_vertex *value_get_vertex(mgp_value *val) { return MgInvoke<mgp_vertex *>(mgp_value_get_vertex, val); }
|
||||
|
||||
mgp_edge *value_get_edge(mgp_value *val) { return MgInvoke<mgp_edge *>(mgp_value_get_edge, val); }
|
||||
inline mgp_edge *value_get_edge(mgp_value *val) { return MgInvoke<mgp_edge *>(mgp_value_get_edge, val); }
|
||||
|
||||
mgp_path *value_get_path(mgp_value *val) { return MgInvoke<mgp_path *>(mgp_value_get_path, val); }
|
||||
inline mgp_path *value_get_path(mgp_value *val) { return MgInvoke<mgp_path *>(mgp_value_get_path, val); }
|
||||
|
||||
mgp_date *value_get_date(mgp_value *val) { return MgInvoke<mgp_date *>(mgp_value_get_date, val); }
|
||||
inline mgp_date *value_get_date(mgp_value *val) { return MgInvoke<mgp_date *>(mgp_value_get_date, val); }
|
||||
|
||||
mgp_local_time *value_get_local_time(mgp_value *val) {
|
||||
inline mgp_local_time *value_get_local_time(mgp_value *val) {
|
||||
return MgInvoke<mgp_local_time *>(mgp_value_get_local_time, val);
|
||||
}
|
||||
|
||||
mgp_local_date_time *value_get_local_date_time(mgp_value *val) {
|
||||
inline mgp_local_date_time *value_get_local_date_time(mgp_value *val) {
|
||||
return MgInvoke<mgp_local_date_time *>(mgp_value_get_local_date_time, val);
|
||||
}
|
||||
|
||||
mgp_duration *value_get_duration(mgp_value *val) { return MgInvoke<mgp_duration *>(mgp_value_get_duration, val); }
|
||||
inline mgp_duration *value_get_duration(mgp_value *val) {
|
||||
return MgInvoke<mgp_duration *>(mgp_value_get_duration, val);
|
||||
}
|
||||
|
||||
// Check type of value
|
||||
|
||||
bool value_is_null(mgp_value *val) { return MgInvoke<int>(mgp_value_is_null, val); }
|
||||
inline bool value_is_null(mgp_value *val) { return MgInvoke<int>(mgp_value_is_null, val); }
|
||||
|
||||
bool value_is_bool(mgp_value *val) { return MgInvoke<int>(mgp_value_is_bool, val); }
|
||||
inline bool value_is_bool(mgp_value *val) { return MgInvoke<int>(mgp_value_is_bool, val); }
|
||||
|
||||
bool value_is_int(mgp_value *val) { return MgInvoke<int>(mgp_value_is_int, val); }
|
||||
inline bool value_is_int(mgp_value *val) { return MgInvoke<int>(mgp_value_is_int, val); }
|
||||
|
||||
bool value_is_double(mgp_value *val) { return MgInvoke<int>(mgp_value_is_double, val); }
|
||||
inline bool value_is_double(mgp_value *val) { return MgInvoke<int>(mgp_value_is_double, val); }
|
||||
|
||||
bool value_is_string(mgp_value *val) { return MgInvoke<int>(mgp_value_is_string, val); }
|
||||
inline bool value_is_string(mgp_value *val) { return MgInvoke<int>(mgp_value_is_string, val); }
|
||||
|
||||
bool value_is_list(mgp_value *val) { return MgInvoke<int>(mgp_value_is_list, val); }
|
||||
inline bool value_is_list(mgp_value *val) { return MgInvoke<int>(mgp_value_is_list, val); }
|
||||
|
||||
bool value_is_map(mgp_value *val) { return MgInvoke<int>(mgp_value_is_map, val); }
|
||||
inline bool value_is_map(mgp_value *val) { return MgInvoke<int>(mgp_value_is_map, val); }
|
||||
|
||||
bool value_is_vertex(mgp_value *val) { return MgInvoke<int>(mgp_value_is_vertex, val); }
|
||||
inline bool value_is_vertex(mgp_value *val) { return MgInvoke<int>(mgp_value_is_vertex, val); }
|
||||
|
||||
bool value_is_edge(mgp_value *val) { return MgInvoke<int>(mgp_value_is_edge, val); }
|
||||
inline bool value_is_edge(mgp_value *val) { return MgInvoke<int>(mgp_value_is_edge, val); }
|
||||
|
||||
bool value_is_path(mgp_value *val) { return MgInvoke<int>(mgp_value_is_path, val); }
|
||||
inline bool value_is_path(mgp_value *val) { return MgInvoke<int>(mgp_value_is_path, val); }
|
||||
|
||||
bool value_is_date(mgp_value *val) { return MgInvoke<int>(mgp_value_is_date, val); }
|
||||
inline bool value_is_date(mgp_value *val) { return MgInvoke<int>(mgp_value_is_date, val); }
|
||||
|
||||
bool value_is_local_time(mgp_value *val) { return MgInvoke<int>(mgp_value_is_local_time, val); }
|
||||
inline bool value_is_local_time(mgp_value *val) { return MgInvoke<int>(mgp_value_is_local_time, val); }
|
||||
|
||||
bool value_is_local_date_time(mgp_value *val) { return MgInvoke<int>(mgp_value_is_local_date_time, val); }
|
||||
inline bool value_is_local_date_time(mgp_value *val) { return MgInvoke<int>(mgp_value_is_local_date_time, val); }
|
||||
|
||||
bool value_is_duration(mgp_value *val) { return MgInvoke<int>(mgp_value_is_duration, val); }
|
||||
inline bool value_is_duration(mgp_value *val) { return MgInvoke<int>(mgp_value_is_duration, val); }
|
||||
|
||||
// Get type
|
||||
|
||||
mgp_type *type_any() { return MgInvoke<mgp_type *>(mgp_type_any); }
|
||||
inline mgp_type *type_any() { return MgInvoke<mgp_type *>(mgp_type_any); }
|
||||
|
||||
mgp_type *type_bool() { return MgInvoke<mgp_type *>(mgp_type_bool); }
|
||||
inline mgp_type *type_bool() { return MgInvoke<mgp_type *>(mgp_type_bool); }
|
||||
|
||||
mgp_type *type_string() { return MgInvoke<mgp_type *>(mgp_type_string); }
|
||||
inline mgp_type *type_string() { return MgInvoke<mgp_type *>(mgp_type_string); }
|
||||
|
||||
mgp_type *type_int() { return MgInvoke<mgp_type *>(mgp_type_int); }
|
||||
inline mgp_type *type_int() { return MgInvoke<mgp_type *>(mgp_type_int); }
|
||||
|
||||
mgp_type *type_float() { return MgInvoke<mgp_type *>(mgp_type_float); }
|
||||
inline mgp_type *type_float() { return MgInvoke<mgp_type *>(mgp_type_float); }
|
||||
|
||||
mgp_type *type_number() { return MgInvoke<mgp_type *>(mgp_type_number); }
|
||||
inline mgp_type *type_number() { return MgInvoke<mgp_type *>(mgp_type_number); }
|
||||
|
||||
mgp_type *type_list(mgp_type *element_type) { return MgInvoke<mgp_type *>(mgp_type_list, element_type); }
|
||||
inline mgp_type *type_list(mgp_type *element_type) { return MgInvoke<mgp_type *>(mgp_type_list, element_type); }
|
||||
|
||||
mgp_type *type_map() { return MgInvoke<mgp_type *>(mgp_type_map); }
|
||||
inline mgp_type *type_map() { return MgInvoke<mgp_type *>(mgp_type_map); }
|
||||
|
||||
mgp_type *type_node() { return MgInvoke<mgp_type *>(mgp_type_node); }
|
||||
inline mgp_type *type_node() { return MgInvoke<mgp_type *>(mgp_type_node); }
|
||||
|
||||
mgp_type *type_relationship() { return MgInvoke<mgp_type *>(mgp_type_relationship); }
|
||||
inline mgp_type *type_relationship() { return MgInvoke<mgp_type *>(mgp_type_relationship); }
|
||||
|
||||
mgp_type *type_path() { return MgInvoke<mgp_type *>(mgp_type_path); }
|
||||
inline mgp_type *type_path() { return MgInvoke<mgp_type *>(mgp_type_path); }
|
||||
|
||||
mgp_type *type_date() { return MgInvoke<mgp_type *>(mgp_type_date); }
|
||||
inline mgp_type *type_date() { return MgInvoke<mgp_type *>(mgp_type_date); }
|
||||
|
||||
mgp_type *type_local_time() { return MgInvoke<mgp_type *>(mgp_type_local_time); }
|
||||
inline mgp_type *type_local_time() { return MgInvoke<mgp_type *>(mgp_type_local_time); }
|
||||
|
||||
mgp_type *type_local_date_time() { return MgInvoke<mgp_type *>(mgp_type_local_date_time); }
|
||||
inline mgp_type *type_local_date_time() { return MgInvoke<mgp_type *>(mgp_type_local_date_time); }
|
||||
|
||||
mgp_type *type_duration() { return MgInvoke<mgp_type *>(mgp_type_duration); }
|
||||
inline mgp_type *type_duration() { return MgInvoke<mgp_type *>(mgp_type_duration); }
|
||||
|
||||
mgp_type *type_nullable(mgp_type *type) { return MgInvoke<mgp_type *>(mgp_type_nullable, type); }
|
||||
inline mgp_type *type_nullable(mgp_type *type) { return MgInvoke<mgp_type *>(mgp_type_nullable, type); }
|
||||
|
||||
// mgp_graph
|
||||
|
||||
bool graph_is_mutable(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_mutable, graph); }
|
||||
inline bool graph_is_mutable(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_mutable, graph); }
|
||||
|
||||
mgp_vertex *graph_create_vertex(mgp_graph *graph, mgp_memory *memory) {
|
||||
inline mgp_vertex *graph_create_vertex(mgp_graph *graph, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_vertex *>(mgp_graph_create_vertex, graph, memory);
|
||||
}
|
||||
|
||||
void graph_delete_vertex(mgp_graph *graph, mgp_vertex *vertex) { MgInvokeVoid(mgp_graph_delete_vertex, graph, vertex); }
|
||||
inline void graph_delete_vertex(mgp_graph *graph, mgp_vertex *vertex) {
|
||||
MgInvokeVoid(mgp_graph_delete_vertex, graph, vertex);
|
||||
}
|
||||
|
||||
void graph_detach_delete_vertex(mgp_graph *graph, mgp_vertex *vertex) {
|
||||
inline void graph_detach_delete_vertex(mgp_graph *graph, mgp_vertex *vertex) {
|
||||
MgInvokeVoid(mgp_graph_detach_delete_vertex, graph, vertex);
|
||||
}
|
||||
|
||||
mgp_edge *graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *to, mgp_edge_type type,
|
||||
mgp_memory *memory) {
|
||||
inline mgp_edge *graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *to, mgp_edge_type type,
|
||||
mgp_memory *memory) {
|
||||
return MgInvoke<mgp_edge *>(mgp_graph_create_edge, graph, from, to, type, memory);
|
||||
}
|
||||
|
||||
void graph_delete_edge(mgp_graph *graph, mgp_edge *edge) { MgInvokeVoid(mgp_graph_delete_edge, graph, edge); }
|
||||
inline void graph_delete_edge(mgp_graph *graph, mgp_edge *edge) { MgInvokeVoid(mgp_graph_delete_edge, graph, edge); }
|
||||
|
||||
mgp_vertex *graph_get_vertex_by_id(mgp_graph *g, mgp_vertex_id id, mgp_memory *memory) {
|
||||
inline mgp_vertex *graph_get_vertex_by_id(mgp_graph *g, mgp_vertex_id id, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_vertex *>(mgp_graph_get_vertex_by_id, g, id, memory);
|
||||
}
|
||||
|
||||
mgp_vertices_iterator *graph_iter_vertices(mgp_graph *g, mgp_memory *memory) {
|
||||
inline mgp_vertices_iterator *graph_iter_vertices(mgp_graph *g, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_vertices_iterator *>(mgp_graph_iter_vertices, g, memory);
|
||||
}
|
||||
|
||||
// mgp_vertices_iterator
|
||||
|
||||
void vertices_iterator_destroy(mgp_vertices_iterator *it) { mgp_vertices_iterator_destroy(it); }
|
||||
inline void vertices_iterator_destroy(mgp_vertices_iterator *it) { mgp_vertices_iterator_destroy(it); }
|
||||
|
||||
mgp_vertex *vertices_iterator_get(mgp_vertices_iterator *it) {
|
||||
inline mgp_vertex *vertices_iterator_get(mgp_vertices_iterator *it) {
|
||||
return MgInvoke<mgp_vertex *>(mgp_vertices_iterator_get, it);
|
||||
}
|
||||
|
||||
mgp_vertex *vertices_iterator_next(mgp_vertices_iterator *it) {
|
||||
inline mgp_vertex *vertices_iterator_next(mgp_vertices_iterator *it) {
|
||||
return MgInvoke<mgp_vertex *>(mgp_vertices_iterator_next, it);
|
||||
}
|
||||
|
||||
// mgp_edges_iterator
|
||||
|
||||
void edges_iterator_destroy(mgp_edges_iterator *it) { mgp_edges_iterator_destroy(it); }
|
||||
inline void edges_iterator_destroy(mgp_edges_iterator *it) { mgp_edges_iterator_destroy(it); }
|
||||
|
||||
mgp_edge *edges_iterator_get(mgp_edges_iterator *it) { return MgInvoke<mgp_edge *>(mgp_edges_iterator_get, it); }
|
||||
inline mgp_edge *edges_iterator_get(mgp_edges_iterator *it) { return MgInvoke<mgp_edge *>(mgp_edges_iterator_get, it); }
|
||||
|
||||
mgp_edge *edges_iterator_next(mgp_edges_iterator *it) { return MgInvoke<mgp_edge *>(mgp_edges_iterator_next, it); }
|
||||
inline mgp_edge *edges_iterator_next(mgp_edges_iterator *it) {
|
||||
return MgInvoke<mgp_edge *>(mgp_edges_iterator_next, it);
|
||||
}
|
||||
|
||||
// mgp_properties_iterator
|
||||
|
||||
void properties_iterator_destroy(mgp_properties_iterator *it) { mgp_properties_iterator_destroy(it); }
|
||||
inline void properties_iterator_destroy(mgp_properties_iterator *it) { mgp_properties_iterator_destroy(it); }
|
||||
|
||||
mgp_property *properties_iterator_get(mgp_properties_iterator *it) {
|
||||
inline mgp_property *properties_iterator_get(mgp_properties_iterator *it) {
|
||||
return MgInvoke<mgp_property *>(mgp_properties_iterator_get, it);
|
||||
}
|
||||
|
||||
mgp_property *properties_iterator_next(mgp_properties_iterator *it) {
|
||||
inline mgp_property *properties_iterator_next(mgp_properties_iterator *it) {
|
||||
return MgInvoke<mgp_property *>(mgp_properties_iterator_next, it);
|
||||
}
|
||||
|
||||
@@ -284,409 +294,440 @@ mgp_property *properties_iterator_next(mgp_properties_iterator *it) {
|
||||
|
||||
// mgp_list
|
||||
|
||||
mgp_list *list_make_empty(size_t capacity, mgp_memory *memory) {
|
||||
inline mgp_list *list_make_empty(size_t capacity, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_list *>(mgp_list_make_empty, capacity, memory);
|
||||
}
|
||||
|
||||
mgp_list *list_copy(mgp_list *list, mgp_memory *memory) { return MgInvoke<mgp_list *>(mgp_list_copy, list, memory); }
|
||||
inline mgp_list *list_copy(mgp_list *list, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_list *>(mgp_list_copy, list, memory);
|
||||
}
|
||||
|
||||
void list_destroy(mgp_list *list) { mgp_list_destroy(list); }
|
||||
inline void list_destroy(mgp_list *list) { mgp_list_destroy(list); }
|
||||
|
||||
void list_append(mgp_list *list, mgp_value *val) { MgInvokeVoid(mgp_list_append, list, val); }
|
||||
inline void list_append(mgp_list *list, mgp_value *val) { MgInvokeVoid(mgp_list_append, list, val); }
|
||||
|
||||
void list_append_extend(mgp_list *list, mgp_value *val) { MgInvokeVoid(mgp_list_append_extend, list, val); }
|
||||
inline void list_append_extend(mgp_list *list, mgp_value *val) { MgInvokeVoid(mgp_list_append_extend, list, val); }
|
||||
|
||||
size_t list_size(mgp_list *list) { return MgInvoke<size_t>(mgp_list_size, list); }
|
||||
inline size_t list_size(mgp_list *list) { return MgInvoke<size_t>(mgp_list_size, list); }
|
||||
|
||||
size_t list_capacity(mgp_list *list) { return MgInvoke<size_t>(mgp_list_capacity, list); }
|
||||
inline size_t list_capacity(mgp_list *list) { return MgInvoke<size_t>(mgp_list_capacity, list); }
|
||||
|
||||
mgp_value *list_at(mgp_list *list, size_t index) { return MgInvoke<mgp_value *>(mgp_list_at, list, index); }
|
||||
inline mgp_value *list_at(mgp_list *list, size_t index) { return MgInvoke<mgp_value *>(mgp_list_at, list, index); }
|
||||
|
||||
// mgp_map
|
||||
|
||||
mgp_map *map_make_empty(mgp_memory *memory) { return MgInvoke<mgp_map *>(mgp_map_make_empty, memory); }
|
||||
inline mgp_map *map_make_empty(mgp_memory *memory) { return MgInvoke<mgp_map *>(mgp_map_make_empty, memory); }
|
||||
|
||||
mgp_map *map_copy(mgp_map *map, mgp_memory *memory) { return MgInvoke<mgp_map *>(mgp_map_copy, map, memory); }
|
||||
inline mgp_map *map_copy(mgp_map *map, mgp_memory *memory) { return MgInvoke<mgp_map *>(mgp_map_copy, map, memory); }
|
||||
|
||||
void map_destroy(mgp_map *map) { mgp_map_destroy(map); }
|
||||
inline void map_destroy(mgp_map *map) { mgp_map_destroy(map); }
|
||||
|
||||
void map_insert(mgp_map *map, const char *key, mgp_value *value) { MgInvokeVoid(mgp_map_insert, map, key, value); }
|
||||
inline void map_insert(mgp_map *map, const char *key, mgp_value *value) {
|
||||
MgInvokeVoid(mgp_map_insert, map, key, value);
|
||||
}
|
||||
|
||||
size_t map_size(mgp_map *map) { return MgInvoke<size_t>(mgp_map_size, map); }
|
||||
inline size_t map_size(mgp_map *map) { return MgInvoke<size_t>(mgp_map_size, map); }
|
||||
|
||||
mgp_value *map_at(mgp_map *map, const char *key) { return MgInvoke<mgp_value *>(mgp_map_at, map, key); }
|
||||
inline mgp_value *map_at(mgp_map *map, const char *key) { return MgInvoke<mgp_value *>(mgp_map_at, map, key); }
|
||||
|
||||
const char *map_item_key(mgp_map_item *item) { return MgInvoke<const char *>(mgp_map_item_key, item); }
|
||||
inline const char *map_item_key(mgp_map_item *item) { return MgInvoke<const char *>(mgp_map_item_key, item); }
|
||||
|
||||
mgp_value *map_item_value(mgp_map_item *item) { return MgInvoke<mgp_value *>(mgp_map_item_value, item); }
|
||||
inline mgp_value *map_item_value(mgp_map_item *item) { return MgInvoke<mgp_value *>(mgp_map_item_value, item); }
|
||||
|
||||
mgp_map_items_iterator *map_iter_items(mgp_map *map, mgp_memory *memory) {
|
||||
inline mgp_map_items_iterator *map_iter_items(mgp_map *map, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_map_items_iterator *>(mgp_map_iter_items, map, memory);
|
||||
}
|
||||
|
||||
void map_items_iterator_destroy(mgp_map_items_iterator *it) { mgp_map_items_iterator_destroy(it); }
|
||||
inline void map_items_iterator_destroy(mgp_map_items_iterator *it) { mgp_map_items_iterator_destroy(it); }
|
||||
|
||||
mgp_map_item *map_items_iterator_get(mgp_map_items_iterator *it) {
|
||||
inline mgp_map_item *map_items_iterator_get(mgp_map_items_iterator *it) {
|
||||
return MgInvoke<mgp_map_item *>(mgp_map_items_iterator_get, it);
|
||||
}
|
||||
|
||||
mgp_map_item *map_items_iterator_next(mgp_map_items_iterator *it) {
|
||||
inline mgp_map_item *map_items_iterator_next(mgp_map_items_iterator *it) {
|
||||
return MgInvoke<mgp_map_item *>(mgp_map_items_iterator_next, it);
|
||||
}
|
||||
|
||||
// mgp_vertex
|
||||
|
||||
mgp_vertex_id vertex_get_id(mgp_vertex *v) { return MgInvoke<mgp_vertex_id>(mgp_vertex_get_id, v); }
|
||||
inline mgp_vertex_id vertex_get_id(mgp_vertex *v) { return MgInvoke<mgp_vertex_id>(mgp_vertex_get_id, v); }
|
||||
|
||||
mgp_vertex *vertex_copy(mgp_vertex *v, mgp_memory *memory) {
|
||||
inline mgp_vertex *vertex_copy(mgp_vertex *v, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_vertex *>(mgp_vertex_copy, v, memory);
|
||||
}
|
||||
|
||||
void vertex_destroy(mgp_vertex *v) { mgp_vertex_destroy(v); }
|
||||
inline void vertex_destroy(mgp_vertex *v) { mgp_vertex_destroy(v); }
|
||||
|
||||
bool vertex_equal(mgp_vertex *v1, mgp_vertex *v2) { return MgInvoke<int>(mgp_vertex_equal, v1, v2); }
|
||||
inline bool vertex_equal(mgp_vertex *v1, mgp_vertex *v2) { return MgInvoke<int>(mgp_vertex_equal, v1, v2); }
|
||||
|
||||
size_t vertex_labels_count(mgp_vertex *v) { return MgInvoke<size_t>(mgp_vertex_labels_count, v); }
|
||||
inline size_t vertex_labels_count(mgp_vertex *v) { return MgInvoke<size_t>(mgp_vertex_labels_count, v); }
|
||||
|
||||
mgp_label vertex_label_at(mgp_vertex *v, size_t index) { return MgInvoke<mgp_label>(mgp_vertex_label_at, v, index); }
|
||||
inline mgp_label vertex_label_at(mgp_vertex *v, size_t index) {
|
||||
return MgInvoke<mgp_label>(mgp_vertex_label_at, v, index);
|
||||
}
|
||||
|
||||
bool vertex_has_label(mgp_vertex *v, mgp_label label) { return MgInvoke<int>(mgp_vertex_has_label, v, label); }
|
||||
inline bool vertex_has_label(mgp_vertex *v, mgp_label label) { return MgInvoke<int>(mgp_vertex_has_label, v, label); }
|
||||
|
||||
bool vertex_has_label_named(mgp_vertex *v, const char *label_name) {
|
||||
inline bool vertex_has_label_named(mgp_vertex *v, const char *label_name) {
|
||||
return MgInvoke<int>(mgp_vertex_has_label_named, v, label_name);
|
||||
}
|
||||
|
||||
void vertex_add_label(mgp_vertex *vertex, mgp_label label) { MgInvokeVoid(mgp_vertex_add_label, vertex, label); }
|
||||
inline void vertex_add_label(mgp_vertex *vertex, mgp_label label) { MgInvokeVoid(mgp_vertex_add_label, vertex, label); }
|
||||
|
||||
mgp_value *vertex_get_property(mgp_vertex *v, const char *property_name, mgp_memory *memory) {
|
||||
inline mgp_value *vertex_get_property(mgp_vertex *v, const char *property_name, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_value *>(mgp_vertex_get_property, v, property_name, memory);
|
||||
}
|
||||
|
||||
mgp_properties_iterator *vertex_iter_properties(mgp_vertex *v, mgp_memory *memory) {
|
||||
inline void vertex_set_property(mgp_vertex *v, const char *property_name, mgp_value *property_value) {
|
||||
MgInvokeVoid(mgp_vertex_set_property, v, property_name, property_value);
|
||||
}
|
||||
|
||||
inline mgp_properties_iterator *vertex_iter_properties(mgp_vertex *v, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_properties_iterator *>(mgp_vertex_iter_properties, v, memory);
|
||||
}
|
||||
|
||||
mgp_edges_iterator *vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory) {
|
||||
inline mgp_edges_iterator *vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_edges_iterator *>(mgp_vertex_iter_in_edges, v, memory);
|
||||
}
|
||||
|
||||
mgp_edges_iterator *vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory) {
|
||||
inline mgp_edges_iterator *vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_edges_iterator *>(mgp_vertex_iter_out_edges, v, memory);
|
||||
}
|
||||
|
||||
// mgp_edge
|
||||
|
||||
mgp_edge_id edge_get_id(mgp_edge *e) { return MgInvoke<mgp_edge_id>(mgp_edge_get_id, e); }
|
||||
inline mgp_edge_id edge_get_id(mgp_edge *e) { return MgInvoke<mgp_edge_id>(mgp_edge_get_id, e); }
|
||||
|
||||
mgp_edge *edge_copy(mgp_edge *e, mgp_memory *memory) { return MgInvoke<mgp_edge *>(mgp_edge_copy, e, memory); }
|
||||
inline mgp_edge *edge_copy(mgp_edge *e, mgp_memory *memory) { return MgInvoke<mgp_edge *>(mgp_edge_copy, e, memory); }
|
||||
|
||||
void edge_destroy(mgp_edge *e) { mgp_edge_destroy(e); }
|
||||
inline void edge_destroy(mgp_edge *e) { mgp_edge_destroy(e); }
|
||||
|
||||
bool edge_equal(mgp_edge *e1, mgp_edge *e2) { return MgInvoke<int>(mgp_edge_equal, e1, e2); }
|
||||
inline bool edge_equal(mgp_edge *e1, mgp_edge *e2) { return MgInvoke<int>(mgp_edge_equal, e1, e2); }
|
||||
|
||||
mgp_edge_type edge_get_type(mgp_edge *e) { return MgInvoke<mgp_edge_type>(mgp_edge_get_type, e); }
|
||||
inline mgp_edge_type edge_get_type(mgp_edge *e) { return MgInvoke<mgp_edge_type>(mgp_edge_get_type, e); }
|
||||
|
||||
mgp_vertex *edge_get_from(mgp_edge *e) { return MgInvoke<mgp_vertex *>(mgp_edge_get_from, e); }
|
||||
inline mgp_vertex *edge_get_from(mgp_edge *e) { return MgInvoke<mgp_vertex *>(mgp_edge_get_from, e); }
|
||||
|
||||
mgp_vertex *edge_get_to(mgp_edge *e) { return MgInvoke<mgp_vertex *>(mgp_edge_get_to, e); }
|
||||
inline mgp_vertex *edge_get_to(mgp_edge *e) { return MgInvoke<mgp_vertex *>(mgp_edge_get_to, e); }
|
||||
|
||||
mgp_value *edge_get_property(mgp_edge *e, const char *property_name, mgp_memory *memory) {
|
||||
inline mgp_value *edge_get_property(mgp_edge *e, const char *property_name, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_value *>(mgp_edge_get_property, e, property_name, memory);
|
||||
}
|
||||
|
||||
mgp_properties_iterator *edge_iter_properties(mgp_edge *e, mgp_memory *memory) {
|
||||
inline void edge_set_property(mgp_edge *e, const char *property_name, mgp_value *property_value) {
|
||||
MgInvokeVoid(mgp_edge_set_property, e, property_name, property_value);
|
||||
}
|
||||
|
||||
inline mgp_properties_iterator *edge_iter_properties(mgp_edge *e, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_properties_iterator *>(mgp_edge_iter_properties, e, memory);
|
||||
}
|
||||
|
||||
// mgp_path
|
||||
|
||||
mgp_path *path_make_with_start(mgp_vertex *vertex, mgp_memory *memory) {
|
||||
inline mgp_path *path_make_with_start(mgp_vertex *vertex, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_path *>(mgp_path_make_with_start, vertex, memory);
|
||||
}
|
||||
|
||||
mgp_path *path_copy(mgp_path *path, mgp_memory *memory) { return MgInvoke<mgp_path *>(mgp_path_copy, path, memory); }
|
||||
inline mgp_path *path_copy(mgp_path *path, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_path *>(mgp_path_copy, path, memory);
|
||||
}
|
||||
|
||||
void path_destroy(mgp_path *path) { mgp_path_destroy(path); }
|
||||
inline void path_destroy(mgp_path *path) { mgp_path_destroy(path); }
|
||||
|
||||
void path_expand(mgp_path *path, mgp_edge *edge) { MgInvokeVoid(mgp_path_expand, path, edge); }
|
||||
inline void path_expand(mgp_path *path, mgp_edge *edge) { MgInvokeVoid(mgp_path_expand, path, edge); }
|
||||
|
||||
size_t path_size(mgp_path *path) { return MgInvoke<size_t>(mgp_path_size, path); }
|
||||
inline size_t path_size(mgp_path *path) { return MgInvoke<size_t>(mgp_path_size, path); }
|
||||
|
||||
mgp_vertex *path_vertex_at(mgp_path *path, size_t index) {
|
||||
inline mgp_vertex *path_vertex_at(mgp_path *path, size_t index) {
|
||||
return MgInvoke<mgp_vertex *>(mgp_path_vertex_at, path, index);
|
||||
}
|
||||
|
||||
mgp_edge *path_edge_at(mgp_path *path, size_t index) { return MgInvoke<mgp_edge *>(mgp_path_edge_at, path, index); }
|
||||
inline mgp_edge *path_edge_at(mgp_path *path, size_t index) {
|
||||
return MgInvoke<mgp_edge *>(mgp_path_edge_at, path, index);
|
||||
}
|
||||
|
||||
bool path_equal(mgp_path *p1, mgp_path *p2) { return MgInvoke<int>(mgp_path_equal, p1, p2); }
|
||||
inline bool path_equal(mgp_path *p1, mgp_path *p2) { return MgInvoke<int>(mgp_path_equal, p1, p2); }
|
||||
|
||||
// Temporal type {mgp_date, mgp_local_time, mgp_local_date_time, mgp_duration} methods
|
||||
|
||||
// mgp_date
|
||||
|
||||
mgp_date *date_from_string(const char *string, mgp_memory *memory) {
|
||||
inline mgp_date *date_from_string(const char *string, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_date *>(mgp_date_from_string, string, memory);
|
||||
}
|
||||
|
||||
mgp_date *date_from_parameters(mgp_date_parameters *parameters, mgp_memory *memory) {
|
||||
inline mgp_date *date_from_parameters(mgp_date_parameters *parameters, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_date *>(mgp_date_from_parameters, parameters, memory);
|
||||
}
|
||||
|
||||
mgp_date *date_copy(mgp_date *date, mgp_memory *memory) { return MgInvoke<mgp_date *>(mgp_date_copy, date, memory); }
|
||||
inline mgp_date *date_copy(mgp_date *date, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_date *>(mgp_date_copy, date, memory);
|
||||
}
|
||||
|
||||
void date_destroy(mgp_date *date) { mgp_date_destroy(date); }
|
||||
inline void date_destroy(mgp_date *date) { mgp_date_destroy(date); }
|
||||
|
||||
bool date_equal(mgp_date *first, mgp_date *second) { return MgInvoke<int>(mgp_date_equal, first, second); }
|
||||
inline bool date_equal(mgp_date *first, mgp_date *second) { return MgInvoke<int>(mgp_date_equal, first, second); }
|
||||
|
||||
int date_get_year(mgp_date *date) { return MgInvoke<int>(mgp_date_get_year, date); }
|
||||
inline int date_get_year(mgp_date *date) { return MgInvoke<int>(mgp_date_get_year, date); }
|
||||
|
||||
int date_get_month(mgp_date *date) { return MgInvoke<int>(mgp_date_get_month, date); }
|
||||
inline int date_get_month(mgp_date *date) { return MgInvoke<int>(mgp_date_get_month, date); }
|
||||
|
||||
int date_get_day(mgp_date *date) { return MgInvoke<int>(mgp_date_get_day, date); }
|
||||
inline int date_get_day(mgp_date *date) { return MgInvoke<int>(mgp_date_get_day, date); }
|
||||
|
||||
int64_t date_timestamp(mgp_date *date) { return MgInvoke<int64_t>(mgp_date_timestamp, date); }
|
||||
inline int64_t date_timestamp(mgp_date *date) { return MgInvoke<int64_t>(mgp_date_timestamp, date); }
|
||||
|
||||
mgp_date *date_now(mgp_memory *memory) { return MgInvoke<mgp_date *>(mgp_date_now, memory); }
|
||||
inline mgp_date *date_now(mgp_memory *memory) { return MgInvoke<mgp_date *>(mgp_date_now, memory); }
|
||||
|
||||
mgp_date *date_add_duration(mgp_date *date, mgp_duration *dur, mgp_memory *memory) {
|
||||
inline mgp_date *date_add_duration(mgp_date *date, mgp_duration *dur, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_date *>(mgp_date_add_duration, date, dur, memory);
|
||||
}
|
||||
|
||||
mgp_date *date_sub_duration(mgp_date *date, mgp_duration *dur, mgp_memory *memory) {
|
||||
inline mgp_date *date_sub_duration(mgp_date *date, mgp_duration *dur, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_date *>(mgp_date_sub_duration, date, dur, memory);
|
||||
}
|
||||
|
||||
mgp_duration *date_diff(mgp_date *first, mgp_date *second, mgp_memory *memory) {
|
||||
inline mgp_duration *date_diff(mgp_date *first, mgp_date *second, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_date_diff, first, second, memory);
|
||||
}
|
||||
|
||||
// mgp_local_time
|
||||
|
||||
mgp_local_time *local_time_from_string(const char *string, mgp_memory *memory) {
|
||||
inline mgp_local_time *local_time_from_string(const char *string, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_time *>(mgp_local_time_from_string, string, memory);
|
||||
}
|
||||
|
||||
mgp_local_time *local_time_from_parameters(mgp_local_time_parameters *parameters, mgp_memory *memory) {
|
||||
inline mgp_local_time *local_time_from_parameters(mgp_local_time_parameters *parameters, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_time *>(mgp_local_time_from_parameters, parameters, memory);
|
||||
}
|
||||
|
||||
mgp_local_time *local_time_copy(mgp_local_time *local_time, mgp_memory *memory) {
|
||||
inline mgp_local_time *local_time_copy(mgp_local_time *local_time, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_time *>(mgp_local_time_copy, local_time, memory);
|
||||
}
|
||||
|
||||
void local_time_destroy(mgp_local_time *local_time) { mgp_local_time_destroy(local_time); }
|
||||
inline void local_time_destroy(mgp_local_time *local_time) { mgp_local_time_destroy(local_time); }
|
||||
|
||||
bool local_time_equal(mgp_local_time *first, mgp_local_time *second) {
|
||||
inline bool local_time_equal(mgp_local_time *first, mgp_local_time *second) {
|
||||
return MgInvoke<int>(mgp_local_time_equal, first, second);
|
||||
}
|
||||
|
||||
int local_time_get_hour(mgp_local_time *local_time) { return MgInvoke<int>(mgp_local_time_get_hour, local_time); }
|
||||
inline int local_time_get_hour(mgp_local_time *local_time) {
|
||||
return MgInvoke<int>(mgp_local_time_get_hour, local_time);
|
||||
}
|
||||
|
||||
int local_time_get_minute(mgp_local_time *local_time) { return MgInvoke<int>(mgp_local_time_get_minute, local_time); }
|
||||
inline int local_time_get_minute(mgp_local_time *local_time) {
|
||||
return MgInvoke<int>(mgp_local_time_get_minute, local_time);
|
||||
}
|
||||
|
||||
int local_time_get_second(mgp_local_time *local_time) { return MgInvoke<int>(mgp_local_time_get_second, local_time); }
|
||||
inline int local_time_get_second(mgp_local_time *local_time) {
|
||||
return MgInvoke<int>(mgp_local_time_get_second, local_time);
|
||||
}
|
||||
|
||||
int local_time_get_millisecond(mgp_local_time *local_time) {
|
||||
inline int local_time_get_millisecond(mgp_local_time *local_time) {
|
||||
return MgInvoke<int>(mgp_local_time_get_millisecond, local_time);
|
||||
}
|
||||
|
||||
int local_time_get_microsecond(mgp_local_time *local_time) {
|
||||
inline int local_time_get_microsecond(mgp_local_time *local_time) {
|
||||
return MgInvoke<int>(mgp_local_time_get_microsecond, local_time);
|
||||
}
|
||||
|
||||
int64_t local_time_timestamp(mgp_local_time *local_time) {
|
||||
inline int64_t local_time_timestamp(mgp_local_time *local_time) {
|
||||
return MgInvoke<int64_t>(mgp_local_time_timestamp, local_time);
|
||||
}
|
||||
|
||||
mgp_local_time *local_time_now(mgp_memory *memory) { return MgInvoke<mgp_local_time *>(mgp_local_time_now, memory); }
|
||||
inline mgp_local_time *local_time_now(mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_time *>(mgp_local_time_now, memory);
|
||||
}
|
||||
|
||||
mgp_local_time *local_time_add_duration(mgp_local_time *local_time, mgp_duration *dur, mgp_memory *memory) {
|
||||
inline mgp_local_time *local_time_add_duration(mgp_local_time *local_time, mgp_duration *dur, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_time *>(mgp_local_time_add_duration, local_time, dur, memory);
|
||||
}
|
||||
|
||||
mgp_local_time *local_time_sub_duration(mgp_local_time *local_time, mgp_duration *dur, mgp_memory *memory) {
|
||||
inline mgp_local_time *local_time_sub_duration(mgp_local_time *local_time, mgp_duration *dur, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_time *>(mgp_local_time_sub_duration, local_time, dur, memory);
|
||||
}
|
||||
|
||||
mgp_duration *local_time_diff(mgp_local_time *first, mgp_local_time *second, mgp_memory *memory) {
|
||||
inline mgp_duration *local_time_diff(mgp_local_time *first, mgp_local_time *second, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_local_time_diff, first, second, memory);
|
||||
}
|
||||
|
||||
// mgp_local_date_time
|
||||
|
||||
mgp_local_date_time *local_date_time_from_string(const char *string, mgp_memory *memory) {
|
||||
inline mgp_local_date_time *local_date_time_from_string(const char *string, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_date_time *>(mgp_local_date_time_from_string, string, memory);
|
||||
}
|
||||
|
||||
mgp_local_date_time *local_date_time_from_parameters(mgp_local_date_time_parameters *parameters, mgp_memory *memory) {
|
||||
inline mgp_local_date_time *local_date_time_from_parameters(mgp_local_date_time_parameters *parameters,
|
||||
mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_date_time *>(mgp_local_date_time_from_parameters, parameters, memory);
|
||||
}
|
||||
|
||||
mgp_local_date_time *local_date_time_copy(mgp_local_date_time *local_date_time, mgp_memory *memory) {
|
||||
inline mgp_local_date_time *local_date_time_copy(mgp_local_date_time *local_date_time, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_date_time *>(mgp_local_date_time_copy, local_date_time, memory);
|
||||
}
|
||||
|
||||
void local_date_time_destroy(mgp_local_date_time *local_date_time) { mgp_local_date_time_destroy(local_date_time); }
|
||||
inline void local_date_time_destroy(mgp_local_date_time *local_date_time) {
|
||||
mgp_local_date_time_destroy(local_date_time);
|
||||
}
|
||||
|
||||
bool local_date_time_equal(mgp_local_date_time *first, mgp_local_date_time *second) {
|
||||
inline bool local_date_time_equal(mgp_local_date_time *first, mgp_local_date_time *second) {
|
||||
return MgInvoke<int>(mgp_local_date_time_equal, first, second);
|
||||
}
|
||||
|
||||
int local_date_time_get_year(mgp_local_date_time *local_date_time) {
|
||||
inline int local_date_time_get_year(mgp_local_date_time *local_date_time) {
|
||||
return MgInvoke<int>(mgp_local_date_time_get_year, local_date_time);
|
||||
}
|
||||
|
||||
int local_date_time_get_month(mgp_local_date_time *local_date_time) {
|
||||
inline int local_date_time_get_month(mgp_local_date_time *local_date_time) {
|
||||
return MgInvoke<int>(mgp_local_date_time_get_month, local_date_time);
|
||||
}
|
||||
|
||||
int local_date_time_get_day(mgp_local_date_time *local_date_time) {
|
||||
inline int local_date_time_get_day(mgp_local_date_time *local_date_time) {
|
||||
return MgInvoke<int>(mgp_local_date_time_get_day, local_date_time);
|
||||
}
|
||||
|
||||
int local_date_time_get_hour(mgp_local_date_time *local_date_time) {
|
||||
inline int local_date_time_get_hour(mgp_local_date_time *local_date_time) {
|
||||
return MgInvoke<int>(mgp_local_date_time_get_hour, local_date_time);
|
||||
}
|
||||
|
||||
int local_date_time_get_minute(mgp_local_date_time *local_date_time) {
|
||||
inline int local_date_time_get_minute(mgp_local_date_time *local_date_time) {
|
||||
return MgInvoke<int>(mgp_local_date_time_get_minute, local_date_time);
|
||||
}
|
||||
|
||||
int local_date_time_get_second(mgp_local_date_time *local_date_time) {
|
||||
inline int local_date_time_get_second(mgp_local_date_time *local_date_time) {
|
||||
return MgInvoke<int>(mgp_local_date_time_get_second, local_date_time);
|
||||
}
|
||||
|
||||
int local_date_time_get_millisecond(mgp_local_date_time *local_date_time) {
|
||||
inline int local_date_time_get_millisecond(mgp_local_date_time *local_date_time) {
|
||||
return MgInvoke<int>(mgp_local_date_time_get_millisecond, local_date_time);
|
||||
}
|
||||
|
||||
int local_date_time_get_microsecond(mgp_local_date_time *local_date_time) {
|
||||
inline int local_date_time_get_microsecond(mgp_local_date_time *local_date_time) {
|
||||
return MgInvoke<int>(mgp_local_date_time_get_microsecond, local_date_time);
|
||||
}
|
||||
|
||||
int64_t local_date_time_timestamp(mgp_local_date_time *local_date_time) {
|
||||
inline int64_t local_date_time_timestamp(mgp_local_date_time *local_date_time) {
|
||||
return MgInvoke<int64_t>(mgp_local_date_time_timestamp, local_date_time);
|
||||
}
|
||||
|
||||
mgp_local_date_time *local_date_time_now(mgp_memory *memory) {
|
||||
inline mgp_local_date_time *local_date_time_now(mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_date_time *>(mgp_local_date_time_now, memory);
|
||||
}
|
||||
|
||||
mgp_local_date_time *local_date_time_add_duration(mgp_local_date_time *local_date_time, mgp_duration *dur,
|
||||
mgp_memory *memory) {
|
||||
inline mgp_local_date_time *local_date_time_add_duration(mgp_local_date_time *local_date_time, mgp_duration *dur,
|
||||
mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_date_time *>(mgp_local_date_time_add_duration, local_date_time, dur, memory);
|
||||
}
|
||||
|
||||
mgp_local_date_time *local_date_time_sub_duration(mgp_local_date_time *local_date_time, mgp_duration *dur,
|
||||
mgp_memory *memory) {
|
||||
inline mgp_local_date_time *local_date_time_sub_duration(mgp_local_date_time *local_date_time, mgp_duration *dur,
|
||||
mgp_memory *memory) {
|
||||
return MgInvoke<mgp_local_date_time *>(mgp_local_date_time_sub_duration, local_date_time, dur, memory);
|
||||
}
|
||||
|
||||
mgp_duration *local_date_time_diff(mgp_local_date_time *first, mgp_local_date_time *second, mgp_memory *memory) {
|
||||
inline mgp_duration *local_date_time_diff(mgp_local_date_time *first, mgp_local_date_time *second, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_local_date_time_diff, first, second, memory);
|
||||
}
|
||||
|
||||
// mgp_duration
|
||||
|
||||
mgp_duration *duration_from_string(const char *string, mgp_memory *memory) {
|
||||
inline mgp_duration *duration_from_string(const char *string, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_duration_from_string, string, memory);
|
||||
}
|
||||
|
||||
mgp_duration *duration_from_parameters(mgp_duration_parameters *parameters, mgp_memory *memory) {
|
||||
inline mgp_duration *duration_from_parameters(mgp_duration_parameters *parameters, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_duration_from_parameters, parameters, memory);
|
||||
}
|
||||
|
||||
mgp_duration *duration_from_microseconds(int64_t microseconds, mgp_memory *memory) {
|
||||
inline mgp_duration *duration_from_microseconds(int64_t microseconds, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_duration_from_microseconds, microseconds, memory);
|
||||
}
|
||||
|
||||
mgp_duration *duration_copy(mgp_duration *duration, mgp_memory *memory) {
|
||||
inline mgp_duration *duration_copy(mgp_duration *duration, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_duration_copy, duration, memory);
|
||||
}
|
||||
|
||||
void duration_destroy(mgp_duration *duration) { mgp_duration_destroy(duration); }
|
||||
inline void duration_destroy(mgp_duration *duration) { mgp_duration_destroy(duration); }
|
||||
|
||||
int64_t duration_get_microseconds(mgp_duration *duration) {
|
||||
inline int64_t duration_get_microseconds(mgp_duration *duration) {
|
||||
return MgInvoke<int64_t>(mgp_duration_get_microseconds, duration);
|
||||
}
|
||||
|
||||
bool duration_equal(mgp_duration *first, mgp_duration *second) {
|
||||
inline bool duration_equal(mgp_duration *first, mgp_duration *second) {
|
||||
return MgInvoke<int>(mgp_duration_equal, first, second);
|
||||
}
|
||||
|
||||
mgp_duration *duration_neg(mgp_duration *duration, mgp_memory *memory) {
|
||||
inline mgp_duration *duration_neg(mgp_duration *duration, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_duration_neg, duration, memory);
|
||||
}
|
||||
|
||||
mgp_duration *duration_add(mgp_duration *first, mgp_duration *second, mgp_memory *memory) {
|
||||
inline mgp_duration *duration_add(mgp_duration *first, mgp_duration *second, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_duration_add, first, second, memory);
|
||||
}
|
||||
|
||||
mgp_duration *duration_sub(mgp_duration *first, mgp_duration *second, mgp_memory *memory) {
|
||||
inline mgp_duration *duration_sub(mgp_duration *first, mgp_duration *second, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_duration *>(mgp_duration_sub, first, second, memory);
|
||||
}
|
||||
|
||||
// Procedure
|
||||
|
||||
mgp_proc *module_add_read_procedure(mgp_module *module, const char *name, mgp_proc_cb cb) {
|
||||
inline mgp_proc *module_add_read_procedure(mgp_module *module, const char *name, mgp_proc_cb cb) {
|
||||
return MgInvoke<mgp_proc *>(mgp_module_add_read_procedure, module, name, cb);
|
||||
}
|
||||
|
||||
mgp_proc *module_add_write_procedure(mgp_module *module, const char *name, mgp_proc_cb cb) {
|
||||
inline mgp_proc *module_add_write_procedure(mgp_module *module, const char *name, mgp_proc_cb cb) {
|
||||
return MgInvoke<mgp_proc *>(mgp_module_add_write_procedure, module, name, cb);
|
||||
}
|
||||
|
||||
void proc_add_arg(mgp_proc *proc, const char *name, mgp_type *type) {
|
||||
inline void proc_add_arg(mgp_proc *proc, const char *name, mgp_type *type) {
|
||||
MgInvokeVoid(mgp_proc_add_arg, proc, name, type);
|
||||
}
|
||||
|
||||
void proc_add_opt_arg(mgp_proc *proc, const char *name, mgp_type *type, mgp_value *default_value) {
|
||||
inline void proc_add_opt_arg(mgp_proc *proc, const char *name, mgp_type *type, mgp_value *default_value) {
|
||||
MgInvokeVoid(mgp_proc_add_opt_arg, proc, name, type, default_value);
|
||||
}
|
||||
|
||||
void proc_add_result(mgp_proc *proc, const char *name, mgp_type *type) {
|
||||
inline void proc_add_result(mgp_proc *proc, const char *name, mgp_type *type) {
|
||||
MgInvokeVoid(mgp_proc_add_result, proc, name, type);
|
||||
}
|
||||
|
||||
void proc_add_deprecated_result(mgp_proc *proc, const char *name, mgp_type *type) {
|
||||
inline void proc_add_deprecated_result(mgp_proc *proc, const char *name, mgp_type *type) {
|
||||
MgInvokeVoid(mgp_proc_add_deprecated_result, proc, name, type);
|
||||
}
|
||||
|
||||
bool must_abort(mgp_graph *graph) { return mgp_must_abort(graph); }
|
||||
inline bool must_abort(mgp_graph *graph) { return mgp_must_abort(graph); }
|
||||
|
||||
// mgp_result
|
||||
|
||||
void result_set_error_msg(mgp_result *res, const char *error_msg) {
|
||||
inline void result_set_error_msg(mgp_result *res, const char *error_msg) {
|
||||
MgInvokeVoid(mgp_result_set_error_msg, res, error_msg);
|
||||
}
|
||||
|
||||
mgp_result_record *result_new_record(mgp_result *res) {
|
||||
inline mgp_result_record *result_new_record(mgp_result *res) {
|
||||
return MgInvoke<mgp_result_record *>(mgp_result_new_record, res);
|
||||
}
|
||||
|
||||
void result_record_insert(mgp_result_record *record, const char *field_name, mgp_value *val) {
|
||||
inline void result_record_insert(mgp_result_record *record, const char *field_name, mgp_value *val) {
|
||||
MgInvokeVoid(mgp_result_record_insert, record, field_name, val);
|
||||
}
|
||||
|
||||
// Function
|
||||
|
||||
mgp_func *module_add_function(mgp_module *module, const char *name, mgp_func_cb cb) {
|
||||
inline mgp_func *module_add_function(mgp_module *module, const char *name, mgp_func_cb cb) {
|
||||
return MgInvoke<mgp_func *>(mgp_module_add_function, module, name, cb);
|
||||
}
|
||||
|
||||
void func_add_arg(mgp_func *func, const char *name, mgp_type *type) {
|
||||
inline void func_add_arg(mgp_func *func, const char *name, mgp_type *type) {
|
||||
MgInvokeVoid(mgp_func_add_arg, func, name, type);
|
||||
}
|
||||
|
||||
void func_add_opt_arg(mgp_func *func, const char *name, mgp_type *type, mgp_value *default_value) {
|
||||
inline void func_add_opt_arg(mgp_func *func, const char *name, mgp_type *type, mgp_value *default_value) {
|
||||
MgInvokeVoid(mgp_func_add_opt_arg, func, name, type, default_value);
|
||||
}
|
||||
|
||||
void func_result_set_error_msg(mgp_func_result *res, const char *msg, mgp_memory *memory) {
|
||||
inline void func_result_set_error_msg(mgp_func_result *res, const char *msg, mgp_memory *memory) {
|
||||
MgInvokeVoid(mgp_func_result_set_error_msg, res, msg, memory);
|
||||
}
|
||||
|
||||
void func_result_set_value(mgp_func_result *res, mgp_value *value, mgp_memory *memory) {
|
||||
inline void func_result_set_value(mgp_func_result *res, mgp_value *value, mgp_memory *memory) {
|
||||
MgInvokeVoid(mgp_func_result_set_value, res, value, memory);
|
||||
}
|
||||
|
||||
|
||||
1892
include/mgp.hpp
1892
include/mgp.hpp
File diff suppressed because it is too large
Load Diff
@@ -1283,7 +1283,7 @@ class Graph:
|
||||
raise InvalidContextError()
|
||||
self._graph.detach_delete_vertex(vertex._vertex)
|
||||
|
||||
def create_edge(self, from_vertex: Vertex, to_vertex: Vertex, edge_type: EdgeType) -> None:
|
||||
def create_edge(self, from_vertex: Vertex, to_vertex: Vertex, edge_type: EdgeType) -> Edge:
|
||||
"""
|
||||
Create an edge.
|
||||
|
||||
@@ -1292,13 +1292,16 @@ class Graph:
|
||||
to_vertex: `Vertex' to where edge is directed.
|
||||
edge_type: `EdgeType` defines the type of edge.
|
||||
|
||||
Returns:
|
||||
Created `Edge`.
|
||||
|
||||
Raises:
|
||||
ImmutableObjectError: If `graph` is immutable.
|
||||
UnableToAllocateError: If unable to allocate an edge.
|
||||
DeletedObjectError: If `from_vertex` or `to_vertex` has been deleted.
|
||||
SerializationError: If `from_vertex` or `to_vertex` has been modified by another transaction.
|
||||
Examples:
|
||||
```graph.create_edge(from_vertex, vertex, edge_type)```
|
||||
```edge = graph.create_edge(from_vertex, vertex, edge_type)```
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidContextError()
|
||||
|
||||
17
init
17
init
@@ -5,6 +5,9 @@ cd "$DIR"
|
||||
|
||||
source "$DIR/environment/util.sh"
|
||||
|
||||
DISTRO=$(operating_system)
|
||||
ARCHITECTURE=$(architecture)
|
||||
|
||||
function print_help () {
|
||||
echo "Usage: $0 [OPTION]"
|
||||
echo -e "Check for missing packages and setup the project.\n"
|
||||
@@ -64,8 +67,6 @@ else
|
||||
done
|
||||
fi
|
||||
|
||||
DISTRO=$(operating_system)
|
||||
ARCHITECTURE=$(architecture)
|
||||
if [ "${ARCHITECTURE}" = "arm64" ] || [ "${ARCHITECTURE}" = "aarch64" ]; then
|
||||
OS_SCRIPT=$DIR/environment/os/$DISTRO-arm.sh
|
||||
else
|
||||
@@ -111,6 +112,12 @@ if [[ "$setup_libs" == "true" ]]; then
|
||||
cd ..
|
||||
fi
|
||||
|
||||
# Fix for centos 7 during release
|
||||
if [ "${DISTRO}" = "centos-7" ] || [ "${DISTRO}" = "debian-11" ]; then
|
||||
python3 -m pip uninstall -y virtualenv
|
||||
python3 -m pip install virtualenv
|
||||
fi
|
||||
|
||||
# setup gql_behave dependencies
|
||||
setup_virtualenv tests/gql_behave
|
||||
|
||||
@@ -140,5 +147,11 @@ done;
|
||||
python3 -m pip install pre-commit
|
||||
python3 -m pre_commit install
|
||||
|
||||
# Install py format tools
|
||||
echo "Install black formatter"
|
||||
python3 -m pip install black==22.8.*
|
||||
echo "Install isort"
|
||||
python3 -m pip install isort==5.10.*
|
||||
|
||||
# Link `include/mgp.py` with `release/mgp/mgp.py`
|
||||
ln -v -f include/mgp.py release/mgp/mgp.py
|
||||
|
||||
@@ -208,7 +208,7 @@ pymgclient_tag="4f85c179e56302d46a1e3e2cf43509db65f062b3" # (2021-01-15)
|
||||
repo_clone_try_double "${primary_urls[pymgclient]}" "${secondary_urls[pymgclient]}" "pymgclient" "$pymgclient_tag"
|
||||
|
||||
# mgconsole
|
||||
mgconsole_tag="v1.1.0" # (2021-10-07)
|
||||
mgconsole_tag="v1.3.0" # (2022-11-20)
|
||||
repo_clone_try_double "${primary_urls[mgconsole]}" "${secondary_urls[mgconsole]}" "mgconsole" "$mgconsole_tag" true
|
||||
|
||||
spdlog_tag="v1.9.2" # (2021-08-12)
|
||||
|
||||
@@ -36,7 +36,7 @@ ADDITIONAL USE GRANT: You may use the Licensed Work in accordance with the
|
||||
3. using the Licensed Work to create a work or solution
|
||||
which competes (or might reasonably be expected to
|
||||
compete) with the Licensed Work.
|
||||
CHANGE DATE: 2026-27-04
|
||||
CHANGE DATE: 2027-26-01
|
||||
CHANGE LICENSE: Apache License, Version 2.0
|
||||
|
||||
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.
|
||||
|
||||
@@ -2,8 +2,8 @@ MEMGRAPH
|
||||
ENTERPRISE LICENCE AGREEMENT
|
||||
|
||||
|
||||
Memgraph Limited is registered in England under registration 10195084 and has its registered office at Suite 4,
|
||||
Ironstone House, Ironstone Way, Brixworth, Northampton, NN6 9UD (“Memgraph”).
|
||||
Memgraph Limited is registered in England under registration 10195084 and has its registered office at 90a High Street,
|
||||
Hertfordshire, Berkhamsted, HP4 2BL United Kingdom ("Memgraph").
|
||||
|
||||
|
||||
Memgraph agrees to license and/or grant you (the “Customer”) access to the Software ( as defined below) and provide
|
||||
|
||||
12
pyproject.toml
Normal file
12
pyproject.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
include = '\.pyi?$'
|
||||
extend-exclude = '''
|
||||
/(
|
||||
| .git
|
||||
| .__pycache__
|
||||
| build
|
||||
| libs
|
||||
| .cache
|
||||
)/
|
||||
'''
|
||||
@@ -69,7 +69,7 @@ extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *mem
|
||||
try {
|
||||
mgp::memory = memory;
|
||||
|
||||
AddProcedure(SampleReadProc, "return_true", mgp::ProdecureType::Read,
|
||||
AddProcedure(SampleReadProc, "return_true", mgp::ProcedureType::Read,
|
||||
{mgp::Parameter("param_1", mgp::Type::Int), mgp::Parameter("param_2", mgp::Type::Double, 2.3)},
|
||||
{mgp::Return("out", mgp::Type::Bool)}, module, memory);
|
||||
} catch (const std::exception &e) {
|
||||
@@ -79,7 +79,7 @@ extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *mem
|
||||
try {
|
||||
mgp::memory = memory;
|
||||
|
||||
mgp::AddProcedure(AddXNodes, "add_x_nodes", mgp::ProdecureType::Write, {mgp::Parameter("param_1", mgp::Type::Int)},
|
||||
mgp::AddProcedure(AddXNodes, "add_x_nodes", mgp::ProcedureType::Write, {mgp::Parameter("param_1", mgp::Type::Int)},
|
||||
{}, module, memory);
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
|
||||
@@ -67,7 +67,7 @@ It aims to deliver developers the speed, simplicity and scale required to build
|
||||
the next generation of applications driver by real-time connected data.")
|
||||
# Add `openssl` package to dependencies list. Used to generate SSL certificates.
|
||||
# We also depend on `python3` because we embed it in Memgraph.
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.0.0, curl >= 7.29.0, python3 >= 3.5.0, libstdc >= 6, logrotate")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.0.0, curl >= 7.29.0, python3 >= 3.5.0, libstdc++ >= 6, logrotate")
|
||||
|
||||
# All variables must be set before including.
|
||||
include(CPack)
|
||||
|
||||
1
release/mgp/.gitignore
vendored
1
release/mgp/.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.venv
|
||||
dist
|
||||
mgp.py
|
||||
poetry.lock
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
# mgp
|
||||
|
||||
PyPi package used for type hinting when creating MAGE modules. The get started
|
||||
using MAGE repository checkout the repository here: https://github.com/memgraph/mage.
|
||||
PyPi package used for type hinting when creating query modules. Repository of already available query modules is called [MAGE](https://github.com/memgraph/mage).
|
||||
|
||||
## 🎬 Get started
|
||||
|
||||
To learn more, head over to the [docs for the query modules Python API](https://memgraph.com/docs/memgraph/reference-guide/query-modules/api/python-api). To get started with query modules, check out the [how-to guide](https://memgraph.com/docs/memgraph/how-to-guides/query-modules) on Memgraph docs.
|
||||
|
||||
## 🔢 Versioning
|
||||
|
||||
- mgp v1.1 is compatible with Memgraph >= 2.4.0
|
||||
|
||||
@@ -257,3 +257,11 @@ class _MODULE:
|
||||
@staticmethod
|
||||
def add_function(wrapper):
|
||||
pass
|
||||
|
||||
|
||||
class SOURCE_TYPE_KAFKA:
|
||||
pass
|
||||
|
||||
|
||||
class SOURCE_TYPE_PULSAR:
|
||||
pass
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[tool.poetry]
|
||||
name = "mgp"
|
||||
version = "1.0.0"
|
||||
version = "1.1.1"
|
||||
description = "Memgraph's module for developing MAGE modules. Used only for type hinting!"
|
||||
authors = [
|
||||
"MasterMedo <mislav.vuletic@gmail.com>",
|
||||
"jbajic <jure.bajic@memgraph.io>",
|
||||
"katarinasupe <katarina.supe@memgraph.io>",
|
||||
"jbajic <jure.bajic@memgraph.io>",
|
||||
"antejavor <ante.javor@memgraph.io>",
|
||||
"antaljanosbenjamin <benjamin.antal@memgraph.io>",
|
||||
"MasterMedo <mislav.vuletic@gmail.com>",
|
||||
]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
|
||||
11
release/package/arm-builders.yml
Normal file
11
release/package/arm-builders.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
debian-11-arm:
|
||||
build:
|
||||
context: debian-11-arm
|
||||
container_name: "mgbuild_debian-11-arm"
|
||||
ubuntu-2204-arm:
|
||||
build:
|
||||
context: ubuntu-22.04-arm
|
||||
container_name: "mgbuild_ubuntu-22.04-arm"
|
||||
@@ -28,3 +28,7 @@ services:
|
||||
build:
|
||||
context: ubuntu-22.04
|
||||
container_name: "mgbuild_ubuntu-22.04"
|
||||
mgbuild_fedora-36:
|
||||
build:
|
||||
context: fedora-36
|
||||
container_name: "mgbuild_fedora-36"
|
||||
|
||||
15
release/package/fedora-36/Dockerfile
Normal file
15
release/package/fedora-36/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM fedora:36
|
||||
|
||||
ARG TOOLCHAIN_VERSION
|
||||
|
||||
# Stops tzdata interactive configuration.
|
||||
RUN yum -y update \
|
||||
&& yum install -y wget git
|
||||
# Do NOT be smart here and clean the cache because the container is used in the
|
||||
# stateful context.
|
||||
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-fedora-36-x86_64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
@@ -3,7 +3,7 @@
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
SUPPORTED_OS=(centos-7 centos-9 debian-10 debian-11 ubuntu-18.04 ubuntu-20.04 ubuntu-22.04 debian-11-arm)
|
||||
SUPPORTED_OS=(centos-7 centos-9 debian-10 debian-11 ubuntu-18.04 ubuntu-20.04 ubuntu-22.04 debian-11-arm fedora-36 ubuntu-22.04-arm)
|
||||
PROJECT_ROOT="$SCRIPT_DIR/../.."
|
||||
TOOLCHAIN_VERSION="toolchain-v4"
|
||||
ACTIVATE_TOOLCHAIN="source /opt/${TOOLCHAIN_VERSION}/activate"
|
||||
@@ -23,9 +23,9 @@ make_package () {
|
||||
echo "Building Memgraph for $os on $build_container..."
|
||||
|
||||
package_command=""
|
||||
if [[ "$os" =~ ^"centos".* ]]; then
|
||||
if [[ "$os" =~ ^"centos".* ]] || [[ "$os" =~ ^"fedora".* ]]; then
|
||||
docker exec "$build_container" bash -c "yum -y update"
|
||||
package_command=" cpack -G RPM --config ../CPackConfig.cmake && rpmlint memgraph*.rpm "
|
||||
package_command=" cpack -G RPM --config ../CPackConfig.cmake && rpmlint --file='../../release/rpm/rpmlintrc' memgraph*.rpm "
|
||||
fi
|
||||
if [[ "$os" =~ ^"debian".* ]]; then
|
||||
docker exec "$build_container" bash -c "apt update"
|
||||
@@ -72,9 +72,11 @@ make_package () {
|
||||
docker exec "$build_container" bash -c "/memgraph/environment/os/$os.sh install MEMGRAPH_BUILD_DEPS"
|
||||
|
||||
echo "Building targeted package..."
|
||||
# Fix issue with git marking directory as not safe
|
||||
docker exec "$build_container" bash -c "cd /memgraph && git config --global --add safe.directory '*'"
|
||||
docker exec "$build_container" bash -c "cd /memgraph && $ACTIVATE_TOOLCHAIN && ./init"
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && rm -rf ./*"
|
||||
if [[ "$os" == "debian-11-arm" ]]; then
|
||||
if [[ "$os" =~ "-arm" ]]; then
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && cmake -DCMAKE_BUILD_TYPE=release -DMG_ARCH="ARM64" $telemetry_id_override_flag .."
|
||||
else
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && cmake -DCMAKE_BUILD_TYPE=release $telemetry_id_override_flag .."
|
||||
|
||||
17
release/package/ubuntu-22.04-arm/Dockerfile
Normal file
17
release/package/ubuntu-22.04-arm/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG TOOLCHAIN_VERSION
|
||||
|
||||
# Stops tzdata interactive configuration.
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt update && apt install -y \
|
||||
ca-certificates wget git
|
||||
# Do NOT be smart here and clean the cache because the container is used in the
|
||||
# stateful context.
|
||||
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-ubuntu-22.04-arm64.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
@@ -30,7 +30,7 @@ BuildRequires: systemd
|
||||
# This is needed to prevent Python compilation errors when building the RPM
|
||||
# package
|
||||
# https://github.com/scylladb/scylla/issues/2235
|
||||
%if 0%{?rhel} < 8
|
||||
%if 0%{?rhel} && 0%{?rhel} < 8
|
||||
%global __os_install_post \
|
||||
/usr/lib/rpm/redhat/brp-compress \
|
||||
%{!?__debug_package:\
|
||||
@@ -40,7 +40,9 @@ BuildRequires: systemd
|
||||
/usr/lib/rpm/redhat/brp-strip-static-archive %{__strip} \
|
||||
%{!?__jar_repack:/usr/lib/rpm/redhat/brp-java-repack-jars} \
|
||||
%{nil}
|
||||
%else
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora} && 0%{?fedora} < 35
|
||||
%global __os_install_post \
|
||||
/usr/lib/rpm/brp-compress \
|
||||
%{!?__debug_package:\
|
||||
|
||||
4
release/rpm/rpmlintrc
Normal file
4
release/rpm/rpmlintrc
Normal file
@@ -0,0 +1,4 @@
|
||||
# from https://github.com/google/earthenterprise/blob/master/earth_enterprise/rpmlintrc
|
||||
|
||||
# We are not packaging log dir
|
||||
addFilter("E: logrotate-log-dir-not-packaged")
|
||||
@@ -15,10 +15,11 @@ add_subdirectory(query)
|
||||
add_subdirectory(glue)
|
||||
add_subdirectory(slk)
|
||||
add_subdirectory(rpc)
|
||||
add_subdirectory(license)
|
||||
add_subdirectory(auth)
|
||||
|
||||
if (MG_ENTERPRISE)
|
||||
add_subdirectory(audit)
|
||||
if(MG_ENTERPRISE)
|
||||
add_subdirectory(audit)
|
||||
endif()
|
||||
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
||||
@@ -36,68 +37,76 @@ set(mg_single_node_v2_sources
|
||||
)
|
||||
|
||||
set(mg_single_node_v2_libs stdc++fs Threads::Threads
|
||||
telemetry_lib mg-query mg-communication mg-memory mg-utils mg-auth mg-license mg-settings mg-glue)
|
||||
if (MG_ENTERPRISE)
|
||||
# These are enterprise subsystems
|
||||
set(mg_single_node_v2_libs ${mg_single_node_v2_libs} mg-audit)
|
||||
mg-telemetry mg-query mg-communication mg-memory mg-utils mg-auth mg-license mg-settings mg-glue)
|
||||
|
||||
if(MG_ENTERPRISE)
|
||||
# These are enterprise subsystems
|
||||
set(mg_single_node_v2_libs ${mg_single_node_v2_libs} mg-audit)
|
||||
endif()
|
||||
|
||||
# memgraph main executable
|
||||
add_executable(memgraph ${mg_single_node_v2_sources})
|
||||
target_include_directories(memgraph PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(memgraph ${mg_single_node_v2_libs})
|
||||
|
||||
# NOTE: `include/mg_procedure.syms` describes a pattern match for symbols which
|
||||
# should be dynamically exported, so that `dlopen` can correctly link the
|
||||
# symbols in custom procedure module libraries.
|
||||
target_link_libraries(memgraph "-Wl,--dynamic-list=${CMAKE_SOURCE_DIR}/include/mg_procedure.syms")
|
||||
set_target_properties(memgraph PROPERTIES
|
||||
# Set the executable output name to include version information.
|
||||
OUTPUT_NAME "memgraph-${MEMGRAPH_VERSION}_${CMAKE_BUILD_TYPE}"
|
||||
# Output the executable in main binary dir.
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# Set the executable output name to include version information.
|
||||
OUTPUT_NAME "memgraph-${MEMGRAPH_VERSION}_${CMAKE_BUILD_TYPE}"
|
||||
|
||||
# Output the executable in main binary dir.
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# Create symlink to the built executable.
|
||||
add_custom_command(TARGET memgraph POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE:memgraph> ${CMAKE_BINARY_DIR}/memgraph
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/memgraph
|
||||
COMMENT "Creating symlink to memgraph executable")
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE:memgraph> ${CMAKE_BINARY_DIR}/memgraph
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/memgraph
|
||||
COMMENT "Creating symlink to memgraph executable")
|
||||
|
||||
# Emulate the installed python_support, by creating a symlink
|
||||
add_custom_command(TARGET memgraph POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/python_support
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/python_support
|
||||
COMMENT "Creating symlink for python_support")
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/python_support
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/python_support
|
||||
COMMENT "Creating symlink for python_support")
|
||||
|
||||
# Strip the executable in release build.
|
||||
if (lower_build_type STREQUAL "release")
|
||||
add_custom_command(TARGET memgraph POST_BUILD
|
||||
COMMAND strip -s $<TARGET_FILE:memgraph>
|
||||
COMMENT "Stripping symbols and sections from memgraph")
|
||||
if(lower_build_type STREQUAL "release")
|
||||
add_custom_command(TARGET memgraph POST_BUILD
|
||||
COMMAND strip -s $<TARGET_FILE:memgraph>
|
||||
COMMENT "Stripping symbols and sections from memgraph")
|
||||
endif()
|
||||
|
||||
# Generate the configuration file.
|
||||
add_custom_command(TARGET memgraph POST_BUILD
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/config/generate.py
|
||||
${CMAKE_BINARY_DIR}/memgraph
|
||||
${CMAKE_BINARY_DIR}/config/memgraph.conf
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/config/generate.py
|
||||
${CMAKE_SOURCE_DIR}/config/flags.yaml
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/config/memgraph.conf
|
||||
COMMENT "Generating memgraph configuration file")
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/config/generate.py
|
||||
${CMAKE_BINARY_DIR}/memgraph
|
||||
${CMAKE_BINARY_DIR}/config/memgraph.conf
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/config/generate.py
|
||||
${CMAKE_SOURCE_DIR}/config/flags.yaml
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/config/memgraph.conf
|
||||
COMMENT "Generating memgraph configuration file")
|
||||
|
||||
# Everything here is under "memgraph" install component.
|
||||
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "memgraph")
|
||||
|
||||
# TODO: Default directory permissions to 755
|
||||
# NOTE: This is added in CMake 3.11, so enable it then
|
||||
#set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
|
||||
# OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ WORLD_READ)
|
||||
# set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
|
||||
# OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ WORLD_READ)
|
||||
|
||||
# Install and rename executable to just 'memgraph' Since we have to rename,
|
||||
# we cannot use the recommended `install(TARGETS ...)`.
|
||||
install(PROGRAMS $<TARGET_FILE:memgraph>
|
||||
DESTINATION lib/memgraph RENAME memgraph)
|
||||
|
||||
# Install Python source for supporting our embedded Python.
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/include/mgp.py
|
||||
DESTINATION lib/memgraph/python_support)
|
||||
|
||||
# Install the includes file for writing custom procedures in C and C++>
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/include/mg_procedure.h
|
||||
DESTINATION include/memgraph)
|
||||
@@ -107,9 +116,11 @@ install(FILES ${CMAKE_SOURCE_DIR}/include/mg_exceptions.hpp
|
||||
DESTINATION include/memgraph)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/include/mgp.hpp
|
||||
DESTINATION include/memgraph)
|
||||
|
||||
# Install the config file (must use absolute path).
|
||||
install(FILES ${CMAKE_BINARY_DIR}/config/memgraph.conf
|
||||
DESTINATION /etc/memgraph RENAME memgraph.conf)
|
||||
|
||||
# Install logrotate configuration (must use absolute path).
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/release/logrotate.conf
|
||||
DESTINATION /etc/logrotate.d RENAME memgraph)
|
||||
@@ -125,15 +136,14 @@ install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}/var/log/memgraph
|
||||
# ----------------------------------------------------------------------------
|
||||
# Memgraph CSV Import Tool Executable
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
add_executable(mg_import_csv mg_import_csv.cpp)
|
||||
target_link_libraries(mg_import_csv mg-storage-v2)
|
||||
|
||||
# Strip the executable in release build.
|
||||
if (lower_build_type STREQUAL "release")
|
||||
add_custom_command(TARGET mg_import_csv POST_BUILD
|
||||
COMMAND strip -s mg_import_csv
|
||||
COMMENT "Stripping symbols and sections from mg_import_csv")
|
||||
if(lower_build_type STREQUAL "release")
|
||||
add_custom_command(TARGET mg_import_csv POST_BUILD
|
||||
COMMAND strip -s mg_import_csv
|
||||
COMMENT "Stripping symbols and sections from mg_import_csv")
|
||||
endif()
|
||||
|
||||
install(TARGETS mg_import_csv RUNTIME DESTINATION bin)
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "auth/exceptions.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/settings.hpp"
|
||||
@@ -68,10 +68,9 @@ Auth::Auth(const std::string &storage_directory) : storage_(storage_directory),
|
||||
|
||||
std::optional<User> Auth::Authenticate(const std::string &username, const std::string &password) {
|
||||
if (module_.IsUsed()) {
|
||||
const auto license_check_result = utils::license::global_license_checker.IsValidLicense(utils::global_settings);
|
||||
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
||||
if (license_check_result.HasError()) {
|
||||
spdlog::warn(
|
||||
utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "authentication modules"));
|
||||
spdlog::warn(license::LicenseCheckErrorToString(license_check_result.GetError(), "authentication modules"));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
#include "auth/crypto.hpp"
|
||||
#include "auth/exceptions.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "utils/cast.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/settings.hpp"
|
||||
#include "utils/string.hpp"
|
||||
@@ -242,7 +242,7 @@ FineGrainedAccessPermissions::FineGrainedAccessPermissions(const std::unordered_
|
||||
|
||||
PermissionLevel FineGrainedAccessPermissions::Has(const std::string &permission,
|
||||
const FineGrainedPermission fine_grained_permission) const {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return PermissionLevel::GRANT;
|
||||
}
|
||||
const auto concrete_permission = std::invoke([&]() -> uint64_t {
|
||||
@@ -281,7 +281,7 @@ void FineGrainedAccessPermissions::Revoke(const std::string &permission) {
|
||||
}
|
||||
|
||||
nlohmann::json FineGrainedAccessPermissions::Serialize() const {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return {};
|
||||
}
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
@@ -294,7 +294,7 @@ FineGrainedAccessPermissions FineGrainedAccessPermissions::Deserialize(const nlo
|
||||
if (!data.is_object()) {
|
||||
throw AuthException("Couldn't load permissions data!");
|
||||
}
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return FineGrainedAccessPermissions{};
|
||||
}
|
||||
std::optional<uint64_t> global_permission;
|
||||
@@ -347,7 +347,7 @@ const FineGrainedAccessPermissions &FineGrainedAccessHandler::edge_type_permissi
|
||||
FineGrainedAccessPermissions &FineGrainedAccessHandler::edge_type_permissions() { return edge_type_permissions_; }
|
||||
|
||||
nlohmann::json FineGrainedAccessHandler::Serialize() const {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return {};
|
||||
}
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
@@ -363,7 +363,7 @@ FineGrainedAccessHandler FineGrainedAccessHandler::Deserialize(const nlohmann::j
|
||||
if (!data["label_permissions"].is_object() || !data["edge_type_permissions"].is_object()) {
|
||||
throw AuthException("Couldn't load label_permissions or edge_type_permissions data!");
|
||||
}
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return FineGrainedAccessHandler{};
|
||||
}
|
||||
auto label_permissions = FineGrainedAccessPermissions::Deserialize(data["label_permissions"]);
|
||||
@@ -414,7 +414,7 @@ nlohmann::json Role::Serialize() const {
|
||||
data["rolename"] = rolename_;
|
||||
data["permissions"] = permissions_.Serialize();
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
data["fine_grained_access_handler"] = fine_grained_access_handler_.Serialize();
|
||||
} else {
|
||||
data["fine_grained_access_handler"] = {};
|
||||
@@ -432,7 +432,7 @@ Role Role::Deserialize(const nlohmann::json &data) {
|
||||
}
|
||||
auto permissions = Permissions::Deserialize(data["permissions"]);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
if (!data["fine_grained_access_handler"].is_object()) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
}
|
||||
@@ -445,7 +445,7 @@ Role Role::Deserialize(const nlohmann::json &data) {
|
||||
|
||||
bool operator==(const Role &first, const Role &second) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return first.rolename_ == second.rolename_ && first.permissions_ == second.permissions_ &&
|
||||
first.fine_grained_access_handler_ == second.fine_grained_access_handler_;
|
||||
}
|
||||
@@ -483,13 +483,13 @@ void User::UpdatePassword(const std::optional<std::string> &password) {
|
||||
}
|
||||
|
||||
if (FLAGS_auth_password_strength_regex != default_password_regex) {
|
||||
if (const auto license_check_result = utils::license::global_license_checker.IsValidLicense(utils::global_settings);
|
||||
if (const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
||||
license_check_result.HasError()) {
|
||||
throw AuthException(
|
||||
"Custom password regex is a Memgraph Enterprise feature. Please set the config "
|
||||
"(\"--auth-password-strength-regex\") to its default value (\"{}\") or remove the flag.\n{}",
|
||||
default_password_regex,
|
||||
utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "password regex"));
|
||||
license::LicenseCheckErrorToString(license_check_result.GetError(), "password regex"));
|
||||
}
|
||||
}
|
||||
std::regex re(FLAGS_auth_password_strength_regex);
|
||||
@@ -517,7 +517,7 @@ Permissions User::GetPermissions() const {
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
FineGrainedAccessPermissions User::GetFineGrainedAccessLabelPermissions() const {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return FineGrainedAccessPermissions{};
|
||||
}
|
||||
|
||||
@@ -530,7 +530,7 @@ FineGrainedAccessPermissions User::GetFineGrainedAccessLabelPermissions() const
|
||||
}
|
||||
|
||||
FineGrainedAccessPermissions User::GetFineGrainedAccessEdgeTypePermissions() const {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return FineGrainedAccessPermissions{};
|
||||
}
|
||||
if (role_) {
|
||||
@@ -563,7 +563,7 @@ nlohmann::json User::Serialize() const {
|
||||
data["password_hash"] = password_hash_;
|
||||
data["permissions"] = permissions_.Serialize();
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
data["fine_grained_access_handler"] = fine_grained_access_handler_.Serialize();
|
||||
} else {
|
||||
data["fine_grained_access_handler"] = {};
|
||||
@@ -582,7 +582,7 @@ User User::Deserialize(const nlohmann::json &data) {
|
||||
}
|
||||
auto permissions = Permissions::Deserialize(data["permissions"]);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
if (!data["fine_grained_access_handler"].is_object()) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
}
|
||||
@@ -595,7 +595,7 @@ User User::Deserialize(const nlohmann::json &data) {
|
||||
|
||||
bool operator==(const User &first, const User &second) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return first.username_ == second.username_ && first.password_hash_ == second.password_hash_ &&
|
||||
first.permissions_ == second.permissions_ && first.role_ == second.role_ &&
|
||||
first.fine_grained_access_handler_ == second.fine_grained_access_handler_;
|
||||
|
||||
@@ -90,7 +90,7 @@ QueryData Client::Execute(const std::string &query, const std::map<std::string,
|
||||
// It is super critical from performance point of view to send the pull message right after the run message. Otherwise
|
||||
// the performance will degrade multiple magnitudes.
|
||||
encoder_.MessageRun(query, parameters, {});
|
||||
encoder_.MessagePull({});
|
||||
encoder_.MessagePull({{"n", Value(-1)}});
|
||||
|
||||
spdlog::debug("Reading run message response");
|
||||
Signature signature{};
|
||||
@@ -146,9 +146,10 @@ QueryData Client::Execute(const std::string &query, const std::map<std::string,
|
||||
throw ServerMalformedDataException();
|
||||
}
|
||||
|
||||
auto &header = fields.ValueMap();
|
||||
|
||||
QueryData ret{{}, std::move(records), std::move(metadata.ValueMap())};
|
||||
|
||||
auto &header = fields.ValueMap();
|
||||
if (header.find("fields") == header.end()) {
|
||||
throw ServerMalformedDataException();
|
||||
}
|
||||
@@ -164,6 +165,10 @@ QueryData Client::Execute(const std::string &query, const std::map<std::string,
|
||||
ret.fields.emplace_back(std::move(field_item.ValueString()));
|
||||
}
|
||||
|
||||
if (header.contains("qid")) {
|
||||
ret.metadata["qid"] = header["qid"];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -73,40 +73,6 @@ inline std::pair<std::string, std::string> ExceptionToErrorMessage(const std::ex
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename TSession>
|
||||
State HandleRun(TSession &session, const State state, const Value &query, const Value ¶ms) {
|
||||
if (state != State::Idle) {
|
||||
// Client could potentially recover if we move to error state, but there is
|
||||
// no legitimate situation in which well working client would end up in this
|
||||
// situation.
|
||||
spdlog::trace("Unexpected RUN command!");
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
|
||||
|
||||
spdlog::debug("[Run] '{}'", query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
const auto [header, qid] = session.Interpret(query.ValueString(), params.ValueMap());
|
||||
// Convert std::string to Value
|
||||
std::vector<Value> vec;
|
||||
std::map<std::string, Value> data;
|
||||
vec.reserve(header.size());
|
||||
for (auto &i : header) vec.emplace_back(std::move(i));
|
||||
data.emplace("fields", std::move(vec));
|
||||
// Send the header.
|
||||
if (!session.encoder_.MessageSuccess(data)) {
|
||||
spdlog::trace("Couldn't send query header!");
|
||||
return State::Close;
|
||||
}
|
||||
return State::Result;
|
||||
} catch (const std::exception &e) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool is_pull, typename TSession>
|
||||
State HandlePullDiscard(TSession &session, std::optional<int> n, std::optional<int> qid) {
|
||||
try {
|
||||
@@ -229,7 +195,36 @@ State HandleRunV1(TSession &session, const State state, const Marker marker) {
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
return details::HandleRun(session, state, query, params);
|
||||
if (state != State::Idle) {
|
||||
// Client could potentially recover if we move to error state, but there is
|
||||
// no legitimate situation in which well working client would end up in this
|
||||
// situation.
|
||||
spdlog::trace("Unexpected RUN command!");
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
|
||||
|
||||
spdlog::debug("[Run] '{}'", query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
const auto [header, qid] = session.Interpret(query.ValueString(), params.ValueMap());
|
||||
// Convert std::string to Value
|
||||
std::vector<Value> vec;
|
||||
std::map<std::string, Value> data;
|
||||
vec.reserve(header.size());
|
||||
for (auto &i : header) vec.emplace_back(std::move(i));
|
||||
data.emplace("fields", std::move(vec));
|
||||
// Send the header.
|
||||
if (!session.encoder_.MessageSuccess(data)) {
|
||||
spdlog::trace("Couldn't send query header!");
|
||||
return State::Close;
|
||||
}
|
||||
return State::Result;
|
||||
} catch (const std::exception &e) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TSession>
|
||||
@@ -257,7 +252,40 @@ State HandleRunV4(TSession &session, const State state, const Marker marker) {
|
||||
spdlog::trace("Couldn't read extra field!");
|
||||
}
|
||||
|
||||
return details::HandleRun(session, state, query, params);
|
||||
if (state != State::Idle) {
|
||||
// Client could potentially recover if we move to error state, but there is
|
||||
// no legitimate situation in which well working client would end up in this
|
||||
// situation.
|
||||
spdlog::trace("Unexpected RUN command!");
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
|
||||
|
||||
spdlog::debug("[Run] '{}'", query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
const auto [header, qid] = session.Interpret(query.ValueString(), params.ValueMap());
|
||||
// Convert std::string to Value
|
||||
std::vector<Value> vec;
|
||||
std::map<std::string, Value> data;
|
||||
vec.reserve(header.size());
|
||||
for (auto &i : header) vec.emplace_back(std::move(i));
|
||||
data.emplace("fields", std::move(vec));
|
||||
if (qid.has_value()) {
|
||||
data.emplace("qid", Value{*qid});
|
||||
}
|
||||
|
||||
// Send the header.
|
||||
if (!session.encoder_.MessageSuccess(data)) {
|
||||
spdlog::trace("Couldn't send query header!");
|
||||
return State::Close;
|
||||
}
|
||||
return State::Result;
|
||||
} catch (const std::exception &e) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TSession>
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include "auth/auth.hpp"
|
||||
#include "auth/models.hpp"
|
||||
#include "glue/auth.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
@@ -23,7 +23,7 @@ namespace {
|
||||
bool IsUserAuthorizedLabels(const memgraph::auth::User &user, const memgraph::query::DbAccessor *dba,
|
||||
const std::vector<memgraph::storage::LabelId> &labels,
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return std::all_of(labels.begin(), labels.end(), [dba, &user, fine_grained_privilege](const auto &label) {
|
||||
@@ -35,7 +35,7 @@ bool IsUserAuthorizedLabels(const memgraph::auth::User &user, const memgraph::qu
|
||||
|
||||
bool IsUserAuthorizedGloballyLabels(const memgraph::auth::User &user,
|
||||
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return user.GetFineGrainedAccessLabelPermissions().Has(memgraph::auth::kAsterisk, fine_grained_permission) ==
|
||||
@@ -44,7 +44,7 @@ bool IsUserAuthorizedGloballyLabels(const memgraph::auth::User &user,
|
||||
|
||||
bool IsUserAuthorizedGloballyEdges(const memgraph::auth::User &user,
|
||||
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return user.GetFineGrainedAccessEdgeTypePermissions().Has(memgraph::auth::kAsterisk, fine_grained_permission) ==
|
||||
@@ -54,7 +54,7 @@ bool IsUserAuthorizedGloballyEdges(const memgraph::auth::User &user,
|
||||
bool IsUserAuthorizedEdgeType(const memgraph::auth::User &user, const memgraph::query::DbAccessor *dba,
|
||||
const memgraph::storage::EdgeTypeId &edgeType,
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return user.GetFineGrainedAccessEdgeTypePermissions().Has(
|
||||
@@ -87,7 +87,7 @@ bool AuthChecker::IsUserAuthorized(const std::optional<std::string> &username,
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::unique_ptr<memgraph::query::FineGrainedAuthChecker> AuthChecker::GetFineGrainedAuthChecker(
|
||||
const std::string &username, const memgraph::query::DbAccessor *dba) const {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
@@ -154,7 +154,7 @@ bool FineGrainedAuthChecker::Has(const memgraph::storage::EdgeTypeId &edge_type,
|
||||
|
||||
bool FineGrainedAuthChecker::HasGlobalPrivilegeOnVertices(
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return IsUserAuthorizedGloballyLabels(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||
@@ -162,7 +162,7 @@ bool FineGrainedAuthChecker::HasGlobalPrivilegeOnVertices(
|
||||
|
||||
bool FineGrainedAuthChecker::HasGlobalPrivilegeOnEdges(
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return IsUserAuthorizedGloballyEdges(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include "auth/models.hpp"
|
||||
#include "glue/auth.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "license/license.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -125,7 +125,7 @@ std::vector<FineGrainedPermissionForPrivilegeResult> GetFineGrainedPermissionFor
|
||||
const memgraph::auth::FineGrainedAccessPermissions &permissions, const std::string &permission_type,
|
||||
const std::string &user_or_role) {
|
||||
std::vector<FineGrainedPermissionForPrivilegeResult> fine_grained_permissions;
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return fine_grained_permissions;
|
||||
}
|
||||
const auto global_permission = permissions.GetGlobalPermission();
|
||||
@@ -166,7 +166,7 @@ std::vector<FineGrainedPermissionForPrivilegeResult> GetFineGrainedPermissionFor
|
||||
std::vector<std::vector<memgraph::query::TypedValue>> ConstructFineGrainedPrivilegesResult(
|
||||
const std::vector<FineGrainedPermissionForPrivilegeResult> &privileges) {
|
||||
std::vector<std::vector<memgraph::query::TypedValue>> grants;
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return {};
|
||||
}
|
||||
grants.reserve(privileges.size());
|
||||
@@ -182,7 +182,7 @@ std::vector<std::vector<memgraph::query::TypedValue>> ConstructFineGrainedPrivil
|
||||
|
||||
std::vector<std::vector<memgraph::query::TypedValue>> ShowFineGrainedUserPrivileges(
|
||||
const std::optional<memgraph::auth::User> &user) {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return {};
|
||||
}
|
||||
const auto &label_permissions = user->GetFineGrainedAccessLabelPermissions();
|
||||
@@ -201,7 +201,7 @@ std::vector<std::vector<memgraph::query::TypedValue>> ShowFineGrainedUserPrivile
|
||||
|
||||
std::vector<std::vector<memgraph::query::TypedValue>> ShowFineGrainedRolePrivileges(
|
||||
const std::optional<memgraph::auth::Role> &role) {
|
||||
if (!memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return {};
|
||||
}
|
||||
const auto &label_permissions = role->GetFineGrainedAccessLabelPermissions();
|
||||
@@ -231,13 +231,13 @@ AuthQueryHandler::AuthQueryHandler(
|
||||
bool AuthQueryHandler::CreateUser(const std::string &username, const std::optional<std::string> &password) {
|
||||
if (name_regex_string_ != kDefaultUserRoleRegex) {
|
||||
if (const auto license_check_result =
|
||||
memgraph::utils::license::global_license_checker.IsValidLicense(memgraph::utils::global_settings);
|
||||
memgraph::license::global_license_checker.IsEnterpriseValid(memgraph::utils::global_settings);
|
||||
license_check_result.HasError()) {
|
||||
throw memgraph::auth::AuthException(
|
||||
"Custom user/role regex is a Memgraph Enterprise feature. Please set the config "
|
||||
"(\"--auth-user-or-role-name-regex\") to its default value (\"{}\") or remove the flag.\n{}",
|
||||
kDefaultUserRoleRegex,
|
||||
memgraph::utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "user/role regex"));
|
||||
memgraph::license::LicenseCheckErrorToString(license_check_result.GetError(), "user/role regex"));
|
||||
}
|
||||
}
|
||||
if (!std::regex_match(username, name_regex_)) {
|
||||
@@ -473,20 +473,20 @@ std::vector<std::vector<memgraph::query::TypedValue>> AuthQueryHandler::GetPrivi
|
||||
if (user) {
|
||||
grants = ShowUserPrivileges(user);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
fine_grained_grants = ShowFineGrainedUserPrivileges(user);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
grants = ShowRolePrivileges(role);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
fine_grained_grants = ShowFineGrainedRolePrivileges(role);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
grants.insert(grants.end(), fine_grained_grants.begin(), fine_grained_grants.end());
|
||||
}
|
||||
#endif
|
||||
@@ -627,7 +627,7 @@ void AuthQueryHandler::EditPermissions(
|
||||
edit_permissions_fun(user->permissions(), permission);
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
for (const auto &label_privilege_collection : label_privileges) {
|
||||
edit_fine_grained_permissions_fun(user->fine_grained_access_handler().label_permissions(),
|
||||
label_privilege_collection);
|
||||
@@ -644,9 +644,9 @@ void AuthQueryHandler::EditPermissions(
|
||||
edit_permissions_fun(role->permissions(), permission);
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
for (const auto &label_privilege : label_privileges) {
|
||||
edit_fine_grained_permissions_fun(user->fine_grained_access_handler().label_permissions(), label_privilege);
|
||||
edit_fine_grained_permissions_fun(role->fine_grained_access_handler().label_permissions(), label_privilege);
|
||||
}
|
||||
for (const auto &edge_type_privilege : edge_type_privileges) {
|
||||
edit_fine_grained_permissions_fun(role->fine_grained_access_handler().edge_type_permissions(),
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
#include "glue/auth.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace memgraph::glue {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 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
|
||||
@@ -12,6 +12,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -61,3 +62,42 @@ inline void LoadConfig(const std::string &product_name) {
|
||||
for (int i = 0; i < custom_argc; ++i) free(custom_argv[i]);
|
||||
delete[] custom_argv;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> LoadUsernameAndPassword(const std::string &pass_file) {
|
||||
std::ifstream file(pass_file);
|
||||
if (file.fail()) {
|
||||
spdlog::warn("Problem with opening MG_PASSFILE, memgraph server will start without user");
|
||||
return {};
|
||||
}
|
||||
std::vector<std::string> result;
|
||||
|
||||
std::string line;
|
||||
std::getline(file, line);
|
||||
size_t pos = 0;
|
||||
std::string token;
|
||||
static constexpr std::string_view delimiter{":"};
|
||||
while ((pos = line.find(delimiter)) != std::string::npos) {
|
||||
if (line[pos - 1] == '\\') {
|
||||
line.erase(pos - 1, 1);
|
||||
token += line.substr(0, pos);
|
||||
line.erase(0, pos);
|
||||
|
||||
} else {
|
||||
token += line.substr(0, pos);
|
||||
result.push_back(token);
|
||||
line.erase(0, pos + delimiter.length());
|
||||
token = "";
|
||||
}
|
||||
}
|
||||
result.push_back(line);
|
||||
file.close();
|
||||
|
||||
if (result.size() != 2) {
|
||||
spdlog::warn(
|
||||
"Wrong data format. Data should be store in format: username:password, memgraph server will start without "
|
||||
"user");
|
||||
return {};
|
||||
}
|
||||
|
||||
return {result[0], result[1]};
|
||||
}
|
||||
|
||||
6
src/license/CMakeLists.txt
Normal file
6
src/license/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
set(license_src_files
|
||||
license_sender.cpp
|
||||
license.cpp)
|
||||
add_library(mg-license STATIC ${license_src_files})
|
||||
|
||||
target_link_libraries(mg-license mg-settings mg-utils mg-requests spdlog::spdlog)
|
||||
@@ -9,17 +9,19 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "utils/license.hpp"
|
||||
#include "license/license.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <charconv>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "slk/serialization.hpp"
|
||||
#include "utils/base64.hpp"
|
||||
#include "utils/cast.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
@@ -27,7 +29,7 @@
|
||||
#include "utils/spin_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
|
||||
namespace memgraph::utils::license {
|
||||
namespace memgraph::license {
|
||||
|
||||
namespace {
|
||||
inline constexpr std::string_view license_key_prefix = "mglk-";
|
||||
@@ -69,6 +71,17 @@ LicenseCheckResult IsValidLicenseInternal(const License &license, const std::str
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string LicenseTypeToString(const LicenseType license_type) {
|
||||
switch (license_type) {
|
||||
case LicenseType::ENTERPRISE: {
|
||||
return "enterprise";
|
||||
}
|
||||
case LicenseType::OEM: {
|
||||
return "oem";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterLicenseSettings(LicenseChecker &license_checker, utils::Settings &settings) {
|
||||
settings.RegisterSetting(std::string{kEnterpriseLicenseSettingKey}, "",
|
||||
[&] { license_checker.RevalidateLicense(settings); });
|
||||
@@ -81,7 +94,7 @@ LicenseChecker global_license_checker;
|
||||
|
||||
LicenseChecker::~LicenseChecker() { scheduler_.Stop(); }
|
||||
|
||||
std::pair<std::string, std::string> LicenseChecker::GetLicenseInfo(const utils::Settings &settings) const {
|
||||
std::pair<std::string, std::string> LicenseChecker::ExtractLicenseInfo(const utils::Settings &settings) const {
|
||||
if (license_info_override_) {
|
||||
spdlog::warn("Ignoring license info stored in the settings because a different source was specified.");
|
||||
return *license_info_override_;
|
||||
@@ -96,7 +109,7 @@ std::pair<std::string, std::string> LicenseChecker::GetLicenseInfo(const utils::
|
||||
}
|
||||
|
||||
void LicenseChecker::RevalidateLicense(const utils::Settings &settings) {
|
||||
const auto license_info = GetLicenseInfo(settings);
|
||||
const auto license_info = ExtractLicenseInfo(settings);
|
||||
RevalidateLicense(license_info.first, license_info.second);
|
||||
}
|
||||
|
||||
@@ -117,18 +130,7 @@ void LicenseChecker::RevalidateLicense(const std::string &license_key, const std
|
||||
return;
|
||||
}
|
||||
|
||||
struct PreviousLicenseInfo {
|
||||
PreviousLicenseInfo(std::string license_key, std::string organization_name)
|
||||
: license_key(std::move(license_key)), organization_name(std::move(organization_name)) {}
|
||||
|
||||
std::string license_key;
|
||||
std::string organization_name;
|
||||
bool is_valid{false};
|
||||
};
|
||||
|
||||
static utils::Synchronized<std::optional<PreviousLicenseInfo>, utils::SpinLock> previous_license_info;
|
||||
|
||||
auto locked_previous_license_info_ptr = previous_license_info.Lock();
|
||||
auto locked_previous_license_info_ptr = previous_license_info_.Lock();
|
||||
auto &locked_previous_license_info = *locked_previous_license_info_ptr;
|
||||
const bool same_license_info = locked_previous_license_info &&
|
||||
locked_previous_license_info->license_key == license_key &&
|
||||
@@ -140,7 +142,7 @@ void LicenseChecker::RevalidateLicense(const std::string &license_key, const std
|
||||
|
||||
locked_previous_license_info.emplace(license_key, organization_name);
|
||||
|
||||
const auto maybe_license = GetLicense(locked_previous_license_info->license_key);
|
||||
auto maybe_license = GetLicense(locked_previous_license_info->license_key);
|
||||
if (!maybe_license) {
|
||||
spdlog::warn(LicenseCheckErrorToString(LicenseCheckError::INVALID_LICENSE_KEY_STRING, "Enterprise features"));
|
||||
is_valid_.store(false, std::memory_order_relaxed);
|
||||
@@ -156,22 +158,30 @@ void LicenseChecker::RevalidateLicense(const std::string &license_key, const std
|
||||
spdlog::warn(LicenseCheckErrorToString(license_check_result.GetError(), "Enterprise features"));
|
||||
is_valid_.store(false, std::memory_order_relaxed);
|
||||
locked_previous_license_info->is_valid = false;
|
||||
license_type_ = maybe_license->type;
|
||||
set_memory_limit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!same_license_info) {
|
||||
spdlog::info("All Enterprise features are active.");
|
||||
license_type_ = maybe_license->type;
|
||||
if (license_type_ == LicenseType::ENTERPRISE) {
|
||||
spdlog::info("Enterprise license is active.");
|
||||
} else {
|
||||
spdlog::info("OEM license is active.");
|
||||
}
|
||||
is_valid_.store(true, std::memory_order_relaxed);
|
||||
locked_previous_license_info->is_valid = true;
|
||||
set_memory_limit(maybe_license->memory_limit);
|
||||
locked_previous_license_info->license = std::move(*maybe_license);
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseChecker::EnableTesting() {
|
||||
void LicenseChecker::EnableTesting(const LicenseType license_type) {
|
||||
enterprise_enabled_ = true;
|
||||
is_valid_.store(true, std::memory_order_relaxed);
|
||||
spdlog::info("All Enterprise features are activated for testing.");
|
||||
license_type_ = license_type;
|
||||
spdlog::info("The license type {} is set for testing.", LicenseTypeToString(license_type));
|
||||
}
|
||||
|
||||
void LicenseChecker::CheckEnvLicense() {
|
||||
@@ -216,20 +226,26 @@ std::string LicenseCheckErrorToString(LicenseCheckError error, const std::string
|
||||
"following query:\n"
|
||||
"SET DATABASE SETTING \"enterprise.license\" TO \"your-license-key\"",
|
||||
feature);
|
||||
case LicenseCheckError::NOT_ENTERPRISE_LICENSE:
|
||||
return fmt::format("Your license has an invalid type. To use {} you need to have an enterprise license. \n",
|
||||
feature);
|
||||
}
|
||||
}
|
||||
|
||||
LicenseCheckResult LicenseChecker::IsValidLicense(const utils::Settings &settings) const {
|
||||
LicenseCheckResult LicenseChecker::IsEnterpriseValid(const utils::Settings &settings) const {
|
||||
if (enterprise_enabled_) [[unlikely]] {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto license_info = GetLicenseInfo(settings);
|
||||
const auto license_info = ExtractLicenseInfo(settings);
|
||||
|
||||
const auto maybe_license = GetLicense(license_info.first);
|
||||
if (!maybe_license) {
|
||||
return LicenseCheckError::INVALID_LICENSE_KEY_STRING;
|
||||
}
|
||||
if (maybe_license->type != LicenseType::ENTERPRISE) {
|
||||
return LicenseCheckError::NOT_ENTERPRISE_LICENSE;
|
||||
}
|
||||
|
||||
return IsValidLicenseInternal(*maybe_license, license_info.second);
|
||||
}
|
||||
@@ -239,7 +255,13 @@ void LicenseChecker::StartBackgroundLicenseChecker(const utils::Settings &settin
|
||||
scheduler_.Run("licensechecker", std::chrono::minutes{5}, [&, this] { RevalidateLicense(settings); });
|
||||
}
|
||||
|
||||
bool LicenseChecker::IsValidLicenseFast() const { return is_valid_.load(std::memory_order_relaxed); }
|
||||
utils::Synchronized<std::optional<LicenseInfo>, utils::SpinLock> &LicenseChecker::GetLicenseInfo() {
|
||||
return previous_license_info_;
|
||||
}
|
||||
|
||||
bool LicenseChecker::IsEnterpriseValidFast() const {
|
||||
return license_type_ == LicenseType::ENTERPRISE && is_valid_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
std::string Encode(const License &license) {
|
||||
std::vector<uint8_t> buffer;
|
||||
@@ -252,9 +274,10 @@ std::string Encode(const License &license) {
|
||||
slk::Save(license.organization_name, &builder);
|
||||
slk::Save(license.valid_until, &builder);
|
||||
slk::Save(license.memory_limit, &builder);
|
||||
slk::Save(utils::UnderlyingCast(license.type), &builder);
|
||||
builder.Finalize();
|
||||
|
||||
return std::string{license_key_prefix} + base64_encode(buffer.data(), buffer.size());
|
||||
return std::string{license_key_prefix} + utils::base64_encode(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
std::optional<License> Decode(std::string_view license_key) {
|
||||
@@ -266,7 +289,7 @@ std::optional<License> Decode(std::string_view license_key) {
|
||||
|
||||
const auto decoded = std::invoke([license_key]() -> std::optional<std::string> {
|
||||
try {
|
||||
return base64_decode(license_key);
|
||||
return utils::base64_decode(license_key);
|
||||
} catch (const std::runtime_error & /*exception*/) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -284,10 +307,12 @@ std::optional<License> Decode(std::string_view license_key) {
|
||||
slk::Load(&valid_until, &reader);
|
||||
int64_t memory_limit{0};
|
||||
slk::Load(&memory_limit, &reader);
|
||||
return License{.organization_name = organization_name, .valid_until = valid_until, .memory_limit = memory_limit};
|
||||
std::underlying_type_t<LicenseType> license_type{0};
|
||||
slk::Load(&license_type, &reader);
|
||||
return {License{organization_name, valid_until, memory_limit, LicenseType(license_type)}};
|
||||
} catch (const slk::SlkReaderException &e) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace memgraph::utils::license
|
||||
} // namespace memgraph::license
|
||||
@@ -12,26 +12,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "utils/result.hpp"
|
||||
#include "utils/scheduler.hpp"
|
||||
#include "utils/settings.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
|
||||
namespace memgraph::utils::license {
|
||||
namespace memgraph::license {
|
||||
|
||||
enum class LicenseType : uint8_t { ENTERPRISE, OEM };
|
||||
|
||||
std::string LicenseTypeToString(LicenseType license_type);
|
||||
|
||||
struct License {
|
||||
License() = default;
|
||||
|
||||
License(std::string organization_name, int64_t valid_until, int64_t memory_limit, LicenseType license_type)
|
||||
: organization_name{std::move(organization_name)},
|
||||
valid_until{valid_until},
|
||||
memory_limit{memory_limit},
|
||||
type{license_type} {}
|
||||
|
||||
std::string organization_name;
|
||||
int64_t valid_until;
|
||||
int64_t memory_limit;
|
||||
LicenseType type;
|
||||
|
||||
bool operator==(const License &) const = default;
|
||||
};
|
||||
|
||||
struct LicenseInfo {
|
||||
LicenseInfo(std::string license_key, std::string organization_name)
|
||||
: license_key(std::move(license_key)), organization_name{std::move(organization_name)} {}
|
||||
|
||||
std::string license_key;
|
||||
std::string organization_name;
|
||||
bool is_valid{false};
|
||||
License license;
|
||||
};
|
||||
|
||||
inline constexpr std::string_view kEnterpriseLicenseSettingKey = "enterprise.license";
|
||||
inline constexpr std::string_view kOrganizationNameSettingKey = "organization.name";
|
||||
|
||||
enum class LicenseCheckError : uint8_t { INVALID_LICENSE_KEY_STRING, INVALID_ORGANIZATION_NAME, EXPIRED_LICENSE };
|
||||
enum class LicenseCheckError : uint8_t {
|
||||
INVALID_LICENSE_KEY_STRING,
|
||||
INVALID_ORGANIZATION_NAME,
|
||||
EXPIRED_LICENSE,
|
||||
NOT_ENTERPRISE_LICENSE
|
||||
};
|
||||
|
||||
std::string LicenseCheckErrorToString(LicenseCheckError error, std::string_view feature);
|
||||
|
||||
@@ -49,19 +80,25 @@ struct LicenseChecker {
|
||||
|
||||
void CheckEnvLicense();
|
||||
void SetLicenseInfoOverride(std::string license_key, std::string organization_name);
|
||||
void EnableTesting();
|
||||
LicenseCheckResult IsValidLicense(const utils::Settings &settings) const;
|
||||
bool IsValidLicenseFast() const;
|
||||
void EnableTesting(LicenseType license_type = LicenseType::ENTERPRISE);
|
||||
// Checks if license is valid and if enterprise is enabled
|
||||
LicenseCheckResult IsEnterpriseValid(const utils::Settings &settings) const;
|
||||
bool IsEnterpriseValidFast() const;
|
||||
|
||||
void StartBackgroundLicenseChecker(const utils::Settings &settings);
|
||||
|
||||
utils::Synchronized<std::optional<LicenseInfo>, utils::SpinLock> &GetLicenseInfo();
|
||||
|
||||
private:
|
||||
std::pair<std::string, std::string> GetLicenseInfo(const utils::Settings &settings) const;
|
||||
std::pair<std::string, std::string> ExtractLicenseInfo(const utils::Settings &settings) const;
|
||||
void RevalidateLicense(const utils::Settings &settings);
|
||||
void RevalidateLicense(const std::string &license_key, const std::string &organization_name);
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> license_info_override_;
|
||||
utils::Synchronized<std::optional<LicenseInfo>, utils::SpinLock> previous_license_info_{std::nullopt};
|
||||
bool enterprise_enabled_{false};
|
||||
std::atomic<bool> is_valid_{false};
|
||||
LicenseType license_type_;
|
||||
utils::Scheduler scheduler_;
|
||||
|
||||
friend void RegisterLicenseSettings(LicenseChecker &license_checker, utils::Settings &settings);
|
||||
@@ -74,4 +111,4 @@ std::string Encode(const License &license);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
extern LicenseChecker global_license_checker;
|
||||
} // namespace memgraph::utils::license
|
||||
} // namespace memgraph::license
|
||||
71
src/license/license_sender.cpp
Normal file
71
src/license/license_sender.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "license/license_sender.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "requests/requests.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
#include "utils/stat.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
#include "utils/system_info.hpp"
|
||||
#include "utils/timestamp.hpp"
|
||||
|
||||
namespace memgraph::license {
|
||||
|
||||
LicenseInfoSender::LicenseInfoSender(std::string url, std::string uuid, std::string machine_id, int64_t memory_limit,
|
||||
utils::Synchronized<std::optional<LicenseInfo>, utils::SpinLock> &license_info,
|
||||
std::chrono::seconds request_frequency)
|
||||
: url_{std::move(url)},
|
||||
uuid_{std::move(uuid)},
|
||||
machine_id_{std::move(machine_id)},
|
||||
memory_limit_{memory_limit},
|
||||
license_info_{license_info} {
|
||||
scheduler_.Run("LicenseCheck", request_frequency, [&] { SendData(); });
|
||||
}
|
||||
|
||||
LicenseInfoSender::~LicenseInfoSender() { scheduler_.Stop(); }
|
||||
|
||||
void LicenseInfoSender::SendData() {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
|
||||
license_info_.WithLock([&data, this](const auto &license_info) mutable {
|
||||
if (license_info && !license_info->organization_name.empty()) {
|
||||
const auto memory_info = utils::GetMemoryInfo();
|
||||
const auto memory_usage = utils::GetMemoryUsage();
|
||||
data = {{"run_id", uuid_},
|
||||
{"machine_id", machine_id_},
|
||||
{"type", "license-check"},
|
||||
{"license_type", LicenseTypeToString(license_info->license.type)},
|
||||
{"license_key", license_info->license_key},
|
||||
{"organization", license_info->organization_name},
|
||||
{"valid", license_info->is_valid},
|
||||
{"physical_memory_size", memory_info.memory},
|
||||
{"swap_memory_size", memory_info.swap},
|
||||
{"memory_used", memory_usage},
|
||||
{"runtime_memory_limit", memory_limit_},
|
||||
{"license_memory_limit", license_info->license.memory_limit},
|
||||
{"timestamp", utils::Timestamp::Now().SecWithNsecSinceTheEpoch()}};
|
||||
}
|
||||
});
|
||||
|
||||
if (data.empty()) {
|
||||
return;
|
||||
}
|
||||
if (!requests::RequestPostJson(url_, data,
|
||||
/* timeout_in_seconds = */ 2 * 60)) {
|
||||
spdlog::trace("Cannot send license information, enable {} availability!", url_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace memgraph::license
|
||||
50
src/license/license_sender.hpp
Normal file
50
src/license/license_sender.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <json/json.hpp>
|
||||
|
||||
#include "license/license.hpp"
|
||||
#include "utils/scheduler.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
|
||||
namespace memgraph::license {
|
||||
|
||||
class LicenseInfoSender final {
|
||||
public:
|
||||
LicenseInfoSender(std::string url, std::string uuid, std::string machine_id, int64_t memory_limit,
|
||||
utils::Synchronized<std::optional<LicenseInfo>, utils::SpinLock> &license_info,
|
||||
std::chrono::seconds request_frequency = std::chrono::seconds(8 * 60 * 60));
|
||||
|
||||
LicenseInfoSender(const LicenseInfoSender &) = delete;
|
||||
LicenseInfoSender(LicenseInfoSender &&) noexcept = delete;
|
||||
LicenseInfoSender &operator=(const LicenseInfoSender &) = delete;
|
||||
LicenseInfoSender &operator=(LicenseInfoSender &&) noexcept = delete;
|
||||
~LicenseInfoSender();
|
||||
|
||||
private:
|
||||
void SendData();
|
||||
|
||||
const std::string url_;
|
||||
const std::string uuid_;
|
||||
const std::string machine_id_;
|
||||
const int64_t memory_limit_;
|
||||
|
||||
utils::Synchronized<std::optional<LicenseInfo>, utils::SpinLock> &license_info_;
|
||||
utils::Scheduler scheduler_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::license
|
||||
107
src/memgraph.cpp
107
src/memgraph.cpp
@@ -26,6 +26,7 @@
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
#include <gflags/gflags.h>
|
||||
#include <spdlog/common.h>
|
||||
@@ -40,6 +41,8 @@
|
||||
#include "glue/auth_checker.hpp"
|
||||
#include "glue/auth_handler.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "license/license_sender.hpp"
|
||||
#include "py/py.hpp"
|
||||
#include "query/auth_checker.hpp"
|
||||
#include "query/discard_value_stream.hpp"
|
||||
@@ -57,7 +60,6 @@
|
||||
#include "utils/event_counter.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
#include "utils/message.hpp"
|
||||
@@ -68,6 +70,7 @@
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
#include "utils/sysinfo/memory.hpp"
|
||||
#include "utils/system_info.hpp"
|
||||
#include "utils/terminate_handler.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
@@ -96,6 +99,10 @@
|
||||
#include "audit/log.hpp"
|
||||
#endif
|
||||
|
||||
constexpr const char *kMgUser = "MEMGRAPH_USER";
|
||||
constexpr const char *kMgPassword = "MEMGRAPH_PASSWORD";
|
||||
constexpr const char *kMgPassfile = "MEMGRAPH_PASSFILE";
|
||||
|
||||
namespace {
|
||||
std::string GetAllowedEnumValuesString(const auto &mappings) {
|
||||
std::vector<std::string> allowed_values;
|
||||
@@ -132,6 +139,10 @@ std::optional<Enum> StringToEnum(const auto &value, const auto &mappings) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Short help flag.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_HIDDEN_bool(h, false, "Print usage and exit.");
|
||||
|
||||
// Bolt server flags.
|
||||
DEFINE_string(bolt_address, "0.0.0.0", "IP address on which the Bolt server should listen.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -161,6 +172,11 @@ DEFINE_string(bolt_key_file, "", "Key file which should be used for the Bolt ser
|
||||
DEFINE_string(bolt_server_name_for_init, "",
|
||||
"Server name which the database should send to the client in the "
|
||||
"Bolt INIT message.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(init_file, "",
|
||||
"Path to cypherl file that is used for configuring users and database schema before server starts.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(init_data_file, "", "Path to cypherl file that is used for creating data after server starts.");
|
||||
|
||||
// General purpose flags.
|
||||
// NOTE: The `data_directory` flag must be the same here and in
|
||||
@@ -470,6 +486,33 @@ struct SessionData {
|
||||
DEFINE_string(auth_user_or_role_name_regex, memgraph::glue::kDefaultUserRoleRegex.data(),
|
||||
"Set to the regular expression that each user or role name must fulfill.");
|
||||
|
||||
void InitFromCypherlFile(memgraph::query::InterpreterContext &ctx, std::string cypherl_file_path
|
||||
#ifdef MG_ENTERPRISE
|
||||
,
|
||||
memgraph::audit::Log *audit_log
|
||||
#endif
|
||||
) {
|
||||
memgraph::query::Interpreter interpreter(&ctx);
|
||||
std::ifstream file(cypherl_file_path);
|
||||
if (file.is_open()) {
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (!line.empty()) {
|
||||
auto results = interpreter.Prepare(line, {}, {});
|
||||
memgraph::query::DiscardValueResultStream stream;
|
||||
interpreter.Pull(&stream, {}, results.qid);
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
audit_log->Record("", "", line, {});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
class BoltSession final : public memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
|
||||
memgraph::communication::v2::OutputStream> {
|
||||
public:
|
||||
@@ -506,7 +549,7 @@ class BoltSession final : public memgraph::communication::bolt::Session<memgraph
|
||||
username = &user_->username();
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
audit_log_->Record(endpoint_.address().to_string(), user_ ? *username : "", query,
|
||||
memgraph::storage::PropertyValue(params_pv));
|
||||
}
|
||||
@@ -685,6 +728,11 @@ int main(int argc, char **argv) {
|
||||
LoadConfig("memgraph");
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
if (FLAGS_h) {
|
||||
gflags::ShowUsageWithFlags(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
InitializeLogger();
|
||||
|
||||
// Unhandled exception handler init.
|
||||
@@ -779,15 +827,15 @@ int main(int argc, char **argv) {
|
||||
memgraph::utils::OnScopeExit settings_finalizer([&] { memgraph::utils::global_settings.Finalize(); });
|
||||
|
||||
// register all runtime settings
|
||||
memgraph::utils::license::RegisterLicenseSettings(memgraph::utils::license::global_license_checker,
|
||||
memgraph::utils::global_settings);
|
||||
memgraph::license::RegisterLicenseSettings(memgraph::license::global_license_checker,
|
||||
memgraph::utils::global_settings);
|
||||
|
||||
memgraph::utils::license::global_license_checker.CheckEnvLicense();
|
||||
memgraph::license::global_license_checker.CheckEnvLicense();
|
||||
if (!FLAGS_organization_name.empty() && !FLAGS_license_key.empty()) {
|
||||
memgraph::utils::license::global_license_checker.SetLicenseInfoOverride(FLAGS_license_key, FLAGS_organization_name);
|
||||
memgraph::license::global_license_checker.SetLicenseInfoOverride(FLAGS_license_key, FLAGS_organization_name);
|
||||
}
|
||||
|
||||
memgraph::utils::license::global_license_checker.StartBackgroundLicenseChecker(memgraph::utils::global_settings);
|
||||
memgraph::license::global_license_checker.StartBackgroundLicenseChecker(memgraph::utils::global_settings);
|
||||
|
||||
// All enterprise features should be constructed before the main database
|
||||
// storage. This will cause them to be destructed *after* the main database
|
||||
@@ -878,6 +926,29 @@ int main(int argc, char **argv) {
|
||||
interpreter_context.auth = &auth_handler;
|
||||
interpreter_context.auth_checker = &auth_checker;
|
||||
|
||||
if (!FLAGS_init_file.empty()) {
|
||||
spdlog::info("Running init file.");
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_file, &audit_log);
|
||||
}
|
||||
#else
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto *maybe_username = std::getenv(kMgUser);
|
||||
auto *maybe_password = std::getenv(kMgPassword);
|
||||
auto *maybe_pass_file = std::getenv(kMgPassfile);
|
||||
if (maybe_username && maybe_password) {
|
||||
auth_handler.CreateUser(maybe_username, maybe_password);
|
||||
} else if (maybe_pass_file) {
|
||||
const auto [username, password] = LoadUsernameAndPassword(maybe_pass_file);
|
||||
if (!username.empty() && !password.empty()) {
|
||||
auth_handler.CreateUser(username, password);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Triggers can execute query procedures, so we need to reload the modules first and then
|
||||
// the triggers
|
||||
@@ -906,12 +977,15 @@ int main(int argc, char **argv) {
|
||||
ServerT server(server_endpoint, &session_data, &context, FLAGS_bolt_session_inactivity_timeout, service_name,
|
||||
FLAGS_bolt_num_workers);
|
||||
|
||||
const auto run_id = memgraph::utils::GenerateUUID();
|
||||
const auto machine_id = memgraph::utils::GetMachineId();
|
||||
session_data.run_id = run_id;
|
||||
|
||||
// Setup telemetry
|
||||
static constexpr auto telemetry_server{"https://telemetry.memgraph.com/88b5e7e8-746a-11e8-9f85-538a9e9690cc/"};
|
||||
std::optional<memgraph::telemetry::Telemetry> telemetry;
|
||||
if (FLAGS_telemetry_enabled) {
|
||||
telemetry.emplace("https://telemetry.memgraph.com/88b5e7e8-746a-11e8-9f85-538a9e9690cc/",
|
||||
data_directory / "telemetry", std::chrono::minutes(10));
|
||||
session_data.run_id = telemetry->GetRunId();
|
||||
telemetry.emplace(telemetry_server, data_directory / "telemetry", run_id, machine_id, std::chrono::minutes(10));
|
||||
telemetry->AddCollector("storage", [&db]() -> nlohmann::json {
|
||||
auto info = db.GetInfo();
|
||||
return {{"vertices", info.vertex_count}, {"edges", info.edge_count}};
|
||||
@@ -927,6 +1001,8 @@ int main(int argc, char **argv) {
|
||||
return memgraph::query::plan::CallProcedure::GetAndResetCounters();
|
||||
});
|
||||
}
|
||||
memgraph::license::LicenseInfoSender license_info_sender(telemetry_server, run_id, machine_id, memory_limit,
|
||||
memgraph::license::global_license_checker.GetLicenseInfo());
|
||||
|
||||
memgraph::communication::websocket::SafeAuth websocket_auth{&auth};
|
||||
memgraph::communication::websocket::Server websocket_server{
|
||||
@@ -950,6 +1026,17 @@ int main(int argc, char **argv) {
|
||||
MG_ASSERT(server.Start(), "Couldn't start the Bolt server!");
|
||||
websocket_server.Start();
|
||||
|
||||
if (!FLAGS_init_data_file.empty()) {
|
||||
spdlog::info("Running init data file.");
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_data_file, &audit_log);
|
||||
}
|
||||
#else
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_data_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
server.AwaitShutdown();
|
||||
websocket_server.AwaitShutdown();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -107,5 +107,37 @@ storage::PropertyValue PropsSetChecked(T *record, const storage::PropertyId &key
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
concept AccessorWithInitProperties = requires(T accessor,
|
||||
const std::map<storage::PropertyId, storage::PropertyValue> &properties) {
|
||||
{ accessor.InitProperties(properties) } -> std::same_as<storage::Result<bool>>;
|
||||
};
|
||||
|
||||
/// Set property `values` mapped with given `key` on a `record`.
|
||||
///
|
||||
/// @throw QueryRuntimeException if value cannot be set as a property value
|
||||
template <AccessorWithInitProperties T>
|
||||
bool MultiPropsInitChecked(T *record, std::map<storage::PropertyId, storage::PropertyValue> &properties) {
|
||||
try {
|
||||
auto maybe_values = record->InitProperties(properties);
|
||||
if (maybe_values.HasError()) {
|
||||
switch (maybe_values.GetError()) {
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
throw TransactionSerializationException();
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to set properties on a deleted object.");
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
throw QueryRuntimeException("Can't set property because properties on edges are disabled.");
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw QueryRuntimeException("Unexpected error when setting a property.");
|
||||
}
|
||||
}
|
||||
return std::move(*maybe_values);
|
||||
} catch (const TypedValueException &) {
|
||||
throw QueryRuntimeException("Cannot set properties.");
|
||||
}
|
||||
}
|
||||
|
||||
int64_t QueryTimestamp();
|
||||
} // namespace memgraph::query
|
||||
|
||||
@@ -115,7 +115,7 @@ auto SubgraphVertexAccessor::OutEdges(storage::View view) const -> decltype(impl
|
||||
auto maybe_edges = impl_.impl_.OutEdges(view, {});
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
auto edges = std::move(*maybe_edges);
|
||||
auto graph_edges = graph_->edges();
|
||||
const auto &graph_edges = graph_->edges();
|
||||
|
||||
std::vector<storage::EdgeAccessor> filteredOutEdges;
|
||||
for (auto &edge : edges) {
|
||||
@@ -132,7 +132,7 @@ auto SubgraphVertexAccessor::InEdges(storage::View view) const -> decltype(impl_
|
||||
auto maybe_edges = impl_.impl_.InEdges(view, {});
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
auto edges = std::move(*maybe_edges);
|
||||
auto graph_edges = graph_->edges();
|
||||
const auto &graph_edges = graph_->edges();
|
||||
|
||||
std::vector<storage::EdgeAccessor> filteredOutEdges;
|
||||
for (auto &edge : edges) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -71,6 +71,10 @@ class EdgeAccessor final {
|
||||
return impl_.SetProperty(key, value);
|
||||
}
|
||||
|
||||
storage::Result<bool> InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties) {
|
||||
return impl_.InitProperties(properties);
|
||||
}
|
||||
|
||||
storage::Result<storage::PropertyValue> RemoveProperty(storage::PropertyId key) {
|
||||
return SetProperty(key, storage::PropertyValue());
|
||||
}
|
||||
@@ -125,6 +129,10 @@ class VertexAccessor final {
|
||||
return impl_.SetProperty(key, value);
|
||||
}
|
||||
|
||||
storage::Result<bool> InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties) {
|
||||
return impl_.InitProperties(properties);
|
||||
}
|
||||
|
||||
storage::Result<storage::PropertyValue> RemoveProperty(storage::PropertyId key) {
|
||||
return SetProperty(key, storage::PropertyValue());
|
||||
}
|
||||
@@ -200,7 +208,7 @@ class SubgraphVertexAccessor final {
|
||||
return impl_ == v.impl_;
|
||||
}
|
||||
|
||||
auto InEdges(storage::View view) const -> decltype(impl_.OutEdges(view));
|
||||
auto InEdges(storage::View view) const -> decltype(impl_.InEdges(view));
|
||||
|
||||
auto OutEdges(storage::View view) const -> decltype(impl_.OutEdges(view));
|
||||
|
||||
|
||||
@@ -301,6 +301,7 @@ cpp<#
|
||||
|
||||
(lcp:define-class expression (tree "::utils::Visitable<HierarchicalTreeVisitor>"
|
||||
"::utils::Visitable<ExpressionVisitor<TypedValue>>"
|
||||
"::utils::Visitable<ExpressionVisitor<TypedValue*>>"
|
||||
"::utils::Visitable<ExpressionVisitor<void>>")
|
||||
()
|
||||
(:abstractp t)
|
||||
@@ -308,6 +309,7 @@ cpp<#
|
||||
#>cpp
|
||||
using utils::Visitable<HierarchicalTreeVisitor>::Accept;
|
||||
using utils::Visitable<ExpressionVisitor<TypedValue>>::Accept;
|
||||
using utils::Visitable<ExpressionVisitor<TypedValue*>>::Accept;
|
||||
using utils::Visitable<ExpressionVisitor<void>>::Accept;
|
||||
|
||||
Expression() = default;
|
||||
@@ -407,6 +409,7 @@ cpp<#
|
||||
(:public
|
||||
#>cpp
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -438,6 +441,7 @@ cpp<#
|
||||
(:public
|
||||
#>cpp
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -461,7 +465,8 @@ cpp<#
|
||||
(lcp:define-class aggregation (binary-operator)
|
||||
((op "Op" :scope :public)
|
||||
(symbol-pos :int32_t :initval -1 :scope :public
|
||||
:documentation "Symbol table position of the symbol this Aggregation is mapped to."))
|
||||
:documentation "Symbol table position of the symbol this Aggregation is mapped to.")
|
||||
(distinct :bool :initval "false" :scope :public))
|
||||
(:public
|
||||
(lcp:define-enum op
|
||||
(count min max sum avg collect-list collect-map project)
|
||||
@@ -484,6 +489,7 @@ cpp<#
|
||||
}
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -505,8 +511,8 @@ cpp<#
|
||||
|
||||
/// Aggregation's first expression is the value being aggregated. The second
|
||||
/// expression is the key used only in COLLECT_MAP.
|
||||
Aggregation(Expression *expression1, Expression *expression2, Op op)
|
||||
: BinaryOperator(expression1, expression2), op_(op) {
|
||||
Aggregation(Expression *expression1, Expression *expression2, Op op, bool distinct)
|
||||
: BinaryOperator(expression1, expression2), op_(op), distinct_(distinct) {
|
||||
// COUNT without expression denotes COUNT(*) in cypher.
|
||||
DMG_ASSERT(expression1 || op == Aggregation::Op::COUNT,
|
||||
"All aggregations, except COUNT require expression");
|
||||
@@ -537,6 +543,7 @@ cpp<#
|
||||
ListSlicingOperator() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -580,6 +587,7 @@ cpp<#
|
||||
IfOperator() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -627,6 +635,7 @@ cpp<#
|
||||
PrimitiveLiteral() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
DEFVISITABLE(HierarchicalTreeVisitor);
|
||||
cpp<#)
|
||||
@@ -655,6 +664,7 @@ cpp<#
|
||||
ListLiteral() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -687,6 +697,7 @@ cpp<#
|
||||
MapLiteral() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -719,6 +730,7 @@ cpp<#
|
||||
Identifier() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
DEFVISITABLE(HierarchicalTreeVisitor);
|
||||
|
||||
@@ -756,6 +768,7 @@ cpp<#
|
||||
PropertyLookup() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -796,6 +809,7 @@ cpp<#
|
||||
LabelsTest() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -836,6 +850,7 @@ cpp<#
|
||||
Function() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -891,6 +906,7 @@ cpp<#
|
||||
Reduce() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -929,6 +945,7 @@ cpp<#
|
||||
Coalesce() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -968,6 +985,7 @@ cpp<#
|
||||
Extract() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -1004,6 +1022,7 @@ cpp<#
|
||||
All() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -1045,6 +1064,7 @@ cpp<#
|
||||
Single() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -1086,6 +1106,7 @@ cpp<#
|
||||
Any() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -1127,6 +1148,7 @@ cpp<#
|
||||
None() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -1158,6 +1180,7 @@ cpp<#
|
||||
ParameterLookup() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
DEFVISITABLE(HierarchicalTreeVisitor);
|
||||
cpp<#)
|
||||
@@ -1185,6 +1208,7 @@ cpp<#
|
||||
RegexMatch() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -1204,6 +1228,7 @@ cpp<#
|
||||
|
||||
(lcp:define-class named-expression (tree "::utils::Visitable<HierarchicalTreeVisitor>"
|
||||
"::utils::Visitable<ExpressionVisitor<TypedValue>>"
|
||||
"::utils::Visitable<ExpressionVisitor<TypedValue*>>"
|
||||
"::utils::Visitable<ExpressionVisitor<void>>")
|
||||
((name "std::string" :scope :public)
|
||||
(expression "Expression *" :initval "nullptr" :scope :public
|
||||
@@ -1222,6 +1247,7 @@ cpp<#
|
||||
NamedExpression() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
@@ -2685,5 +2711,41 @@ cpp<#
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
(lcp:define-class exists (expression)
|
||||
((pattern "Pattern *" :initval "nullptr" :scope :public
|
||||
:slk-save #'slk-save-ast-pointer
|
||||
:slk-load (slk-load-ast-pointer "Pattern"))
|
||||
(symbol-pos :int32_t :initval -1 :scope :public
|
||||
:documentation "Symbol table position of the symbol this Aggregation is mapped to."))
|
||||
|
||||
(:public
|
||||
#>cpp
|
||||
Exists() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue*>);
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
pattern_->Accept(visitor);
|
||||
}
|
||||
return visitor.PostVisit(*this);
|
||||
}
|
||||
Exists *MapTo(const Symbol &symbol) {
|
||||
symbol_pos_ = symbol.position();
|
||||
return this;
|
||||
}
|
||||
cpp<#)
|
||||
(:protected
|
||||
#>cpp
|
||||
Exists(Pattern * pattern) : pattern_(pattern) {}
|
||||
cpp<#)
|
||||
(:private
|
||||
#>cpp
|
||||
friend class AstStorage;
|
||||
cpp<#)
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
(lcp:pop-namespace) ;; namespace query
|
||||
(lcp:pop-namespace) ;; namespace memgraph
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -95,6 +95,7 @@ class SettingQuery;
|
||||
class VersionQuery;
|
||||
class Foreach;
|
||||
class ShowConfigQuery;
|
||||
class Exists;
|
||||
|
||||
using TreeCompositeVisitor = utils::CompositeVisitor<
|
||||
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
|
||||
@@ -103,7 +104,7 @@ using TreeCompositeVisitor = utils::CompositeVisitor<
|
||||
ListSlicingOperator, IfOperator, UnaryPlusOperator, UnaryMinusOperator, IsNullOperator, ListLiteral, MapLiteral,
|
||||
PropertyLookup, LabelsTest, Aggregation, Function, Reduce, Coalesce, Extract, All, Single, Any, None, CallProcedure,
|
||||
Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom, Delete, Where, SetProperty, SetProperties, SetLabels,
|
||||
RemoveProperty, RemoveLabels, Merge, Unwind, RegexMatch, LoadCsv, Foreach>;
|
||||
RemoveProperty, RemoveLabels, Merge, Unwind, RegexMatch, LoadCsv, Foreach, Exists>;
|
||||
|
||||
using TreeLeafVisitor = utils::LeafVisitor<Identifier, PrimitiveLiteral, ParameterLookup>;
|
||||
|
||||
@@ -123,7 +124,7 @@ class ExpressionVisitor
|
||||
LessOperator, GreaterOperator, LessEqualOperator, GreaterEqualOperator, InListOperator, SubscriptOperator,
|
||||
ListSlicingOperator, IfOperator, UnaryPlusOperator, UnaryMinusOperator, IsNullOperator, ListLiteral,
|
||||
MapLiteral, PropertyLookup, LabelsTest, Aggregation, Function, Reduce, Coalesce, Extract, All, Single, Any,
|
||||
None, ParameterLookup, Identifier, PrimitiveLiteral, RegexMatch> {};
|
||||
None, ParameterLookup, Identifier, PrimitiveLiteral, RegexMatch, Exists> {};
|
||||
|
||||
template <class TResult>
|
||||
class QueryVisitor : public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -2106,7 +2106,7 @@ antlrcpp::Any CypherMainVisitor::visitAtom(MemgraphCypher::AtomContext *ctx) {
|
||||
// Here we handle COUNT(*). COUNT(expression) is handled in
|
||||
// visitFunctionInvocation with other aggregations. This is visible in
|
||||
// functionInvocation and atom producions in opencypher grammar.
|
||||
return static_cast<Expression *>(storage_->Create<Aggregation>(nullptr, nullptr, Aggregation::Op::COUNT));
|
||||
return static_cast<Expression *>(storage_->Create<Aggregation>(nullptr, nullptr, Aggregation::Op::COUNT, false));
|
||||
} else if (ctx->ALL()) {
|
||||
auto *ident = storage_->Create<Identifier>(
|
||||
std::any_cast<std::string>(ctx->filterExpression()->idInColl()->variable()->accept(this)));
|
||||
@@ -2160,7 +2160,10 @@ antlrcpp::Any CypherMainVisitor::visitAtom(MemgraphCypher::AtomContext *ctx) {
|
||||
auto *list = std::any_cast<Expression *>(ctx->extractExpression()->idInColl()->expression()->accept(this));
|
||||
auto *expr = std::any_cast<Expression *>(ctx->extractExpression()->expression()->accept(this));
|
||||
return static_cast<Expression *>(storage_->Create<Extract>(ident, list, expr));
|
||||
} else if (ctx->existsExpression()) {
|
||||
return std::any_cast<Expression *>(ctx->existsExpression()->accept(this));
|
||||
}
|
||||
|
||||
// TODO: Implement this. We don't support comprehensions, filtering... at
|
||||
// the moment.
|
||||
throw utils::NotYetImplemented("atom expression '{}'", ctx->getText());
|
||||
@@ -2204,6 +2207,17 @@ antlrcpp::Any CypherMainVisitor::visitLiteral(MemgraphCypher::LiteralContext *ct
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitExistsExpression(MemgraphCypher::ExistsExpressionContext *ctx) {
|
||||
auto *exists = storage_->Create<Exists>();
|
||||
exists->pattern_ = std::any_cast<Pattern *>(ctx->patternPart()->accept(this));
|
||||
|
||||
if (exists->pattern_->identifier_) {
|
||||
throw SyntaxException("Identifiers are not supported in exists(...).");
|
||||
}
|
||||
|
||||
return static_cast<Expression *>(exists);
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitParenthesizedExpression(MemgraphCypher::ParenthesizedExpressionContext *ctx) {
|
||||
return std::any_cast<Expression *>(ctx->expression()->accept(this));
|
||||
}
|
||||
@@ -2222,9 +2236,7 @@ antlrcpp::Any CypherMainVisitor::visitNumberLiteral(MemgraphCypher::NumberLitera
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitFunctionInvocation(MemgraphCypher::FunctionInvocationContext *ctx) {
|
||||
if (ctx->DISTINCT()) {
|
||||
throw utils::NotYetImplemented("DISTINCT function call");
|
||||
}
|
||||
const auto is_distinct = ctx->DISTINCT() != nullptr;
|
||||
auto function_name = std::any_cast<std::string>(ctx->functionName()->accept(this));
|
||||
std::vector<Expression *> expressions;
|
||||
for (auto *expression : ctx->expression()) {
|
||||
@@ -2232,33 +2244,38 @@ antlrcpp::Any CypherMainVisitor::visitFunctionInvocation(MemgraphCypher::Functio
|
||||
}
|
||||
if (expressions.size() == 1U) {
|
||||
if (function_name == Aggregation::kCount) {
|
||||
return static_cast<Expression *>(storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::COUNT));
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::COUNT, is_distinct));
|
||||
}
|
||||
if (function_name == Aggregation::kMin) {
|
||||
return static_cast<Expression *>(storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::MIN));
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::MIN, is_distinct));
|
||||
}
|
||||
if (function_name == Aggregation::kMax) {
|
||||
return static_cast<Expression *>(storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::MAX));
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::MAX, is_distinct));
|
||||
}
|
||||
if (function_name == Aggregation::kSum) {
|
||||
return static_cast<Expression *>(storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::SUM));
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::SUM, is_distinct));
|
||||
}
|
||||
if (function_name == Aggregation::kAvg) {
|
||||
return static_cast<Expression *>(storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::AVG));
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::AVG, is_distinct));
|
||||
}
|
||||
if (function_name == Aggregation::kCollect) {
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::COLLECT_LIST));
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::COLLECT_LIST, is_distinct));
|
||||
}
|
||||
if (function_name == Aggregation::kProject) {
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::PROJECT));
|
||||
storage_->Create<Aggregation>(expressions[0], nullptr, Aggregation::Op::PROJECT, is_distinct));
|
||||
}
|
||||
}
|
||||
|
||||
if (expressions.size() == 2U && function_name == Aggregation::kCollect) {
|
||||
return static_cast<Expression *>(
|
||||
storage_->Create<Aggregation>(expressions[1], expressions[0], Aggregation::Op::COLLECT_MAP));
|
||||
storage_->Create<Aggregation>(expressions[1], expressions[0], Aggregation::Op::COLLECT_MAP, is_distinct));
|
||||
}
|
||||
|
||||
auto is_user_defined_function = [](const std::string &function_name) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -763,6 +763,11 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
*/
|
||||
antlrcpp::Any visitParameter(MemgraphCypher::ParameterContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Exists* (Expression)
|
||||
*/
|
||||
antlrcpp::Any visitExistsExpression(MemgraphCypher::ExistsExpressionContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Expression*
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -60,6 +60,7 @@ class ExpressionPrettyPrinter : public ExpressionVisitor<void> {
|
||||
void Visit(Reduce &op) override;
|
||||
void Visit(Coalesce &op) override;
|
||||
void Visit(Extract &op) override;
|
||||
void Visit(Exists &op) override;
|
||||
void Visit(All &op) override;
|
||||
void Visit(Single &op) override;
|
||||
void Visit(Any &op) override;
|
||||
@@ -264,6 +265,8 @@ void ExpressionPrettyPrinter::Visit(Extract &op) {
|
||||
PrintOperator(out_, "Extract", op.identifier_, op.list_, op.expression_);
|
||||
}
|
||||
|
||||
void ExpressionPrettyPrinter::Visit(Exists & /*op*/) { PrintOperator(out_, "Exists", "expression"); }
|
||||
|
||||
void ExpressionPrettyPrinter::Visit(All &op) {
|
||||
PrintOperator(out_, "All", op.identifier_, op.list_expression_, op.where_->expression_);
|
||||
}
|
||||
|
||||
@@ -236,6 +236,7 @@ atom : literal
|
||||
| ( ANY '(' filterExpression ')' )
|
||||
| ( NONE '(' filterExpression ')' )
|
||||
| ( SINGLE '(' filterExpression ')' )
|
||||
| ( EXISTS '(' existsExpression ')' )
|
||||
| relationshipsPattern
|
||||
| parenthesizedExpression
|
||||
| functionInvocation
|
||||
@@ -275,6 +276,8 @@ reduceExpression : accumulator=variable '=' initial=expression ',' idInColl '|'
|
||||
|
||||
extractExpression : idInColl '|' expression ;
|
||||
|
||||
existsExpression : patternPart ;
|
||||
|
||||
idInColl : variable IN expression ;
|
||||
|
||||
functionInvocation : functionName '(' ( DISTINCT )? ( expression ( ',' expression )* )? ')' ;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -64,12 +64,9 @@ auto SymbolGenerator::CreateSymbol(const std::string &name, bool user_declared,
|
||||
return symbol;
|
||||
}
|
||||
|
||||
auto SymbolGenerator::GetOrCreateSymbolLocalScope(const std::string &name, bool user_declared, Symbol::Type type) {
|
||||
auto &scope = scopes_.back();
|
||||
if (auto maybe_symbol = FindSymbolInScope(name, scope, type); maybe_symbol) {
|
||||
return *maybe_symbol;
|
||||
}
|
||||
return CreateSymbol(name, user_declared, type);
|
||||
auto SymbolGenerator::CreateAnonymousSymbol(Symbol::Type /*type*/) {
|
||||
auto symbol = symbol_table_->CreateAnonymousSymbol();
|
||||
return symbol;
|
||||
}
|
||||
|
||||
auto SymbolGenerator::GetOrCreateSymbol(const std::string &name, bool user_declared, Symbol::Type type) {
|
||||
@@ -206,7 +203,7 @@ bool SymbolGenerator::PreVisit(CallProcedure &call_proc) {
|
||||
|
||||
bool SymbolGenerator::PostVisit(CallProcedure &call_proc) {
|
||||
for (auto *ident : call_proc.result_identifiers_) {
|
||||
if (HasSymbolLocalScope(ident->name_)) {
|
||||
if (HasSymbol(ident->name_)) {
|
||||
throw RedeclareVariableError(ident->name_);
|
||||
}
|
||||
ident->MapTo(CreateSymbol(ident->name_, true));
|
||||
@@ -217,7 +214,7 @@ bool SymbolGenerator::PostVisit(CallProcedure &call_proc) {
|
||||
bool SymbolGenerator::PreVisit(LoadCsv &load_csv) { return false; }
|
||||
|
||||
bool SymbolGenerator::PostVisit(LoadCsv &load_csv) {
|
||||
if (HasSymbolLocalScope(load_csv.row_var_->name_)) {
|
||||
if (HasSymbol(load_csv.row_var_->name_)) {
|
||||
throw RedeclareVariableError(load_csv.row_var_->name_);
|
||||
}
|
||||
load_csv.row_var_->MapTo(CreateSymbol(load_csv.row_var_->name_, true));
|
||||
@@ -265,7 +262,7 @@ bool SymbolGenerator::PostVisit(Merge &) {
|
||||
|
||||
bool SymbolGenerator::PostVisit(Unwind &unwind) {
|
||||
const auto &name = unwind.named_expression_->name_;
|
||||
if (HasSymbolLocalScope(name)) {
|
||||
if (HasSymbol(name)) {
|
||||
throw RedeclareVariableError(name);
|
||||
}
|
||||
unwind.named_expression_->MapTo(CreateSymbol(name, true));
|
||||
@@ -282,7 +279,7 @@ bool SymbolGenerator::PostVisit(Match &) {
|
||||
// Check variables in property maps after visiting Match, so that they can
|
||||
// reference symbols out of bind order.
|
||||
for (auto &ident : scope.identifiers_in_match) {
|
||||
if (!HasSymbolLocalScope(ident->name_) && !ConsumePredefinedIdentifier(ident->name_))
|
||||
if (!HasSymbol(ident->name_) && !ConsumePredefinedIdentifier(ident->name_))
|
||||
throw UnboundVariableError(ident->name_);
|
||||
ident->MapTo(scope.symbols[ident->name_]);
|
||||
}
|
||||
@@ -310,11 +307,19 @@ SymbolGenerator::ReturnType SymbolGenerator::Visit(Identifier &ident) {
|
||||
if (scope.in_skip || scope.in_limit) {
|
||||
throw SemanticException("Variables are not allowed in {}.", scope.in_skip ? "SKIP" : "LIMIT");
|
||||
}
|
||||
|
||||
if (scope.in_exists && (scope.visiting_edge || scope.in_node_atom)) {
|
||||
auto has_symbol = HasSymbol(ident.name_);
|
||||
if (!has_symbol && !ConsumePredefinedIdentifier(ident.name_) && ident.user_declared_) {
|
||||
throw SemanticException("Unbounded variables are not allowed in exists!");
|
||||
}
|
||||
}
|
||||
|
||||
Symbol symbol;
|
||||
if (scope.in_pattern && !(scope.in_node_atom || scope.visiting_edge)) {
|
||||
// If we are in the pattern, and outside of a node or an edge, the
|
||||
// identifier is the pattern name.
|
||||
symbol = GetOrCreateSymbolLocalScope(ident.name_, ident.user_declared_, Symbol::Type::PATH);
|
||||
symbol = GetOrCreateSymbol(ident.name_, ident.user_declared_, Symbol::Type::PATH);
|
||||
} else if (scope.in_pattern && scope.in_pattern_atom_identifier) {
|
||||
// Patterns used to create nodes and edges cannot redeclare already
|
||||
// established bindings. Declaration only happens in single node
|
||||
@@ -322,21 +327,22 @@ SymbolGenerator::ReturnType SymbolGenerator::Visit(Identifier &ident) {
|
||||
// `MATCH (n) CREATE (n)` should throw an error that `n` is already
|
||||
// declared. While `MATCH (n) CREATE (n) -[:R]-> (n)` is allowed,
|
||||
// since `n` now references the bound node instead of declaring it.
|
||||
if ((scope.in_create_node || scope.in_create_edge) && HasSymbolLocalScope(ident.name_)) {
|
||||
if ((scope.in_create_node || scope.in_create_edge) && HasSymbol(ident.name_)) {
|
||||
throw RedeclareVariableError(ident.name_);
|
||||
}
|
||||
auto type = Symbol::Type::VERTEX;
|
||||
if (scope.visiting_edge) {
|
||||
// Edge referencing is not allowed (like in Neo4j):
|
||||
// `MATCH (n) - [r] -> (n) - [r] -> (n) RETURN r` is not allowed.
|
||||
if (HasSymbolLocalScope(ident.name_)) {
|
||||
if (HasSymbol(ident.name_)) {
|
||||
throw RedeclareVariableError(ident.name_);
|
||||
}
|
||||
type = scope.visiting_edge->IsVariable() ? Symbol::Type::EDGE_LIST : Symbol::Type::EDGE;
|
||||
}
|
||||
symbol = GetOrCreateSymbolLocalScope(ident.name_, ident.user_declared_, type);
|
||||
symbol = GetOrCreateSymbol(ident.name_, ident.user_declared_, type);
|
||||
} else if (scope.in_pattern && !scope.in_pattern_atom_identifier && scope.in_match) {
|
||||
if (scope.in_edge_range && scope.visiting_edge->identifier_->name_ == ident.name_) {
|
||||
if (scope.in_edge_range && scope.visiting_edge && scope.visiting_edge->identifier_ &&
|
||||
scope.visiting_edge->identifier_->name_ == ident.name_) {
|
||||
// Prevent variable path bounds to reference the identifier which is bound
|
||||
// by the variable path itself.
|
||||
throw UnboundVariableError(ident.name_);
|
||||
@@ -438,6 +444,46 @@ bool SymbolGenerator::PreVisit(Extract &extract) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SymbolGenerator::PreVisit(Exists &exists) {
|
||||
auto &scope = scopes_.back();
|
||||
|
||||
if (scope.in_set_property) {
|
||||
throw utils::NotYetImplemented("Set property can not be used with exists, but only during matching!");
|
||||
}
|
||||
|
||||
if (scope.in_with) {
|
||||
throw utils::NotYetImplemented("WITH can not be used with exists, but only during matching!");
|
||||
}
|
||||
|
||||
scope.in_exists = true;
|
||||
|
||||
const auto &symbol = CreateAnonymousSymbol();
|
||||
exists.MapTo(symbol);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SymbolGenerator::PostVisit(Exists & /*exists*/) {
|
||||
auto &scope = scopes_.back();
|
||||
scope.in_exists = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SymbolGenerator::PreVisit(SetProperty & /*set_property*/) {
|
||||
auto &scope = scopes_.back();
|
||||
scope.in_set_property = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SymbolGenerator::PostVisit(SetProperty & /*set_property*/) {
|
||||
auto &scope = scopes_.back();
|
||||
scope.in_set_property = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pattern and its subparts.
|
||||
|
||||
bool SymbolGenerator::PreVisit(Pattern &pattern) {
|
||||
@@ -447,6 +493,7 @@ bool SymbolGenerator::PreVisit(Pattern &pattern) {
|
||||
MG_ASSERT(utils::IsSubtype(*pattern.atoms_[0], NodeAtom::kType), "Expected a single NodeAtom in Pattern");
|
||||
scope.in_create_node = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -461,7 +508,7 @@ bool SymbolGenerator::PreVisit(NodeAtom &node_atom) {
|
||||
auto &scope = scopes_.back();
|
||||
auto check_node_semantic = [&node_atom, &scope, this](const bool props_or_labels) {
|
||||
const auto &node_name = node_atom.identifier_->name_;
|
||||
if ((scope.in_create || scope.in_merge) && props_or_labels && HasSymbolLocalScope(node_name)) {
|
||||
if ((scope.in_create || scope.in_merge) && props_or_labels && HasSymbol(node_name)) {
|
||||
throw SemanticException("Cannot create node '" + node_name +
|
||||
"' with labels or properties, because it is already declared.");
|
||||
}
|
||||
@@ -555,11 +602,11 @@ bool SymbolGenerator::PreVisit(EdgeAtom &edge_atom) {
|
||||
edge_atom.identifier_->Accept(*this);
|
||||
scope.in_pattern_atom_identifier = false;
|
||||
if (edge_atom.total_weight_) {
|
||||
if (HasSymbolLocalScope(edge_atom.total_weight_->name_)) {
|
||||
if (HasSymbol(edge_atom.total_weight_->name_)) {
|
||||
throw RedeclareVariableError(edge_atom.total_weight_->name_);
|
||||
}
|
||||
edge_atom.total_weight_->MapTo(GetOrCreateSymbolLocalScope(
|
||||
edge_atom.total_weight_->name_, edge_atom.total_weight_->user_declared_, Symbol::Type::NUMBER));
|
||||
edge_atom.total_weight_->MapTo(GetOrCreateSymbol(edge_atom.total_weight_->name_,
|
||||
edge_atom.total_weight_->user_declared_, Symbol::Type::NUMBER));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -602,10 +649,6 @@ bool SymbolGenerator::HasSymbol(const std::string &name) const {
|
||||
return std::ranges::any_of(scopes_, [&name](const auto &scope) { return scope.symbols.contains(name); });
|
||||
}
|
||||
|
||||
bool SymbolGenerator::HasSymbolLocalScope(const std::string &name) const {
|
||||
return scopes_.back().symbols.contains(name);
|
||||
}
|
||||
|
||||
bool SymbolGenerator::ConsumePredefinedIdentifier(const std::string &name) {
|
||||
auto it = predefined_identifiers_.find(name);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -64,6 +64,8 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
|
||||
bool PostVisit(Match &) override;
|
||||
bool PreVisit(Foreach &) override;
|
||||
bool PostVisit(Foreach &) override;
|
||||
bool PreVisit(SetProperty & /*set_property*/) override;
|
||||
bool PostVisit(SetProperty & /*set_property*/) override;
|
||||
|
||||
// Expressions
|
||||
ReturnType Visit(Identifier &) override;
|
||||
@@ -79,6 +81,8 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
|
||||
bool PreVisit(None &) override;
|
||||
bool PreVisit(Reduce &) override;
|
||||
bool PreVisit(Extract &) override;
|
||||
bool PreVisit(Exists & /*exists*/) override;
|
||||
bool PostVisit(Exists & /*exists*/) override;
|
||||
|
||||
// Pattern and its subparts.
|
||||
bool PreVisit(Pattern &) override;
|
||||
@@ -113,6 +117,8 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
|
||||
bool in_where{false};
|
||||
bool in_match{false};
|
||||
bool in_foreach{false};
|
||||
bool in_exists{false};
|
||||
bool in_set_property{false};
|
||||
// True when visiting a pattern atom (node or edge) identifier, which can be
|
||||
// reused or created in the pattern itself.
|
||||
bool in_pattern_atom_identifier{false};
|
||||
@@ -134,7 +140,6 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
|
||||
static std::optional<Symbol> FindSymbolInScope(const std::string &name, const Scope &scope, Symbol::Type type);
|
||||
|
||||
bool HasSymbol(const std::string &name) const;
|
||||
bool HasSymbolLocalScope(const std::string &name) const;
|
||||
|
||||
// @return true if it added a predefined identifier with that name
|
||||
bool ConsumePredefinedIdentifier(const std::string &name);
|
||||
@@ -144,10 +149,12 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
|
||||
auto CreateSymbol(const std::string &name, bool user_declared, Symbol::Type type = Symbol::Type::ANY,
|
||||
int token_position = -1);
|
||||
|
||||
// Returns a freshly generated anonymous symbol.
|
||||
auto CreateAnonymousSymbol(Symbol::Type type = Symbol::Type::ANY);
|
||||
|
||||
auto GetOrCreateSymbol(const std::string &name, bool user_declared, Symbol::Type type = Symbol::Type::ANY);
|
||||
// Returns the symbol by name. If the mapping already exists, checks if the
|
||||
// types match. Otherwise, returns a new symbol.
|
||||
auto GetOrCreateSymbolLocalScope(const std::string &name, bool user_declared, Symbol::Type type = Symbol::Type::ANY);
|
||||
|
||||
void VisitReturnBody(ReturnBody &body, Where *where = nullptr);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -51,6 +51,7 @@ class SymbolTable final {
|
||||
const Symbol &at(const Identifier &ident) const { return table_.at(ident.symbol_pos_); }
|
||||
const Symbol &at(const NamedExpression &nexpr) const { return table_.at(nexpr.symbol_pos_); }
|
||||
const Symbol &at(const Aggregation &aggr) const { return table_.at(aggr.symbol_pos_); }
|
||||
const Symbol &at(const Exists &exists) const { return table_.at(exists.symbol_pos_); }
|
||||
|
||||
// TODO: Remove these since members are public
|
||||
int32_t max_position() const { return static_cast<int32_t>(table_.size()); }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -31,6 +31,72 @@
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
class ReferenceExpressionEvaluator : public ExpressionVisitor<TypedValue *> {
|
||||
public:
|
||||
ReferenceExpressionEvaluator(Frame *frame, const SymbolTable *symbol_table, const EvaluationContext *ctx)
|
||||
: frame_(frame), symbol_table_(symbol_table), ctx_(ctx) {}
|
||||
|
||||
using ExpressionVisitor<TypedValue *>::Visit;
|
||||
|
||||
utils::MemoryResource *GetMemoryResource() const { return ctx_->memory; }
|
||||
|
||||
#define UNSUCCESSFUL_VISIT(expr_name) \
|
||||
TypedValue *Visit(expr_name &expr) override { return nullptr; }
|
||||
|
||||
TypedValue *Visit(Identifier &ident) override { return &frame_->at(symbol_table_->at(ident)); }
|
||||
|
||||
UNSUCCESSFUL_VISIT(NamedExpression);
|
||||
UNSUCCESSFUL_VISIT(OrOperator);
|
||||
UNSUCCESSFUL_VISIT(XorOperator);
|
||||
UNSUCCESSFUL_VISIT(AdditionOperator);
|
||||
UNSUCCESSFUL_VISIT(SubtractionOperator);
|
||||
UNSUCCESSFUL_VISIT(MultiplicationOperator);
|
||||
UNSUCCESSFUL_VISIT(DivisionOperator);
|
||||
UNSUCCESSFUL_VISIT(ModOperator);
|
||||
UNSUCCESSFUL_VISIT(NotEqualOperator);
|
||||
UNSUCCESSFUL_VISIT(EqualOperator);
|
||||
UNSUCCESSFUL_VISIT(LessOperator);
|
||||
UNSUCCESSFUL_VISIT(GreaterOperator);
|
||||
UNSUCCESSFUL_VISIT(LessEqualOperator);
|
||||
UNSUCCESSFUL_VISIT(GreaterEqualOperator);
|
||||
|
||||
UNSUCCESSFUL_VISIT(NotOperator);
|
||||
UNSUCCESSFUL_VISIT(UnaryPlusOperator);
|
||||
UNSUCCESSFUL_VISIT(UnaryMinusOperator);
|
||||
|
||||
UNSUCCESSFUL_VISIT(AndOperator);
|
||||
UNSUCCESSFUL_VISIT(IfOperator);
|
||||
UNSUCCESSFUL_VISIT(InListOperator);
|
||||
|
||||
UNSUCCESSFUL_VISIT(SubscriptOperator);
|
||||
|
||||
UNSUCCESSFUL_VISIT(ListSlicingOperator);
|
||||
UNSUCCESSFUL_VISIT(IsNullOperator);
|
||||
UNSUCCESSFUL_VISIT(PropertyLookup);
|
||||
UNSUCCESSFUL_VISIT(LabelsTest);
|
||||
|
||||
UNSUCCESSFUL_VISIT(PrimitiveLiteral);
|
||||
UNSUCCESSFUL_VISIT(ListLiteral);
|
||||
UNSUCCESSFUL_VISIT(MapLiteral);
|
||||
UNSUCCESSFUL_VISIT(Aggregation);
|
||||
UNSUCCESSFUL_VISIT(Coalesce);
|
||||
UNSUCCESSFUL_VISIT(Function);
|
||||
UNSUCCESSFUL_VISIT(Reduce);
|
||||
UNSUCCESSFUL_VISIT(Extract);
|
||||
UNSUCCESSFUL_VISIT(All);
|
||||
UNSUCCESSFUL_VISIT(Single);
|
||||
UNSUCCESSFUL_VISIT(Any);
|
||||
UNSUCCESSFUL_VISIT(None);
|
||||
UNSUCCESSFUL_VISIT(ParameterLookup);
|
||||
UNSUCCESSFUL_VISIT(RegexMatch);
|
||||
UNSUCCESSFUL_VISIT(Exists);
|
||||
|
||||
private:
|
||||
Frame *frame_;
|
||||
const SymbolTable *symbol_table_;
|
||||
const EvaluationContext *ctx_;
|
||||
};
|
||||
|
||||
class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
public:
|
||||
ExpressionEvaluator(Frame *frame, const SymbolTable &symbol_table, const EvaluationContext &ctx, DbAccessor *dba,
|
||||
@@ -159,50 +225,53 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
|
||||
TypedValue Visit(SubscriptOperator &list_indexing) override {
|
||||
auto lhs = list_indexing.expression1_->Accept(*this);
|
||||
ReferenceExpressionEvaluator referenceExpressionEvaluator(frame_, symbol_table_, ctx_);
|
||||
|
||||
TypedValue *lhs_ptr = list_indexing.expression1_->Accept(referenceExpressionEvaluator);
|
||||
TypedValue lhs;
|
||||
const auto referenced = nullptr != lhs_ptr;
|
||||
if (!referenced) {
|
||||
lhs = list_indexing.expression1_->Accept(*this);
|
||||
lhs_ptr = &lhs;
|
||||
}
|
||||
auto index = list_indexing.expression2_->Accept(*this);
|
||||
if (!lhs.IsList() && !lhs.IsMap() && !lhs.IsVertex() && !lhs.IsEdge() && !lhs.IsNull())
|
||||
if (!lhs_ptr->IsList() && !lhs_ptr->IsMap() && !lhs_ptr->IsVertex() && !lhs_ptr->IsEdge() && !lhs_ptr->IsNull())
|
||||
throw QueryRuntimeException(
|
||||
"Expected a list, a map, a node or an edge to index with '[]', got "
|
||||
"{}.",
|
||||
lhs.type());
|
||||
if (lhs.IsNull() || index.IsNull()) return TypedValue(ctx_->memory);
|
||||
if (lhs.IsList()) {
|
||||
lhs_ptr->type());
|
||||
if (lhs_ptr->IsNull() || index.IsNull()) return TypedValue(ctx_->memory);
|
||||
if (lhs_ptr->IsList()) {
|
||||
if (!index.IsInt()) throw QueryRuntimeException("Expected an integer as a list index, got {}.", index.type());
|
||||
auto index_int = index.ValueInt();
|
||||
// NOTE: Take non-const reference to list, so that we can move out the
|
||||
// indexed element as the result.
|
||||
auto &list = lhs.ValueList();
|
||||
auto &list = lhs_ptr->ValueList();
|
||||
if (index_int < 0) {
|
||||
index_int += static_cast<int64_t>(list.size());
|
||||
}
|
||||
if (index_int >= static_cast<int64_t>(list.size()) || index_int < 0) return TypedValue(ctx_->memory);
|
||||
// NOTE: Explicit move is needed, so that we return the move constructed
|
||||
// value and preserve the correct MemoryResource.
|
||||
return std::move(list[index_int]);
|
||||
return referenced ? TypedValue(list[index_int], ctx_->memory)
|
||||
: TypedValue(std::move(list[index_int]), ctx_->memory);
|
||||
}
|
||||
|
||||
if (lhs.IsMap()) {
|
||||
if (lhs_ptr->IsMap()) {
|
||||
if (!index.IsString()) throw QueryRuntimeException("Expected a string as a map index, got {}.", index.type());
|
||||
// NOTE: Take non-const reference to map, so that we can move out the
|
||||
// looked-up element as the result.
|
||||
auto &map = lhs.ValueMap();
|
||||
auto &map = lhs_ptr->ValueMap();
|
||||
auto found = map.find(index.ValueString());
|
||||
if (found == map.end()) return TypedValue(ctx_->memory);
|
||||
// NOTE: Explicit move is needed, so that we return the move constructed
|
||||
// value and preserve the correct MemoryResource.
|
||||
return std::move(found->second);
|
||||
return referenced ? TypedValue(found->second, ctx_->memory) : TypedValue(std::move(found->second), ctx_->memory);
|
||||
}
|
||||
|
||||
if (lhs.IsVertex()) {
|
||||
if (lhs_ptr->IsVertex()) {
|
||||
if (!index.IsString()) throw QueryRuntimeException("Expected a string as a property name, got {}.", index.type());
|
||||
return TypedValue(GetProperty(lhs.ValueVertex(), index.ValueString()), ctx_->memory);
|
||||
return {GetProperty(lhs_ptr->ValueVertex(), index.ValueString()), ctx_->memory};
|
||||
}
|
||||
|
||||
if (lhs.IsEdge()) {
|
||||
if (lhs_ptr->IsEdge()) {
|
||||
if (!index.IsString()) throw QueryRuntimeException("Expected a string as a property name, got {}.", index.type());
|
||||
return TypedValue(GetProperty(lhs.ValueEdge(), index.ValueString()), ctx_->memory);
|
||||
}
|
||||
return {GetProperty(lhs_ptr->ValueEdge(), index.ValueString()), ctx_->memory};
|
||||
};
|
||||
|
||||
// lhs is Null
|
||||
return TypedValue(ctx_->memory);
|
||||
@@ -258,7 +327,15 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
|
||||
TypedValue Visit(PropertyLookup &property_lookup) override {
|
||||
auto expression_result = property_lookup.expression_->Accept(*this);
|
||||
ReferenceExpressionEvaluator referenceExpressionEvaluator(frame_, symbol_table_, ctx_);
|
||||
|
||||
TypedValue *expression_result_ptr = property_lookup.expression_->Accept(referenceExpressionEvaluator);
|
||||
TypedValue expression_result;
|
||||
|
||||
if (nullptr == expression_result_ptr) {
|
||||
expression_result = property_lookup.expression_->Accept(*this);
|
||||
expression_result_ptr = &expression_result;
|
||||
}
|
||||
auto maybe_date = [this](const auto &date, const auto &prop_name) -> std::optional<TypedValue> {
|
||||
if (prop_name == "year") {
|
||||
return TypedValue(date.year, ctx_->memory);
|
||||
@@ -332,42 +409,38 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
switch (expression_result.type()) {
|
||||
switch (expression_result_ptr->type()) {
|
||||
case TypedValue::Type::Null:
|
||||
return TypedValue(ctx_->memory);
|
||||
case TypedValue::Type::Vertex:
|
||||
return TypedValue(GetProperty(expression_result.ValueVertex(), property_lookup.property_), ctx_->memory);
|
||||
return TypedValue(GetProperty(expression_result_ptr->ValueVertex(), property_lookup.property_), ctx_->memory);
|
||||
case TypedValue::Type::Edge:
|
||||
return TypedValue(GetProperty(expression_result.ValueEdge(), property_lookup.property_), ctx_->memory);
|
||||
return TypedValue(GetProperty(expression_result_ptr->ValueEdge(), property_lookup.property_), ctx_->memory);
|
||||
case TypedValue::Type::Map: {
|
||||
// NOTE: Take non-const reference to map, so that we can move out the
|
||||
// looked-up element as the result.
|
||||
auto &map = expression_result.ValueMap();
|
||||
auto &map = expression_result_ptr->ValueMap();
|
||||
auto found = map.find(property_lookup.property_.name.c_str());
|
||||
if (found == map.end()) return TypedValue(ctx_->memory);
|
||||
// NOTE: Explicit move is needed, so that we return the move constructed
|
||||
// value and preserve the correct MemoryResource.
|
||||
return std::move(found->second);
|
||||
return TypedValue(found->second, ctx_->memory);
|
||||
}
|
||||
case TypedValue::Type::Duration: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto &dur = expression_result.ValueDuration();
|
||||
const auto &dur = expression_result_ptr->ValueDuration();
|
||||
if (auto dur_field = maybe_duration(dur, prop_name); dur_field) {
|
||||
return std::move(*dur_field);
|
||||
return TypedValue(*dur_field, ctx_->memory);
|
||||
}
|
||||
throw QueryRuntimeException("Invalid property name {} for Duration", prop_name);
|
||||
}
|
||||
case TypedValue::Type::Date: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto &date = expression_result.ValueDate();
|
||||
const auto &date = expression_result_ptr->ValueDate();
|
||||
if (auto date_field = maybe_date(date, prop_name); date_field) {
|
||||
return std::move(*date_field);
|
||||
return TypedValue(*date_field, ctx_->memory);
|
||||
}
|
||||
throw QueryRuntimeException("Invalid property name {} for Date", prop_name);
|
||||
}
|
||||
case TypedValue::Type::LocalTime: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto < = expression_result.ValueLocalTime();
|
||||
const auto < = expression_result_ptr->ValueLocalTime();
|
||||
if (auto lt_field = maybe_local_time(lt, prop_name); lt_field) {
|
||||
return std::move(*lt_field);
|
||||
}
|
||||
@@ -375,20 +448,20 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
case TypedValue::Type::LocalDateTime: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto &ldt = expression_result.ValueLocalDateTime();
|
||||
const auto &ldt = expression_result_ptr->ValueLocalDateTime();
|
||||
if (auto date_field = maybe_date(ldt.date, prop_name); date_field) {
|
||||
return std::move(*date_field);
|
||||
}
|
||||
if (auto lt_field = maybe_local_time(ldt.local_time, prop_name); lt_field) {
|
||||
return std::move(*lt_field);
|
||||
return TypedValue(*lt_field, ctx_->memory);
|
||||
}
|
||||
throw QueryRuntimeException("Invalid property name {} for LocalDateTime", prop_name);
|
||||
}
|
||||
case TypedValue::Type::Graph: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto &graph = expression_result.ValueGraph();
|
||||
const auto &graph = expression_result_ptr->ValueGraph();
|
||||
if (auto graph_field = maybe_graph(graph, prop_name); graph_field) {
|
||||
return std::move(*graph_field);
|
||||
return TypedValue(*graph_field, ctx_->memory);
|
||||
}
|
||||
throw QueryRuntimeException("Invalid property name {} for Graph", prop_name);
|
||||
}
|
||||
@@ -547,6 +620,8 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
return TypedValue(result, ctx_->memory);
|
||||
}
|
||||
|
||||
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);
|
||||
if (list_value.IsNull()) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "auth/models.hpp"
|
||||
#include "glue/communication.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "memory/memory_control.hpp"
|
||||
#include "query/constants.hpp"
|
||||
#include "query/context.hpp"
|
||||
@@ -54,7 +55,6 @@
|
||||
#include "utils/event_counter.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/likely.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
@@ -299,7 +299,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa
|
||||
|
||||
Callback callback;
|
||||
|
||||
const auto license_check_result = utils::license::global_license_checker.IsValidLicense(utils::global_settings);
|
||||
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
||||
|
||||
static const std::unordered_set enterprise_only_methods{
|
||||
AuthQuery::Action::CREATE_ROLE, AuthQuery::Action::DROP_ROLE, AuthQuery::Action::SET_ROLE,
|
||||
@@ -309,7 +309,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa
|
||||
|
||||
if (license_check_result.HasError() && enterprise_only_methods.contains(auth_query->action_)) {
|
||||
throw utils::BasicException(
|
||||
utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "advanced authentication features"));
|
||||
license::LicenseCheckErrorToString(license_check_result.GetError(), "advanced authentication features"));
|
||||
}
|
||||
|
||||
switch (auth_query->action_) {
|
||||
@@ -1017,7 +1017,7 @@ PullPlan::PullPlan(const std::shared_ptr<CachedPlan> plan, const Parameters &par
|
||||
ctx_.evaluation_context.properties = NamesToProperties(plan->ast_storage().properties_, dba);
|
||||
ctx_.evaluation_context.labels = NamesToLabels(plan->ast_storage().labels_, dba);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && username.has_value() && dba) {
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && username.has_value() && dba) {
|
||||
ctx_.auth_checker = interpreter_context->auth_checker->GetFineGrainedAuthChecker(*username, dba);
|
||||
}
|
||||
#endif
|
||||
@@ -1044,7 +1044,7 @@ std::optional<plan::ProfilingStatsWithTotalTime> PullPlan::Pull(AnyStream *strea
|
||||
// Also, we want to throw only when the query engine requests more memory and not the storage
|
||||
// so we add the exception to the allocator.
|
||||
// TODO (mferencevic): Tune the parameters accordingly.
|
||||
utils::PoolResource pool_memory(128, 1024, &monotonic_memory);
|
||||
utils::PoolResource pool_memory(128, 1024, &monotonic_memory, utils::NewDeleteResource());
|
||||
std::optional<utils::LimitedMemoryResource> maybe_limited_resource;
|
||||
|
||||
if (memory_limit_) {
|
||||
@@ -2496,13 +2496,13 @@ void RunTriggersIndividually(const utils::SkipList<Trigger> &triggers, Interpret
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Interpreter::Commit() {
|
||||
Interpreter::TransactionCommitResponse Interpreter::Commit() {
|
||||
// It's possible that some queries did not finish because the user did
|
||||
// not pull all of the results from the query.
|
||||
// For now, we will not check if there are some unfinished queries.
|
||||
// We should document clearly that all results should be pulled to complete
|
||||
// a query.
|
||||
if (!db_accessor_) return;
|
||||
if (!db_accessor_) return Interpreter::TransactionCommitResponse{.success = true};
|
||||
|
||||
std::optional<TriggerContext> trigger_context = std::nullopt;
|
||||
if (trigger_context_collector_) {
|
||||
@@ -2590,8 +2590,15 @@ void Interpreter::Commit() {
|
||||
|
||||
SPDLOG_DEBUG("Finished committing the transaction");
|
||||
if (!commit_confirmed_by_all_sync_repplicas) {
|
||||
throw ReplicationException("At least one SYNC replica has not confirmed committing last transaction.");
|
||||
std::vector<Notification> notifications;
|
||||
notifications.reserve(1);
|
||||
notifications.emplace_back(SeverityLevel::WARNING, NotificationCode::REPLICA_PORT_WARNING,
|
||||
"At least one SYNC replica has not confirmed committing last transaction.");
|
||||
|
||||
return TransactionCommitResponse{.success = true, .notifications = std::move(notifications)};
|
||||
}
|
||||
|
||||
return Interpreter::TransactionCommitResponse{.success = true};
|
||||
}
|
||||
|
||||
void Interpreter::AdvanceCommand() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -326,6 +326,11 @@ class Interpreter final {
|
||||
}
|
||||
};
|
||||
|
||||
struct TransactionCommitResponse {
|
||||
bool success;
|
||||
std::vector<Notification> notifications;
|
||||
};
|
||||
|
||||
// Interpreter supports multiple prepared queries at the same time.
|
||||
// The client can reference a specific query for pull using an arbitrary qid
|
||||
// which is in our case the index of the query in the vector.
|
||||
@@ -354,7 +359,7 @@ class Interpreter final {
|
||||
std::optional<storage::IsolationLevel> next_transaction_isolation_level;
|
||||
|
||||
PreparedQuery PrepareTransactionQuery(std::string_view query_upper);
|
||||
void Commit();
|
||||
TransactionCommitResponse Commit();
|
||||
void AdvanceCommand();
|
||||
void AbortCommand(std::unique_ptr<QueryExecution> *query_execution);
|
||||
std::optional<storage::IsolationLevel> GetIsolationLevelOverride();
|
||||
@@ -402,28 +407,34 @@ std::map<std::string, TypedValue> Interpreter::Pull(TStream *result_stream, std:
|
||||
if (maybe_res) {
|
||||
// Save its summary
|
||||
maybe_summary.emplace(std::move(query_execution->summary));
|
||||
if (!query_execution->notifications.empty()) {
|
||||
std::vector<TypedValue> notifications;
|
||||
notifications.reserve(query_execution->notifications.size());
|
||||
for (const auto ¬ification : query_execution->notifications) {
|
||||
notifications.emplace_back(notification.ConvertToMap());
|
||||
}
|
||||
maybe_summary->insert_or_assign("notifications", std::move(notifications));
|
||||
std::vector<TypedValue> notifications;
|
||||
|
||||
notifications.reserve(query_execution->notifications.size());
|
||||
for (const auto ¬ification : query_execution->notifications) {
|
||||
notifications.emplace_back(notification.ConvertToMap());
|
||||
}
|
||||
|
||||
if (!in_explicit_transaction_) {
|
||||
switch (*maybe_res) {
|
||||
case QueryHandlerResult::COMMIT:
|
||||
Commit();
|
||||
case QueryHandlerResult::COMMIT: {
|
||||
auto commit_response = Commit();
|
||||
for (const auto ¬ification : commit_response.notifications) {
|
||||
notifications.emplace_back(notification.ConvertToMap());
|
||||
}
|
||||
|
||||
break;
|
||||
case QueryHandlerResult::ABORT:
|
||||
}
|
||||
case QueryHandlerResult::ABORT: {
|
||||
Abort();
|
||||
break;
|
||||
case QueryHandlerResult::NOTHING:
|
||||
}
|
||||
case QueryHandlerResult::NOTHING: {
|
||||
// The only cases in which we have nothing to do are those where
|
||||
// we're either in an explicit transaction or the query is such that
|
||||
// a transaction wasn't started on a call to `Prepare()`.
|
||||
MG_ASSERT(in_explicit_transaction_ || !db_accessor_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// As the transaction is done we can clear all the executions
|
||||
// NOTE: we cannot clear query_execution inside the Abort and Commit
|
||||
@@ -435,6 +446,10 @@ std::map<std::string, TypedValue> Interpreter::Pull(TStream *result_stream, std:
|
||||
// in the transaction can be in unfinished state
|
||||
query_execution.reset(nullptr);
|
||||
}
|
||||
|
||||
if (!notifications.empty()) {
|
||||
maybe_summary->insert_or_assign("notifications", std::move(notifications));
|
||||
}
|
||||
}
|
||||
} catch (const ExplicitTransactionUsageException &) {
|
||||
query_execution.reset(nullptr);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -42,6 +42,8 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
|
||||
return "CheckStream"sv;
|
||||
case NotificationCode::CREATE_TRIGGER:
|
||||
return "CreateTrigger"sv;
|
||||
case NotificationCode::COMMIT_TO_REPLICAS:
|
||||
return "CommitToReplicas"sv;
|
||||
case NotificationCode::DROP_CONSTRAINT:
|
||||
return "DropConstraint"sv;
|
||||
case NotificationCode::DROP_REPLICA:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -24,9 +24,10 @@ namespace memgraph::query {
|
||||
enum class SeverityLevel : uint8_t { INFO, WARNING };
|
||||
|
||||
enum class NotificationCode : uint8_t {
|
||||
CHECK_STREAM,
|
||||
COMMIT_TO_REPLICAS,
|
||||
CREATE_CONSTRAINT,
|
||||
CREATE_INDEX,
|
||||
CHECK_STREAM,
|
||||
CREATE_STREAM,
|
||||
CREATE_TRIGGER,
|
||||
DROP_CONSTRAINT,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -25,8 +25,10 @@
|
||||
|
||||
#include <cppitertools/chain.hpp>
|
||||
#include <cppitertools/imap.hpp>
|
||||
#include "query/common.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include "license/license.hpp"
|
||||
#include "query/auth_checker.hpp"
|
||||
#include "query/context.hpp"
|
||||
#include "query/db_accessor.hpp"
|
||||
@@ -40,6 +42,7 @@
|
||||
#include "query/procedure/cypher_types.hpp"
|
||||
#include "query/procedure/mg_procedure_impl.hpp"
|
||||
#include "query/procedure/module.hpp"
|
||||
#include "query/typed_value.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/view.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
@@ -47,7 +50,6 @@
|
||||
#include "utils/event_counter.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/fnv.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/likely.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
@@ -113,6 +115,8 @@ extern const Event UnionOperator;
|
||||
extern const Event CartesianOperator;
|
||||
extern const Event CallProcedureOperator;
|
||||
extern const Event ForeachOperator;
|
||||
extern const Event EmptyResultOperator;
|
||||
extern const Event EvaluatePatternFilterOperator;
|
||||
} // namespace EventCounter
|
||||
|
||||
namespace memgraph::query::plan {
|
||||
@@ -206,17 +210,18 @@ VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *fram
|
||||
storage::View::NEW);
|
||||
// TODO: PropsSetChecked allocates a PropertyValue, make it use context.memory
|
||||
// when we update PropertyValue with custom allocator.
|
||||
std::map<storage::PropertyId, storage::PropertyValue> properties;
|
||||
if (const auto *node_info_properties = std::get_if<PropertiesMapList>(&node_info.properties)) {
|
||||
for (const auto &[key, value_expression] : *node_info_properties) {
|
||||
PropsSetChecked(&new_node, key, value_expression->Accept(evaluator));
|
||||
properties.emplace(key, value_expression->Accept(evaluator));
|
||||
}
|
||||
} else {
|
||||
auto property_map = evaluator.Visit(*std::get<ParameterLookup *>(node_info.properties));
|
||||
for (const auto &[key, value] : property_map.ValueMap()) {
|
||||
auto property_id = dba.NameToProperty(key);
|
||||
PropsSetChecked(&new_node, property_id, value);
|
||||
properties.emplace(dba.NameToProperty(key), value);
|
||||
}
|
||||
}
|
||||
MultiPropsInitChecked(&new_node, properties);
|
||||
|
||||
(*frame)[node_info.symbol] = new_node;
|
||||
return (*frame)[node_info.symbol].ValueVertex();
|
||||
@@ -242,7 +247,7 @@ CreateNode::CreateNodeCursor::CreateNodeCursor(const CreateNode &self, utils::Me
|
||||
bool CreateNode::CreateNodeCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
SCOPED_PROFILE_OP("CreateNode");
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(self_.node_info_.labels,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||
throw QueryRuntimeException("Vertex not created due to not having enough permission!");
|
||||
@@ -297,17 +302,18 @@ EdgeAccessor CreateEdge(const EdgeCreationInfo &edge_info, DbAccessor *dba, Vert
|
||||
auto maybe_edge = dba->InsertEdge(from, to, edge_info.edge_type);
|
||||
if (maybe_edge.HasValue()) {
|
||||
auto &edge = *maybe_edge;
|
||||
if (const auto *properties = std::get_if<PropertiesMapList>(&edge_info.properties)) {
|
||||
for (const auto &[key, value_expression] : *properties) {
|
||||
PropsSetChecked(&edge, key, value_expression->Accept(*evaluator));
|
||||
std::map<storage::PropertyId, storage::PropertyValue> properties;
|
||||
if (const auto *edge_info_properties = std::get_if<PropertiesMapList>(&edge_info.properties)) {
|
||||
for (const auto &[key, value_expression] : *edge_info_properties) {
|
||||
properties.emplace(key, value_expression->Accept(*evaluator));
|
||||
}
|
||||
} else {
|
||||
auto property_map = evaluator->Visit(*std::get<ParameterLookup *>(edge_info.properties));
|
||||
for (const auto &[key, value] : property_map.ValueMap()) {
|
||||
auto property_id = dba->NameToProperty(key);
|
||||
PropsSetChecked(&edge, property_id, value);
|
||||
properties.emplace(dba->NameToProperty(key), value);
|
||||
}
|
||||
}
|
||||
if (!properties.empty()) MultiPropsInitChecked(&edge, properties);
|
||||
|
||||
(*frame)[edge_info.symbol] = edge;
|
||||
} else {
|
||||
@@ -334,7 +340,7 @@ bool CreateExpand::CreateExpandCursor::Pull(Frame &frame, ExecutionContext &cont
|
||||
if (!input_cursor_->Pull(frame, context)) return false;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
const auto fine_grained_permission = self_.existing_node_
|
||||
? memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE
|
||||
|
||||
@@ -408,10 +414,11 @@ VertexAccessor &CreateExpand::CreateExpandCursor::OtherVertex(Frame &frame, Exec
|
||||
template <class TVerticesFun>
|
||||
class ScanAllCursor : public Cursor {
|
||||
public:
|
||||
explicit ScanAllCursor(Symbol output_symbol, UniqueCursorPtr input_cursor, TVerticesFun get_vertices,
|
||||
const char *op_name)
|
||||
explicit ScanAllCursor(Symbol output_symbol, UniqueCursorPtr input_cursor, storage::View view,
|
||||
TVerticesFun get_vertices, const char *op_name)
|
||||
: output_symbol_(output_symbol),
|
||||
input_cursor_(std::move(input_cursor)),
|
||||
view_(view),
|
||||
get_vertices_(std::move(get_vertices)),
|
||||
op_name_(op_name) {}
|
||||
|
||||
@@ -433,8 +440,7 @@ class ScanAllCursor : public Cursor {
|
||||
vertices_it_.emplace(vertices_.value().begin());
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
!FindNextVertex(context)) {
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker && !FindNextVertex(context)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -447,7 +453,7 @@ class ScanAllCursor : public Cursor {
|
||||
#ifdef MG_ENTERPRISE
|
||||
bool FindNextVertex(const ExecutionContext &context) {
|
||||
while (vertices_it_.value() != vertices_.value().end()) {
|
||||
if (context.auth_checker->Has(*vertices_it_.value(), memgraph::storage::View::OLD,
|
||||
if (context.auth_checker->Has(*vertices_it_.value(), view_,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ)) {
|
||||
return true;
|
||||
}
|
||||
@@ -468,6 +474,7 @@ class ScanAllCursor : public Cursor {
|
||||
private:
|
||||
const Symbol output_symbol_;
|
||||
const UniqueCursorPtr input_cursor_;
|
||||
storage::View view_;
|
||||
TVerticesFun get_vertices_;
|
||||
std::optional<typename std::result_of<TVerticesFun(Frame &, ExecutionContext &)>::type::value_type> vertices_;
|
||||
std::optional<decltype(vertices_.value().begin())> vertices_it_;
|
||||
@@ -486,7 +493,7 @@ UniqueCursorPtr ScanAll::MakeCursor(utils::MemoryResource *mem) const {
|
||||
auto *db = context.db_accessor;
|
||||
return std::make_optional(db->Vertices(view_));
|
||||
};
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem),
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
|
||||
std::move(vertices), "ScanAll");
|
||||
}
|
||||
|
||||
@@ -509,7 +516,7 @@ UniqueCursorPtr ScanAllByLabel::MakeCursor(utils::MemoryResource *mem) const {
|
||||
auto *db = context.db_accessor;
|
||||
return std::make_optional(db->Vertices(view_, label_));
|
||||
};
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem),
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
|
||||
std::move(vertices), "ScanAllByLabel");
|
||||
}
|
||||
|
||||
@@ -574,7 +581,7 @@ UniqueCursorPtr ScanAllByLabelPropertyRange::MakeCursor(utils::MemoryResource *m
|
||||
if (maybe_upper && maybe_upper->value().IsNull()) return std::nullopt;
|
||||
return std::make_optional(db->Vertices(view_, label_, property_, maybe_lower, maybe_upper));
|
||||
};
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem),
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
|
||||
std::move(vertices), "ScanAllByLabelPropertyRange");
|
||||
}
|
||||
|
||||
@@ -606,7 +613,7 @@ UniqueCursorPtr ScanAllByLabelPropertyValue::MakeCursor(utils::MemoryResource *m
|
||||
}
|
||||
return std::make_optional(db->Vertices(view_, label_, property_, storage::PropertyValue(value)));
|
||||
};
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem),
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
|
||||
std::move(vertices), "ScanAllByLabelPropertyValue");
|
||||
}
|
||||
|
||||
@@ -624,7 +631,7 @@ UniqueCursorPtr ScanAllByLabelProperty::MakeCursor(utils::MemoryResource *mem) c
|
||||
auto *db = context.db_accessor;
|
||||
return std::make_optional(db->Vertices(view_, label_, property_));
|
||||
};
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem),
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
|
||||
std::move(vertices), "ScanAllByLabelProperty");
|
||||
}
|
||||
|
||||
@@ -650,7 +657,7 @@ UniqueCursorPtr ScanAllById::MakeCursor(utils::MemoryResource *mem) const {
|
||||
if (!maybe_vertex) return std::nullopt;
|
||||
return std::vector<VertexAccessor>{*maybe_vertex};
|
||||
};
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem),
|
||||
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
|
||||
std::move(vertices), "ScanAllById");
|
||||
}
|
||||
|
||||
@@ -731,7 +738,7 @@ bool Expand::ExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
if (in_edges_ && *in_edges_it_ != in_edges_->end()) {
|
||||
auto edge = *(*in_edges_it_)++;
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge.From(), self_.view_,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -752,7 +759,7 @@ bool Expand::ExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
// already done in the block above
|
||||
if (self_.common_.direction == EdgeAtom::Direction::BOTH && edge.IsCycle()) continue;
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge.To(), self_.view_,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1089,7 +1096,7 @@ class ExpandVariableCursor : public Cursor {
|
||||
VertexAccessor current_vertex =
|
||||
current_edge.second == EdgeAtom::Direction::IN ? current_edge.first.From() : current_edge.first.To();
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(current_edge.first, memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(current_vertex, storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1258,7 +1265,7 @@ class STShortestPathCursor : public query::plan::Cursor {
|
||||
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
for (const auto &edge : out_edges) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge.To(), storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1284,7 +1291,7 @@ class STShortestPathCursor : public query::plan::Cursor {
|
||||
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
for (const auto &edge : in_edges) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge.From(), storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1324,7 +1331,7 @@ class STShortestPathCursor : public query::plan::Cursor {
|
||||
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
for (const auto &edge : out_edges) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge.To(), storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1349,7 +1356,7 @@ class STShortestPathCursor : public query::plan::Cursor {
|
||||
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
for (const auto &edge : in_edges) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge.From(), storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1406,7 +1413,7 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
|
||||
// if we already processed the given vertex it doesn't get expanded
|
||||
if (processed_.find(vertex) != processed_.end()) return;
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(vertex, storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1592,7 +1599,7 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
|
||||
int64_t depth) {
|
||||
auto *memory = evaluator.GetMemoryResource();
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(vertex, storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1902,7 +1909,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
for (const auto &edge : out_edges) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge.To(), storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1916,7 +1923,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
for (const auto &edge : in_edges) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge.From(), storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
@@ -1973,7 +1980,6 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
edges_on_frame.emplace(edges_on_frame.begin(), current_edge);
|
||||
|
||||
auto next_vertex = current_edge_direction == EdgeAtom::Direction::IN ? current_edge.From() : current_edge.To();
|
||||
frame[self_.common_.node_symbol] = next_vertex;
|
||||
frame[self_.total_weight_.value()] = current_weight;
|
||||
|
||||
if (next_edges_.find({next_vertex, traversal_stack_.size()}) != next_edges_.end()) {
|
||||
@@ -1986,6 +1992,15 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
}
|
||||
|
||||
if ((current_weight > visited_cost_.at(next_vertex)).ValueBool()) continue;
|
||||
|
||||
// Place destination node on the frame, handle existence flag
|
||||
if (self_.common_.existing_node) {
|
||||
const auto &node = frame[self_.common_.node_symbol];
|
||||
ExpectType(self_.common_.node_symbol, node, TypedValue::Type::Vertex);
|
||||
if (node.ValueVertex() != next_vertex) continue;
|
||||
} else {
|
||||
frame[self_.common_.node_symbol] = next_vertex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2011,7 +2026,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
next_edges_.clear();
|
||||
traversal_stack_.clear();
|
||||
|
||||
pq_.push({TypedValue(), 0, *start_vertex, std::nullopt});
|
||||
expand_from_vertex(*start_vertex, TypedValue(), 0);
|
||||
visited_cost_.emplace(*start_vertex, 0);
|
||||
frame[self_.common_.edge_symbol] = TypedValue::TVector(memory);
|
||||
}
|
||||
@@ -2020,33 +2035,28 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
while (!pq_.empty()) {
|
||||
if (MustAbort(context)) throw HintedAbortError();
|
||||
|
||||
auto [current_weight, current_depth, current_vertex, maybe_directed_edge] = pq_.top();
|
||||
const auto [current_weight, current_depth, current_vertex, directed_edge] = pq_.top();
|
||||
pq_.pop();
|
||||
|
||||
const auto &[current_edge, direction, weight] = directed_edge;
|
||||
if (expanded_.contains(current_edge)) continue;
|
||||
expanded_.emplace(current_edge);
|
||||
|
||||
// Expand only if what we've just expanded is less than max depth.
|
||||
if (current_depth < upper_bound_) {
|
||||
if (maybe_directed_edge) {
|
||||
auto &[current_edge, direction, weight] = *maybe_directed_edge;
|
||||
if (expanded_.find(current_edge) != expanded_.end()) continue;
|
||||
expanded_.emplace(current_edge);
|
||||
}
|
||||
expand_from_vertex(current_vertex, current_weight, current_depth);
|
||||
}
|
||||
|
||||
// if current vertex is not starting vertex, maybe_directed_edge will not be nullopt
|
||||
if (maybe_directed_edge) {
|
||||
auto &[current_edge, direction, weight] = *maybe_directed_edge;
|
||||
// Searching for a previous vertex in the expansion
|
||||
auto prev_vertex = direction == EdgeAtom::Direction::IN ? current_edge.To() : current_edge.From();
|
||||
// Searching for a previous vertex in the expansion
|
||||
auto prev_vertex = direction == EdgeAtom::Direction::IN ? current_edge.To() : current_edge.From();
|
||||
|
||||
// Update the parent
|
||||
if (next_edges_.find({prev_vertex, current_depth - 1}) == next_edges_.end()) {
|
||||
utils::pmr::list<DirectedEdge> empty(memory);
|
||||
next_edges_[{prev_vertex, current_depth - 1}] = std::move(empty);
|
||||
}
|
||||
|
||||
next_edges_.at({prev_vertex, current_depth - 1}).emplace_back(*maybe_directed_edge);
|
||||
// Update the parent
|
||||
if (next_edges_.find({prev_vertex, current_depth - 1}) == next_edges_.end()) {
|
||||
utils::pmr::list<DirectedEdge> empty(memory);
|
||||
next_edges_[{prev_vertex, current_depth - 1}] = std::move(empty);
|
||||
}
|
||||
|
||||
next_edges_.at({prev_vertex, current_depth - 1}).emplace_back(directed_edge);
|
||||
}
|
||||
|
||||
if (start_vertex && next_edges_.find({*start_vertex, 0}) != next_edges_.end()) {
|
||||
@@ -2103,8 +2113,8 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
// Priority queue comparator. Keep lowest weight on top of the queue.
|
||||
class PriorityQueueComparator {
|
||||
public:
|
||||
bool operator()(const std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<DirectedEdge>> &lhs,
|
||||
const std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<DirectedEdge>> &rhs) {
|
||||
bool operator()(const std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge> &lhs,
|
||||
const std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge> &rhs) {
|
||||
const auto &lhs_weight = std::get<0>(lhs);
|
||||
const auto &rhs_weight = std::get<0>(rhs);
|
||||
// Null defines minimum value for all types
|
||||
@@ -2123,8 +2133,8 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
|
||||
|
||||
// Priority queue - core element of the algorithm.
|
||||
// Stores: {weight, depth, next vertex, edge and direction}
|
||||
std::priority_queue<std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<DirectedEdge>>,
|
||||
utils::pmr::vector<std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<DirectedEdge>>>,
|
||||
std::priority_queue<std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge>,
|
||||
utils::pmr::vector<std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge>>,
|
||||
PriorityQueueComparator>
|
||||
pq_;
|
||||
|
||||
@@ -2248,10 +2258,19 @@ std::vector<Symbol> ConstructNamedPath::ModifiedSymbols(const SymbolTable &table
|
||||
return symbols;
|
||||
}
|
||||
|
||||
Filter::Filter(const std::shared_ptr<LogicalOperator> &input, Expression *expression)
|
||||
: input_(input ? input : std::make_shared<Once>()), expression_(expression) {}
|
||||
Filter::Filter(const std::shared_ptr<LogicalOperator> &input,
|
||||
const std::vector<std::shared_ptr<LogicalOperator>> &pattern_filters, Expression *expression)
|
||||
: input_(input ? input : std::make_shared<Once>()), pattern_filters_(pattern_filters), expression_(expression) {}
|
||||
|
||||
ACCEPT_WITH_INPUT(Filter)
|
||||
bool Filter::Accept(HierarchicalLogicalOperatorVisitor &visitor) {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
input_->Accept(visitor);
|
||||
for (const auto &pattern_filter : pattern_filters_) {
|
||||
pattern_filter->Accept(visitor);
|
||||
}
|
||||
}
|
||||
return visitor.PostVisit(*this);
|
||||
}
|
||||
|
||||
UniqueCursorPtr Filter::MakeCursor(utils::MemoryResource *mem) const {
|
||||
EventCounter::IncrementCounter(EventCounter::FilterOperator);
|
||||
@@ -2261,8 +2280,24 @@ UniqueCursorPtr Filter::MakeCursor(utils::MemoryResource *mem) const {
|
||||
|
||||
std::vector<Symbol> Filter::ModifiedSymbols(const SymbolTable &table) const { return input_->ModifiedSymbols(table); }
|
||||
|
||||
static std::vector<UniqueCursorPtr> MakeCursorVector(const std::vector<std::shared_ptr<LogicalOperator>> &ops,
|
||||
utils::MemoryResource *mem) {
|
||||
std::vector<UniqueCursorPtr> cursors;
|
||||
cursors.reserve(ops.size());
|
||||
|
||||
if (!ops.empty()) {
|
||||
for (const auto &op : ops) {
|
||||
cursors.push_back(op->MakeCursor(mem));
|
||||
}
|
||||
}
|
||||
|
||||
return cursors;
|
||||
}
|
||||
|
||||
Filter::FilterCursor::FilterCursor(const Filter &self, utils::MemoryResource *mem)
|
||||
: self_(self), input_cursor_(self_.input_->MakeCursor(mem)) {}
|
||||
: self_(self),
|
||||
input_cursor_(self_.input_->MakeCursor(mem)),
|
||||
pattern_filter_cursors_(MakeCursorVector(self_.pattern_filters_, mem)) {}
|
||||
|
||||
bool Filter::FilterCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
SCOPED_PROFILE_OP("Filter");
|
||||
@@ -2272,6 +2307,10 @@ bool Filter::FilterCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
|
||||
storage::View::OLD);
|
||||
while (input_cursor_->Pull(frame, context)) {
|
||||
for (const auto &pattern_filter_cursor : pattern_filter_cursors_) {
|
||||
pattern_filter_cursor->Pull(frame, context);
|
||||
}
|
||||
|
||||
if (EvaluateFilter(evaluator, self_.expression_)) return true;
|
||||
}
|
||||
return false;
|
||||
@@ -2281,6 +2320,39 @@ void Filter::FilterCursor::Shutdown() { input_cursor_->Shutdown(); }
|
||||
|
||||
void Filter::FilterCursor::Reset() { input_cursor_->Reset(); }
|
||||
|
||||
EvaluatePatternFilter::EvaluatePatternFilter(const std::shared_ptr<LogicalOperator> &input, Symbol output_symbol)
|
||||
: input_(input), output_symbol_(output_symbol) {}
|
||||
|
||||
ACCEPT_WITH_INPUT(EvaluatePatternFilter);
|
||||
|
||||
UniqueCursorPtr EvaluatePatternFilter::MakeCursor(utils::MemoryResource *mem) const {
|
||||
EventCounter::IncrementCounter(EventCounter::EvaluatePatternFilterOperator);
|
||||
|
||||
return MakeUniqueCursorPtr<EvaluatePatternFilterCursor>(mem, *this, mem);
|
||||
}
|
||||
|
||||
EvaluatePatternFilter::EvaluatePatternFilterCursor::EvaluatePatternFilterCursor(const EvaluatePatternFilter &self,
|
||||
utils::MemoryResource *mem)
|
||||
: self_(self), input_cursor_(self_.input_->MakeCursor(mem)) {}
|
||||
|
||||
std::vector<Symbol> EvaluatePatternFilter::ModifiedSymbols(const SymbolTable &table) const {
|
||||
return input_->ModifiedSymbols(table);
|
||||
}
|
||||
|
||||
bool EvaluatePatternFilter::EvaluatePatternFilterCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
SCOPED_PROFILE_OP("EvaluatePatternFilter");
|
||||
|
||||
input_cursor_->Reset();
|
||||
|
||||
frame[self_.output_symbol_] = TypedValue(input_cursor_->Pull(frame, context), context.evaluation_context.memory);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EvaluatePatternFilter::EvaluatePatternFilterCursor::Shutdown() { input_cursor_->Shutdown(); }
|
||||
|
||||
void EvaluatePatternFilter::EvaluatePatternFilterCursor::Reset() { input_cursor_->Reset(); }
|
||||
|
||||
Produce::Produce(const std::shared_ptr<LogicalOperator> &input, const std::vector<NamedExpression *> &named_expressions)
|
||||
: input_(input ? input : std::make_shared<Once>()), named_expressions_(named_expressions) {}
|
||||
|
||||
@@ -2366,7 +2438,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
if (expression_result.type() == TypedValue::Type::Edge) {
|
||||
auto &ea = expression_result.ValueEdge();
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(ea, query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE) &&
|
||||
context.auth_checker->Has(ea.To(), storage::View::NEW, query::AuthQuery::FineGrainedPrivilege::UPDATE) &&
|
||||
context.auth_checker->Has(ea.From(), storage::View::NEW, query::AuthQuery::FineGrainedPrivilege::UPDATE))) {
|
||||
@@ -2399,7 +2471,7 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
case TypedValue::Type::Vertex: {
|
||||
auto &va = expression_result.ValueVertex();
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(va, storage::View::NEW, query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||
throw QueryRuntimeException("Vertex not deleted due to not having enough permission!");
|
||||
}
|
||||
@@ -2508,7 +2580,7 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
|
||||
switch (lhs.type()) {
|
||||
case TypedValue::Type::Vertex: {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(lhs.ValueVertex(), storage::View::NEW,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw QueryRuntimeException("Vertex property not set due to not having enough permission!");
|
||||
@@ -2525,7 +2597,7 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
|
||||
}
|
||||
case TypedValue::Type::Edge: {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(lhs.ValueEdge(), memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw QueryRuntimeException("Edge property not set due to not having enough permission!");
|
||||
}
|
||||
@@ -2724,7 +2796,7 @@ bool SetProperties::SetPropertiesCursor::Pull(Frame &frame, ExecutionContext &co
|
||||
switch (lhs.type()) {
|
||||
case TypedValue::Type::Vertex:
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(lhs.ValueVertex(), storage::View::NEW,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw QueryRuntimeException("Vertex properties not set due to not having enough permission!");
|
||||
@@ -2735,7 +2807,7 @@ bool SetProperties::SetPropertiesCursor::Pull(Frame &frame, ExecutionContext &co
|
||||
break;
|
||||
case TypedValue::Type::Edge:
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(lhs.ValueEdge(), memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw QueryRuntimeException("Edge properties not set due to not having enough permission!");
|
||||
}
|
||||
@@ -2778,7 +2850,7 @@ bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
SCOPED_PROFILE_OP("SetLabels");
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(self_.labels_, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||
throw QueryRuntimeException("Couldn't set label due to not having enough permission!");
|
||||
}
|
||||
@@ -2793,7 +2865,7 @@ bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
auto &vertex = vertex_value.ValueVertex();
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(vertex, storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw QueryRuntimeException("Couldn't set label due to not having enough permission!");
|
||||
@@ -2883,7 +2955,7 @@ bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame, ExecutionContext &
|
||||
switch (lhs.type()) {
|
||||
case TypedValue::Type::Vertex:
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(lhs.ValueVertex(), storage::View::NEW,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw QueryRuntimeException("Vertex property not removed due to not having enough permission!");
|
||||
@@ -2893,7 +2965,7 @@ bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame, ExecutionContext &
|
||||
break;
|
||||
case TypedValue::Type::Edge:
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(lhs.ValueEdge(), memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw QueryRuntimeException("Edge property not removed due to not having enough permission!");
|
||||
}
|
||||
@@ -2936,7 +3008,7 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &cont
|
||||
SCOPED_PROFILE_OP("RemoveLabels");
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(self_.labels_, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||
throw QueryRuntimeException("Couldn't remove label due to not having enough permission!");
|
||||
}
|
||||
@@ -2951,7 +3023,7 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &cont
|
||||
auto &vertex = vertex_value.ValueVertex();
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (utils::license::global_license_checker.IsValidLicenseFast() && context.auth_checker &&
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!context.auth_checker->Has(vertex, storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw QueryRuntimeException("Couldn't remove label due to not having enough permission!");
|
||||
@@ -3050,6 +3122,56 @@ void EdgeUniquenessFilter::EdgeUniquenessFilterCursor::Shutdown() { input_cursor
|
||||
|
||||
void EdgeUniquenessFilter::EdgeUniquenessFilterCursor::Reset() { input_cursor_->Reset(); }
|
||||
|
||||
EmptyResult::EmptyResult(const std::shared_ptr<LogicalOperator> &input)
|
||||
: input_(input ? input : std::make_shared<Once>()) {}
|
||||
|
||||
ACCEPT_WITH_INPUT(EmptyResult)
|
||||
|
||||
std::vector<Symbol> EmptyResult::OutputSymbols(const SymbolTable &) const { // NOLINT(hicpp-named-parameter)
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Symbol> EmptyResult::ModifiedSymbols(const SymbolTable &) const { // NOLINT(hicpp-named-parameter)
|
||||
return {};
|
||||
}
|
||||
|
||||
class EmptyResultCursor : public Cursor {
|
||||
public:
|
||||
EmptyResultCursor(const EmptyResult &self, utils::MemoryResource *mem)
|
||||
: input_cursor_(self.input_->MakeCursor(mem)) {}
|
||||
|
||||
bool Pull(Frame &frame, ExecutionContext &context) override {
|
||||
SCOPED_PROFILE_OP("EmptyResult");
|
||||
|
||||
if (!pulled_all_input_) {
|
||||
while (input_cursor_->Pull(frame, context)) {
|
||||
if (MustAbort(context)) {
|
||||
throw HintedAbortError();
|
||||
}
|
||||
}
|
||||
pulled_all_input_ = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Shutdown() override { input_cursor_->Shutdown(); }
|
||||
|
||||
void Reset() override {
|
||||
input_cursor_->Reset();
|
||||
pulled_all_input_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
const UniqueCursorPtr input_cursor_;
|
||||
bool pulled_all_input_{false};
|
||||
};
|
||||
|
||||
UniqueCursorPtr EmptyResult::MakeCursor(utils::MemoryResource *mem) const {
|
||||
EventCounter::IncrementCounter(EventCounter::EmptyResultOperator);
|
||||
|
||||
return MakeUniqueCursorPtr<EmptyResultCursor>(mem, *this, mem);
|
||||
}
|
||||
|
||||
Accumulate::Accumulate(const std::shared_ptr<LogicalOperator> &input, const std::vector<Symbol> &symbols,
|
||||
bool advance_command)
|
||||
: input_(input), symbols_(symbols), advance_command_(advance_command) {}
|
||||
@@ -3204,7 +3326,8 @@ class AggregateCursor : public Cursor {
|
||||
// aggregation map. The vectors in an AggregationValue contain one element for
|
||||
// each aggregation in this LogicalOp.
|
||||
struct AggregationValue {
|
||||
explicit AggregationValue(utils::MemoryResource *mem) : counts_(mem), values_(mem), remember_(mem) {}
|
||||
explicit AggregationValue(utils::MemoryResource *mem)
|
||||
: counts_(mem), values_(mem), remember_(mem), unique_values_(mem) {}
|
||||
|
||||
// how many input rows have been aggregated in respective values_ element so
|
||||
// far
|
||||
@@ -3217,6 +3340,10 @@ class AggregateCursor : public Cursor {
|
||||
utils::pmr::vector<TypedValue> values_;
|
||||
// remember values.
|
||||
utils::pmr::vector<TypedValue> remember_;
|
||||
|
||||
using TSet = utils::pmr::unordered_set<TypedValue, TypedValue::Hash, TypedValue::BoolEqual>;
|
||||
|
||||
utils::pmr::vector<TSet> unique_values_;
|
||||
};
|
||||
|
||||
const Aggregate &self_;
|
||||
@@ -3292,6 +3419,7 @@ class AggregateCursor : public Cursor {
|
||||
for (const auto &agg_elem : self_.aggregations_) {
|
||||
auto *mem = agg_value->values_.get_allocator().GetMemoryResource();
|
||||
agg_value->values_.emplace_back(DefaultAggregationOpValue(agg_elem, mem));
|
||||
agg_value->unique_values_.emplace_back(AggregationValue::TSet(mem));
|
||||
}
|
||||
agg_value->counts_.resize(self_.aggregations_.size(), 0);
|
||||
|
||||
@@ -3310,8 +3438,9 @@ class AggregateCursor : public Cursor {
|
||||
|
||||
auto count_it = agg_value->counts_.begin();
|
||||
auto value_it = agg_value->values_.begin();
|
||||
auto unique_values_it = agg_value->unique_values_.begin();
|
||||
auto agg_elem_it = self_.aggregations_.begin();
|
||||
for (; count_it < agg_value->counts_.end(); count_it++, value_it++, agg_elem_it++) {
|
||||
for (; count_it < agg_value->counts_.end(); count_it++, value_it++, unique_values_it++, agg_elem_it++) {
|
||||
// COUNT(*) is the only case where input expression is optional
|
||||
// handle it here
|
||||
auto input_expr_ptr = agg_elem_it->value;
|
||||
@@ -3326,6 +3455,12 @@ class AggregateCursor : public Cursor {
|
||||
// Aggregations skip Null input values.
|
||||
if (input_value.IsNull()) continue;
|
||||
const auto &agg_op = agg_elem_it->op;
|
||||
if (agg_elem_it->distinct) {
|
||||
auto insert_result = unique_values_it->insert(input_value);
|
||||
if (!insert_result.second) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*count_it += 1;
|
||||
if (*count_it == 1) {
|
||||
// first value, nothing to aggregate. check type, set and continue.
|
||||
@@ -4456,24 +4591,24 @@ auto ToOptionalString(ExpressionEvaluator *evaluator, Expression *expression) ->
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
TypedValue CsvRowToTypedList(csv::Reader::Row row) {
|
||||
TypedValue CsvRowToTypedList(csv::Reader::Row &row) {
|
||||
auto *mem = row.get_allocator().GetMemoryResource();
|
||||
auto typed_columns = utils::pmr::vector<TypedValue>(mem);
|
||||
typed_columns.reserve(row.size());
|
||||
for (auto &column : row) {
|
||||
typed_columns.emplace_back(std::move(column));
|
||||
}
|
||||
return TypedValue(typed_columns, mem);
|
||||
return {std::move(typed_columns), mem};
|
||||
}
|
||||
|
||||
TypedValue CsvRowToTypedMap(csv::Reader::Row row, csv::Reader::Header header) {
|
||||
TypedValue CsvRowToTypedMap(csv::Reader::Row &row, csv::Reader::Header header) {
|
||||
// a valid row has the same number of elements as the header
|
||||
auto *mem = row.get_allocator().GetMemoryResource();
|
||||
utils::pmr::map<utils::pmr::string, TypedValue> m(mem);
|
||||
for (auto i = 0; i < row.size(); ++i) {
|
||||
m.emplace(std::move(header[i]), std::move(row[i]));
|
||||
}
|
||||
return TypedValue(m, mem);
|
||||
return {std::move(m), mem};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -4512,18 +4647,17 @@ class LoadCsvCursor : public Cursor {
|
||||
// have to read at most cardinality(n) rows (but we can read less and stop
|
||||
// pulling MATCH).
|
||||
if (!input_is_once_ && !input_pulled) return false;
|
||||
|
||||
if (auto row = reader_->GetNextRow(context.evaluation_context.memory)) {
|
||||
if (!reader_->HasHeader()) {
|
||||
frame[self_->row_var_] = CsvRowToTypedList(std::move(*row));
|
||||
} else {
|
||||
frame[self_->row_var_] = CsvRowToTypedMap(
|
||||
std::move(*row), csv::Reader::Header(reader_->GetHeader(), context.evaluation_context.memory));
|
||||
}
|
||||
return true;
|
||||
auto row = reader_->GetNextRow(context.evaluation_context.memory);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!reader_->HasHeader()) {
|
||||
frame[self_->row_var_] = CsvRowToTypedList(*row);
|
||||
} else {
|
||||
frame[self_->row_var_] =
|
||||
CsvRowToTypedMap(*row, csv::Reader::Header(reader_->GetHeader(), context.evaluation_context.memory));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Reset() override { input_cursor_->Reset(); }
|
||||
|
||||
@@ -132,6 +132,8 @@ class Cartesian;
|
||||
class CallProcedure;
|
||||
class LoadCsv;
|
||||
class Foreach;
|
||||
class EmptyResult;
|
||||
class EvaluatePatternFilter;
|
||||
|
||||
using LogicalOperatorCompositeVisitor = utils::CompositeVisitor<
|
||||
Once, CreateNode, CreateExpand, ScanAll, ScanAllByLabel,
|
||||
@@ -140,7 +142,7 @@ using LogicalOperatorCompositeVisitor = utils::CompositeVisitor<
|
||||
Expand, ExpandVariable, ConstructNamedPath, Filter, Produce, Delete,
|
||||
SetProperty, SetProperties, SetLabels, RemoveProperty, RemoveLabels,
|
||||
EdgeUniquenessFilter, Accumulate, Aggregate, Skip, Limit, OrderBy, Merge,
|
||||
Optional, Unwind, Distinct, Union, Cartesian, CallProcedure, LoadCsv, Foreach>;
|
||||
Optional, Unwind, Distinct, Union, Cartesian, CallProcedure, LoadCsv, Foreach, EmptyResult, EvaluatePatternFilter>;
|
||||
|
||||
using LogicalOperatorLeafVisitor = utils::LeafVisitor<Once>;
|
||||
|
||||
@@ -1121,6 +1123,9 @@ pulled.")
|
||||
((input "std::shared_ptr<LogicalOperator>" :scope :public
|
||||
:slk-save #'slk-save-operator-pointer
|
||||
:slk-load #'slk-load-operator-pointer)
|
||||
(pattern_filters "std::vector<std::shared_ptr<LogicalOperator>>" :scope :public
|
||||
:slk-save #'slk-save-ast-vector
|
||||
:slk-load (slk-load-ast-vector "std::shared_ptr<LogicalOperator>"))
|
||||
(expression "Expression *" :scope :public
|
||||
:slk-save #'slk-save-ast-pointer
|
||||
:slk-load (slk-load-ast-pointer "Expression")))
|
||||
@@ -1135,6 +1140,7 @@ a boolean value.")
|
||||
Filter() {}
|
||||
|
||||
Filter(const std::shared_ptr<LogicalOperator> &input_,
|
||||
const std::vector<std::shared_ptr<LogicalOperator>> &pattern_filters_,
|
||||
Expression *expression_);
|
||||
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
||||
UniqueCursorPtr MakeCursor(utils::MemoryResource *) const override;
|
||||
@@ -1158,6 +1164,7 @@ a boolean value.")
|
||||
private:
|
||||
const Filter &self_;
|
||||
const UniqueCursorPtr input_cursor_;
|
||||
const std::vector<UniqueCursorPtr> pattern_filter_cursors_;
|
||||
};
|
||||
cpp<#)
|
||||
(:serialize (:slk))
|
||||
@@ -1554,6 +1561,41 @@ edge lists).")
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
|
||||
(lcp:define-class empty-result (logical-operator)
|
||||
((input "std::shared_ptr<LogicalOperator>" :scope :public
|
||||
:slk-save #'slk-save-operator-pointer
|
||||
:slk-load #'slk-load-operator-pointer))
|
||||
(:documentation
|
||||
"Pulls everything from the input and discards it.
|
||||
|
||||
On the first Pull from this operator's Cursor the input Cursor will be Pulled
|
||||
until it is empty. The results won't be accumulated in the temporary cache.
|
||||
|
||||
This technique is used for ensuring that the cursor has been exhausted after
|
||||
a WriteHandleClause. A typical use case is a `MATCH--SET` query with RETURN statement
|
||||
missing.
|
||||
@param input Input @c LogicalOperator. ")
|
||||
(:public
|
||||
#>cpp
|
||||
EmptyResult() {}
|
||||
|
||||
EmptyResult(const std::shared_ptr<LogicalOperator> &input);
|
||||
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
||||
UniqueCursorPtr MakeCursor(utils::MemoryResource *) const override;
|
||||
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
||||
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
||||
|
||||
bool HasSingleInput() const override { return true; }
|
||||
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
||||
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
||||
input_ = input;
|
||||
}
|
||||
cpp<#)
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
|
||||
(lcp:define-class accumulate (logical-operator)
|
||||
((input "std::shared_ptr<LogicalOperator>" :scope :public
|
||||
:slk-save #'slk-save-operator-pointer
|
||||
@@ -1657,7 +1699,8 @@ elements are in an undefined state after aggregation.")
|
||||
:slk-save #'slk-save-ast-pointer
|
||||
:slk-load (slk-load-ast-pointer "Expression"))
|
||||
(op "::Aggregation::Op")
|
||||
(output-sym "Symbol"))
|
||||
(output-sym "Symbol")
|
||||
(distinct bool :initval "false" ))
|
||||
(:documentation
|
||||
"An aggregation element, contains:
|
||||
(input data expression, key expression - only used in COLLECT_MAP, type of
|
||||
@@ -1740,6 +1783,45 @@ operator's implementation does not expect this.")
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
(lcp:define-class evaluate-pattern-filter (logical-operator)
|
||||
((input "std::shared_ptr<LogicalOperator>" :scope :public
|
||||
:slk-save #'slk-save-operator-pointer
|
||||
:slk-load #'slk-load-operator-pointer)
|
||||
(output-symbol "Symbol" :scope :public))
|
||||
(:documentation "Applies the pattern filter by putting the value of the input cursor to the frame.")
|
||||
|
||||
(:public
|
||||
#>cpp
|
||||
EvaluatePatternFilter() {}
|
||||
|
||||
EvaluatePatternFilter(const std::shared_ptr<LogicalOperator> &input, Symbol output_symbol);
|
||||
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
||||
UniqueCursorPtr MakeCursor(utils::MemoryResource *) const override;
|
||||
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
||||
|
||||
bool HasSingleInput() const override { return true; }
|
||||
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
||||
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
||||
input_ = input;
|
||||
}
|
||||
cpp<#)
|
||||
(:private
|
||||
#>cpp
|
||||
class EvaluatePatternFilterCursor : public Cursor {
|
||||
public:
|
||||
EvaluatePatternFilterCursor(const EvaluatePatternFilter &, utils::MemoryResource *);
|
||||
bool Pull(Frame &, ExecutionContext &) override;
|
||||
void Shutdown() override;
|
||||
void Reset() override;
|
||||
|
||||
private:
|
||||
const EvaluatePatternFilter &self_;
|
||||
UniqueCursorPtr input_cursor_;
|
||||
};
|
||||
cpp<#)
|
||||
(:serialize (:slk))
|
||||
(:clone))
|
||||
|
||||
(lcp:define-class limit (logical-operator)
|
||||
((input "std::shared_ptr<LogicalOperator>" :scope :public
|
||||
:slk-save #'slk-save-operator-pointer
|
||||
@@ -2282,9 +2364,9 @@ clauses.
|
||||
(:public
|
||||
#>cpp
|
||||
Foreach() = default;
|
||||
Foreach(std::shared_ptr<LogicalOperator> input,
|
||||
Foreach(std::shared_ptr<LogicalOperator> input,
|
||||
std::shared_ptr<LogicalOperator> updates,
|
||||
Expression *named_expr,
|
||||
Expression *named_expr,
|
||||
Symbol loop_variable_symbol);
|
||||
|
||||
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -84,56 +84,6 @@ std::vector<Expansion> NormalizePatterns(const SymbolTable &symbol_table, const
|
||||
return expansions;
|
||||
}
|
||||
|
||||
// Fills the given Matching, by converting the Match patterns to normalized
|
||||
// representation as Expansions. Filters used in the Match are also collected,
|
||||
// as well as edge symbols which determine Cyphermorphism. Collecting filters
|
||||
// will lift them out of a pattern and generate new expressions (just like they
|
||||
// were in a Where clause).
|
||||
void AddMatching(const std::vector<Pattern *> &patterns, Where *where, SymbolTable &symbol_table, AstStorage &storage,
|
||||
Matching &matching) {
|
||||
auto expansions = NormalizePatterns(symbol_table, patterns);
|
||||
std::unordered_set<Symbol> edge_symbols;
|
||||
for (const auto &expansion : expansions) {
|
||||
// Matching may already have some expansions, so offset our index.
|
||||
const size_t expansion_ix = matching.expansions.size();
|
||||
// Map node1 symbol to expansion
|
||||
const auto &node1_sym = symbol_table.at(*expansion.node1->identifier_);
|
||||
matching.node_symbol_to_expansions[node1_sym].insert(expansion_ix);
|
||||
// Add node1 to all symbols.
|
||||
matching.expansion_symbols.insert(node1_sym);
|
||||
if (expansion.edge) {
|
||||
const auto &edge_sym = symbol_table.at(*expansion.edge->identifier_);
|
||||
// Fill edge symbols for Cyphermorphism.
|
||||
edge_symbols.insert(edge_sym);
|
||||
// Map node2 symbol to expansion
|
||||
const auto &node2_sym = symbol_table.at(*expansion.node2->identifier_);
|
||||
matching.node_symbol_to_expansions[node2_sym].insert(expansion_ix);
|
||||
// Add edge and node2 to all symbols
|
||||
matching.expansion_symbols.insert(edge_sym);
|
||||
matching.expansion_symbols.insert(node2_sym);
|
||||
}
|
||||
matching.expansions.push_back(expansion);
|
||||
}
|
||||
if (!edge_symbols.empty()) {
|
||||
matching.edge_symbols.emplace_back(edge_symbols);
|
||||
}
|
||||
for (auto *pattern : patterns) {
|
||||
matching.filters.CollectPatternFilters(*pattern, symbol_table, storage);
|
||||
if (pattern->identifier_->user_declared_) {
|
||||
std::vector<Symbol> path_elements;
|
||||
for (auto *pattern_atom : pattern->atoms_)
|
||||
path_elements.emplace_back(symbol_table.at(*pattern_atom->identifier_));
|
||||
matching.named_paths.emplace(symbol_table.at(*pattern->identifier_), std::move(path_elements));
|
||||
}
|
||||
}
|
||||
if (where) {
|
||||
matching.filters.CollectWhereFilter(*where, symbol_table);
|
||||
}
|
||||
}
|
||||
void AddMatching(const Match &match, SymbolTable &symbol_table, AstStorage &storage, Matching &matching) {
|
||||
return AddMatching(match.patterns_, match.where_, symbol_table, storage, matching);
|
||||
}
|
||||
|
||||
auto SplitExpressionOnAnd(Expression *expression) {
|
||||
// TODO: Think about converting all filtering expression into CNF to improve
|
||||
// the granularity of filters which can be stand alone.
|
||||
@@ -519,6 +469,8 @@ void Filters::AnalyzeAndStoreFilter(Expression *expr, const SymbolTable &symbol_
|
||||
if (!add_prop_is_not_null_check(is_not_null)) {
|
||||
all_filters_.emplace_back(make_filter(FilterInfo::Type::Generic));
|
||||
}
|
||||
} else if (auto *exists = utils::Downcast<Exists>(expr)) {
|
||||
all_filters_.emplace_back(make_filter(FilterInfo::Type::Pattern));
|
||||
} else {
|
||||
all_filters_.emplace_back(make_filter(FilterInfo::Type::Generic));
|
||||
}
|
||||
@@ -528,6 +480,78 @@ void Filters::AnalyzeAndStoreFilter(Expression *expr, const SymbolTable &symbol_
|
||||
// as `expr1 < n.prop AND n.prop < expr2`.
|
||||
}
|
||||
|
||||
// Fills the given Matching, by converting the Match patterns to normalized
|
||||
// representation as Expansions. Filters used in the Match are also collected,
|
||||
// as well as edge symbols which determine Cyphermorphism. Collecting filters
|
||||
// will lift them out of a pattern and generate new expressions (just like they
|
||||
// were in a Where clause).
|
||||
void AddMatching(const std::vector<Pattern *> &patterns, Where *where, SymbolTable &symbol_table, AstStorage &storage,
|
||||
Matching &matching) {
|
||||
auto expansions = NormalizePatterns(symbol_table, patterns);
|
||||
std::unordered_set<Symbol> edge_symbols;
|
||||
for (const auto &expansion : expansions) {
|
||||
// Matching may already have some expansions, so offset our index.
|
||||
const size_t expansion_ix = matching.expansions.size();
|
||||
// Map node1 symbol to expansion
|
||||
const auto &node1_sym = symbol_table.at(*expansion.node1->identifier_);
|
||||
matching.node_symbol_to_expansions[node1_sym].insert(expansion_ix);
|
||||
// Add node1 to all symbols.
|
||||
matching.expansion_symbols.insert(node1_sym);
|
||||
if (expansion.edge) {
|
||||
const auto &edge_sym = symbol_table.at(*expansion.edge->identifier_);
|
||||
// Fill edge symbols for Cyphermorphism.
|
||||
edge_symbols.insert(edge_sym);
|
||||
// Map node2 symbol to expansion
|
||||
const auto &node2_sym = symbol_table.at(*expansion.node2->identifier_);
|
||||
matching.node_symbol_to_expansions[node2_sym].insert(expansion_ix);
|
||||
// Add edge and node2 to all symbols
|
||||
matching.expansion_symbols.insert(edge_sym);
|
||||
matching.expansion_symbols.insert(node2_sym);
|
||||
}
|
||||
matching.expansions.push_back(expansion);
|
||||
}
|
||||
if (!edge_symbols.empty()) {
|
||||
matching.edge_symbols.emplace_back(edge_symbols);
|
||||
}
|
||||
for (auto *const pattern : patterns) {
|
||||
matching.filters.CollectPatternFilters(*pattern, symbol_table, storage);
|
||||
if (pattern->identifier_->user_declared_) {
|
||||
std::vector<Symbol> path_elements;
|
||||
for (auto *const pattern_atom : pattern->atoms_)
|
||||
path_elements.push_back(symbol_table.at(*pattern_atom->identifier_));
|
||||
matching.named_paths.emplace(symbol_table.at(*pattern->identifier_), std::move(path_elements));
|
||||
}
|
||||
}
|
||||
if (where) {
|
||||
matching.filters.CollectWhereFilter(*where, symbol_table);
|
||||
}
|
||||
}
|
||||
|
||||
void AddMatching(const Match &match, SymbolTable &symbol_table, AstStorage &storage, Matching &matching) {
|
||||
AddMatching(match.patterns_, match.where_, symbol_table, storage, matching);
|
||||
|
||||
// If there are any pattern filters, we add those as well
|
||||
for (auto &filter : matching.filters) {
|
||||
PatternFilterVisitor visitor(symbol_table, storage);
|
||||
|
||||
filter.expression->Accept(visitor);
|
||||
filter.matchings = visitor.getMatchings();
|
||||
}
|
||||
}
|
||||
|
||||
void PatternFilterVisitor::Visit(Exists &op) {
|
||||
std::vector<Pattern *> patterns;
|
||||
patterns.push_back(op.pattern_);
|
||||
|
||||
FilterMatching filter_matching;
|
||||
AddMatching(patterns, nullptr, symbol_table_, storage_, filter_matching);
|
||||
|
||||
filter_matching.type = PatternFilterType::EXISTS;
|
||||
filter_matching.symbol = std::make_optional<Symbol>(symbol_table_.at(op));
|
||||
|
||||
matchings_.push_back(std::move(filter_matching));
|
||||
}
|
||||
|
||||
static void ParseForeach(query::Foreach &foreach, SingleQueryPart &query_part, AstStorage &storage,
|
||||
SymbolTable &symbol_table) {
|
||||
for (auto *clause : foreach.clauses_) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -70,7 +70,26 @@ class UsedSymbolsCollector : public HierarchicalTreeVisitor {
|
||||
}
|
||||
|
||||
bool Visit(Identifier &ident) override {
|
||||
symbols_.insert(symbol_table_.at(ident));
|
||||
if (!in_exists || ident.user_declared_) {
|
||||
symbols_.insert(symbol_table_.at(ident));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Exists &exists) override {
|
||||
in_exists = true;
|
||||
|
||||
// We do not visit pattern identifier since we're in exists filter pattern
|
||||
for (auto &atom : exists.pattern_->atoms_) {
|
||||
atom->Accept(*this);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PostVisit(Exists & /*exists*/) override {
|
||||
in_exists = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -79,6 +98,9 @@ class UsedSymbolsCollector : public HierarchicalTreeVisitor {
|
||||
|
||||
std::unordered_set<Symbol> symbols_;
|
||||
const SymbolTable &symbol_table_;
|
||||
|
||||
private:
|
||||
bool in_exists{false};
|
||||
};
|
||||
|
||||
/// Normalized representation of a pattern that needs to be matched.
|
||||
@@ -99,6 +121,93 @@ struct Expansion {
|
||||
NodeAtom *node2 = nullptr;
|
||||
};
|
||||
|
||||
struct FilterMatching;
|
||||
|
||||
enum class PatternFilterType { EXISTS };
|
||||
|
||||
/// Collects matchings from filters that include patterns
|
||||
class PatternFilterVisitor : public ExpressionVisitor<void> {
|
||||
public:
|
||||
explicit PatternFilterVisitor(SymbolTable &symbol_table, AstStorage &storage)
|
||||
: symbol_table_(symbol_table), storage_(storage) {}
|
||||
|
||||
using ExpressionVisitor<void>::Visit;
|
||||
|
||||
// Unary operators
|
||||
void Visit(NotOperator &op) override { op.expression_->Accept(*this); }
|
||||
void Visit(IsNullOperator &op) override { op.expression_->Accept(*this); };
|
||||
void Visit(UnaryPlusOperator &op) override{};
|
||||
void Visit(UnaryMinusOperator &op) override{};
|
||||
|
||||
// Binary operators
|
||||
void Visit(OrOperator &op) override {
|
||||
op.expression1_->Accept(*this);
|
||||
op.expression2_->Accept(*this);
|
||||
}
|
||||
void Visit(XorOperator &op) override {
|
||||
op.expression1_->Accept(*this);
|
||||
op.expression2_->Accept(*this);
|
||||
}
|
||||
void Visit(AndOperator &op) override {
|
||||
op.expression1_->Accept(*this);
|
||||
op.expression2_->Accept(*this);
|
||||
}
|
||||
void Visit(NotEqualOperator &op) override {
|
||||
op.expression1_->Accept(*this);
|
||||
op.expression2_->Accept(*this);
|
||||
};
|
||||
void Visit(EqualOperator &op) override {
|
||||
op.expression1_->Accept(*this);
|
||||
op.expression2_->Accept(*this);
|
||||
};
|
||||
void Visit(InListOperator &op) override {
|
||||
op.expression1_->Accept(*this);
|
||||
op.expression2_->Accept(*this);
|
||||
};
|
||||
void Visit(AdditionOperator &op) override{};
|
||||
void Visit(SubtractionOperator &op) override{};
|
||||
void Visit(MultiplicationOperator &op) override{};
|
||||
void Visit(DivisionOperator &op) override{};
|
||||
void Visit(ModOperator &op) override{};
|
||||
void Visit(LessOperator &op) override{};
|
||||
void Visit(GreaterOperator &op) override{};
|
||||
void Visit(LessEqualOperator &op) override{};
|
||||
void Visit(GreaterEqualOperator &op) override{};
|
||||
void Visit(SubscriptOperator &op) override{};
|
||||
|
||||
// Other
|
||||
void Visit(ListSlicingOperator &op) override{};
|
||||
void Visit(IfOperator &op) override{};
|
||||
void Visit(ListLiteral &op) override{};
|
||||
void Visit(MapLiteral &op) override{};
|
||||
void Visit(LabelsTest &op) override{};
|
||||
void Visit(Aggregation &op) override{};
|
||||
void Visit(Function &op) override{};
|
||||
void Visit(Reduce &op) override{};
|
||||
void Visit(Coalesce &op) override{};
|
||||
void Visit(Extract &op) override{};
|
||||
void Visit(Exists &op) override;
|
||||
void Visit(All &op) override{};
|
||||
void Visit(Single &op) override{};
|
||||
void Visit(Any &op) override{};
|
||||
void Visit(None &op) override{};
|
||||
void Visit(Identifier &op) override{};
|
||||
void Visit(PrimitiveLiteral &op) override{};
|
||||
void Visit(PropertyLookup &op) override{};
|
||||
void Visit(ParameterLookup &op) override{};
|
||||
void Visit(NamedExpression &op) override{};
|
||||
void Visit(RegexMatch &op) override{};
|
||||
|
||||
std::vector<FilterMatching> getMatchings() { return matchings_; }
|
||||
|
||||
SymbolTable &symbol_table_;
|
||||
AstStorage &storage_;
|
||||
|
||||
private:
|
||||
/// Collection of matchings in the filter expression being analyzed.
|
||||
std::vector<FilterMatching> matchings_;
|
||||
};
|
||||
|
||||
/// Stores the symbols and expression used to filter a property.
|
||||
class PropertyFilter {
|
||||
public:
|
||||
@@ -153,7 +262,7 @@ struct FilterInfo {
|
||||
/// applied for labels or a property. Non generic types contain extra
|
||||
/// information which can be used to produce indexed scans of graph
|
||||
/// elements.
|
||||
enum class Type { Generic, Label, Property, Id };
|
||||
enum class Type { Generic, Label, Property, Id, Pattern };
|
||||
|
||||
Type type;
|
||||
/// The original filter expression which must be satisfied.
|
||||
@@ -166,6 +275,8 @@ struct FilterInfo {
|
||||
std::optional<PropertyFilter> property_filter;
|
||||
/// Information for Type::Id filtering.
|
||||
std::optional<IdFilter> id_filter;
|
||||
/// Matchings for filters that include patterns
|
||||
std::vector<FilterMatching> matchings;
|
||||
};
|
||||
|
||||
/// Stores information on filters used inside the @c Matching of a @c QueryPart.
|
||||
@@ -287,6 +398,13 @@ struct Matching {
|
||||
std::unordered_set<Symbol> expansion_symbols{};
|
||||
};
|
||||
|
||||
struct FilterMatching : Matching {
|
||||
/// Type of pattern filter
|
||||
PatternFilterType type;
|
||||
/// Symbol for the filter expression
|
||||
std::optional<Symbol> symbol;
|
||||
};
|
||||
|
||||
/// @brief Represents a read (+ write) part of a query. Parts are split on
|
||||
/// `WITH` clauses.
|
||||
///
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/frontend/ast/pretty_print.hpp"
|
||||
#include "query/plan/operator.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace memgraph::query::plan {
|
||||
@@ -148,7 +149,6 @@ bool PlanPrinter::PreVisit(query::plan::Produce &op) {
|
||||
}
|
||||
|
||||
PRE_VISIT(ConstructNamedPath);
|
||||
PRE_VISIT(Filter);
|
||||
PRE_VISIT(SetProperty);
|
||||
PRE_VISIT(SetProperties);
|
||||
PRE_VISIT(SetLabels);
|
||||
@@ -156,6 +156,8 @@ PRE_VISIT(RemoveProperty);
|
||||
PRE_VISIT(RemoveLabels);
|
||||
PRE_VISIT(EdgeUniquenessFilter);
|
||||
PRE_VISIT(Accumulate);
|
||||
PRE_VISIT(EmptyResult);
|
||||
PRE_VISIT(EvaluatePatternFilter);
|
||||
|
||||
bool PlanPrinter::PreVisit(query::plan::Aggregate &op) {
|
||||
WithPrintLn([&](auto &out) {
|
||||
@@ -250,6 +252,15 @@ bool PlanPrinter::PreVisit(query::plan::Foreach &op) {
|
||||
op.input_->Accept(*this);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlanPrinter::PreVisit(query::plan::Filter &op) {
|
||||
WithPrintLn([](auto &out) { out << "* Filter"; });
|
||||
for (const auto &pattern_filter : op.pattern_filters_) {
|
||||
Branch(*pattern_filter);
|
||||
}
|
||||
op.input_->Accept(*this);
|
||||
return false;
|
||||
}
|
||||
#undef PRE_VISIT
|
||||
|
||||
bool PlanPrinter::DefaultPreVisit() {
|
||||
@@ -401,6 +412,8 @@ json ToJson(const Aggregate::Element &elem) {
|
||||
}
|
||||
json["op"] = utils::ToLowerCase(Aggregation::OpToString(elem.op));
|
||||
json["output_symbol"] = ToJson(elem.output_sym);
|
||||
json["distinct"] = elem.distinct;
|
||||
|
||||
return json;
|
||||
}
|
||||
////////////////////////// END HELPER FUNCTIONS ////////////////////////////////
|
||||
@@ -586,6 +599,13 @@ bool PlanToJsonVisitor::PreVisit(Filter &op) {
|
||||
op.input_->Accept(*this);
|
||||
self["input"] = PopOutput();
|
||||
|
||||
for (auto pattern_idx = 0; pattern_idx < op.pattern_filters_.size(); pattern_idx++) {
|
||||
auto pattern_filter_key = "pattern_filter" + std::to_string(pattern_idx + 1);
|
||||
|
||||
op.pattern_filters_[pattern_idx]->Accept(*this);
|
||||
self[pattern_filter_key] = PopOutput();
|
||||
}
|
||||
|
||||
output_ = std::move(self);
|
||||
return false;
|
||||
}
|
||||
@@ -703,6 +723,17 @@ bool PlanToJsonVisitor::PreVisit(EdgeUniquenessFilter &op) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlanToJsonVisitor::PreVisit(EmptyResult &op) {
|
||||
json self;
|
||||
self["name"] = "EmptyResult";
|
||||
|
||||
op.input_->Accept(*this);
|
||||
self["input"] = PopOutput();
|
||||
|
||||
output_ = std::move(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlanToJsonVisitor::PreVisit(Accumulate &op) {
|
||||
json self;
|
||||
self["name"] = "Accumulate";
|
||||
@@ -894,6 +925,7 @@ bool PlanToJsonVisitor::PreVisit(Cartesian &op) {
|
||||
output_ = std::move(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlanToJsonVisitor::PreVisit(Foreach &op) {
|
||||
json self;
|
||||
self["name"] = "Foreach";
|
||||
@@ -910,6 +942,18 @@ bool PlanToJsonVisitor::PreVisit(Foreach &op) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlanToJsonVisitor::PreVisit(EvaluatePatternFilter &op) {
|
||||
json self;
|
||||
self["name"] = "EvaluatePatternFilter";
|
||||
self["output_symbol"] = ToJson(op.output_symbol_);
|
||||
|
||||
op.input_->Accept(*this);
|
||||
self["input"] = PopOutput();
|
||||
|
||||
output_ = std::move(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
} // namespace memgraph::query::plan
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -74,12 +74,14 @@ class PlanPrinter : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
bool PreVisit(ConstructNamedPath &) override;
|
||||
|
||||
bool PreVisit(Filter &) override;
|
||||
bool PreVisit(EvaluatePatternFilter & /*unused*/) override;
|
||||
bool PreVisit(EdgeUniquenessFilter &) override;
|
||||
|
||||
bool PreVisit(Merge &) override;
|
||||
bool PreVisit(Optional &) override;
|
||||
bool PreVisit(Cartesian &) override;
|
||||
|
||||
bool PreVisit(EmptyResult &) override;
|
||||
bool PreVisit(Produce &) override;
|
||||
bool PreVisit(Accumulate &) override;
|
||||
bool PreVisit(Aggregate &) override;
|
||||
@@ -185,6 +187,7 @@ class PlanToJsonVisitor : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
bool PreVisit(Optional &) override;
|
||||
|
||||
bool PreVisit(Filter &) override;
|
||||
bool PreVisit(EvaluatePatternFilter & /*op*/) override;
|
||||
bool PreVisit(EdgeUniquenessFilter &) override;
|
||||
bool PreVisit(Cartesian &) override;
|
||||
|
||||
@@ -195,6 +198,7 @@ class PlanToJsonVisitor : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
bool PreVisit(ScanAllByLabelProperty &) override;
|
||||
bool PreVisit(ScanAllById &) override;
|
||||
|
||||
bool PreVisit(EmptyResult &) override;
|
||||
bool PreVisit(Produce &) override;
|
||||
bool PreVisit(Accumulate &) override;
|
||||
bool PreVisit(Aggregate &) override;
|
||||
|
||||
@@ -11,10 +11,11 @@
|
||||
|
||||
#include "query/plan/read_write_type_checker.hpp"
|
||||
|
||||
#define PRE_VISIT(TOp, RWType, continue_visiting) \
|
||||
bool ReadWriteTypeChecker::PreVisit(TOp &op) { \
|
||||
UpdateType(RWType); \
|
||||
return continue_visiting; \
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define PRE_VISIT(TOp, RWType, continue_visiting) \
|
||||
bool ReadWriteTypeChecker::PreVisit(TOp &) { /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
UpdateType(RWType); \
|
||||
return continue_visiting; \
|
||||
}
|
||||
|
||||
namespace memgraph::query::plan {
|
||||
@@ -54,6 +55,7 @@ bool ReadWriteTypeChecker::PreVisit(Cartesian &op) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PRE_VISIT(EmptyResult, RWType::NONE, true)
|
||||
PRE_VISIT(Produce, RWType::NONE, true)
|
||||
PRE_VISIT(Accumulate, RWType::NONE, true)
|
||||
PRE_VISIT(Aggregate, RWType::NONE, true)
|
||||
@@ -86,21 +88,28 @@ bool ReadWriteTypeChecker::PreVisit([[maybe_unused]] Foreach &op) {
|
||||
|
||||
#undef PRE_VISIT
|
||||
|
||||
bool ReadWriteTypeChecker::Visit(Once &op) { return false; }
|
||||
bool ReadWriteTypeChecker::Visit(Once &) { return false; } // NOLINT(hicpp-named-parameter)
|
||||
|
||||
void ReadWriteTypeChecker::UpdateType(RWType op_type) {
|
||||
// Update type only if it's not the NONE type and the current operator's type
|
||||
// is different than the one that's currently inferred.
|
||||
if (type != RWType::NONE && type != op_type) {
|
||||
type = RWType::RW;
|
||||
}
|
||||
// Stop inference because RW is the most "dominant" type, i.e. it isn't
|
||||
// affected by the type of nodes in the plan appearing after the node for
|
||||
// which the type is set to RW.
|
||||
if (type == RWType::RW) {
|
||||
return;
|
||||
}
|
||||
if (type == RWType::NONE && op_type != RWType::NONE) {
|
||||
|
||||
// if op_type is NONE, type doesn't change.
|
||||
if (op_type == RWType::NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update type only if it's not the NONE type and the current operator's type
|
||||
// is different than the one that's currently inferred.
|
||||
if (type != RWType::NONE && type != op_type) {
|
||||
type = RWType::RW;
|
||||
}
|
||||
|
||||
if (type == RWType::NONE) {
|
||||
type = op_type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace memgraph::query::plan {
|
||||
|
||||
class ReadWriteTypeChecker : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
struct ReadWriteTypeChecker : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
public:
|
||||
ReadWriteTypeChecker() = default;
|
||||
|
||||
@@ -73,6 +73,7 @@ class ReadWriteTypeChecker : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
bool PreVisit(Optional &) override;
|
||||
bool PreVisit(Cartesian &) override;
|
||||
|
||||
bool PreVisit(EmptyResult &) override;
|
||||
bool PreVisit(Produce &) override;
|
||||
bool PreVisit(Accumulate &) override;
|
||||
bool PreVisit(Aggregate &) override;
|
||||
@@ -88,7 +89,6 @@ class ReadWriteTypeChecker : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
|
||||
bool Visit(Once &) override;
|
||||
|
||||
private:
|
||||
void UpdateType(RWType op_type);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -298,6 +298,15 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(EmptyResult &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(EmptyResult &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Delete &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
@@ -435,6 +444,8 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
|
||||
bool PreVisit(Foreach &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
op.input()->Accept(*this);
|
||||
RewriteBranch(&op.update_clauses_);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -443,6 +454,16 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(EvaluatePatternFilter &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostVisit(EvaluatePatternFilter & /*op*/) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<LogicalOperator> new_root_;
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -25,15 +25,6 @@ namespace memgraph::query::plan {
|
||||
|
||||
namespace {
|
||||
|
||||
bool HasBoundFilterSymbols(const std::unordered_set<Symbol> &bound_symbols, const FilterInfo &filter) {
|
||||
for (const auto &symbol : filter.used_symbols) {
|
||||
if (bound_symbols.find(symbol) == bound_symbols.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ast tree visitor which collects the context for a return body.
|
||||
// The return body of WITH and RETURN clauses consists of:
|
||||
//
|
||||
@@ -344,7 +335,8 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
|
||||
bool PostVisit(Aggregation &aggr) override {
|
||||
// Aggregation contains a virtual symbol, where the result will be stored.
|
||||
const auto &symbol = symbol_table_.at(aggr);
|
||||
aggregations_.emplace_back(Aggregate::Element{aggr.expression1_, aggr.expression2_, aggr.op_, symbol});
|
||||
aggregations_.emplace_back(
|
||||
Aggregate::Element{aggr.expression1_, aggr.expression2_, aggr.op_, symbol, aggr.distinct_});
|
||||
// Aggregation expression1_ is optional in COUNT(*), and COLLECT_MAP uses
|
||||
// two expressions, so we can have 0, 1 or 2 elements on the
|
||||
// has_aggregation_stack for this Aggregation expression.
|
||||
@@ -494,7 +486,8 @@ std::unique_ptr<LogicalOperator> GenReturnBody(std::unique_ptr<LogicalOperator>
|
||||
// Where may see new symbols so it comes after we generate Produce and in
|
||||
// general, comes after any OrderBy, Skip or Limit.
|
||||
if (body.where()) {
|
||||
last_op = std::make_unique<Filter>(std::move(last_op), body.where()->expression_);
|
||||
last_op = std::make_unique<Filter>(std::move(last_op), std::vector<std::shared_ptr<LogicalOperator>>{},
|
||||
body.where()->expression_);
|
||||
}
|
||||
return last_op;
|
||||
}
|
||||
@@ -503,6 +496,12 @@ std::unique_ptr<LogicalOperator> GenReturnBody(std::unique_ptr<LogicalOperator>
|
||||
|
||||
namespace impl {
|
||||
|
||||
bool HasBoundFilterSymbols(const std::unordered_set<Symbol> &bound_symbols, const FilterInfo &filter) {
|
||||
return std::ranges::all_of(
|
||||
filter.used_symbols.begin(), filter.used_symbols.end(),
|
||||
[&bound_symbols](const auto &symbol) { return bound_symbols.find(symbol) != bound_symbols.end(); });
|
||||
}
|
||||
|
||||
Expression *ExtractFilters(const std::unordered_set<Symbol> &bound_symbols, Filters &filters, AstStorage &storage) {
|
||||
Expression *filter_expr = nullptr;
|
||||
for (auto filters_it = filters.begin(); filters_it != filters.end();) {
|
||||
@@ -516,16 +515,6 @@ Expression *ExtractFilters(const std::unordered_set<Symbol> &bound_symbols, Filt
|
||||
return filter_expr;
|
||||
}
|
||||
|
||||
std::unique_ptr<LogicalOperator> GenFilters(std::unique_ptr<LogicalOperator> last_op,
|
||||
const std::unordered_set<Symbol> &bound_symbols, Filters &filters,
|
||||
AstStorage &storage) {
|
||||
auto *filter_expr = ExtractFilters(bound_symbols, filters, storage);
|
||||
if (filter_expr) {
|
||||
last_op = std::make_unique<Filter>(std::move(last_op), filter_expr);
|
||||
}
|
||||
return last_op;
|
||||
}
|
||||
|
||||
std::unique_ptr<LogicalOperator> GenNamedPaths(std::unique_ptr<LogicalOperator> last_op,
|
||||
std::unordered_set<Symbol> &bound_symbols,
|
||||
std::unordered_map<Symbol, std::vector<Symbol>> &named_paths) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -81,8 +81,8 @@ namespace impl {
|
||||
// removed from `Filters`.
|
||||
Expression *ExtractFilters(const std::unordered_set<Symbol> &, Filters &, AstStorage &);
|
||||
|
||||
std::unique_ptr<LogicalOperator> GenFilters(std::unique_ptr<LogicalOperator>, const std::unordered_set<Symbol> &,
|
||||
Filters &, AstStorage &);
|
||||
/// Checks if the filters has all the bound symbols to be included in the current part of the query
|
||||
bool HasBoundFilterSymbols(const std::unordered_set<Symbol> &bound_symbols, const FilterInfo &filter);
|
||||
|
||||
/// Utility function for iterating pattern atoms and accumulating a result.
|
||||
///
|
||||
@@ -174,13 +174,17 @@ class RuleBasedPlanner {
|
||||
input_op = PlanMatching(match_ctx, std::move(input_op));
|
||||
for (const auto &matching : query_part.optional_matching) {
|
||||
MatchContext opt_ctx{matching, *context.symbol_table, context.bound_symbols};
|
||||
auto match_op = PlanMatching(opt_ctx, nullptr);
|
||||
|
||||
std::vector<Symbol> bound_symbols(context_->bound_symbols.begin(), context_->bound_symbols.end());
|
||||
auto once_with_symbols = std::make_unique<Once>(bound_symbols);
|
||||
|
||||
auto match_op = PlanMatching(opt_ctx, std::move(once_with_symbols));
|
||||
if (match_op) {
|
||||
input_op = std::make_unique<Optional>(std::move(input_op), std::move(match_op), opt_ctx.new_symbols);
|
||||
}
|
||||
}
|
||||
uint64_t merge_id = 0;
|
||||
for (auto *clause : query_part.remaining_clauses) {
|
||||
for (const auto &clause : query_part.remaining_clauses) {
|
||||
MG_ASSERT(!utils::IsSubtype(*clause, Match::kType), "Unexpected Match in remaining clauses");
|
||||
if (auto *ret = utils::Downcast<Return>(clause)) {
|
||||
input_op = impl::GenReturn(*ret, std::move(input_op), *context.symbol_table, is_write, context.bound_symbols,
|
||||
@@ -203,6 +207,7 @@ class RuleBasedPlanner {
|
||||
context.bound_symbols.insert(symbol);
|
||||
input_op =
|
||||
std::make_unique<plan::Unwind>(std::move(input_op), unwind->named_expression_->expression_, symbol);
|
||||
|
||||
} else if (auto *call_proc = utils::Downcast<query::CallProcedure>(clause)) {
|
||||
std::vector<Symbol> result_symbols;
|
||||
result_symbols.reserve(call_proc->result_identifiers_.size());
|
||||
@@ -224,6 +229,7 @@ class RuleBasedPlanner {
|
||||
input_op =
|
||||
std::make_unique<plan::LoadCsv>(std::move(input_op), load_csv->file_, load_csv->with_header_,
|
||||
load_csv->ignore_bad_, load_csv->delimiter_, load_csv->quote_, row_sym);
|
||||
|
||||
} else if (auto *foreach = utils::Downcast<query::Foreach>(clause)) {
|
||||
is_write = true;
|
||||
input_op = HandleForeachClause(foreach, std::move(input_op), *context.symbol_table, context.bound_symbols,
|
||||
@@ -233,6 +239,10 @@ class RuleBasedPlanner {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Is this the only situation that should be covered
|
||||
if (input_op->OutputSymbols(*context.symbol_table).empty()) {
|
||||
input_op = std::make_unique<EmptyResult>(std::move(input_op));
|
||||
}
|
||||
return input_op;
|
||||
}
|
||||
|
||||
@@ -388,118 +398,11 @@ class RuleBasedPlanner {
|
||||
// Try to generate any filters even before the 1st match operator. This
|
||||
// optimizes the optional match which filters only on symbols bound in
|
||||
// regular match.
|
||||
auto last_op = impl::GenFilters(std::move(input_op), bound_symbols, filters, storage);
|
||||
for (const auto &expansion : matching.expansions) {
|
||||
const auto &node1_symbol = symbol_table.at(*expansion.node1->identifier_);
|
||||
if (bound_symbols.insert(node1_symbol).second) {
|
||||
// We have just bound this symbol, so generate ScanAll which fills it.
|
||||
last_op = std::make_unique<ScanAll>(std::move(last_op), node1_symbol, match_context.view);
|
||||
match_context.new_symbols.emplace_back(node1_symbol);
|
||||
last_op = impl::GenFilters(std::move(last_op), bound_symbols, filters, storage);
|
||||
last_op = impl::GenNamedPaths(std::move(last_op), bound_symbols, named_paths);
|
||||
last_op = impl::GenFilters(std::move(last_op), bound_symbols, filters, storage);
|
||||
}
|
||||
// We have an edge, so generate Expand.
|
||||
if (expansion.edge) {
|
||||
auto *edge = expansion.edge;
|
||||
// If the expand symbols were already bound, then we need to indicate
|
||||
// that they exist. The Expand will then check whether the pattern holds
|
||||
// instead of writing the expansion to symbols.
|
||||
const auto &node_symbol = symbol_table.at(*expansion.node2->identifier_);
|
||||
auto existing_node = utils::Contains(bound_symbols, node_symbol);
|
||||
const auto &edge_symbol = symbol_table.at(*edge->identifier_);
|
||||
MG_ASSERT(!utils::Contains(bound_symbols, edge_symbol), "Existing edges are not supported");
|
||||
std::vector<storage::EdgeTypeId> edge_types;
|
||||
edge_types.reserve(edge->edge_types_.size());
|
||||
for (const auto &type : edge->edge_types_) {
|
||||
edge_types.push_back(GetEdgeType(type));
|
||||
}
|
||||
if (edge->IsVariable()) {
|
||||
std::optional<ExpansionLambda> weight_lambda;
|
||||
std::optional<Symbol> total_weight;
|
||||
auto last_op = GenFilters(std::move(input_op), bound_symbols, filters, storage, symbol_table);
|
||||
|
||||
if (edge->type_ == EdgeAtom::Type::WEIGHTED_SHORTEST_PATH || edge->type_ == EdgeAtom::Type::ALL_SHORTEST_PATHS) {
|
||||
weight_lambda.emplace(ExpansionLambda{symbol_table.at(*edge->weight_lambda_.inner_edge),
|
||||
symbol_table.at(*edge->weight_lambda_.inner_node),
|
||||
edge->weight_lambda_.expression});
|
||||
last_op = HandleExpansion(std::move(last_op), matching, symbol_table, storage, bound_symbols,
|
||||
match_context.new_symbols, named_paths, filters, match_context.view);
|
||||
|
||||
total_weight.emplace(symbol_table.at(*edge->total_weight_));
|
||||
}
|
||||
|
||||
ExpansionLambda filter_lambda;
|
||||
filter_lambda.inner_edge_symbol = symbol_table.at(*edge->filter_lambda_.inner_edge);
|
||||
filter_lambda.inner_node_symbol = symbol_table.at(*edge->filter_lambda_.inner_node);
|
||||
{
|
||||
// Bind the inner edge and node symbols so they're available for
|
||||
// inline filtering in ExpandVariable.
|
||||
bool inner_edge_bound = bound_symbols.insert(filter_lambda.inner_edge_symbol).second;
|
||||
bool inner_node_bound = bound_symbols.insert(filter_lambda.inner_node_symbol).second;
|
||||
MG_ASSERT(inner_edge_bound && inner_node_bound, "An inner edge and node can't be bound from before");
|
||||
}
|
||||
// Join regular filters with lambda filter expression, so that they
|
||||
// are done inline together. Semantic analysis should guarantee that
|
||||
// lambda filtering uses bound symbols.
|
||||
filter_lambda.expression = impl::BoolJoin<AndOperator>(
|
||||
storage, impl::ExtractFilters(bound_symbols, filters, storage), edge->filter_lambda_.expression);
|
||||
// At this point it's possible we have leftover filters for inline
|
||||
// filtering (they use the inner symbols. If they were not collected,
|
||||
// we have to remove them manually because no other filter-extraction
|
||||
// will ever bind them again.
|
||||
filters.erase(std::remove_if(
|
||||
filters.begin(), filters.end(),
|
||||
[e = filter_lambda.inner_edge_symbol, n = filter_lambda.inner_node_symbol](FilterInfo &fi) {
|
||||
return utils::Contains(fi.used_symbols, e) || utils::Contains(fi.used_symbols, n);
|
||||
}),
|
||||
filters.end());
|
||||
// Unbind the temporarily bound inner symbols for filtering.
|
||||
bound_symbols.erase(filter_lambda.inner_edge_symbol);
|
||||
bound_symbols.erase(filter_lambda.inner_node_symbol);
|
||||
|
||||
if (total_weight) {
|
||||
bound_symbols.insert(*total_weight);
|
||||
}
|
||||
|
||||
// TODO: Pass weight lambda.
|
||||
MG_ASSERT(match_context.view == storage::View::OLD,
|
||||
"ExpandVariable should only be planned with storage::View::OLD");
|
||||
last_op = std::make_unique<ExpandVariable>(std::move(last_op), node1_symbol, node_symbol, edge_symbol,
|
||||
edge->type_, expansion.direction, edge_types, expansion.is_flipped,
|
||||
edge->lower_bound_, edge->upper_bound_, existing_node,
|
||||
filter_lambda, weight_lambda, total_weight);
|
||||
} else {
|
||||
last_op = std::make_unique<Expand>(std::move(last_op), node1_symbol, node_symbol, edge_symbol,
|
||||
expansion.direction, edge_types, existing_node, match_context.view);
|
||||
}
|
||||
|
||||
// Bind the expanded edge and node.
|
||||
bound_symbols.insert(edge_symbol);
|
||||
match_context.new_symbols.emplace_back(edge_symbol);
|
||||
if (bound_symbols.insert(node_symbol).second) {
|
||||
match_context.new_symbols.emplace_back(node_symbol);
|
||||
}
|
||||
|
||||
// Ensure Cyphermorphism (different edge symbols always map to
|
||||
// different edges).
|
||||
for (const auto &edge_symbols : matching.edge_symbols) {
|
||||
if (edge_symbols.find(edge_symbol) == edge_symbols.end()) {
|
||||
continue;
|
||||
}
|
||||
std::vector<Symbol> other_symbols;
|
||||
for (const auto &symbol : edge_symbols) {
|
||||
if (symbol == edge_symbol || bound_symbols.find(symbol) == bound_symbols.end()) {
|
||||
continue;
|
||||
}
|
||||
other_symbols.push_back(symbol);
|
||||
}
|
||||
if (!other_symbols.empty()) {
|
||||
last_op = std::make_unique<EdgeUniquenessFilter>(std::move(last_op), edge_symbol, other_symbols);
|
||||
}
|
||||
}
|
||||
last_op = impl::GenFilters(std::move(last_op), bound_symbols, filters, storage);
|
||||
last_op = impl::GenNamedPaths(std::move(last_op), bound_symbols, named_paths);
|
||||
last_op = impl::GenFilters(std::move(last_op), bound_symbols, filters, storage);
|
||||
}
|
||||
}
|
||||
MG_ASSERT(named_paths.empty(), "Expected to generate all named paths");
|
||||
// We bound all named path symbols, so just add them to new_symbols.
|
||||
for (const auto &named_path : matching.named_paths) {
|
||||
@@ -536,6 +439,143 @@ class RuleBasedPlanner {
|
||||
return std::make_unique<plan::Merge>(std::move(input_op), std::move(on_match), std::move(on_create));
|
||||
}
|
||||
|
||||
std::unique_ptr<LogicalOperator> HandleExpansion(std::unique_ptr<LogicalOperator> last_op, const Matching &matching,
|
||||
const SymbolTable &symbol_table, AstStorage &storage,
|
||||
std::unordered_set<Symbol> &bound_symbols,
|
||||
std::vector<Symbol> &new_symbols,
|
||||
std::unordered_map<Symbol, std::vector<Symbol>> &named_paths,
|
||||
Filters &filters, storage::View view) {
|
||||
for (const auto &expansion : matching.expansions) {
|
||||
const auto &node1_symbol = symbol_table.at(*expansion.node1->identifier_);
|
||||
if (bound_symbols.insert(node1_symbol).second) {
|
||||
// We have just bound this symbol, so generate ScanAll which fills it.
|
||||
last_op = std::make_unique<ScanAll>(std::move(last_op), node1_symbol, view);
|
||||
new_symbols.emplace_back(node1_symbol);
|
||||
|
||||
last_op = GenFilters(std::move(last_op), bound_symbols, filters, storage, symbol_table);
|
||||
last_op = impl::GenNamedPaths(std::move(last_op), bound_symbols, named_paths);
|
||||
last_op = GenFilters(std::move(last_op), bound_symbols, filters, storage, symbol_table);
|
||||
}
|
||||
|
||||
if (expansion.edge) {
|
||||
last_op = GenExpand(std::move(last_op), expansion, symbol_table, bound_symbols, matching, storage, filters,
|
||||
named_paths, new_symbols, view);
|
||||
}
|
||||
}
|
||||
|
||||
return last_op;
|
||||
}
|
||||
|
||||
std::unique_ptr<LogicalOperator> GenExpand(std::unique_ptr<LogicalOperator> last_op, const Expansion &expansion,
|
||||
const SymbolTable &symbol_table, std::unordered_set<Symbol> &bound_symbols,
|
||||
const Matching &matching, AstStorage &storage, Filters &filters,
|
||||
std::unordered_map<Symbol, std::vector<Symbol>> &named_paths,
|
||||
std::vector<Symbol> &new_symbols, storage::View view) {
|
||||
// If the expand symbols were already bound, then we need to indicate
|
||||
// that they exist. The Expand will then check whether the pattern holds
|
||||
// instead of writing the expansion to symbols.
|
||||
const auto &node1_symbol = symbol_table.at(*expansion.node1->identifier_);
|
||||
bound_symbols.insert(node1_symbol);
|
||||
|
||||
const auto &node_symbol = symbol_table.at(*expansion.node2->identifier_);
|
||||
auto *edge = expansion.edge;
|
||||
|
||||
auto existing_node = utils::Contains(bound_symbols, node_symbol);
|
||||
const auto &edge_symbol = symbol_table.at(*edge->identifier_);
|
||||
MG_ASSERT(!utils::Contains(bound_symbols, edge_symbol), "Existing edges are not supported");
|
||||
std::vector<storage::EdgeTypeId> edge_types;
|
||||
edge_types.reserve(edge->edge_types_.size());
|
||||
for (const auto &type : edge->edge_types_) {
|
||||
edge_types.push_back(GetEdgeType(type));
|
||||
}
|
||||
if (edge->IsVariable()) {
|
||||
std::optional<ExpansionLambda> weight_lambda;
|
||||
std::optional<Symbol> total_weight;
|
||||
|
||||
if (edge->type_ == EdgeAtom::Type::WEIGHTED_SHORTEST_PATH || edge->type_ == EdgeAtom::Type::ALL_SHORTEST_PATHS) {
|
||||
weight_lambda.emplace(ExpansionLambda{symbol_table.at(*edge->weight_lambda_.inner_edge),
|
||||
symbol_table.at(*edge->weight_lambda_.inner_node),
|
||||
edge->weight_lambda_.expression});
|
||||
|
||||
total_weight.emplace(symbol_table.at(*edge->total_weight_));
|
||||
}
|
||||
|
||||
ExpansionLambda filter_lambda;
|
||||
filter_lambda.inner_edge_symbol = symbol_table.at(*edge->filter_lambda_.inner_edge);
|
||||
filter_lambda.inner_node_symbol = symbol_table.at(*edge->filter_lambda_.inner_node);
|
||||
{
|
||||
// Bind the inner edge and node symbols so they're available for
|
||||
// inline filtering in ExpandVariable.
|
||||
bool inner_edge_bound = bound_symbols.insert(filter_lambda.inner_edge_symbol).second;
|
||||
bool inner_node_bound = bound_symbols.insert(filter_lambda.inner_node_symbol).second;
|
||||
MG_ASSERT(inner_edge_bound && inner_node_bound, "An inner edge and node can't be bound from before");
|
||||
}
|
||||
// Join regular filters with lambda filter expression, so that they
|
||||
// are done inline together. Semantic analysis should guarantee that
|
||||
// lambda filtering uses bound symbols.
|
||||
filter_lambda.expression = impl::BoolJoin<AndOperator>(
|
||||
storage, impl::ExtractFilters(bound_symbols, filters, storage), edge->filter_lambda_.expression);
|
||||
// At this point it's possible we have leftover filters for inline
|
||||
// filtering (they use the inner symbols. If they were not collected,
|
||||
// we have to remove them manually because no other filter-extraction
|
||||
// will ever bind them again.
|
||||
filters.erase(
|
||||
std::remove_if(filters.begin(), filters.end(),
|
||||
[e = filter_lambda.inner_edge_symbol, n = filter_lambda.inner_node_symbol](FilterInfo &fi) {
|
||||
return utils::Contains(fi.used_symbols, e) || utils::Contains(fi.used_symbols, n);
|
||||
}),
|
||||
filters.end());
|
||||
// Unbind the temporarily bound inner symbols for filtering.
|
||||
bound_symbols.erase(filter_lambda.inner_edge_symbol);
|
||||
bound_symbols.erase(filter_lambda.inner_node_symbol);
|
||||
|
||||
if (total_weight) {
|
||||
bound_symbols.insert(*total_weight);
|
||||
}
|
||||
|
||||
// TODO: Pass weight lambda.
|
||||
MG_ASSERT(view == storage::View::OLD, "ExpandVariable should only be planned with storage::View::OLD");
|
||||
last_op = std::make_unique<ExpandVariable>(std::move(last_op), node1_symbol, node_symbol, edge_symbol,
|
||||
edge->type_, expansion.direction, edge_types, expansion.is_flipped,
|
||||
edge->lower_bound_, edge->upper_bound_, existing_node, filter_lambda,
|
||||
weight_lambda, total_weight);
|
||||
} else {
|
||||
last_op = std::make_unique<Expand>(std::move(last_op), node1_symbol, node_symbol, edge_symbol,
|
||||
expansion.direction, edge_types, existing_node, view);
|
||||
}
|
||||
|
||||
// Bind the expanded edge and node.
|
||||
bound_symbols.insert(edge_symbol);
|
||||
new_symbols.emplace_back(edge_symbol);
|
||||
if (bound_symbols.insert(node_symbol).second) {
|
||||
new_symbols.emplace_back(node_symbol);
|
||||
}
|
||||
|
||||
// Ensure Cyphermorphism (different edge symbols always map to
|
||||
// different edges).
|
||||
for (const auto &edge_symbols : matching.edge_symbols) {
|
||||
if (edge_symbols.find(edge_symbol) == edge_symbols.end()) {
|
||||
continue;
|
||||
}
|
||||
std::vector<Symbol> other_symbols;
|
||||
for (const auto &symbol : edge_symbols) {
|
||||
if (symbol == edge_symbol || bound_symbols.find(symbol) == bound_symbols.end()) {
|
||||
continue;
|
||||
}
|
||||
other_symbols.push_back(symbol);
|
||||
}
|
||||
if (!other_symbols.empty()) {
|
||||
last_op = std::make_unique<EdgeUniquenessFilter>(std::move(last_op), edge_symbol, other_symbols);
|
||||
}
|
||||
}
|
||||
|
||||
last_op = GenFilters(std::move(last_op), bound_symbols, filters, storage, symbol_table);
|
||||
last_op = impl::GenNamedPaths(std::move(last_op), bound_symbols, named_paths);
|
||||
last_op = GenFilters(std::move(last_op), bound_symbols, filters, storage, symbol_table);
|
||||
|
||||
return last_op;
|
||||
}
|
||||
|
||||
std::unique_ptr<LogicalOperator> HandleForeachClause(query::Foreach *foreach,
|
||||
std::unique_ptr<LogicalOperator> input_op,
|
||||
const SymbolTable &symbol_table,
|
||||
@@ -556,6 +596,64 @@ class RuleBasedPlanner {
|
||||
return std::make_unique<plan::Foreach>(std::move(input_op), std::move(op), foreach->named_expression_->expression_,
|
||||
symbol);
|
||||
}
|
||||
|
||||
std::unique_ptr<LogicalOperator> GenFilters(std::unique_ptr<LogicalOperator> last_op,
|
||||
const std::unordered_set<Symbol> &bound_symbols, Filters &filters,
|
||||
AstStorage &storage, const SymbolTable &symbol_table) {
|
||||
auto pattern_filters = ExtractPatternFilters(filters, symbol_table, storage, bound_symbols);
|
||||
auto *filter_expr = impl::ExtractFilters(bound_symbols, filters, storage);
|
||||
|
||||
if (filter_expr) {
|
||||
last_op = std::make_unique<Filter>(std::move(last_op), std::move(pattern_filters), filter_expr);
|
||||
}
|
||||
return last_op;
|
||||
}
|
||||
|
||||
std::unique_ptr<LogicalOperator> MakeExistsFilter(const FilterMatching &matching, const SymbolTable &symbol_table,
|
||||
AstStorage &storage,
|
||||
const std::unordered_set<Symbol> &bound_symbols) {
|
||||
std::vector<Symbol> once_symbols(bound_symbols.begin(), bound_symbols.end());
|
||||
std::unique_ptr<LogicalOperator> last_op = std::make_unique<Once>(once_symbols);
|
||||
|
||||
std::vector<Symbol> new_symbols;
|
||||
std::unordered_set<Symbol> expand_symbols(bound_symbols.begin(), bound_symbols.end());
|
||||
|
||||
auto filters = matching.filters;
|
||||
|
||||
std::unordered_map<Symbol, std::vector<Symbol>> named_paths;
|
||||
|
||||
last_op = HandleExpansion(std::move(last_op), matching, symbol_table, storage, expand_symbols, new_symbols,
|
||||
named_paths, filters, storage::View::OLD);
|
||||
|
||||
last_op = std::make_unique<Limit>(std::move(last_op), storage.Create<PrimitiveLiteral>(1));
|
||||
|
||||
last_op = std::make_unique<EvaluatePatternFilter>(std::move(last_op), matching.symbol.value());
|
||||
|
||||
return last_op;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<LogicalOperator>> ExtractPatternFilters(Filters &filters, const SymbolTable &symbol_table,
|
||||
AstStorage &storage,
|
||||
const std::unordered_set<Symbol> &bound_symbols) {
|
||||
std::vector<std::shared_ptr<LogicalOperator>> operators;
|
||||
|
||||
for (const auto &filter : filters) {
|
||||
for (const auto &matching : filter.matchings) {
|
||||
if (!impl::HasBoundFilterSymbols(bound_symbols, filter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (matching.type) {
|
||||
case PatternFilterType::EXISTS: {
|
||||
operators.push_back(MakeExistsFilter(matching, symbol_table, storage, bound_symbols));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return operators;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace memgraph::query::plan
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -155,6 +155,18 @@ auto ExpansionNodes(const std::vector<Expansion> &expansions, const SymbolTable
|
||||
return nodes;
|
||||
}
|
||||
|
||||
FilterMatching ToFilterMatching(Matching &matching) {
|
||||
FilterMatching filter_matching;
|
||||
filter_matching.expansions = matching.expansions;
|
||||
filter_matching.edge_symbols = matching.edge_symbols;
|
||||
filter_matching.filters = matching.filters;
|
||||
filter_matching.node_symbol_to_expansions = matching.node_symbol_to_expansions;
|
||||
filter_matching.named_paths = matching.named_paths;
|
||||
filter_matching.expansion_symbols = matching.expansion_symbols;
|
||||
|
||||
return filter_matching;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VaryMatchingStart::VaryMatchingStart(Matching matching, const SymbolTable &symbol_table)
|
||||
@@ -209,11 +221,31 @@ CartesianProduct<VaryMatchingStart> VaryMultiMatchingStarts(const std::vector<Ma
|
||||
return MakeCartesianProduct(std::move(variants));
|
||||
}
|
||||
|
||||
CartesianProduct<VaryMatchingStart> VaryFilterMatchingStarts(const Matching &matching,
|
||||
const SymbolTable &symbol_table) {
|
||||
auto filter_matchings_cnt = 0;
|
||||
for (const auto &filter : matching.filters) {
|
||||
filter_matchings_cnt += static_cast<int>(filter.matchings.size());
|
||||
}
|
||||
|
||||
std::vector<VaryMatchingStart> variants;
|
||||
variants.reserve(filter_matchings_cnt);
|
||||
|
||||
for (const auto &filter : matching.filters) {
|
||||
for (const auto &filter_matching : filter.matchings) {
|
||||
variants.emplace_back(filter_matching, symbol_table);
|
||||
}
|
||||
}
|
||||
|
||||
return MakeCartesianProduct(std::move(variants));
|
||||
}
|
||||
|
||||
VaryQueryPartMatching::VaryQueryPartMatching(SingleQueryPart query_part, const SymbolTable &symbol_table)
|
||||
: query_part_(std::move(query_part)),
|
||||
matchings_(VaryMatchingStart(query_part_.matching, symbol_table)),
|
||||
optional_matchings_(VaryMultiMatchingStarts(query_part_.optional_matching, symbol_table)),
|
||||
merge_matchings_(VaryMultiMatchingStarts(query_part_.merge_matching, symbol_table)) {}
|
||||
merge_matchings_(VaryMultiMatchingStarts(query_part_.merge_matching, symbol_table)),
|
||||
filter_matchings_(VaryFilterMatchingStarts(query_part_.matching, symbol_table)) {}
|
||||
|
||||
VaryQueryPartMatching::iterator::iterator(const SingleQueryPart &query_part,
|
||||
VaryMatchingStart::iterator matchings_begin,
|
||||
@@ -221,7 +253,9 @@ VaryQueryPartMatching::iterator::iterator(const SingleQueryPart &query_part,
|
||||
CartesianProduct<VaryMatchingStart>::iterator optional_begin,
|
||||
CartesianProduct<VaryMatchingStart>::iterator optional_end,
|
||||
CartesianProduct<VaryMatchingStart>::iterator merge_begin,
|
||||
CartesianProduct<VaryMatchingStart>::iterator merge_end)
|
||||
CartesianProduct<VaryMatchingStart>::iterator merge_end,
|
||||
CartesianProduct<VaryMatchingStart>::iterator filter_begin,
|
||||
CartesianProduct<VaryMatchingStart>::iterator filter_end)
|
||||
: current_query_part_(query_part),
|
||||
matchings_it_(matchings_begin),
|
||||
matchings_end_(matchings_end),
|
||||
@@ -230,7 +264,10 @@ VaryQueryPartMatching::iterator::iterator(const SingleQueryPart &query_part,
|
||||
optional_end_(optional_end),
|
||||
merge_it_(merge_begin),
|
||||
merge_begin_(merge_begin),
|
||||
merge_end_(merge_end) {
|
||||
merge_end_(merge_end),
|
||||
filter_it_(filter_begin),
|
||||
filter_begin_(filter_begin),
|
||||
filter_end_(filter_end) {
|
||||
if (matchings_it_ != matchings_end_) {
|
||||
// Fill the query part with the first variation of matchings
|
||||
SetCurrentQueryPart();
|
||||
@@ -242,26 +279,37 @@ VaryQueryPartMatching::iterator &VaryQueryPartMatching::iterator::operator++() {
|
||||
// * matchings (m1) and (m2)
|
||||
// * optional matchings (o1) and (o2)
|
||||
// * merge matching (g1)
|
||||
// * filter matching (f1) and (f2)
|
||||
// We want to produce parts for:
|
||||
// * (m1), (o1), (g1)
|
||||
// * (m1), (o2), (g1)
|
||||
// * (m2), (o1), (g1)
|
||||
// * (m2), (o2), (g1)
|
||||
// Create variations by changing the merge part first.
|
||||
if (merge_it_ != merge_end_) ++merge_it_;
|
||||
// If all merge variations are done, start them from beginning and move to the
|
||||
// next optional matching variation.
|
||||
if (merge_it_ == merge_end_) {
|
||||
// * (m1), (o1), (g1), (f1)
|
||||
// * (m1), (o1), (g1), (f2)
|
||||
// * (m1), (o2), (g1), (f1)
|
||||
// * (m1), (o2), (g1), (f2)
|
||||
// * (m2), (o1), (g1), (f1)
|
||||
// * (m2), (o1), (g1), (f2)
|
||||
// * (m2), (o2), (g1), (f1)
|
||||
// * (m2), (o2), (g1), (f2)
|
||||
|
||||
// Create variations by changing the filter part first.
|
||||
if (filter_it_ != filter_end_) ++filter_it_;
|
||||
|
||||
// Create variations by changing the merge part.
|
||||
if (filter_it_ == filter_end_) {
|
||||
filter_it_ = filter_begin_;
|
||||
if (merge_it_ != merge_end_) ++merge_it_;
|
||||
}
|
||||
|
||||
// Create variations by changing the optional part.
|
||||
if (merge_it_ == merge_end_ && filter_it_ == filter_begin_) {
|
||||
merge_it_ = merge_begin_;
|
||||
if (optional_it_ != optional_end_) ++optional_it_;
|
||||
}
|
||||
// If all optional matching variations are done (after exhausting merge
|
||||
// variations), start them from beginning and move to the next regular
|
||||
// matching variation.
|
||||
if (optional_it_ == optional_end_ && merge_it_ == merge_begin_) {
|
||||
|
||||
if (optional_it_ == optional_end_ && merge_it_ == merge_begin_ && filter_it_ == filter_begin_) {
|
||||
optional_it_ = optional_begin_;
|
||||
if (matchings_it_ != matchings_end_) ++matchings_it_;
|
||||
}
|
||||
|
||||
// We have reached the end, so return;
|
||||
if (matchings_it_ == matchings_end_) return *this;
|
||||
// Fill the query part with the new variation of matchings.
|
||||
@@ -283,6 +331,28 @@ void VaryQueryPartMatching::iterator::SetCurrentQueryPart() {
|
||||
if (merge_it_ != merge_end_) {
|
||||
current_query_part_.merge_matching = *merge_it_;
|
||||
}
|
||||
DMG_ASSERT(filter_it_ != filter_end_ || filter_begin_ == filter_end_,
|
||||
"Either there are no filter matchings or we can always generate"
|
||||
"a variation");
|
||||
|
||||
auto all_filter_matchings = *filter_it_;
|
||||
auto all_filter_matchings_idx = 0;
|
||||
for (auto &filter : current_query_part_.matching.filters) {
|
||||
auto matchings_size = filter.matchings.size();
|
||||
|
||||
std::vector<FilterMatching> new_matchings;
|
||||
new_matchings.reserve(matchings_size);
|
||||
|
||||
for (auto i = 0; i < matchings_size; i++) {
|
||||
new_matchings.push_back(ToFilterMatching(all_filter_matchings[all_filter_matchings_idx]));
|
||||
new_matchings[i].symbol = filter.matchings[i].symbol;
|
||||
new_matchings[i].type = filter.matchings[i].type;
|
||||
|
||||
all_filter_matchings_idx++;
|
||||
}
|
||||
|
||||
filter.matchings = std::move(new_matchings);
|
||||
}
|
||||
}
|
||||
|
||||
bool VaryQueryPartMatching::iterator::operator==(const iterator &other) const {
|
||||
@@ -291,7 +361,8 @@ bool VaryQueryPartMatching::iterator::operator==(const iterator &other) const {
|
||||
// iterators can be at any position.
|
||||
return true;
|
||||
}
|
||||
return matchings_it_ == other.matchings_it_ && optional_it_ == other.optional_it_ && merge_it_ == other.merge_it_;
|
||||
return matchings_it_ == other.matchings_it_ && optional_it_ == other.optional_it_ && merge_it_ == other.merge_it_ &&
|
||||
filter_it_ == other.filter_it_;
|
||||
}
|
||||
|
||||
} // namespace memgraph::query::plan::impl
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -230,6 +230,8 @@ class VaryMatchingStart {
|
||||
// Cartesian product of all of them is returned.
|
||||
CartesianProduct<VaryMatchingStart> VaryMultiMatchingStarts(const std::vector<Matching> &, const SymbolTable &);
|
||||
|
||||
CartesianProduct<VaryMatchingStart> VaryFilterMatchingStarts(const Matching &matching, const SymbolTable &symbol_table);
|
||||
|
||||
// Produces alternative query parts out of a single part by varying how each
|
||||
// graph matching is done.
|
||||
class VaryQueryPartMatching {
|
||||
@@ -245,6 +247,7 @@ class VaryQueryPartMatching {
|
||||
typedef const SingleQueryPart *pointer;
|
||||
|
||||
iterator(const SingleQueryPart &, VaryMatchingStart::iterator, VaryMatchingStart::iterator,
|
||||
CartesianProduct<VaryMatchingStart>::iterator, CartesianProduct<VaryMatchingStart>::iterator,
|
||||
CartesianProduct<VaryMatchingStart>::iterator, CartesianProduct<VaryMatchingStart>::iterator,
|
||||
CartesianProduct<VaryMatchingStart>::iterator, CartesianProduct<VaryMatchingStart>::iterator);
|
||||
|
||||
@@ -266,15 +269,20 @@ class VaryQueryPartMatching {
|
||||
CartesianProduct<VaryMatchingStart>::iterator merge_it_;
|
||||
CartesianProduct<VaryMatchingStart>::iterator merge_begin_;
|
||||
CartesianProduct<VaryMatchingStart>::iterator merge_end_;
|
||||
CartesianProduct<VaryMatchingStart>::iterator filter_it_;
|
||||
CartesianProduct<VaryMatchingStart>::iterator filter_begin_;
|
||||
CartesianProduct<VaryMatchingStart>::iterator filter_end_;
|
||||
};
|
||||
|
||||
auto begin() {
|
||||
return iterator(query_part_, matchings_.begin(), matchings_.end(), optional_matchings_.begin(),
|
||||
optional_matchings_.end(), merge_matchings_.begin(), merge_matchings_.end());
|
||||
optional_matchings_.end(), merge_matchings_.begin(), merge_matchings_.end(),
|
||||
filter_matchings_.begin(), filter_matchings_.end());
|
||||
}
|
||||
auto end() {
|
||||
return iterator(query_part_, matchings_.end(), matchings_.end(), optional_matchings_.end(),
|
||||
optional_matchings_.end(), merge_matchings_.end(), merge_matchings_.end());
|
||||
optional_matchings_.end(), merge_matchings_.end(), merge_matchings_.end(), filter_matchings_.end(),
|
||||
filter_matchings_.end());
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -286,6 +294,7 @@ class VaryQueryPartMatching {
|
||||
CartesianProduct<VaryMatchingStart> optional_matchings_;
|
||||
// Like optional matching, but for merge matchings.
|
||||
CartesianProduct<VaryMatchingStart> merge_matchings_;
|
||||
CartesianProduct<VaryMatchingStart> filter_matchings_;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "license/license.hpp"
|
||||
#include "mg_procedure.h"
|
||||
#include "module.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
@@ -32,7 +33,6 @@
|
||||
#include "storage/v2/view.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
#include "utils/concepts.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/math.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
@@ -1653,7 +1653,7 @@ mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_nam
|
||||
auto *ctx = v->graph->ctx;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast() && ctx && ctx->auth_checker &&
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast() && ctx && ctx->auth_checker &&
|
||||
!ctx->auth_checker->Has(v->getImpl(), v->graph->view,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw AuthorizationException{"Insufficient permissions for setting a property on vertex!"};
|
||||
@@ -1706,7 +1706,7 @@ mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) {
|
||||
const auto label_id = std::visit([label](auto *impl) { return impl->NameToLabel(label.name); }, v->graph->impl);
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast() && ctx && ctx->auth_checker &&
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast() && ctx && ctx->auth_checker &&
|
||||
!(ctx->auth_checker->Has(v->getImpl(), v->graph->view,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE) &&
|
||||
ctx->auth_checker->Has({label_id}, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE))) {
|
||||
@@ -1750,7 +1750,7 @@ mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, mgp_label label) {
|
||||
const auto label_id = std::visit([&label](auto *impl) { return impl->NameToLabel(label.name); }, v->graph->impl);
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast() && ctx && ctx->auth_checker &&
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast() && ctx && ctx->auth_checker &&
|
||||
!(ctx->auth_checker->Has(v->getImpl(), v->graph->view,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE) &&
|
||||
ctx->auth_checker->Has({label_id}, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE))) {
|
||||
@@ -1846,7 +1846,8 @@ mgp_error mgp_vertex_label_at(mgp_vertex *v, size_t i, mgp_label *result) {
|
||||
"Expected LabelToName to return a pointer or reference, so we "
|
||||
"don't have to take a copy and manage memory.");
|
||||
|
||||
const auto &name = std::visit([label](const auto *impl) { return impl->LabelToName(label); }, v->graph->impl);
|
||||
const auto &name = std::visit(
|
||||
[label](const auto *impl) -> const std::string & { return impl->LabelToName(label); }, v->graph->impl);
|
||||
return name.c_str();
|
||||
},
|
||||
&result->name);
|
||||
@@ -1988,7 +1989,7 @@ mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_
|
||||
it->in.emplace(std::move(*maybe_edges));
|
||||
it->in_it.emplace(it->in->begin());
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
NextPermittedEdge(*it, true);
|
||||
}
|
||||
#endif
|
||||
@@ -2040,7 +2041,7 @@ mgp_error mgp_vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges
|
||||
it->out_it.emplace(it->out->begin());
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
NextPermittedEdge(*it, false);
|
||||
}
|
||||
#endif
|
||||
@@ -2098,7 +2099,7 @@ mgp_error mgp_edges_iterator_next(mgp_edges_iterator *it, mgp_edge **result) {
|
||||
++*impl_it;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
NextPermittedEdge(*it, for_in);
|
||||
}
|
||||
#endif
|
||||
@@ -2123,10 +2124,7 @@ mgp_error mgp_edges_iterator_next(mgp_edges_iterator *it, mgp_edge **result) {
|
||||
return &*it->current_e;
|
||||
};
|
||||
if (it->in_it) {
|
||||
auto *result = next(true);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
return next(true);
|
||||
}
|
||||
return next(false);
|
||||
},
|
||||
@@ -2157,8 +2155,9 @@ mgp_error mgp_edge_equal(mgp_edge *e1, mgp_edge *e2, int *result) {
|
||||
mgp_error mgp_edge_get_type(mgp_edge *e, mgp_edge_type *result) {
|
||||
return WrapExceptions(
|
||||
[e] {
|
||||
const auto &name =
|
||||
std::visit([e](const auto *impl) { return impl->EdgeTypeToName(e->impl.EdgeType()); }, e->from.graph->impl);
|
||||
const auto &name = std::visit(
|
||||
[e](const auto *impl) -> const std::string & { return impl->EdgeTypeToName(e->impl.EdgeType()); },
|
||||
e->from.graph->impl);
|
||||
return name.c_str();
|
||||
},
|
||||
&result->name);
|
||||
@@ -2203,7 +2202,7 @@ mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, m
|
||||
auto *ctx = e->from.graph->ctx;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast() && ctx && ctx->auth_checker &&
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast() && ctx && ctx->auth_checker &&
|
||||
!ctx->auth_checker->Has(e->impl, memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||
throw AuthorizationException{"Insufficient permissions for setting a property on edge!"};
|
||||
}
|
||||
@@ -2311,7 +2310,7 @@ mgp_error mgp_graph_create_vertex(struct mgp_graph *graph, mgp_memory *memory, m
|
||||
[=]() -> mgp_vertex * {
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast() && graph->ctx &&
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast() && graph->ctx &&
|
||||
graph->ctx->auth_checker &&
|
||||
!graph->ctx->auth_checker->HasGlobalPrivilegeOnVertices(
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||
@@ -2341,7 +2340,7 @@ mgp_error mgp_graph_delete_vertex(struct mgp_graph *graph, mgp_vertex *vertex) {
|
||||
auto *ctx = graph->ctx;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast() && ctx && ctx->auth_checker &&
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast() && ctx && ctx->auth_checker &&
|
||||
!ctx->auth_checker->Has(vertex->getImpl(), graph->view,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||
throw AuthorizationException{"Insufficient permissions for deleting a vertex!"};
|
||||
@@ -2392,7 +2391,7 @@ mgp_error mgp_graph_detach_delete_vertex(struct mgp_graph *graph, mgp_vertex *ve
|
||||
return WrapExceptions([=] {
|
||||
auto *ctx = graph->ctx;
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast() && ctx && ctx->auth_checker &&
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast() && ctx && ctx->auth_checker &&
|
||||
!ctx->auth_checker->Has(vertex->getImpl(), graph->view,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||
throw AuthorizationException{"Insufficient permissions for deleting a vertex!"};
|
||||
@@ -2456,7 +2455,7 @@ mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *
|
||||
#ifdef MG_ENTERPRISE
|
||||
const auto edge_id =
|
||||
std::visit([type](auto *impl) { return impl->NameToEdgeType(type.name); }, from->graph->impl);
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast() && ctx && ctx->auth_checker &&
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast() && ctx && ctx->auth_checker &&
|
||||
!ctx->auth_checker->Has(edge_id, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||
throw AuthorizationException{"Insufficient permissions for creating edges!"};
|
||||
}
|
||||
@@ -2517,7 +2516,7 @@ mgp_error mgp_graph_delete_edge(struct mgp_graph *graph, mgp_edge *edge) {
|
||||
return WrapExceptions([=] {
|
||||
auto *ctx = graph->ctx;
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast() && ctx && ctx->auth_checker &&
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast() && ctx && ctx->auth_checker &&
|
||||
!ctx->auth_checker->Has(edge->impl, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||
throw AuthorizationException{"Insufficient permissions for deleting an edge!"};
|
||||
}
|
||||
@@ -2579,7 +2578,7 @@ mgp_vertices_iterator::mgp_vertices_iterator(mgp_graph *graph, memgraph::utils::
|
||||
vertices(std::visit([graph](auto *impl) { return impl->Vertices(graph->view); }, graph->impl)),
|
||||
current_it(vertices.begin()) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
NextPermitted(*this);
|
||||
}
|
||||
#endif
|
||||
@@ -2628,7 +2627,7 @@ mgp_error mgp_vertices_iterator_next(mgp_vertices_iterator *it, mgp_vertex **res
|
||||
|
||||
++it->current_it;
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::utils::license::global_license_checker.IsValidLicenseFast()) {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
NextPermitted(*it);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -146,10 +146,10 @@ struct mgp_date {
|
||||
mgp_date(const memgraph::utils::Date &date, memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), date(date) {}
|
||||
|
||||
mgp_date(const std::string_view string, memgraph::utils::MemoryResource *memory) noexcept
|
||||
mgp_date(const std::string_view string, memgraph::utils::MemoryResource *memory)
|
||||
: memory(memory), date(memgraph::utils::ParseDateParameters(string).first) {}
|
||||
|
||||
mgp_date(const mgp_date_parameters *parameters, memgraph::utils::MemoryResource *memory) noexcept
|
||||
mgp_date(const mgp_date_parameters *parameters, memgraph::utils::MemoryResource *memory)
|
||||
: memory(memory), date(MapDateParameters(parameters)) {}
|
||||
|
||||
mgp_date(const int64_t microseconds, memgraph::utils::MemoryResource *memory) noexcept
|
||||
@@ -194,10 +194,10 @@ struct mgp_local_time {
|
||||
// have everything noexcept here.
|
||||
static_assert(std::is_nothrow_copy_constructible_v<memgraph::utils::LocalTime>);
|
||||
|
||||
mgp_local_time(const std::string_view string, memgraph::utils::MemoryResource *memory) noexcept
|
||||
mgp_local_time(const std::string_view string, memgraph::utils::MemoryResource *memory)
|
||||
: memory(memory), local_time(memgraph::utils::ParseLocalTimeParameters(string).first) {}
|
||||
|
||||
mgp_local_time(const mgp_local_time_parameters *parameters, memgraph::utils::MemoryResource *memory) noexcept
|
||||
mgp_local_time(const mgp_local_time_parameters *parameters, memgraph::utils::MemoryResource *memory)
|
||||
: memory(memory), local_time(MapLocalTimeParameters(parameters)) {}
|
||||
|
||||
mgp_local_time(const memgraph::utils::LocalTime &local_time, memgraph::utils::MemoryResource *memory) noexcept
|
||||
@@ -250,8 +250,7 @@ struct mgp_local_date_time {
|
||||
mgp_local_date_time(const std::string_view string, memgraph::utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), local_date_time(CreateLocalDateTimeFromString(string)) {}
|
||||
|
||||
mgp_local_date_time(const mgp_local_date_time_parameters *parameters,
|
||||
memgraph::utils::MemoryResource *memory) noexcept
|
||||
mgp_local_date_time(const mgp_local_date_time_parameters *parameters, memgraph::utils::MemoryResource *memory)
|
||||
: memory(memory),
|
||||
local_date_time(MapDateParameters(parameters->date_parameters),
|
||||
MapLocalTimeParameters(parameters->local_time_parameters)) {}
|
||||
@@ -301,7 +300,7 @@ struct mgp_duration {
|
||||
// have everything noexcept here.
|
||||
static_assert(std::is_nothrow_copy_constructible_v<memgraph::utils::Duration>);
|
||||
|
||||
mgp_duration(const std::string_view string, memgraph::utils::MemoryResource *memory) noexcept
|
||||
mgp_duration(const std::string_view string, memgraph::utils::MemoryResource *memory)
|
||||
: memory(memory), duration(memgraph::utils::ParseDurationParameters(string)) {}
|
||||
|
||||
mgp_duration(const mgp_duration_parameters *parameters, memgraph::utils::MemoryResource *memory) noexcept
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -33,6 +33,28 @@ extern "C" {
|
||||
|
||||
namespace memgraph::query::procedure {
|
||||
|
||||
constexpr const char *func_code =
|
||||
"import ast\n\n"
|
||||
"no_removals = ['collections', 'abc', 'sys', 'torch', 'torch_geometric', 'igraph']\n"
|
||||
"modules = set()\n\n"
|
||||
"def visit_Import(node):\n"
|
||||
" for name in node.names:\n"
|
||||
" mod_name = name.name.split('.')[0]\n"
|
||||
" if mod_name not in no_removals:\n"
|
||||
" modules.add(mod_name)\n\n"
|
||||
"def visit_ImportFrom(node):\n"
|
||||
" if node.module is not None and node.level == 0:\n"
|
||||
" mod_name = node.module.split('.')[0]\n"
|
||||
" if mod_name not in no_removals:\n"
|
||||
" modules.add(mod_name)\n"
|
||||
"node_iter = ast.NodeVisitor()\n"
|
||||
"node_iter.visit_Import = visit_Import\n"
|
||||
"node_iter.visit_ImportFrom = visit_ImportFrom\n"
|
||||
"node_iter.visit(ast.parse(code))\n";
|
||||
|
||||
void ProcessFileDependencies(std::filesystem::path file_path_, const char *module_path, const char *func_code,
|
||||
PyObject *sys_mod_ref);
|
||||
|
||||
ModuleRegistry gModuleRegistry;
|
||||
|
||||
Module::~Module() {}
|
||||
@@ -995,11 +1017,45 @@ bool PythonModule::Close() {
|
||||
procedures_.clear();
|
||||
transformations_.clear();
|
||||
functions_.clear();
|
||||
// Delete the module from the `sys.modules` directory so that the module will
|
||||
// be properly imported if imported again.
|
||||
|
||||
// Get the reference to sys.modules dictionary
|
||||
py::Object sys(PyImport_ImportModule("sys"));
|
||||
if (PyDict_DelItemString(sys.GetAttr("modules").Ptr(), file_path_.stem().c_str()) != 0) {
|
||||
spdlog::warn("Failed to remove the module from sys.modules");
|
||||
PyObject *sys_mod_ref = sys.GetAttr("modules").Ptr();
|
||||
|
||||
std::string stem = file_path_.stem().string();
|
||||
|
||||
ProcessFileDependencies(file_path_, file_path_.stem().c_str(), func_code, sys_mod_ref);
|
||||
|
||||
std::vector<std::filesystem::path> submodules;
|
||||
|
||||
for (auto it = std::filesystem::recursive_directory_iterator(file_path_.parent_path());
|
||||
it != std::filesystem::recursive_directory_iterator(); ++it) {
|
||||
std::string dir_entry_stem = it->path().stem().string();
|
||||
if (it->is_regular_file() || dir_entry_stem == "__pycache__") continue;
|
||||
if (dir_entry_stem.find(stem) != std::string_view::npos) {
|
||||
it.disable_recursion_pending();
|
||||
submodules.emplace_back(it->path());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &submodule : submodules) {
|
||||
if (std::filesystem::exists(submodule)) {
|
||||
std::filesystem::remove_all(submodule / "__pycache__");
|
||||
for (auto const &rec_dir_entry : std::filesystem::recursive_directory_iterator(submodule)) {
|
||||
std::string rec_dir_entry_stem = rec_dir_entry.path().stem().string();
|
||||
if (rec_dir_entry.is_directory() && rec_dir_entry_stem != "__pycache__") {
|
||||
std::filesystem::remove_all(rec_dir_entry.path() / "__pycache__");
|
||||
}
|
||||
std::string rec_dir_entry_ext = rec_dir_entry.path().extension().string();
|
||||
if (!rec_dir_entry.is_regular_file() || rec_dir_entry_ext != ".py") continue;
|
||||
ProcessFileDependencies(rec_dir_entry.path().c_str(), file_path_.stem().c_str(), func_code, sys_mod_ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// first throw out of cache file
|
||||
if (PyDict_DelItemString(sys_mod_ref, file_path_.stem().c_str()) != 0) {
|
||||
spdlog::warn("Failed to remove the module {} from sys.modules", file_path_.stem().c_str());
|
||||
py_module_ = py::Object(nullptr);
|
||||
return false;
|
||||
}
|
||||
@@ -1011,6 +1067,51 @@ bool PythonModule::Close() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProcessFileDependencies(std::filesystem::path file_path_, const char *module_path, const char *func_code,
|
||||
PyObject *sys_mod_ref) {
|
||||
const auto maybe_content =
|
||||
ReadFile(file_path_); // this is already done at Load so it can somehow be optimized but not sure how yet
|
||||
|
||||
if (maybe_content) {
|
||||
const char *content_value = maybe_content->c_str();
|
||||
if (content_value) {
|
||||
PyObject *py_main = PyImport_ImportModule("__main__");
|
||||
PyObject *py_global_dict = PyModule_GetDict(py_main);
|
||||
|
||||
PyDict_SetItemString(py_global_dict, "code", PyUnicode_FromString(content_value));
|
||||
PyRun_String(func_code, Py_file_input, py_global_dict, py_global_dict);
|
||||
PyObject *py_res = PyDict_GetItemString(py_global_dict, "modules");
|
||||
|
||||
PyObject *iterator = PyObject_GetIter(py_res);
|
||||
PyObject *module = nullptr;
|
||||
|
||||
if (iterator != nullptr) {
|
||||
while ((module = PyIter_Next(iterator))) {
|
||||
const char *module_name = PyUnicode_AsUTF8(module);
|
||||
auto module_name_str = std::string(module_name);
|
||||
PyObject *sys_iterator = PyObject_GetIter(PyDict_Keys(sys_mod_ref));
|
||||
if (sys_iterator == nullptr) {
|
||||
spdlog::warn("Cannot get reference to the sys.modules.keys()");
|
||||
break;
|
||||
}
|
||||
PyObject *sys_mod_key = nullptr;
|
||||
while ((sys_mod_key = PyIter_Next(sys_iterator))) {
|
||||
const char *sys_mod_key_name = PyUnicode_AsUTF8(sys_mod_key);
|
||||
auto sys_mod_key_name_str = std::string(sys_mod_key_name);
|
||||
if (sys_mod_key_name_str.rfind(module_name_str, 0) == 0 && sys_mod_key_name_str.compare(module_path) != 0) {
|
||||
PyDict_DelItemString(sys_mod_ref, sys_mod_key_name); // don't test output
|
||||
}
|
||||
Py_DECREF(sys_mod_key);
|
||||
}
|
||||
Py_DECREF(sys_iterator);
|
||||
Py_DECREF(module);
|
||||
}
|
||||
Py_DECREF(iterator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<std::string, mgp_proc, std::less<>> *PythonModule::Procedures() const {
|
||||
MG_ASSERT(py_module_,
|
||||
"Attempting to access procedures of a module that has "
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/fnv.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
@@ -215,7 +216,8 @@ TypedValue::TypedValue(const TypedValue &other, utils::MemoryResource *memory) :
|
||||
new (&duration_v) utils::Duration(other.duration_v);
|
||||
return;
|
||||
case Type::Graph:
|
||||
new (&graph_v) Graph(other.graph_v, memory_);
|
||||
auto *graph_ptr = utils::Allocator<Graph>(memory_).new_object<Graph>(*other.graph_v);
|
||||
new (&graph_v) std::unique_ptr<Graph>(graph_ptr);
|
||||
return;
|
||||
}
|
||||
LOG_FATAL("Unsupported TypedValue::Type");
|
||||
@@ -267,7 +269,12 @@ TypedValue::TypedValue(TypedValue &&other, utils::MemoryResource *memory) : memo
|
||||
new (&duration_v) utils::Duration(other.duration_v);
|
||||
break;
|
||||
case Type::Graph:
|
||||
new (&graph_v) Graph(std::move(other.graph_v), memory_);
|
||||
if (other.GetMemoryResource() == memory_) {
|
||||
new (&graph_v) std::unique_ptr<Graph>(std::move(other.graph_v));
|
||||
} else {
|
||||
auto *graph_ptr = utils::Allocator<Graph>(memory_).new_object<Graph>(std::move(*other.graph_v));
|
||||
new (&graph_v) std::unique_ptr<Graph>(graph_ptr);
|
||||
}
|
||||
}
|
||||
other.DestroyValue();
|
||||
}
|
||||
@@ -336,7 +343,22 @@ DEFINE_VALUE_AND_TYPE_GETTERS(utils::Date, Date, date_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(utils::LocalTime, LocalTime, local_time_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(utils::LocalDateTime, LocalDateTime, local_date_time_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(utils::Duration, Duration, duration_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(Graph, Graph, graph_v)
|
||||
|
||||
Graph &TypedValue::ValueGraph() {
|
||||
if (type_ != Type::Graph) {
|
||||
throw TypedValueException("TypedValue is of type '{}', not '{}'", type_, Type::Graph);
|
||||
}
|
||||
return *graph_v;
|
||||
}
|
||||
|
||||
const Graph &TypedValue::ValueGraph() const {
|
||||
if (type_ != Type::Graph) {
|
||||
throw TypedValueException("TypedValue is of type '{}', not '{}'", type_, Type::Graph);
|
||||
}
|
||||
return *graph_v;
|
||||
}
|
||||
|
||||
bool TypedValue::IsGraph() const { return type_ == Type::Graph; }
|
||||
|
||||
#undef DEFINE_VALUE_AND_TYPE_GETTERS
|
||||
|
||||
@@ -530,9 +552,11 @@ TypedValue &TypedValue::operator=(const TypedValue &other) {
|
||||
case TypedValue::Type::Path:
|
||||
new (&path_v) Path(other.path_v, memory_);
|
||||
return *this;
|
||||
case TypedValue::Type::Graph:
|
||||
new (&graph_v) Graph(other.graph_v, memory_);
|
||||
case TypedValue::Type::Graph: {
|
||||
auto *graph_ptr = utils::Allocator<Graph>(memory_).new_object<Graph>(*other.graph_v);
|
||||
new (&graph_v) std::unique_ptr<Graph>(graph_ptr);
|
||||
return *this;
|
||||
}
|
||||
case Type::Date:
|
||||
new (&date_v) utils::Date(other.date_v);
|
||||
return *this;
|
||||
@@ -605,7 +629,12 @@ TypedValue &TypedValue::operator=(TypedValue &&other) noexcept(false) {
|
||||
new (&duration_v) utils::Duration(other.duration_v);
|
||||
break;
|
||||
case Type::Graph:
|
||||
new (&graph_v) Graph(std::move(other.graph_v), memory_);
|
||||
if (other.GetMemoryResource() == memory_) {
|
||||
new (&graph_v) std::unique_ptr<Graph>(std::move(other.graph_v));
|
||||
} else {
|
||||
auto *graph_ptr = utils::Allocator<Graph>(memory_).new_object<Graph>(std::move(*other.graph_v));
|
||||
new (&graph_v) std::unique_ptr<Graph>(graph_ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
other.DestroyValue();
|
||||
@@ -625,31 +654,36 @@ void TypedValue::DestroyValue() {
|
||||
// we need to call destructors for non primitive types since we used
|
||||
// placement new
|
||||
case Type::String:
|
||||
string_v.~TString();
|
||||
std::destroy_at(&string_v);
|
||||
break;
|
||||
case Type::List:
|
||||
list_v.~TVector();
|
||||
std::destroy_at(&list_v);
|
||||
break;
|
||||
case Type::Map:
|
||||
map_v.~TMap();
|
||||
std::destroy_at(&map_v);
|
||||
break;
|
||||
case Type::Vertex:
|
||||
vertex_v.~VertexAccessor();
|
||||
std::destroy_at(&vertex_v);
|
||||
break;
|
||||
case Type::Edge:
|
||||
edge_v.~EdgeAccessor();
|
||||
std::destroy_at(&edge_v);
|
||||
break;
|
||||
case Type::Path:
|
||||
path_v.~Path();
|
||||
std::destroy_at(&path_v);
|
||||
break;
|
||||
case Type::Date:
|
||||
case Type::LocalTime:
|
||||
case Type::LocalDateTime:
|
||||
case Type::Duration:
|
||||
break;
|
||||
case Type::Graph:
|
||||
graph_v.~Graph();
|
||||
case Type::Graph: {
|
||||
auto *graph = graph_v.release();
|
||||
std::destroy_at(&graph_v);
|
||||
if (graph) {
|
||||
utils::Allocator<Graph>(memory_).delete_object(graph);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
type_ = TypedValue::Type::Null;
|
||||
|
||||
@@ -416,7 +416,8 @@ class TypedValue {
|
||||
* element-wise move and graph is not guaranteed to be empty.
|
||||
*/
|
||||
TypedValue(Graph &&graph, utils::MemoryResource *memory) : memory_(memory), type_(Type::Graph) {
|
||||
new (&graph_v) Graph(std::move(graph), memory_);
|
||||
auto *graph_ptr = utils::Allocator<Graph>(memory_).new_object<Graph>(std::move(graph));
|
||||
new (&graph_v) std::unique_ptr<Graph>(graph_ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -547,7 +548,8 @@ class TypedValue {
|
||||
utils::LocalTime local_time_v;
|
||||
utils::LocalDateTime local_date_time_v;
|
||||
utils::Duration duration_v;
|
||||
Graph graph_v;
|
||||
// As the unique_ptr is not allocator aware, it requires special attention when copying or moving graphs
|
||||
std::unique_ptr<Graph> graph_v;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "storage/v2/edge_accessor.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include "storage/v2/mvcc.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
@@ -21,8 +22,47 @@
|
||||
namespace memgraph::storage {
|
||||
|
||||
bool EdgeAccessor::IsVisible(const View view) const {
|
||||
bool deleted = true;
|
||||
bool exists = true;
|
||||
bool deleted = true;
|
||||
// When edges don't have properties, their isolation level is still dictated by MVCC ->
|
||||
// iterate over the deltas of the from_vertex_ and see which deltas can be applied on edges.
|
||||
if (!config_.properties_on_edges) {
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(from_vertex_->lock);
|
||||
// Initialize deleted by checking if out edges contain edge_
|
||||
deleted = std::find_if(from_vertex_->out_edges.begin(), from_vertex_->out_edges.end(), [&](const auto &out_edge) {
|
||||
return std::get<2>(out_edge) == edge_;
|
||||
}) == from_vertex_->out_edges.end();
|
||||
delta = from_vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::RECREATE_OBJECT:
|
||||
case Delta::Action::DELETE_OBJECT:
|
||||
break;
|
||||
case Delta::Action::ADD_OUT_EDGE: { // relevant for the from_vertex_ -> we just deleted the edge
|
||||
if (delta.vertex_edge.edge == edge_) {
|
||||
deleted = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::REMOVE_OUT_EDGE: { // also relevant for the from_vertex_ -> we just added the edge
|
||||
if (delta.vertex_edge.edge == edge_) {
|
||||
exists = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return exists && (for_deleted_ || !deleted);
|
||||
}
|
||||
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock);
|
||||
@@ -49,7 +89,6 @@ bool EdgeAccessor::IsVisible(const View view) const {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return exists && (for_deleted_ || !deleted);
|
||||
}
|
||||
|
||||
@@ -84,6 +123,24 @@ Result<storage::PropertyValue> EdgeAccessor::SetProperty(PropertyId property, co
|
||||
return std::move(current_value);
|
||||
}
|
||||
|
||||
Result<bool> EdgeAccessor::InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties) {
|
||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||
if (!config_.properties_on_edges) return Error::PROPERTIES_DISABLED;
|
||||
|
||||
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (edge_.ptr->deleted) return Error::DELETED_OBJECT;
|
||||
|
||||
if (!edge_.ptr->properties.InitProperties(properties)) return false;
|
||||
for (const auto &[property, _] : properties) {
|
||||
CreateAndLinkDelta(transaction_, edge_.ptr, Delta::SetPropertyTag(), property, PropertyValue());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::ClearProperties() {
|
||||
if (!config_.properties_on_edges) return Error::PROPERTIES_DISABLED;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -58,6 +58,11 @@ class EdgeAccessor final {
|
||||
/// @throw std::bad_alloc
|
||||
Result<storage::PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
|
||||
|
||||
/// Set property values only if property store is empty. Returns `true` if successully set all values,
|
||||
/// `false` otherwise.
|
||||
/// @throw std::bad_alloc
|
||||
Result<bool> InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties);
|
||||
|
||||
/// Remove all properties and return old values for each removed property.
|
||||
/// @throw std::bad_alloc
|
||||
Result<std::map<PropertyId, PropertyValue>> ClearProperties();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -1053,7 +1053,7 @@ bool PropertyStore::SetProperty(PropertyId property, const PropertyValue &value)
|
||||
in_local_buffer = true;
|
||||
} else {
|
||||
// Allocate a new external buffer.
|
||||
auto alloc_data = new uint8_t[property_size_to_power_of_8];
|
||||
auto *alloc_data = new uint8_t[property_size_to_power_of_8];
|
||||
auto alloc_size = property_size_to_power_of_8;
|
||||
|
||||
SetSizeData(buffer_, alloc_size, alloc_data);
|
||||
@@ -1144,6 +1144,64 @@ bool PropertyStore::SetProperty(PropertyId property, const PropertyValue &value)
|
||||
return !existed;
|
||||
}
|
||||
|
||||
bool PropertyStore::InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties) {
|
||||
uint64_t size = 0;
|
||||
uint8_t *data = nullptr;
|
||||
std::tie(size, data) = GetSizeData(buffer_);
|
||||
if (size != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t property_size = 0;
|
||||
{
|
||||
Writer writer;
|
||||
for (const auto &[property, value] : properties) {
|
||||
if (value.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
EncodeProperty(&writer, property, value);
|
||||
property_size = writer.Written();
|
||||
}
|
||||
}
|
||||
|
||||
auto property_size_to_power_of_8 = ToPowerOf8(property_size);
|
||||
if (property_size <= sizeof(buffer_) - 1) {
|
||||
// Use the local buffer.
|
||||
buffer_[0] = kUseLocalBuffer;
|
||||
size = sizeof(buffer_) - 1;
|
||||
data = &buffer_[1];
|
||||
} else {
|
||||
// Allocate a new external buffer.
|
||||
auto *alloc_data = new uint8_t[property_size_to_power_of_8];
|
||||
auto alloc_size = property_size_to_power_of_8;
|
||||
|
||||
SetSizeData(buffer_, alloc_size, alloc_data);
|
||||
|
||||
size = alloc_size;
|
||||
data = alloc_data;
|
||||
}
|
||||
|
||||
// Encode the property into the data buffer.
|
||||
Writer writer(data, size);
|
||||
|
||||
for (const auto &[property, value] : properties) {
|
||||
if (value.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
MG_ASSERT(EncodeProperty(&writer, property, value), "Invalid database state!");
|
||||
writer.Written();
|
||||
}
|
||||
|
||||
auto metadata = writer.WriteMetadata();
|
||||
if (metadata) {
|
||||
// If there is any space left in the buffer we add a tombstone to
|
||||
// indicate that there are no more properties to be decoded.
|
||||
metadata->Set({Type::EMPTY});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyStore::ClearProperties() {
|
||||
bool in_local_buffer = false;
|
||||
uint64_t size;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -59,6 +59,12 @@ class PropertyStore {
|
||||
/// @throw std::bad_alloc
|
||||
bool SetProperty(PropertyId property, const PropertyValue &value);
|
||||
|
||||
/// Init property values and return `true` if insertion took place. `false` is
|
||||
/// returned if there exists property in property store and insertion couldn't take place. The time complexity of this
|
||||
/// function is O(n).
|
||||
/// @throw std::bad_alloc
|
||||
bool InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties);
|
||||
|
||||
/// Remove all properties and return `true` if any removal took place.
|
||||
/// `false` is returned if there were no properties to remove. The time
|
||||
/// complexity of this function is O(1).
|
||||
|
||||
@@ -109,9 +109,11 @@ void Storage::ReplicationClient::InitializeClient() {
|
||||
}
|
||||
if (branching_point) {
|
||||
spdlog::error(
|
||||
"Replica {} cannot be used with this instance. Please start a clean "
|
||||
"instance of Memgraph server on the specified endpoint.",
|
||||
name_);
|
||||
"You cannot register Replica {} to this Main because at one point "
|
||||
"Replica {} acted as the Main instance. Both the Main and Replica {} "
|
||||
"now hold unique data. Please resolve data conflicts and start the "
|
||||
"replication on a clean instance.",
|
||||
name_, name_, name_);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -230,6 +230,23 @@ Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const Pro
|
||||
return std::move(current_value);
|
||||
}
|
||||
|
||||
Result<bool> VertexAccessor::InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties) {
|
||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
|
||||
if (!vertex_->properties.InitProperties(properties)) return false;
|
||||
for (const auto &[property, value] : properties) {
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, PropertyValue());
|
||||
UpdateOnSetProperty(indices_, property, value, vertex_, *transaction_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -68,6 +68,11 @@ class VertexAccessor final {
|
||||
/// @throw std::bad_alloc
|
||||
Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
|
||||
|
||||
/// Set property values only if property store is empty. Returns `true` if successully set all values,
|
||||
/// `false` otherwise.
|
||||
/// @throw std::bad_alloc
|
||||
Result<bool> InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties);
|
||||
|
||||
/// Remove all properties and return the values of the removed properties.
|
||||
/// @throw std::bad_alloc
|
||||
Result<std::map<PropertyId, PropertyValue>> ClearProperties();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
set(telemetry_src_files
|
||||
collectors.cpp
|
||||
telemetry.cpp
|
||||
system_info.cpp)
|
||||
collectors.cpp
|
||||
telemetry.cpp)
|
||||
|
||||
add_library(telemetry_lib STATIC ${telemetry_src_files})
|
||||
target_link_libraries(telemetry_lib mg-requests mg-kvstore mg-utils)
|
||||
add_library(mg-telemetry STATIC ${telemetry_src_files})
|
||||
target_link_libraries(mg-telemetry mg-requests mg-kvstore mg-utils)
|
||||
option(MG_TELEMETRY_ID_OVERRIDE "Override for the telemetry ID" STRING)
|
||||
if (MG_TELEMETRY_ID_OVERRIDE)
|
||||
|
||||
if(MG_TELEMETRY_ID_OVERRIDE)
|
||||
message(WARNING "Using telemetry ID override: ${MG_TELEMETRY_ID_OVERRIDE}")
|
||||
target_compile_definitions(telemetry_lib PRIVATE MG_TELEMETRY_ID_OVERRIDE="${MG_TELEMETRY_ID_OVERRIDE}")
|
||||
target_compile_definitions(mg-telemetry PRIVATE MG_TELEMETRY_ID_OVERRIDE="${MG_TELEMETRY_ID_OVERRIDE}")
|
||||
endif()
|
||||
|
||||
@@ -17,38 +17,24 @@
|
||||
|
||||
#include "requests/requests.hpp"
|
||||
#include "telemetry/collectors.hpp"
|
||||
#include "telemetry/system_info.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/system_info.hpp"
|
||||
#include "utils/timestamp.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
namespace memgraph::telemetry {
|
||||
namespace {
|
||||
std::string GetMachineId() {
|
||||
#ifdef MG_TELEMETRY_ID_OVERRIDE
|
||||
return MG_TELEMETRY_ID_OVERRIDE;
|
||||
#else
|
||||
// We assume we're on linux and we need to read the machine id from /etc/machine-id
|
||||
const auto machine_id_lines = utils::ReadLines("/etc/machine-id");
|
||||
if (machine_id_lines.size() != 1) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return machine_id_lines[0];
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const int kMaxBatchSize = 100;
|
||||
constexpr auto kMaxBatchSize{100};
|
||||
|
||||
Telemetry::Telemetry(std::string url, std::filesystem::path storage_directory,
|
||||
Telemetry::Telemetry(std::string url, std::filesystem::path storage_directory, std::string uuid, std::string machine_id,
|
||||
std::chrono::duration<int64_t> refresh_interval, const uint64_t send_every_n)
|
||||
: url_(std::move(url)),
|
||||
uuid_(utils::GenerateUUID()),
|
||||
machine_id_(GetMachineId()),
|
||||
uuid_(uuid),
|
||||
machine_id_(machine_id),
|
||||
send_every_n_(send_every_n),
|
||||
storage_(std::move(storage_directory)) {
|
||||
StoreData("startup", GetSystemInfo());
|
||||
StoreData("startup", utils::GetSystemInfo());
|
||||
AddCollector("resources", GetResourceUsage);
|
||||
AddCollector("uptime", [&]() -> nlohmann::json { return GetUptime(); });
|
||||
scheduler_.Run("Telemetry", refresh_interval, [&] { CollectData(); });
|
||||
@@ -59,19 +45,15 @@ void Telemetry::AddCollector(const std::string &name, const std::function<const
|
||||
collectors_.emplace_back(name, func);
|
||||
}
|
||||
|
||||
std::string Telemetry::GetRunId() const { return uuid_; }
|
||||
|
||||
Telemetry::~Telemetry() {
|
||||
scheduler_.Stop();
|
||||
CollectData("shutdown");
|
||||
}
|
||||
|
||||
void Telemetry::StoreData(const nlohmann::json &event, const nlohmann::json &data) {
|
||||
nlohmann::json payload = {{"run_id", uuid_},
|
||||
{"machine_id", machine_id_},
|
||||
{"event", event},
|
||||
{"data", data},
|
||||
{"timestamp", utils::Timestamp::Now().SecWithNsecSinceTheEpoch()}};
|
||||
nlohmann::json payload = {
|
||||
{"run_id", uuid_}, {"type", "telemetry"}, {"machine_id", machine_id_},
|
||||
{"event", event}, {"data", data}, {"timestamp", utils::Timestamp::Now().SecWithNsecSinceTheEpoch()}};
|
||||
storage_.Put(fmt::format("{}:{}", uuid_, event.dump()), payload.dump());
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace memgraph::telemetry {
|
||||
* This class implements the telemetry collector service. It periodically scapes
|
||||
* all registered collectors and stores their data. With periodically scraping
|
||||
* the collectors the service collects machine information in the constructor
|
||||
* and stores it. Also, it calles all collectors once more in the destructor so
|
||||
* and stores it. Also, it calls all collectors once more in the destructor so
|
||||
* that final stats can be collected. All data is stored persistently. If there
|
||||
* is no internet connection the data will be sent when the internet connection
|
||||
* is reestablished. If there is an issue with the internet connection that
|
||||
@@ -34,14 +34,11 @@ namespace memgraph::telemetry {
|
||||
*/
|
||||
class Telemetry final {
|
||||
public:
|
||||
Telemetry(std::string url, std::filesystem::path storage_directory,
|
||||
Telemetry(std::string url, std::filesystem::path storage_directory, std::string uuid, std::string machine_id,
|
||||
std::chrono::duration<int64_t> refresh_interval = std::chrono::minutes(10), uint64_t send_every_n = 10);
|
||||
|
||||
void AddCollector(const std::string &name, const std::function<const nlohmann::json(void)> &func);
|
||||
|
||||
/// Required to expose run_id to Bolt server.
|
||||
std::string GetRunId() const;
|
||||
|
||||
~Telemetry();
|
||||
|
||||
Telemetry(const Telemetry &) = delete;
|
||||
|
||||
@@ -14,6 +14,7 @@ set(utils_src_files
|
||||
thread.cpp
|
||||
thread_pool.cpp
|
||||
tsc.cpp
|
||||
system_info.cpp
|
||||
uuid.cpp)
|
||||
|
||||
find_package(Boost REQUIRED)
|
||||
@@ -23,16 +24,10 @@ find_package(Threads REQUIRED)
|
||||
|
||||
add_library(mg-utils STATIC ${utils_src_files})
|
||||
target_link_libraries(mg-utils PUBLIC Boost::headers fmt::fmt spdlog::spdlog)
|
||||
target_link_libraries(mg-utils PRIVATE librdtsc stdc++fs Threads::Threads gflags uuid rt)
|
||||
target_link_libraries(mg-utils PRIVATE librdtsc stdc++fs Threads::Threads gflags json uuid rt)
|
||||
|
||||
set(settings_src_files
|
||||
settings.cpp)
|
||||
|
||||
add_library(mg-settings STATIC ${settings_src_files})
|
||||
target_link_libraries(mg-settings mg-kvstore mg-slk mg-utils)
|
||||
|
||||
set(license_src_files
|
||||
license.cpp)
|
||||
add_library(mg-license STATIC ${license_src_files})
|
||||
target_link_libraries(mg-license mg-settings mg-utils)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user