Compare commits

..

57 Commits

Author SHA1 Message Date
János Benjamin Antal
24fa630279 Add patch 2022-06-10 17:04:49 +02:00
János Benjamin Antal
c651a8d15a Merge branch 'master' into T0804-MG-base-thrift-project 2022-06-08 11:21:53 +02:00
János Benjamin Antal
5b4ef8de99 Trial vmuch 2022-06-08 11:19:40 +02:00
Marko Budiselić
21ad5d4328 Fix SHOW REPLICATION ROLE and SHOW REPLICAS (#376) 2022-05-20 20:17:59 -07:00
Marko Budiselić
8e3ab1ad0f Add frequent replica ping (#380) 2022-05-20 19:29:17 -07:00
Marko Budiselić
cccf32e79d Add default commented values for Bolt SSL flags (#398) 2022-05-18 08:34:07 +02:00
Jure Bajic
22bd60c613 Fix shutdown call (#395)
* Fix shutdown not called

* Add ssl server tests
2022-05-18 07:50:06 +02:00
János Benjamin Antal
8059a3e653 Improve magic functions docs (#391)
* Improve documentation of magic functions

* Improve wording for decorators
2022-05-17 15:33:28 +02:00
Antonio Andelic
a7f4c98bea Fix module symbol loading (#335)
* Use DEEPBIND

* Add dependency on libstdc++

Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
Co-authored-by: Jure Bajic <jure.bajic@memgraph.com>
2022-05-13 11:43:10 +02:00
Jure Bajic
483f4d04bd Update precommit hooks (#393)
* Add pre-commit

* Add clang pre-commit

* Install pre-commit in init
2022-05-11 21:07:04 +02:00
János Benjamin Antal
3e7aef432f Improve docker-compose setup for running streams e2e tests locally (#388)
* Separate pulsar and kafka docker-compose files

* Add docker-compose for redpanda

* Add small docs how to use docker-compose files
2022-05-10 13:31:45 +02:00
Jeremy B
10ea9c773e Making mgp_error enum class when compiling c++. Remains enum when com… (#389)
* Making mgp_error enum class when compiling c++. Remains enum when compiling c.
2022-05-05 17:48:17 +02:00
János Benjamin Antal
3dd90e2acb Update the storage interface 2022-05-02 09:09:31 +02:00
János Benjamin Antal
62945c3b3a Merge remote-tracking branch 'origin/master' into T0804-MG-base-thrift-project 2022-04-29 15:48:00 +02:00
János Benjamin Antal
e1704ff2d0 Revert "Use attribute instead of UNLIKELY"
This reverts commit 5780ee6c5b.
2022-04-29 15:46:49 +02:00
János Benjamin Antal
7f0c53196b Revert unvanted change 2022-04-29 15:19:23 +02:00
Jeremy B
b782271be8 Fix shared module loading
* Moving function add_query_module from CMakeLists from tests/e2e/magic_functions to tests/e2e

* Adding failing test copying behavior when loading c module (.so) into memgraph.

* Fixing issue where NO_ERROR status returned  MgpTransAddFixedResult was converted to false

* Removing unnecessary transformation

* removing incorrect parameterization of test

* re-adding parametrized transformation
2022-04-28 20:28:44 +02:00
Jure Bajic
a8ffcfa046 Update license year
* Update license year

Co-authored-by: János Benjamin Antal <antaljanosbenjamin@users.noreply.github.com>

Co-authored-by: János Benjamin Antal <antaljanosbenjamin@users.noreply.github.com>
2022-04-27 13:31:37 +02:00
Jure Bajic
7b78665cd8 Implement Bolt over WebSocket with asio
* Replace server implementation with asio

* Add support for bolt over WebSocket
2022-04-27 10:13:16 +02:00
János Benjamin Antal
2da28c2c87 Add meta service interface 2022-04-22 17:06:48 +02:00
Josip Matak
4abaf27765 Memgraph magic functions (#345)
* Extend mgp_module with include adding functions

* Add return type to the function API

* Change Cypher grammar

* Add Python support for functions

* Implement error handling

* E2e tests for functions

* Write cpp e2e functions

* Create mg.functions() procedure

* Implement case insensitivity for user-defined Magic Functions.
2022-04-21 15:45:31 +02:00
Kostas Kyrimis
ea2806bd57 Implement foreach clause (#351) 2022-04-11 13:55:34 +03:00
Siniša Šušnjar
c8dbaf5979 Small io network socket fixes (#360)
* Modernize AddrInfo

* Modernize Socket
2022-04-08 14:38:13 +02:00
Jure Bajic
17049ada09 Resolve python dependency issues (#372) 2022-04-07 17:56:18 +02:00
Jure Bajic
1b619f51b2 Add docker release action (#356)
* Add docker release action

* Add debian-11 arm script

* Remove test prefix

* Update package_docker script

* Update release script

* Unify architecture extension
2022-04-07 15:23:18 +02:00
János Benjamin Antal
537855a0b2 Fix usages of constexpr (#367)
* Fix usages of constexpr
2022-03-31 13:52:43 +02:00
János Benjamin Antal
b36cce2428 Add thrift demo 2022-03-31 09:05:52 +02:00
Jure Bajic
5822b44b15 Fix CentOS 7 release process (#369) 2022-03-31 07:20:53 +02:00
János Benjamin Antal
6afcfcbb89 Merge commit 'bf01c58ed9950a1b2d05a884aad9544e8193b7f3' into T0804-MG-base-thrift-project 2022-03-24 15:25:39 +01:00
János Benjamin Antal
d21798c350 Implement scanVertices 2022-03-24 08:51:37 +01:00
g-despot
bf01c58ed9 Update README.md 2022-03-22 04:50:01 +01:00
g-despot
89a5566f3f Update README.md 2022-03-22 04:49:03 +01:00
g-despot
29452f8774 Update README.md 2022-03-21 16:49:02 +01:00
jbajic
60ad05acff Address review comments 2022-03-14 15:47:41 +01:00
jbajic
4f593c7fca Adapt precommit to run through all files 2022-03-14 15:47:41 +01:00
jbajic
770ea1189a Fix SSL errors 2022-03-14 15:47:41 +01:00
jbajic
695bb343f1 Resolve clang-tidy error 2022-03-14 15:47:41 +01:00
jbajic
12b4ec1589 Add memgraph namespace 2022-03-14 15:47:41 +01:00
Antonio Andelic
b33d2c3940 use boost ssl 2022-03-14 15:47:41 +01:00
János Benjamin Antal
6fe2293f4a Merge remote-tracking branch 'origin/master' into T0804-MG-base-thrift-project 2022-02-28 15:34:53 +01:00
János Benjamin Antal
2f1acff7d7 Replace outdated wrapper header file 2022-02-28 13:27:44 +01:00
Marko Budiselić
477acad1f6 Update CODEOWNERS file (#362) 2022-02-25 13:42:27 +01:00
János Benjamin Antal
ddca2b40f5 Remove unimplemented function declaration (#357) 2022-02-24 11:49:22 +01:00
János Benjamin Antal
e298f11968 Add storage client demo 2022-02-10 09:07:33 +01:00
János Benjamin Antal
5a4c0f7a1b Resolve conflict of ANTLR generated function and macro from glog 2022-02-08 12:33:39 +01:00
János Benjamin Antal
52960a8877 Add base of server and client 2022-02-07 15:14:29 +01:00
János Benjamin Antal
4811024e75 Add createVertices to Thrift 2022-02-07 15:13:19 +01:00
János Benjamin Antal
9193fcade3 CMake cleanup 2022-02-07 15:12:23 +01:00
János Benjamin Antal
066cfa32e3 Fix typo 2022-02-07 15:12:13 +01:00
János Benjamin Antal
77c27482f7 Link generated thrift interface to memgraph 2022-01-28 15:17:45 +01:00
János Benjamin Antal
8f42632fd2 CMake cleanup 2022-01-28 15:14:58 +01:00
János Benjamin Antal
5780ee6c5b Use attribute instead of UNLIKELY 2022-01-28 13:42:21 +01:00
János Benjamin Antal
542928b690 Fix namespace 2022-01-28 10:17:33 +01:00
János Benjamin Antal
67d39597d7 Remove coroutines from folly 2022-01-28 10:16:11 +01:00
János Benjamin Antal
bd0efa1159 Use patch files 2022-01-28 10:15:48 +01:00
János Benjamin Antal
e60dd252d0 Use std::unordered_map 2022-01-27 08:21:03 +01:00
János Benjamin Antal
84b78ded07 Add base of thrift interface 2022-01-27 07:52:39 +01:00
579 changed files with 17510 additions and 11470 deletions

View File

@@ -88,4 +88,3 @@ CheckOptions:
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
...

View File

@@ -16,6 +16,7 @@ tmpdir=$(mktemp -d repo-XXXXXXXX)
trap "rm -rf $tmpdir" EXIT INT
modified_files=$(git diff --cached --name-only --diff-filter=AM $against | sed -nE "/.*\.(cpp|cc|cxx|c|h|hpp)$/p")
FAIL=0
for file in $modified_files; do
echo "Checking $file..."
@@ -23,21 +24,13 @@ for file in $modified_files; do
git checkout-index --prefix="$tmpdir/" -- $file
echo "Running clang-format..."
$project_folder/tools/git-clang-format $tmpdir/$file
code=$?
if [ $code -ne 0 ]; then
break
fi
# Do not break header checker
echo "Running header checker..."
$project_folder/tools/header-checker.py $tmpdir/$file $file --amend-year
code=$?
if [ $code -ne 0 ]; then
break
CODE=$?
if [ $CODE -ne 0 ]; then
FAIL=1
fi
done;
return $code
return ${FAIL}

49
.github/workflows/release_docker.yaml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Publish Docker images
on:
workflow_dispatch:
inputs:
version:
description: "Memgraph binary version to publish on Dockerhub."
required: true
jobs:
docker_publish:
runs-on: ubuntu-latest
env:
DOCKER_ORGANIZATION_NAME: memgraph
DOCKER_REPOSITORY_NAME: memgraph
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Log in to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Download memgraph binary
run: |
cd release/docker
curl -L https://download.memgraph.com/memgraph/v${{ github.event.inputs.version }}/debian-11/memgraph_${{ github.event.inputs.version }}-1_amd64.deb > memgraph-amd64.deb
curl -L https://download.memgraph.com/memgraph/v${{ github.event.inputs.version }}/debian-11-aarch64/memgraph_${{ github.event.inputs.version }}-1_arm64.deb > memgraph-arm64.deb
- name: Build & push docker images
run: |
cd release/docker
docker buildx build \
--build-arg BINARY_NAME="memgraph-" \
--build-arg EXTENSION="deb" \
--platform linux/amd64,linux/arm64 \
--tag $DOCKER_ORGANIZATION_NAME/$DOCKER_REPOSITORY_NAME:${{ github.event.inputs.version }} \
--tag $DOCKER_ORGANIZATION_NAME/$DOCKER_REPOSITORY_NAME:latest \
--file memgraph_deb.dockerfile \
--push .

24
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,24 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
args: # arguments to configure black
- --line-length=120
- --include='\.pyi?$'
# these folders wont be formatted by black
- --exclude="""\.git |
\.__pycache__|
build|
libs|
.cache"""
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v13.0.0
hooks:
- id: clang-format

View File

@@ -184,7 +184,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \
-Werror=switch -Werror=switch-bool -Werror=return-type \
-Werror=return-stack-address \
-Wno-c99-designator")
-Wno-c99-designator \
-DBOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT")
# Don't omit frame pointer in RelWithDebInfo, for additional callchain debug.
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO

View File

@@ -1 +1 @@
* @gitbuda @antonio2368 @antaljanosbenjamin @kostasrim @jbajic
* @antaljanosbenjamin @kostasrim

View File

@@ -54,6 +54,18 @@ Memgraph is implemented in C/C++ and leverages an in-memory first architecture
to ensure that youre getting the best possible performance consistently and
without surprises. Its also ACID-compliant and highly available.
## :video_game: Memgraph Playground
You don't need to install anything to try out Memgraph. Check out
our **[Memgraph Playground](https://playground.memgraph.com/)** sandboxes in
your browser.
<p align="left">
<a href="https://playground.memgraph.com/">
<img width="450px" alt="Memgraph Playground" src="https://download.memgraph.com/asset/github/memgraph/memgraph-playground.png">
</a>
</p>
## :floppy_disk: Download & Install
### Windows

297
cmake/FindSodium.cmake Normal file
View File

@@ -0,0 +1,297 @@
# Written in 2016 by Henrik Steffen Gaßmann <henrik@gassmann.onl>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
########################################################################
# Tries to find the local libsodium installation.
#
# On Windows the sodium_DIR environment variable is used as a default
# hint which can be overridden by setting the corresponding cmake variable.
#
# Once done the following variables will be defined:
#
# sodium_FOUND
# sodium_INCLUDE_DIR
# sodium_LIBRARY_DEBUG
# sodium_LIBRARY_RELEASE
#
#
# Furthermore an imported "sodium" target is created.
#
if (CMAKE_C_COMPILER_ID STREQUAL "GNU"
OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(_GCC_COMPATIBLE 1)
endif()
# static library option
if (NOT DEFINED sodium_USE_STATIC_LIBS)
option(sodium_USE_STATIC_LIBS "enable to statically link against sodium" OFF)
endif()
if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST))
unset(sodium_LIBRARY CACHE)
unset(sodium_LIBRARY_DEBUG CACHE)
unset(sodium_LIBRARY_RELEASE CACHE)
unset(sodium_DLL_DEBUG CACHE)
unset(sodium_DLL_RELEASE CACHE)
set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL "internal change tracking variable")
endif()
########################################################################
# UNIX
if (UNIX)
# import pkg-config
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_check_modules(sodium_PKG QUIET libsodium)
endif()
if(sodium_USE_STATIC_LIBS)
foreach(_libname ${sodium_PKG_STATIC_LIBRARIES})
if (NOT _libname MATCHES "^lib.*\\.a$") # ignore strings already ending with .a
list(INSERT sodium_PKG_STATIC_LIBRARIES 0 "lib${_libname}.a")
endif()
endforeach()
list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES)
# if pkgconfig for libsodium doesn't provide
# static lib info, then override PKG_STATIC here..
if (NOT sodium_PKG_STATIC_FOUND)
set(sodium_PKG_STATIC_LIBRARIES libsodium.a)
endif()
set(XPREFIX sodium_PKG_STATIC)
else()
if (NOT sodium_PKG_FOUND)
set(sodium_PKG_LIBRARIES sodium)
endif()
set(XPREFIX sodium_PKG)
endif()
find_path(sodium_INCLUDE_DIR sodium.h
HINTS ${${XPREFIX}_INCLUDE_DIRS}
)
find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES}
HINTS ${${XPREFIX}_LIBRARY_DIRS}
)
find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES}
HINTS ${${XPREFIX}_LIBRARY_DIRS}
)
########################################################################
# Windows
elseif (WIN32)
set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory")
mark_as_advanced(sodium_DIR)
find_path(sodium_INCLUDE_DIR sodium.h
HINTS ${sodium_DIR}
PATH_SUFFIXES include
)
if (MSVC)
# detect target architecture
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" [=[
#if defined _M_IX86
#error ARCH_VALUE x86_32
#elif defined _M_X64
#error ARCH_VALUE x86_64
#endif
#error ARCH_VALUE unknown
]=])
try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp"
OUTPUT_VARIABLE _COMPILATION_LOG
)
string(REGEX REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*" "\\1" _TARGET_ARCH "${_COMPILATION_LOG}")
# construct library path
if (_TARGET_ARCH STREQUAL "x86_32")
string(APPEND _PLATFORM_PATH "Win32")
elseif(_TARGET_ARCH STREQUAL "x86_64")
string(APPEND _PLATFORM_PATH "x64")
else()
message(FATAL_ERROR "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.")
endif()
string(APPEND _PLATFORM_PATH "/$$CONFIG$$")
if (MSVC_VERSION LESS 1900)
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60")
else()
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50")
endif()
string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}")
if (sodium_USE_STATIC_LIBS)
string(APPEND _PLATFORM_PATH "/static")
else()
string(APPEND _PLATFORM_PATH "/dynamic")
endif()
string(REPLACE "$$CONFIG$$" "Debug" _DEBUG_PATH_SUFFIX "${_PLATFORM_PATH}")
string(REPLACE "$$CONFIG$$" "Release" _RELEASE_PATH_SUFFIX "${_PLATFORM_PATH}")
find_library(sodium_LIBRARY_DEBUG libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
)
find_library(sodium_LIBRARY_RELEASE libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
)
if (NOT sodium_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
find_library(sodium_DLL_DEBUG libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
)
find_library(sodium_DLL_RELEASE libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK})
endif()
elseif(_GCC_COMPATIBLE)
if (sodium_USE_STATIC_LIBS)
find_library(sodium_LIBRARY_DEBUG libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
find_library(sodium_LIBRARY_RELEASE libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
else()
find_library(sodium_LIBRARY_DEBUG libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
find_library(sodium_LIBRARY_RELEASE libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
file(GLOB _DLL
LIST_DIRECTORIES false
RELATIVE "${sodium_DIR}/bin"
"${sodium_DIR}/bin/libsodium*.dll"
)
find_library(sodium_DLL_DEBUG ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin
)
find_library(sodium_DLL_RELEASE ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin
)
endif()
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
########################################################################
# unsupported
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
########################################################################
# common stuff
# extract sodium version
if (sodium_INCLUDE_DIR)
set(_VERSION_HEADER "${_INCLUDE_DIR}/sodium/version.h")
if (EXISTS _VERSION_HEADER)
file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT)
string(REGEX REPLACE ".*#[ \t]*define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*" "\\1"
sodium_VERSION "${_VERSION_HEADER_CONTENT}")
set(sodium_VERSION "${sodium_VERSION}" PARENT_SCOPE)
endif()
endif()
# communicate results
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
Sodium # The name must be either uppercase or match the filename case.
REQUIRED_VARS
sodium_LIBRARY_RELEASE
sodium_LIBRARY_DEBUG
sodium_INCLUDE_DIR
VERSION_VAR
sodium_VERSION
)
if(Sodium_FOUND)
set(sodium_LIBRARIES
optimized ${sodium_LIBRARY_RELEASE} debug ${sodium_LIBRARY_DEBUG})
endif()
# mark file paths as advanced
mark_as_advanced(sodium_INCLUDE_DIR)
mark_as_advanced(sodium_LIBRARY_DEBUG)
mark_as_advanced(sodium_LIBRARY_RELEASE)
if (WIN32)
mark_as_advanced(sodium_DLL_DEBUG)
mark_as_advanced(sodium_DLL_RELEASE)
endif()
# create imported target
if(sodium_USE_STATIC_LIBS)
set(_LIB_TYPE STATIC)
else()
set(_LIB_TYPE SHARED)
endif()
if(NOT TARGET sodium)
add_library(sodium ${_LIB_TYPE} IMPORTED)
endif()
set_target_properties(sodium PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${sodium_INCLUDE_DIR}"
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
)
if (sodium_USE_STATIC_LIBS)
set_target_properties(sodium PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "SODIUM_STATIC"
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
)
else()
if (UNIX)
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
)
elseif (WIN32)
set_target_properties(sodium PROPERTIES
IMPORTED_IMPLIB "${sodium_LIBRARY_RELEASE}"
IMPORTED_IMPLIB_DEBUG "${sodium_LIBRARY_DEBUG}"
)
if (NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND"))
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION_DEBUG "${sodium_DLL_DEBUG}"
)
endif()
if (NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND"))
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION_RELWITHDEBINFO "${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_MINSIZEREL "${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_RELEASE "${sodium_DLL_RELEASE}"
)
endif()
endif()
endif()

169
cmake/MgThrift.cmake Normal file
View File

@@ -0,0 +1,169 @@
find_package(Gflags)
find_package(Folly)
find_package(wangle)
find_package(fizz)
find_package(proxygen)
find_package(FBThrift)
set(THRIFTCPP2 "FBThrift::thriftcpp2")
set(THRIFT1 ${FBTHRIFT_COMPILER})
include(${FBTHRIFT_INCLUDE_DIR}/thrift/ThriftLibrary.cmake)
set(MG_INTERFACE_TARGET_NAME_PREFIX "mg-interface")
function(_mg_thrift_generate
file_name
services
language
options
file_path
output_path
include_prefix
)
cmake_parse_arguments(THRIFT_GENERATE # Prefix
"" # Options
"" # One Value args
"THRIFT_INCLUDE_DIRECTORIES" # Multi-value args
"${ARGN}")
set(thrift_include_directories)
foreach(dir ${THRIFT_GENERATE_THRIFT_INCLUDE_DIRECTORIES})
list(APPEND thrift_include_directories "-I" "${dir}")
endforeach()
set("${file_name}-${language}-HEADERS"
${output_path}/gen-${language}/${file_name}_constants.h
${output_path}/gen-${language}/${file_name}_data.h
${output_path}/gen-${language}/${file_name}_metadata.h
${output_path}/gen-${language}/${file_name}_types.h
${output_path}/gen-${language}/${file_name}_types.tcc
)
set("${file_name}-${language}-SOURCES"
${output_path}/gen-${language}/${file_name}_constants.cpp
${output_path}/gen-${language}/${file_name}_data.cpp
${output_path}/gen-${language}/${file_name}_types.cpp
)
if(NOT "${options}" MATCHES "no_metadata")
set("${file_name}-${language}-SOURCES"
${${file_name}-${language}-SOURCES}
${output_path}/gen-${language}/${file_name}_metadata.cpp
)
endif()
foreach(service ${services})
set("${file_name}-${language}-HEADERS"
${${file_name}-${language}-HEADERS}
${output_path}/gen-${language}/${service}.h
${output_path}/gen-${language}/${service}.tcc
${output_path}/gen-${language}/${service}AsyncClient.h
${output_path}/gen-${language}/${service}_custom_protocol.h
)
set("${file_name}-${language}-SOURCES"
${${file_name}-${language}-SOURCES}
${output_path}/gen-${language}/${service}.cpp
${output_path}/gen-${language}/${service}AsyncClient.cpp
)
endforeach()
if("${include_prefix}" STREQUAL "")
set(include_prefix_text "")
else()
set(include_prefix_text "include_prefix=${include_prefix}")
if(NOT "${options}" STREQUAL "")
set(include_prefix_text ",${include_prefix_text}")
endif()
endif()
set(gen_language ${language})
if("${language}" STREQUAL "cpp2")
set(gen_language "mstch_cpp2")
elseif("${language}" STREQUAL "py3")
set(gen_language "mstch_py3")
file(WRITE "${output_path}/gen-${language}/${file_name}/__init__.py")
endif()
add_custom_command(
OUTPUT ${${file_name}-${language}-HEADERS}
${${file_name}-${language}-SOURCES}
COMMAND ${THRIFT1}
--gen "${gen_language}:${options}${include_prefix_text}"
-o ${output_path}
${thrift_include_directories}
"${file_path}/${file_name}.thrift"
DEPENDS
${THRIFT1}
"${file_path}/${file_name}.thrift"
COMMENT "Generating ${MG_INTERFACE_TARGET_NAME_PREFIX}-${file_name}-${language} files. Output: ${output_path}"
)
add_custom_target(
${MG_INTERFACE_TARGET_NAME_PREFIX}-${file_name}-${language}-target ALL
DEPENDS ${${language}-${language}-HEADERS}
${${file_name}-${language}-SOURCES}
)
set("${file_name}-${language}-SOURCES" ${${file_name}-${language}-SOURCES} PARENT_SCOPE)
install(
DIRECTORY gen-${language}
DESTINATION include/${include_prefix}
FILES_MATCHING PATTERN "*.h")
install(
DIRECTORY gen-${language}
DESTINATION include/${include_prefix}
FILES_MATCHING PATTERN "*.tcc")
endfunction()
function(_mg_thrift_object
file_name
services
language
options
file_path
output_path
include_prefix
)
_mg_thrift_generate(
"${file_name}"
"${services}"
"${language}"
"${options}"
"${file_path}"
"${output_path}"
"${include_prefix}"
"${ARGN}"
)
bypass_source_check(${${file_name}-${language}-SOURCES})
add_library(
"${MG_INTERFACE_TARGET_NAME_PREFIX}-${file_name}-${language}-obj"
OBJECT
${${file_name}-${language}-SOURCES}
)
add_dependencies(
"${MG_INTERFACE_TARGET_NAME_PREFIX}-${file_name}-${language}-obj"
"${MG_INTERFACE_TARGET_NAME_PREFIX}-${file_name}-${language}-target"
)
target_include_directories(${MG_INTERFACE_TARGET_NAME_PREFIX}-${file_name}-${language}-obj PUBLIC ${output_path})
message(STATUS "MgThrift will create the Object file : ${MG_INTERFACE_TARGET_NAME_PREFIX}-${file_name}-${language}-obj")
endfunction()
function(mg_thrift_library
file_name
services
file_path
output_path
include_prefix
)
_mg_thrift_object(
"${file_name}"
"${services}"
"cpp2"
"stack_arguments" # options
"${file_path}"
"${output_path}"
"${include_prefix}"
THRIFT_INCLUDE_DIRECTORIES "${FBTHRIFT_INCLUDE_DIR}"
)
set(LIBRARY_NAME "${MG_INTERFACE_TARGET_NAME_PREFIX}-${file_name}-cpp2")
add_library(
"${LIBRARY_NAME}"
$<TARGET_OBJECTS:${LIBRARY_NAME}-obj>
)
target_link_libraries("${LIBRARY_NAME}" ${THRIFTCPP2})
message("MgThrift will create the library file : ${LIBRARY_NAME}")
endfunction()

View File

@@ -47,6 +47,14 @@ modifications:
value: ""
override: false
- name: "bolt_cert_file"
value: "/etc/memgraph/ssl/cert.pem"
override: false
- name: "bolt_key_file"
value: "/etc/memgraph/ssl/key.pem"
override: false
- name: "storage_properties_on_edges"
value: "true"
override: true

View File

@@ -106,7 +106,7 @@ install() {
https://repo.ius.io/ius-release-el7.rpm
yum update -y
yum install -y wget python3 python3-pip
yum install -y git224
yum install -y git
for pkg in $1; do
if [ "$pkg" == libipt ]; then
if ! yum list installed libipt >/dev/null 2>/dev/null; then

104
environment/os/debian-11-arm.sh Executable file
View File

@@ -0,0 +1,104 @@
#!/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 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
curl # snappy
file # for libunwind
libssl-dev # for libevent
libgmp-dev
gperf # for proxygen
git # for fbthrift
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 liblzma5 python3 # for gdb
libcurl4 # for cmake
file # for CPack
libreadline8 # for cmake and llvm
libffi7 libxml2 # for llvm
libssl-dev # for libevent
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make pkg-config # build system
curl wget # for downloading libs
uuid-dev default-jre-headless # required by antlr
libreadline-dev # for memgraph console
libpython3-dev python3-dev # for query modules
libssl-dev
libseccomp-dev
netcat # tests are using nc to wait for memgraph
python3 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
golang nodejs npm
autoconf # for jemalloc code generation
libtool # for protobuf 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}"

View File

@@ -52,7 +52,7 @@ MEMGRAPH_BUILD_DEPS=(
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless # for driver tests
golang nodejs npm
dotnet-sdk-3.1 golang nodejs npm
autoconf # for jemalloc code generation
libtool # for protobuf code generation
)

View File

@@ -0,0 +1,12 @@
diff -ur a/thrift/lib/cpp2/server/IOWorkerContext.h b/thrift/lib/cpp2/server/IOWorkerContext.h
--- a/thrift/lib/cpp2/server/IOWorkerContext.h 2022-06-08 11:50:43.043948657 +0200
+++ b/thrift/lib/cpp2/server/IOWorkerContext.h 2022-06-08 11:47:33.232695125 +0200
@@ -59,7 +59,7 @@
auto aliveLocked = alive->rlock();
if (*aliveLocked) {
// IOWorkerContext is still alive and so is replyQueue_
- queue->startConsumingInternal(&evb);
+ queue->startConsuming(&evb);
}
});
}

View File

@@ -5,6 +5,10 @@ operating_system() {
sort | cut -d '=' -f 2- | sed 's/"//g' | paste -s -d '-'
}
architecture() {
uname -m
}
check_all_yum() {
local missing=""
for pkg in $1; do

File diff suppressed because it is too large Load Diff

View File

@@ -40,6 +40,7 @@ class InvalidContextError(Exception):
"""
Signals using a graph element instance outside of the registered procedure.
"""
pass
@@ -47,6 +48,7 @@ class UnknownError(_mgp.UnknownError):
"""
Signals unspecified failure.
"""
pass
@@ -54,6 +56,7 @@ class UnableToAllocateError(_mgp.UnableToAllocateError):
"""
Signals failed memory allocation.
"""
pass
@@ -61,6 +64,7 @@ class InsufficientBufferError(_mgp.InsufficientBufferError):
"""
Signals that some buffer is not big enough.
"""
pass
@@ -69,6 +73,7 @@ class OutOfRangeError(_mgp.OutOfRangeError):
Signals that an index-like parameter has a value that is outside its
possible values.
"""
pass
@@ -77,6 +82,7 @@ class LogicErrorError(_mgp.LogicErrorError):
Signals faulty logic within the program such as violating logical
preconditions or class invariants and may be preventable.
"""
pass
@@ -84,6 +90,7 @@ class DeletedObjectError(_mgp.DeletedObjectError):
"""
Signals accessing an already deleted object.
"""
pass
@@ -91,6 +98,7 @@ class InvalidArgumentError(_mgp.InvalidArgumentError):
"""
Signals that some of the arguments have invalid values.
"""
pass
@@ -98,6 +106,7 @@ class KeyAlreadyExistsError(_mgp.KeyAlreadyExistsError):
"""
Signals that a key already exists in a container-like object.
"""
pass
@@ -105,6 +114,7 @@ class ImmutableObjectError(_mgp.ImmutableObjectError):
"""
Signals modification of an immutable object.
"""
pass
@@ -112,6 +122,7 @@ class ValueConversionError(_mgp.ValueConversionError):
"""
Signals that the conversion failed between python and cypher values.
"""
pass
@@ -120,12 +131,14 @@ class SerializationError(_mgp.SerializationError):
Signals serialization error caused by concurrent modifications from
different transactions.
"""
pass
class Label:
"""Label of a Vertex."""
__slots__ = ('_name',)
__slots__ = ("_name",)
def __init__(self, name: str):
self._name = name
@@ -145,19 +158,22 @@ class Label:
# Named property value of a Vertex or an Edge.
# It would be better to use typing.NamedTuple with typed fields, but that is
# not available in Python 3.5.
Property = namedtuple('Property', ('name', 'value'))
Property = namedtuple("Property", ("name", "value"))
class Properties:
"""
A collection of properties either on a Vertex or an Edge.
"""
__slots__ = ('_vertex_or_edge', '_len',)
__slots__ = (
"_vertex_or_edge",
"_len",
)
def __init__(self, vertex_or_edge):
if not isinstance(vertex_or_edge, (_mgp.Vertex, _mgp.Edge)):
raise TypeError("Expected '_mgp.Vertex' or '_mgp.Edge', \
got {}".format(type(vertex_or_edge)))
raise TypeError("Expected '_mgp.Vertex' or '_mgp.Edge', got {}".format(type(vertex_or_edge)))
self._len = None
self._vertex_or_edge = vertex_or_edge
@@ -330,7 +346,8 @@ class Properties:
class EdgeType:
"""Type of an Edge."""
__slots__ = ('_name',)
__slots__ = ("_name",)
def __init__(self, name):
self._name = name
@@ -348,7 +365,7 @@ class EdgeType:
if sys.version_info >= (3, 5, 2):
EdgeId = typing.NewType('EdgeId', int)
EdgeId = typing.NewType("EdgeId", int)
else:
EdgeId = int
@@ -360,12 +377,12 @@ class Edge:
a query. You should not globally store an instance of an Edge. Using an
invalid Edge instance will raise InvalidContextError.
"""
__slots__ = ('_edge',)
__slots__ = ("_edge",)
def __init__(self, edge):
if not isinstance(edge, _mgp.Edge):
raise TypeError(
"Expected '_mgp.Edge', got '{}'".format(type(edge)))
raise TypeError("Expected '_mgp.Edge', got '{}'".format(type(edge)))
self._edge = edge
def __deepcopy__(self, memo):
@@ -408,7 +425,7 @@ class Edge:
return EdgeType(self._edge.get_type_name())
@property
def from_vertex(self) -> 'Vertex':
def from_vertex(self) -> "Vertex":
"""
Get the source vertex.
@@ -419,7 +436,7 @@ class Edge:
return Vertex(self._edge.from_vertex())
@property
def to_vertex(self) -> 'Vertex':
def to_vertex(self) -> "Vertex":
"""
Get the destination vertex.
@@ -453,7 +470,7 @@ class Edge:
if sys.version_info >= (3, 5, 2):
VertexId = typing.NewType('VertexId', int)
VertexId = typing.NewType("VertexId", int)
else:
VertexId = int
@@ -465,12 +482,12 @@ class Vertex:
in a query. You should not globally store an instance of a Vertex. Using an
invalid Vertex instance will raise InvalidContextError.
"""
__slots__ = ('_vertex',)
__slots__ = ("_vertex",)
def __init__(self, vertex):
if not isinstance(vertex, _mgp.Vertex):
raise TypeError(
"Expected '_mgp.Vertex', got '{}'".format(type(vertex)))
raise TypeError("Expected '_mgp.Vertex', got '{}'".format(type(vertex)))
self._vertex = vertex
def __deepcopy__(self, memo):
@@ -513,8 +530,7 @@ class Vertex:
"""
if not self.is_valid():
raise InvalidContextError()
return tuple(Label(self._vertex.label_at(i))
for i in range(self._vertex.labels_count()))
return tuple(Label(self._vertex.label_at(i)) for i in range(self._vertex.labels_count()))
def add_label(self, label: str) -> None:
"""
@@ -615,7 +631,8 @@ class Vertex:
class Path:
"""Path containing Vertex and Edge instances."""
__slots__ = ('_path', '_vertices', '_edges')
__slots__ = ("_path", "_vertices", "_edges")
def __init__(self, starting_vertex_or_path: typing.Union[_mgp.Path, Vertex]):
"""Initialize with a starting Vertex.
@@ -636,8 +653,7 @@ class Path:
raise InvalidContextError()
self._path = _mgp.Path.make_with_start(vertex)
else:
raise TypeError("Expected '_mgp.Vertex' or '_mgp.Path', got '{}'"
.format(type(starting_vertex_or_path)))
raise TypeError("Expected '_mgp.Vertex' or '_mgp.Path', got '{}'".format(type(starting_vertex_or_path)))
def __copy__(self):
if not self.is_valid():
@@ -678,8 +694,7 @@ class Path:
extension.
"""
if not isinstance(edge, Edge):
raise TypeError(
"Expected '_mgp.Edge', got '{}'".format(type(edge)))
raise TypeError("Expected '_mgp.Edge', got '{}'".format(type(edge)))
if not self.is_valid() or not edge.is_valid():
raise InvalidContextError()
self._path.expand(edge._edge)
@@ -698,8 +713,7 @@ class Path:
raise InvalidContextError()
if self._vertices is None:
num_vertices = self._path.size() + 1
self._vertices = tuple(Vertex(self._path.vertex_at(i))
for i in range(num_vertices))
self._vertices = tuple(Vertex(self._path.vertex_at(i)) for i in range(num_vertices))
return self._vertices
@property
@@ -713,14 +727,14 @@ class Path:
raise InvalidContextError()
if self._edges is None:
num_edges = self._path.size()
self._edges = tuple(Edge(self._path.edge_at(i))
for i in range(num_edges))
self._edges = tuple(Edge(self._path.edge_at(i)) for i in range(num_edges))
return self._edges
class Record:
"""Represents a record of resulting field values."""
__slots__ = ('fields',)
__slots__ = ("fields",)
def __init__(self, **kwargs):
"""Initialize with name=value fields in kwargs."""
@@ -729,12 +743,12 @@ class Record:
class Vertices:
"""Iterable over vertices in a graph."""
__slots__ = ('_graph', '_len')
__slots__ = ("_graph", "_len")
def __init__(self, graph):
if not isinstance(graph, _mgp.Graph):
raise TypeError(
"Expected '_mgp.Graph', got '{}'".format(type(graph)))
raise TypeError("Expected '_mgp.Graph', got '{}'".format(type(graph)))
self._graph = graph
self._len = None
@@ -791,12 +805,12 @@ class Vertices:
class Graph:
"""State of the graph database in current ProcCtx."""
__slots__ = ('_graph',)
__slots__ = ("_graph",)
def __init__(self, graph):
if not isinstance(graph, _mgp.Graph):
raise TypeError(
"Expected '_mgp.Graph', got '{}'".format(type(graph)))
raise TypeError("Expected '_mgp.Graph', got '{}'".format(type(graph)))
self._graph = graph
def __deepcopy__(self, memo):
@@ -885,8 +899,7 @@ class Graph:
raise InvalidContextError()
self._graph.detach_delete_vertex(vertex._vertex)
def create_edge(self, from_vertex: Vertex, to_vertex: Vertex,
edge_type: EdgeType) -> None:
def create_edge(self, from_vertex: Vertex, to_vertex: Vertex, edge_type: EdgeType) -> None:
"""
Create an edge.
@@ -899,8 +912,7 @@ class Graph:
"""
if not self.is_valid():
raise InvalidContextError()
return Edge(self._graph.create_edge(from_vertex._vertex,
to_vertex._vertex, edge_type.name))
return Edge(self._graph.create_edge(from_vertex._vertex, to_vertex._vertex, edge_type.name))
def delete_edge(self, edge: Edge) -> None:
"""
@@ -918,6 +930,7 @@ class Graph:
class AbortError(Exception):
"""Signals that the procedure was asked to abort its execution."""
pass
@@ -927,12 +940,12 @@ class ProcCtx:
Access to a ProcCtx is only valid during a single execution of a procedure
in a query. You should not globally store a ProcCtx instance.
"""
__slots__ = ('_graph',)
__slots__ = ("_graph",)
def __init__(self, graph):
if not isinstance(graph, _mgp.Graph):
raise TypeError(
"Expected '_mgp.Graph', got '{}'".format(type(graph)))
raise TypeError("Expected '_mgp.Graph', got '{}'".format(type(graph)))
self._graph = Graph(graph)
def is_valid(self) -> bool:
@@ -969,8 +982,7 @@ LocalDateTime = datetime.datetime
Duration = datetime.timedelta
Any = typing.Union[bool, str, Number, Map, Path,
list, Date, LocalTime, LocalDateTime, Duration]
Any = typing.Union[bool, str, Number, Map, Path, list, Date, LocalTime, LocalDateTime, Duration]
List = typing.List
@@ -1003,7 +1015,7 @@ def _typing_to_cypher_type(type_):
Date: _mgp.type_date(),
LocalTime: _mgp.type_local_time(),
LocalDateTime: _mgp.type_local_date_time(),
Duration: _mgp.type_duration()
Duration: _mgp.type_duration(),
}
try:
return simple_types[type_]
@@ -1021,14 +1033,14 @@ def _typing_to_cypher_type(type_):
if type(None) in type_args:
types = tuple(t for t in type_args if t is not type(None)) # noqa E721
if len(types) == 1:
type_arg, = types
(type_arg,) = types
else:
# We cannot do typing.Union[*types], so do the equivalent
# with __getitem__ which does not even need arg unpacking.
type_arg = typing.Union.__getitem__(types)
return _mgp.type_nullable(_typing_to_cypher_type(type_arg))
elif complex_type == list:
type_arg, = type_args
(type_arg,) = type_args
return _mgp.type_list(_typing_to_cypher_type(type_arg))
raise UnsupportedTypingError(type_)
else:
@@ -1038,13 +1050,17 @@ def _typing_to_cypher_type(type_):
# printed the same way. `typing.List[type]` is printed as such, while
# `typing.Optional[type]` is printed as 'typing.Union[type, NoneType]'
def parse_type_args(type_as_str):
return tuple(map(str.strip,
type_as_str[type_as_str.index('[') + 1: -1].split(',')))
return tuple(
map(
str.strip,
type_as_str[type_as_str.index("[") + 1 : -1].split(","),
)
)
def fully_qualified_name(cls):
if cls.__module__ is None or cls.__module__ == 'builtins':
if cls.__module__ is None or cls.__module__ == "builtins":
return cls.__name__
return cls.__module__ + '.' + cls.__name__
return cls.__module__ + "." + cls.__name__
def get_simple_type(type_as_str):
for simple_type, cypher_type in simple_types.items():
@@ -1060,28 +1076,26 @@ def _typing_to_cypher_type(type_):
pass
def parse_typing(type_as_str):
if type_as_str.startswith('typing.Union'):
if type_as_str.startswith("typing.Union"):
type_args_as_str = parse_type_args(type_as_str)
none_type_as_str = type(None).__name__
if none_type_as_str in type_args_as_str:
types = tuple(
t for t in type_args_as_str if t != none_type_as_str)
types = tuple(t for t in type_args_as_str if t != none_type_as_str)
if len(types) == 1:
type_arg_as_str, = types
(type_arg_as_str,) = types
else:
type_arg_as_str = 'typing.Union[' + \
', '.join(types) + ']'
type_arg_as_str = "typing.Union[" + ", ".join(types) + "]"
simple_type = get_simple_type(type_arg_as_str)
if simple_type is not None:
return _mgp.type_nullable(simple_type)
return _mgp.type_nullable(parse_typing(type_arg_as_str))
elif type_as_str.startswith('typing.List'):
elif type_as_str.startswith("typing.List"):
type_arg_as_str = parse_type_args(type_as_str)
if len(type_arg_as_str) > 1:
# Nested object could be a type consisting of a list of types (e.g. mgp.Map)
# so we need to join the parts.
type_arg_as_str = ', '.join(type_arg_as_str)
type_arg_as_str = ", ".join(type_arg_as_str)
else:
type_arg_as_str = type_arg_as_str[0]
@@ -1096,9 +1110,11 @@ def _typing_to_cypher_type(type_):
# Procedure registration
class Deprecated:
"""Annotate a resulting Record's field as deprecated."""
__slots__ = ('field_type',)
__slots__ = ("field_type",)
def __init__(self, type_):
self.field_type = type_
@@ -1106,8 +1122,7 @@ class Deprecated:
def raise_if_does_not_meet_requirements(func: typing.Callable[..., Record]):
if not callable(func):
raise TypeError("Expected a callable object, got an instance of '{}'"
.format(type(func)))
raise TypeError("Expected a callable object, got an instance of '{}'".format(type(func)))
if inspect.iscoroutinefunction(func):
raise TypeError("Callable must not be 'async def' function")
if sys.version_info >= (3, 6):
@@ -1117,24 +1132,25 @@ def raise_if_does_not_meet_requirements(func: typing.Callable[..., Record]):
raise NotImplementedError("Generator functions are not supported")
def _register_proc(func: typing.Callable[..., Record],
is_write: bool):
def _register_proc(func: typing.Callable[..., Record], is_write: bool):
raise_if_does_not_meet_requirements(func)
register_func = (
_mgp.Module.add_write_procedure if is_write
else _mgp.Module.add_read_procedure)
register_func = _mgp.Module.add_write_procedure if is_write else _mgp.Module.add_read_procedure
sig = inspect.signature(func)
params = tuple(sig.parameters.values())
if params and params[0].annotation is ProcCtx:
@wraps(func)
def wrapper(graph, args):
return func(ProcCtx(graph), *args)
params = params[1:]
mgp_proc = register_func(_mgp._MODULE, wrapper)
else:
@wraps(func)
def wrapper(graph, args):
return func(*args)
mgp_proc = register_func(_mgp._MODULE, wrapper)
for param in params:
name = param.name
@@ -1149,8 +1165,7 @@ def _register_proc(func: typing.Callable[..., Record],
if sig.return_annotation is not sig.empty:
record = sig.return_annotation
if not isinstance(record, Record):
raise TypeError("Expected '{}' to return 'mgp.Record', got '{}'"
.format(func.__name__, type(record)))
raise TypeError("Expected '{}' to return 'mgp.Record', got '{}'".format(func.__name__, type(record)))
for name, type_ in record.fields.items():
if isinstance(type_, Deprecated):
cypher_type = _typing_to_cypher_type(type_.field_type)
@@ -1164,16 +1179,15 @@ def read_proc(func: typing.Callable[..., Record]):
"""
Register `func` as a read-only procedure of the current module.
`read_proc` is meant to be used as a decorator function to register module
procedures. The registered `func` needs to be a callable which optionally
takes `ProcCtx` as the first argument. Other arguments of `func` will be
bound to values passed in the cypherQuery. The full signature of `func`
needs to be annotated with types. The return type must be
`Record(field_name=type, ...)` and the procedure must produce either a
complete Record or None. To mark a field as deprecated, use
`Record(field_name=Deprecated(type), ...)`. Multiple records can be
produced by returning an iterable of them. Registering generator functions
is currently not supported.
The decorator `read_proc` is meant to be used to register module procedures.
The registered `func` needs to be a callable which optionally takes
`ProcCtx` as its first argument. Other arguments of `func` will be bound to
values passed in the cypherQuery. The full signature of `func` needs to be
annotated with types. The return type must be `Record(field_name=type, ...)`
and the procedure must produce either a complete Record or None. To mark a
field as deprecated, use `Record(field_name=Deprecated(type), ...)`.
Multiple records can be produced by returning an iterable of them.
Registering generator functions is currently not supported.
Example usage.
@@ -1207,16 +1221,16 @@ def write_proc(func: typing.Callable[..., Record]):
"""
Register `func` as a writeable procedure of the current module.
`write_proc` is meant to be used as a decorator function to register module
The decorator `write_proc` is meant to be used to register module
procedures. The registered `func` needs to be a callable which optionally
takes `ProcCtx` as the first argument. Other arguments of `func` will be
bound to values passed in the cypherQuery. The full signature of `func`
needs to be annotated with types. The return type must be
`Record(field_name=type, ...)` and the procedure must produce either a
complete Record or None. To mark a field as deprecated, use
`Record(field_name=Deprecated(type), ...)`. Multiple records can be
produced by returning an iterable of them. Registering generator functions
is currently not supported.
`Record(field_name=Deprecated(type), ...)`. Multiple records can be produced
by returning an iterable of them. Registering generator functions is
currently not supported.
Example usage.
@@ -1257,20 +1271,22 @@ class InvalidMessageError(Exception):
"""
Signals using a message instance outside of the registered transformation.
"""
pass
SOURCE_TYPE_KAFKA = _mgp.SOURCE_TYPE_KAFKA
SOURCE_TYPE_PULSAR = _mgp.SOURCE_TYPE_PULSAR
class Message:
"""Represents a message from a stream."""
__slots__ = ('_message',)
__slots__ = ("_message",)
def __init__(self, message):
if not isinstance(message, _mgp.Message):
raise TypeError(
"Expected '_mgp.Message', got '{}'".format(type(message)))
raise TypeError("Expected '_mgp.Message', got '{}'".format(type(message)))
self._message = message
def __deepcopy__(self, memo):
@@ -1353,17 +1369,18 @@ class Message:
class InvalidMessagesError(Exception):
"""Signals using a messages instance outside of the registered transformation."""
pass
class Messages:
"""Represents a list of messages from a stream."""
__slots__ = ('_messages',)
__slots__ = ("_messages",)
def __init__(self, messages):
if not isinstance(messages, _mgp.Messages):
raise TypeError(
"Expected '_mgp.Messages', got '{}'".format(type(messages)))
raise TypeError("Expected '_mgp.Messages', got '{}'".format(type(messages)))
self._messages = messages
def __deepcopy__(self, memo):
@@ -1395,12 +1412,12 @@ class TransCtx:
Access to a TransCtx is only valid during a single execution of a transformation.
You should not globally store a TransCtx instance.
"""
__slots__ = ('_graph')
__slots__ = "_graph"
def __init__(self, graph):
if not isinstance(graph, _mgp.Graph):
raise TypeError(
"Expected '_mgp.Graph', got '{}'".format(type(graph)))
raise TypeError("Expected '_mgp.Graph', got '{}'".format(type(graph)))
self._graph = Graph(graph)
def is_valid(self) -> bool:
@@ -1420,21 +1437,116 @@ def transformation(func: typing.Callable[..., Record]):
params = tuple(sig.parameters.values())
if not params or not params[0].annotation is Messages:
if not len(params) == 2 or not params[1].annotation is Messages:
raise NotImplementedError(
"Valid signatures for transformations are (TransCtx, Messages) or (Messages)")
raise NotImplementedError("Valid signatures for transformations are (TransCtx, Messages) or (Messages)")
if params[0].annotation is TransCtx:
@wraps(func)
def wrapper(graph, messages):
return func(TransCtx(graph), messages)
_mgp._MODULE.add_transformation(wrapper)
else:
@wraps(func)
def wrapper(graph, messages):
return func(messages)
_mgp._MODULE.add_transformation(wrapper)
return func
class FuncCtx:
"""Context of a function being executed.
Access to a FuncCtx is only valid during a single execution of a function in
a query. You should not globally store a FuncCtx instance. The graph object
within the FuncCtx is not mutable.
"""
__slots__ = "_graph"
def __init__(self, graph):
if not isinstance(graph, _mgp.Graph):
raise TypeError("Expected '_mgp.Graph', got '{}'".format(type(graph)))
self._graph = Graph(graph)
def is_valid(self) -> bool:
return self._graph.is_valid()
def function(func: typing.Callable):
"""
Register `func` as a user-defined function in the current module.
The decorator `function` is meant to be used to register module functions.
The registered `func` needs to be a callable which optionally takes
`FuncCtx` as its first argument. Other arguments of `func` will be bound to
values passed in the Cypher query. Only the funcion arguments need to be
annotated with types. The return type doesn't need to be specified, but it
has to be supported by `mgp.Any`. Registering generator functions is
currently not supported.
Example usage.
```
import mgp
@mgp.function
def func_example(context: mgp.FuncCtx,
required_arg: str,
optional_arg: mgp.Nullable[str] = None
):
return_args = [required_arg]
if optional_arg is not None:
return_args.append(optional_arg)
# Return any kind of result supported by mgp.Any
return return_args
```
The example function above returns a list of provided arguments:
* `required_arg` is always present and its value is the first argument of
the function.
* `optional_arg` is present if the second argument of the function is not
`null`.
Any errors can be reported by raising an Exception.
The function can be invoked in Cypher using the following calls:
RETURN example.func_example("first argument", "second_argument");
RETURN example.func_example("first argument");
Naturally, you may pass in different arguments.
"""
raise_if_does_not_meet_requirements(func)
register_func = _mgp.Module.add_function
sig = inspect.signature(func)
params = tuple(sig.parameters.values())
if params and params[0].annotation is FuncCtx:
@wraps(func)
def wrapper(graph, args):
return func(FuncCtx(graph), *args)
params = params[1:]
mgp_func = register_func(_mgp._MODULE, wrapper)
else:
@wraps(func)
def wrapper(graph, args):
return func(*args)
mgp_func = register_func(_mgp._MODULE, wrapper)
for param in params:
name = param.name
type_ = param.annotation
if type_ is param.empty:
type_ = object
cypher_type = _typing_to_cypher_type(type_)
if param.default is param.empty:
mgp_func.add_arg(name, cypher_type)
else:
mgp_func.add_opt_arg(name, cypher_type, param.default)
return func
def _wrap_exceptions():
def wrap_function(func):
@wraps(func)
@@ -1463,6 +1575,7 @@ def _wrap_exceptions():
raise ValueConversionError(e)
except _mgp.SerializationError as e:
raise SerializationError(e)
return wrapped_func
def wrap_prop_func(func):
@@ -1473,11 +1586,16 @@ def _wrap_exceptions():
if inspect.isfunction(obj):
setattr(cls, name, wrap_function(obj))
elif isinstance(obj, property):
setattr(cls, name, property(
wrap_prop_func(obj.fget),
wrap_prop_func(obj.fset),
wrap_prop_func(obj.fdel),
obj.__doc__))
setattr(
cls,
name,
property(
wrap_prop_func(obj.fget),
wrap_prop_func(obj.fset),
wrap_prop_func(obj.fdel),
obj.__doc__,
),
)
def defined_in_this_module(obj: object):
return getattr(obj, "__module__", "") == __name__

14
init
View File

@@ -65,8 +65,14 @@ else
fi
DISTRO=$(operating_system)
echo "ALL BUILD PACKAGES: $($DIR/environment/os/$DISTRO.sh list MEMGRAPH_BUILD_DEPS)"
$DIR/environment/os/$DISTRO.sh check MEMGRAPH_BUILD_DEPS
ARCHITECTURE=$(architecture)
if [ "${ARCHITECTURE}" = "arm64" ]; then
OS_SCRIPT=$DIR/environment/os/$DISTRO-arm.sh
else
OS_SCRIPT=$DIR/environment/os/$DISTRO.sh
fi
echo "ALL BUILD PACKAGES: $($OS_SCRIPT list MEMGRAPH_BUILD_DEPS)"
$OS_SCRIPT check MEMGRAPH_BUILD_DEPS
echo "All packages are in-place..."
# create a default build directory
@@ -129,3 +135,7 @@ for hook in $(find $DIR/.githooks -type f -printf "%f\n"); do
ln -s -f "$DIR/.githooks/$hook" "$DIR/.git/hooks/$hook"
echo "Added $hook hook"
done;
# Install precommit hook
python3 -m pip install pre-commit
python3 -m pre_commit install

View File

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

View File

@@ -10,12 +10,12 @@ set(CPACK_PACKAGE_VENDOR "Memgraph Ltd.")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
"High performance, in-memory, transactional graph database")
set(MG_ARCH_EXTENSION "noarch")
# Setting arhitecture extension for deb packages
set(MG_ARCH_EXTENSION_DEB "all")
if (${MG_ARCH} STREQUAL "x86_64")
set(MG_ARCH_EXTENSION "x86_64")
set(MG_ARCH_EXTENSION_DEB "amd64")
elseif (${MG_ARCH} STREQUAL "ARM64")
set(MG_ARCH_EXTENSION "aarch64")
set(MG_ARCH_EXTENSION_DEB "arm64")
endif()
# DEB specific
@@ -25,7 +25,7 @@ set(CPACK_DEBIAN_PACKAGE_MAINTAINER "tech@memgraph.com (Memgraph Ltd.)")
set(CPACK_DEBIAN_PACKAGE_SECTION non-free/database)
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE https://memgraph.com)
set(CPACK_DEBIAN_PACKAGE_VERSION "${MEMGRAPH_VERSION_DEB}")
set(CPACK_DEBIAN_FILE_NAME "memgraph_${MEMGRAPH_VERSION_DEB}_${MG_ARCH_EXTENSION}.deb")
set(CPACK_DEBIAN_FILE_NAME "memgraph_${MEMGRAPH_VERSION_DEB}_${MG_ARCH_EXTENSION_DEB}.deb")
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
"${CMAKE_CURRENT_SOURCE_DIR}/debian/conffiles;"
"${CMAKE_CURRENT_SOURCE_DIR}/debian/copyright;"
@@ -41,13 +41,20 @@ set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}
applications driver by real-time connected data.")
# Add `openssl` package to dependencies list. Used to generate SSL certificates.
# We also depend on `python3` because we embed it in Memgraph.
set(CPACK_DEBIAN_PACKAGE_DEPENDS "openssl (>= 1.1.0), python3 (>= 3.5.0)")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "openssl (>= 1.1.0), python3 (>= 3.5.0), libstdc++6")
# Setting arhitecture extension for rpm packages
set(MG_ARCH_EXTENSION_RPM "noarch")
if (${MG_ARCH} STREQUAL "x86_64")
set(MG_ARCH_EXTENSION_RPM "x86_64")
elseif (${MG_ARCH} STREQUAL "ARM64")
set(MG_ARCH_EXTENSION_RPM "aarch64")
endif()
# RPM specific
set(CPACK_RPM_PACKAGE_URL https://memgraph.com)
set(CPACK_RPM_PACKAGE_VERSION "${MEMGRAPH_VERSION_RPM}")
set(CPACK_RPM_FILE_NAME "memgraph-${MEMGRAPH_VERSION_RPM}-1.${MG_ARCH_EXTENSION}.rpm")
set(CPACK_RPM_FILE_NAME "memgraph-${MEMGRAPH_VERSION_RPM}-1.${MG_ARCH_EXTENSION_RPM}.rpm")
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
/var /var/lib /var/log /etc/logrotate.d
/lib /lib/systemd /lib/systemd/system /lib/systemd/system/memgraph.service)
@@ -60,7 +67,7 @@ It aims to deliver developers the speed, simplicity and scale required to build
the next generation of applications driver by real-time connected data.")
# Add `openssl` package to dependencies list. Used to generate SSL certificates.
# We also depend on `python3` because we embed it in Memgraph.
set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.0.0, curl >= 7.29.0, python3 >= 3.5.0")
set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.0.0, curl >= 7.29.0, python3 >= 3.5.0, libstdc >= 6")
# All variables must be set before including.
include(CPack)

View File

@@ -1,19 +1,19 @@
FROM debian:bullseye
FROM dokken/centos-stream-9
ARG env_folder
ARG toolchain_version
COPY ${env_folder} /env_folder
RUN apt update && apt install -y curl git
RUN yum update && yum install -y curl git
RUN /${env_folder}/os/debian-11-arm.sh install MEMGRAPH_BUILD_DEPS
RUN /${env_folder}/os/debian-11-arm.sh install TOOLCHAIN_RUN_DEPS
RUN /${env_folder}/os/centos-9.sh install MEMGRAPH_BUILD_DEPS
RUN /${env_folder}/os/centos-9.sh install TOOLCHAIN_RUN_DEPS
RUN rm -rf /env_folder
RUN apt clean
RUN yum clean all
RUN curl https://s3.eu-west-1.amazonaws.com/deps.memgraph.io/${toolchain_version}/${toolchain_version}-binaries-debian-11-aarch64.tar.gz -o /tmp/toolchain.tar.gz \
RUN curl https://s3.eu-west-1.amazonaws.com/deps.memgraph.io/${toolchain_version}/${toolchain_version}-binaries-centos-9-arm64.tar.gz -o /tmp/toolchain.tar.gz \
&& tar xvzf /tmp/toolchain.tar.gz -C /opt \
&& rm /tmp/toolchain.tar.gz

View File

@@ -1,19 +1,21 @@
FROM debian:bullseye
# NOTE: If you change the base distro update release/package as well.
ARG release
ARG BINARY_NAME
ARG EXTENSION
ARG TARGETARCH
RUN apt-get update && apt-get install -y \
openssl libcurl4 libssl1.1 libseccomp2 python3 libpython3.9 python3-pip \
--no-install-recommends \
openssl libcurl4 libssl1.1 libseccomp2 python3 libpython3.9 python3-pip \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN pip3 install networkx==2.4 numpy==1.21.4 scipy==1.7.3
COPY ${release} /
COPY "${BINARY_NAME}${TARGETARCH}.${EXTENSION}" /
# Install memgraph package
RUN dpkg -i ${release}
RUN dpkg -i "${BINARY_NAME}${TARGETARCH}.deb"
# Memgraph listens for Bolt Protocol on this port by default.
EXPOSE 7687

View File

@@ -55,7 +55,10 @@ image_name="memgraph:${version}"
image_package_name="memgraph-${version}-docker.tar.gz"
# Build docker image.
docker build -t ${image_name} ${tag_latest} -f ${dockerfile_path} --build-arg release=${package_name}.${extension} .
docker build -t ${image_name} ${tag_latest} -f ${dockerfile_path} \
--build-arg BINARY_NAME=${package_name} \
--build-arg EXTENSION=${extension} \
--build-arg TARGETARCH="" .
docker save ${image_name} ${latest_image} | gzip > ${image_package_name}
rm "${package_name}.${extension}"
echo "Built Docker image at '${working_dir}/${image_package_name}'"

View File

@@ -1,6 +1,7 @@
# CMake configuration for the main memgraph library and executable
# add memgraph sub libraries, ordered by dependency
add_subdirectory(interface)
add_subdirectory(lisp)
add_subdirectory(utils)
add_subdirectory(requests)
@@ -37,7 +38,7 @@ set(mg_single_node_v2_sources
)
set(mg_single_node_v2_libs stdc++fs Threads::Threads
telemetry_lib mg-query mg-communication mg-memory mg-utils mg-auth mg-license mg-settings)
telemetry_lib mg-query mg-communication mg-memory mg-utils mg-auth mg-license mg-settings mg-interface-storage-cpp2)
if (MG_ENTERPRISE)
# These are enterprise subsystems
set(mg_single_node_v2_libs ${mg_single_node_v2_libs} mg-audit)

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -18,7 +18,7 @@
#include "utils/logging.hpp"
#include "utils/string.hpp"
namespace audit {
namespace memgraph::audit {
// Helper function that converts a `storage::PropertyValue` to `nlohmann::json`.
inline nlohmann::json PropertyValueToJson(const storage::PropertyValue &pv) {
@@ -143,4 +143,4 @@ void Log::Flush() {
log_.Sync();
}
} // namespace audit
} // namespace memgraph::audit

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -17,7 +17,7 @@
#include "utils/file.hpp"
#include "utils/scheduler.hpp"
namespace audit {
namespace memgraph::audit {
const uint64_t kBufferSizeDefault = 100000;
const uint64_t kBufferFlushIntervalMillisDefault = 200;
@@ -71,4 +71,4 @@ class Log {
std::mutex lock_;
};
} // namespace audit
} // namespace memgraph::audit

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -42,8 +42,7 @@ DEFINE_VALIDATED_int32(auth_module_timeout_ms, 10000,
"response from the auth module.",
FLAG_IN_RANGE(100, 1800000));
namespace auth {
namespace memgraph::auth {
const std::string kUserPrefix = "user:";
const std::string kRolePrefix = "role:";
const std::string kLinkPrefix = "link:";
@@ -316,4 +315,4 @@ std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig)
return ret;
}
} // namespace auth
} // namespace memgraph::auth

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -18,8 +18,7 @@
#include "kvstore/kvstore.hpp"
#include "utils/settings.hpp"
namespace auth {
namespace memgraph::auth {
/**
* This class serves as the main Authentication/Authorization storage.
* It provides functions for managing Users, Roles and Permissions.
@@ -163,4 +162,4 @@ class Auth final {
kvstore::KVStore storage_;
auth::Module module_;
};
} // namespace auth
} // namespace memgraph::auth

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -12,8 +12,7 @@
#include "auth/exceptions.hpp"
namespace auth {
namespace memgraph::auth {
const std::string EncryptPassword(const std::string &password) {
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
@@ -40,4 +39,4 @@ bool VerifyPassword(const std::string &password, const std::string &hash) {
return ret == 0;
}
} // namespace auth
} // namespace memgraph::auth

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -10,12 +10,11 @@
#include <string>
namespace auth {
namespace memgraph::auth {
/// @throw AuthException if unable to encrypt the password.
const std::string EncryptPassword(const std::string &password);
/// @throw AuthException if unable to verify the password.
bool VerifyPassword(const std::string &password, const std::string &hash);
} // namespace auth
} // namespace memgraph::auth

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,8 +13,7 @@
#include "utils/exceptions.hpp"
namespace auth {
namespace memgraph::auth {
/**
* This exception class is thrown for all exceptions that can occur when dealing
* with the Auth library.
@@ -23,4 +22,4 @@ class AuthException : public utils::BasicException {
public:
using utils::BasicException::BasicException;
};
} // namespace auth
} // namespace memgraph::auth

View File

@@ -22,14 +22,13 @@
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_bool(auth_password_permit_null, true, "Set to false to disable null passwords.");
constexpr std::string_view default_password_regex = ".+";
inline constexpr std::string_view default_password_regex = ".+";
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_string(auth_password_strength_regex, default_password_regex.data(),
"The regular expression that should be used to match the entire "
"entered password to ensure its strength.");
namespace auth {
namespace memgraph::auth {
namespace {
// Constant list of all available permissions.
const std::vector<Permission> kPermissionsAll = {
@@ -303,4 +302,4 @@ bool operator==(const User &first, const User &second) {
return first.username_ == second.username_ && first.password_hash_ == second.password_hash_ &&
first.permissions_ == second.permissions_ && first.role_ == second.role_;
}
} // namespace auth
} // namespace memgraph::auth

View File

@@ -13,8 +13,7 @@
#include <json/json.hpp>
namespace auth {
namespace memgraph::auth {
// These permissions must have values that are applicable for usage in a
// bitmask.
// clang-format off
@@ -154,4 +153,4 @@ class User final {
};
bool operator==(const User &first, const User &second);
} // namespace auth
} // namespace memgraph::auth

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -153,7 +153,7 @@ int Target(void *arg) {
// process and something really bad could happen.
// Get a pointer to the passed arguments.
auto *ta = reinterpret_cast<auth::TargetArguments *>(arg);
auto *ta = reinterpret_cast<memgraph::auth::TargetArguments *>(arg);
// Redirect `stdin` to `/dev/null`.
int fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
@@ -312,8 +312,7 @@ nlohmann::json GetData(int fd, int timeout_millisec) {
} // namespace
namespace auth {
namespace memgraph::auth {
Module::Module(const std::filesystem::path &module_executable_path) {
if (!module_executable_path.empty()) {
module_executable_path_ = std::filesystem::absolute(module_executable_path);
@@ -447,4 +446,4 @@ void Module::Shutdown() {
Module::~Module() { Shutdown(); }
} // namespace auth
} // namespace memgraph::auth

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -16,8 +16,7 @@
#include <json/json.hpp>
namespace auth {
namespace memgraph::auth {
struct TargetArguments {
std::filesystem::path module_executable_path;
int pipe_to_module{-1};
@@ -70,4 +69,4 @@ class Module final {
int pipe_from_module_[2] = {-1, -1};
};
} // namespace auth
} // namespace memgraph::auth

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -21,7 +21,7 @@
#include "utils/exceptions.hpp"
#include "utils/logging.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/// This exception is thrown whenever an error occurs during query execution
/// that isn't fatal (eg. mistyped query or some transient error occurred).
@@ -315,4 +315,4 @@ class Client final {
ChunkedEncoderBuffer<communication::ClientOutputStream> encoder_buffer_{output_stream_};
ClientEncoder<ChunkedEncoderBuffer<communication::ClientOutputStream>> encoder_{encoder_buffer_};
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,10 +13,10 @@
#include <cstdint>
namespace communication::bolt {
namespace memgraph::communication::bolt {
static constexpr uint8_t kPreamble[4] = {0x60, 0x60, 0xB0, 0x17};
static constexpr uint8_t kProtocol[4] = {0x00, 0x00, 0x00, 0x01};
inline constexpr uint8_t kPreamble[4] = {0x60, 0x60, 0xB0, 0x17};
inline constexpr uint8_t kProtocol[4] = {0x00, 0x00, 0x00, 0x01};
enum class Signature : uint8_t {
Noop = 0x00,
@@ -95,9 +95,9 @@ enum class Marker : uint8_t {
Struct16 = 0xDD,
};
static constexpr uint8_t MarkerString = 0, MarkerList = 1, MarkerMap = 2;
static constexpr Marker MarkerTiny[3] = {Marker::TinyString, Marker::TinyList, Marker::TinyMap};
static constexpr Marker Marker8[3] = {Marker::String8, Marker::List8, Marker::Map8};
static constexpr Marker Marker16[3] = {Marker::String16, Marker::List16, Marker::Map16};
static constexpr Marker Marker32[3] = {Marker::String32, Marker::List32, Marker::Map32};
} // namespace communication::bolt
inline constexpr uint8_t MarkerString = 0, MarkerList = 1, MarkerMap = 2;
inline constexpr Marker MarkerTiny[3] = {Marker::TinyString, Marker::TinyList, Marker::TinyMap};
inline constexpr Marker Marker8[3] = {Marker::String8, Marker::List8, Marker::Map8};
inline constexpr Marker Marker16[3] = {Marker::String16, Marker::List16, Marker::Map16};
inline constexpr Marker Marker32[3] = {Marker::String32, Marker::List32, Marker::Map32};
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -14,22 +14,22 @@
#include <cstddef>
#include <cstdint>
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* Sizes related to the chunk defined in Bolt protocol.
*/
static constexpr size_t kChunkHeaderSize = 2;
static constexpr size_t kChunkMaxDataSize = 65535;
static constexpr size_t kChunkWholeSize = kChunkHeaderSize + kChunkMaxDataSize;
inline constexpr size_t kChunkHeaderSize = 2;
inline constexpr size_t kChunkMaxDataSize = 65535;
inline constexpr size_t kChunkWholeSize = kChunkHeaderSize + kChunkMaxDataSize;
/**
* Handshake size defined in the Bolt protocol.
*/
static constexpr size_t kHandshakeSize = 20;
inline constexpr size_t kHandshakeSize = 20;
static constexpr uint16_t kSupportedVersions[] = {0x0100, 0x0400, 0x0401, 0x0403};
inline constexpr uint16_t kSupportedVersions[] = {0x0100, 0x0400, 0x0401, 0x0403};
static constexpr int kPullAll = -1;
static constexpr int kPullLast = -1;
} // namespace communication::bolt
inline constexpr int kPullAll = -1;
inline constexpr int kPullLast = -1;
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -20,7 +20,7 @@
#include "communication/bolt/v1/constants.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* This class is used as the return value of the GetChunk function of the
@@ -136,4 +136,4 @@ class ChunkedDecoderBuffer {
std::vector<uint8_t> data_;
size_t pos_{0};
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -22,7 +22,7 @@
#include "utils/logging.hpp"
#include "utils/temporal.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* Bolt Decoder.
@@ -591,4 +591,4 @@ class Decoder {
return true;
}
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -22,7 +22,7 @@ static_assert(std::is_same_v<std::uint8_t, char> || std::is_same_v<std::uint8_t,
"communication::bolt::Encoder requires uint8_t to be "
"implemented as char or unsigned char.");
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* Bolt BaseEncoder. Has public interfaces for writing Bolt encoded data.
@@ -273,4 +273,4 @@ class BaseEncoder {
}
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -18,7 +18,7 @@
#include "communication/bolt/v1/constants.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* @brief ChunkedEncoderBuffer
@@ -123,4 +123,4 @@ class ChunkedEncoderBuffer {
// Amount of data in chunk array.
size_t have_{0};
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -14,7 +14,7 @@
#include "communication/bolt/v1/codes.hpp"
#include "communication/bolt/v1/encoder/base_encoder.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* Bolt Client Encoder.
@@ -169,4 +169,4 @@ class ClientEncoder : private BaseEncoder<Buffer> {
return buffer_.Flush();
}
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -14,7 +14,7 @@
#include "communication/bolt/v1/codes.hpp"
#include "communication/bolt/v1/encoder/base_encoder.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* Bolt Encoder.
@@ -158,4 +158,4 @@ class Encoder : private BaseEncoder<Buffer> {
return buffer_.Flush();
}
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -17,7 +17,7 @@
#include "utils/exceptions.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* Used to indicate something is wrong with the client but the transaction is
@@ -83,4 +83,4 @@ class VerboseError : public utils::BasicException {
std::string code_;
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -27,7 +27,7 @@
#include "utils/exceptions.hpp"
#include "utils/logging.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* Bolt Session Exception
@@ -195,4 +195,4 @@ class Session {
}
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,7 +13,7 @@
#include <cstdint>
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* This class represents states in execution of the Bolt protocol.
@@ -55,4 +55,4 @@ enum class State : uint8_t {
*/
Close
};
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -20,7 +20,7 @@
#include "utils/likely.hpp"
#include "utils/logging.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/**
* Error state run function
@@ -95,4 +95,4 @@ State StateErrorRun(TSession &session, State state) {
return state;
}
}
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -26,7 +26,7 @@
#include "utils/logging.hpp"
#include "utils/message.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
template <typename TSession>
State RunHandlerV1(Signature signature, TSession &session, State state, Marker marker) {
@@ -118,4 +118,4 @@ State StateExecutingRun(TSession &session, State state) {
return State::Close;
}
}
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -24,7 +24,7 @@
#include "utils/logging.hpp"
#include "utils/message.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
// TODO: Revise these error messages
inline std::pair<std::string, std::string> ExceptionToErrorMessage(const std::exception &e) {
if (const auto *verbose = dynamic_cast<const VerboseError *>(&e)) {
@@ -415,4 +415,4 @@ State HandleRoute(TSession &session) {
}
return State::Error;
}
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -22,7 +22,7 @@
#include "utils/likely.hpp"
#include "utils/logging.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
inline bool CopyProtocolInformationIfSupported(uint16_t version, uint8_t *protocol) {
const auto *supported_version = std::find(std::begin(kSupportedVersions), std::end(kSupportedVersions), version);
@@ -110,4 +110,4 @@ State StateHandshakeRun(TSession &session) {
return State::Init;
}
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -21,7 +21,7 @@
#include "utils/likely.hpp"
#include "utils/logging.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
namespace details {
template <typename TSession>
@@ -212,4 +212,4 @@ State StateInitRun(TSession &session) {
spdlog::trace("Unsupported bolt version:{}.{})!", session.version_.major, session.version_.minor);
return State::Close;
}
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -14,7 +14,7 @@
#include "utils/algorithm.hpp"
#include "utils/string.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
#define DEF_GETTER_BY_VAL(type, value_type, field) \
value_type &Value::Value##type() { \
@@ -461,4 +461,4 @@ std::ostream &operator<<(std::ostream &os, const Value::Type type) {
return os << "duration";
}
}
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -20,7 +20,7 @@
#include "utils/exceptions.hpp"
#include "utils/temporal.hpp"
namespace communication::bolt {
namespace memgraph::communication::bolt {
/** Forward declaration of Value class. */
class Value;
@@ -282,4 +282,4 @@ std::ostream &operator<<(std::ostream &os, const UnboundedEdge &edge);
std::ostream &operator<<(std::ostream &os, const Path &path);
std::ostream &operator<<(std::ostream &os, const Value &value);
std::ostream &operator<<(std::ostream &os, const Value::Type type);
} // namespace communication::bolt
} // namespace memgraph::communication::bolt

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,7 +13,7 @@
#include "utils/logging.hpp"
namespace communication {
namespace memgraph::communication {
Buffer::Buffer() : data_(kBufferInitialSize, 0), read_end_(this), write_end_(this) {}
@@ -77,4 +77,4 @@ void Buffer::Resize(size_t len) {
void Buffer::Clear() { have_ = 0; }
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -15,7 +15,7 @@
#include "io/network/stream_buffer.hpp"
namespace communication {
namespace memgraph::communication {
/**
* @brief Buffer
@@ -171,4 +171,4 @@ class Buffer final {
ReadEnd read_end_;
WriteEnd write_end_;
};
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -14,7 +14,7 @@
#include "communication/helpers.hpp"
#include "utils/logging.hpp"
namespace communication {
namespace memgraph::communication {
Client::Client(ClientContext *context) : context_(context) {}
@@ -239,4 +239,4 @@ bool ClientOutputStream::Write(const uint8_t *data, size_t len, bool have_more)
}
bool ClientOutputStream::Write(const std::string &str, bool have_more) { return client_.Write(str, have_more); }
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -21,14 +21,14 @@
#include "io/network/endpoint.hpp"
#include "io/network/socket.hpp"
namespace communication {
namespace memgraph::communication {
/**
* This class implements a generic network Client.
* It uses blocking sockets and provides an API that can be used to receive/send
* data over the network connection.
*
* NOTE: If you use this client you **must** create `communication::SSLInit`
* NOTE: If you use this client you **must** create `memgraph::communication::SSLInit`
* from the `main` function before using the client!
*/
class Client final {
@@ -167,4 +167,4 @@ class ClientOutputStream final {
Client &client_;
};
} // namespace communication
} // namespace memgraph::communication

View File

@@ -10,10 +10,13 @@
// licenses/APL.txt.
#include "communication/context.hpp"
#include <boost/asio/ssl/context.hpp>
#include <boost/asio/ssl/verify_mode.hpp>
#include <boost/system/detail/error_code.hpp>
#include "utils/logging.hpp"
namespace communication {
namespace memgraph::communication {
ClientContext::ClientContext(bool use_ssl) : use_ssl_(use_ssl), ctx_(nullptr) {
if (use_ssl_) {
@@ -74,42 +77,35 @@ SSL_CTX *ClientContext::context() { return ctx_; }
bool ClientContext::use_ssl() { return use_ssl_; }
ServerContext::ServerContext(const std::string &key_file, const std::string &cert_file, const std::string &ca_file,
bool verify_peer)
:
#if OPENSSL_VERSION_NUMBER < 0x10100000L
ctx_(SSL_CTX_new(SSLv23_server_method()))
#else
ctx_(SSL_CTX_new(TLS_server_method()))
#endif
{
// TODO (mferencevic): add support for encrypted private keys
// TODO (mferencevic): add certificate revocation list (CRL)
MG_ASSERT(SSL_CTX_use_certificate_file(ctx_, cert_file.c_str(), SSL_FILETYPE_PEM) == 1,
"Couldn't load server certificate from file: {}", cert_file);
MG_ASSERT(SSL_CTX_use_PrivateKey_file(ctx_, key_file.c_str(), SSL_FILETYPE_PEM) == 1,
"Couldn't load server private key from file: {}", key_file);
bool verify_peer) {
namespace ssl = boost::asio::ssl;
ctx_.emplace(ssl::context::tls_server);
// NOLINTNEXTLINE(hicpp-signed-bitwise)
ctx_->set_options(ssl::context::default_workarounds | ssl::context::no_sslv2 | ssl::context::no_sslv3 |
ssl::context::single_dh_use);
ctx_->set_default_verify_paths();
// TODO: add support for encrypted private keys
// TODO: add certificate revocation list (CRL)
boost::system::error_code ec;
ctx_->use_certificate_chain_file(cert_file, ec);
MG_ASSERT(!ec, "Couldn't load server certificate from file: {}", cert_file);
ctx_->use_private_key_file(key_file, ssl::context::pem, ec);
MG_ASSERT(!ec, "Couldn't load server private key from file: {}", key_file);
// Disable legacy SSL support. Other options can be seen here:
// https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_options.html
SSL_CTX_set_options(ctx_, SSL_OP_NO_SSLv3);
ctx_->set_options(SSL_OP_NO_SSLv3, ec);
MG_ASSERT(!ec, "Setting options to SSL context failed!");
if (ca_file != "") {
if (!ca_file.empty()) {
// Load the certificate authority file.
MG_ASSERT(SSL_CTX_load_verify_locations(ctx_, ca_file.c_str(), nullptr) == 1,
"Couldn't load certificate authority from file: {}", ca_file);
boost::system::error_code ec;
ctx_->load_verify_file(ca_file, ec);
MG_ASSERT(!ec, "Couldn't load certificate authority from file: {}", ca_file);
if (verify_peer) {
// Add the CA to list of accepted CAs that is sent to the client.
STACK_OF(X509_NAME) *ca_names = SSL_load_client_CA_file(ca_file.c_str());
MG_ASSERT(ca_names != nullptr, "Couldn't load certificate authority from file: {}", ca_file);
// `ca_names` doesn' need to be free'd because we pass it to
// `SSL_CTX_set_client_CA_list`:
// https://mta.openssl.org/pipermail/openssl-users/2015-May/001363.html
SSL_CTX_set_client_CA_list(ctx_, ca_names);
// Enable verification of the client certificate.
// NOLINTNEXTLINE(hicpp-signed-bitwise)
SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
ctx_->set_verify_mode(ssl::verify_peer | ssl::verify_fail_if_no_peer_cert, ec);
MG_ASSERT(!ec, "Setting SSL verification mode failed!");
}
}
}
@@ -119,36 +115,27 @@ ServerContext::ServerContext(ServerContext &&other) noexcept { std::swap(ctx_, o
ServerContext &ServerContext::operator=(ServerContext &&other) noexcept {
if (this == &other) return *this;
// destroy my objects
if (ctx_) {
SSL_CTX_free(ctx_);
}
// move other objects to self
ctx_ = other.ctx_;
ctx_ = std::move(other.ctx_);
// reset other objects
other.ctx_ = nullptr;
other.ctx_.reset();
return *this;
}
ServerContext::~ServerContext() {
if (ctx_) {
SSL_CTX_free(ctx_);
}
}
ServerContext::~ServerContext() {}
SSL_CTX *ServerContext::context() {
MG_ASSERT(ctx_);
return ctx_;
return ctx_->native_handle();
}
SSL_CTX *ServerContext::context_clone() {
boost::asio::ssl::context &ServerContext::context_clone() {
MG_ASSERT(ctx_);
SSL_CTX_up_ref(ctx_);
return ctx_;
return *ctx_;
}
bool ServerContext::use_ssl() const { return ctx_ != nullptr; }
bool ServerContext::use_ssl() const { return ctx_.has_value(); }
} // namespace communication
} // namespace memgraph::communication

View File

@@ -11,11 +11,13 @@
#pragma once
#include <optional>
#include <string>
#include <openssl/ssl.h>
#include <boost/asio/ssl/context.hpp>
namespace communication {
namespace memgraph::communication {
/**
* This class represents a context that should be used with network clients. One
@@ -94,12 +96,12 @@ class ServerContext final {
~ServerContext();
SSL_CTX *context();
SSL_CTX *context_clone();
boost::asio::ssl::context &context_clone();
bool use_ssl() const;
private:
SSL_CTX *ctx_{nullptr};
std::optional<boost::asio::ssl::context> ctx_;
};
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,7 +13,7 @@
#include "utils/exceptions.hpp"
namespace communication {
namespace memgraph::communication {
/**
* This exception is thrown to indicate to the communication stack that the
@@ -22,4 +22,4 @@ namespace communication {
class SessionClosedException : public utils::BasicException {
using utils::BasicException::BasicException;
};
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,7 +13,7 @@
#include "communication/helpers.hpp"
namespace communication {
namespace memgraph::communication {
const std::string SslGetLastError() {
char buff[2048];
@@ -21,4 +21,4 @@ const std::string SslGetLastError() {
ERR_error_string_n(err, buff, sizeof(buff));
return std::string(buff);
}
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,11 +13,11 @@
#include <string>
namespace communication {
namespace memgraph::communication {
/**
* This function reads and returns a string describing the last OpenSSL error.
*/
const std::string SslGetLastError();
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -20,7 +20,7 @@
#include "utils/signals.hpp"
#include "utils/spin_lock.hpp"
namespace communication {
namespace memgraph::communication {
namespace {
// OpenSSL before 1.1 did not have a out-of-the-box multithreading support
@@ -72,4 +72,4 @@ SSLInit::SSLInit() {
}
SSLInit::~SSLInit() { Cleanup(); }
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -11,7 +11,7 @@
#pragma once
namespace communication {
namespace memgraph::communication {
/**
* Create this object in each `main` file that uses the Communication stack. It
@@ -36,4 +36,4 @@ struct SSLInit {
~SSLInit();
};
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -28,7 +28,7 @@
#include "utils/spin_lock.hpp"
#include "utils/thread.hpp"
namespace communication {
namespace memgraph::communication {
/**
* This class listens to events on an epoll object and processes them.
@@ -273,4 +273,4 @@ class Listener final {
const std::string service_name_;
const size_t workers_count_;
};
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -29,7 +29,7 @@
*/
class ResultStreamFaker {
public:
explicit ResultStreamFaker(storage::Storage *store) : store_(store) {}
explicit ResultStreamFaker(memgraph::storage::Storage *store) : store_(store) {}
ResultStreamFaker(const ResultStreamFaker &) = delete;
ResultStreamFaker &operator=(const ResultStreamFaker &) = delete;
@@ -38,25 +38,25 @@ class ResultStreamFaker {
void Header(const std::vector<std::string> &fields) { header_ = fields; }
void Result(const std::vector<communication::bolt::Value> &values) { results_.push_back(values); }
void Result(const std::vector<memgraph::communication::bolt::Value> &values) { results_.push_back(values); }
void Result(const std::vector<query::TypedValue> &values) {
std::vector<communication::bolt::Value> bvalues;
void Result(const std::vector<memgraph::query::TypedValue> &values) {
std::vector<memgraph::communication::bolt::Value> bvalues;
bvalues.reserve(values.size());
for (const auto &value : values) {
auto maybe_value = glue::ToBoltValue(value, *store_, storage::View::NEW);
auto maybe_value = memgraph::glue::ToBoltValue(value, *store_, memgraph::storage::View::NEW);
MG_ASSERT(maybe_value.HasValue());
bvalues.push_back(std::move(*maybe_value));
}
results_.push_back(std::move(bvalues));
}
void Summary(const std::map<std::string, communication::bolt::Value> &summary) { summary_ = summary; }
void Summary(const std::map<std::string, memgraph::communication::bolt::Value> &summary) { summary_ = summary; }
void Summary(const std::map<std::string, query::TypedValue> &summary) {
std::map<std::string, communication::bolt::Value> bsummary;
void Summary(const std::map<std::string, memgraph::query::TypedValue> &summary) {
std::map<std::string, memgraph::communication::bolt::Value> bsummary;
for (const auto &item : summary) {
auto maybe_value = glue::ToBoltValue(item.second, *store_, storage::View::NEW);
auto maybe_value = memgraph::glue::ToBoltValue(item.second, *store_, memgraph::storage::View::NEW);
MG_ASSERT(maybe_value.HasValue());
bsummary.insert({item.first, std::move(*maybe_value)});
}
@@ -119,17 +119,17 @@ class ResultStreamFaker {
// output the summary
os << "Query summary: {";
utils::PrintIterable(os, results.GetSummary(), ", ",
[&](auto &stream, const auto &kv) { stream << kv.first << ": " << kv.second; });
memgraph::utils::PrintIterable(os, results.GetSummary(), ", ",
[&](auto &stream, const auto &kv) { stream << kv.first << ": " << kv.second; });
os << "}" << std::endl;
return os;
}
private:
storage::Storage *store_;
memgraph::storage::Storage *store_;
// the data that the record stream can accept
std::vector<std::string> header_;
std::vector<std::vector<communication::bolt::Value>> results_;
std::map<std::string, communication::bolt::Value> summary_;
std::vector<std::vector<memgraph::communication::bolt::Value>> results_;
std::map<std::string, memgraph::communication::bolt::Value> summary_;
};

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -27,7 +27,7 @@
#include "utils/message.hpp"
#include "utils/thread.hpp"
namespace communication {
namespace memgraph::communication {
/**
* Communication server.
@@ -37,10 +37,10 @@ namespace communication {
* that has `num_workers` threads. It is started automatically on constructor,
* and stopped at destructor.
*
* Current Server achitecture:
* Current Server architecture:
* incoming connection -> server -> listener -> session
*
* NOTE: If you use this server you **must** create `communication::SSLInit`
* NOTE: If you use this server you **must** create communication::SSLInit`
* from the `main` function before using the server!
*
* @tparam TSession the server can handle different Sessions, each session
@@ -161,4 +161,4 @@ class Server final {
const std::string service_name_;
};
} // namespace communication
} // namespace memgraph::communication

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -33,7 +33,7 @@
#include "utils/on_scope_exit.hpp"
#include "utils/spin_lock.hpp"
namespace communication {
namespace memgraph::communication {
/**
* This is used to provide input to user sessions. All sessions used with the
@@ -327,4 +327,4 @@ class Session final {
SSL *ssl_{nullptr};
BIO *bio_{nullptr};
}; // namespace communication
} // namespace communication
} // namespace memgraph::communication

View File

@@ -0,0 +1,135 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string_view>
#include <thread>
#include <vector>
#include <spdlog/spdlog.h>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/system/detail/error_code.hpp>
#include "communication/context.hpp"
#include "communication/v2/pool.hpp"
#include "communication/v2/session.hpp"
#include "utils/spin_lock.hpp"
#include "utils/synchronized.hpp"
namespace memgraph::communication::v2 {
template <class TSession, class TSessionData>
class Listener final : public std::enable_shared_from_this<Listener<TSession, TSessionData>> {
using tcp = boost::asio::ip::tcp;
using SessionHandler = Session<TSession, TSessionData>;
using std::enable_shared_from_this<Listener<TSession, TSessionData>>::shared_from_this;
public:
Listener(const Listener &) = delete;
Listener(Listener &&) = delete;
Listener &operator=(const Listener &) = delete;
Listener &operator=(Listener &&) = delete;
~Listener() {}
template <typename... Args>
static std::shared_ptr<Listener> Create(Args &&...args) {
return std::shared_ptr<Listener>{new Listener(std::forward<Args>(args)...)};
}
void Start() { DoAccept(); }
bool IsRunning() const noexcept { return alive_.load(std::memory_order_relaxed); }
private:
Listener(boost::asio::io_context &io_context, TSessionData *data, ServerContext *server_context,
tcp::endpoint &endpoint, const std::string_view service_name, const uint64_t inactivity_timeout_sec)
: io_context_(io_context),
data_(data),
server_context_(server_context),
acceptor_(io_context_),
endpoint_{endpoint},
service_name_{service_name},
inactivity_timeout_{inactivity_timeout_sec} {
boost::system::error_code ec;
// Open the acceptor
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
OnError(ec, "open");
return;
}
// Allow address reuse
acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec);
if (ec) {
OnError(ec, "set_option");
return;
}
// Bind to the server address
acceptor_.bind(endpoint, ec);
if (ec) {
spdlog::error(
utils::MessageWithLink("Cannot bind to socket on endpoint {}.", endpoint, "https://memgr.ph/socket"));
OnError(ec, "bind");
return;
}
acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec);
if (ec) {
OnError(ec, "listen");
return;
}
}
void DoAccept() {
acceptor_.async_accept(io_context_,
[shared_this = shared_from_this()](auto ec, boost::asio::ip::tcp::socket &&socket) {
shared_this->OnAccept(ec, std::move(socket));
});
}
void OnAccept(boost::system::error_code ec, tcp::socket socket) {
if (ec) {
return OnError(ec, "accept");
}
auto session = SessionHandler::Create(std::move(socket), data_, *server_context_, endpoint_, inactivity_timeout_,
service_name_);
session->Start();
DoAccept();
}
void OnError(const boost::system::error_code &ec, const std::string_view what) {
spdlog::error("Listener failed on {}: {}", what, ec.message());
alive_.store(false, std::memory_order_relaxed);
}
boost::asio::io_context &io_context_;
TSessionData *data_;
ServerContext *server_context_;
tcp::acceptor acceptor_;
tcp::endpoint endpoint_;
std::string_view service_name_;
std::chrono::seconds inactivity_timeout_;
std::atomic<bool> alive_;
};
} // namespace memgraph::communication::v2

View File

@@ -0,0 +1,68 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <cstddef>
#include <thread>
#include <vector>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/io_context.hpp>
#include "utils/logging.hpp"
namespace memgraph::communication::v2 {
class IOContextThreadPool final {
private:
using IOContext = boost::asio::io_context;
using IOContextGuard = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
public:
explicit IOContextThreadPool(size_t pool_size) : guard_{io_context_.get_executor()}, pool_size_{pool_size} {
MG_ASSERT(pool_size != 0, "Pool size must be greater then 0!");
}
IOContextThreadPool(const IOContextThreadPool &) = delete;
IOContextThreadPool &operator=(const IOContextThreadPool &) = delete;
IOContextThreadPool(IOContextThreadPool &&) = delete;
IOContextThreadPool &operator=(IOContextThreadPool &&) = delete;
~IOContextThreadPool() = default;
void Run() {
background_threads_.reserve(pool_size_);
for (size_t i = 0; i < pool_size_; ++i) {
background_threads_.emplace_back([this]() { io_context_.run(); });
}
running_ = true;
}
void Shutdown() {
io_context_.stop();
running_ = false;
}
void AwaitShutdown() { background_threads_.clear(); }
bool IsRunning() const noexcept { return running_; }
IOContext &GetIOContext() noexcept { return io_context_; }
private:
/// The pool of io_context.
IOContext io_context_;
IOContextGuard guard_;
size_t pool_size_;
std::vector<std::jthread> background_threads_;
bool running_{false};
};
} // namespace memgraph::communication::v2

View File

@@ -0,0 +1,128 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <memory>
#include <optional>
#include <string>
#include <thread>
#include <vector>
#include <fmt/format.h>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/tcp.hpp>
#include "communication/context.hpp"
#include "communication/init.hpp"
#include "communication/v2/listener.hpp"
#include "communication/v2/pool.hpp"
#include "utils/logging.hpp"
#include "utils/message.hpp"
#include "utils/thread.hpp"
namespace memgraph::communication::v2 {
using Socket = boost::asio::ip::tcp::socket;
using ServerEndpoint = boost::asio::ip::tcp::endpoint;
/**
* Communication server.
*
* Listens for incoming connections on the server port and assigns them to the
* connection listener. The listener and session are implemented using asio
* async model. Currently the implemented model is thread per core model
* opposed to io_context per core. The reasoning for opting for the former model
* is the robustness to the multiple resource demanding queries that can be split
* across multiple threads, and then a single thread would not block io_context,
* unlike in the latter model where it is possible that thread that accepts
* request is being blocked by demanding query.
* All logic is contained within handlers that are being dispatched
* on a single strand per session. The only exception is write which is
* synchronous since the nature of the clients conenction is synchronous as
* well.
*
* Current Server architecture:
* incoming connection -> server -> listener -> session
*
* @tparam TSession the server can handle different Sessions, each session
* represents a different protocol so the same network infrastructure
* can be used for handling different protocols
* @tparam TSessionData the class with objects that will be forwarded to the
* session
*/
template <typename TSession, typename TSessionData>
class Server final {
using ServerHandler = Server<TSession, TSessionData>;
public:
/**
* Constructs and binds server to endpoint, operates on session data and
* invokes workers_count workers
*/
Server(ServerEndpoint &endpoint, TSessionData *session_data, ServerContext *server_context,
const int inactivity_timeout_sec, const std::string_view service_name,
size_t workers_count = std::thread::hardware_concurrency())
: endpoint_{endpoint},
service_name_{service_name},
context_thread_pool_{workers_count},
listener_{Listener<TSession, TSessionData>::Create(context_thread_pool_.GetIOContext(), session_data,
server_context, endpoint_, service_name_,
inactivity_timeout_sec)} {}
~Server() { MG_ASSERT(!IsRunning(), "Server wasn't shutdown properly"); }
Server(const Server &) = delete;
Server(Server &&) = delete;
Server &operator=(const Server &) = delete;
Server &operator=(Server &&) = delete;
const auto &Endpoint() const {
MG_ASSERT(IsRunning(), "You can't get the server endpoint when it's not running!");
return endpoint_;
}
bool Start() {
if (IsRunning()) {
spdlog::error("The server is already running");
return false;
}
listener_->Start();
spdlog::info("{} server is fully armed and operational", service_name_);
spdlog::info("{} listening on {}", service_name_, endpoint_.address());
context_thread_pool_.Run();
return true;
}
void Shutdown() {
context_thread_pool_.Shutdown();
spdlog::info("{} shutting down...", service_name_);
}
void AwaitShutdown() { context_thread_pool_.AwaitShutdown(); }
bool IsRunning() const noexcept { return context_thread_pool_.IsRunning() && listener_->IsRunning(); }
private:
ServerEndpoint endpoint_;
std::string service_name_;
IOContextThreadPool context_thread_pool_;
std::shared_ptr<Listener<TSession, TSessionData>> listener_;
};
} // namespace memgraph::communication::v2

View File

@@ -0,0 +1,508 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <deque>
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <spdlog/spdlog.h>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/socket_base.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/ssl/stream_base.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/system_context.hpp>
#include <boost/asio/write.hpp>
#include <boost/beast/core/tcp_stream.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/websocket/rfc6455.hpp>
#include <boost/system/detail/error_code.hpp>
#include "communication/context.hpp"
#include "communication/exceptions.hpp"
#include "utils/logging.hpp"
#include "utils/variant_helpers.hpp"
namespace memgraph::communication::v2 {
/**
* This is used to provide input to user Sessions. All Sessions used with the
* network stack should use this class as their input stream.
*/
using InputStream = communication::Buffer::ReadEnd;
using tcp = boost::asio::ip::tcp;
/**
* This is used to provide output from user Sessions. All Sessions used with the
* network stack should use this class for their output stream.
*/
class OutputStream final {
public:
explicit OutputStream(std::function<bool(const uint8_t *, size_t, bool)> write_function)
: write_function_(write_function) {}
OutputStream(const OutputStream &) = delete;
OutputStream(OutputStream &&) = delete;
OutputStream &operator=(const OutputStream &) = delete;
OutputStream &operator=(OutputStream &&) = delete;
~OutputStream() = default;
bool Write(const uint8_t *data, size_t len, bool have_more = false) { return write_function_(data, len, have_more); }
bool Write(const std::string &str, bool have_more = false) {
return Write(reinterpret_cast<const uint8_t *>(str.data()), str.size(), have_more);
}
private:
std::function<bool(const uint8_t *, size_t, bool)> write_function_;
};
/**
* This class is used internally in the communication stack to handle all user
* Websocket Sessions. It handles socket ownership, inactivity timeout and protocol
* wrapping.
*/
template <typename TSession, typename TSessionData>
class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TSession, TSessionData>> {
using WebSocket = boost::beast::websocket::stream<boost::beast::tcp_stream>;
using std::enable_shared_from_this<WebsocketSession<TSession, TSessionData>>::shared_from_this;
public:
template <typename... Args>
static std::shared_ptr<WebsocketSession> Create(Args &&...args) {
return std::shared_ptr<WebsocketSession>(new WebsocketSession(std::forward<Args>(args)...));
}
// Start the asynchronous accept operation
template <class Body, class Allocator>
void DoAccept(boost::beast::http::request<Body, boost::beast::http::basic_fields<Allocator>> req) {
execution_active_ = true;
// Set suggested timeout settings for the websocket
ws_.set_option(boost::beast::websocket::stream_base::timeout::suggested(boost::beast::role_type::server));
boost::asio::socket_base::keep_alive option(true);
// Set a decorator to change the Server of the handshake
ws_.set_option(boost::beast::websocket::stream_base::decorator([](boost::beast::websocket::response_type &res) {
res.set(boost::beast::http::field::server, std::string("Memgraph Bolt WS"));
res.set(boost::beast::http::field::sec_websocket_protocol, "binary");
}));
ws_.binary(true);
// Accept the websocket handshake
ws_.async_accept(
req, boost::asio::bind_executor(strand_, std::bind_front(&WebsocketSession::OnAccept, shared_from_this())));
}
bool Write(const uint8_t *data, size_t len) {
if (!IsConnected()) {
return false;
}
boost::system::error_code ec;
ws_.write(boost::asio::buffer(data, len), ec);
if (ec) {
OnError(ec, "write");
return false;
}
return true;
}
private:
// Take ownership of the socket
explicit WebsocketSession(tcp::socket &&socket, TSessionData *data, tcp::endpoint endpoint,
std::string_view service_name)
: ws_(std::move(socket)),
strand_{boost::asio::make_strand(ws_.get_executor())},
output_stream_([this](const uint8_t *data, size_t len, bool /*have_more*/) { return Write(data, len); }),
session_(data, endpoint, input_buffer_.read_end(), &output_stream_),
endpoint_{endpoint},
remote_endpoint_{ws_.next_layer().socket().remote_endpoint()},
service_name_{service_name} {}
void OnAccept(boost::beast::error_code ec) {
if (ec) {
return OnError(ec, "accept");
}
// Read a message
DoRead();
}
void DoRead() {
if (!IsConnected()) {
return;
}
// Read a message into our buffer
auto buffer = input_buffer_.write_end()->Allocate();
ws_.async_read_some(
boost::asio::buffer(buffer.data, buffer.len),
boost::asio::bind_executor(strand_, std::bind_front(&WebsocketSession::OnRead, shared_from_this())));
}
void OnRead(const boost::system::error_code &ec, [[maybe_unused]] const size_t bytes_transferred) {
// This indicates that the WebsocketSession was closed
if (ec == boost::beast::websocket::error::closed) {
return;
}
if (ec) {
OnError(ec, "read");
}
input_buffer_.write_end()->Written(bytes_transferred);
try {
session_.Execute();
DoRead();
} catch (const SessionClosedException &e) {
spdlog::info("{} client {}:{} closed the connection.", service_name_, remote_endpoint_.address(),
remote_endpoint_.port());
DoClose();
} catch (const std::exception &e) {
spdlog::error(
"Exception was thrown while processing event in {} session "
"associated with {}:{}",
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
spdlog::debug("Exception message: {}", e.what());
DoClose();
}
}
void OnError(const boost::system::error_code &ec, const std::string_view action) {
spdlog::error("Websocket Bolt session error: {} on {}", ec.message(), action);
DoClose();
}
void DoClose() {
ws_.async_close(
boost::beast::websocket::close_code::normal,
boost::asio::bind_executor(
strand_, [shared_this = shared_from_this()](boost::beast::error_code ec) { shared_this->OnClose(ec); }));
}
void OnClose(const boost::system::error_code &ec) {
if (!IsConnected()) {
return;
}
if (ec) {
return OnError(ec, "close");
}
}
bool IsConnected() const { return ws_.is_open() && execution_active_; }
WebSocket ws_;
boost::asio::strand<WebSocket::executor_type> strand_;
communication::Buffer input_buffer_;
OutputStream output_stream_;
TSession session_;
tcp::endpoint endpoint_;
tcp::endpoint remote_endpoint_;
std::string_view service_name_;
bool execution_active_{false};
};
/**
* This class is used internally in the communication stack to handle all user
* Sessions. It handles socket ownership, inactivity timeout and protocol
* wrapping.
*/
template <typename TSession, typename TSessionData>
class Session final : public std::enable_shared_from_this<Session<TSession, TSessionData>> {
using TCPSocket = tcp::socket;
using SSLSocket = boost::asio::ssl::stream<TCPSocket>;
using std::enable_shared_from_this<Session<TSession, TSessionData>>::shared_from_this;
public:
template <typename... Args>
static std::shared_ptr<Session> Create(Args &&...args) {
return std::shared_ptr<Session>(new Session(std::forward<Args>(args)...));
}
Session(const Session &) = delete;
Session(Session &&) = delete;
Session &operator=(const Session &) = delete;
Session &operator=(Session &&) = delete;
~Session() = default;
bool Start() {
if (execution_active_) {
return false;
}
execution_active_ = true;
timeout_timer_.async_wait(boost::asio::bind_executor(strand_, std::bind(&Session::OnTimeout, shared_from_this())));
if (std::holds_alternative<SSLSocket>(socket_)) {
boost::asio::dispatch(strand_, [shared_this = shared_from_this()] { shared_this->DoHandshake(); });
} else {
boost::asio::dispatch(strand_, [shared_this = shared_from_this()] { shared_this->DoRead(); });
}
return true;
}
bool Write(const uint8_t *data, size_t len, bool have_more = false) {
if (!IsConnected()) {
return false;
}
return std::visit(
utils::Overloaded{[shared_this = shared_from_this(), data, len, have_more](TCPSocket &socket) mutable {
boost::system::error_code ec;
while (len > 0) {
const auto sent = socket.send(boost::asio::buffer(data, len),
MSG_NOSIGNAL | (have_more ? MSG_MORE : 0), ec);
if (ec) {
shared_this->OnError(ec);
return false;
}
data += sent;
len -= sent;
}
return true;
},
[shared_this = shared_from_this(), data, len](SSLSocket &socket) mutable {
boost::system::error_code ec;
while (len > 0) {
const auto sent = socket.write_some(boost::asio::buffer(data, len), ec);
if (ec) {
shared_this->OnError(ec);
return false;
}
data += sent;
len -= sent;
}
return true;
}},
socket_);
}
bool IsConnected() const {
return std::visit([this](const auto &socket) { return execution_active_ && socket.lowest_layer().is_open(); },
socket_);
}
private:
explicit Session(tcp::socket &&socket, TSessionData *data, ServerContext &server_context, tcp::endpoint endpoint,
const std::chrono::seconds inactivity_timeout_sec, std::string_view service_name)
: socket_(CreateSocket(std::move(socket), server_context)),
strand_{boost::asio::make_strand(GetExecutor())},
output_stream_([this](const uint8_t *data, size_t len, bool have_more) { return Write(data, len, have_more); }),
session_(data, endpoint, input_buffer_.read_end(), &output_stream_),
data_{data},
endpoint_{endpoint},
remote_endpoint_{GetRemoteEndpoint()},
service_name_{service_name},
timeout_seconds_(inactivity_timeout_sec),
timeout_timer_(GetExecutor()) {
ExecuteForSocket([](auto &&socket) {
socket.lowest_layer().set_option(tcp::no_delay(true)); // enable PSH
socket.lowest_layer().set_option(boost::asio::socket_base::keep_alive(true)); // enable SO_KEEPALIVE
socket.lowest_layer().non_blocking(false);
});
timeout_timer_.expires_at(boost::asio::steady_timer::time_point::max());
spdlog::info("Accepted a connection from {}:", service_name_, remote_endpoint_.address(), remote_endpoint_.port());
}
void DoRead() {
if (!IsConnected()) {
return;
}
timeout_timer_.expires_after(timeout_seconds_);
ExecuteForSocket([this](auto &&socket) {
auto buffer = input_buffer_.write_end()->Allocate();
socket.async_read_some(
boost::asio::buffer(buffer.data, buffer.len),
boost::asio::bind_executor(strand_, std::bind_front(&Session::OnRead, shared_from_this())));
});
}
bool IsWebsocketUpgrade(boost::beast::http::request_parser<boost::beast::http::string_body> &parser) {
boost::system::error_code error_code_parsing;
parser.put(boost::asio::buffer(input_buffer_.read_end()->data(), input_buffer_.read_end()->size()),
error_code_parsing);
if (error_code_parsing) {
return false;
}
return boost::beast::websocket::is_upgrade(parser.get());
}
void OnRead(const boost::system::error_code &ec, const size_t bytes_transferred) {
if (ec) {
return OnError(ec);
}
input_buffer_.write_end()->Written(bytes_transferred);
// Can be a websocket connection only on the first read, since it is not
// expected from clients to upgrade from tcp to websocket
if (!has_received_msg_) {
has_received_msg_ = true;
boost::beast::http::request_parser<boost::beast::http::string_body> parser;
if (IsWebsocketUpgrade(parser)) {
spdlog::info("Switching {} to websocket connection", remote_endpoint_);
if (std::holds_alternative<TCPSocket>(socket_)) {
auto sock = std::get<TCPSocket>(std::move(socket_));
WebsocketSession<TSession, TSessionData>::Create(std::move(sock), data_, endpoint_, service_name_)
->DoAccept(parser.release());
execution_active_ = false;
return;
}
spdlog::error("Error while upgrading connection to websocket");
DoShutdown();
}
}
try {
session_.Execute();
DoRead();
} catch (const SessionClosedException &e) {
spdlog::info("{} client {}:{} closed the connection.", service_name_, remote_endpoint_.address(),
remote_endpoint_.port());
DoShutdown();
} catch (const std::exception &e) {
spdlog::error(
"Exception was thrown while processing event in {} session "
"associated with {}:{}",
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
spdlog::debug("Exception message: {}", e.what());
DoShutdown();
}
}
void OnError(const boost::system::error_code &ec) {
if (ec == boost::asio::error::operation_aborted) {
return;
}
if (ec == boost::asio::error::eof) {
spdlog::info("Session closed by peer");
} else {
spdlog::error("Session error: {}", ec.message());
}
DoShutdown();
}
void DoShutdown() {
if (!IsConnected()) {
return;
}
execution_active_ = false;
timeout_timer_.cancel();
ExecuteForSocket([](auto &socket) {
boost::system::error_code ec;
auto &lowest_layer = socket.lowest_layer();
lowest_layer.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
if (ec) {
spdlog::error("Session shutdown failed: {}", ec.what());
}
lowest_layer.close();
});
}
void DoHandshake() {
if (!IsConnected()) {
return;
}
if (auto *socket = std::get_if<SSLSocket>(&socket_); socket) {
socket->async_handshake(
boost::asio::ssl::stream_base::server,
boost::asio::bind_executor(strand_, std::bind_front(&Session::OnHandshake, shared_from_this())));
}
}
void OnHandshake(const boost::system::error_code &ec) {
if (ec) {
return OnError(ec);
}
DoRead();
}
void OnClose(const boost::system::error_code &ec) {
if (ec) {
return OnError(ec);
}
}
void OnTimeout() {
if (!IsConnected()) {
return;
}
// Check whether the deadline has passed. We compare the deadline against
// the current time since a new asynchronous operation may have moved the
// deadline before this actor had a chance to run.
if (timeout_timer_.expiry() <= boost::asio::steady_timer::clock_type::now()) {
// The deadline has passed. Stop the session. The other actors will
// terminate as soon as possible.
spdlog::info("Shutting down session after {} of inactivity", timeout_seconds_);
DoShutdown();
} else {
// Put the actor back to sleep.
timeout_timer_.async_wait(
boost::asio::bind_executor(strand_, std::bind(&Session::OnTimeout, shared_from_this())));
}
}
std::variant<TCPSocket, SSLSocket> CreateSocket(tcp::socket &&socket, ServerContext &context) {
if (context.use_ssl()) {
ssl_context_.emplace(context.context_clone());
return SSLSocket{std::move(socket), *ssl_context_};
}
return TCPSocket{std::move(socket)};
}
auto GetExecutor() {
return std::visit(utils::Overloaded{[](auto &&socket) { return socket.get_executor(); }}, socket_);
}
auto GetRemoteEndpoint() const {
return std::visit(utils::Overloaded{[](const auto &socket) { return socket.lowest_layer().remote_endpoint(); }},
socket_);
}
template <typename F>
decltype(auto) ExecuteForSocket(F &&fun) {
return std::visit(utils::Overloaded{std::forward<F>(fun)}, socket_);
}
std::variant<TCPSocket, SSLSocket> socket_;
std::optional<std::reference_wrapper<boost::asio::ssl::context>> ssl_context_;
boost::asio::strand<tcp::socket::executor_type> strand_;
communication::Buffer input_buffer_;
OutputStream output_stream_;
TSession session_;
TSessionData *data_;
tcp::endpoint endpoint_;
tcp::endpoint remote_endpoint_;
std::string_view service_name_;
std::chrono::seconds timeout_seconds_;
boost::asio::steady_timer timeout_timer_;
bool execution_active_{false};
bool has_received_msg_{false};
};
} // namespace memgraph::communication::v2

View File

@@ -13,7 +13,7 @@
#include <string>
namespace communication::websocket {
namespace memgraph::communication::websocket {
bool SafeAuth::Authenticate(const std::string &username, const std::string &password) const {
return auth_->Lock()->Authenticate(username, password).has_value();
@@ -27,4 +27,4 @@ bool SafeAuth::HasUserPermission(const std::string &username, const auth::Permis
}
bool SafeAuth::HasAnyUsers() const { return auth_->ReadLock()->HasUsers(); }
} // namespace communication::websocket
} // namespace memgraph::communication::websocket

View File

@@ -17,7 +17,7 @@
#include "utils/spin_lock.hpp"
#include "utils/synchronized.hpp"
namespace communication::websocket {
namespace memgraph::communication::websocket {
class AuthenticationInterface {
public:
@@ -41,4 +41,4 @@ class SafeAuth : public AuthenticationInterface {
private:
utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock> *auth_;
};
} // namespace communication::websocket
} // namespace memgraph::communication::websocket

View File

@@ -11,7 +11,7 @@
#include "communication/websocket/listener.hpp"
namespace communication::websocket {
namespace memgraph::communication::websocket {
namespace {
void LogError(boost::beast::error_code ec, const std::string_view what) {
spdlog::warn("Websocket listener failed on {}: {}", what, ec.message());
@@ -87,4 +87,4 @@ void Listener::OnAccept(boost::beast::error_code ec, tcp::socket socket) {
DoAccept();
}
} // namespace communication::websocket
} // namespace memgraph::communication::websocket

View File

@@ -11,8 +11,6 @@
#pragma once
#define BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
#include <list>
#include <memory>
@@ -27,7 +25,7 @@
#include "utils/spin_lock.hpp"
#include "utils/synchronized.hpp"
namespace communication::websocket {
namespace memgraph::communication::websocket {
class Listener : public std::enable_shared_from_this<Listener> {
using tcp = boost::asio::ip::tcp;
@@ -54,4 +52,4 @@ class Listener : public std::enable_shared_from_this<Listener> {
utils::Synchronized<std::list<std::shared_ptr<Session>>, utils::SpinLock> sessions_;
AuthenticationInterface &auth_;
};
} // namespace communication::websocket
} // namespace memgraph::communication::websocket

View File

@@ -13,7 +13,7 @@
#include <spdlog/pattern_formatter.h>
namespace communication::websocket {
namespace memgraph::communication::websocket {
Server::~Server() {
MG_ASSERT(!background_thread_ || (ioc_.stopped() && !background_thread_->joinable()),
@@ -44,10 +44,10 @@ class QuoteEscapeFormatter : public spdlog::custom_flag_formatter {
void format(const spdlog::details::log_msg &msg, const std::tm & /*time*/, spdlog::memory_buf_t &dest) override {
for (const auto c : msg.payload) {
if (c == '"') {
constexpr std::string_view escaped_quote = "\\\"";
static constexpr std::string_view escaped_quote = "\\\"";
dest.append(escaped_quote.data(), escaped_quote.data() + escaped_quote.size());
} else if (c == '\n') {
constexpr std::string_view escaped_newline = "\\n";
static constexpr std::string_view escaped_newline = "\\n";
dest.append(escaped_newline.data(), escaped_newline.data() + escaped_newline.size());
} else {
dest.push_back(c);
@@ -81,4 +81,4 @@ std::shared_ptr<Server::LoggingSink> Server::GetLoggingSink() {
return sink;
}
} // namespace communication::websocket
} // namespace memgraph::communication::websocket

View File

@@ -11,8 +11,6 @@
#pragma once
#define BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
#include <thread>
#include <spdlog/sinks/base_sink.h>
@@ -22,7 +20,7 @@
#include "communication/websocket/listener.hpp"
#include "io/network/endpoint.hpp"
namespace communication::websocket {
namespace memgraph::communication::websocket {
class Server final {
using tcp = boost::asio::ip::tcp;
@@ -65,4 +63,4 @@ class Server final {
std::shared_ptr<Listener> listener_;
std::optional<std::thread> background_thread_;
};
} // namespace communication::websocket
} // namespace memgraph::communication::websocket

View File

@@ -26,7 +26,7 @@
#include "communication/websocket/auth.hpp"
#include "utils/logging.hpp"
namespace communication::websocket {
namespace memgraph::communication::websocket {
namespace {
void LogError(const boost::beast::error_code ec, const std::string_view what) {
spdlog::warn("Websocket session failed on {}: {}", what, ec.message());
@@ -232,4 +232,4 @@ void Session::DoShutdown() {
ws_);
}
} // namespace communication::websocket
} // namespace memgraph::communication::websocket

View File

@@ -11,8 +11,6 @@
#pragma once
#define BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
#include <deque>
#include <memory>
#include <optional>
@@ -32,7 +30,7 @@
#include "utils/synchronized.hpp"
#include "utils/variant_helpers.hpp"
namespace communication::websocket {
namespace memgraph::communication::websocket {
class Session : public std::enable_shared_from_this<Session> {
using tcp = boost::asio::ip::tcp;
@@ -78,7 +76,7 @@ class Session : public std::enable_shared_from_this<Session> {
std::variant<PlainWebSocket, SSLWebSocket> CreateWebSocket(tcp::socket &&socket, ServerContext &context);
std::optional<boost::asio::ssl::context> ssl_context_;
std::optional<std::reference_wrapper<boost::asio::ssl::context>> ssl_context_;
std::variant<PlainWebSocket, SSLWebSocket> ws_;
boost::beast::flat_buffer buffer_;
std::deque<std::shared_ptr<std::string>> messages_;
@@ -88,4 +86,4 @@ class Session : public std::enable_shared_from_this<Session> {
bool close_{false};
AuthenticationInterface &auth_;
};
} // namespace communication::websocket
} // namespace memgraph::communication::websocket

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -49,7 +49,7 @@ class RingBuffer {
void emplace(TArgs &&...args) {
while (true) {
{
std::lock_guard<utils::SpinLock> guard(lock_);
std::lock_guard<memgraph::utils::SpinLock> guard(lock_);
if (size_ < capacity_) {
buffer_[write_pos_++] = TElement(std::forward<TArgs>(args)...);
write_pos_ %= capacity_;
@@ -70,7 +70,7 @@ class RingBuffer {
* empty, nullopt is returned.
*/
std::optional<TElement> pop() {
std::lock_guard<utils::SpinLock> guard(lock_);
std::lock_guard<memgraph::utils::SpinLock> guard(lock_);
if (size_ == 0) return std::nullopt;
size_--;
std::optional<TElement> result(std::move(buffer_[read_pos_++]));
@@ -80,7 +80,7 @@ class RingBuffer {
/** Removes all elements from the buffer. */
void clear() {
std::lock_guard<utils::SpinLock> guard(lock_);
std::lock_guard<memgraph::utils::SpinLock> guard(lock_);
read_pos_ = 0;
write_pos_ = 0;
size_ = 0;
@@ -89,7 +89,7 @@ class RingBuffer {
private:
int capacity_;
std::unique_ptr<TElement[]> buffer_;
utils::SpinLock lock_;
memgraph::utils::SpinLock lock_;
int read_pos_{0};
int write_pos_{0};
int size_{0};

View File

@@ -11,7 +11,7 @@
#include "glue/auth.hpp"
namespace glue {
namespace memgraph::glue {
auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege) {
switch (privilege) {
@@ -59,4 +59,4 @@ auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege) {
return auth::Permission::WEBSOCKET;
}
}
} // namespace glue
} // namespace memgraph::glue

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -12,7 +12,7 @@
#include "auth/models.hpp"
#include "query/frontend/ast/ast.hpp"
namespace glue {
namespace memgraph::glue {
/**
* This function converts query::AuthQuery::Privilege to its corresponding
@@ -20,4 +20,4 @@ namespace glue {
*/
auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege);
} // namespace glue
} // namespace memgraph::glue

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -20,14 +20,14 @@
#include "storage/v2/vertex_accessor.hpp"
#include "utils/temporal.hpp"
using communication::bolt::Value;
using memgraph::communication::bolt::Value;
namespace glue {
namespace memgraph::glue {
query::TypedValue ToTypedValue(const Value &value) {
switch (value.type()) {
case Value::Type::Null:
return query::TypedValue();
return {};
case Value::Type::Bool:
return query::TypedValue(value.ValueBool());
case Value::Type::Int:
@@ -271,4 +271,4 @@ Value ToBoltValue(const storage::PropertyValue &value) {
}
}
} // namespace glue
} // namespace memgraph::glue

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -18,13 +18,13 @@
#include "storage/v2/result.hpp"
#include "storage/v2/view.hpp"
namespace storage {
namespace memgraph::storage {
class EdgeAccessor;
class Storage;
class VertexAccessor;
} // namespace storage
} // namespace memgraph::storage
namespace glue {
namespace memgraph::glue {
/// @param storage::VertexAccessor for converting to
/// communication::bolt::Vertex.
@@ -65,4 +65,4 @@ communication::bolt::Value ToBoltValue(const storage::PropertyValue &value);
storage::PropertyValue ToPropertyValue(const communication::bolt::Value &value);
} // namespace glue
} // namespace memgraph::glue

View File

@@ -14,10 +14,12 @@
#include <chrono>
#include <string>
namespace integrations {
constexpr int64_t kDefaultCheckBatchLimit{1};
constexpr std::chrono::milliseconds kDefaultCheckTimeout{30000};
constexpr std::chrono::milliseconds kMinimumInterval{1};
constexpr int64_t kMinimumSize{1};
namespace memgraph::integrations {
inline constexpr int64_t kDefaultCheckBatchLimit{1};
inline constexpr std::chrono::milliseconds kDefaultCheckTimeout{30000};
inline constexpr std::chrono::milliseconds kMinimumInterval{1};
inline constexpr int64_t kMinimumSize{1};
const std::string kReducted{"<REDUCTED>"};
} // namespace integrations
} // namespace memgraph::integrations

View File

@@ -27,7 +27,7 @@
#include "utils/on_scope_exit.hpp"
#include "utils/thread.hpp"
namespace integrations::kafka {
namespace memgraph::integrations::kafka {
namespace {
utils::BasicResult<std::string, std::vector<Message>> GetBatch(RdKafka::KafkaConsumer &consumer,
@@ -185,8 +185,10 @@ Consumer::Consumer(ConsumerInfo info, ConsumerFunction consumer_function)
std::inserter(topic_names_from_metadata, topic_names_from_metadata.begin()),
[](const auto topic_metadata) { return topic_metadata->topic(); });
constexpr size_t max_topic_name_length = 249;
constexpr auto is_valid_topic_name = [](const auto c) { return std::isalnum(c) || c == '.' || c == '_' || c == '-'; };
static constexpr size_t max_topic_name_length = 249;
static constexpr auto is_valid_topic_name = [](const auto c) {
return std::isalnum(c) || c == '.' || c == '_' || c == '-';
};
for (const auto &topic_name : info_.topics) {
if (topic_name.size() > max_topic_name_length ||
@@ -351,7 +353,7 @@ void Consumer::StartConsuming() {
}
thread_ = std::thread([this] {
constexpr auto kMaxThreadNameSize = utils::GetMaxThreadNameSize();
static constexpr auto kMaxThreadNameSize = utils::GetMaxThreadNameSize();
const auto full_thread_name = "Cons#" + info_.consumer_name;
utils::ThreadSetName(full_thread_name.substr(0, kMaxThreadNameSize));
@@ -448,4 +450,4 @@ void Consumer::ConsumerRebalanceCb::rebalance_cb(RdKafka::KafkaConsumer *consume
}
}
void Consumer::ConsumerRebalanceCb::set_offset(int64_t offset) { offset_ = offset; }
} // namespace integrations::kafka
} // namespace memgraph::integrations::kafka

View File

@@ -26,7 +26,7 @@
#include <librdkafka/rdkafkacpp.h>
#include "utils/result.hpp"
namespace integrations::kafka {
namespace memgraph::integrations::kafka {
/// Wraps the message returned from librdkafka.
///
@@ -183,4 +183,4 @@ class Consumer final : public RdKafka::EventCb {
std::thread thread_;
ConsumerRebalanceCb cb_;
};
} // namespace integrations::kafka
} // namespace memgraph::integrations::kafka

View File

@@ -15,7 +15,7 @@
#include "utils/exceptions.hpp"
namespace integrations::kafka {
namespace memgraph::integrations::kafka {
class KafkaStreamException : public utils::BasicException {
using utils::BasicException::BasicException;
};
@@ -64,4 +64,4 @@ class TopicNotFoundException : public KafkaStreamException {
TopicNotFoundException(const std::string_view consumer_name, const std::string_view topic_name)
: KafkaStreamException("Kafka consumer {} cannot find topic {}", consumer_name, topic_name) {}
};
} // namespace integrations::kafka
} // namespace memgraph::integrations::kafka

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -26,7 +26,7 @@
#include "utils/result.hpp"
#include "utils/thread.hpp"
namespace integrations::pulsar {
namespace memgraph::integrations::pulsar {
namespace {
@@ -231,7 +231,7 @@ void Consumer::StartConsuming() {
is_running_.store(true);
thread_ = std::thread([this] {
constexpr auto kMaxThreadNameSize = utils::GetMaxThreadNameSize();
static constexpr auto kMaxThreadNameSize = utils::GetMaxThreadNameSize();
const auto full_thread_name = "Cons#" + info_.consumer_name;
utils::ThreadSetName(full_thread_name.substr(0, kMaxThreadNameSize));
@@ -284,4 +284,4 @@ void Consumer::StopConsuming() {
}
}
} // namespace integrations::pulsar
} // namespace memgraph::integrations::pulsar

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -17,7 +17,7 @@
#include <pulsar/Client.h>
namespace integrations::pulsar {
namespace memgraph::integrations::pulsar {
namespace pulsar_client = ::pulsar;
@@ -79,4 +79,4 @@ class Consumer final {
pulsar_client::MessageId last_message_id_{pulsar_client::MessageId::earliest()};
std::thread thread_;
};
} // namespace integrations::pulsar
} // namespace memgraph::integrations::pulsar

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -15,7 +15,7 @@
#include "utils/exceptions.hpp"
namespace integrations::pulsar {
namespace memgraph::integrations::pulsar {
class PulsarStreamException : public utils::BasicException {
using utils::BasicException::BasicException;
};
@@ -55,4 +55,4 @@ class TopicNotFoundException : public PulsarStreamException {
TopicNotFoundException(const std::string &consumer_name, const std::string &topic_name)
: PulsarStreamException("Pulsar consumer {} cannot find topic {}", consumer_name, topic_name) {}
};
} // namespace integrations::pulsar
} // namespace memgraph::integrations::pulsar

1
src/interface/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
gen-cpp2

View File

@@ -0,0 +1,27 @@
include(MgThrift)
set(MG_INTERFACE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
mg_thrift_library(
"storage" #file_name
"Storage" #services
"${MG_INTERFACE_PATH}" #file_path
"${MG_INTERFACE_PATH}" #output_path
"interface" #include_prefix
)
mg_thrift_library(
"meta" #file_name
"Meta" #services
"${MG_INTERFACE_PATH}" #file_path
"${MG_INTERFACE_PATH}" #output_path
"interface" #include_prefix
)
mg_thrift_library(
"trial" #file_name
"PingPong" #services
"${MG_INTERFACE_PATH}" #file_path
"${MG_INTERFACE_PATH}" #output_path
"interface" #include_prefix
)

74
src/interface/meta.thrift Normal file
View File

@@ -0,0 +1,74 @@
namespace cpp2 interface.meta
typedef i64 LabelId
typedef i64 IndexId
struct Result {
1: bool success;
}
struct CreatePrimaryLabelRequest {
1: binary name;
2: list<binary> primary_keys;
}
struct CreateLabelResponse {
1: Result result;
2: LabelId label_id;
}
struct GetLabelInfosRequest {
// The empty list means return all of the label infos
1: list<binary> label_names;
}
struct LabelInfo {
1: binary name;
2: LabelId label_id;
3: list<binary> primary_keys;
}
struct GetLabelInfoResponse {
1: Result result;
2: LabelInfo label_info;
}
struct GetLabelInfosResponse {
1: Result result;
2: list<LabelInfo> label_infos;
}
struct CreateIndexRequest {
1: LabelId label_id;
2: list<binary> property_names;
}
struct CreateIndexResponse {
1: Result result;
2: IndexId index_id;
}
struct IndexInfo {
1: LabelId label_id;
2: list<binary> property_names;
}
struct GetIndexInfosResponse {
1: Result result;
2: list<IndexInfo> index_infos;
}
service Meta {
CreateLabelResponse createPrimaryLabel(1: CreatePrimaryLabelRequest req);
CreateLabelResponse createSecondaryLabel(1: binary label_name);
Result dropLabel(1: binary label_name);
GetLabelInfoResponse getLabelInfo(1:binary label_name);
GetLabelInfosResponse getLabelInfos(1: list<binary> label_names);
CreateIndexResponse createIndex(1: CreateIndexRequest req);
// Don't have index names, and the label doesn't identify the index uniquely, therefore
Result dropIndex(1: IndexId index_id);
GetIndexInfosResponse getIndexInfos();
GetIndexInfosResponse getIndexInfosForLabel(1: LabelId label_id);
}

View File

@@ -0,0 +1,273 @@
namespace cpp2 interface.storage
// https://stackoverflow.com/a/34234874/6639989
cpp_include "storage/v2/view.hpp"
typedef i64 VertexId
typedef i64 Gid
// TODO(antaljanosbenjamin): Use this after introducing 128 bit vertex ids
// struct VertexId {
// 1: i64 upper_half;
// 2: i64 lower_half;
// }
struct Label {
1: i64 id;
}
struct EdgeType {
1: binary name;
}
struct EdgeId {
1: VertexId src;
// QUESTION(antaljanosbenjamin): is it okay to have vertex based (edge id = vertex id + edge id inside vertex)?
2: Gid gid;
}
struct Date {
1: i16 year;
2: byte month;
3: byte day;
}
struct LocalTime {
1: byte hour;
2: byte minute;
3: byte second;
4: i16 millisecond;
5: i16 microsecond;
}
struct LocalDateTime {
1: Date date;
2: LocalTime local_time;
}
struct Duration {
1: i64 milliseconds;
}
union Value {
1: Null null_v;
2: bool bool_v;
3: i64 int_v;
4: double double_v;
5: binary string_v;
6: list<Value> list_v;
7: map<binary, Value> (cpp.template = "std::unordered_map") map_v (cpp2.ref_type = "unique");
8: Vertex vertex_v (cpp2.ref_type = "unique");
9: Edge edge_v (cpp2.ref_type = "unique");
10: Path path_v (cpp2.ref_type = "unique");
11: Date date_v;
12: LocalTime local_time_v;
13: LocalDateTime local_date_time_v;
14: Duration duration_v;
}
struct Null {
}
struct Vertex {
1: VertexId id;
// TODO(antaljanosbenjamin): Change to sperate primary and secondary labels when schema is implemented
2: list<Label> labels;
}
struct Edge {
1: VertexId src;
2: VertexId dst;
3: EdgeType type;
}
struct PathPart {
1: Vertex dst;
2: Edge edge;
}
struct Path {
1: Vertex src;
2: list<PathPart> parts;
}
struct ValuesMap {
1: map<i64, Value> (cpp.template = "std::unordered_map") values_map;
}
struct MappedValues {
1: list<ValuesMap> properties;
}
struct ListedValues {
1: list<list<Value>> properties;
}
union Values {
// This struct is necessary because depending on the request the response
// has two different formats:
// 1. When the request specifies the returned properties, then they are
// returned in that order, therefore no extra mapping is necessary.
// 2. When the request doesn't specify the returned properties, then all
// of the properties are returned. In this case the `mapped` field is
// used. To extract the <key,value> pairs from this struct the
// mapping of i64 -> property name has to be used.
1: ListedValues listed;
2: MappedValues mapped;
}
struct Expression {
1: binary alias;
2: binary expression;
}
struct Filter {
1: binary filter_expression;
}
enum OrderingDirection {
ASCENDING = 1;
DESCENDING = 2;
}
struct OrderBy {
1: Expression expression;
2: OrderingDirection direction;
}
struct Result {
// Just placeholder data for now
1: bool success;
}
enum View {
OLD = 0,
NEW = 1
} (cpp.enum_strict, cpp.type = "memgraph::storage::View")
struct ScanVerticesRequest {
1: i64 transaction_id;
2: optional i64 start_id;
// Special values are accepted:
// * __mg__id (Vertex, but without labels)
// * __mg__labels (Vertex, but without the id)
// If both of them is specified, then it will result in a single, fully populated vertex
// QUESTION(antaljanosbenjamin): Does the `__mg__labels` is necessary? What about passing the `labels` function
// as an expression? Maybe it is an optimization. For communicating the vertex id
// the Vertex struct is really handy.
3: optional list<binary> props_to_return;
4: list<Expression> expressions;
5: optional i64 limit;
6: View view;
7: optional Filter filter;
}
struct ScanVerticesResponse {
1: Result result;
2: Values values;
3: optional map<i64, binary> (cpp.template = "std::unordered_map") property_name_map;
// contains the next start_id if there is any
4: optional VertexId next_start_id;
}
union VertexOrEdgeIds {
1: list<VertexId> vertex_ids;
2: list<EdgeId> edge_ids;
}
struct GetPropertiesRequest {
1: i64 transaction_id;
2: VertexOrEdgeIds vertex_or_edge_ids;
3: list<binary> property_names;
4: list<Expression> expressions;
5: bool only_unique = false;
6: optional list<OrderBy> order_by;
7: optional i64 limit;
8: optional Filter filter;
}
struct GetPropertiesResponse {
1: Values values;
2: optional map<i64, binary> (cpp.template = "std::unordered_map") property_name_map;
}
enum EdgeDirection {
OUT = 1;
IN = 2;
BOTH = 3;
}
struct ExpandOneRequest {
1: i64 transaction_id;
2: list<VertexId> src_vertices;
3: list<EdgeType> edge_types;
4: EdgeDirection direction;
5: bool only_unique_neighbor_rows = false;
// The empty optional means return all of the properties, while an empty
// list means do not return any properties
// TODO(antaljanosbenjamin): All of the special values should be communicated through a single vertex object
// after schema is implemented
// Special values are accepted:
// * __mg__labels
6: optional list<binary> src_vertex_properties;
// TODO(antaljanosbenjamin): All of the special values should be communicated through a single vertex object
// after schema is implemented
// Special values are accepted:
// * __mg__dst_id (Vertex, but without labels)
// * __mg__type (binary)
7: optional list<binary> edge_properties;
// QUESTION(antaljanosbenjamin): Maybe also add possibility to expressions evaluated on the source vertex?
// List of expressions evaluated on edges
8: list<Expression> expressions;
9: optional list<OrderBy> order_by;
10: optional i64 limit;
11: optional Filter filter;
}
struct ExpandOneResultRow {
// NOTE: This struct could be a single Values with columns something like this:
// src_vertex(Vertex), vertex_prop1(Value), vertex_prop2(Value), edges(list<Value>)
// where edges might be a list of:
// 1. list<Value> if only a defined list of edge properties are returned
// 2. map<binary, Value> if all of the edge properties are returned
// The drawback of this is currently the key of the map is always interpreted as a string in Value, not as an
// integer, which should be in case of mapped properties.
1: Vertex src_vertex;
2: optional Values src_vertex_properties;
3: Values edges;
}
struct ExpandOneResponse {
// This approach might not suit the expand with per shard parrallelization,
// because the property_name_map has to be accessed from multiple threads
// in order to avoid duplicated keys (two threads might map the same
// property with different numbers) and multiple passes (to unify the
// mapping amond result returned from different shards).
1: list<ExpandOneResultRow> result;
2: optional map<i64, binary> (cpp.template = "std::unordered_map") property_name_map;
}
struct NewVertex {
1: list<i64> label_ids;
2: map<i64, Value> properties;
}
struct CreateVerticesRequest {
1: required i64 transaction_id;
2: map<i64, binary> (cpp.template = "std::unordered_map") labels_name_map;
3: map<i64, binary> (cpp.template = "std::unordered_map") property_name_map;
4: list<NewVertex> new_vertices;
}
service Storage {
i64 startTransaction()
Result commitTransaction(1: i64 transaction_id)
void abortTransaction(1: i64 transaction_id)
Result createVertices(1: CreateVerticesRequest req)
ScanVerticesResponse scanVertices(1: ScanVerticesRequest req)
GetPropertiesResponse getProperties(1: GetPropertiesRequest req)
ExpandOneResponse expandOne(1: ExpandOneRequest req)
}

View File

@@ -0,0 +1,11 @@
struct Ping {
1: binary message;
}
struct Pong{
1: binary message;
}
service PingPong {
Pong ping(1: Ping req)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -9,34 +9,52 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include <netdb.h>
#include <cstring>
#include "io/network/addrinfo.hpp"
#include <concepts>
#include <iterator>
#include "io/network/network_error.hpp"
namespace io::network {
namespace memgraph::io::network {
AddrInfo::AddrInfo(struct addrinfo *info) : info(info) {}
static_assert(std::forward_iterator<AddrInfo::Iterator> && std::equality_comparable<AddrInfo::Iterator>);
AddrInfo::~AddrInfo() { freeaddrinfo(info); }
AddrInfo AddrInfo::Get(const char *addr, const char *port) {
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; // IPv4 and IPv6
hints.ai_socktype = SOCK_STREAM; // TCP socket
hints.ai_flags = AI_PASSIVE;
struct addrinfo *result;
auto status = getaddrinfo(addr, port, &hints, &result);
AddrInfo::AddrInfo(const Endpoint &endpoint) : AddrInfo(endpoint.address, endpoint.port) {}
AddrInfo::AddrInfo(const std::string &addr, uint16_t port) : info_{nullptr, nullptr} {
addrinfo hints{
.ai_flags = AI_PASSIVE,
.ai_family = AF_UNSPEC, // IPv4 and IPv6
.ai_socktype = SOCK_STREAM // TCP socket
};
addrinfo *info = nullptr;
auto status = getaddrinfo(addr.c_str(), std::to_string(port).c_str(), &hints, &info);
if (status != 0) throw NetworkError(gai_strerror(status));
return AddrInfo(result);
info_ = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>(info, &freeaddrinfo);
}
AddrInfo::operator struct addrinfo *() { return info; }
} // namespace io::network
AddrInfo::Iterator::Iterator(addrinfo *p) noexcept : ptr_(p) {}
AddrInfo::Iterator::reference AddrInfo::Iterator::operator*() const noexcept { return *ptr_; }
AddrInfo::Iterator::pointer AddrInfo::Iterator::operator->() const noexcept { return ptr_; }
// NOLINTNEXTLINE(cert-dcl21-cpp)
AddrInfo::Iterator AddrInfo::Iterator::operator++(int) noexcept {
auto it = *this;
++(*this);
return it;
}
AddrInfo::Iterator &AddrInfo::Iterator::operator++() noexcept {
ptr_ = ptr_->ai_next;
return *this;
}
bool operator==(const AddrInfo::Iterator &lhs, const AddrInfo::Iterator &rhs) noexcept { return lhs.ptr_ == rhs.ptr_; };
bool operator!=(const AddrInfo::Iterator &lhs, const AddrInfo::Iterator &rhs) noexcept { return !(lhs == rhs); };
void swap(AddrInfo::Iterator &lhs, AddrInfo::Iterator &rhs) noexcept { std::swap(lhs.ptr_, rhs.ptr_); };
} // namespace memgraph::io::network

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Memgraph Ltd.
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -11,23 +11,53 @@
#pragma once
namespace io::network {
#include <netdb.h>
#include <iterator>
#include <memory>
#include <string>
#include "io/network/endpoint.hpp"
namespace memgraph::io::network {
/**
* Wrapper class for getaddrinfo.
* see: man 3 getaddrinfo
*/
class AddrInfo {
explicit AddrInfo(struct addrinfo *info);
public:
~AddrInfo();
struct Iterator {
using iterator_category = std::forward_iterator_tag;
using value_type = addrinfo;
using difference_type = std::ptrdiff_t;
using pointer = addrinfo *;
using reference = addrinfo &;
static AddrInfo Get(const char *addr, const char *port);
Iterator() = default;
Iterator(const Iterator &) = default;
explicit Iterator(addrinfo *p) noexcept;
Iterator &operator=(const Iterator &) = default;
reference operator*() const noexcept;
pointer operator->() const noexcept;
Iterator operator++(int) noexcept;
Iterator &operator++() noexcept;
operator struct addrinfo *();
friend bool operator==(const Iterator &lhs, const Iterator &rhs) noexcept;
friend bool operator!=(const Iterator &lhs, const Iterator &rhs) noexcept;
friend void swap(Iterator &lhs, Iterator &rhs) noexcept;
private:
addrinfo *ptr_{nullptr};
};
AddrInfo(const std::string &addr, uint16_t port);
explicit AddrInfo(const Endpoint &endpoint);
auto begin() const noexcept { return Iterator(info_.get()); }
auto end() const noexcept { return Iterator{nullptr}; }
private:
struct addrinfo *info;
std::unique_ptr<addrinfo, void (*)(addrinfo *)> info_;
};
} // namespace io::network
} // namespace memgraph::io::network

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