gqlite 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Cargo.toml +12 -6
  3. data/ext/db-index/Cargo.toml +29 -0
  4. data/ext/db-index/src/hnsw/error.rs +66 -0
  5. data/ext/db-index/src/hnsw/graph_store.rs +272 -0
  6. data/ext/db-index/src/hnsw/id.rs +36 -0
  7. data/ext/db-index/src/hnsw/implementation.rs +1517 -0
  8. data/ext/db-index/src/hnsw/kernels.rs +1228 -0
  9. data/ext/db-index/src/hnsw/metric.rs +244 -0
  10. data/ext/db-index/src/hnsw/scalar.rs +72 -0
  11. data/ext/db-index/src/hnsw/simple_store.rs +140 -0
  12. data/ext/db-index/src/hnsw/store.rs +472 -0
  13. data/ext/db-index/src/hnsw/vector.rs +105 -0
  14. data/ext/db-index/src/hnsw/vectors.rs +568 -0
  15. data/ext/db-index/src/hnsw.rs +42 -0
  16. data/ext/db-index/src/lib.rs +3 -0
  17. data/ext/gqlitedb/Cargo.toml +19 -8
  18. data/ext/gqlitedb/benches/common/pokec.rs +62 -2
  19. data/ext/gqlitedb/benches/pokec_divan.rs +66 -2
  20. data/ext/gqlitedb/benches/pokec_iai.rs +60 -3
  21. data/ext/gqlitedb/release.toml +2 -2
  22. data/ext/gqlitedb/src/aggregators/arithmetic.rs +3 -1
  23. data/ext/gqlitedb/src/aggregators/containers.rs +1 -1
  24. data/ext/gqlitedb/src/aggregators/stats.rs +21 -12
  25. data/ext/gqlitedb/src/capi.rs +20 -24
  26. data/ext/gqlitedb/src/compiler/expression_analyser.rs +28 -20
  27. data/ext/gqlitedb/src/compiler/variables_manager.rs +104 -38
  28. data/ext/gqlitedb/src/compiler.rs +506 -227
  29. data/ext/gqlitedb/src/connection.rs +151 -11
  30. data/ext/gqlitedb/src/consts.rs +1 -2
  31. data/ext/gqlitedb/src/error.rs +123 -61
  32. data/ext/gqlitedb/src/functions/common.rs +64 -0
  33. data/ext/gqlitedb/src/functions/containers.rs +2 -46
  34. data/ext/gqlitedb/src/functions/edge.rs +1 -1
  35. data/ext/gqlitedb/src/functions/math.rs +87 -15
  36. data/ext/gqlitedb/src/functions/node.rs +1 -1
  37. data/ext/gqlitedb/src/functions/path.rs +48 -11
  38. data/ext/gqlitedb/src/functions/scalar.rs +47 -4
  39. data/ext/gqlitedb/src/functions/string.rs +123 -1
  40. data/ext/gqlitedb/src/functions/value.rs +1 -1
  41. data/ext/gqlitedb/src/functions.rs +165 -28
  42. data/ext/gqlitedb/src/graph.rs +1 -9
  43. data/ext/gqlitedb/src/interpreter/evaluators.rs +968 -130
  44. data/ext/gqlitedb/src/interpreter/instructions.rs +39 -2
  45. data/ext/gqlitedb/src/lib.rs +5 -4
  46. data/ext/gqlitedb/src/parser/gql.pest +1 -1
  47. data/ext/gqlitedb/src/planner.rs +329 -0
  48. data/ext/gqlitedb/src/prelude.rs +3 -3
  49. data/ext/gqlitedb/src/store/pgrx.rs +1 -1
  50. data/ext/gqlitedb/src/store/postgres.rs +742 -16
  51. data/ext/gqlitedb/src/store/redb/hnsw_store.rs +702 -0
  52. data/ext/gqlitedb/src/store/redb/index.rs +274 -0
  53. data/ext/gqlitedb/src/store/redb.rs +1268 -113
  54. data/ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs +28 -0
  55. data/ext/gqlitedb/src/store/sqlbase/sqlstore.rs +103 -0
  56. data/ext/gqlitedb/src/store/sqlbase.rs +146 -16
  57. data/ext/gqlitedb/src/store/sqlite.rs +576 -14
  58. data/ext/gqlitedb/src/store/vector_extract.rs +56 -0
  59. data/ext/gqlitedb/src/store.rs +140 -29
  60. data/ext/gqlitedb/src/tests/compiler.rs +207 -10
  61. data/ext/gqlitedb/src/tests/connection/postgres.rs +38 -3
  62. data/ext/gqlitedb/src/tests/connection/redb.rs +23 -0
  63. data/ext/gqlitedb/src/tests/connection/sqlite.rs +31 -0
  64. data/ext/gqlitedb/src/tests/connection.rs +61 -1
  65. data/ext/gqlitedb/src/tests/evaluators.rs +162 -7
  66. data/ext/gqlitedb/src/tests/parser.rs +54 -23
  67. data/ext/gqlitedb/src/tests/planner.rs +511 -0
  68. data/ext/gqlitedb/src/tests/store/postgres.rs +7 -0
  69. data/ext/gqlitedb/src/tests/store/redb.rs +8 -0
  70. data/ext/gqlitedb/src/tests/store/sqlite.rs +8 -0
  71. data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +182 -0
  72. data/ext/gqlitedb/src/tests/store/vector_index/redb.rs +386 -0
  73. data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +166 -0
  74. data/ext/gqlitedb/src/tests/store/vector_index.rs +2313 -0
  75. data/ext/gqlitedb/src/tests/store.rs +78 -14
  76. data/ext/gqlitedb/src/tests/templates/ast.rs +92 -16
  77. data/ext/gqlitedb/src/tests/templates/programs.rs +14 -7
  78. data/ext/gqlitedb/src/tests.rs +15 -9
  79. data/ext/gqlitedb/src/utils.rs +6 -1
  80. data/ext/gqlitedb/src/value/compare.rs +61 -3
  81. data/ext/gqlitedb/src/value.rs +136 -7
  82. data/ext/gqlitedb/templates/sql/postgres/metadata_delete.sql +1 -0
  83. data/ext/gqlitedb/templates/sql/postgres/node_select.sql +1 -1
  84. data/ext/gqlitedb/templates/sql/sqlite/metadata_delete.sql +1 -0
  85. data/ext/gqliterb/src/lib.rs +53 -8
  86. data/ext/gqlparser/Cargo.toml +25 -0
  87. data/ext/gqlparser/README.MD +9 -0
  88. data/ext/gqlparser/benches/pokec_divan.rs +34 -0
  89. data/ext/gqlparser/src/common.rs +69 -0
  90. data/ext/gqlparser/src/gqls/ast.rs +69 -0
  91. data/ext/gqlparser/src/gqls/constraint.rs +79 -0
  92. data/ext/gqlparser/src/gqls/error.rs +22 -0
  93. data/ext/gqlparser/src/gqls/parser.rs +813 -0
  94. data/ext/gqlparser/src/gqls/prelude.rs +8 -0
  95. data/ext/gqlparser/src/gqls/properties.rs +115 -0
  96. data/ext/gqlparser/src/gqls/resolve.rs +207 -0
  97. data/ext/gqlparser/src/gqls.rs +265 -0
  98. data/ext/gqlparser/src/lib.rs +7 -0
  99. data/ext/gqlparser/src/oc/ast.rs +680 -0
  100. data/ext/gqlparser/src/oc/error.rs +172 -0
  101. data/ext/gqlparser/src/oc/lexer.rs +429 -0
  102. data/ext/gqlparser/src/oc/parser/tests.rs +2284 -0
  103. data/ext/gqlparser/src/oc/parser.rs +2005 -0
  104. data/ext/gqlparser/src/oc.rs +33 -0
  105. data/ext/gqlparser/src/prelude.rs +3 -0
  106. data/ext/graphcore/Cargo.toml +1 -0
  107. data/ext/graphcore/src/error.rs +26 -0
  108. data/ext/graphcore/src/graph.rs +177 -51
  109. data/ext/graphcore/src/lib.rs +4 -2
  110. data/ext/graphcore/src/open_cypher.rs +12 -0
  111. data/ext/graphcore/src/table.rs +50 -2
  112. data/ext/graphcore/src/timestamp.rs +127 -104
  113. data/ext/graphcore/src/value/tensor.rs +739 -0
  114. data/ext/graphcore/src/value/value_map.rs +1 -1
  115. data/ext/graphcore/src/value.rs +343 -19
  116. metadata +93 -28
  117. data/ext/gqlitedb/src/parser/ast.rs +0 -604
  118. data/ext/gqlitedb/src/parser/parser_impl.rs +0 -1213
  119. data/ext/gqlitedb/src/parser.rs +0 -4
@@ -1,10 +1,10 @@
1
1
  mod compare;
2
2
  mod contains;
3
3
 
4
- pub(crate) use compare::{compare, Ordering};
5
- pub(crate) use contains::{contains, ContainResult};
4
+ pub(crate) use compare::{Ordering, compare};
5
+ pub(crate) use contains::{ContainResult, contains};
6
6
 
7
- pub use graphcore::{array, value_map, TimeStamp, Value, ValueMap, ValueTryIntoRef};
7
+ pub use graphcore::{DType, Tensor, TimeStamp, Value, ValueMap, ValueTryIntoRef, array, value_map};
8
8
 
9
9
  pub(crate) trait ValueExt
10
10
  {
@@ -115,6 +115,36 @@ impl ValueExt for Value
115
115
  Value::Float(rhs) => orderability_float(lhs, rhs),
116
116
  _ => std::cmp::Ordering::Greater,
117
117
  },
118
+ Value::Tensor(lhs) => match rhs
119
+ {
120
+ Value::Null | Value::Key(..) => std::cmp::Ordering::Less,
121
+ Value::Tensor(rhs) =>
122
+ {
123
+ let dc = (lhs.dtype() as u8).cmp(&(rhs.dtype() as u8));
124
+ if dc != std::cmp::Ordering::Equal
125
+ {
126
+ return dc;
127
+ }
128
+ let sc = lhs.shape().cmp(rhs.shape());
129
+ if sc != std::cmp::Ordering::Equal
130
+ {
131
+ return sc;
132
+ }
133
+ lhs.as_bytes().cmp(rhs.as_bytes())
134
+ }
135
+ Value::Array(rhs) =>
136
+ {
137
+ let left_arr = Value::Array(
138
+ lhs
139
+ .to_f32_vec()
140
+ .into_iter()
141
+ .map(|f| Value::Float(f as f64))
142
+ .collect(),
143
+ );
144
+ left_arr.orderability(&Value::Array(rhs.clone()))
145
+ }
146
+ _ => std::cmp::Ordering::Greater,
147
+ },
118
148
  Value::Boolean(lhs) => match rhs
119
149
  {
120
150
  Value::Null | Value::Key(..) | Value::Integer(..) | Value::Float(..) =>
@@ -145,7 +175,7 @@ impl ValueExt for Value
145
175
  Value::TimeStamp(rhs) => lhs.cmp(rhs),
146
176
  _ => std::cmp::Ordering::Greater,
147
177
  },
148
- Value::Path(lhs) => match rhs
178
+ Value::SinglePath(lhs) => match rhs
149
179
  {
150
180
  Value::Null
151
181
  | Value::Key(..)
@@ -154,7 +184,7 @@ impl ValueExt for Value
154
184
  | Value::Boolean(..)
155
185
  | Value::String(..)
156
186
  | Value::TimeStamp(..) => std::cmp::Ordering::Less,
157
- Value::Path(rhs) =>
187
+ Value::SinglePath(rhs) =>
158
188
  {
159
189
  match orderability_map(lhs.source().properties(), rhs.source().properties())
160
190
  {
@@ -172,6 +202,97 @@ impl ValueExt for Value
172
202
  o => o,
173
203
  }
174
204
  }
205
+ Value::Path(rhs) =>
206
+ {
207
+ if rhs.elements_count() == 1
208
+ {
209
+ match orderability_map(lhs.source().properties(), rhs.source().properties())
210
+ {
211
+ std::cmp::Ordering::Equal =>
212
+ {
213
+ match orderability_map(lhs.properties(), rhs.elements()[0].0.properties())
214
+ {
215
+ std::cmp::Ordering::Equal => orderability_map(
216
+ lhs.destination().properties(),
217
+ rhs.elements()[0].1.properties(),
218
+ ),
219
+ o => o,
220
+ }
221
+ }
222
+ o => o,
223
+ }
224
+ }
225
+ else
226
+ {
227
+ // rhs has more elements
228
+ std::cmp::Ordering::Less
229
+ }
230
+ }
231
+ _ => std::cmp::Ordering::Greater,
232
+ },
233
+ Value::Path(lhs) => match rhs
234
+ {
235
+ Value::Null
236
+ | Value::Key(..)
237
+ | Value::Integer(..)
238
+ | Value::Float(..)
239
+ | Value::Boolean(..)
240
+ | Value::String(..)
241
+ | Value::TimeStamp(..) => std::cmp::Ordering::Less,
242
+ Value::SinglePath(rhs) =>
243
+ {
244
+ if lhs.elements_count() == 1
245
+ {
246
+ match orderability_map(lhs.source().properties(), rhs.source().properties())
247
+ {
248
+ std::cmp::Ordering::Equal =>
249
+ {
250
+ match orderability_map(lhs.elements()[0].0.properties(), rhs.properties())
251
+ {
252
+ std::cmp::Ordering::Equal => orderability_map(
253
+ lhs.elements()[0].1.properties(),
254
+ rhs.destination().properties(),
255
+ ),
256
+ o => o,
257
+ }
258
+ }
259
+ o => o,
260
+ }
261
+ }
262
+ else
263
+ {
264
+ // lhs has more elements
265
+ std::cmp::Ordering::Greater
266
+ }
267
+ }
268
+ Value::Path(rhs) =>
269
+ {
270
+ match orderability_map(lhs.source().properties(), rhs.source().properties())
271
+ {
272
+ std::cmp::Ordering::Equal =>
273
+ {
274
+ for ((lhs_edge, lhs_destination), (rhs_edge, rhs_destination)) in
275
+ lhs.elements().iter().zip(rhs.elements().iter())
276
+ {
277
+ match orderability_map(lhs_edge.properties(), rhs_edge.properties())
278
+ {
279
+ std::cmp::Ordering::Equal => match orderability_map(
280
+ lhs_destination.properties(),
281
+ rhs_destination.properties(),
282
+ )
283
+ {
284
+ std::cmp::Ordering::Equal =>
285
+ {}
286
+ o => return o,
287
+ },
288
+ o => return o,
289
+ }
290
+ }
291
+ std::cmp::Ordering::Equal
292
+ }
293
+ o => o,
294
+ }
295
+ }
175
296
  _ => std::cmp::Ordering::Greater,
176
297
  },
177
298
  Value::Array(lhs) => match rhs
@@ -183,7 +304,9 @@ impl ValueExt for Value
183
304
  | Value::Boolean(..)
184
305
  | Value::String(..)
185
306
  | Value::TimeStamp(..)
186
- | Value::Path(..) => std::cmp::Ordering::Less,
307
+ | Value::SinglePath(..)
308
+ | Value::Path(..)
309
+ | Value::Tensor(..) => std::cmp::Ordering::Less,
187
310
  Value::Array(rhs) => lhs
188
311
  .iter()
189
312
  .zip(rhs.iter())
@@ -201,8 +324,10 @@ impl ValueExt for Value
201
324
  | Value::Boolean(..)
202
325
  | Value::String(..)
203
326
  | Value::TimeStamp(..)
327
+ | Value::SinglePath(..)
204
328
  | Value::Path(..)
205
- | Value::Array(..) => std::cmp::Ordering::Less,
329
+ | Value::Array(..)
330
+ | Value::Tensor(..) => std::cmp::Ordering::Less,
206
331
  Value::Edge(rhs) => orderability_map(lhs.properties(), rhs.properties()),
207
332
  _ => std::cmp::Ordering::Greater,
208
333
  },
@@ -215,8 +340,10 @@ impl ValueExt for Value
215
340
  | Value::Boolean(..)
216
341
  | Value::String(..)
217
342
  | Value::TimeStamp(..)
343
+ | Value::SinglePath(..)
218
344
  | Value::Path(..)
219
345
  | Value::Array(..)
346
+ | Value::Tensor(..)
220
347
  | Value::Edge(..) => std::cmp::Ordering::Less,
221
348
  Value::Node(rhs) => orderability_map(lhs.properties(), rhs.properties()),
222
349
  _ => std::cmp::Ordering::Greater,
@@ -230,8 +357,10 @@ impl ValueExt for Value
230
357
  | Value::Boolean(..)
231
358
  | Value::String(..)
232
359
  | Value::TimeStamp(..)
360
+ | Value::SinglePath(..)
233
361
  | Value::Path(..)
234
362
  | Value::Array(..)
363
+ | Value::Tensor(..)
235
364
  | Value::Edge(..)
236
365
  | Value::Node(..) => std::cmp::Ordering::Less,
237
366
  Value::Map(rhs) => orderability_map(lhs, rhs),
@@ -0,0 +1 @@
1
+ DELETE FROM gqlite_metadata WHERE name = $1
@@ -4,7 +4,7 @@ WHERE
4
4
  -- Filter by key list (if not empty)
5
5
  {% if let Some(keys_var) = keys_var %}
6
6
  (
7
- nodes.node_key = ANY( ${{ keys_var }}::uuid[] )
7
+ node_key = ANY(${{ keys_var }})
8
8
  )
9
9
  {% else %}
10
10
  TRUE
@@ -0,0 +1 @@
1
+ DELETE FROM gqlite_metadata WHERE name = ?
@@ -2,13 +2,12 @@ use std::fmt::Display;
2
2
 
3
3
  use gqlitedb::TimeStamp;
4
4
  use magnus::{
5
- function, method,
5
+ Error, ExceptionClass, Integer, IntoValue, RModule, Ruby, Symbol, function, method,
6
6
  prelude::*,
7
7
  r_array,
8
8
  r_hash::{self, ForEach},
9
9
  scan_args,
10
10
  value::Lazy,
11
- Error, ExceptionClass, Integer, IntoValue, RModule, Ruby, Symbol,
12
11
  };
13
12
 
14
13
  static MODULE: Lazy<RModule> = Lazy::new(|ruby| ruby.define_module("GQLite").unwrap());
@@ -125,8 +124,12 @@ fn integer_from_u128(ruby: &Ruby, i: u128) -> Result<Integer, Error>
125
124
  fn node_to_rhash(ruby: &Ruby, node: gqlitedb::Node) -> Result<magnus::Value, Error>
126
125
  {
127
126
  let r_hash = ruby.hash_new();
128
- let (key, labels, properties) = node.unpack();
127
+ let (graph_name, key, labels, properties) = node.unpack();
129
128
  r_hash.aset("type", "node")?;
129
+ if let Some(graph_name) = graph_name
130
+ {
131
+ r_hash.aset("graph_name", graph_name)?;
132
+ }
130
133
  r_hash.aset("key", integer_from_u128(ruby, key.into())?)?;
131
134
  r_hash.aset("labels", labels)?;
132
135
  r_hash.aset("properties", to_rhash(ruby, properties)?)?;
@@ -136,19 +139,28 @@ fn node_to_rhash(ruby: &Ruby, node: gqlitedb::Node) -> Result<magnus::Value, Err
136
139
  fn edge_to_rhash(ruby: &Ruby, edge: gqlitedb::Edge) -> Result<magnus::Value, Error>
137
140
  {
138
141
  let r_hash = ruby.hash_new();
139
- let (key, labels, properties) = edge.unpack();
142
+ let (graph_name, key, labels, properties) = edge.unpack();
140
143
  r_hash.aset("type", "edge")?;
144
+ if let Some(graph_name) = graph_name
145
+ {
146
+ r_hash.aset("graph_name", graph_name)?;
147
+ }
141
148
  r_hash.aset("key", integer_from_u128(ruby, key.into())?)?;
142
149
  r_hash.aset("labels", labels)?;
143
150
  r_hash.aset("properties", to_rhash(ruby, properties)?)?;
144
151
  Ok(r_hash.into_value_with(ruby))
145
152
  }
146
153
 
147
- fn path_to_rhash(ruby: &Ruby, path: gqlitedb::Path) -> Result<magnus::Value, Error>
154
+ fn single_path_to_rhash(ruby: &Ruby, path: gqlitedb::SinglePath) -> Result<magnus::Value, Error>
148
155
  {
149
156
  let r_hash = ruby.hash_new();
150
- let (key, source, labels, properties, destination) = path.unpack();
151
- r_hash.aset("type", "path")?;
157
+ let (source, edge, destination) = path.unpack();
158
+ r_hash.aset("type", "single_path")?;
159
+ let (graph_name, key, labels, properties) = edge.unpack();
160
+ if let Some(graph_name) = graph_name
161
+ {
162
+ r_hash.aset("graph_name", graph_name)?;
163
+ }
152
164
  r_hash.aset("key", integer_from_u128(ruby, key.into())?)?;
153
165
  r_hash.aset("labels", labels)?;
154
166
  r_hash.aset("properties", to_rhash(ruby, properties)?)?;
@@ -157,6 +169,29 @@ fn path_to_rhash(ruby: &Ruby, path: gqlitedb::Path) -> Result<magnus::Value, Err
157
169
  Ok(r_hash.into_value_with(ruby))
158
170
  }
159
171
 
172
+ fn path_to_rhash(ruby: &Ruby, path: gqlitedb::Path) -> Result<magnus::Value, Error>
173
+ {
174
+ let r_hash = ruby.hash_new();
175
+ let (source, elements) = path.unpack();
176
+ r_hash.aset("type", "path")?;
177
+ r_hash.aset("source", node_to_rhash(ruby, source)?)?;
178
+
179
+ let r_elements = ruby.ary_new();
180
+
181
+ for el in elements.into_iter().map(|(edge, destination)| {
182
+ let r_hash_element = ruby.hash_new();
183
+ r_hash_element.aset("edge", edge_to_rhash(ruby, edge)?)?;
184
+ r_hash_element.aset("destination", node_to_rhash(ruby, destination)?)?;
185
+ Ok::<_, Error>(r_hash_element)
186
+ })
187
+ {
188
+ r_elements.push(el?)?;
189
+ }
190
+
191
+ r_hash.aset("elements", r_elements)?;
192
+ Ok(r_hash.into_value_with(ruby))
193
+ }
194
+
160
195
  fn to_rvalue(ruby: &Ruby, val: gqlitedb::Value) -> Result<magnus::Value, Error>
161
196
  {
162
197
  match val
@@ -172,7 +207,17 @@ fn to_rvalue(ruby: &Ruby, val: gqlitedb::Value) -> Result<magnus::Value, Error>
172
207
  gqlitedb::Value::Null => Ok(ruby.qnil().into_value_with(ruby)),
173
208
  gqlitedb::Value::Edge(e) => Ok(edge_to_rhash(ruby, e)?),
174
209
  gqlitedb::Value::Node(n) => Ok(node_to_rhash(ruby, n)?),
175
- gqlitedb::Value::Path(p) => Ok(path_to_rhash(ruby, p)?),
210
+ gqlitedb::Value::SinglePath(p) => Ok(single_path_to_rhash(ruby, *p)?),
211
+ gqlitedb::Value::Path(p) => Ok(path_to_rhash(ruby, *p)?),
212
+ gqlitedb::Value::Tensor(t) =>
213
+ {
214
+ // Represent tensor as an array of floats in Ruby
215
+ let arr = t
216
+ .into_iter()
217
+ .map(|f| gqlitedb::Value::Float(f as f64))
218
+ .collect();
219
+ Ok(to_rarray(ruby, arr)?.into_value_with(ruby))
220
+ }
176
221
  }
177
222
  }
178
223
 
@@ -0,0 +1,25 @@
1
+ [package]
2
+ name = "gqlparser"
3
+ description = "GQL Parsing library, including GQLS."
4
+ readme = "README.MD"
5
+ version.workspace = true
6
+ edition.workspace = true
7
+ license.workspace = true
8
+ homepage.workspace = true
9
+ repository.workspace = true
10
+
11
+ [dependencies]
12
+ ccutils = { workspace = true, features = ["static_asserts"] }
13
+ graphcore = { workspace = true }
14
+ indexmap = { workspace = true }
15
+ logos = "0.16"
16
+ nom = "8"
17
+ nom-language = "0.1"
18
+ thiserror = { workspace = true }
19
+
20
+ [dev-dependencies]
21
+ divan.workspace = true
22
+
23
+ [[bench]]
24
+ name = "pokec_divan"
25
+ harness = false
@@ -0,0 +1,9 @@
1
+ gqlparser
2
+ =========
3
+
4
+ Home of the future GQL parser for [GQLite](http://gqlite.org). Currently only supporter GQL-Schemas, a GQLite extension to GQL for expressing Graph constraints.
5
+
6
+ GQL-Schemas
7
+ -----------
8
+
9
+ `Schema Validation and Evolution for Graph Databases`, Bonifati et al, International Conference on Conceptual Modeling, 2019 [arxiv](https://arxiv.org/pdf/1902.06427).
@@ -0,0 +1,34 @@
1
+ fn main()
2
+ {
3
+ if std::fs::exists("gqlite_bench_data").unwrap()
4
+ {
5
+ std::process::Command::new("git")
6
+ .arg("pull")
7
+ .spawn()
8
+ .unwrap()
9
+ .wait()
10
+ .unwrap();
11
+ }
12
+ else
13
+ {
14
+ std::process::Command::new("git")
15
+ .arg("clone")
16
+ .arg("https://gitlab.com/auksys/data/gqlite_bench.git")
17
+ .arg("gqlite_bench_data")
18
+ .spawn()
19
+ .unwrap()
20
+ .wait()
21
+ .unwrap();
22
+ }
23
+ // Run registered benchmarks.
24
+ divan::main();
25
+ }
26
+
27
+ // Parse the small `pokec` dataset.
28
+ #[divan::bench()]
29
+ fn parse_small_pokec()
30
+ {
31
+ let filename = "gqlite_bench_data/pokec_small_import.cypher";
32
+ let import_query = std::fs::read_to_string(filename).unwrap();
33
+ let _ = gqlparser::oc::parse_oc_query(&import_query);
34
+ }
@@ -0,0 +1,69 @@
1
+ use nom::{
2
+ AsChar, Emit, IResult, Input, Mode as _, OutputM, OutputMode, PResult, Parser,
3
+ character::complete::multispace0,
4
+ error::{FromExternalError, ParseError},
5
+ sequence::preceded,
6
+ };
7
+ use nom_language::error::VerboseError;
8
+
9
+ pub(crate) type VResult<I, O> = IResult<I, O, VerboseError<I>>;
10
+
11
+ pub(crate) fn ws0<I, E: ParseError<I>, F>(
12
+ f: F,
13
+ ) -> impl Parser<I, Output = <F as Parser<I>>::Output, Error = E>
14
+ where
15
+ F: Parser<I, Error = E>,
16
+ I: Input,
17
+ <I as Input>::Item: AsChar,
18
+ {
19
+ preceded(multispace0, f)
20
+ }
21
+
22
+ pub fn map_res_failure<I: Clone, O, E: ParseError<I> + FromExternalError<I, E2>, E2, F, G>(
23
+ parser: F,
24
+ g: G,
25
+ ) -> impl Parser<I, Output = O, Error = E>
26
+ where
27
+ F: Parser<I, Error = E>,
28
+ G: FnMut(<F as Parser<I>>::Output) -> Result<O, E2>,
29
+ {
30
+ MapResFailure { f: parser, g }
31
+ }
32
+
33
+ /// Implementation of `Parser::map_res`
34
+ pub struct MapResFailure<F, G>
35
+ {
36
+ f: F,
37
+ g: G,
38
+ }
39
+
40
+ impl<I, O2, E2, F, G> Parser<I> for MapResFailure<F, G>
41
+ where
42
+ I: Clone,
43
+ <F as Parser<I>>::Error: FromExternalError<I, E2>,
44
+ F: Parser<I>,
45
+ G: FnMut(<F as Parser<I>>::Output) -> Result<O2, E2>,
46
+ {
47
+ type Output = O2;
48
+ type Error = <F as Parser<I>>::Error;
49
+
50
+ fn process<OM: OutputMode>(&mut self, i: I) -> PResult<OM, I, Self::Output, Self::Error>
51
+ {
52
+ let (input, o1) = self
53
+ .f
54
+ .process::<OutputM<Emit, OM::Error, OM::Incomplete>>(i.clone())?;
55
+
56
+ match (self.g)(o1)
57
+ {
58
+ Ok(o2) => Ok((input, OM::Output::bind(|| o2))),
59
+ Err(e) => Err(nom::Err::Failure(
60
+ <F as Parser<I>>::Error::from_external_error(i, nom::error::ErrorKind::MapRes, e),
61
+ )),
62
+ }
63
+ }
64
+ }
65
+
66
+ pub(crate) fn fixed_digit<const N: usize>(input: &str) -> VResult<&str, &str>
67
+ {
68
+ nom::bytes::complete::take_while_m_n(N, N, |c: char| c.is_ascii_digit())(input)
69
+ }
@@ -0,0 +1,69 @@
1
+ //! AST module
2
+
3
+ use indexmap::IndexMap;
4
+
5
+ use crate::gqls::prelude::*;
6
+
7
+ /// Abstract Syntax Tree of the schema
8
+ #[derive(Debug)]
9
+ pub struct Ast
10
+ {
11
+ /// List of elements
12
+ pub elements: IndexMap<String, PropertiesDefinition>,
13
+ /// List of nodes
14
+ pub nodes: Vec<Node>,
15
+ /// List of edges
16
+ pub edges: Vec<Edge>,
17
+ /// List of named constrained type definitions
18
+ pub constrained_types: IndexMap<String, ConstrainedTypeDef>,
19
+ }
20
+
21
+ /// Represent an element
22
+ #[derive(Debug)]
23
+ pub struct PropertiesDefinition
24
+ {
25
+ /// List of parament element
26
+ pub parents: Vec<String>,
27
+ /// Properties of the element
28
+ pub properties: IndexMap<String, Property>,
29
+ }
30
+
31
+ /// Represent a node
32
+ #[derive(Debug)]
33
+ pub struct Node
34
+ {
35
+ /// Main label of the node, used as identifer
36
+ pub label: String,
37
+ }
38
+
39
+ /// Represent an edge
40
+ #[derive(Debug)]
41
+ pub struct Edge
42
+ {
43
+ /// Identifier of the source
44
+ pub source: String,
45
+ /// Main label of the edge, used as identifer
46
+ pub label: String,
47
+ /// Identifier of the destination
48
+ pub destination: String,
49
+ }
50
+
51
+ /// Either a base scalar type, or a reference to another named constrained type.
52
+ #[derive(Debug, Clone, PartialEq)]
53
+ pub enum TypeRef
54
+ {
55
+ /// A base scalar type
56
+ Base(LiteralBaseType),
57
+ /// A reference to another named constrained type
58
+ Named(String),
59
+ }
60
+
61
+ /// A `type Name = TypeRef: Constraint?;` declaration, pre-resolution.
62
+ #[derive(Debug, Clone)]
63
+ pub struct ConstrainedTypeDef
64
+ {
65
+ /// Base type or reference to another named type
66
+ pub base: TypeRef,
67
+ /// Optional constraint on this type
68
+ pub constraint: Option<Constraint>,
69
+ }
@@ -0,0 +1,79 @@
1
+ //! Constraint data model: literal values and the constraints that can be
2
+ //! placed on them.
3
+ use graphcore::Value;
4
+
5
+ use super::{Error, Result, properties::LiteralBaseType};
6
+
7
+ /// The base type this value belongs to.
8
+ pub fn base_type(value: &Value) -> Result<LiteralBaseType>
9
+ {
10
+ match value
11
+ {
12
+ Value::Boolean(_) => Ok(LiteralBaseType::Boolean),
13
+ Value::Integer(_) => Ok(LiteralBaseType::Integer),
14
+ Value::Float(_) => Ok(LiteralBaseType::Float),
15
+ Value::String(_) => Ok(LiteralBaseType::String),
16
+ Value::TimeStamp(_) => Ok(LiteralBaseType::TimeStamp),
17
+ _ => Err(Error::InvalidValue),
18
+ }
19
+ }
20
+
21
+ /// A constraint tree, restricting the set of values a property may take.
22
+ #[derive(Debug, Clone, PartialEq)]
23
+ pub enum Constraint
24
+ {
25
+ In(Vec<Value>),
26
+ Eq(Value),
27
+ Ne(Value),
28
+ LessThan(Value),
29
+ LessOrEqual(Value),
30
+ GreaterThan(Value),
31
+ GreaterOrEqual(Value),
32
+ And(Vec<Constraint>),
33
+ Or(Vec<Constraint>),
34
+ Not(Box<Constraint>),
35
+ }
36
+
37
+ #[cfg(test)]
38
+ mod tests
39
+ {
40
+ use super::*;
41
+
42
+ #[test]
43
+ fn value_base_type_matches_variant()
44
+ {
45
+ assert_eq!(
46
+ base_type(&Value::Boolean(true)).unwrap(),
47
+ LiteralBaseType::Boolean
48
+ );
49
+ assert_eq!(
50
+ base_type(&Value::Integer(1)).unwrap(),
51
+ LiteralBaseType::Integer
52
+ );
53
+ assert_eq!(
54
+ base_type(&Value::Float(1.0)).unwrap(),
55
+ LiteralBaseType::Float
56
+ );
57
+ assert_eq!(
58
+ base_type(&Value::String("x".into())).unwrap(),
59
+ LiteralBaseType::String
60
+ );
61
+ assert_eq!(
62
+ base_type(&Value::TimeStamp(graphcore::TimeStamp::now())).unwrap(),
63
+ LiteralBaseType::TimeStamp
64
+ );
65
+ }
66
+
67
+ #[test]
68
+ fn nested_constraint_tree_clones_and_compares_equal()
69
+ {
70
+ let c = Constraint::And(vec![
71
+ Constraint::Or(vec![
72
+ Constraint::GreaterThan(Value::Integer(10)),
73
+ Constraint::LessThan(Value::Integer(0)),
74
+ ]),
75
+ Constraint::Not(Box::new(Constraint::Eq(Value::Integer(5)))),
76
+ ]);
77
+ assert_eq!(c, c.clone());
78
+ }
79
+ }
@@ -0,0 +1,22 @@
1
+ #[derive(thiserror::Error, Debug, PartialEq, Clone)]
2
+ #[allow(missing_docs)]
3
+ #[non_exhaustive]
4
+ pub enum Error
5
+ {
6
+ #[error("ParseError: {0}.")]
7
+ Parse(String),
8
+ #[error("IncompleteParsingError: '{0}' was not parsed.")]
9
+ IncompleteParsing(String),
10
+ #[error("MultiTypeCannotBeConvertedToSingle")]
11
+ MultiTypeCannotBeConvertedToSingle,
12
+ #[error("InvalidType")]
13
+ InvalidType,
14
+ #[error("InvalidValue")]
15
+ InvalidValue,
16
+ /// A type reference points to an undefined type.
17
+ #[error("undefined type '{0}'")]
18
+ UndefinedType(String),
19
+ /// A cycle was detected in the type hierarchy.
20
+ #[error("cycle detected: {}", .0.join(" -> "))]
21
+ Cycle(Vec<String>),
22
+ }