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
@@ -0,0 +1,56 @@
1
+ use crate::value::Value;
2
+
3
+ /// Extract a flat Vec<f32> from a Value.
4
+ /// Handles Value::Tensor (new) and Value::Array (legacy).
5
+ pub(crate) fn extract_f32_vec(value: &Value) -> Option<Vec<f32>>
6
+ {
7
+ match value
8
+ {
9
+ Value::Tensor(t) => Some(t.to_f32_vec()),
10
+ Value::Array(arr) => arr
11
+ .iter()
12
+ .map(|v| match v
13
+ {
14
+ Value::Float(f) => Some(*f as f32),
15
+ Value::Integer(i) => Some(*i as f32),
16
+ _ => None,
17
+ })
18
+ .collect(),
19
+ _ => None,
20
+ }
21
+ }
22
+
23
+ #[cfg(any(feature = "sqlite", feature = "postgres", feature = "_pgrx"))]
24
+ fn extract_f32_from_json_value(val: &serde_json::Value) -> Option<Vec<f32>>
25
+ {
26
+ match val
27
+ {
28
+ serde_json::Value::Array(arr) => arr.iter().map(|e| e.as_f64().map(|f| f as f32)).collect(),
29
+ serde_json::Value::Object(map)
30
+ if map.get("__gql_type").and_then(|v| v.as_str()) == Some("tensor") =>
31
+ {
32
+ let arr = map.get("data")?.as_array()?;
33
+ arr.iter().map(|v| v.as_f64().map(|f| f as f32)).collect()
34
+ }
35
+ _ => None,
36
+ }
37
+ }
38
+
39
+ /// Extract a flat Vec<f32> from a raw JSON string.
40
+ /// Handles:
41
+ /// - legacy form: plain JSON array [1.0, 0.0, ...]
42
+ /// - new form: {"__gql_type":"tensor","dtype":"F32","shape":[...],"data":[...]}
43
+ #[cfg(any(feature = "sqlite", feature = "postgres", feature = "_pgrx"))]
44
+ pub(crate) fn extract_f32_vec_from_json(json_str: &str) -> Option<Vec<f32>>
45
+ {
46
+ let val: serde_json::Value = serde_json::from_str(json_str).ok()?;
47
+ extract_f32_from_json_value(&val)
48
+ }
49
+
50
+ /// Same as extract_f32_vec_from_json but for callers that already hold a serde_json::Value
51
+ /// (PostgreSQL retroactive population, where properties is a JSONB column).
52
+ #[cfg(any(feature = "postgres", feature = "_pgrx"))]
53
+ pub(crate) fn extract_f32_vec_from_serde_value(val: &serde_json::Value) -> Option<Vec<f32>>
54
+ {
55
+ extract_f32_from_json_value(val)
56
+ }
@@ -10,6 +10,8 @@ pub(crate) mod redb;
10
10
  #[cfg(feature = "sqlite")]
11
11
  pub(crate) mod sqlite;
12
12
 
13
+ pub(crate) mod vector_extract;
14
+
13
15
  use crate::prelude::*;
14
16
 
15
17
  // ____ _ _ _ _ _
@@ -103,6 +105,64 @@ where
103
105
  // ___) | || (_) | | | __/
104
106
  // |____/ \__\___/|_| \___|
105
107
 
108
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
109
+ pub(crate) enum VectorMetric
110
+ {
111
+ #[default]
112
+ Cosine,
113
+ L2,
114
+ DotProduct,
115
+ }
116
+
117
+ impl VectorMetric
118
+ {
119
+ pub(crate) fn from_debug_str(s: &str) -> Self
120
+ {
121
+ match s
122
+ {
123
+ "Cosine" => Self::Cosine,
124
+ "L2" => Self::L2,
125
+ "DotProduct" => Self::DotProduct,
126
+ _ => Self::Cosine,
127
+ }
128
+ }
129
+ }
130
+
131
+ #[derive(Debug, Clone)]
132
+ pub(crate) struct VectorIndexSpec
133
+ {
134
+ pub name: String,
135
+ pub label: Option<String>,
136
+ pub property: Option<String>,
137
+ pub dimension: usize,
138
+ pub metric: VectorMetric,
139
+ pub hnsw_params: redb::HnswParams,
140
+ pub dtype: value::DType,
141
+ }
142
+
143
+ impl Default for VectorIndexSpec
144
+ {
145
+ fn default() -> Self
146
+ {
147
+ Self {
148
+ name: String::new(),
149
+ label: None,
150
+ property: None,
151
+ dimension: 0,
152
+ metric: VectorMetric::Cosine,
153
+ hnsw_params: Default::default(),
154
+ dtype: value::DType::F32,
155
+ }
156
+ }
157
+ }
158
+
159
+ #[derive(Debug, Clone)]
160
+ pub(crate) struct VectorSearchResult
161
+ {
162
+ pub node_id: graph::Key,
163
+ pub score: f32,
164
+ }
165
+
106
166
  pub(crate) trait Store
107
167
  {
108
168
  type TransactionBox: TransactionBoxable;
@@ -153,6 +213,7 @@ pub(crate) trait Store
153
213
  graph_name: impl AsRef<str>,
154
214
  query: SelectNodeQuery,
155
215
  ) -> Result<Vec<crate::graph::Node>>;
216
+
156
217
  /// Add edge
157
218
  fn create_edges<'a, T: IntoIterator<Item = &'a crate::graph::SinglePath>>(
158
219
  &self,
@@ -172,7 +233,7 @@ pub(crate) trait Store
172
233
  transaction: &mut Self::TransactionBox,
173
234
  graph_name: impl AsRef<str>,
174
235
  query: SelectEdgeQuery,
175
- directivity: graph::EdgeDirectivity,
236
+ directivity: graphcore::EdgeDirectivity,
176
237
  ) -> Result<()>;
177
238
  /// Select edges
178
239
  fn select_edges(
@@ -180,10 +241,63 @@ pub(crate) trait Store
180
241
  transaction: &mut Self::TransactionBox,
181
242
  graph_name: impl AsRef<str>,
182
243
  query: SelectEdgeQuery,
183
- directivity: graph::EdgeDirectivity,
244
+ directivity: graphcore::EdgeDirectivity,
184
245
  ) -> Result<Vec<EdgeResult>>;
185
246
  /// Compute store statistics
186
247
  fn compute_statistics(&self, transaction: &mut Self::TransactionBox) -> Result<Statistics>;
248
+
249
+ // Vector search API
250
+ /// Create a vector index for a graph
251
+ fn create_vector_index(
252
+ &self,
253
+ transaction: &mut Self::TransactionBox,
254
+ graph_name: impl AsRef<str>,
255
+ spec: VectorIndexSpec,
256
+ ) -> Result<()>;
257
+
258
+ /// List vector indices for a graph
259
+ #[allow(dead_code)]
260
+ fn list_vector_indexes(
261
+ &self,
262
+ transaction: &mut Self::TransactionBox,
263
+ graph_name: impl AsRef<str>,
264
+ ) -> Result<Vec<VectorIndexSpec>>;
265
+
266
+ /// Insert or update a node vector embedding
267
+ fn upsert_vector(
268
+ &self,
269
+ transaction: &mut Self::TransactionBox,
270
+ graph_name: impl AsRef<str>,
271
+ index_name: impl AsRef<str>,
272
+ node_id: graph::Key,
273
+ tensor: &value::Tensor,
274
+ ) -> Result<()>;
275
+
276
+ /// Perform top-K similarity search over a vector index
277
+ fn vector_search(
278
+ &self,
279
+ transaction: &mut Self::TransactionBox,
280
+ graph_name: impl AsRef<str>,
281
+ index_name: impl AsRef<str>,
282
+ query: &value::Tensor,
283
+ k: usize,
284
+ ) -> Result<Vec<VectorSearchResult>>;
285
+
286
+ /// Drop a vector index from a graph
287
+ fn drop_vector_index(
288
+ &self,
289
+ transaction: &mut Self::TransactionBox,
290
+ graph_name: impl AsRef<str>,
291
+ index_name: impl AsRef<str>,
292
+ ) -> Result<()>;
293
+
294
+ /// Apply vector indices to a node (upsert vectors into applicable indices)
295
+ fn apply_vector_indices_for_node(
296
+ &self,
297
+ transaction: &mut Self::TransactionBox,
298
+ graph_name: impl AsRef<str>,
299
+ node: &graph::Node,
300
+ ) -> Result<()>;
187
301
  }
188
302
 
189
303
  // _____ _ ____ _ _
@@ -286,27 +400,21 @@ impl SelectNodeQuery
286
400
  return true;
287
401
  }
288
402
  if let Some(keys) = &self.keys
403
+ && !keys.iter().any(|x| node.key() == *x)
289
404
  {
290
- if !keys.iter().any(|x| node.key() == *x)
291
- {
292
- return false;
293
- }
405
+ return false;
294
406
  }
295
407
  if let Some(labels) = &self.labels
408
+ && !labels.iter().all(|x| node.labels().contains(x))
296
409
  {
297
- if !labels.iter().all(|x| node.labels().contains(x))
298
- {
299
- return false;
300
- }
410
+ return false;
301
411
  }
302
412
  if let Some(properties) = &self.properties
303
- {
304
- if !properties
413
+ && !properties
305
414
  .iter()
306
415
  .all(|(k, v)| node.properties().get(k) == Some(v))
307
- {
308
- return false;
309
- }
416
+ {
417
+ return false;
310
418
  }
311
419
  true
312
420
  }
@@ -341,6 +449,12 @@ impl SelectEdgeQuery
341
449
  && self.destination.select_all
342
450
  }
343
451
 
452
+ // expose source selector for callers that need start nodes (e.g., recursive traversals)
453
+ pub(crate) fn source_query(&self) -> SelectNodeQuery
454
+ {
455
+ self.source.clone()
456
+ }
457
+
344
458
  pub(crate) fn select_all() -> Self
345
459
  {
346
460
  Self {
@@ -411,31 +525,28 @@ impl SelectEdgeQuery
411
525
  destination: destination_query,
412
526
  }
413
527
  }
414
- pub(crate) fn is_match(&self, edge: &graph::Path) -> bool
528
+ pub(crate) fn is_match(&self, edge: &graph::SinglePath) -> bool
415
529
  {
416
530
  if let Some(keys) = &self.keys
531
+ && !keys.iter().any(|x| edge.key() == *x)
417
532
  {
418
- if !keys.iter().any(|x| edge.key() == *x)
419
- {
420
- return false;
421
- }
533
+ return false;
422
534
  }
535
+
423
536
  if let Some(labels) = &self.labels
537
+ && !labels.iter().all(|x| edge.labels().contains(x))
424
538
  {
425
- if !labels.iter().all(|x| edge.labels().contains(x))
426
- {
427
- return false;
428
- }
539
+ return false;
429
540
  }
541
+
430
542
  if let Some(properties) = &self.properties
431
- {
432
- if !properties
543
+ && !properties
433
544
  .iter()
434
545
  .all(|(k, v)| edge.properties().get(k) == Some(v))
435
- {
436
- return false;
437
- }
546
+ {
547
+ return false;
438
548
  }
549
+
439
550
  self.source.is_match(edge.source()) && self.destination.is_match(edge.destination())
440
551
  }
441
552
  }
@@ -1,6 +1,12 @@
1
1
  use crate::{
2
2
  compiler::compile,
3
- interpreter::Program,
3
+ interpreter::{
4
+ Program,
5
+ instructions::{
6
+ Block, BlockMatch, Instruction, Modifiers, OrderBy, RWExpression, VariablesSizes,
7
+ },
8
+ },
9
+ planner::{self, LogicalPlan, ProjectionKind},
4
10
  prelude::*,
5
11
  tests::templates::{ast, programs},
6
12
  };
@@ -10,12 +16,28 @@ fn compare_program(actual: Program, expected: Program)
10
16
  assert_eq!(format!("{:?}", actual), format!("{:?}", expected));
11
17
  }
12
18
 
19
+ fn compile_statements(
20
+ function_manager: &functions::Manager,
21
+ statements: gqlparser::oc::ast::Statements,
22
+ ) -> Program
23
+ {
24
+ compile(
25
+ function_manager,
26
+ statements
27
+ .into_iter()
28
+ .map(planner::plan)
29
+ .collect::<Result<Vec<_>, _>>()
30
+ .unwrap(),
31
+ )
32
+ .unwrap()
33
+ }
34
+
13
35
  #[test]
14
36
  fn test_compile_simple_create_node()
15
37
  {
16
38
  let function_manager = functions::Manager::new();
17
39
 
18
- let program = compile(&function_manager, ast::simple_create_node()).unwrap();
40
+ let program = compile_statements(&function_manager, ast::simple_create_node());
19
41
  compare_program(program, programs::simple_create())
20
42
  }
21
43
 
@@ -24,7 +46,7 @@ fn test_compile_create_named_node()
24
46
  {
25
47
  let function_manager = functions::Manager::new();
26
48
 
27
- let program = compile(&function_manager, ast::create_named_node()).unwrap();
49
+ let program = compile_statements(&function_manager, ast::create_named_node());
28
50
  compare_program(program, programs::create_named_node())
29
51
  }
30
52
 
@@ -33,7 +55,7 @@ fn test_compile_create_named_node_double_return()
33
55
  {
34
56
  let function_manager = functions::Manager::new();
35
57
 
36
- let program = compile(&function_manager, ast::create_named_node_double_return()).unwrap();
58
+ let program = compile_statements(&function_manager, ast::create_named_node_double_return());
37
59
  compare_program(program, programs::create_named_node_double_return())
38
60
  }
39
61
 
@@ -42,7 +64,7 @@ fn test_compile_double_with_return()
42
64
  {
43
65
  let function_manager = functions::Manager::new();
44
66
 
45
- let program = compile(&function_manager, ast::double_with_return()).unwrap();
67
+ let program = compile_statements(&function_manager, ast::double_with_return());
46
68
  compare_program(program, programs::double_with_return())
47
69
  }
48
70
 
@@ -51,7 +73,7 @@ fn test_compile_unwind()
51
73
  {
52
74
  let function_manager = functions::Manager::new();
53
75
 
54
- let program = compile(&function_manager, ast::unwind()).unwrap();
76
+ let program = compile_statements(&function_manager, ast::unwind());
55
77
  compare_program(program, programs::unwind())
56
78
  }
57
79
 
@@ -60,7 +82,7 @@ fn test_compile_match_loop()
60
82
  {
61
83
  let function_manager = functions::Manager::new();
62
84
 
63
- let program = compile(&function_manager, ast::match_loop()).unwrap();
85
+ let program = compile_statements(&function_manager, ast::match_loop());
64
86
  compare_program(program, programs::match_loop())
65
87
  }
66
88
 
@@ -69,7 +91,7 @@ fn test_compile_optional_match()
69
91
  {
70
92
  let function_manager = functions::Manager::new();
71
93
 
72
- let program = compile(&function_manager, ast::optional_match()).unwrap();
94
+ let program = compile_statements(&function_manager, ast::optional_match());
73
95
  compare_program(program, programs::optional_match())
74
96
  }
75
97
 
@@ -78,7 +100,7 @@ fn test_compile_match_count()
78
100
  {
79
101
  let function_manager = functions::Manager::new();
80
102
 
81
- let program = compile(&function_manager, ast::match_count()).unwrap();
103
+ let program = compile_statements(&function_manager, ast::match_count());
82
104
  compare_program(program, programs::match_count(&function_manager))
83
105
  }
84
106
 
@@ -87,6 +109,181 @@ fn test_compile_aggregation()
87
109
  {
88
110
  let function_manager = functions::Manager::new();
89
111
 
90
- let program = compile(&function_manager, ast::aggregation()).unwrap();
112
+ let program = compile_statements(&function_manager, ast::aggregation());
91
113
  compare_program(program, programs::aggregation(&function_manager))
92
114
  }
115
+
116
+ #[test]
117
+ fn test_compile_logicalplan_nodescan_filter()
118
+ {
119
+ let function_manager = functions::Manager::new();
120
+ let identifiers = gqlparser::oc::ast::VariableIdentifiers::default();
121
+ let node_identifier = identifiers.create_variable_from_name("n");
122
+
123
+ let program = compile(
124
+ &function_manager,
125
+ vec![LogicalPlan::Filter {
126
+ source: Box::new(LogicalPlan::NodeScan {
127
+ patterns: vec![gqlparser::oc::ast::Pattern::Node(
128
+ gqlparser::oc::ast::NodePattern {
129
+ variable: Some(node_identifier.clone()),
130
+ labels: gqlparser::oc::ast::LabelExpression::None,
131
+ properties: None,
132
+ },
133
+ )],
134
+ optional: false,
135
+ }),
136
+ expression: gqlparser::oc::ast::Variable {
137
+ identifier: node_identifier,
138
+ }
139
+ .into(),
140
+ }],
141
+ )
142
+ .unwrap();
143
+
144
+ compare_program(
145
+ program,
146
+ vec![Block::Match {
147
+ blocks: vec![BlockMatch::MatchNode {
148
+ instructions: vec![
149
+ Instruction::Push {
150
+ value: crate::ValueMap::default().into(),
151
+ },
152
+ Instruction::CreateNodeQuery { labels: vec![] },
153
+ ],
154
+ variable: Some(0),
155
+ filter: vec![],
156
+ }],
157
+ filter: vec![Instruction::GetVariable { col_id: 0 }],
158
+ optional: false,
159
+ variables_size: VariablesSizes {
160
+ persistent_variables: 1,
161
+ temporary_variables: 0,
162
+ },
163
+ }],
164
+ );
165
+ }
166
+
167
+ #[test]
168
+ fn test_compile_logicalplan_projection_modifiers()
169
+ {
170
+ let function_manager = functions::Manager::new();
171
+ let identifiers = gqlparser::oc::ast::VariableIdentifiers::default();
172
+ let item_identifier = identifiers.create_variable_from_name("i");
173
+
174
+ let program = compile(
175
+ &function_manager,
176
+ vec![
177
+ LogicalPlan::Unwind {
178
+ identifier: item_identifier.clone(),
179
+ expression: gqlparser::oc::ast::Array {
180
+ array: vec![gqlparser::oc::ast::Value { value: 0.into() }.into()],
181
+ }
182
+ .into(),
183
+ },
184
+ LogicalPlan::Limit {
185
+ source: Box::new(LogicalPlan::Skip {
186
+ source: Box::new(LogicalPlan::Sort {
187
+ source: Box::new(LogicalPlan::Filter {
188
+ source: Box::new(LogicalPlan::Projection {
189
+ kind: ProjectionKind::Return,
190
+ all: false,
191
+ expressions: vec![gqlparser::oc::ast::NamedExpression {
192
+ identifier: item_identifier.clone(),
193
+ expression: gqlparser::oc::ast::Variable {
194
+ identifier: item_identifier.clone(),
195
+ }
196
+ .into(),
197
+ }],
198
+ }),
199
+ expression: gqlparser::oc::ast::Variable {
200
+ identifier: item_identifier.clone(),
201
+ }
202
+ .into(),
203
+ }),
204
+ expressions: vec![gqlparser::oc::ast::OrderByExpression {
205
+ asc: false,
206
+ expression: gqlparser::oc::ast::Variable {
207
+ identifier: item_identifier.clone(),
208
+ }
209
+ .into(),
210
+ }],
211
+ }),
212
+ expression: gqlparser::oc::ast::Value { value: 0.into() }.into(),
213
+ }),
214
+ expression: gqlparser::oc::ast::Value { value: 1.into() }.into(),
215
+ },
216
+ ],
217
+ )
218
+ .unwrap();
219
+
220
+ compare_program(
221
+ program,
222
+ vec![
223
+ Block::Unwind {
224
+ col_id: 0,
225
+ instructions: vec![
226
+ Instruction::Push { value: 0.into() },
227
+ Instruction::CreateArray { length: 1 },
228
+ ],
229
+ variables_size: VariablesSizes {
230
+ persistent_variables: 1,
231
+ temporary_variables: 0,
232
+ },
233
+ },
234
+ Block::Return {
235
+ variables: vec![(
236
+ "i".into(),
237
+ RWExpression {
238
+ col_id: 0,
239
+ instructions: vec![Instruction::GetVariable { col_id: 0 }],
240
+ aggregations: vec![],
241
+ },
242
+ )],
243
+ filter: vec![Instruction::GetVariable { col_id: 0 }],
244
+ modifiers: Modifiers {
245
+ limit: Some(vec![Instruction::Push { value: 1.into() }]),
246
+ skip: Some(vec![Instruction::Push { value: 0.into() }]),
247
+ order_by: vec![OrderBy {
248
+ asc: false,
249
+ instructions: vec![Instruction::GetVariable { col_id: 0 }],
250
+ }],
251
+ },
252
+ variables_sizes: VariablesSizes {
253
+ persistent_variables: 1,
254
+ temporary_variables: 0,
255
+ },
256
+ },
257
+ ],
258
+ );
259
+ }
260
+
261
+ #[test]
262
+ fn test_compile_match_with_vector_search()
263
+ {
264
+ let function_manager = functions::Manager::new();
265
+ let program = compile_statements(&function_manager, ast::match_with_vector_search("idx"));
266
+ // At this stage (pre-WI-3), Block::VectorSearch still carries `graph`.
267
+ // This test only asserts compilation succeeds without error and produces
268
+ // a VectorSearch block referencing the correct index name -- it does not
269
+ // assert on the (currently wrong, always "default") graph value, since
270
+ // that assertion belongs to the evaluator-level regression test below,
271
+ // where the wrongness is actually observable as a behavioral failure.
272
+ let has_vector_search_block = format!("{:?}", program).contains("VectorSearch");
273
+ assert!(
274
+ has_vector_search_block,
275
+ "expected a VectorSearch block in the compiled program"
276
+ );
277
+ }
278
+
279
+ #[test]
280
+ fn test_compile_multiple_vector_indexes_produces_distinct_instructions()
281
+ {
282
+ let function_manager = functions::Manager::new();
283
+ let mut statements = ast::create_vector_index("idx1");
284
+ statements.extend(ast::create_vector_index("idx2"));
285
+ let program = compile_statements(&function_manager, statements);
286
+ let debug = format!("{:?}", program);
287
+ assert!(debug.contains("\"idx1\""));
288
+ assert!(debug.contains("\"idx2\""));
289
+ }
@@ -1,8 +1,43 @@
1
1
  #[test]
2
- fn testconnection()
2
+ fn test_create_match_return()
3
3
  {
4
4
  let db = crate::tests::postgres::create_tmp_db(10);
5
- let builder = crate::Connection::builder().set_option("uri", db.connection_uri());
5
+ let builder = crate::Connection::builder()
6
+ .set_option("url", db.connection_uri())
7
+ .backend(crate::Backend::Postgres);
6
8
  let connection = builder.create().unwrap();
7
- super::test_connection(connection);
9
+ super::test_create_match_return(connection);
10
+ }
11
+
12
+ #[test]
13
+ fn test_create_return()
14
+ {
15
+ let db = crate::tests::postgres::create_tmp_db(11);
16
+ let builder = crate::Connection::builder()
17
+ .set_option("url", db.connection_uri())
18
+ .backend(crate::Backend::Postgres);
19
+ let connection = builder.create().unwrap();
20
+ super::test_create_return(connection);
21
+ }
22
+
23
+ #[test]
24
+ fn test_create_edge_return()
25
+ {
26
+ let db = crate::tests::postgres::create_tmp_db(12);
27
+ let builder = crate::Connection::builder()
28
+ .set_option("url", db.connection_uri())
29
+ .backend(crate::Backend::Postgres);
30
+ let connection = builder.create().unwrap();
31
+ super::test_create_edge_return(connection);
32
+ }
33
+
34
+ #[test]
35
+ fn test_vector_search_dimension_mismatch()
36
+ {
37
+ let db = crate::tests::postgres::create_tmp_db(13);
38
+ let builder = crate::Connection::builder()
39
+ .set_option("url", db.connection_uri())
40
+ .backend(crate::Backend::Postgres);
41
+ let connection = builder.create().unwrap();
42
+ super::test_vector_search_dimension_mismatch(connection);
8
43
  }
@@ -0,0 +1,23 @@
1
+ #[test]
2
+ fn test_create_match_return()
3
+ {
4
+ let builder = crate::Connection::builder().backend(crate::Backend::Redb);
5
+ let connection = builder.create().unwrap();
6
+ super::test_create_match_return(connection);
7
+ }
8
+
9
+ #[test]
10
+ fn test_create_return()
11
+ {
12
+ let builder = crate::Connection::builder().backend(crate::Backend::Redb);
13
+ let connection = builder.create().unwrap();
14
+ super::test_create_return(connection);
15
+ }
16
+
17
+ #[test]
18
+ fn test_create_edge_return()
19
+ {
20
+ let builder = crate::Connection::builder().backend(crate::Backend::Redb);
21
+ let connection = builder.create().unwrap();
22
+ super::test_create_edge_return(connection);
23
+ }
@@ -0,0 +1,31 @@
1
+ #[test]
2
+ fn test_create_match_return()
3
+ {
4
+ let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
5
+ let connection = builder.create().unwrap();
6
+ super::test_create_match_return(connection);
7
+ }
8
+
9
+ #[test]
10
+ fn test_create_return()
11
+ {
12
+ let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
13
+ let connection = builder.create().unwrap();
14
+ super::test_create_return(connection);
15
+ }
16
+
17
+ #[test]
18
+ fn test_create_edge_return()
19
+ {
20
+ let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
21
+ let connection = builder.create().unwrap();
22
+ super::test_create_edge_return(connection);
23
+ }
24
+
25
+ #[test]
26
+ fn test_vector_search_dimension_mismatch()
27
+ {
28
+ let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
29
+ let connection = builder.create().unwrap();
30
+ super::test_vector_search_dimension_mismatch(connection);
31
+ }