gqlite 1.5.1 → 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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Cargo.toml +10 -4
  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 -9
  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 +1 -1
  26. data/ext/gqlitedb/src/compiler/expression_analyser.rs +28 -20
  27. data/ext/gqlitedb/src/compiler/variables_manager.rs +97 -29
  28. data/ext/gqlitedb/src/compiler.rs +505 -225
  29. data/ext/gqlitedb/src/connection.rs +149 -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 +39 -2
  33. data/ext/gqlitedb/src/functions/math.rs +8 -3
  34. data/ext/gqlitedb/src/functions/path.rs +48 -11
  35. data/ext/gqlitedb/src/functions/value.rs +1 -1
  36. data/ext/gqlitedb/src/functions.rs +23 -7
  37. data/ext/gqlitedb/src/graph.rs +1 -9
  38. data/ext/gqlitedb/src/interpreter/evaluators.rs +968 -130
  39. data/ext/gqlitedb/src/interpreter/instructions.rs +39 -2
  40. data/ext/gqlitedb/src/lib.rs +5 -4
  41. data/ext/gqlitedb/src/planner.rs +329 -0
  42. data/ext/gqlitedb/src/prelude.rs +3 -3
  43. data/ext/gqlitedb/src/store/pgrx.rs +1 -1
  44. data/ext/gqlitedb/src/store/postgres.rs +735 -7
  45. data/ext/gqlitedb/src/store/redb/hnsw_store.rs +702 -0
  46. data/ext/gqlitedb/src/store/redb/index.rs +274 -0
  47. data/ext/gqlitedb/src/store/redb.rs +1268 -113
  48. data/ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs +28 -0
  49. data/ext/gqlitedb/src/store/sqlbase/sqlstore.rs +103 -0
  50. data/ext/gqlitedb/src/store/sqlbase.rs +146 -16
  51. data/ext/gqlitedb/src/store/sqlite.rs +569 -5
  52. data/ext/gqlitedb/src/store/vector_extract.rs +56 -0
  53. data/ext/gqlitedb/src/store.rs +123 -3
  54. data/ext/gqlitedb/src/tests/compiler.rs +207 -10
  55. data/ext/gqlitedb/src/tests/connection/postgres.rs +38 -3
  56. data/ext/gqlitedb/src/tests/connection/redb.rs +23 -0
  57. data/ext/gqlitedb/src/tests/connection/sqlite.rs +31 -0
  58. data/ext/gqlitedb/src/tests/connection.rs +61 -1
  59. data/ext/gqlitedb/src/tests/evaluators.rs +162 -7
  60. data/ext/gqlitedb/src/tests/parser.rs +54 -23
  61. data/ext/gqlitedb/src/tests/planner.rs +511 -0
  62. data/ext/gqlitedb/src/tests/store/postgres.rs +7 -0
  63. data/ext/gqlitedb/src/tests/store/redb.rs +8 -0
  64. data/ext/gqlitedb/src/tests/store/sqlite.rs +8 -0
  65. data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +182 -0
  66. data/ext/gqlitedb/src/tests/store/vector_index/redb.rs +386 -0
  67. data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +166 -0
  68. data/ext/gqlitedb/src/tests/store/vector_index.rs +2313 -0
  69. data/ext/gqlitedb/src/tests/store.rs +78 -14
  70. data/ext/gqlitedb/src/tests/templates/ast.rs +92 -16
  71. data/ext/gqlitedb/src/tests/templates/programs.rs +14 -7
  72. data/ext/gqlitedb/src/tests.rs +15 -9
  73. data/ext/gqlitedb/src/utils.rs +6 -1
  74. data/ext/gqlitedb/src/value/compare.rs +61 -3
  75. data/ext/gqlitedb/src/value.rs +136 -7
  76. data/ext/gqlitedb/templates/sql/postgres/metadata_delete.sql +1 -0
  77. data/ext/gqlitedb/templates/sql/postgres/node_select.sql +1 -1
  78. data/ext/gqlitedb/templates/sql/sqlite/metadata_delete.sql +1 -0
  79. data/ext/gqliterb/src/lib.rs +53 -8
  80. data/ext/gqlparser/Cargo.toml +25 -0
  81. data/ext/gqlparser/README.MD +9 -0
  82. data/ext/gqlparser/benches/pokec_divan.rs +34 -0
  83. data/ext/gqlparser/src/common.rs +69 -0
  84. data/ext/gqlparser/src/gqls/ast.rs +69 -0
  85. data/ext/gqlparser/src/gqls/constraint.rs +79 -0
  86. data/ext/gqlparser/src/gqls/error.rs +22 -0
  87. data/ext/gqlparser/src/gqls/parser.rs +813 -0
  88. data/ext/gqlparser/src/gqls/prelude.rs +8 -0
  89. data/ext/gqlparser/src/gqls/properties.rs +115 -0
  90. data/ext/gqlparser/src/gqls/resolve.rs +207 -0
  91. data/ext/gqlparser/src/gqls.rs +265 -0
  92. data/ext/gqlparser/src/lib.rs +7 -0
  93. data/ext/gqlparser/src/oc/ast.rs +680 -0
  94. data/ext/gqlparser/src/oc/error.rs +172 -0
  95. data/ext/gqlparser/src/oc/lexer.rs +429 -0
  96. data/ext/gqlparser/src/oc/parser/tests.rs +2284 -0
  97. data/ext/gqlparser/src/oc/parser.rs +2005 -0
  98. data/ext/gqlparser/src/oc.rs +33 -0
  99. data/ext/gqlparser/src/prelude.rs +3 -0
  100. data/ext/graphcore/Cargo.toml +1 -0
  101. data/ext/graphcore/src/error.rs +26 -0
  102. data/ext/graphcore/src/graph.rs +177 -51
  103. data/ext/graphcore/src/lib.rs +4 -2
  104. data/ext/graphcore/src/open_cypher.rs +12 -0
  105. data/ext/graphcore/src/table.rs +50 -2
  106. data/ext/graphcore/src/timestamp.rs +127 -104
  107. data/ext/graphcore/src/value/tensor.rs +739 -0
  108. data/ext/graphcore/src/value/value_map.rs +1 -1
  109. data/ext/graphcore/src/value.rs +343 -19
  110. metadata +90 -22
  111. data/ext/gqlitedb/src/parser/ast.rs +0 -604
  112. data/ext/gqlitedb/src/parser/parser_impl.rs +0 -1213
  113. data/ext/gqlitedb/src/parser.rs +0 -4
@@ -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
  // _____ _ ____ _ _
@@ -335,6 +449,12 @@ impl SelectEdgeQuery
335
449
  && self.destination.select_all
336
450
  }
337
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
+
338
458
  pub(crate) fn select_all() -> Self
339
459
  {
340
460
  Self {
@@ -405,7 +525,7 @@ impl SelectEdgeQuery
405
525
  destination: destination_query,
406
526
  }
407
527
  }
408
- pub(crate) fn is_match(&self, edge: &graph::Path) -> bool
528
+ pub(crate) fn is_match(&self, edge: &graph::SinglePath) -> bool
409
529
  {
410
530
  if let Some(keys) = &self.keys
411
531
  && !keys.iter().any(|x| edge.key() == *x)
@@ -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
+ }
@@ -1,7 +1,11 @@
1
1
  #[cfg(all(feature = "postgres", not(windows)))]
2
2
  pub(crate) mod postgres;
3
+ #[cfg(feature = "redb")]
4
+ mod redb;
5
+ #[cfg(feature = "sqlite")]
6
+ pub(crate) mod sqlite;
3
7
 
4
- fn test_connection(connection: crate::Connection)
8
+ fn test_create_match_return(connection: crate::Connection)
5
9
  {
6
10
  connection
7
11
  .execute_oc_query("CREATE (n:a)", Default::default())
@@ -14,6 +18,62 @@ fn test_connection(connection: crate::Connection)
14
18
  assert_eq!(table.rows(), 1);
15
19
  assert_eq!(*table.headers(), vec!["n".to_string()]);
16
20
  let node: graphcore::Node = table.value(0, 0).unwrap().try_into().unwrap();
21
+ println!("{node:?}");
22
+ assert_eq!(node.graph_name().as_ref().unwrap(), "default");
17
23
  assert_eq!(*node.labels(), vec!["a".to_string()]);
18
24
  assert_eq!(node.properties().len(), 0);
19
25
  }
26
+
27
+ fn test_create_return(connection: crate::Connection)
28
+ {
29
+ let r = connection
30
+ .execute_oc_query("CREATE (n:a) RETURN n", Default::default())
31
+ .unwrap();
32
+ let table = r.try_into_table().unwrap();
33
+ assert_eq!(table.columns(), 1);
34
+ assert_eq!(table.rows(), 1);
35
+ assert_eq!(*table.headers(), vec!["n".to_string()]);
36
+ let node: graphcore::Node = table.value(0, 0).unwrap().try_into().unwrap();
37
+ println!("{node:?}");
38
+ assert_eq!(node.graph_name().as_ref().unwrap(), "default");
39
+ assert_eq!(*node.labels(), vec!["a".to_string()]);
40
+ assert_eq!(node.properties().len(), 0);
41
+ }
42
+
43
+ fn test_create_edge_return(connection: crate::Connection)
44
+ {
45
+ let r = connection
46
+ .execute_oc_query("CREATE ()-[e:a]->() RETURN e", Default::default())
47
+ .unwrap();
48
+ let table = r.try_into_table().unwrap();
49
+ assert_eq!(table.columns(), 1);
50
+ assert_eq!(table.rows(), 1);
51
+ assert_eq!(*table.headers(), vec!["e".to_string()]);
52
+ let edge: graphcore::Edge = table.value(0, 0).unwrap().try_into().unwrap();
53
+ println!("{edge:?}");
54
+ assert_eq!(edge.graph_name().as_ref().unwrap(), "default");
55
+ assert_eq!(*edge.labels(), vec!["a".to_string()]);
56
+ assert_eq!(edge.properties().len(), 0);
57
+ }
58
+
59
+ fn test_vector_search_dimension_mismatch(connection: crate::Connection)
60
+ {
61
+ connection
62
+ .execute_oc_query(
63
+ "CREATE (:Doc { embedding: [0.1, 0.2, 0.3], title: 'doc1' })",
64
+ Default::default(),
65
+ )
66
+ .unwrap();
67
+ connection
68
+ .execute_oc_query(
69
+ "CREATE VECTOR INDEX doc_idx ON :Doc (embedding) OPTIONS { dimension: 3, metric: \"cosine\" }",
70
+ Default::default(),
71
+ )
72
+ .unwrap();
73
+
74
+ let result = connection.execute_oc_query(
75
+ "MATCH (n:Doc) SEARCH n IN (VECTOR INDEX doc_idx FOR [0.5, 0.5] LIMIT 1) RETURN n.title",
76
+ Default::default(),
77
+ );
78
+ assert!(result.is_err(), "dimension mismatch should raise an error");
79
+ }