rsmgp_sys/memgraph/
mod.rs

1// Copyright (c) 2016-2021 Memgraph Ltd. [https://memgraph.com]
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//! Abstraction to interact with Memgraph.
15
16use std::ffi::CStr;
17
18use crate::list::*;
19use crate::mgp::*;
20use crate::result::*;
21use crate::rsmgp::*;
22use crate::vertex::*;
23// Required here, if not present, tests linking fails.
24#[double]
25use crate::mgp::ffi;
26use mockall_double::double;
27
28/// Combines the given array of types from left to right to construct [mgp_type]. E.g., if the
29/// input is [Type::List, Type::Int], the constructed [mgp_type] is going to be list of integers.
30fn resolve_mgp_type(types: &[Type]) -> *const mgp_type {
31    unsafe {
32        let mut mgp_type: *const mgp_type = std::ptr::null_mut();
33        for field_type in types.iter().rev() {
34            mgp_type = match field_type {
35                Type::Any => ffi::mgp_type_any(),
36                Type::Bool => ffi::mgp_type_bool(),
37                Type::Number => ffi::mgp_type_number(),
38                Type::Int => ffi::mgp_type_int(),
39                Type::Double => ffi::mgp_type_float(),
40                Type::String => ffi::mgp_type_string(),
41                Type::Map => ffi::mgp_type_map(),
42                Type::Vertex => ffi::mgp_type_node(),
43                Type::Edge => ffi::mgp_type_relationship(),
44                Type::Path => ffi::mgp_type_path(),
45                Type::Nullable => ffi::mgp_type_nullable(mgp_type),
46                Type::List => ffi::mgp_type_list(mgp_type),
47            };
48        }
49        mgp_type
50    }
51}
52
53/// Main object to interact with Memgraph instance.
54#[derive(Clone)]
55pub struct Memgraph {
56    args: *const mgp_list,
57    graph: *const mgp_graph,
58    result: *mut mgp_result,
59    memory: *mut mgp_memory,
60    module: *mut mgp_module,
61}
62
63impl Memgraph {
64    /// Create a new Memgraph object.
65    ///
66    /// Required to be public because the required pointers have to passed during module
67    /// initialization and procedure call phase.
68    pub fn new(
69        args: *const mgp_list,
70        graph: *const mgp_graph,
71        result: *mut mgp_result,
72        memory: *mut mgp_memory,
73        module: *mut mgp_module,
74    ) -> Memgraph {
75        Memgraph {
76            args,
77            graph,
78            result,
79            memory,
80            module,
81        }
82    }
83
84    /// Creates a new object with all underlying data set to null. Used for the testing purposes.
85    #[cfg(test)]
86    pub(crate) fn new_default() -> Memgraph {
87        Memgraph {
88            args: std::ptr::null(),
89            graph: std::ptr::null(),
90            result: std::ptr::null_mut(),
91            memory: std::ptr::null_mut(),
92            module: std::ptr::null_mut(),
93        }
94    }
95
96    /// Arguments passed to the procedure call.
97    pub fn args(&self) -> MgpResult<List> {
98        // TODO(gitbuda): Avoid list copy when accessing procedure arguments.
99        unsafe { List::mgp_copy(self.args_ptr(), &self) }
100    }
101
102    /// Returns pointer to the object with all arguments passed to the procedure call.
103    pub(crate) fn args_ptr(&self) -> *const mgp_list {
104        self.args
105    }
106
107    /// Returns pointer to the object with graph data.
108    pub(crate) fn graph_ptr(&self) -> *const mgp_graph {
109        self.graph
110    }
111
112    /// Returns pointer to the object where results could be stored.
113    pub(crate) fn result_ptr(&self) -> *mut mgp_result {
114        self.result
115    }
116
117    /// Returns pointer to the memory object for advanced memory control.
118    pub(crate) fn memory_ptr(&self) -> *mut mgp_memory {
119        self.memory
120    }
121
122    /// Returns pointer to the module object.
123    pub fn module_ptr(&self) -> *mut mgp_module {
124        self.module
125    }
126
127    pub fn vertices_iter(&self) -> MgpResult<VerticesIterator> {
128        unsafe {
129            let mgp_iterator = ffi::mgp_graph_iter_vertices(self.graph_ptr(), self.memory_ptr());
130            if mgp_iterator.is_null() {
131                return Err(MgpError::UnableToCreateGraphVerticesIterator);
132            }
133            Ok(VerticesIterator::new(mgp_iterator, &self))
134        }
135    }
136
137    pub fn vertex_by_id(&self, id: i64) -> MgpResult<Vertex> {
138        unsafe {
139            let mgp_vertex = ffi::mgp_graph_get_vertex_by_id(
140                self.graph_ptr(),
141                mgp_vertex_id { as_int: id },
142                self.memory_ptr(),
143            );
144            if mgp_vertex.is_null() {
145                return Err(MgpError::UnableToFindVertexById);
146            }
147            Ok(Vertex::new(mgp_vertex, &self))
148        }
149    }
150
151    /// Creates a new result record.
152    ///
153    /// Keep this object on the stack and add data that will be returned to Memgraph / client
154    /// during/after the procedure call.
155    pub fn result_record(&self) -> MgpResult<ResultRecord> {
156        ResultRecord::create(self)
157    }
158
159    /// Registers a new read procedure.
160    ///
161    /// * `proc_ptr` - Identifier of the top level C function that represents the procedure.
162    /// * `name` - A string that will be registered as a procedure name inside Memgraph instance.
163    /// * `required_arg_types` - An array of all [NamedType]s, each one define by name and an array
164    ///    of [Type]s.
165    /// * `optional_arg_types` - An array of all [OptionalNamedType]s, each one defined by name, an
166    ///    array of [Type]s, and default value.
167    /// * `result_field_types` - An array of all [NamedType]s, each one defined by name and an
168    ///    array of [Type]s.
169    pub fn add_read_procedure(
170        &self,
171        proc_ptr: extern "C" fn(
172            *const mgp_list,
173            *const mgp_graph,
174            *mut mgp_result,
175            *mut mgp_memory,
176        ),
177        name: &CStr,
178        required_arg_types: &[NamedType],
179        optional_arg_types: &[OptionalNamedType],
180        result_field_types: &[NamedType],
181    ) -> MgpResult<()> {
182        unsafe {
183            let procedure = ffi::mgp_module_add_read_procedure(
184                self.module_ptr(),
185                name.as_ptr(),
186                Some(proc_ptr),
187            );
188            if procedure.is_null() {
189                return Err(MgpError::UnableToRegisterReadProcedure);
190            }
191
192            for required_type in required_arg_types {
193                let mgp_type = resolve_mgp_type(&required_type.types);
194                if ffi::mgp_proc_add_arg(procedure, required_type.name.as_ptr(), mgp_type) == 0 {
195                    return Err(MgpError::UnableToAddRequiredArguments);
196                }
197            }
198
199            for optional_input in optional_arg_types {
200                let mgp_type = resolve_mgp_type(&optional_input.types);
201                if ffi::mgp_proc_add_opt_arg(
202                    procedure,
203                    optional_input.name.as_ptr(),
204                    mgp_type,
205                    optional_input.default.mgp_ptr(),
206                ) == 0
207                {
208                    return Err(MgpError::UnableToAddOptionalArguments);
209                }
210            }
211
212            for result_field in result_field_types {
213                let mgp_type = resolve_mgp_type(&result_field.types);
214                if result_field.deprecated {
215                    if ffi::mgp_proc_add_deprecated_result(
216                        procedure,
217                        result_field.name.as_ptr(),
218                        mgp_type,
219                    ) == 0
220                    {
221                        return Err(MgpError::UnableToAddDeprecatedReturnType);
222                    }
223                } else if ffi::mgp_proc_add_result(procedure, result_field.name.as_ptr(), mgp_type)
224                    == 0
225                {
226                    return Err(MgpError::UnableToAddReturnType);
227                }
228            }
229
230            Ok(())
231        }
232    }
233
234    /// Return `true` if the currently executing procedure should abort as soon as possible.
235    ///
236    /// Procedures which perform heavyweight processing run the risk of running too long and going
237    /// over the query execution time limit. To prevent this, such procedures should periodically
238    /// call this function at critical points in their code in order to determine whether they
239    /// should abort or not. Note that this mechanism is purely cooperative and depends on the
240    /// procedure doing the checking and aborting on its own.
241    pub fn must_abort(&self) -> bool {
242        unsafe { ffi::mgp_must_abort(self.graph_ptr()) != 0 }
243    }
244}
245
246#[cfg(test)]
247mod tests;