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.
- checksums.yaml +4 -4
- data/ext/Cargo.toml +12 -6
- data/ext/db-index/Cargo.toml +29 -0
- data/ext/db-index/src/hnsw/error.rs +66 -0
- data/ext/db-index/src/hnsw/graph_store.rs +272 -0
- data/ext/db-index/src/hnsw/id.rs +36 -0
- data/ext/db-index/src/hnsw/implementation.rs +1517 -0
- data/ext/db-index/src/hnsw/kernels.rs +1228 -0
- data/ext/db-index/src/hnsw/metric.rs +244 -0
- data/ext/db-index/src/hnsw/scalar.rs +72 -0
- data/ext/db-index/src/hnsw/simple_store.rs +140 -0
- data/ext/db-index/src/hnsw/store.rs +472 -0
- data/ext/db-index/src/hnsw/vector.rs +105 -0
- data/ext/db-index/src/hnsw/vectors.rs +568 -0
- data/ext/db-index/src/hnsw.rs +42 -0
- data/ext/db-index/src/lib.rs +3 -0
- data/ext/gqlitedb/Cargo.toml +19 -8
- data/ext/gqlitedb/benches/common/pokec.rs +62 -2
- data/ext/gqlitedb/benches/pokec_divan.rs +66 -2
- data/ext/gqlitedb/benches/pokec_iai.rs +60 -3
- data/ext/gqlitedb/release.toml +2 -2
- data/ext/gqlitedb/src/aggregators/arithmetic.rs +3 -1
- data/ext/gqlitedb/src/aggregators/containers.rs +1 -1
- data/ext/gqlitedb/src/aggregators/stats.rs +21 -12
- data/ext/gqlitedb/src/capi.rs +20 -24
- data/ext/gqlitedb/src/compiler/expression_analyser.rs +28 -20
- data/ext/gqlitedb/src/compiler/variables_manager.rs +104 -38
- data/ext/gqlitedb/src/compiler.rs +506 -227
- data/ext/gqlitedb/src/connection.rs +151 -11
- data/ext/gqlitedb/src/consts.rs +1 -2
- data/ext/gqlitedb/src/error.rs +123 -61
- data/ext/gqlitedb/src/functions/common.rs +64 -0
- data/ext/gqlitedb/src/functions/containers.rs +2 -46
- data/ext/gqlitedb/src/functions/edge.rs +1 -1
- data/ext/gqlitedb/src/functions/math.rs +87 -15
- data/ext/gqlitedb/src/functions/node.rs +1 -1
- data/ext/gqlitedb/src/functions/path.rs +48 -11
- data/ext/gqlitedb/src/functions/scalar.rs +47 -4
- data/ext/gqlitedb/src/functions/string.rs +123 -1
- data/ext/gqlitedb/src/functions/value.rs +1 -1
- data/ext/gqlitedb/src/functions.rs +165 -28
- data/ext/gqlitedb/src/graph.rs +1 -9
- data/ext/gqlitedb/src/interpreter/evaluators.rs +968 -130
- data/ext/gqlitedb/src/interpreter/instructions.rs +39 -2
- data/ext/gqlitedb/src/lib.rs +5 -4
- data/ext/gqlitedb/src/parser/gql.pest +1 -1
- data/ext/gqlitedb/src/planner.rs +329 -0
- data/ext/gqlitedb/src/prelude.rs +3 -3
- data/ext/gqlitedb/src/store/pgrx.rs +1 -1
- data/ext/gqlitedb/src/store/postgres.rs +742 -16
- data/ext/gqlitedb/src/store/redb/hnsw_store.rs +702 -0
- data/ext/gqlitedb/src/store/redb/index.rs +274 -0
- data/ext/gqlitedb/src/store/redb.rs +1268 -113
- data/ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs +28 -0
- data/ext/gqlitedb/src/store/sqlbase/sqlstore.rs +103 -0
- data/ext/gqlitedb/src/store/sqlbase.rs +146 -16
- data/ext/gqlitedb/src/store/sqlite.rs +576 -14
- data/ext/gqlitedb/src/store/vector_extract.rs +56 -0
- data/ext/gqlitedb/src/store.rs +140 -29
- data/ext/gqlitedb/src/tests/compiler.rs +207 -10
- data/ext/gqlitedb/src/tests/connection/postgres.rs +38 -3
- data/ext/gqlitedb/src/tests/connection/redb.rs +23 -0
- data/ext/gqlitedb/src/tests/connection/sqlite.rs +31 -0
- data/ext/gqlitedb/src/tests/connection.rs +61 -1
- data/ext/gqlitedb/src/tests/evaluators.rs +162 -7
- data/ext/gqlitedb/src/tests/parser.rs +54 -23
- data/ext/gqlitedb/src/tests/planner.rs +511 -0
- data/ext/gqlitedb/src/tests/store/postgres.rs +7 -0
- data/ext/gqlitedb/src/tests/store/redb.rs +8 -0
- data/ext/gqlitedb/src/tests/store/sqlite.rs +8 -0
- data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +182 -0
- data/ext/gqlitedb/src/tests/store/vector_index/redb.rs +386 -0
- data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +166 -0
- data/ext/gqlitedb/src/tests/store/vector_index.rs +2313 -0
- data/ext/gqlitedb/src/tests/store.rs +78 -14
- data/ext/gqlitedb/src/tests/templates/ast.rs +92 -16
- data/ext/gqlitedb/src/tests/templates/programs.rs +14 -7
- data/ext/gqlitedb/src/tests.rs +15 -9
- data/ext/gqlitedb/src/utils.rs +6 -1
- data/ext/gqlitedb/src/value/compare.rs +61 -3
- data/ext/gqlitedb/src/value.rs +136 -7
- data/ext/gqlitedb/templates/sql/postgres/metadata_delete.sql +1 -0
- data/ext/gqlitedb/templates/sql/postgres/node_select.sql +1 -1
- data/ext/gqlitedb/templates/sql/sqlite/metadata_delete.sql +1 -0
- data/ext/gqliterb/src/lib.rs +53 -8
- data/ext/gqlparser/Cargo.toml +25 -0
- data/ext/gqlparser/README.MD +9 -0
- data/ext/gqlparser/benches/pokec_divan.rs +34 -0
- data/ext/gqlparser/src/common.rs +69 -0
- data/ext/gqlparser/src/gqls/ast.rs +69 -0
- data/ext/gqlparser/src/gqls/constraint.rs +79 -0
- data/ext/gqlparser/src/gqls/error.rs +22 -0
- data/ext/gqlparser/src/gqls/parser.rs +813 -0
- data/ext/gqlparser/src/gqls/prelude.rs +8 -0
- data/ext/gqlparser/src/gqls/properties.rs +115 -0
- data/ext/gqlparser/src/gqls/resolve.rs +207 -0
- data/ext/gqlparser/src/gqls.rs +265 -0
- data/ext/gqlparser/src/lib.rs +7 -0
- data/ext/gqlparser/src/oc/ast.rs +680 -0
- data/ext/gqlparser/src/oc/error.rs +172 -0
- data/ext/gqlparser/src/oc/lexer.rs +429 -0
- data/ext/gqlparser/src/oc/parser/tests.rs +2284 -0
- data/ext/gqlparser/src/oc/parser.rs +2005 -0
- data/ext/gqlparser/src/oc.rs +33 -0
- data/ext/gqlparser/src/prelude.rs +3 -0
- data/ext/graphcore/Cargo.toml +1 -0
- data/ext/graphcore/src/error.rs +26 -0
- data/ext/graphcore/src/graph.rs +177 -51
- data/ext/graphcore/src/lib.rs +4 -2
- data/ext/graphcore/src/open_cypher.rs +12 -0
- data/ext/graphcore/src/table.rs +50 -2
- data/ext/graphcore/src/timestamp.rs +127 -104
- data/ext/graphcore/src/value/tensor.rs +739 -0
- data/ext/graphcore/src/value/value_map.rs +1 -1
- data/ext/graphcore/src/value.rs +343 -19
- metadata +93 -28
- data/ext/gqlitedb/src/parser/ast.rs +0 -604
- data/ext/gqlitedb/src/parser/parser_impl.rs +0 -1213
- 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
|
+
}
|
data/ext/gqlitedb/src/store.rs
CHANGED
|
@@ -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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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::
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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::
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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()
|
|
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::
|
|
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
|
+
}
|