Compare commits
18 Commits
v2.0.0
...
demo_addit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2303c40992 | ||
|
|
a647eb7926 | ||
|
|
e9a937ad6d | ||
|
|
6dc2cdfae4 | ||
|
|
69ea410406 | ||
|
|
24a576c8e9 | ||
|
|
d417ffee6e | ||
|
|
f868b51483 | ||
|
|
26dc73c809 | ||
|
|
9119c89eba | ||
|
|
4e7ea34ae9 | ||
|
|
d7d291217b | ||
|
|
42facfacbc | ||
|
|
bd1e848363 | ||
|
|
0ffb58b764 | ||
|
|
9551b6973a | ||
|
|
f7ef5f50a4 | ||
|
|
046cd80054 |
@@ -30,6 +30,14 @@ for file in $modified_files; do
|
||||
if [ $code -ne 0 ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
echo "Running header checker..."
|
||||
$project_folder/tools/header-checker.py $tmpdir/$file
|
||||
code=$?
|
||||
|
||||
if [ $code -ne 0 ]; then
|
||||
break
|
||||
fi
|
||||
done;
|
||||
|
||||
return $code
|
||||
|
||||
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG] "
|
||||
labels: bug
|
||||
assignees: gitbuda, antonio2368
|
||||
|
||||
---
|
||||
|
||||
|
||||
**Memgraph version**
|
||||
Which version did you use?
|
||||
|
||||
**Environment**
|
||||
Some information about the environment you are using Memgraph on: operating
|
||||
system, how do you connect, with or without docker, which driver etc.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Run the following query '...'
|
||||
2. Click on '....'
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Logs**
|
||||
If applicable, add logs of Memgraph, CLI output or screenshots to help explain
|
||||
your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
38
.github/workflows/diff.yaml
vendored
38
.github/workflows/diff.yaml
vendored
@@ -1,7 +1,8 @@
|
||||
name: Diff
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**/*.md'
|
||||
@@ -47,32 +48,6 @@ jobs:
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure -j$THREADS
|
||||
|
||||
- name: Run stress test
|
||||
run: |
|
||||
cd tests/stress
|
||||
./continuous_integration
|
||||
|
||||
- name: Create community DEB package
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
cd build
|
||||
|
||||
# create mgconsole
|
||||
# we use the -B to force the build
|
||||
make -j$THREADS -B mgconsole
|
||||
|
||||
# Create community DEB package.
|
||||
mkdir output && cd output
|
||||
cpack -G DEB --config ../CPackConfig.cmake
|
||||
|
||||
- name: Save community DEB package
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "Community DEB package"
|
||||
path: build/output/memgraph*.deb
|
||||
|
||||
code_analysis:
|
||||
name: "Code analysis"
|
||||
runs-on: [self-hosted, Linux, X64, Diff]
|
||||
@@ -257,6 +232,15 @@ jobs:
|
||||
tests/gql_behave/gql_behave_status.csv
|
||||
tests/gql_behave/gql_behave_status.html
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure -j$THREADS
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
# TODO(gitbuda): Setup mgclient and pymgclient properly.
|
||||
|
||||
48
.github/workflows/package_all.yaml
vendored
48
.github/workflows/package_all.yaml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: centos-7_enterprise
|
||||
name: centos-7
|
||||
path: build/output/centos-7/memgraph*.rpm
|
||||
|
||||
centos-8:
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: centos-8_enterprise
|
||||
name: centos-8
|
||||
path: build/output/centos-8/memgraph*.rpm
|
||||
|
||||
debian-9:
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: debian-9_enterprise
|
||||
name: debian-9
|
||||
path: build/output/debian-9/memgraph*.deb
|
||||
|
||||
debian-10:
|
||||
@@ -70,9 +70,26 @@ jobs:
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: debian-10_enterprise
|
||||
name: debian-10
|
||||
path: build/output/debian-10/memgraph*.deb
|
||||
|
||||
debian-11:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package debian-11
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: debian-11
|
||||
path: build/output/debian-11/memgraph*.deb
|
||||
|
||||
docker:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
timeout-minutes: 60
|
||||
@@ -89,7 +106,7 @@ jobs:
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: docker_enterprise
|
||||
name: docker
|
||||
path: build/output/docker/memgraph*.tar.gz
|
||||
|
||||
ubuntu-1804:
|
||||
@@ -106,7 +123,7 @@ jobs:
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ubuntu-1804_enterprise
|
||||
name: ubuntu-1804
|
||||
path: build/output/ubuntu-18.04/memgraph*.deb
|
||||
|
||||
ubuntu-2004:
|
||||
@@ -123,5 +140,22 @@ jobs:
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ubuntu-2004_enterprise
|
||||
name: ubuntu-2004
|
||||
path: build/output/ubuntu-20.04/memgraph*.deb
|
||||
|
||||
debian-10-platform:
|
||||
runs-on: [self-hosted, DockerMgBuild]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package debian-10 --for-platform
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: debian-10-platform
|
||||
path: build/output/debian-10/memgraph*.deb
|
||||
|
||||
41
.github/workflows/release_centos8.yaml
vendored
41
.github/workflows/release_centos8.yaml
vendored
@@ -36,28 +36,6 @@ jobs:
|
||||
cmake -DCMAKE_BUILD_TYPE=release -DMG_ENTERPRISE=OFF ..
|
||||
make -j$THREADS
|
||||
|
||||
- name: Create community RPM package
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
cd build
|
||||
|
||||
# create mgconsole
|
||||
# we use the -B to force the build
|
||||
make -j$THREADS -B mgconsole
|
||||
|
||||
# Create community RPM package.
|
||||
mkdir output && cd output
|
||||
cpack -G RPM --config ../CPackConfig.cmake
|
||||
rpmlint memgraph*.rpm
|
||||
|
||||
- name: Save community RPM package
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "Community RPM package"
|
||||
path: build/output/memgraph*.rpm
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
@@ -67,16 +45,6 @@ jobs:
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure
|
||||
|
||||
- name: Run stress test (plain)
|
||||
run: |
|
||||
cd tests/stress
|
||||
./continuous_integration
|
||||
|
||||
- name: Run stress test (large)
|
||||
run: |
|
||||
cd tests/stress
|
||||
./continuous_integration --large-dataset
|
||||
|
||||
coverage_build:
|
||||
name: "Coverage build"
|
||||
runs-on: [self-hosted, Linux, X64, CentOS8]
|
||||
@@ -301,6 +269,15 @@ jobs:
|
||||
tests/gql_behave/gql_behave_status.csv
|
||||
tests/gql_behave/gql_behave_status.html
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
# TODO(gitbuda): Setup mgclient and pymgclient properly.
|
||||
|
||||
40
.github/workflows/release_debian10.yaml
vendored
40
.github/workflows/release_debian10.yaml
vendored
@@ -36,27 +36,6 @@ jobs:
|
||||
cmake -DCMAKE_BUILD_TYPE=release -DMG_ENTERPRISE=OFF ..
|
||||
make -j$THREADS
|
||||
|
||||
- name: Create community DEB package
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
cd build
|
||||
|
||||
# create mgconsole
|
||||
# we use the -B to force the build
|
||||
make -j$THREADS -B mgconsole
|
||||
|
||||
# Create community DEB package.
|
||||
mkdir output && cd output
|
||||
cpack -G DEB --config ../CPackConfig.cmake
|
||||
|
||||
- name: Save community DEB package
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "Community DEB package"
|
||||
path: build/output/memgraph*.deb
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
@@ -66,16 +45,6 @@ jobs:
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure
|
||||
|
||||
- name: Run stress test (plain)
|
||||
run: |
|
||||
cd tests/stress
|
||||
./continuous_integration
|
||||
|
||||
- name: Run stress test (large)
|
||||
run: |
|
||||
cd tests/stress
|
||||
./continuous_integration --large-dataset
|
||||
|
||||
coverage_build:
|
||||
name: "Coverage build"
|
||||
runs-on: [self-hosted, Linux, X64, Debian10]
|
||||
@@ -299,6 +268,15 @@ jobs:
|
||||
tests/gql_behave/gql_behave_status.csv
|
||||
tests/gql_behave/gql_behave_status.html
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
# TODO(gitbuda): Setup mgclient and pymgclient properly.
|
||||
|
||||
40
.github/workflows/release_ubuntu2004.yaml
vendored
40
.github/workflows/release_ubuntu2004.yaml
vendored
@@ -36,27 +36,6 @@ jobs:
|
||||
cmake -DCMAKE_BUILD_TYPE=release -DMG_ENTERPRISE=OFF ..
|
||||
make -j$THREADS
|
||||
|
||||
- name: Create community DEB package
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
cd build
|
||||
|
||||
# create mgconsole
|
||||
# we use the -B to force the build
|
||||
make -j$THREADS -B mgconsole
|
||||
|
||||
# Create community DEB package.
|
||||
mkdir output && cd output
|
||||
cpack -G DEB --config ../CPackConfig.cmake
|
||||
|
||||
- name: Save community DEB package
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "Community DEB package"
|
||||
path: build/output/memgraph*.deb
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
@@ -66,16 +45,6 @@ jobs:
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure
|
||||
|
||||
- name: Run stress test (plain)
|
||||
run: |
|
||||
cd tests/stress
|
||||
./continuous_integration
|
||||
|
||||
- name: Run stress test (large)
|
||||
run: |
|
||||
cd tests/stress
|
||||
./continuous_integration --large-dataset
|
||||
|
||||
coverage_build:
|
||||
name: "Coverage build"
|
||||
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
|
||||
@@ -299,6 +268,15 @@ jobs:
|
||||
tests/gql_behave/gql_behave_status.csv
|
||||
tests/gql_behave/gql_behave_status.html
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v3/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
# TODO(gitbuda): Setup mgclient and pymgclient properly.
|
||||
|
||||
@@ -35,9 +35,6 @@ else()
|
||||
message(FATAL_ERROR "Couldn't find clang and/or clang++!")
|
||||
endif()
|
||||
|
||||
option(BUILD_FOR_DOCKER "Build Memgraph binary for docker." OFF)
|
||||
message(STATUS "BUILD_FOR_DOCKER: ${BUILD_FOR_DOCKER}")
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
project(memgraph)
|
||||
@@ -57,7 +54,7 @@ option(MG_ENTERPRISE "Build Memgraph Enterprise Edition" ON)
|
||||
# Set the current version here to override the automatic version detection. The
|
||||
# version must be specified as `X.Y.Z`. Primarily used when building new patch
|
||||
# versions.
|
||||
set(MEMGRAPH_OVERRIDE_VERSION "")
|
||||
set(MEMGRAPH_OVERRIDE_VERSION "2.0.1")
|
||||
|
||||
# Custom suffix that this version should have. The suffix can be any arbitrary
|
||||
# string. Primarily used when building a version for a specific customer.
|
||||
|
||||
127
CODE_OF_CONDUCT.md
Normal file
127
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing other's private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[contact@memgraph.com](contact@memgraph.com). All complaints will be reviewed
|
||||
and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant, version 2.1,
|
||||
available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder][mozilla coc].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq).
|
||||
Translations are available at
|
||||
[https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations).
|
||||
121
CONTRIBUTING.md
Normal file
121
CONTRIBUTING.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# How to contribute?
|
||||
|
||||
This is a general purpose guide for contributing to Memgraph. We're still
|
||||
working out the kinks to make contributing to this project as easy and
|
||||
transparent as possible, but we're not quite there yet. Hopefully, this document
|
||||
makes the process for contributing clear and answers some questions that you may
|
||||
have.
|
||||
|
||||
- [How to contribute?](#how-to-contribute)
|
||||
- [Open development](#open-development)
|
||||
- [Branch organization](#branch-organization)
|
||||
- [Bugs & changes](#bugs--changes)
|
||||
- [Where to find known issues?](#where-to-find-known-issues)
|
||||
- [Proposing a change](#proposing-a-change)
|
||||
- [Your first pull request](#your-first-pull-request)
|
||||
- [Sending a pull request](#sending-a-pull-request)
|
||||
- [Style guide](#style-guide)
|
||||
- [How to get in touch?](#how-to-get-in-touch)
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [License](#license)
|
||||
- [Attribution](#attribution)
|
||||
|
||||
## Open development
|
||||
|
||||
All work on Memgraph is done via [GitHub](https://github.com/memgraph/memgraph).
|
||||
Both core team members and external contributors send pull requests which go
|
||||
through the same review process.
|
||||
|
||||
## Branch organization
|
||||
|
||||
Most pull requests should target the [`master
|
||||
branch`](https://github.com/memgraph/memgraph/tree/master). We only use separate
|
||||
branches for developing new features and fixing bugs before they are merged with
|
||||
`master`. We do our best to keep `master` in good shape, with all tests passing.
|
||||
|
||||
Code that lands in `master` must be compatible with the latest stable release.
|
||||
It may contain additional features but no breaking changes if it's not
|
||||
absolutely necessary. We should be able to release a new minor version from the
|
||||
tip of `master` at any time.
|
||||
|
||||
## Bugs & changes
|
||||
|
||||
### Where to find known issues?
|
||||
|
||||
We are using [GitHub Issues](https://github.com/memgraph/memgraph/issues) for
|
||||
our public bugs. We keep a close eye on this and try to make it clear when we
|
||||
have an internal fix in progress. Before filing a new task, try to make sure
|
||||
your problem doesn't already exist.
|
||||
|
||||
### Proposing a change
|
||||
|
||||
If you intend to change the public API, or make any non-trivial changes to the
|
||||
implementation, we recommend [filing an
|
||||
issue](https://github.com/memgraph/memgraph/issues/new). This lets us reach an
|
||||
agreement on your proposal before you put significant effort into it.
|
||||
|
||||
If you're only fixing a bug, it's fine to submit a pull request right away but
|
||||
we still recommend to file an issue detailing what you're fixing. This is
|
||||
helpful in case we don't accept that specific fix but want to keep track of the
|
||||
issue.
|
||||
|
||||
### Your first pull request
|
||||
|
||||
Working on your first Pull Request? You can learn how from this free video
|
||||
series:
|
||||
|
||||
**[How to Contribute to an Open Source Project on
|
||||
GitHub](https://app.egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)**
|
||||
|
||||
If you decide to fix an issue, please be sure to check the comment thread in
|
||||
case somebody is already working on a fix. If nobody is working on it at the
|
||||
moment, please leave a comment stating that you intend to work on it so other
|
||||
people don't accidentally duplicate your effort.
|
||||
|
||||
If somebody claims an issue but doesn't follow up for more than two weeks, it's
|
||||
fine to take it over but you should still leave a comment.
|
||||
|
||||
### Sending a pull request
|
||||
|
||||
The core team is monitoring for pull requests. We will review your pull request
|
||||
and either merge it, request changes to it, or close it with an explanation.
|
||||
**Before submitting a pull request,** please make sure the following is done:
|
||||
|
||||
1. Fork [the repository](https://github.com/memgraph/memgraph) and create your
|
||||
branch from `master`.
|
||||
2. If you've fixed a bug or added code that should be tested, add tests!
|
||||
3. Use the formatter `clang-format` for C/C++ code and `flake8` for Python code.
|
||||
`clang-format` will automatically detect the `.clang-format` file in the root
|
||||
directory while `flake8` can be used with the default configuration.
|
||||
|
||||
### Style guide
|
||||
|
||||
Memgraph uses the [Google Style
|
||||
Guide](https://google.github.io/styleguide/cppguide.html) for C++ in most of its
|
||||
code. You should follow them whenever writing new code.
|
||||
|
||||
## How to get in touch?
|
||||
|
||||
Aside from communicating directly via Pull Requests and Issues, the Memgraph
|
||||
Community [Discord Server](https://discord.gg/memgraph) is the best place for
|
||||
conversing with project maintainers and other community members.
|
||||
|
||||
## [Code of Conduct](https://github.com/memgraph/memgraph/blob/master/CODE_OF_CONDUCT.md)
|
||||
|
||||
Memgraph has adopted the [Contributor
|
||||
Covenant](https://www.contributor-covenant.org/) as its Code of Conduct, and we
|
||||
expect project participants to adhere to it. Please read [the full
|
||||
text](https://github.com/memgraph/memgraph/blob/master/CODE_OF_CONDUCT.md) so
|
||||
that you can understand what actions will and will not be tolerated.
|
||||
|
||||
## License
|
||||
|
||||
By contributing to Memgraph, you agree that your contributions will be licensed
|
||||
under the [Memgraph licensing
|
||||
scheme](https://github.com/memgraph/memgraph/blob/master/LICENSE).
|
||||
|
||||
## Attribution
|
||||
|
||||
This Contributing guide is adapted from the **React.js Contributing guide**
|
||||
available at
|
||||
[https://reactjs.org/docs/how-to-contribute.html](https://reactjs.org/docs/how-to-contribute.html).
|
||||
135
README.md
135
README.md
@@ -1,21 +1,128 @@
|
||||
# Memgraph
|
||||
<p align="center">
|
||||
<img width="400px" src="https://uploads-ssl.webflow.com/5e7ceb09657a69bdab054b3a/5e7ceb09657a6937ab054bba_Black_Original%20_Logo.png">
|
||||
</p>
|
||||
|
||||
Memgraph is an ACID compliant high performance transactional distributed
|
||||
in-memory graph database featuring runtime native query compiling, lock free
|
||||
data structures, multi-version concurrency control and asynchronous IO.
|
||||
---
|
||||
|
||||
## Development Documentation
|
||||
<p align="center">
|
||||
Build modern, graph-based applications on top of your streaming data in minutes.
|
||||
</p>
|
||||
|
||||
Please continue in
|
||||
[Notion](https://www.notion.so/memgraph/memgraph-0428591638604c8385550e214ea9f3e6).
|
||||
<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"/>
|
||||
</a>
|
||||
<a href="https://github.com/memgraph/memgraph/blob/master/licenses/BSL.txt">
|
||||
<img src="https://img.shields.io/badge/license-BSL-yellowgreen" alt="license" title="license"/>
|
||||
</a>
|
||||
<a href="https://github.com/memgraph/memgraph/blob/master/licenses/MEL.txt" alt="Documentation">
|
||||
<img src="https://img.shields.io/badge/license-MEL-yellow" alt="license" title="license"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## User Documentation
|
||||
<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"/>
|
||||
</a>
|
||||
<a href="https://memgraph.com/docs/" alt="Documentation">
|
||||
<img src="https://img.shields.io/badge/documentation-Memgraph-orange" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Memgraph user documentation is maintained within
|
||||
[docs](https://github.com/memgraph/docs) repository. The documentation is also
|
||||
available on [GitBook](https://docs.memgraph.com).
|
||||
<p align="center">
|
||||
<a href="https://memgr.ph/join-discord">
|
||||
<img src="https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white" alt="Discord"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Licences
|
||||
## :clipboard: Description
|
||||
|
||||
* [Community](release/LICENSE_COMMUNITY.md)
|
||||
* [Enterprise](release/LICENSE_ENTERPRISE.md)
|
||||
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 directly connects to your streaming infrastructure. You can ingest data
|
||||
from sources like Kafka, SQL, or plain CSV files. Memgraph provides a standard
|
||||
interface to query your data with Cypher, a widely-used and declarative query
|
||||
language that is easy to write, understand and optimize for performance. This is
|
||||
achieved by using the property graph data model, which stores data in terms of
|
||||
objects, their attributes, and the relationships that connect them. This is a
|
||||
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.
|
||||
|
||||
## :floppy_disk: Download & Install
|
||||
|
||||
### Windows
|
||||
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-on-windows-docker)
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-on-windows-wsl)
|
||||
|
||||
### macOS
|
||||
|
||||
[](https://memgraph.com/docs/memgraph/install-memgraph-on-macos-docker)
|
||||
|
||||
### 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)
|
||||
|
||||
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
|
||||
|
||||
## :bookmark_tabs: Documentation
|
||||
|
||||
The Memgraph documentation is available at
|
||||
[memgraph.com/docs](https://memgraph.com/docs).
|
||||
|
||||
## :question: Configuration
|
||||
|
||||
Command line options that Memgraph accepts are available in the [reference
|
||||
guide](https://memgraph.com/docs/memgraph/reference-guide/configuration).
|
||||
|
||||
## :trophy: Contributing
|
||||
|
||||
The main purpose of this repository is to continue evolving Memgraph, making it
|
||||
faster and easier to use. Development of Memgraph happens in the open on GitHub,
|
||||
and we are grateful to the community for contributing bug fixes and
|
||||
improvements. Read below to learn how you can take part in improving Memgraph.
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
Memgraph has adopted a Code of Conduct that we expect project participants to
|
||||
adhere to. Please read [the full text](CODE_OF_CONDUCT.md) so that you can
|
||||
understand what actions will and will not be tolerated.
|
||||
|
||||
### Contributing Guide
|
||||
|
||||
Read our [contributing guide](CONTRIBUTING.md) to learn about our development
|
||||
process and how to propose bug fixes and improvements.
|
||||
|
||||
### :scroll: License
|
||||
|
||||
Memgraph Community is available under the [BSL
|
||||
license](./licenses/BSL.txt).</br> Memgraph Enterprise is available under the
|
||||
[MEL license](./licenses/MEL.txt).
|
||||
|
||||
<p align="center">
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/⬆️back_to_top_⬆️-white" alt="Back to top" title="Back to top"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
95
environment/os/debian-11.sh
Executable file
95
environment/os/debian-11.sh
Executable file
@@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg # used for archive signature verification
|
||||
tar gzip bzip2 xz-utils unzip # used for archive unpacking
|
||||
zlib1g-dev # zlib library used for all builds
|
||||
libexpat1-dev libipt-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
|
||||
libcurl4-openssl-dev # for cmake
|
||||
libreadline-dev # for cmake and llvm
|
||||
libffi-dev libxml2-dev # for llvm
|
||||
libedit-dev libpcre3-dev automake bison # for swig
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz-utils # used for archive unpacking
|
||||
zlib1g # zlib library used for all builds
|
||||
libexpat1 libipt2 libbabeltrace1 liblzma5 python3 # for gdb
|
||||
libcurl4 # for cmake
|
||||
libreadline8 # for cmake and llvm
|
||||
libffi7 libxml2 # for llvm
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make pkg-config # build system
|
||||
curl wget # for downloading libs
|
||||
uuid-dev default-jre-headless # required by antlr
|
||||
libreadline-dev # for memgraph console
|
||||
libpython3-dev python3-dev # for query modules
|
||||
libssl-dev
|
||||
libseccomp-dev
|
||||
netcat # tests are using nc to wait for memgraph
|
||||
python3 virtualenv 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-3.1 golang nodejs npm
|
||||
autoconf # for jemalloc code generation
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
check_all_dpkg "$1"
|
||||
}
|
||||
|
||||
install() {
|
||||
cat >/etc/apt/sources.list <<EOF
|
||||
deb http://deb.debian.org/debian bullseye main
|
||||
deb-src http://deb.debian.org/debian bullseye main
|
||||
|
||||
deb http://deb.debian.org/debian-security/ bullseye-security main
|
||||
deb-src http://deb.debian.org/debian-security/ bullseye-security main
|
||||
|
||||
deb http://deb.debian.org/debian bullseye-updates main
|
||||
deb-src http://deb.debian.org/debian bullseye-updates main
|
||||
EOF
|
||||
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-3.1 ]; then
|
||||
if ! dpkg -s "$pkg" 2>/dev/null >/dev/null; then
|
||||
wget -nv https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
|
||||
dpkg -i packages-microsoft-prod.deb
|
||||
apt-get update
|
||||
apt-get install -y apt-transport-https dotnet-sdk-3.1
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
apt install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
@@ -1,7 +1,8 @@
|
||||
# Copyright 2021 Memgraph Ltd.
|
||||
#
|
||||
# Use of this software is governed by the Business Source License
|
||||
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source// License, and you may not use this file except in compliance with the Business Source License.
|
||||
# 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
|
||||
@@ -1117,7 +1118,7 @@ def raise_if_does_not_meet_requirements(func: typing.Callable[..., Record]):
|
||||
|
||||
|
||||
def _register_proc(func: typing.Callable[..., Record],
|
||||
is_write: bool):
|
||||
is_write: bool, is_timed: bool):
|
||||
raise_if_does_not_meet_requirements(func)
|
||||
register_func = (
|
||||
_mgp.Module.add_write_procedure if is_write
|
||||
@@ -1127,13 +1128,23 @@ def _register_proc(func: typing.Callable[..., Record],
|
||||
if params and params[0].annotation is ProcCtx:
|
||||
@wraps(func)
|
||||
def wrapper(graph, args):
|
||||
return func(ProcCtx(graph), *args)
|
||||
start = datetime.datetime.now()
|
||||
result = func(ProcCtx(graph), *args)
|
||||
if is_timed:
|
||||
delta = datetime.datetime.now() - start
|
||||
result.fields["duration"] = delta.total_seconds() * 10**6 + delta.microseconds % 10**6
|
||||
return result
|
||||
params = params[1:]
|
||||
mgp_proc = register_func(_mgp._MODULE, wrapper)
|
||||
else:
|
||||
@wraps(func)
|
||||
def wrapper(graph, args):
|
||||
return func(*args)
|
||||
start = datetime.datetime.now()
|
||||
result = func(*args)
|
||||
if is_timed:
|
||||
delta = datetime.datetime.now() - start
|
||||
result.fields["duration"] = delta.total_seconds() * 10**6 + delta.microseconds % 10**6
|
||||
return result
|
||||
mgp_proc = register_func(_mgp._MODULE, wrapper)
|
||||
for param in params:
|
||||
name = param.name
|
||||
@@ -1156,10 +1167,17 @@ def _register_proc(func: typing.Callable[..., Record],
|
||||
mgp_proc.add_deprecated_result(name, cypher_type)
|
||||
else:
|
||||
mgp_proc.add_result(name, _typing_to_cypher_type(type_))
|
||||
if is_timed:
|
||||
mgp_proc.add_result("duration", _typing_to_cypher_type(int))
|
||||
return func
|
||||
|
||||
|
||||
def read_proc(func: typing.Callable[..., Record]):
|
||||
_read_proc(func, False)
|
||||
|
||||
def timed_read_proc(func: typing.Callable[..., Record]):
|
||||
_read_proc(func, True)
|
||||
|
||||
def _read_proc(func: typing.Callable[..., Record], is_timed: bool):
|
||||
"""
|
||||
Register `func` as a read-only procedure of the current module.
|
||||
|
||||
@@ -1199,7 +1217,7 @@ def read_proc(func: typing.Callable[..., Record]):
|
||||
CALL example.procedure(1) YIELD args, result;
|
||||
Naturally, you may pass in different arguments or yield less fields.
|
||||
"""
|
||||
return _register_proc(func, False)
|
||||
return _register_proc(func, False, is_timed)
|
||||
|
||||
|
||||
def write_proc(func: typing.Callable[..., Record]):
|
||||
@@ -1249,7 +1267,7 @@ def write_proc(func: typing.Callable[..., Record]):
|
||||
CALL example.procedure("single argument") YIELD result;
|
||||
Naturally, you may pass in different arguments.
|
||||
"""
|
||||
return _register_proc(func, True)
|
||||
return _register_proc(func, True, False)
|
||||
|
||||
|
||||
class InvalidMessageError(Exception):
|
||||
|
||||
@@ -221,7 +221,7 @@ pymgclient_tag="4f85c179e56302d46a1e3e2cf43509db65f062b3" # (2021-01-15)
|
||||
repo_clone_try_double "${primary_urls[pymgclient]}" "${secondary_urls[pymgclient]}" "pymgclient" "$pymgclient_tag"
|
||||
|
||||
# mgconsole
|
||||
mgconsole_tag="01ae99bfce772e540e75c076ba03cf06c0c2ac7d" # (2021-05-26)
|
||||
mgconsole_tag="v1.1.0" # (2021-10-07)
|
||||
repo_clone_try_double "${primary_urls[mgconsole]}" "${secondary_urls[mgconsole]}" "mgconsole" "$mgconsole_tag"
|
||||
|
||||
spdlog_tag="46d418164dd4cd9822cf8ca62a116a3f71569241" # (2020-12-01)
|
||||
|
||||
@@ -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: 2025-10-05
|
||||
CHANGE DATE: 2025-10-12
|
||||
CHANGE LICENSE: Apache License, Version 2.0
|
||||
|
||||
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.
|
||||
|
||||
@@ -56,7 +56,10 @@ def procedure(context: mgp.ProcCtx,
|
||||
|
||||
|
||||
@mgp.write_proc
|
||||
def write_procedure(context: mgp.ProcCtx, property_name: str, property_value: mgp.Nullable[mgp.Any]) -> mgp.Record(created_vertex=mgp.Vertex):
|
||||
def write_procedure(context: mgp.ProcCtx,
|
||||
property_name: str,
|
||||
property_value: mgp.Nullable[mgp.Any]
|
||||
) -> mgp.Record(created_vertex=mgp.Vertex):
|
||||
"""
|
||||
This example procedure creates a new vertex with the specified property
|
||||
and connects it to all existing vertex which has the same property with
|
||||
@@ -86,7 +89,6 @@ def write_procedure(context: mgp.ProcCtx, property_name: str, property_value: mg
|
||||
vertex.properties.set(property_name, property_value)
|
||||
# Connect the new vertex to the other vertices
|
||||
for v in vertices_to_connect:
|
||||
print("ALMA")
|
||||
context.graph.create_edge(vertex, v, mgp.EdgeType("HAS_SAME_VALUE"))
|
||||
|
||||
return mgp.Record(created_vertex=vertex)
|
||||
|
||||
@@ -35,8 +35,7 @@ cd ${working_dir}
|
||||
# Extract version and offering from deb name.
|
||||
deb_name=`echo $(basename "$deb_path") | sed 's/.deb$//'`
|
||||
version=`echo ${deb_name} | cut -d '_' -f 2 | rev | cut -d '-' -f 2- | rev | tr '+~' '__'`
|
||||
offering=`echo ${version} | cut -d '-' -f 2`
|
||||
dockerfile_path="${working_dir}/memgraph_${offering}.dockerfile"
|
||||
dockerfile_path="${working_dir}/memgraph.dockerfile"
|
||||
image_name="memgraph:${version}"
|
||||
package_name="memgraph-${version}-docker.tar.gz"
|
||||
|
||||
|
||||
17
release/package/debian-11/Dockerfile
Normal file
17
release/package/debian-11/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM debian:11
|
||||
|
||||
ARG TOOLCHAIN_VERSION
|
||||
|
||||
# Stops tzdata interactive configuration.
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt update && apt install -y \
|
||||
ca-certificates wget git
|
||||
# Do NOT be smart here and clean the cache because the container is used in the
|
||||
# stateful context.
|
||||
|
||||
RUN wget -q https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/${TOOLCHAIN_VERSION}/${TOOLCHAIN_VERSION}-binaries-debian-11.tar.gz \
|
||||
-O ${TOOLCHAIN_VERSION}-binaries-debian-11.tar.gz \
|
||||
&& tar xzvf ${TOOLCHAIN_VERSION}-binaries-debian-11.tar.gz -C /opt
|
||||
|
||||
ENTRYPOINT ["sleep", "infinity"]
|
||||
@@ -16,6 +16,10 @@ services:
|
||||
build:
|
||||
context: debian-10
|
||||
container_name: "mgbuild_debian-10"
|
||||
mgbuild_debian-11:
|
||||
build:
|
||||
context: debian-11
|
||||
container_name: "mgbuild_debian-11"
|
||||
mgbuild_ubuntu-18.04:
|
||||
build:
|
||||
context: ubuntu-18.04
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
SUPPORTED_OS=(centos-7 centos-8 debian-9 debian-10 ubuntu-18.04 ubuntu-20.04)
|
||||
SUPPORTED_OS=(centos-7 centos-8 debian-9 debian-10 debian-11 ubuntu-18.04 ubuntu-20.04)
|
||||
PROJECT_ROOT="$SCRIPT_DIR/../.."
|
||||
TOOLCHAIN_VERSION="toolchain-v3"
|
||||
ACTIVATE_TOOLCHAIN="source /opt/${TOOLCHAIN_VERSION}/activate"
|
||||
HOST_OUTPUT_DIR="$PROJECT_ROOT/build/output"
|
||||
|
||||
print_help () {
|
||||
echo "$0 init|package {os} [--for-docker]|docker|test"
|
||||
echo "$0 init|package {os} [--for-docker|--for-platform]|docker|test"
|
||||
echo ""
|
||||
echo " OSs: ${SUPPORTED_OS[*]}"
|
||||
exit 1
|
||||
@@ -28,10 +28,15 @@ make_package () {
|
||||
if [[ "$os" =~ ^"ubuntu".* ]]; then
|
||||
package_command=" cpack -G DEB --config ../CPackConfig.cmake "
|
||||
fi
|
||||
docker_flag=" -DBUILD_FOR_DOCKER=OFF "
|
||||
if [[ "$#" -gt 2 ]]; then
|
||||
if [[ "$3" == "--for-docker" ]]; then
|
||||
docker_flag=" -DBUILD_FOR_DOCKER=ON "
|
||||
telemetry_id_override_flag=""
|
||||
if [[ "$#" -gt 1 ]]; then
|
||||
if [[ "$2" == "--for-docker" ]]; then
|
||||
telemetry_id_override_flag=" -DMG_TELEMETRY_ID_OVERRIDE=DOCKER "
|
||||
elif [[ "$2" == "--for-platform" ]]; then
|
||||
telemetry_id_override_flag=" -DMG_TELEMETRY_ID_OVERRIDE=DOCKER-PLATFORM"
|
||||
else
|
||||
print_help
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
build_container="mgbuild_$os"
|
||||
@@ -64,7 +69,7 @@ make_package () {
|
||||
echo "Building targeted package..."
|
||||
docker exec "$build_container" bash -c "cd /memgraph && ./init"
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && rm -rf ./*"
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && cmake -DCMAKE_BUILD_TYPE=release $docker_flag .."
|
||||
docker exec "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && cmake -DCMAKE_BUILD_TYPE=release $telemetry_id_override_flag .."
|
||||
# ' is used instead of " because we need to run make within the allowed
|
||||
# container resources.
|
||||
# shellcheck disable=SC2016
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "utils/flag_validation.hpp"
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/settings.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
@@ -102,17 +103,15 @@ std::optional<User> Auth::Authenticate(const std::string &username, const std::s
|
||||
if (FLAGS_auth_module_create_missing_user) {
|
||||
user = AddUser(username, password);
|
||||
if (!user) {
|
||||
spdlog::warn(
|
||||
"Couldn't authenticate user '{}' using the auth module because "
|
||||
"the user already exists as a role!",
|
||||
username);
|
||||
spdlog::warn(utils::MessageWithLink(
|
||||
"Couldn't create the missing user '{}' using the auth module because the user already exists as a role.",
|
||||
username, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
spdlog::warn(
|
||||
"Couldn't authenticate user '{}' using the auth module because the "
|
||||
"user doesn't exist!",
|
||||
username);
|
||||
spdlog::warn(utils::MessageWithLink(
|
||||
"Couldn't authenticate user '{}' using the auth module because the user doesn't exist.", username,
|
||||
"https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
@@ -126,17 +125,16 @@ std::optional<User> Auth::Authenticate(const std::string &username, const std::s
|
||||
role = AddRole(rolename);
|
||||
if (!role) {
|
||||
spdlog::warn(
|
||||
"Couldn't authenticate user '{}' using the auth module "
|
||||
"because the user's role '{}' already exists as a user!",
|
||||
username, rolename);
|
||||
utils::MessageWithLink("Couldn't authenticate user '{}' using the auth module because the user's "
|
||||
"role '{}' already exists as a user.",
|
||||
username, rolename, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
SaveRole(*role);
|
||||
} else {
|
||||
spdlog::warn(
|
||||
"Couldn't authenticate user '{}' using the auth module because "
|
||||
"the user's role '{}' doesn't exist!",
|
||||
username, rolename);
|
||||
spdlog::warn(utils::MessageWithLink(
|
||||
"Couldn't authenticate user '{}' using the auth module because the user's role '{}' doesn't exist.",
|
||||
username, rolename, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -150,11 +148,13 @@ std::optional<User> Auth::Authenticate(const std::string &username, const std::s
|
||||
} else {
|
||||
auto user = GetUser(username);
|
||||
if (!user) {
|
||||
spdlog::warn("Couldn't authenticate user '{}' because the user doesn't exist", username);
|
||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the user doesn't exist.", username,
|
||||
"https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!user->CheckPassword(password)) {
|
||||
spdlog::warn("Couldn't authenticate user '{}'", username);
|
||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the password is not correct.",
|
||||
username, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
return user;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "communication/exceptions.hpp"
|
||||
#include "utils/likely.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
|
||||
namespace communication::bolt {
|
||||
|
||||
@@ -64,7 +65,8 @@ inline std::pair<std::string, std::string> ExceptionToErrorMessage(const std::ex
|
||||
// All exceptions used in memgraph are derived from BasicException. Since
|
||||
// we caught some other exception we don't know what is going on. Return
|
||||
// DatabaseError, log real message and return generic string.
|
||||
spdlog::error("Unknown exception occurred during query execution {}", e.what());
|
||||
spdlog::error(
|
||||
utils::MessageWithLink("Unknown exception occurred during query execution {}.", e.what(), "https://memgr.ph/unknown"));
|
||||
return {"Memgraph.DatabaseError.MemgraphError.MemgraphError",
|
||||
"An unknown exception occurred, this is unexpected. Real message "
|
||||
"should be in database logs."};
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "communication/listener.hpp"
|
||||
#include "io/network/socket.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/thread.hpp"
|
||||
|
||||
namespace communication {
|
||||
@@ -88,13 +89,13 @@ class Server final {
|
||||
alive_.store(true);
|
||||
|
||||
if (!socket_.Bind(endpoint_)) {
|
||||
spdlog::error("Cannot bind to socket on {}", endpoint_);
|
||||
spdlog::error(utils::MessageWithLink("Cannot bind to socket on endpoint {}.", endpoint_, "https://memgr.ph/socket"));
|
||||
alive_.store(false);
|
||||
return false;
|
||||
}
|
||||
socket_.SetTimeout(1, 0);
|
||||
if (!socket_.Listen(1024)) {
|
||||
spdlog::error("Cannot listen on socket {}", endpoint_);
|
||||
spdlog::error(utils::MessageWithLink("Cannot listen on socket {}", endpoint_, "https://memgr.ph/socket"));
|
||||
alive_.store(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "io/network/socket.hpp"
|
||||
#include "io/network/stream_buffer.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/on_scope_exit.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
|
||||
@@ -190,10 +191,10 @@ class Session final {
|
||||
throw utils::BasicException(SslGetLastError());
|
||||
} else {
|
||||
// This is a fatal error.
|
||||
spdlog::error(
|
||||
"An unknown error occured while processing SSL message."
|
||||
" Please make sure that you have SSL properly configured on "
|
||||
"the server and the client.");
|
||||
spdlog::error(utils::MessageWithLink(
|
||||
"An unknown error occurred while processing SSL messages. "
|
||||
"Please make sure that you have SSL properly configured on the server and the client.",
|
||||
"https://memgr.ph/ssl"));
|
||||
throw utils::BasicException(SslGetLastError());
|
||||
}
|
||||
} else if (len == 0) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "io/network/endpoint.hpp"
|
||||
#include "io/network/network_error.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace io::network {
|
||||
@@ -65,15 +66,17 @@ std::optional<std::pair<std::string, uint16_t>> Endpoint::ParseSocketOrIpAddress
|
||||
try {
|
||||
int_port = utils::ParseInt(parts[1]);
|
||||
} catch (utils::BasicException &e) {
|
||||
spdlog::error("Invalid port number: {}", parts[1]);
|
||||
spdlog::error(utils::MessageWithLink("Invalid port number {}.", parts[1], "https://memgr.ph/ports"));
|
||||
return std::nullopt;
|
||||
}
|
||||
if (int_port < 0) {
|
||||
spdlog::error("Port number must be a positive integer!");
|
||||
spdlog::error(utils::MessageWithLink("Invalid port number {}. The port number must be a positive integer.",
|
||||
int_port, "https://memgr.ph/ports"));
|
||||
return std::nullopt;
|
||||
}
|
||||
if (int_port > std::numeric_limits<uint16_t>::max()) {
|
||||
spdlog::error("Port number exceeded maximum possible size!");
|
||||
spdlog::error(utils::MessageWithLink("Invalid port number. The port number exceedes the maximum possible size.",
|
||||
"https://memgr.ph/ports"));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "utils/license.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/readable_size.hpp"
|
||||
#include "utils/rw_lock.hpp"
|
||||
#include "utils/settings.hpp"
|
||||
@@ -998,13 +999,16 @@ int main(int argc, char **argv) {
|
||||
auto gil = py::EnsureGIL();
|
||||
auto maybe_exc = py::AppendToSysPath(py_support_dir.c_str());
|
||||
if (maybe_exc) {
|
||||
spdlog::error("Unable to load support for embedded Python: {}", *maybe_exc);
|
||||
spdlog::error(
|
||||
utils::MessageWithLink("Unable to load support for embedded Python: {}.", *maybe_exc, "https://memgr.ph/python"));
|
||||
}
|
||||
} else {
|
||||
spdlog::error("Unable to load support for embedded Python: missing directory {}", py_support_dir);
|
||||
spdlog::error(utils::MessageWithLink("Unable to load support for embedded Python: missing directory {}.",
|
||||
py_support_dir, "https://memgr.ph/python"));
|
||||
}
|
||||
} catch (const std::filesystem::filesystem_error &e) {
|
||||
spdlog::error("Unable to load support for embedded Python: {}", e.what());
|
||||
spdlog::error(
|
||||
utils::MessageWithLink("Unable to load support for embedded Python: {}.", e.what(), "https://memgr.ph/python"));
|
||||
}
|
||||
|
||||
// Initialize the communication library.
|
||||
@@ -1021,7 +1025,8 @@ int main(int argc, char **argv) {
|
||||
mem_log_scheduler.Run("Memory warning", std::chrono::seconds(3), [] {
|
||||
auto free_ram = utils::sysinfo::AvailableMemory();
|
||||
if (free_ram && *free_ram / 1024 < FLAGS_memory_warning_threshold)
|
||||
spdlog::warn("Running out of available RAM, only {} MB left", *free_ram / 1024);
|
||||
spdlog::warn(utils::MessageWithLink("Running out of available RAM, only {} MB left.", *free_ram / 1024,
|
||||
"https://memgr.ph/ram"));
|
||||
});
|
||||
} else {
|
||||
// Kernel version for the `MemAvailable` value is from: man procfs
|
||||
@@ -1033,6 +1038,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
std::cout << "You are running Memgraph v" << gflags::VersionString() << std::endl;
|
||||
std::cout << "To get started with Memgraph, visit https://memgr.ph/start" << std::endl;
|
||||
|
||||
auto data_directory = std::filesystem::path(FLAGS_data_directory);
|
||||
|
||||
@@ -1152,7 +1158,7 @@ int main(int argc, char **argv) {
|
||||
service_name = "BoltS";
|
||||
spdlog::info("Using secure Bolt connection (with SSL)");
|
||||
} else {
|
||||
spdlog::warn("Using non-secure Bolt connection (without SSL)");
|
||||
spdlog::warn(utils::MessageWithLink("Using non-secure Bolt connection (without SSL).", "https://memgr.ph/ssl"));
|
||||
}
|
||||
|
||||
ServerT server({FLAGS_bolt_address, static_cast<uint16_t>(FLAGS_bolt_port)}, &session_data, &context,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
#include "version.hpp"
|
||||
@@ -433,7 +434,7 @@ void ProcessNodeRow(storage::Storage *store, const std::vector<Field> &fields, c
|
||||
auto it = node_id_map->find(node_id);
|
||||
if (it != node_id_map->end()) {
|
||||
if (FLAGS_skip_duplicate_nodes) {
|
||||
spdlog::warn("Skipping duplicate node with ID '{}'", node_id);
|
||||
spdlog::warn(utils::MessageWithLink("Skipping duplicate node with ID '{}'.", node_id, "https://memgr.ph/csv"));
|
||||
return;
|
||||
} else {
|
||||
throw LoadException("Node with ID '{}' already exists", node_id);
|
||||
@@ -524,7 +525,8 @@ void ProcessRelationshipsRow(storage::Storage *store, const std::vector<Field> &
|
||||
auto it = node_id_map.find(node_id);
|
||||
if (it == node_id_map.end()) {
|
||||
if (FLAGS_skip_bad_relationships) {
|
||||
spdlog::warn("Skipping bad relationship with START_ID '{}'", node_id);
|
||||
spdlog::warn(
|
||||
utils::MessageWithLink("Skipping bad relationship with START_ID '{}'.", node_id, "https://memgr.ph/csv"));
|
||||
return;
|
||||
} else {
|
||||
throw LoadException("Node with ID '{}' does not exist", node_id);
|
||||
@@ -541,7 +543,7 @@ void ProcessRelationshipsRow(storage::Storage *store, const std::vector<Field> &
|
||||
auto it = node_id_map.find(node_id);
|
||||
if (it == node_id_map.end()) {
|
||||
if (FLAGS_skip_bad_relationships) {
|
||||
spdlog::warn("Skipping bad relationship with END_ID '{}'", node_id);
|
||||
spdlog::warn(utils::MessageWithLink("Skipping bad relationship with END_ID '{}'.", node_id, "https://memgr.ph/csv"));
|
||||
return;
|
||||
} else {
|
||||
throw LoadException("Node with ID '{}' does not exist", node_id);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
;; Copyright 2021 Memgraph Ltd.
|
||||
;;
|
||||
;; Use of this software is governed by the Business Source License
|
||||
;; included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source License, and you may not use this file except in compliance with the Business Source License.
|
||||
;; 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
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
* Copyright 2021 Memgraph Ltd.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License
|
||||
* included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source License, and you may not use this file except in compliance with the Business Source License.
|
||||
* 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
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
* Copyright 2021 Memgraph Ltd.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License
|
||||
* included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source License, and you may not use this file except in compliance with the Business Source License.
|
||||
* 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
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
;; Copyright 2021 Memgraph Ltd.
|
||||
;;
|
||||
;; Use of this software is governed by the Business Source License
|
||||
;; included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source License, and you may not use this file except in compliance with the Business Source License.
|
||||
;; 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
|
||||
|
||||
@@ -1908,13 +1908,20 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
throw QueryRuntimeException("Unexpected error when deleting a node.");
|
||||
}
|
||||
}
|
||||
if (context.trigger_context_collector &&
|
||||
context.trigger_context_collector->ShouldRegisterDeletedObject<EdgeAccessor>() && res.GetValue()) {
|
||||
context.trigger_context_collector->RegisterDeletedObject(res.GetValue()->first);
|
||||
for (const auto &deleted_edge : res.GetValue()->second) {
|
||||
context.trigger_context_collector->RegisterDeletedObject(deleted_edge);
|
||||
|
||||
std::invoke([&] {
|
||||
if (!context.trigger_context_collector || !*res) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
context.trigger_context_collector->RegisterDeletedObject((*res)->first);
|
||||
if (!context.trigger_context_collector->ShouldRegisterDeletedObject<query::EdgeAccessor>()) {
|
||||
return;
|
||||
}
|
||||
for (const auto &edge : (*res)->second) {
|
||||
context.trigger_context_collector->RegisterDeletedObject(edge);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
auto res = dba.RemoveVertex(&va);
|
||||
if (res.HasError()) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
;; Copyright 2021 Memgraph Ltd.
|
||||
;;
|
||||
;; Use of this software is governed by the Business Source License
|
||||
;; included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source License, and you may not use this file except in compliance with the Business Source License.
|
||||
;; 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
|
||||
|
||||
@@ -1565,9 +1565,8 @@ mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_nam
|
||||
if (!MgpVertexIsMutable(*v)) {
|
||||
throw ImmutableObjectException{"Cannot set a property on an immutable vertex!"};
|
||||
}
|
||||
const auto result =
|
||||
v->impl.SetProperty(v->graph->impl->NameToProperty(property_name), ToPropertyValue(*property_value));
|
||||
|
||||
const auto prop_key = v->graph->impl->NameToProperty(property_name);
|
||||
const auto result = v->impl.SetProperty(prop_key, ToPropertyValue(*property_value));
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
@@ -1581,6 +1580,18 @@ mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_nam
|
||||
throw SerializationException{"Cannot serialize setting a property of a vertex."};
|
||||
}
|
||||
}
|
||||
|
||||
auto *trigger_ctx_collector = v->graph->ctx->trigger_context_collector;
|
||||
if (!trigger_ctx_collector || !trigger_ctx_collector->ShouldRegisterObjectPropertyChange<query::VertexAccessor>()) {
|
||||
return;
|
||||
}
|
||||
const auto old_value = query::TypedValue(*result);
|
||||
if (property_value->type == mgp_value_type::MGP_VALUE_TYPE_NULL) {
|
||||
trigger_ctx_collector->RegisterRemovedObjectProperty(v->impl, prop_key, old_value);
|
||||
return;
|
||||
}
|
||||
const auto new_value = ToTypedValue(*property_value, property_value->memory);
|
||||
trigger_ctx_collector->RegisterSetObjectProperty(v->impl, prop_key, old_value, new_value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1589,7 +1600,8 @@ mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) {
|
||||
if (!MgpVertexIsMutable(*v)) {
|
||||
throw ImmutableObjectException{"Cannot add a label to an immutable vertex!"};
|
||||
}
|
||||
const auto result = v->impl.AddLabel(v->graph->impl->NameToLabel(label.name));
|
||||
const auto label_id = v->graph->impl->NameToLabel(label.name);
|
||||
const auto result = v->impl.AddLabel(label_id);
|
||||
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
@@ -1604,6 +1616,10 @@ mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) {
|
||||
throw SerializationException{"Cannot serialize adding a label to a vertex."};
|
||||
}
|
||||
}
|
||||
|
||||
if (v->graph->ctx->trigger_context_collector) {
|
||||
v->graph->ctx->trigger_context_collector->RegisterSetVertexLabel(v->impl, label_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1612,7 +1628,8 @@ mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, mgp_label label) {
|
||||
if (!MgpVertexIsMutable(*v)) {
|
||||
throw ImmutableObjectException{"Cannot remove a label from an immutable vertex!"};
|
||||
}
|
||||
const auto result = v->impl.RemoveLabel(v->graph->impl->NameToLabel(label.name));
|
||||
const auto label_id = v->graph->impl->NameToLabel(label.name);
|
||||
const auto result = v->impl.RemoveLabel(label_id);
|
||||
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
@@ -1627,6 +1644,9 @@ mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, mgp_label label) {
|
||||
throw SerializationException{"Cannot serialize removing a label from a vertex."};
|
||||
}
|
||||
}
|
||||
if (v->graph->ctx->trigger_context_collector) {
|
||||
v->graph->ctx->trigger_context_collector->RegisterRemovedVertexLabel(v->impl, label_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1954,8 +1974,8 @@ mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, m
|
||||
if (!MgpEdgeIsMutable(*e)) {
|
||||
throw ImmutableObjectException{"Cannot set a property on an immutable edge!"};
|
||||
}
|
||||
const auto result =
|
||||
e->impl.SetProperty(e->from.graph->impl->NameToProperty(property_name), ToPropertyValue(*property_value));
|
||||
const auto prop_key = e->from.graph->impl->NameToProperty(property_name);
|
||||
const auto result = e->impl.SetProperty(prop_key, ToPropertyValue(*property_value));
|
||||
|
||||
if (result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
@@ -1971,6 +1991,18 @@ mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, m
|
||||
throw SerializationException{"Cannot serialize setting a property of an edge."};
|
||||
}
|
||||
}
|
||||
|
||||
auto *trigger_ctx_collector = e->from.graph->ctx->trigger_context_collector;
|
||||
if (!trigger_ctx_collector || !trigger_ctx_collector->ShouldRegisterObjectPropertyChange<query::EdgeAccessor>()) {
|
||||
return;
|
||||
}
|
||||
const auto old_value = query::TypedValue(*result);
|
||||
if (property_value->type == mgp_value_type::MGP_VALUE_TYPE_NULL) {
|
||||
e->from.graph->ctx->trigger_context_collector->RegisterRemovedObjectProperty(e->impl, prop_key, old_value);
|
||||
return;
|
||||
}
|
||||
const auto new_value = ToTypedValue(*property_value, property_value->memory);
|
||||
e->from.graph->ctx->trigger_context_collector->RegisterSetObjectProperty(e->impl, prop_key, old_value, new_value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2024,8 +2056,10 @@ mgp_error mgp_graph_create_vertex(struct mgp_graph *graph, mgp_memory *memory, m
|
||||
if (!MgpGraphIsMutable(*graph)) {
|
||||
throw ImmutableObjectException{"Cannot create a vertex in an immutable graph!"};
|
||||
}
|
||||
|
||||
auto vertex = graph->impl->InsertVertex();
|
||||
if (graph->ctx->trigger_context_collector) {
|
||||
graph->ctx->trigger_context_collector->RegisterCreatedObject(vertex);
|
||||
}
|
||||
return NewRawMgpObject<mgp_vertex>(memory, vertex, graph);
|
||||
},
|
||||
result);
|
||||
@@ -2051,6 +2085,9 @@ mgp_error mgp_graph_delete_vertex(struct mgp_graph *graph, mgp_vertex *vertex) {
|
||||
throw SerializationException{"Cannot serialize removing a vertex."};
|
||||
}
|
||||
}
|
||||
if (graph->ctx->trigger_context_collector && *result) {
|
||||
graph->ctx->trigger_context_collector->RegisterDeletedObject(**result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2073,6 +2110,18 @@ mgp_error mgp_graph_detach_delete_vertex(struct mgp_graph *graph, mgp_vertex *ve
|
||||
throw SerializationException{"Cannot serialize removing a vertex."};
|
||||
}
|
||||
}
|
||||
|
||||
auto *trigger_ctx_collector = graph->ctx->trigger_context_collector;
|
||||
if (!trigger_ctx_collector || !*result) {
|
||||
return;
|
||||
}
|
||||
trigger_ctx_collector->RegisterDeletedObject((*result)->first);
|
||||
if (!trigger_ctx_collector->ShouldRegisterDeletedObject<query::EdgeAccessor>()) {
|
||||
return;
|
||||
}
|
||||
for (const auto &edge : (*result)->second) {
|
||||
trigger_ctx_collector->RegisterDeletedObject(edge);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2098,6 +2147,9 @@ mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *
|
||||
throw SerializationException{"Cannot serialize creating an edge."};
|
||||
}
|
||||
}
|
||||
if (graph->ctx->trigger_context_collector) {
|
||||
graph->ctx->trigger_context_collector->RegisterCreatedObject(*edge);
|
||||
}
|
||||
return NewRawMgpObject<mgp_edge>(memory, edge.GetValue(), from->graph);
|
||||
},
|
||||
result);
|
||||
@@ -2122,6 +2174,9 @@ mgp_error mgp_graph_delete_edge(struct mgp_graph *graph, mgp_edge *edge) {
|
||||
throw SerializationException{"Cannot serialize removing an edge."};
|
||||
}
|
||||
}
|
||||
if (graph->ctx->trigger_context_collector && *result) {
|
||||
graph->ctx->trigger_context_collector->RegisterDeletedObject(**result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ extern "C" {
|
||||
#include "query/procedure/py_module.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/pmr/vector.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
@@ -324,14 +325,15 @@ bool SharedLibraryModule::Load(const std::filesystem::path &file_path) {
|
||||
dlerror(); // Clear any existing error.
|
||||
handle_ = dlopen(file_path.c_str(), RTLD_NOW | RTLD_LOCAL);
|
||||
if (!handle_) {
|
||||
spdlog::error("Unable to load module {}; {}", file_path, dlerror());
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
spdlog::error(utils::MessageWithLink("Unable to load module {}; {}.", file_path, dlerror(), "https://memgr.ph/modules"));
|
||||
return false;
|
||||
}
|
||||
// Get required mgp_init_module
|
||||
init_fn_ = reinterpret_cast<int (*)(mgp_module *, mgp_memory *)>(dlsym(handle_, "mgp_init_module"));
|
||||
char *dl_errored = dlerror();
|
||||
if (!init_fn_ || dl_errored) {
|
||||
spdlog::error("Unable to load module {}; {}", file_path, dl_errored);
|
||||
spdlog::error(utils::MessageWithLink("Unable to load module {}; {}.", file_path, dl_errored, "https://memgr.ph/modules"));
|
||||
dlclose(handle_);
|
||||
handle_ = nullptr;
|
||||
return false;
|
||||
@@ -382,7 +384,8 @@ bool SharedLibraryModule::Close() {
|
||||
spdlog::warn("When closing module {}; mgp_shutdown_module returned {}", file_path_, shutdown_res);
|
||||
}
|
||||
if (dlclose(handle_) != 0) {
|
||||
spdlog::error("Failed to close module {}; {}", file_path_, dlerror());
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
spdlog::error(utils::MessageWithLink("Failed to close module {}; {}.", file_path_, dlerror(), "https://memgr.ph/modules"));
|
||||
return false;
|
||||
}
|
||||
spdlog::info("Closed module {}", file_path_);
|
||||
@@ -441,7 +444,7 @@ bool PythonModule::Load(const std::filesystem::path &file_path) {
|
||||
auto gil = py::EnsureGIL();
|
||||
auto maybe_exc = py::AppendToSysPath(file_path.parent_path().c_str());
|
||||
if (maybe_exc) {
|
||||
spdlog::error("Unable to load module {}; {}", file_path, *maybe_exc);
|
||||
spdlog::error(utils::MessageWithLink("Unable to load module {}; {}.", file_path, *maybe_exc, "https://memgr.ph/modules"));
|
||||
return false;
|
||||
}
|
||||
bool succ = true;
|
||||
@@ -466,7 +469,7 @@ bool PythonModule::Load(const std::filesystem::path &file_path) {
|
||||
return true;
|
||||
}
|
||||
auto exc_info = py::FetchError().value();
|
||||
spdlog::error("Unable to load module {}; {}", file_path, exc_info);
|
||||
spdlog::error(utils::MessageWithLink("Unable to load module {}; {}.", file_path, exc_info, "https://memgr.ph/modules"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -509,7 +512,7 @@ namespace {
|
||||
std::unique_ptr<Module> LoadModuleFromFile(const std::filesystem::path &path) {
|
||||
const auto &ext = path.extension();
|
||||
if (ext != ".so" && ext != ".py") {
|
||||
spdlog::warn("Unknown query module file {}", path);
|
||||
spdlog::warn(utils::MessageWithLink("Unknown query module file {}.", path, "https://memgr.ph/modules"));
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Module> module;
|
||||
@@ -531,7 +534,7 @@ bool ModuleRegistry::RegisterModule(const std::string_view &name, std::unique_pt
|
||||
MG_ASSERT(!name.empty(), "Module name cannot be empty");
|
||||
MG_ASSERT(module, "Tried to register an invalid module");
|
||||
if (modules_.find(name) != modules_.end()) {
|
||||
spdlog::error("Unable to overwrite an already loaded module {}", name);
|
||||
spdlog::error(utils::MessageWithLink("Unable to overwrite an already loaded module {}.", name, "https://memgr.ph/modules"));
|
||||
return false;
|
||||
}
|
||||
modules_.emplace(name, std::move(module));
|
||||
@@ -561,7 +564,7 @@ void ModuleRegistry::SetModulesDirectory(std::vector<std::filesystem::path> modu
|
||||
|
||||
bool ModuleRegistry::LoadModuleIfFound(const std::filesystem::path &modules_dir, const std::string_view name) {
|
||||
if (!utils::DirExists(modules_dir)) {
|
||||
spdlog::error("Module directory {} doesn't exist", modules_dir);
|
||||
spdlog::error(utils::MessageWithLink("Module directory {} doesn't exist.", modules_dir, "https://memgr.ph/modules"));
|
||||
return false;
|
||||
}
|
||||
for (const auto &entry : std::filesystem::directory_iterator(modules_dir)) {
|
||||
@@ -598,7 +601,7 @@ bool ModuleRegistry::LoadOrReloadModuleFromName(const std::string_view name) {
|
||||
void ModuleRegistry::LoadModulesFromDirectory(const std::filesystem::path &modules_dir) {
|
||||
if (modules_dir.empty()) return;
|
||||
if (!utils::DirExists(modules_dir)) {
|
||||
spdlog::error("Module directory {} doesn't exist", modules_dir);
|
||||
spdlog::error(utils::MessageWithLink("Module directory {} doesn't exist.", modules_dir, "https://memgr.ph/modules"));
|
||||
return;
|
||||
}
|
||||
for (const auto &entry : std::filesystem::directory_iterator(modules_dir)) {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "storage/v2/durability/wal.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
#include "utils/message.hpp"
|
||||
|
||||
namespace storage::durability {
|
||||
|
||||
@@ -168,7 +169,8 @@ std::optional<RecoveryInfo> RecoverData(const std::filesystem::path &snapshot_di
|
||||
spdlog::info("Recovering persisted data using snapshot ({}) and WAL directory ({}).", snapshot_directory,
|
||||
wal_directory);
|
||||
if (!utils::DirExists(snapshot_directory) && !utils::DirExists(wal_directory)) {
|
||||
spdlog::warn("Snapshot or WAL directory don't exist, there is nothing to recover.");
|
||||
spdlog::warn(utils::MessageWithLink("Snapshot or WAL directory don't exist, there is nothing to recover.",
|
||||
"https://memgr.ph/durability"));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -242,7 +244,7 @@ std::optional<RecoveryInfo> RecoverData(const std::filesystem::path &snapshot_di
|
||||
}
|
||||
MG_ASSERT(!error_code, "Couldn't recover data because an error occurred: {}!", error_code.message());
|
||||
if (wal_files.empty()) {
|
||||
spdlog::warn("No snapshot or WAL file found!");
|
||||
spdlog::warn(utils::MessageWithLink("No snapshot or WAL file found.", "https://memgr.ph/durability"));
|
||||
return std::nullopt;
|
||||
}
|
||||
std::sort(wal_files.begin(), wal_files.end());
|
||||
@@ -254,7 +256,7 @@ std::optional<RecoveryInfo> RecoverData(const std::filesystem::path &snapshot_di
|
||||
|
||||
auto maybe_wal_files = GetWalFiles(wal_directory, *uuid);
|
||||
if (!maybe_wal_files) {
|
||||
spdlog::warn("Couldn't get WAL file info from the WAL directory!");
|
||||
spdlog::warn(utils::MessageWithLink("Couldn't get WAL file info from the WAL directory.", "https://memgr.ph/durability"));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "storage/v2/vertex_accessor.hpp"
|
||||
#include "utils/file_locker.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
|
||||
namespace storage::durability {
|
||||
|
||||
@@ -913,9 +914,8 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
|
||||
|
||||
if (error_code) {
|
||||
spdlog::error(
|
||||
"Couldn't ensure that exactly {} snapshots exist because an error "
|
||||
"occurred: {}",
|
||||
snapshot_retention_count, error_code.message());
|
||||
utils::MessageWithLink("Couldn't ensure that exactly {} snapshots exist because an error occurred: {}.",
|
||||
snapshot_retention_count, error_code.message(), "https://memgr.ph/snapshots"));
|
||||
}
|
||||
std::sort(old_snapshot_files.begin(), old_snapshot_files.end());
|
||||
if (old_snapshot_files.size() > snapshot_retention_count - 1) {
|
||||
@@ -945,9 +945,9 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
|
||||
|
||||
if (error_code) {
|
||||
spdlog::error(
|
||||
"Couldn't ensure that only the absolutely necessary WAL files exist "
|
||||
"because an error occurred: {}",
|
||||
error_code.message());
|
||||
utils::MessageWithLink("Couldn't ensure that only the absolutely necessary WAL files exist "
|
||||
"because an error occurred: {}.",
|
||||
error_code.message(), "https://memgr.ph/snapshots"));
|
||||
}
|
||||
std::sort(wal_files.begin(), wal_files.end());
|
||||
uint64_t snapshot_start_timestamp = transaction->start_timestamp;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "storage/v2/transaction.hpp"
|
||||
#include "utils/file_locker.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
|
||||
namespace storage {
|
||||
|
||||
@@ -105,12 +106,13 @@ void Storage::ReplicationClient::TryInitializeClient() {
|
||||
} catch (const rpc::RpcFailedException &) {
|
||||
std::unique_lock client_guarde{client_lock_};
|
||||
replica_state_.store(replication::ReplicaState::INVALID);
|
||||
spdlog::error("Failed to connect to replica {} at {}", name_, rpc_client_->Endpoint());
|
||||
spdlog::error(utils::MessageWithLink("Failed to connect to replica {} at the endpoint {}.", name_,
|
||||
rpc_client_->Endpoint(), "https://memgr.ph/replication"));
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::ReplicationClient::HandleRpcFailure() {
|
||||
spdlog::error("Couldn't replicate data to {}", name_);
|
||||
spdlog::error(utils::MessageWithLink("Couldn't replicate data to {}.", name_, "https://memgr.ph/replication"));
|
||||
thread_pool_.AddTask([this] {
|
||||
rpc_client_->Abort();
|
||||
this->TryInitializeClient();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
;; Copyright 2021 Memgraph Ltd.
|
||||
;;
|
||||
;; Use of this software is governed by the Business Source License
|
||||
;; included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source License, and you may not use this file except in compliance with the Business Source License.
|
||||
;; 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
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
#include "utils/message.hpp"
|
||||
#include "utils/rw_lock.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
#include "utils/stat.hpp"
|
||||
@@ -376,7 +377,7 @@ Storage::Storage(Config config)
|
||||
if (auto maybe_error = this->CreateSnapshot(); maybe_error.HasError()) {
|
||||
switch (maybe_error.GetError()) {
|
||||
case CreateSnapshotError::DisabledForReplica:
|
||||
spdlog::warn("Snapshots are disabled for replicas!");
|
||||
spdlog::warn(utils::MessageWithLink("Snapshots are disabled for replicas.", "https://memgr.ph/replication"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -413,7 +414,7 @@ Storage::~Storage() {
|
||||
if (auto maybe_error = this->CreateSnapshot(); maybe_error.HasError()) {
|
||||
switch (maybe_error.GetError()) {
|
||||
case CreateSnapshotError::DisabledForReplica:
|
||||
spdlog::warn("Snapshots are disabled for replicas!");
|
||||
spdlog::warn(utils::MessageWithLink("Snapshots are disabled for replicas.", "https://memgr.ph/replication"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ set(telemetry_src_files
|
||||
|
||||
add_library(telemetry_lib STATIC ${telemetry_src_files})
|
||||
target_link_libraries(telemetry_lib mg-requests mg-kvstore mg-utils)
|
||||
if (BUILD_FOR_DOCKER)
|
||||
target_compile_definitions(telemetry_lib PRIVATE DOCKER_BUILD)
|
||||
option(MG_TELEMETRY_ID_OVERRIDE "Override for the telemetry ID" STRING)
|
||||
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}")
|
||||
endif()
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
namespace telemetry {
|
||||
namespace {
|
||||
std::string GetMachineId() {
|
||||
#ifdef DOCKER_BUILD
|
||||
return "DOCKER";
|
||||
#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");
|
||||
|
||||
23
src/utils/message.hpp
Normal file
23
src/utils/message.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2021 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
template <typename... Args>
|
||||
std::string MessageWithLink(const std::string_view format, Args &&...args) {
|
||||
return fmt::format(fmt::format("{} For more details, visit {{}}.", format), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
@@ -12,3 +12,9 @@ target_link_libraries(memgraph__e2e__triggers__on_delete memgraph__e2e__triggers
|
||||
|
||||
add_executable(memgraph__e2e__triggers__privileges privilige_check.cpp)
|
||||
target_link_libraries(memgraph__e2e__triggers__privileges memgraph__e2e__triggers_common)
|
||||
|
||||
add_subdirectory(procedures)
|
||||
|
||||
add_dependencies(memgraph__e2e__triggers__on_create memgraph__e2e__triggers__write.py)
|
||||
add_dependencies(memgraph__e2e__triggers__on_update memgraph__e2e__triggers__write.py)
|
||||
add_dependencies(memgraph__e2e__triggers__on_delete memgraph__e2e__triggers__write.py)
|
||||
|
||||
@@ -100,3 +100,10 @@ void CheckVertexExists(mg::Client &client, std::string_view label, int vertex_id
|
||||
MG_ASSERT(VertexExists(client, label, vertex_id), "Expected vertex doesn't exist with label {} and id {}!", label,
|
||||
vertex_id);
|
||||
}
|
||||
|
||||
void ExecuteCreateVertex(mg::Client &client, int id) {
|
||||
client.Execute(fmt::format("CALL write.create_vertex({}) YIELD v RETURN v", id));
|
||||
const auto v1 = client.FetchAll();
|
||||
MG_ASSERT(v1);
|
||||
MG_ASSERT(v1->size() == 1);
|
||||
}
|
||||
|
||||
@@ -21,4 +21,5 @@ void CheckNumberOfAllVertices(mg::Client &client, int expected_number_of_vertice
|
||||
std::optional<mg::Value> GetVertex(mg::Client &client, std::string_view label, int vertex_id);
|
||||
bool VertexExists(mg::Client &client, std::string_view label, int vertex_id);
|
||||
void CheckVertexMissing(mg::Client &client, std::string_view label, int vertex_id);
|
||||
void CheckVertexExists(mg::Client &client, std::string_view label, int vertex_id);
|
||||
void CheckVertexExists(mg::Client &client, std::string_view label, int vertex_id);
|
||||
void ExecuteCreateVertex(mg::Client &client, int id);
|
||||
|
||||
@@ -103,5 +103,34 @@ int main(int argc, char **argv) {
|
||||
run_create_trigger_tests(kBeforeCommit);
|
||||
run_create_trigger_tests(kAfterCommit);
|
||||
|
||||
const auto run_create_trigger_write_proc_create_vertex_test = [&]() {
|
||||
CreateOnCreateTriggers(*client, true);
|
||||
ExecuteCreateVertex(*client, 1);
|
||||
constexpr auto kNumberOfExpectedVertices = 3;
|
||||
CheckNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
CheckVertexExists(*client, kTriggerCreatedVertexLabel, 1);
|
||||
CheckVertexExists(*client, kTriggerCreatedObjectLabel, 1);
|
||||
DropOnCreateTriggers(*client);
|
||||
client->Execute("MATCH (n) DETACH DELETE n;");
|
||||
client->DiscardAll();
|
||||
};
|
||||
run_create_trigger_write_proc_create_vertex_test();
|
||||
|
||||
const auto run_create_trigger_write_proc_create_edge_test = [&]() {
|
||||
ExecuteCreateVertex(*client, 1);
|
||||
ExecuteCreateVertex(*client, 2);
|
||||
CreateOnCreateTriggers(*client, true);
|
||||
client->Execute("MATCH (n {id:1}), (m {id:2}) CALL write.create_edge(n, m, 'edge') YIELD e RETURN e");
|
||||
client->DiscardAll();
|
||||
constexpr auto kNumberOfExpectedVertices = 4;
|
||||
CheckNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
CheckVertexExists(*client, kTriggerCreatedEdgeLabel, 1);
|
||||
CheckVertexExists(*client, kTriggerCreatedObjectLabel, 1);
|
||||
DropOnCreateTriggers(*client);
|
||||
client->Execute("MATCH (n) DETACH DELETE n;");
|
||||
client->DiscardAll();
|
||||
};
|
||||
run_create_trigger_write_proc_create_edge_test();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <mgclient.hpp>
|
||||
@@ -10,6 +11,12 @@ constexpr std::string_view kTriggerDeletedVertexLabel{"DELETED_VERTEX"};
|
||||
constexpr std::string_view kTriggerDeletedEdgeLabel{"DELETED_EDGE"};
|
||||
constexpr std::string_view kTriggerDeletedObjectLabel{"DELETED_OBJECT"};
|
||||
|
||||
enum class AllowedTriggerType : uint8_t {
|
||||
VERTEX,
|
||||
EDGE,
|
||||
OBJECT,
|
||||
};
|
||||
|
||||
void DetachDeleteVertex(mg::Client &client, int vertex_id) {
|
||||
mg::Map parameters{{"id", mg::Value{vertex_id}}};
|
||||
client.Execute(fmt::format("MATCH (n: {} {{id: $id}}) DETACH DELETE n", kVertexLabel),
|
||||
@@ -23,43 +30,77 @@ void DeleteEdge(mg::Client &client, int edge_id) {
|
||||
client.DiscardAll();
|
||||
}
|
||||
|
||||
void CreateOnDeleteTriggers(mg::Client &client, bool is_before) {
|
||||
void CreateOnDeleteTriggers(mg::Client &client, bool is_before,
|
||||
const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
|
||||
const std::string_view before_or_after = is_before ? "BEFORE" : "AFTER";
|
||||
client.Execute(
|
||||
fmt::format("CREATE TRIGGER DeletedVerticesTrigger ON () DELETE "
|
||||
"{} COMMIT "
|
||||
"EXECUTE "
|
||||
"UNWIND deletedVertices as deletedVertex "
|
||||
"CREATE (n: {} {{ id: deletedVertex.id }})",
|
||||
before_or_after, kTriggerDeletedVertexLabel));
|
||||
client.DiscardAll();
|
||||
client.Execute(
|
||||
fmt::format("CREATE TRIGGER DeletedEdgesTrigger ON --> DELETE "
|
||||
"{} COMMIT "
|
||||
"EXECUTE "
|
||||
"UNWIND deletedEdges as deletedEdge "
|
||||
"CREATE (n: {} {{ id: deletedEdge.id }})",
|
||||
before_or_after, kTriggerDeletedEdgeLabel));
|
||||
client.DiscardAll();
|
||||
client.Execute(
|
||||
fmt::format("CREATE TRIGGER DeletedObjectsTrigger ON DELETE "
|
||||
"{} COMMIT "
|
||||
"EXECUTE "
|
||||
"UNWIND deletedObjects as deletedObjectEvent "
|
||||
"WITH CASE deletedObjectEvent.event_type WHEN \"deleted_vertex\" THEN deletedObjectEvent.vertex.id "
|
||||
"ELSE deletedObjectEvent.edge.id END as id "
|
||||
"CREATE (n: {} {{ id: id }})",
|
||||
before_or_after, kTriggerDeletedObjectLabel));
|
||||
client.DiscardAll();
|
||||
|
||||
const auto create_on_vertex_delete_trigger = [&, before_or_after] {
|
||||
client.Execute(
|
||||
fmt::format("CREATE TRIGGER DeletedVerticesTrigger ON () DELETE "
|
||||
"{} COMMIT "
|
||||
"EXECUTE "
|
||||
"UNWIND deletedVertices as deletedVertex "
|
||||
"CREATE (n: {} {{ id: deletedVertex.id }})",
|
||||
before_or_after, kTriggerDeletedVertexLabel));
|
||||
client.DiscardAll();
|
||||
};
|
||||
|
||||
const auto create_on_edge_delete_trigger = [&, before_or_after] {
|
||||
client.Execute(
|
||||
fmt::format("CREATE TRIGGER DeletedEdgesTrigger ON --> DELETE "
|
||||
"{} COMMIT "
|
||||
"EXECUTE "
|
||||
"UNWIND deletedEdges as deletedEdge "
|
||||
"CREATE (n: {} {{ id: deletedEdge.id }})",
|
||||
before_or_after, kTriggerDeletedEdgeLabel));
|
||||
client.DiscardAll();
|
||||
};
|
||||
|
||||
const auto create_on_object_delete_trigger = [&, before_or_after] {
|
||||
client.Execute(
|
||||
fmt::format("CREATE TRIGGER DeletedObjectsTrigger ON DELETE "
|
||||
"{} COMMIT "
|
||||
"EXECUTE "
|
||||
"UNWIND deletedObjects as deletedObjectEvent "
|
||||
"WITH CASE deletedObjectEvent.event_type WHEN \"deleted_vertex\" THEN deletedObjectEvent.vertex.id "
|
||||
"ELSE deletedObjectEvent.edge.id END as id "
|
||||
"CREATE (n: {} {{ id: id }})",
|
||||
before_or_after, kTriggerDeletedObjectLabel));
|
||||
client.DiscardAll();
|
||||
};
|
||||
|
||||
for (const auto allowed_trigger_type : allowed_trigger_types) {
|
||||
switch (allowed_trigger_type) {
|
||||
case AllowedTriggerType::VERTEX:
|
||||
create_on_vertex_delete_trigger();
|
||||
break;
|
||||
case AllowedTriggerType::EDGE:
|
||||
create_on_edge_delete_trigger();
|
||||
break;
|
||||
case AllowedTriggerType::OBJECT:
|
||||
create_on_object_delete_trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DropOnDeleteTriggers(mg::Client &client) {
|
||||
client.Execute("DROP TRIGGER DeletedVerticesTrigger");
|
||||
client.DiscardAll();
|
||||
client.Execute("DROP TRIGGER DeletedEdgesTrigger");
|
||||
client.DiscardAll();
|
||||
client.Execute("DROP TRIGGER DeletedObjectsTrigger");
|
||||
client.DiscardAll();
|
||||
void DropOnDeleteTriggers(mg::Client &client, const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
|
||||
for (const auto allowed_trigger_type : allowed_trigger_types) {
|
||||
switch (allowed_trigger_type) {
|
||||
case AllowedTriggerType::VERTEX: {
|
||||
client.Execute("DROP TRIGGER DeletedVerticesTrigger");
|
||||
client.DiscardAll();
|
||||
}
|
||||
case AllowedTriggerType::EDGE: {
|
||||
client.Execute("DROP TRIGGER DeletedEdgesTrigger");
|
||||
client.DiscardAll();
|
||||
}
|
||||
case AllowedTriggerType::OBJECT: {
|
||||
client.Execute("DROP TRIGGER DeletedObjectsTrigger");
|
||||
client.DiscardAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EdgeInfo {
|
||||
@@ -68,6 +109,10 @@ struct EdgeInfo {
|
||||
int edge_id;
|
||||
};
|
||||
|
||||
void ValidateVertexExistance(mg::Client &client, const bool should_exist, const std::string_view label, const int id) {
|
||||
should_exist ? CheckVertexExists(client, label, id) : CheckVertexMissing(client, label, id);
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
gflags::SetUsageMessage("Memgraph E2E ON DELETE Triggers");
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
@@ -77,11 +122,12 @@ int main(int argc, char **argv) {
|
||||
|
||||
auto client = Connect();
|
||||
|
||||
const auto run_delete_trigger_tests = [&](bool is_before) {
|
||||
const std::array vertex_ids{1, 2, 3, 4};
|
||||
const std::array edges{EdgeInfo{vertex_ids[0], vertex_ids[1], 5}, EdgeInfo{vertex_ids[2], vertex_ids[3], 6}};
|
||||
const auto run_delete_trigger_tests = [&](const bool is_before,
|
||||
const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
|
||||
constexpr std::array vertex_ids{1, 2, 3, 4};
|
||||
constexpr std::array edges{EdgeInfo{vertex_ids[0], vertex_ids[1], 5}, EdgeInfo{vertex_ids[2], vertex_ids[3], 6}};
|
||||
{
|
||||
CreateOnDeleteTriggers(*client, is_before);
|
||||
CreateOnDeleteTriggers(*client, is_before, allowed_trigger_types);
|
||||
|
||||
client->BeginTransaction();
|
||||
for (const auto vertex_id : vertex_ids) {
|
||||
@@ -100,34 +146,153 @@ int main(int argc, char **argv) {
|
||||
|
||||
// :VERTEX x 4
|
||||
// deleted :VERTEX x -1
|
||||
// :DELETED_VERTEX x 1
|
||||
// :DELETED_EDGE x 2
|
||||
// :DELETED_OBJECT x 3
|
||||
constexpr auto kNumberOfExpectedVertices = 9;
|
||||
auto number_of_expected_vertices = 3;
|
||||
for (const auto allowed_trigger_type : allowed_trigger_types) {
|
||||
switch (allowed_trigger_type) {
|
||||
case AllowedTriggerType::VERTEX:
|
||||
number_of_expected_vertices += 1;
|
||||
break;
|
||||
case AllowedTriggerType::EDGE:
|
||||
number_of_expected_vertices += 2;
|
||||
break;
|
||||
case AllowedTriggerType::OBJECT:
|
||||
number_of_expected_vertices += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_before) {
|
||||
CheckNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
CheckNumberOfAllVertices(*client, number_of_expected_vertices);
|
||||
} else {
|
||||
WaitForNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
WaitForNumberOfAllVertices(*client, number_of_expected_vertices);
|
||||
}
|
||||
|
||||
CheckVertexExists(*client, kTriggerDeletedVertexLabel, vertex_ids[0]);
|
||||
CheckVertexExists(*client, kTriggerDeletedObjectLabel, vertex_ids[0]);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::VERTEX),
|
||||
kTriggerDeletedVertexLabel, vertex_ids[0]);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
|
||||
kTriggerDeletedObjectLabel, vertex_ids[0]);
|
||||
|
||||
for (const auto &edge : edges) {
|
||||
CheckVertexExists(*client, kTriggerDeletedEdgeLabel, edge.edge_id);
|
||||
CheckVertexExists(*client, kTriggerDeletedObjectLabel, edge.edge_id);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::EDGE),
|
||||
kTriggerDeletedEdgeLabel, edge.edge_id);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
|
||||
kTriggerDeletedObjectLabel, edge.edge_id);
|
||||
}
|
||||
|
||||
DropOnDeleteTriggers(*client);
|
||||
DropOnDeleteTriggers(*client, allowed_trigger_types);
|
||||
client->Execute("MATCH (n) DETACH DELETE n;");
|
||||
client->DiscardAll();
|
||||
}
|
||||
};
|
||||
|
||||
const auto run_delete_trigger_write_procedure_tests =
|
||||
[&](const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
|
||||
ExecuteCreateVertex(*client, 2);
|
||||
ExecuteCreateVertex(*client, 3);
|
||||
client->Execute("MATCH (n {id:2}), (m {id:3}) CALL write.create_edge(n, m, 'edge') YIELD e RETURN e");
|
||||
client->DiscardAll();
|
||||
CreateOnDeleteTriggers(*client, true, allowed_trigger_types);
|
||||
client->Execute("MATCH ()-[e]->() CALL write.delete_edge(e)");
|
||||
client->DiscardAll();
|
||||
client->Execute("MATCH (n {id:2}) CALL write.delete_vertex(n)");
|
||||
client->DiscardAll();
|
||||
|
||||
auto number_of_expected_vertices = 1;
|
||||
for (const auto allowed_trigger_type : allowed_trigger_types) {
|
||||
switch (allowed_trigger_type) {
|
||||
case AllowedTriggerType::VERTEX:
|
||||
number_of_expected_vertices += 1;
|
||||
break;
|
||||
case AllowedTriggerType::EDGE:
|
||||
number_of_expected_vertices += 1;
|
||||
break;
|
||||
case AllowedTriggerType::OBJECT:
|
||||
number_of_expected_vertices += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CheckNumberOfAllVertices(*client, number_of_expected_vertices);
|
||||
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::EDGE),
|
||||
kTriggerDeletedEdgeLabel, 1);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
|
||||
kTriggerDeletedObjectLabel, 1);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::VERTEX),
|
||||
kTriggerDeletedVertexLabel, 2);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
|
||||
kTriggerDeletedObjectLabel, 2);
|
||||
|
||||
DropOnDeleteTriggers(*client, allowed_trigger_types);
|
||||
client->Execute("MATCH (n) DETACH DELETE n;");
|
||||
client->DiscardAll();
|
||||
};
|
||||
|
||||
const auto run_delete_trigger_write_procedure_delete_detach_test =
|
||||
[&](const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
|
||||
ExecuteCreateVertex(*client, 2);
|
||||
ExecuteCreateVertex(*client, 3);
|
||||
client->Execute("MATCH (n {id:2}), (m {id:3}) CALL write.create_edge(n, m, 'edge') YIELD e RETURN e");
|
||||
client->DiscardAll();
|
||||
|
||||
CreateOnDeleteTriggers(*client, true, allowed_trigger_types);
|
||||
|
||||
client->Execute("MATCH (v {id:2}) CALL write.detach_delete_vertex(v)");
|
||||
client->DiscardAll();
|
||||
|
||||
auto number_of_expected_vertices = 1;
|
||||
for (const auto allowed_trigger_type : allowed_trigger_types) {
|
||||
switch (allowed_trigger_type) {
|
||||
case AllowedTriggerType::VERTEX:
|
||||
number_of_expected_vertices += 1;
|
||||
break;
|
||||
case AllowedTriggerType::EDGE:
|
||||
number_of_expected_vertices += 1;
|
||||
break;
|
||||
case AllowedTriggerType::OBJECT:
|
||||
number_of_expected_vertices += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CheckNumberOfAllVertices(*client, number_of_expected_vertices);
|
||||
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::EDGE),
|
||||
kTriggerDeletedEdgeLabel, 1);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
|
||||
kTriggerDeletedObjectLabel, 1);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::VERTEX),
|
||||
kTriggerDeletedVertexLabel, 2);
|
||||
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
|
||||
kTriggerDeletedObjectLabel, 2);
|
||||
|
||||
DropOnDeleteTriggers(*client, allowed_trigger_types);
|
||||
client->Execute("MATCH (n) DETACH DELETE n;");
|
||||
client->DiscardAll();
|
||||
};
|
||||
|
||||
const auto run_for_trigger_combinations = [&](const bool is_before) {
|
||||
const std::array trigger_type_combinations{
|
||||
std::unordered_set{AllowedTriggerType::VERTEX},
|
||||
std::unordered_set{AllowedTriggerType::EDGE},
|
||||
std::unordered_set{AllowedTriggerType::OBJECT},
|
||||
std::unordered_set{AllowedTriggerType::VERTEX, AllowedTriggerType::EDGE},
|
||||
std::unordered_set{AllowedTriggerType::VERTEX, AllowedTriggerType::OBJECT},
|
||||
std::unordered_set{AllowedTriggerType::EDGE, AllowedTriggerType::OBJECT},
|
||||
std::unordered_set{AllowedTriggerType::VERTEX, AllowedTriggerType::EDGE, AllowedTriggerType::OBJECT},
|
||||
};
|
||||
|
||||
for (const auto &allowed_trigger_types : trigger_type_combinations) {
|
||||
run_delete_trigger_tests(is_before, allowed_trigger_types);
|
||||
run_delete_trigger_write_procedure_tests(allowed_trigger_types);
|
||||
run_delete_trigger_write_procedure_delete_detach_test(allowed_trigger_types);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool kBeforeCommit = true;
|
||||
constexpr bool kAfterCommit = false;
|
||||
run_delete_trigger_tests(kBeforeCommit);
|
||||
run_delete_trigger_tests(kAfterCommit);
|
||||
run_for_trigger_combinations(kBeforeCommit);
|
||||
run_for_trigger_combinations(kAfterCommit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -282,5 +282,87 @@ int main(int argc, char **argv) {
|
||||
run_update_trigger_tests(kBeforeCommit);
|
||||
run_update_trigger_tests(kAfterCommit);
|
||||
|
||||
const auto run_update_trigger_write_procedure_vertex_set_property_test = [&]() {
|
||||
CreateOnUpdateTriggers(*client, true);
|
||||
ExecuteCreateVertex(*client, 1);
|
||||
client->Execute("MATCH (n) CALL write.set_property(n)");
|
||||
client->DiscardAll();
|
||||
constexpr auto kNumberOfExpectedVertices = 4;
|
||||
constexpr int expected_updated_id = 2;
|
||||
CheckNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
CheckVertexExists(*client, kTriggerUpdatedVertexLabel, expected_updated_id);
|
||||
CheckVertexExists(*client, kTriggerUpdatedObjectLabel, expected_updated_id);
|
||||
CheckVertexExists(*client, kTriggerSetVertexPropertyLabel, expected_updated_id);
|
||||
client->Execute(fmt::format("MATCH (n1:{}), (n2:{}), (n3:{}) DELETE n1, n2, n3", kTriggerUpdatedVertexLabel,
|
||||
kTriggerUpdatedObjectLabel, kTriggerSetVertexPropertyLabel));
|
||||
client->DiscardAll();
|
||||
client->Execute("MATCH (n) CALL write.remove_property(n)");
|
||||
client->DiscardAll();
|
||||
CheckNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
CheckVertexExists(*client, kTriggerUpdatedVertexLabel, expected_updated_id);
|
||||
CheckVertexExists(*client, kTriggerUpdatedObjectLabel, expected_updated_id);
|
||||
CheckVertexExists(*client, kTriggerRemovedVertexPropertyLabel, 2);
|
||||
DropOnUpdateTriggers(*client);
|
||||
client->Execute("MATCH (n) DETACH DELETE n;");
|
||||
client->DiscardAll();
|
||||
};
|
||||
run_update_trigger_write_procedure_vertex_set_property_test();
|
||||
|
||||
const auto run_update_trigger_write_procedure_edge_set_property_test = [&]() {
|
||||
CreateOnUpdateTriggers(*client, true);
|
||||
ExecuteCreateVertex(*client, 1);
|
||||
ExecuteCreateVertex(*client, 3);
|
||||
client->Execute("MATCH (n {id:1}), (m {id:3}) CALL write.create_edge(n, m, 'edge') YIELD e RETURN e");
|
||||
client->DiscardAll();
|
||||
client->Execute("MATCH ()-[e]->() CALL write.set_property(e)");
|
||||
client->DiscardAll();
|
||||
constexpr auto kNumberOfExpectedVertices = 5;
|
||||
constexpr int expected_updated_id = 2;
|
||||
CheckNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
CheckVertexExists(*client, kTriggerSetEdgePropertyLabel, expected_updated_id);
|
||||
CheckVertexExists(*client, kTriggerUpdatedObjectLabel, expected_updated_id);
|
||||
CheckVertexExists(*client, kTriggerUpdatedEdgeLabel, expected_updated_id);
|
||||
client->Execute(fmt::format("MATCH (n1:{}), (n2:{}), (n3:{}) DELETE n1, n2, n3", kTriggerSetEdgePropertyLabel,
|
||||
kTriggerUpdatedObjectLabel, kTriggerUpdatedEdgeLabel));
|
||||
client->DiscardAll();
|
||||
client->Execute("MATCH ()-[e]->() CALL write.remove_property(e)");
|
||||
client->DiscardAll();
|
||||
CheckNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
CheckVertexExists(*client, kTriggerUpdatedObjectLabel, expected_updated_id);
|
||||
CheckVertexExists(*client, kTriggerUpdatedEdgeLabel, expected_updated_id);
|
||||
CheckVertexExists(*client, kTriggerRemovedEdgePropertyLabel, 2);
|
||||
DropOnUpdateTriggers(*client);
|
||||
client->Execute("MATCH (n) DETACH DELETE n;");
|
||||
client->DiscardAll();
|
||||
};
|
||||
run_update_trigger_write_procedure_edge_set_property_test();
|
||||
|
||||
const auto run_update_trigger_write_procedure_set_label_test = [&]() {
|
||||
ExecuteCreateVertex(*client, 1);
|
||||
client->Execute("MATCH (n) CALL write.add_label(n, 'label') YIELD o RETURN o");
|
||||
client->DiscardAll();
|
||||
CreateOnUpdateTriggers(*client, true);
|
||||
client->Execute("MATCH (n) CALL write.add_label(n, 'new') YIELD o RETURN o");
|
||||
client->DiscardAll();
|
||||
constexpr auto kNumberOfExpectedVertices = 4;
|
||||
CheckNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
CheckVertexExists(*client, kTriggerSetVertexLabelLabel, 1);
|
||||
CheckVertexExists(*client, kTriggerUpdatedVertexLabel, 1);
|
||||
CheckVertexExists(*client, kTriggerUpdatedObjectLabel, 1);
|
||||
client->Execute(fmt::format("MATCH (n1:{}), (n2:{}), (n3:{}) DELETE n1, n2, n3", kTriggerSetVertexLabelLabel,
|
||||
kTriggerUpdatedVertexLabel, kTriggerUpdatedObjectLabel));
|
||||
client->DiscardAll();
|
||||
client->Execute("MATCH (n:new) CALL write.remove_label(n, 'new') YIELD o RETURN o");
|
||||
client->DiscardAll();
|
||||
CheckNumberOfAllVertices(*client, kNumberOfExpectedVertices);
|
||||
CheckVertexExists(*client, kTriggerRemovedVertexLabelLabel, 1);
|
||||
CheckVertexExists(*client, kTriggerUpdatedVertexLabel, 1);
|
||||
CheckVertexExists(*client, kTriggerUpdatedObjectLabel, 1);
|
||||
DropOnUpdateTriggers(*client);
|
||||
client->Execute("MATCH (n) DETACH DELETE n;");
|
||||
client->DiscardAll();
|
||||
};
|
||||
run_update_trigger_write_procedure_set_label_test();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
1
tests/e2e/triggers/procedures/CMakeLists.txt
Normal file
1
tests/e2e/triggers/procedures/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
copy_e2e_python_files(triggers write.py)
|
||||
68
tests/e2e/triggers/procedures/write.py
Normal file
68
tests/e2e/triggers/procedures/write.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import mgp
|
||||
|
||||
@mgp.write_proc
|
||||
def create_vertex(ctx: mgp.ProcCtx, id: mgp.Any) -> mgp.Record(v=mgp.Any):
|
||||
v = None
|
||||
try:
|
||||
v = ctx.graph.create_vertex()
|
||||
v.properties.set("id", id)
|
||||
v.properties.set("tbd", 0)
|
||||
except RuntimeError as e:
|
||||
return mgp.Record(v=str(e))
|
||||
return mgp.Record(v=v)
|
||||
|
||||
|
||||
@mgp.write_proc
|
||||
def delete_vertex(ctx: mgp.ProcCtx, v: mgp.Any) -> mgp.Record():
|
||||
ctx.graph.delete_vertex(v)
|
||||
return mgp.Record()
|
||||
|
||||
|
||||
@mgp.write_proc
|
||||
def detach_delete_vertex(ctx: mgp.ProcCtx, v: mgp.Any) -> mgp.Record():
|
||||
ctx.graph.detach_delete_vertex(v)
|
||||
return mgp.Record()
|
||||
|
||||
|
||||
@mgp.write_proc
|
||||
def create_edge(ctx: mgp.ProcCtx, from_vertex: mgp.Vertex,
|
||||
to_vertex: mgp.Vertex,
|
||||
edge_type: str) -> mgp.Record(e=mgp.Any):
|
||||
e = None
|
||||
try:
|
||||
e = ctx.graph.create_edge(
|
||||
from_vertex, to_vertex, mgp.EdgeType(edge_type))
|
||||
e.properties.set("id", 1);
|
||||
e.properties.set("tbd", 0);
|
||||
except RuntimeError as ex:
|
||||
return mgp.Record(e=str(ex))
|
||||
return mgp.Record(e=e)
|
||||
|
||||
|
||||
@mgp.write_proc
|
||||
def delete_edge(ctx: mgp.ProcCtx, edge: mgp.Edge) -> mgp.Record():
|
||||
ctx.graph.delete_edge(edge)
|
||||
return mgp.Record()
|
||||
|
||||
|
||||
@mgp.write_proc
|
||||
def set_property(ctx: mgp.ProcCtx, object: mgp.Any) -> mgp.Record():
|
||||
object.properties.set("id", 2)
|
||||
return mgp.Record()
|
||||
|
||||
@mgp.write_proc
|
||||
def remove_property(ctx: mgp.ProcCtx, object: mgp.Any) -> mgp.Record():
|
||||
object.properties.set("tbd", None)
|
||||
return mgp.Record()
|
||||
|
||||
@mgp.write_proc
|
||||
def add_label(ctx: mgp.ProcCtx, object: mgp.Any,
|
||||
name: str) -> mgp.Record(o=mgp.Any):
|
||||
object.add_label(name)
|
||||
return mgp.Record(o=object)
|
||||
|
||||
@mgp.write_proc
|
||||
def remove_label(ctx: mgp.ProcCtx, object: mgp.Any,
|
||||
name: str) -> mgp.Record(o=mgp.Any):
|
||||
object.remove_label(name)
|
||||
return mgp.Record(o=object)
|
||||
@@ -11,14 +11,17 @@ workloads:
|
||||
- name: "ON CREATE Triggers"
|
||||
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_create"
|
||||
args: ["--bolt-port", *bolt_port]
|
||||
proc: "tests/e2e/triggers/procedures/"
|
||||
<<: *template_cluster
|
||||
- name: "ON UPDATE Triggers"
|
||||
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_update"
|
||||
args: ["--bolt-port", *bolt_port]
|
||||
proc: "tests/e2e/triggers/procedures/"
|
||||
<<: *template_cluster
|
||||
- name: "ON DELETE Triggers"
|
||||
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_delete"
|
||||
args: ["--bolt-port", *bolt_port]
|
||||
proc: "tests/e2e/triggers/procedures/"
|
||||
<<: *template_cluster
|
||||
- name: "Triggers privilege check"
|
||||
binary: "tests/e2e/triggers/memgraph__e2e__triggers__privileges"
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "mg_procedure.h"
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/plan/operator.hpp"
|
||||
#include "query/procedure/mg_procedure_impl.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
@@ -89,7 +90,7 @@ void CheckEdgeCountBetween(const MgpVertexPtr &from, const MgpVertexPtr &to, con
|
||||
struct MgpGraphTest : public ::testing::Test {
|
||||
mgp_graph CreateGraph(const storage::View view = storage::View::NEW) {
|
||||
// the execution context can be null as it shouldn't be used in these tests
|
||||
return mgp_graph{&CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION), view, nullptr};
|
||||
return mgp_graph{&CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION), view, ctx_.get()};
|
||||
}
|
||||
|
||||
std::array<storage::Gid, 2> CreateEdge() {
|
||||
@@ -133,6 +134,7 @@ struct MgpGraphTest : public ::testing::Test {
|
||||
private:
|
||||
std::list<storage::Storage::Accessor> accessors_;
|
||||
std::list<query::DbAccessor> db_accessors_;
|
||||
std::unique_ptr<query::ExecutionContext> ctx_ = std::make_unique<query::ExecutionContext>();
|
||||
};
|
||||
|
||||
TEST_F(MgpGraphTest, IsMutable) {
|
||||
|
||||
46
tools/header-checker.py
Executable file
46
tools/header-checker.py
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
BSL_HEADER = """// Copyright 2021 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt."""
|
||||
|
||||
MEL_HEADER = """// Copyright 2021 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the License at https://memgraph.com/legal.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("file", help="directory with source files", nargs="?")
|
||||
|
||||
args = parser.parse_args()
|
||||
with open(args.file, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
has_header = content.startswith(BSL_HEADER) or content.startswith(MEL_HEADER)
|
||||
if not has_header:
|
||||
|
||||
def red(s):
|
||||
return f"\x1b[31m{s}\x1b[0m"
|
||||
|
||||
sys.stdout.writelines(red("The file is missing a header. Please add the BSL or MEL license header!\n"))
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user