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,2284 @@
1
+ use std::{fmt::Debug, vec};
2
+
3
+ use crate::oc::{Error, ast, error::ErrorKind, lexer};
4
+
5
+ use super::*;
6
+ use nom::Finish;
7
+
8
+ fn compare_ast<T: Debug>(actual: T, expected: impl Into<T>)
9
+ {
10
+ assert_eq!(format!("{:?}", actual), format!("{:?}", expected.into()));
11
+ }
12
+
13
+ fn compare_parse_to_ast<T, P>(s: &str, parser: P, expected: impl Into<T>)
14
+ where
15
+ T: Debug,
16
+ for<'a> P: nom::Parser<Input<'a>, Output = T, Error = Error>,
17
+ {
18
+ let r: T = parse(s, parser);
19
+ compare_ast(r, expected);
20
+ }
21
+
22
+ /// Unwrap a nom result in tests with a readable error message
23
+ pub(crate) fn unwrap_nom<'a, T>(input: Input<'a>, result: OCPResult<Input<'a>, T>) -> T
24
+ {
25
+ match result.finish()
26
+ {
27
+ Ok((remaining, value)) =>
28
+ {
29
+ if !remaining.0.is_empty()
30
+ {
31
+ panic!(
32
+ "\nParse error: unexpected remaining input: {:?}",
33
+ remaining.0
34
+ );
35
+ }
36
+ value
37
+ }
38
+ Err(e) =>
39
+ {
40
+ panic!("\nParse error:\n{}", e.nice_error_string(input.1.source));
41
+ }
42
+ }
43
+ }
44
+
45
+ fn parse<T, P>(s: &str, mut parser: P) -> T
46
+ where
47
+ for<'a> P: nom::Parser<Input<'a>, Output = T, Error = Error>,
48
+ {
49
+ let ctx = Context::new(s);
50
+ let tokens = lexer::tokenize(s).expect("tokenization failed");
51
+ let input = Input(&tokens, &ctx);
52
+ unwrap_nom(input.clone(), parser.parse(input))
53
+ }
54
+
55
+ fn parse_fail<T, P>(s: &str, mut parser: P, f: impl Fn(&ErrorKind) -> bool)
56
+ where
57
+ T: std::fmt::Debug,
58
+ for<'a> P: nom::Parser<Input<'a>, Output = T, Error = Error>,
59
+ {
60
+ let ctx = Context::new(s);
61
+ let tokens = lexer::tokenize(s).expect("tokenization failed");
62
+ let input = Input(&tokens, &ctx);
63
+ let result = parser.parse(input);
64
+ match result
65
+ {
66
+ Ok(r) =>
67
+ {
68
+ if r.0.is_empty()
69
+ {
70
+ panic!(
71
+ "Expected parsing of '{s}' to fail, but it succeeded: {:#?}",
72
+ r.1
73
+ );
74
+ }
75
+ else
76
+ {
77
+ panic!(
78
+ "Unexpected error for '{s}': it failed with remaining input: {:?}",
79
+ r.0
80
+ );
81
+ }
82
+ }
83
+ Err(nom::Err::Failure(e)) | Err(nom::Err::Error(e)) =>
84
+ {
85
+ if !f(e.error_kind())
86
+ {
87
+ panic!("Unexpected error for '{s}': got {:?}", e);
88
+ }
89
+ }
90
+ Err(e) => panic!("Unexpected parsing error for '{s}': got {:?}", e),
91
+ }
92
+ }
93
+
94
+ fn parse_str<P>(s: &str, mut parser: P, exp: &str)
95
+ where
96
+ for<'a> P: nom::Parser<Input<'a>, Output = &'a str, Error = Error>,
97
+ {
98
+ let ctx = Context::new(s);
99
+ let tokens = lexer::tokenize(s).expect("tokenization failed");
100
+ let input = Input(&tokens, &ctx);
101
+ assert_eq!(unwrap_nom(input.clone(), parser.parse(input)), exp);
102
+ }
103
+
104
+ #[test]
105
+ fn test_oc_parse_context_variable_identity()
106
+ {
107
+ let ctx = Context::new("");
108
+
109
+ let a1 = ctx.identifiers.create_variable_from_name("a");
110
+ let a2 = ctx.identifiers.create_variable_from_name("a");
111
+ let b = ctx.identifiers.create_variable_from_name("b");
112
+
113
+ assert_eq!(a1.id, a2.id);
114
+ assert_eq!(a1.name, "a");
115
+ assert_ne!(a1.id, b.id);
116
+ }
117
+
118
+ #[test]
119
+ fn test_oc_parse_ident()
120
+ {
121
+ parse_str("abc123_", ident, "abc123_");
122
+ parse_str("null", ident, "null");
123
+ }
124
+
125
+ #[test]
126
+ fn test_oc_parse_parameter_name()
127
+ {
128
+ parse_str("$param_1", parameter_name, "$param_1");
129
+ }
130
+
131
+ #[test]
132
+ fn test_oc_parse_string_literal_double_quote()
133
+ {
134
+ parse_str(r#""hello world""#, string_literal, "hello world");
135
+ }
136
+
137
+ #[test]
138
+ fn test_oc_parse_string_literal_single_quote()
139
+ {
140
+ parse_str("'hello world'", string_literal, "hello world");
141
+ parse_str("', as#?lßdj '", string_literal, ", as#?lßdj ");
142
+ }
143
+ #[test]
144
+ fn test_oc_parse_string_literal_backtick()
145
+ {
146
+ parse_str("`hello`", string_literal, "hello");
147
+ }
148
+
149
+ #[test]
150
+ fn test_oc_parse_int_literal()
151
+ {
152
+ let value = parse("42", int);
153
+ assert_eq!(value, 42);
154
+
155
+ let value = parse("0x162CD4F6", int);
156
+ assert_eq!(value, 0x162CD4F6);
157
+
158
+ let value = parse("0o2613152366", int);
159
+ assert_eq!(value, 0o2613152366);
160
+
161
+ compare_parse_to_ast(
162
+ "-42",
163
+ expression,
164
+ ast::Value {
165
+ value: (-42).into(),
166
+ },
167
+ );
168
+
169
+ compare_parse_to_ast("+42", expression, ast::Value { value: 42.into() });
170
+ compare_parse_to_ast("123", expression, ast::Value { value: 123.into() });
171
+
172
+ compare_parse_to_ast(
173
+ "0x162CD4F6",
174
+ expression,
175
+ ast::Value {
176
+ value: 0x162CD4F6.into(),
177
+ },
178
+ );
179
+
180
+ let value = parse("0o2613152366", expression);
181
+ let expected = ast::Expression::Value(ast::Value {
182
+ value: 0o2613152366.into(),
183
+ });
184
+ compare_ast(value, expected);
185
+
186
+ parse_fail("0o1000000000000000000000", expression, |e| {
187
+ matches!(e, ErrorKind::IntegerOverflow)
188
+ });
189
+ }
190
+
191
+ #[test]
192
+ fn test_oc_parse_float_literal()
193
+ {
194
+ let value = parse("3.1315", num);
195
+ assert!((value - 3.1315).abs() < 1e-6);
196
+ let value = parse("2E-01", num);
197
+ assert!((value - 2E-01).abs() < 1e-6);
198
+
199
+ let value = parse(".1e-5", expression);
200
+
201
+ let expected = ast::Expression::Value(ast::Value {
202
+ value: 0.1e-5_f64.into(),
203
+ });
204
+
205
+ compare_ast(value, expected);
206
+
207
+ parse_fail("1.34E999", num, |e| {
208
+ matches!(e, ErrorKind::FloatingPointOverflow)
209
+ });
210
+ }
211
+
212
+ #[test]
213
+ fn test_oc_parse_labels()
214
+ {
215
+ compare_ast(
216
+ parse(":A|B|C", labels),
217
+ ast::LabelExpression::Or(vec![
218
+ ast::LabelExpression::String("A".to_string()),
219
+ ast::LabelExpression::String("B".to_string()),
220
+ ast::LabelExpression::String("C".to_string()),
221
+ ]),
222
+ );
223
+ compare_ast(
224
+ parse(":A|:B", labels),
225
+ ast::LabelExpression::Or(vec![
226
+ ast::LabelExpression::String("A".to_string()),
227
+ ast::LabelExpression::String("B".to_string()),
228
+ ]),
229
+ );
230
+ compare_ast(
231
+ parse(":A:B:C", labels),
232
+ ast::LabelExpression::And(vec![
233
+ ast::LabelExpression::String("A".to_string()),
234
+ ast::LabelExpression::String("B".to_string()),
235
+ ast::LabelExpression::String("C".to_string()),
236
+ ]),
237
+ );
238
+ }
239
+
240
+ #[test]
241
+ fn test_oc_parse_member_access()
242
+ {
243
+ compare_parse_to_ast(
244
+ "n.name.first",
245
+ member_access,
246
+ ast::MemberAccess {
247
+ left: ast::Variable {
248
+ identifier: ast::VariableIdentifier {
249
+ name: "n".to_string(),
250
+ id: 0,
251
+ },
252
+ }
253
+ .into(),
254
+ path: vec!["name".to_string(), "first".to_string()],
255
+ },
256
+ );
257
+ }
258
+
259
+ #[test]
260
+ fn test_oc_parse_index_access()
261
+ {
262
+ compare_parse_to_ast(
263
+ "arr[0]",
264
+ array_access,
265
+ ast::IndexAccess {
266
+ left: ast::Variable {
267
+ identifier: ast::VariableIdentifier {
268
+ name: "arr".to_string(),
269
+ id: 0,
270
+ },
271
+ }
272
+ .into(),
273
+ index: ast::Value { value: 0.into() }.into(),
274
+ },
275
+ );
276
+ }
277
+
278
+ #[test]
279
+ fn test_oc_parse_range_access()
280
+ {
281
+ compare_parse_to_ast(
282
+ "arr[1..2]",
283
+ array_access,
284
+ ast::RangeAccess {
285
+ left: ast::Variable {
286
+ identifier: ast::VariableIdentifier {
287
+ name: "arr".to_string(),
288
+ id: 0,
289
+ },
290
+ }
291
+ .into(),
292
+ start: Some(ast::Value { value: 1.into() }.into()),
293
+ end: Some(ast::Value { value: 2.into() }.into()),
294
+ },
295
+ );
296
+ }
297
+
298
+ #[test]
299
+ fn test_oc_parse_function_call()
300
+ {
301
+ compare_parse_to_ast(
302
+ "func(1, 2)",
303
+ primary,
304
+ ast::FunctionCall {
305
+ name: "func".to_string(),
306
+ arguments: vec![
307
+ ast::Expression::Value(ast::Value { value: 1.into() }),
308
+ ast::Expression::Value(ast::Value { value: 2.into() }),
309
+ ],
310
+ },
311
+ );
312
+ }
313
+
314
+ #[test]
315
+ fn test_oc_parse_function_call_all()
316
+ {
317
+ compare_parse_to_ast(
318
+ "func(*)",
319
+ primary,
320
+ ast::FunctionCall {
321
+ name: "func".to_string(),
322
+ arguments: vec![ast::Expression::Value(ast::Value { value: 0.into() })],
323
+ },
324
+ );
325
+ }
326
+
327
+ #[test]
328
+ fn test_oc_parse_relational_binary_operator()
329
+ {
330
+ compare_parse_to_ast(
331
+ "a = b",
332
+ expression,
333
+ ast::RelationalEqual {
334
+ left: ast::Variable {
335
+ identifier: ast::VariableIdentifier {
336
+ name: "a".to_string(),
337
+ id: 0,
338
+ },
339
+ }
340
+ .into(),
341
+ right: ast::Variable {
342
+ identifier: ast::VariableIdentifier {
343
+ name: "b".to_string(),
344
+ id: 1,
345
+ },
346
+ }
347
+ .into(),
348
+ },
349
+ );
350
+
351
+ compare_parse_to_ast(
352
+ "a < b",
353
+ relational,
354
+ ast::RelationalInferior {
355
+ left: ast::Variable {
356
+ identifier: ast::VariableIdentifier {
357
+ name: "a".to_string(),
358
+ id: 0,
359
+ },
360
+ }
361
+ .into(),
362
+ right: ast::Variable {
363
+ identifier: ast::VariableIdentifier {
364
+ name: "b".to_string(),
365
+ id: 1,
366
+ },
367
+ }
368
+ .into(),
369
+ },
370
+ );
371
+
372
+ compare_parse_to_ast(
373
+ "id(a) = id(b)",
374
+ relational,
375
+ ast::RelationalEqual {
376
+ left: ast::FunctionCall {
377
+ name: "id".to_string(),
378
+ arguments: vec![
379
+ ast::Variable {
380
+ identifier: ast::VariableIdentifier {
381
+ name: "a".to_string(),
382
+ id: 0,
383
+ },
384
+ }
385
+ .into(),
386
+ ],
387
+ }
388
+ .into(),
389
+ right: ast::FunctionCall {
390
+ name: "id".to_string(),
391
+ arguments: vec![
392
+ ast::Variable {
393
+ identifier: ast::VariableIdentifier {
394
+ name: "b".to_string(),
395
+ id: 1,
396
+ },
397
+ }
398
+ .into(),
399
+ ],
400
+ }
401
+ .into(),
402
+ },
403
+ );
404
+ }
405
+
406
+ #[test]
407
+ fn test_oc_parse_node_pattern_simple()
408
+ {
409
+ let actual = parse("(n:Person)", node_pattern);
410
+
411
+ let expected = ast::NodePattern {
412
+ variable: ast::VariableIdentifier {
413
+ name: "n".to_string(),
414
+ id: 0, // assumes first variable created gets id 0
415
+ }
416
+ .into(),
417
+ labels: ast::LabelExpression::String("Person".to_string()),
418
+ properties: None,
419
+ };
420
+ compare_ast(actual, expected);
421
+ }
422
+
423
+ #[test]
424
+ fn test_oc_parse_node_pattern_parameters()
425
+ {
426
+ let node = parse("(n {id: 12, name: 'foo'})", node_pattern);
427
+
428
+ let expected_node = ast::NodePattern {
429
+ variable: ast::VariableIdentifier {
430
+ name: "n".to_string(),
431
+ id: 0, // assumes first variable created gets id 0
432
+ }
433
+ .into(),
434
+ labels: ast::LabelExpression::None,
435
+ properties: Some(ast::Expression::Map(ast::Map {
436
+ map: vec![
437
+ (
438
+ "id".to_string(),
439
+ ast::Expression::Value(ast::Value { value: 12.into() }),
440
+ ),
441
+ (
442
+ "name".to_string(),
443
+ ast::Expression::Value(ast::Value {
444
+ value: "foo".into(),
445
+ }),
446
+ ),
447
+ ],
448
+ })),
449
+ };
450
+
451
+ compare_ast(node, expected_node);
452
+
453
+ let node = parse(r#"(n {id: "12", name: "foo"})"#, node_pattern);
454
+
455
+ let expected_node = ast::NodePattern {
456
+ variable: ast::VariableIdentifier {
457
+ name: "n".to_string(),
458
+ id: 0, // assumes first variable created gets id 0
459
+ }
460
+ .into(),
461
+ labels: ast::LabelExpression::None,
462
+ properties: Some(ast::Expression::Map(ast::Map {
463
+ map: vec![
464
+ (
465
+ "id".to_string(),
466
+ ast::Expression::Value(ast::Value { value: "12".into() }),
467
+ ),
468
+ (
469
+ "name".to_string(),
470
+ ast::Expression::Value(ast::Value {
471
+ value: "foo".into(),
472
+ }),
473
+ ),
474
+ ],
475
+ })),
476
+ };
477
+
478
+ compare_ast(node, expected_node);
479
+ }
480
+
481
+ #[test]
482
+ fn test_oc_parse_edge_patterns()
483
+ {
484
+ compare_parse_to_ast(
485
+ "(n)-[m]->(p)",
486
+ patterns::<AllowUndirected>,
487
+ vec![
488
+ ast::EdgePattern {
489
+ source: ast::NodePattern {
490
+ variable: ast::VariableIdentifier {
491
+ name: "n".to_string(),
492
+ id: 0,
493
+ }
494
+ .into(),
495
+ labels: ast::LabelExpression::None,
496
+ properties: None,
497
+ },
498
+ variable: ast::VariableIdentifier {
499
+ name: "m".to_string(),
500
+ id: 2,
501
+ }
502
+ .into(),
503
+ destination: ast::NodePattern {
504
+ variable: ast::VariableIdentifier {
505
+ name: "p".to_string(),
506
+ id: 1,
507
+ }
508
+ .into(),
509
+ labels: ast::LabelExpression::None,
510
+ properties: None,
511
+ },
512
+ directivity: graphcore::EdgeDirectivity::Directed,
513
+ labels: ast::LabelExpression::None,
514
+ properties: None,
515
+ recursive_range: None,
516
+ }
517
+ .into(),
518
+ ],
519
+ );
520
+
521
+ parse_fail("(n)<-[m]->(p)", patterns::<DisallowUndirected>, |e| {
522
+ matches!(e, ErrorKind::RequiresDirectedEdge)
523
+ });
524
+ parse_fail("(n)-[m]-(p)", patterns::<DisallowUndirected>, |e| {
525
+ matches!(e, ErrorKind::RequiresDirectedEdge)
526
+ });
527
+ parse_fail("(n)->[m]<-(p)", patterns::<DisallowUndirected>, |e| {
528
+ matches!(e, ErrorKind::ConflictingEdgeDirections)
529
+ });
530
+ parse_fail("(a)-[:A|:B]->(b)", patterns::<DisallowUndirected>, |e| {
531
+ matches!(e, ErrorKind::NoSingleRelationshipType)
532
+ });
533
+ }
534
+
535
+ #[test]
536
+ fn test_oc_parse_edge_range()
537
+ {
538
+ compare_parse_to_ast("*1..", edge_range, 1..usize::MAX);
539
+ compare_parse_to_ast("*1..3", edge_range, 1..3);
540
+ compare_parse_to_ast("*..3", edge_range, 0..3);
541
+ }
542
+
543
+ #[test]
544
+ fn test_oc_parse_edge_recursive_patterns()
545
+ {
546
+ compare_parse_to_ast(
547
+ "(n)-[m*1..2]->(p)",
548
+ patterns::<AllowUndirected>,
549
+ vec![
550
+ ast::EdgePattern {
551
+ source: ast::NodePattern {
552
+ variable: ast::VariableIdentifier {
553
+ name: "n".to_string(),
554
+ id: 0,
555
+ }
556
+ .into(),
557
+ labels: ast::LabelExpression::None,
558
+ properties: None,
559
+ },
560
+ variable: ast::VariableIdentifier {
561
+ name: "m".to_string(),
562
+ id: 2,
563
+ }
564
+ .into(),
565
+ destination: ast::NodePattern {
566
+ variable: ast::VariableIdentifier {
567
+ name: "p".to_string(),
568
+ id: 1,
569
+ }
570
+ .into(),
571
+ labels: ast::LabelExpression::None,
572
+ properties: None,
573
+ },
574
+ directivity: graphcore::EdgeDirectivity::Directed,
575
+ labels: ast::LabelExpression::None,
576
+ properties: None,
577
+ recursive_range: Some(1..2),
578
+ }
579
+ .into(),
580
+ ],
581
+ );
582
+ }
583
+
584
+ #[test]
585
+ fn test_oc_parse_array_literal()
586
+ {
587
+ compare_parse_to_ast(
588
+ "['male', 'female', null]",
589
+ array_literal,
590
+ ast::Array {
591
+ array: vec![
592
+ ast::Expression::Value(ast::Value {
593
+ value: "male".into(),
594
+ }),
595
+ ast::Expression::Value(ast::Value {
596
+ value: "female".into(),
597
+ }),
598
+ ast::Expression::Value(ast::Value {
599
+ value: graphcore::Value::Null,
600
+ }),
601
+ ],
602
+ },
603
+ );
604
+
605
+ compare_parse_to_ast("[]", array_literal, ast::Array { array: vec![] });
606
+
607
+ compare_parse_to_ast(
608
+ "[[]]",
609
+ array_literal,
610
+ ast::Array {
611
+ array: vec![ast::Array { array: vec![] }.into()],
612
+ },
613
+ );
614
+
615
+ compare_parse_to_ast(
616
+ "[null, 2]",
617
+ array_literal,
618
+ ast::Array {
619
+ array: vec![
620
+ ast::Expression::Value(ast::Value {
621
+ value: graphcore::Value::Null,
622
+ }),
623
+ ast::Expression::Value(ast::Value { value: 2.into() }),
624
+ ],
625
+ },
626
+ );
627
+
628
+ compare_parse_to_ast(
629
+ "[null, [ ' a ', ' ' ], ' [ a ', ' [ ], ] ', ' [ ', [ ' ' ], ' ] ' ]",
630
+ array_literal,
631
+ ast::Array {
632
+ array: vec![
633
+ ast::Expression::Value(ast::Value {
634
+ value: graphcore::Value::Null,
635
+ }),
636
+ ast::Array {
637
+ array: vec![
638
+ ast::Expression::Value(ast::Value {
639
+ value: " a ".into(),
640
+ }),
641
+ ast::Expression::Value(ast::Value { value: " ".into() }),
642
+ ],
643
+ }
644
+ .into(),
645
+ ast::Expression::Value(ast::Value {
646
+ value: " [ a ".into(),
647
+ }),
648
+ ast::Expression::Value(ast::Value {
649
+ value: " [ ], ] ".into(),
650
+ }),
651
+ ast::Expression::Value(ast::Value {
652
+ value: " [ ".into(),
653
+ }),
654
+ ast::Array {
655
+ array: vec![ast::Expression::Value(ast::Value { value: " ".into() })],
656
+ }
657
+ .into(),
658
+ ast::Expression::Value(ast::Value {
659
+ value: " ] ".into(),
660
+ }),
661
+ ],
662
+ },
663
+ );
664
+ }
665
+
666
+ #[test]
667
+ fn test_oc_parse_map_literal()
668
+ {
669
+ let node = parse("{id:12, name: 'foo'}", map_literal);
670
+
671
+ let expected = ast::Expression::Map(ast::Map {
672
+ map: vec![
673
+ (
674
+ "id".to_string(),
675
+ ast::Expression::Value(ast::Value { value: 12.into() }),
676
+ ),
677
+ (
678
+ "name".to_string(),
679
+ ast::Expression::Value(ast::Value {
680
+ value: "foo".into(),
681
+ }),
682
+ ),
683
+ ],
684
+ });
685
+
686
+ compare_ast(node, expected);
687
+ }
688
+
689
+ #[test]
690
+ fn test_oc_parse_map_pair()
691
+ {
692
+ let node = parse("id:12", map_pair);
693
+
694
+ let expected_pair = (
695
+ "id".to_string(),
696
+ ast::Expression::Value(ast::Value { value: 12.into() }),
697
+ );
698
+
699
+ assert_eq!(node, expected_pair);
700
+ }
701
+
702
+ #[test]
703
+ fn test_oc_parse_null()
704
+ {
705
+ let value = parse("null", expression);
706
+
707
+ let expected = ast::Expression::Value(ast::Value {
708
+ value: graphcore::Value::Null,
709
+ });
710
+
711
+ compare_ast(value, expected);
712
+ }
713
+
714
+ #[test]
715
+ fn test_oc_parse_booleans()
716
+ {
717
+ let value = parse("true", primary);
718
+
719
+ let expected = ast::Expression::Value(ast::Value { value: true.into() });
720
+
721
+ compare_ast(value, expected);
722
+ let value = parse("false", primary);
723
+
724
+ let expected = ast::Expression::Value(ast::Value {
725
+ value: false.into(),
726
+ });
727
+
728
+ compare_ast(value, expected);
729
+ }
730
+
731
+ #[test]
732
+ fn test_oc_parse_string_literal()
733
+ {
734
+ let value = parse("'foo'", expression);
735
+
736
+ let expected = ast::Expression::Value(ast::Value {
737
+ value: "foo".into(),
738
+ });
739
+
740
+ compare_ast(vec![value], vec![expected]);
741
+
742
+ let value = parse("''", expression);
743
+
744
+ let expected = ast::Expression::Value(ast::Value { value: "".into() });
745
+
746
+ compare_ast(vec![value], vec![expected]);
747
+ }
748
+ #[test]
749
+ fn test_oc_parse_node_pattern_with_parameter_properties()
750
+ {
751
+ let node = parse("(n $props)", node_pattern);
752
+
753
+ let expr = node.properties.expect("properties expected");
754
+
755
+ match expr
756
+ {
757
+ ast::Expression::Parameter(p) => assert_eq!(p.name, "$props"),
758
+ _ => panic!("expected parameter expression"),
759
+ }
760
+ }
761
+
762
+ #[test]
763
+ fn test_oc_parse_match_statement_without_where()
764
+ {
765
+ let actual = parse("MATCH (n)", match_statement);
766
+
767
+ let expected = ast::Statement::Match(ast::Match {
768
+ patterns: vec![
769
+ ast::NodePattern {
770
+ variable: ast::VariableIdentifier {
771
+ name: "n".to_string(),
772
+ id: 0,
773
+ }
774
+ .into(),
775
+ labels: ast::LabelExpression::None,
776
+ properties: None,
777
+ }
778
+ .into(),
779
+ ],
780
+ optional: false,
781
+ where_expression: None,
782
+ search: None,
783
+ });
784
+
785
+ compare_ast(actual, expected);
786
+ }
787
+
788
+ #[test]
789
+ fn test_oc_parse_optional_match_statement_without_where()
790
+ {
791
+ let actual = parse("OPTIONAL MATCH (n)", match_statement);
792
+
793
+ let expected = ast::Statement::Match(ast::Match {
794
+ patterns: vec![
795
+ ast::NodePattern {
796
+ variable: ast::VariableIdentifier {
797
+ name: "n".to_string(),
798
+ id: 0,
799
+ }
800
+ .into(),
801
+ labels: ast::LabelExpression::None,
802
+ properties: None,
803
+ }
804
+ .into(),
805
+ ],
806
+ optional: true,
807
+ where_expression: None,
808
+ search: None,
809
+ });
810
+
811
+ compare_ast(actual, expected);
812
+ }
813
+
814
+ #[test]
815
+ fn test_oc_parse_match_statement_double_edge()
816
+ {
817
+ let actual = parse("MATCH ()-->()-->()", match_statement);
818
+
819
+ let expected = ast::Statement::Match(ast::Match {
820
+ patterns: vec![
821
+ ast::EdgePattern {
822
+ source: ast::NodePattern {
823
+ variable: None,
824
+ labels: ast::LabelExpression::None,
825
+ properties: None,
826
+ },
827
+ destination: ast::NodePattern {
828
+ variable: Some(ast::VariableIdentifier {
829
+ name: "anonymous_0".to_string(),
830
+ id: 0,
831
+ }),
832
+ labels: ast::LabelExpression::None,
833
+ properties: None,
834
+ },
835
+ variable: None,
836
+ directivity: graphcore::EdgeDirectivity::Directed,
837
+ labels: ast::LabelExpression::None,
838
+ properties: None,
839
+ recursive_range: None,
840
+ }
841
+ .into(),
842
+ ast::EdgePattern {
843
+ source: ast::NodePattern {
844
+ variable: Some(ast::VariableIdentifier {
845
+ name: "anonymous_0".to_string(),
846
+ id: 0,
847
+ }),
848
+ labels: ast::LabelExpression::None,
849
+ properties: None,
850
+ },
851
+ destination: ast::NodePattern {
852
+ variable: None,
853
+ labels: ast::LabelExpression::None,
854
+ properties: None,
855
+ },
856
+ variable: None,
857
+ directivity: graphcore::EdgeDirectivity::Directed,
858
+ labels: ast::LabelExpression::None,
859
+ properties: None,
860
+ recursive_range: None,
861
+ }
862
+ .into(),
863
+ ],
864
+ optional: false,
865
+ where_expression: None,
866
+ search: None,
867
+ });
868
+
869
+ compare_ast(actual, expected);
870
+ }
871
+
872
+ #[test]
873
+ fn test_oc_parse_merge_basic_pattern()
874
+ {
875
+ let actual = parse("MERGE (n:Person {name: \"Alice\"})", merge_statement);
876
+
877
+ let expected = ast::Statement::Merge(ast::Merge {
878
+ patterns: vec![
879
+ ast::NodePattern {
880
+ variable: Some(ast::VariableIdentifier {
881
+ name: "n".to_string(),
882
+ id: 0,
883
+ }),
884
+ labels: ast::LabelExpression::String("Person".to_string()),
885
+ properties: Some(ast::Expression::Map(ast::Map {
886
+ map: vec![(
887
+ "name".to_string(),
888
+ ast::Expression::Value(ast::Value {
889
+ value: "Alice".into(),
890
+ }),
891
+ )],
892
+ })),
893
+ }
894
+ .into(),
895
+ ],
896
+ on_create: vec![],
897
+ on_match: vec![],
898
+ });
899
+
900
+ compare_ast(actual, expected);
901
+ }
902
+
903
+ #[test]
904
+ fn test_oc_parse_merge_with_on_create_set()
905
+ {
906
+ let actual = parse("MERGE (n:Person) ON CREATE SET n.x = 1", merge_statement);
907
+
908
+ let expected = ast::Statement::Merge(ast::Merge {
909
+ patterns: vec![
910
+ ast::NodePattern {
911
+ variable: Some(ast::VariableIdentifier {
912
+ name: "n".to_string(),
913
+ id: 0,
914
+ }),
915
+ labels: ast::LabelExpression::String("Person".to_string()),
916
+ properties: None,
917
+ }
918
+ .into(),
919
+ ],
920
+ on_create: vec![ast::OneUpdate::SetProperty(ast::UpdateProperty {
921
+ target: ast::VariableIdentifier {
922
+ name: "n".to_string(),
923
+ id: 0,
924
+ },
925
+ path: vec!["x".to_string()],
926
+ expression: ast::Expression::Value(ast::Value { value: 1.into() }),
927
+ })],
928
+ on_match: vec![],
929
+ });
930
+
931
+ compare_ast(actual, expected);
932
+ }
933
+
934
+ #[test]
935
+ fn test_oc_parse_merge_with_on_match_set()
936
+ {
937
+ let actual = parse("MERGE (n:Person) ON MATCH SET n.x = 2", merge_statement);
938
+
939
+ let expected = ast::Statement::Merge(ast::Merge {
940
+ patterns: vec![
941
+ ast::NodePattern {
942
+ variable: Some(ast::VariableIdentifier {
943
+ name: "n".to_string(),
944
+ id: 0,
945
+ }),
946
+ labels: ast::LabelExpression::String("Person".to_string()),
947
+ properties: None,
948
+ }
949
+ .into(),
950
+ ],
951
+ on_create: vec![],
952
+ on_match: vec![ast::OneUpdate::SetProperty(ast::UpdateProperty {
953
+ target: ast::VariableIdentifier {
954
+ name: "n".to_string(),
955
+ id: 0,
956
+ },
957
+ path: vec!["x".to_string()],
958
+ expression: ast::Expression::Value(ast::Value { value: 2.into() }),
959
+ })],
960
+ });
961
+
962
+ compare_ast(actual, expected);
963
+ }
964
+
965
+ #[test]
966
+ fn test_oc_parse_merge_with_both_on_clauses()
967
+ {
968
+ let actual = parse(
969
+ "MERGE (n:Person) ON CREATE SET n.x = 1 ON MATCH SET n.y = 2",
970
+ merge_statement,
971
+ );
972
+
973
+ let expected = ast::Statement::Merge(ast::Merge {
974
+ patterns: vec![
975
+ ast::NodePattern {
976
+ variable: Some(ast::VariableIdentifier {
977
+ name: "n".to_string(),
978
+ id: 0,
979
+ }),
980
+ labels: ast::LabelExpression::String("Person".to_string()),
981
+ properties: None,
982
+ }
983
+ .into(),
984
+ ],
985
+ on_create: vec![ast::OneUpdate::SetProperty(ast::UpdateProperty {
986
+ target: ast::VariableIdentifier {
987
+ name: "n".to_string(),
988
+ id: 0,
989
+ },
990
+ path: vec!["x".to_string()],
991
+ expression: ast::Expression::Value(ast::Value { value: 1.into() }),
992
+ })],
993
+ on_match: vec![ast::OneUpdate::SetProperty(ast::UpdateProperty {
994
+ target: ast::VariableIdentifier {
995
+ name: "n".to_string(),
996
+ id: 0,
997
+ },
998
+ path: vec!["y".to_string()],
999
+ expression: ast::Expression::Value(ast::Value { value: 2.into() }),
1000
+ })],
1001
+ });
1002
+
1003
+ compare_ast(actual, expected);
1004
+ }
1005
+
1006
+ #[test]
1007
+ fn test_oc_parse_merge_with_both_on_clauses_reversed()
1008
+ {
1009
+ let actual = parse(
1010
+ "MERGE (n:Person) ON MATCH SET n.y = 2 ON CREATE SET n.x = 1",
1011
+ merge_statement,
1012
+ );
1013
+
1014
+ let expected = ast::Statement::Merge(ast::Merge {
1015
+ patterns: vec![
1016
+ ast::NodePattern {
1017
+ variable: Some(ast::VariableIdentifier {
1018
+ name: "n".to_string(),
1019
+ id: 0,
1020
+ }),
1021
+ labels: ast::LabelExpression::String("Person".to_string()),
1022
+ properties: None,
1023
+ }
1024
+ .into(),
1025
+ ],
1026
+ on_create: vec![ast::OneUpdate::SetProperty(ast::UpdateProperty {
1027
+ target: ast::VariableIdentifier {
1028
+ name: "n".to_string(),
1029
+ id: 0,
1030
+ },
1031
+ path: vec!["x".to_string()],
1032
+ expression: ast::Expression::Value(ast::Value { value: 1.into() }),
1033
+ })],
1034
+ on_match: vec![ast::OneUpdate::SetProperty(ast::UpdateProperty {
1035
+ target: ast::VariableIdentifier {
1036
+ name: "n".to_string(),
1037
+ id: 0,
1038
+ },
1039
+ path: vec!["y".to_string()],
1040
+ expression: ast::Expression::Value(ast::Value { value: 2.into() }),
1041
+ })],
1042
+ });
1043
+
1044
+ compare_ast(actual, expected);
1045
+ }
1046
+
1047
+ #[test]
1048
+ fn test_oc_parse_merge_with_edge_pattern()
1049
+ {
1050
+ let actual = parse("MERGE (a)-[:KNOWS]->(b)", merge_statement);
1051
+
1052
+ let expected = ast::Statement::Merge(ast::Merge {
1053
+ patterns: vec![
1054
+ ast::EdgePattern {
1055
+ source: ast::NodePattern {
1056
+ variable: Some(ast::VariableIdentifier {
1057
+ name: "a".to_string(),
1058
+ id: 0,
1059
+ }),
1060
+ labels: ast::LabelExpression::None,
1061
+ properties: None,
1062
+ },
1063
+ destination: ast::NodePattern {
1064
+ variable: Some(ast::VariableIdentifier {
1065
+ name: "b".to_string(),
1066
+ id: 1,
1067
+ }),
1068
+ labels: ast::LabelExpression::None,
1069
+ properties: None,
1070
+ },
1071
+ variable: None,
1072
+ labels: ast::LabelExpression::String("KNOWS".to_string()),
1073
+ properties: None,
1074
+ directivity: graphcore::EdgeDirectivity::Directed,
1075
+ recursive_range: None,
1076
+ }
1077
+ .into(),
1078
+ ],
1079
+ on_create: vec![],
1080
+ on_match: vec![],
1081
+ });
1082
+
1083
+ compare_ast(actual, expected);
1084
+ }
1085
+
1086
+ #[test]
1087
+ fn test_oc_parse_merge_statement()
1088
+ {
1089
+ compare_parse_to_ast(
1090
+ "CREATE (n)",
1091
+ create_statement,
1092
+ ast::Create {
1093
+ patterns: vec![
1094
+ ast::NodePattern {
1095
+ variable: ast::VariableIdentifier {
1096
+ name: "n".to_string(),
1097
+ id: 0,
1098
+ }
1099
+ .into(),
1100
+ labels: ast::LabelExpression::None,
1101
+ properties: None,
1102
+ }
1103
+ .into(),
1104
+ ],
1105
+ },
1106
+ );
1107
+ compare_parse_to_ast(
1108
+ "CREATE ()",
1109
+ create_statement,
1110
+ ast::Create {
1111
+ patterns: vec![
1112
+ ast::NodePattern {
1113
+ variable: None,
1114
+ labels: ast::LabelExpression::None,
1115
+ properties: None,
1116
+ }
1117
+ .into(),
1118
+ ],
1119
+ },
1120
+ );
1121
+ compare_parse_to_ast(
1122
+ "CREATE (:A)",
1123
+ create_statement,
1124
+ ast::Create {
1125
+ patterns: vec![
1126
+ ast::NodePattern {
1127
+ variable: None,
1128
+ labels: ast::LabelExpression::String("A".to_string()),
1129
+ properties: None,
1130
+ }
1131
+ .into(),
1132
+ ],
1133
+ },
1134
+ );
1135
+ compare_parse_to_ast(
1136
+ "CREATE (:A:B)",
1137
+ create_statement,
1138
+ ast::Create {
1139
+ patterns: vec![
1140
+ ast::NodePattern {
1141
+ variable: None,
1142
+ labels: ast::LabelExpression::And(vec![
1143
+ ast::LabelExpression::String("A".to_string()),
1144
+ ast::LabelExpression::String("B".to_string()),
1145
+ ]),
1146
+ properties: None,
1147
+ }
1148
+ .into(),
1149
+ ],
1150
+ },
1151
+ );
1152
+ }
1153
+
1154
+ #[test]
1155
+ fn test_oc_parse_create_edge_statements()
1156
+ {
1157
+ compare_parse_to_ast(
1158
+ "CREATE ()-[:R]->()",
1159
+ create_statement,
1160
+ ast::Create {
1161
+ patterns: vec![
1162
+ ast::EdgePattern {
1163
+ source: ast::NodePattern {
1164
+ variable: None,
1165
+ labels: ast::LabelExpression::None,
1166
+ properties: None,
1167
+ },
1168
+ destination: ast::NodePattern {
1169
+ variable: None,
1170
+ labels: ast::LabelExpression::None,
1171
+ properties: None,
1172
+ },
1173
+ variable: None,
1174
+ directivity: graphcore::EdgeDirectivity::Directed,
1175
+ labels: ast::LabelExpression::String("R".to_string()),
1176
+ properties: None,
1177
+ recursive_range: None,
1178
+ }
1179
+ .into(),
1180
+ ],
1181
+ },
1182
+ );
1183
+ compare_parse_to_ast(
1184
+ "CREATE (a)<-[:FOO]-(b)",
1185
+ create_statement,
1186
+ ast::Create {
1187
+ patterns: vec![
1188
+ ast::EdgePattern {
1189
+ source: ast::NodePattern {
1190
+ variable: Some(ast::VariableIdentifier {
1191
+ name: "b".to_string(),
1192
+ id: 1,
1193
+ }),
1194
+ labels: ast::LabelExpression::None,
1195
+ properties: None,
1196
+ },
1197
+ destination: ast::NodePattern {
1198
+ variable: Some(ast::VariableIdentifier {
1199
+ name: "a".to_string(),
1200
+ id: 0,
1201
+ }),
1202
+ labels: ast::LabelExpression::None,
1203
+ properties: None,
1204
+ },
1205
+ variable: None,
1206
+ directivity: graphcore::EdgeDirectivity::Directed,
1207
+ labels: ast::LabelExpression::String("FOO".to_string()),
1208
+ properties: None,
1209
+ recursive_range: None,
1210
+ }
1211
+ .into(),
1212
+ ],
1213
+ },
1214
+ );
1215
+ parse_fail("CREATE (a)-[:FOO]-(b)", create_statement, |e| {
1216
+ matches!(e, ErrorKind::RequiresDirectedEdge)
1217
+ });
1218
+ parse_fail("CREATE (a)<-[:FOO]->(b)", create_statement, |e| {
1219
+ matches!(e, ErrorKind::RequiresDirectedEdge)
1220
+ });
1221
+ }
1222
+
1223
+ #[test]
1224
+ fn test_oc_parse_return_star()
1225
+ {
1226
+ let stmt = parse("RETURN *", return_statement);
1227
+
1228
+ match stmt
1229
+ {
1230
+ ast::Statement::Return(r) =>
1231
+ {
1232
+ assert!(r.all);
1233
+ }
1234
+ _ => panic!("expected RETURN statement"),
1235
+ }
1236
+ }
1237
+
1238
+ #[test]
1239
+ fn test_oc_parse_return_n()
1240
+ {
1241
+ let stmt = parse("RETURN n", return_statement);
1242
+
1243
+ let exppected_stmt = ast::Statement::Return(ast::Return {
1244
+ all: false,
1245
+ expressions: vec![ast::NamedExpression {
1246
+ identifier: ast::VariableIdentifier {
1247
+ name: "n".to_string(),
1248
+ id: 0,
1249
+ },
1250
+ expression: ast::Variable {
1251
+ identifier: ast::VariableIdentifier {
1252
+ name: "n".to_string(),
1253
+ id: 0,
1254
+ },
1255
+ }
1256
+ .into(),
1257
+ }],
1258
+ where_expression: None,
1259
+ modifiers: Default::default(),
1260
+ });
1261
+ compare_ast(vec![stmt], vec![exppected_stmt]);
1262
+ }
1263
+
1264
+ #[test]
1265
+ fn test_oc_parse_return_n_m()
1266
+ {
1267
+ let stmt = parse("RETURN n, m", return_statement);
1268
+
1269
+ let exppected_stmt = ast::Statement::Return(ast::Return {
1270
+ all: false,
1271
+ expressions: vec![
1272
+ ast::NamedExpression {
1273
+ identifier: ast::VariableIdentifier {
1274
+ name: "n".to_string(),
1275
+ id: 0,
1276
+ },
1277
+ expression: ast::Variable {
1278
+ identifier: ast::VariableIdentifier {
1279
+ name: "n".to_string(),
1280
+ id: 0,
1281
+ },
1282
+ }
1283
+ .into(),
1284
+ },
1285
+ ast::NamedExpression {
1286
+ identifier: ast::VariableIdentifier {
1287
+ name: "m".to_string(),
1288
+ id: 1,
1289
+ },
1290
+ expression: ast::Variable {
1291
+ identifier: ast::VariableIdentifier {
1292
+ name: "m".to_string(),
1293
+ id: 1,
1294
+ },
1295
+ }
1296
+ .into(),
1297
+ },
1298
+ ],
1299
+ where_expression: None,
1300
+ modifiers: Default::default(),
1301
+ });
1302
+ compare_ast(vec![stmt], vec![exppected_stmt]);
1303
+ }
1304
+
1305
+ #[test]
1306
+ fn test_oc_parse_return_n_p_1()
1307
+ {
1308
+ let stmt = parse("RETURN n+1", return_statement);
1309
+
1310
+ let expected_stmt = ast::Statement::Return(ast::Return {
1311
+ all: false,
1312
+ expressions: vec![ast::NamedExpression {
1313
+ identifier: ast::VariableIdentifier {
1314
+ name: "n+1".to_string(),
1315
+ id: 1,
1316
+ },
1317
+ expression: ast::Addition {
1318
+ left: ast::Variable {
1319
+ identifier: ast::VariableIdentifier {
1320
+ name: "n".to_string(),
1321
+ id: 0,
1322
+ },
1323
+ }
1324
+ .into(),
1325
+ right: ast::Value { value: 1.into() }.into(),
1326
+ }
1327
+ .into(),
1328
+ }],
1329
+ where_expression: None,
1330
+ modifiers: Default::default(),
1331
+ });
1332
+ compare_ast(vec![stmt], vec![expected_stmt]);
1333
+ }
1334
+
1335
+ #[test]
1336
+ fn test_oc_parse_return_n_as_p()
1337
+ {
1338
+ let stmt = parse("RETURN n AS p", return_statement);
1339
+
1340
+ let expected_stmt = ast::Statement::Return(ast::Return {
1341
+ all: false,
1342
+ expressions: vec![ast::NamedExpression {
1343
+ identifier: ast::VariableIdentifier {
1344
+ name: "p".to_string(),
1345
+ id: 1,
1346
+ },
1347
+ expression: ast::Variable {
1348
+ identifier: ast::VariableIdentifier {
1349
+ name: "n".to_string(),
1350
+ id: 0,
1351
+ },
1352
+ }
1353
+ .into(),
1354
+ }],
1355
+ where_expression: None,
1356
+ modifiers: Default::default(),
1357
+ });
1358
+ compare_ast(vec![stmt], vec![expected_stmt]);
1359
+ }
1360
+
1361
+ #[test]
1362
+ fn test_oc_parse_return_n_name_as_p()
1363
+ {
1364
+ let stmt = parse("RETURN n.name AS p", return_statement);
1365
+
1366
+ let expected_stmt = ast::Statement::Return(ast::Return {
1367
+ all: false,
1368
+ expressions: vec![ast::NamedExpression {
1369
+ identifier: ast::VariableIdentifier {
1370
+ name: "p".to_string(),
1371
+ id: 1,
1372
+ },
1373
+ expression: ast::MemberAccess {
1374
+ left: ast::Variable {
1375
+ identifier: ast::VariableIdentifier {
1376
+ name: "n".to_string(),
1377
+ id: 0,
1378
+ },
1379
+ }
1380
+ .into(),
1381
+ path: vec!["name".to_string()],
1382
+ }
1383
+ .into(),
1384
+ }],
1385
+ where_expression: None,
1386
+ modifiers: Default::default(),
1387
+ });
1388
+ compare_ast(vec![stmt], vec![expected_stmt]);
1389
+ }
1390
+
1391
+ #[test]
1392
+ fn test_oc_parse_return_n_modifiers()
1393
+ {
1394
+ compare_parse_to_ast(
1395
+ "RETURN n LIMIT -1",
1396
+ return_statement,
1397
+ ast::Statement::Return(ast::Return {
1398
+ all: false,
1399
+ expressions: vec![ast::NamedExpression {
1400
+ identifier: ast::VariableIdentifier {
1401
+ name: "n".to_string(),
1402
+ id: 0,
1403
+ },
1404
+ expression: ast::Variable {
1405
+ identifier: ast::VariableIdentifier {
1406
+ name: "n".to_string(),
1407
+ id: 0,
1408
+ },
1409
+ }
1410
+ .into(),
1411
+ }],
1412
+ where_expression: None,
1413
+ modifiers: ast::Modifiers {
1414
+ order_by: None,
1415
+ limit: Some(ast::Expression::Value(ast::Value { value: (-1).into() })),
1416
+ skip: None,
1417
+ },
1418
+ }),
1419
+ );
1420
+ }
1421
+
1422
+ #[test]
1423
+ fn test_oc_parse_with_n_m()
1424
+ {
1425
+ let stmt = parse("WITH n, m", with_statement);
1426
+
1427
+ let exppected_stmt = ast::With {
1428
+ all: false,
1429
+ expressions: vec![
1430
+ ast::NamedExpression {
1431
+ identifier: ast::VariableIdentifier {
1432
+ name: "n".to_string(),
1433
+ id: 0,
1434
+ },
1435
+ expression: ast::Variable {
1436
+ identifier: ast::VariableIdentifier {
1437
+ name: "n".to_string(),
1438
+ id: 0,
1439
+ },
1440
+ }
1441
+ .into(),
1442
+ },
1443
+ ast::NamedExpression {
1444
+ identifier: ast::VariableIdentifier {
1445
+ name: "m".to_string(),
1446
+ id: 1,
1447
+ },
1448
+ expression: ast::Variable {
1449
+ identifier: ast::VariableIdentifier {
1450
+ name: "m".to_string(),
1451
+ id: 1,
1452
+ },
1453
+ }
1454
+ .into(),
1455
+ },
1456
+ ],
1457
+ where_expression: None,
1458
+ modifiers: Default::default(),
1459
+ }
1460
+ .into();
1461
+ compare_ast(vec![stmt], vec![exppected_stmt]);
1462
+ }
1463
+
1464
+ #[test]
1465
+ fn test_oc_parse_with_n_as_m()
1466
+ {
1467
+ compare_parse_to_ast(
1468
+ "WITH n AS m",
1469
+ with_statement,
1470
+ ast::With {
1471
+ all: false,
1472
+ expressions: vec![ast::NamedExpression {
1473
+ identifier: ast::VariableIdentifier {
1474
+ name: "m".to_string(),
1475
+ id: 1,
1476
+ },
1477
+ expression: ast::Variable {
1478
+ identifier: ast::VariableIdentifier {
1479
+ name: "n".to_string(),
1480
+ id: 0,
1481
+ },
1482
+ }
1483
+ .into(),
1484
+ }],
1485
+ where_expression: None,
1486
+ modifiers: Default::default(),
1487
+ },
1488
+ );
1489
+ }
1490
+
1491
+ #[test]
1492
+ fn test_oc_parse_modifiers()
1493
+ {
1494
+ compare_parse_to_ast(
1495
+ "ORDER BY n.name DESC, n.age ASC LIMIT 4 SKIP 2",
1496
+ modifiers,
1497
+ ast::Modifiers {
1498
+ order_by: Some(ast::OrderBy {
1499
+ expressions: vec![
1500
+ ast::OrderByExpression {
1501
+ asc: false,
1502
+ expression: ast::MemberAccess {
1503
+ left: ast::Variable {
1504
+ identifier: ast::VariableIdentifier {
1505
+ name: "n".to_string(),
1506
+ id: 0,
1507
+ },
1508
+ }
1509
+ .into(),
1510
+ path: vec!["name".to_string()],
1511
+ }
1512
+ .into(),
1513
+ },
1514
+ ast::OrderByExpression {
1515
+ asc: true,
1516
+ expression: ast::MemberAccess {
1517
+ left: ast::Variable {
1518
+ identifier: ast::VariableIdentifier {
1519
+ name: "n".to_string(),
1520
+ id: 0,
1521
+ },
1522
+ }
1523
+ .into(),
1524
+ path: vec!["age".to_string()],
1525
+ }
1526
+ .into(),
1527
+ },
1528
+ ],
1529
+ }),
1530
+ limit: Some(ast::Expression::Value(ast::Value { value: 4.into() })),
1531
+ skip: Some(ast::Expression::Value(ast::Value { value: 2.into() })),
1532
+ },
1533
+ );
1534
+ compare_parse_to_ast(
1535
+ "ORDER BY n.name DESC, n.age LIMIT 4",
1536
+ modifiers,
1537
+ ast::Modifiers {
1538
+ order_by: Some(ast::OrderBy {
1539
+ expressions: vec![
1540
+ ast::OrderByExpression {
1541
+ asc: false,
1542
+ expression: ast::MemberAccess {
1543
+ left: ast::Variable {
1544
+ identifier: ast::VariableIdentifier {
1545
+ name: "n".to_string(),
1546
+ id: 0,
1547
+ },
1548
+ }
1549
+ .into(),
1550
+ path: vec!["name".to_string()],
1551
+ }
1552
+ .into(),
1553
+ },
1554
+ ast::OrderByExpression {
1555
+ asc: true,
1556
+ expression: ast::MemberAccess {
1557
+ left: ast::Variable {
1558
+ identifier: ast::VariableIdentifier {
1559
+ name: "n".to_string(),
1560
+ id: 0,
1561
+ },
1562
+ }
1563
+ .into(),
1564
+ path: vec!["age".to_string()],
1565
+ }
1566
+ .into(),
1567
+ },
1568
+ ],
1569
+ }),
1570
+ limit: Some(ast::Expression::Value(ast::Value { value: 4.into() })),
1571
+ skip: None,
1572
+ },
1573
+ );
1574
+ compare_parse_to_ast(
1575
+ "LIMIT -1",
1576
+ modifiers,
1577
+ ast::Modifiers {
1578
+ order_by: None,
1579
+ limit: Some(ast::Expression::Value(ast::Value { value: (-1).into() })),
1580
+ skip: None,
1581
+ },
1582
+ );
1583
+ }
1584
+
1585
+ #[test]
1586
+ fn test_oc_parse_with_modifiers()
1587
+ {
1588
+ compare_parse_to_ast(
1589
+ "WITH n ORDER BY n.name DESC, n.age LIMIT 4",
1590
+ with_statement,
1591
+ ast::With {
1592
+ all: false,
1593
+ expressions: vec![ast::NamedExpression {
1594
+ identifier: ast::VariableIdentifier {
1595
+ name: "n".to_string(),
1596
+ id: 0,
1597
+ },
1598
+ expression: ast::Variable {
1599
+ identifier: ast::VariableIdentifier {
1600
+ name: "n".to_string(),
1601
+ id: 0,
1602
+ },
1603
+ }
1604
+ .into(),
1605
+ }],
1606
+ where_expression: None,
1607
+ modifiers: ast::Modifiers {
1608
+ order_by: Some(ast::OrderBy {
1609
+ expressions: vec![
1610
+ ast::OrderByExpression {
1611
+ asc: false,
1612
+ expression: ast::MemberAccess {
1613
+ left: ast::Variable {
1614
+ identifier: ast::VariableIdentifier {
1615
+ name: "n".to_string(),
1616
+ id: 0,
1617
+ },
1618
+ }
1619
+ .into(),
1620
+ path: vec!["name".to_string()],
1621
+ }
1622
+ .into(),
1623
+ },
1624
+ ast::OrderByExpression {
1625
+ asc: true,
1626
+ expression: ast::MemberAccess {
1627
+ left: ast::Variable {
1628
+ identifier: ast::VariableIdentifier {
1629
+ name: "n".to_string(),
1630
+ id: 0,
1631
+ },
1632
+ }
1633
+ .into(),
1634
+ path: vec!["age".to_string()],
1635
+ }
1636
+ .into(),
1637
+ },
1638
+ ],
1639
+ }),
1640
+ limit: Some(ast::Expression::Value(ast::Value { value: 4.into() })),
1641
+ skip: None,
1642
+ },
1643
+ },
1644
+ );
1645
+ }
1646
+
1647
+ #[test]
1648
+ fn test_oc_parse_unwind_n_as_m()
1649
+ {
1650
+ compare_parse_to_ast(
1651
+ "UNWIND n AS m",
1652
+ unwind_statement,
1653
+ ast::Unwind {
1654
+ identifier: ast::VariableIdentifier {
1655
+ name: "m".to_string(),
1656
+ id: 1,
1657
+ },
1658
+ expression: ast::Variable {
1659
+ identifier: ast::VariableIdentifier {
1660
+ name: "n".to_string(),
1661
+ id: 0,
1662
+ },
1663
+ }
1664
+ .into(),
1665
+ },
1666
+ );
1667
+ }
1668
+
1669
+ #[test]
1670
+ fn test_oc_parse_queries_multiple_statements()
1671
+ {
1672
+ let qs = parse("MATCH (n) RETURN *", queries);
1673
+
1674
+ let expected: ast::Queries = vec![vec![
1675
+ ast::Match {
1676
+ patterns: vec![
1677
+ ast::NodePattern {
1678
+ variable: ast::VariableIdentifier {
1679
+ name: "n".to_string(),
1680
+ id: 0,
1681
+ }
1682
+ .into(),
1683
+ labels: ast::LabelExpression::None,
1684
+ properties: None,
1685
+ }
1686
+ .into(),
1687
+ ],
1688
+ optional: false,
1689
+ where_expression: None,
1690
+ search: None,
1691
+ }
1692
+ .into(),
1693
+ ast::Statement::Return(ast::Return {
1694
+ all: true,
1695
+ expressions: Default::default(),
1696
+ where_expression: None,
1697
+ modifiers: Default::default(),
1698
+ }),
1699
+ ]]
1700
+ .into_iter()
1701
+ .map(ast::Query::Statements)
1702
+ .collect();
1703
+
1704
+ compare_ast(qs, expected);
1705
+ }
1706
+
1707
+ #[test]
1708
+ fn test_oc_parse_queries_multiple_queries()
1709
+ {
1710
+ let qs = parse("MATCH (n); RETURN *", queries);
1711
+
1712
+ let expected: ast::Queries = vec![
1713
+ vec![
1714
+ ast::Match {
1715
+ patterns: vec![
1716
+ ast::NodePattern {
1717
+ variable: ast::VariableIdentifier {
1718
+ name: "n".to_string(),
1719
+ id: 0,
1720
+ }
1721
+ .into(),
1722
+ labels: ast::LabelExpression::None,
1723
+ properties: None,
1724
+ }
1725
+ .into(),
1726
+ ],
1727
+ optional: false,
1728
+ where_expression: None,
1729
+ search: None,
1730
+ }
1731
+ .into(),
1732
+ ],
1733
+ vec![ast::Statement::Return(ast::Return {
1734
+ all: true,
1735
+ expressions: Default::default(),
1736
+ where_expression: None,
1737
+ modifiers: Default::default(),
1738
+ })],
1739
+ ]
1740
+ .into_iter()
1741
+ .map(ast::Query::Statements)
1742
+ .collect();
1743
+
1744
+ compare_ast(qs, expected);
1745
+ }
1746
+
1747
+ #[test]
1748
+ fn test_oc_parse_query_union_basic()
1749
+ {
1750
+ compare_parse_to_ast(
1751
+ "MATCH (n) UNION MATCH (m)",
1752
+ queries,
1753
+ vec![ast::Query::Union {
1754
+ branches: vec![
1755
+ vec![
1756
+ ast::Match {
1757
+ patterns: vec![
1758
+ ast::NodePattern {
1759
+ variable: ast::VariableIdentifier {
1760
+ name: "n".to_string(),
1761
+ id: 0,
1762
+ }
1763
+ .into(),
1764
+ labels: ast::LabelExpression::None,
1765
+ properties: None,
1766
+ }
1767
+ .into(),
1768
+ ],
1769
+ optional: false,
1770
+ where_expression: None,
1771
+ search: None,
1772
+ }
1773
+ .into(),
1774
+ ],
1775
+ vec![
1776
+ ast::Match {
1777
+ patterns: vec![
1778
+ ast::NodePattern {
1779
+ variable: ast::VariableIdentifier {
1780
+ name: "m".to_string(),
1781
+ id: 1,
1782
+ }
1783
+ .into(),
1784
+ labels: ast::LabelExpression::None,
1785
+ properties: None,
1786
+ }
1787
+ .into(),
1788
+ ],
1789
+ optional: false,
1790
+ where_expression: None,
1791
+ search: None,
1792
+ }
1793
+ .into(),
1794
+ ],
1795
+ ],
1796
+ all: false,
1797
+ }],
1798
+ );
1799
+ }
1800
+
1801
+ #[test]
1802
+ fn test_oc_parse_query_union_all_basic()
1803
+ {
1804
+ compare_parse_to_ast(
1805
+ "MATCH (n) UNION ALL MATCH (m)",
1806
+ queries,
1807
+ vec![ast::Query::Union {
1808
+ branches: vec![
1809
+ vec![
1810
+ ast::Match {
1811
+ patterns: vec![
1812
+ ast::NodePattern {
1813
+ variable: ast::VariableIdentifier {
1814
+ name: "n".to_string(),
1815
+ id: 0,
1816
+ }
1817
+ .into(),
1818
+ labels: ast::LabelExpression::None,
1819
+ properties: None,
1820
+ }
1821
+ .into(),
1822
+ ],
1823
+ optional: false,
1824
+ where_expression: None,
1825
+ search: None,
1826
+ }
1827
+ .into(),
1828
+ ],
1829
+ vec![
1830
+ ast::Match {
1831
+ patterns: vec![
1832
+ ast::NodePattern {
1833
+ variable: ast::VariableIdentifier {
1834
+ name: "m".to_string(),
1835
+ id: 1,
1836
+ }
1837
+ .into(),
1838
+ labels: ast::LabelExpression::None,
1839
+ properties: None,
1840
+ }
1841
+ .into(),
1842
+ ],
1843
+ optional: false,
1844
+ where_expression: None,
1845
+ search: None,
1846
+ }
1847
+ .into(),
1848
+ ],
1849
+ ],
1850
+ all: true,
1851
+ }],
1852
+ );
1853
+ }
1854
+
1855
+ #[test]
1856
+ fn test_oc_parse_query_chained_union_basic()
1857
+ {
1858
+ compare_parse_to_ast(
1859
+ "MATCH (a) UNION MATCH (b) UNION MATCH (c)",
1860
+ queries,
1861
+ vec![ast::Query::Union {
1862
+ branches: vec![
1863
+ vec![
1864
+ ast::Match {
1865
+ patterns: vec![
1866
+ ast::NodePattern {
1867
+ variable: ast::VariableIdentifier {
1868
+ name: "a".to_string(),
1869
+ id: 0,
1870
+ }
1871
+ .into(),
1872
+ labels: ast::LabelExpression::None,
1873
+ properties: None,
1874
+ }
1875
+ .into(),
1876
+ ],
1877
+ optional: false,
1878
+ where_expression: None,
1879
+ search: None,
1880
+ }
1881
+ .into(),
1882
+ ],
1883
+ vec![
1884
+ ast::Match {
1885
+ patterns: vec![
1886
+ ast::NodePattern {
1887
+ variable: ast::VariableIdentifier {
1888
+ name: "b".to_string(),
1889
+ id: 1,
1890
+ }
1891
+ .into(),
1892
+ labels: ast::LabelExpression::None,
1893
+ properties: None,
1894
+ }
1895
+ .into(),
1896
+ ],
1897
+ optional: false,
1898
+ where_expression: None,
1899
+ search: None,
1900
+ }
1901
+ .into(),
1902
+ ],
1903
+ vec![
1904
+ ast::Match {
1905
+ patterns: vec![
1906
+ ast::NodePattern {
1907
+ variable: ast::VariableIdentifier {
1908
+ name: "c".to_string(),
1909
+ id: 2,
1910
+ }
1911
+ .into(),
1912
+ labels: ast::LabelExpression::None,
1913
+ properties: None,
1914
+ }
1915
+ .into(),
1916
+ ],
1917
+ optional: false,
1918
+ where_expression: None,
1919
+ search: None,
1920
+ }
1921
+ .into(),
1922
+ ],
1923
+ ],
1924
+ all: false,
1925
+ }],
1926
+ );
1927
+ }
1928
+
1929
+ #[test]
1930
+ fn test_oc_parse_query_plain()
1931
+ {
1932
+ compare_parse_to_ast(
1933
+ "MATCH (n) RETURN n",
1934
+ queries,
1935
+ vec![ast::Query::Statements(vec![
1936
+ ast::Match {
1937
+ patterns: vec![
1938
+ ast::NodePattern {
1939
+ variable: ast::VariableIdentifier {
1940
+ name: "n".to_string(),
1941
+ id: 0,
1942
+ }
1943
+ .into(),
1944
+ labels: ast::LabelExpression::None,
1945
+ properties: None,
1946
+ }
1947
+ .into(),
1948
+ ],
1949
+ optional: false,
1950
+ where_expression: None,
1951
+ search: None,
1952
+ }
1953
+ .into(),
1954
+ ast::Statement::Return(ast::Return {
1955
+ all: false,
1956
+ expressions: vec![ast::NamedExpression {
1957
+ identifier: ast::VariableIdentifier {
1958
+ name: "n".to_string(),
1959
+ id: 0,
1960
+ },
1961
+ expression: ast::Variable {
1962
+ identifier: ast::VariableIdentifier {
1963
+ name: "n".to_string(),
1964
+ id: 0,
1965
+ },
1966
+ }
1967
+ .into(),
1968
+ }],
1969
+ where_expression: None,
1970
+ modifiers: Default::default(),
1971
+ }),
1972
+ ])],
1973
+ );
1974
+ }
1975
+
1976
+ #[test]
1977
+ fn test_oc_parse_delete_statement()
1978
+ {
1979
+ compare_parse_to_ast(
1980
+ "DELETE n, m",
1981
+ delete_statement,
1982
+ ast::Delete {
1983
+ detach: false,
1984
+ expressions: vec![
1985
+ ast::Variable {
1986
+ identifier: ast::VariableIdentifier {
1987
+ name: "n".to_string(),
1988
+ id: 0,
1989
+ },
1990
+ }
1991
+ .into(),
1992
+ ast::Variable {
1993
+ identifier: ast::VariableIdentifier {
1994
+ name: "m".to_string(),
1995
+ id: 1,
1996
+ },
1997
+ }
1998
+ .into(),
1999
+ ],
2000
+ },
2001
+ );
2002
+
2003
+ compare_parse_to_ast(
2004
+ "DETACH DELETE n, m",
2005
+ delete_statement,
2006
+ ast::Delete {
2007
+ detach: true,
2008
+ expressions: vec![
2009
+ ast::Variable {
2010
+ identifier: ast::VariableIdentifier {
2011
+ name: "n".to_string(),
2012
+ id: 0,
2013
+ },
2014
+ }
2015
+ .into(),
2016
+ ast::Variable {
2017
+ identifier: ast::VariableIdentifier {
2018
+ name: "m".to_string(),
2019
+ id: 1,
2020
+ },
2021
+ }
2022
+ .into(),
2023
+ ],
2024
+ },
2025
+ );
2026
+ }
2027
+
2028
+ #[test]
2029
+ fn test_oc_parse_set_statement()
2030
+ {
2031
+ compare_parse_to_ast(
2032
+ "SET n.name = 'Alice', n.age = 30",
2033
+ set_statement,
2034
+ ast::Update {
2035
+ updates: vec![
2036
+ ast::OneUpdate::SetProperty(ast::UpdateProperty {
2037
+ target: ast::VariableIdentifier {
2038
+ name: "n".into(),
2039
+ id: 0,
2040
+ },
2041
+ path: vec!["name".into()],
2042
+ expression: ast::Expression::Value(ast::Value {
2043
+ value: "Alice".into(),
2044
+ }),
2045
+ }),
2046
+ ast::OneUpdate::SetProperty(ast::UpdateProperty {
2047
+ target: ast::VariableIdentifier {
2048
+ name: "n".into(),
2049
+ id: 0,
2050
+ },
2051
+ path: vec!["age".into()],
2052
+ expression: ast::Expression::Value(ast::Value { value: 30.into() }),
2053
+ }),
2054
+ ],
2055
+ },
2056
+ );
2057
+
2058
+ compare_parse_to_ast(
2059
+ "SET a += {num: 42}",
2060
+ set_statement,
2061
+ ast::Update {
2062
+ updates: vec![ast::OneUpdate::AddProperty(ast::UpdateProperty {
2063
+ target: ast::VariableIdentifier {
2064
+ name: "a".into(),
2065
+ id: 0,
2066
+ },
2067
+ path: vec![],
2068
+ expression: ast::Expression::Map(ast::Map {
2069
+ map: vec![(
2070
+ "num".to_string(),
2071
+ ast::Expression::Value(ast::Value { value: 42.into() }),
2072
+ )],
2073
+ }),
2074
+ })],
2075
+ },
2076
+ );
2077
+ }
2078
+
2079
+ #[test]
2080
+ fn test_oc_parse_remove_statement()
2081
+ {
2082
+ compare_parse_to_ast(
2083
+ "REMOVE n:Person:Employee",
2084
+ remove_statement,
2085
+ ast::Update {
2086
+ updates: vec![ast::OneUpdate::RemoveLabels(ast::AddRemoveLabels {
2087
+ target: ast::VariableIdentifier {
2088
+ name: "n".to_string(),
2089
+ id: 0,
2090
+ },
2091
+ labels: vec!["Person".into(), "Employee".into()],
2092
+ })],
2093
+ },
2094
+ );
2095
+ }
2096
+
2097
+ #[test]
2098
+ fn test_oc_call_internal_stats()
2099
+ {
2100
+ compare_parse_to_ast(
2101
+ "CALL gqlite.internal.stats()",
2102
+ call_statement,
2103
+ ast::Call {
2104
+ name: "gqlite.internal.stats".to_string(),
2105
+ arguments: vec![],
2106
+ },
2107
+ );
2108
+ }
2109
+
2110
+ #[test]
2111
+ fn test_oc_parse_match_with_search_basic()
2112
+ {
2113
+ let actual = parse(
2114
+ "MATCH (n:Doc) SEARCH n IN (VECTOR INDEX idx FOR $q LIMIT 5)",
2115
+ match_statement,
2116
+ );
2117
+
2118
+ assert!(matches!(actual, ast::Statement::Match(ref m) if m.search.is_some()));
2119
+ if let ast::Statement::Match(m) = actual
2120
+ {
2121
+ let search = m.search.unwrap();
2122
+ assert_eq!(search.variable.name(), "n");
2123
+ assert_eq!(search.index, "idx");
2124
+ assert_eq!(search.limit, 5);
2125
+ assert!(matches!(search.query, ast::Expression::Parameter(_)));
2126
+ assert!(search.score_alias.is_none());
2127
+ }
2128
+ }
2129
+
2130
+ #[test]
2131
+ fn test_oc_parse_match_with_search_and_score()
2132
+ {
2133
+ let actual = parse(
2134
+ "MATCH (n:Doc) SEARCH n IN (VECTOR INDEX idx FOR $q LIMIT 10) SCORE AS score",
2135
+ match_statement,
2136
+ );
2137
+
2138
+ assert!(matches!(actual, ast::Statement::Match(ref m) if m.search.is_some()));
2139
+ if let ast::Statement::Match(m) = actual
2140
+ {
2141
+ let search = m.search.unwrap();
2142
+ assert_eq!(search.variable.name(), "n");
2143
+ assert_eq!(search.index, "idx");
2144
+ assert_eq!(search.limit, 10);
2145
+ assert!(search.score_alias.is_some());
2146
+ assert_eq!(search.score_alias.unwrap().name(), "score");
2147
+ }
2148
+ }
2149
+
2150
+ #[test]
2151
+ fn test_oc_parse_match_without_search()
2152
+ {
2153
+ let actual = parse("MATCH (n:Doc) WHERE n.id > 5", match_statement);
2154
+
2155
+ assert!(matches!(actual, ast::Statement::Match(ref m) if m.search.is_none()));
2156
+ }
2157
+
2158
+ #[test]
2159
+ fn test_oc_parse_match_with_search_and_where()
2160
+ {
2161
+ let actual = parse(
2162
+ "MATCH (n:Doc) WHERE n.active = true SEARCH n IN (VECTOR INDEX idx FOR $q LIMIT 5)",
2163
+ match_statement,
2164
+ );
2165
+
2166
+ assert!(
2167
+ matches!(actual, ast::Statement::Match(ref m) if m.search.is_some() && m.where_expression.is_some())
2168
+ );
2169
+ }
2170
+
2171
+ #[test]
2172
+ fn test_oc_parse_search_variable_not_in_pattern()
2173
+ {
2174
+ // This should fail because 'm' is not in the MATCH pattern
2175
+ let query = "MATCH (n:Doc) SEARCH m IN (VECTOR INDEX idx FOR $q LIMIT 5)";
2176
+ let ctx = Context::new(query);
2177
+ let tokens = lexer::tokenize(query).expect("tokenization failed");
2178
+ let input = Input(&tokens, &ctx);
2179
+
2180
+ let result = match_statement(input);
2181
+ assert!(result.is_err());
2182
+ }
2183
+
2184
+ #[test]
2185
+ fn test_oc_parse_create_vector_index_basic()
2186
+ {
2187
+ compare_parse_to_ast(
2188
+ "CREATE VECTOR INDEX doc_idx ON :Doc (embedding)",
2189
+ statement,
2190
+ ast::CreateVectorIndex {
2191
+ name: "doc_idx".to_string(),
2192
+ label: "Doc".to_string(),
2193
+ property: "embedding".to_string(),
2194
+ dimension: None,
2195
+ metric: None,
2196
+ if_not_exists: false,
2197
+ },
2198
+ );
2199
+ }
2200
+
2201
+ #[test]
2202
+ fn test_oc_parse_create_vector_index_with_options()
2203
+ {
2204
+ compare_parse_to_ast(
2205
+ "CREATE VECTOR INDEX idx ON :Node (vec) OPTIONS { dimension: 384, metric: \"cosine\" }",
2206
+ statement,
2207
+ ast::CreateVectorIndex {
2208
+ name: "idx".to_string(),
2209
+ label: "Node".to_string(),
2210
+ property: "vec".to_string(),
2211
+ dimension: Some(384),
2212
+ metric: Some(ast::Metric::Cosine),
2213
+ if_not_exists: false,
2214
+ },
2215
+ );
2216
+ }
2217
+
2218
+ #[test]
2219
+ fn test_oc_parse_create_vector_index_l2_metric()
2220
+ {
2221
+ compare_parse_to_ast(
2222
+ "CREATE VECTOR INDEX emb_idx ON :Entity (embedding) OPTIONS { dimension: 768, metric: \"l2\" }",
2223
+ statement,
2224
+ ast::CreateVectorIndex {
2225
+ name: "emb_idx".to_string(),
2226
+ label: "Entity".to_string(),
2227
+ property: "embedding".to_string(),
2228
+ dimension: Some(768),
2229
+ metric: Some(ast::Metric::L2),
2230
+ if_not_exists: false,
2231
+ },
2232
+ );
2233
+ }
2234
+
2235
+ #[test]
2236
+ fn test_oc_parse_create_vector_index_dotproduct_metric()
2237
+ {
2238
+ compare_parse_to_ast(
2239
+ "CREATE VECTOR INDEX dot_idx ON :Data (vector) OPTIONS { metric: \"dot_product\" }",
2240
+ statement,
2241
+ ast::CreateVectorIndex {
2242
+ name: "dot_idx".to_string(),
2243
+ label: "Data".to_string(),
2244
+ property: "vector".to_string(),
2245
+ dimension: None,
2246
+ metric: Some(ast::Metric::DotProduct),
2247
+ if_not_exists: false,
2248
+ },
2249
+ );
2250
+ }
2251
+
2252
+ #[test]
2253
+ fn test_oc_parse_create_vector_index_dimension_only()
2254
+ {
2255
+ compare_parse_to_ast(
2256
+ "CREATE VECTOR INDEX idx ON :Doc (emb) OPTIONS { dimension: 512 }",
2257
+ statement,
2258
+ ast::CreateVectorIndex {
2259
+ name: "idx".to_string(),
2260
+ label: "Doc".to_string(),
2261
+ property: "emb".to_string(),
2262
+ dimension: Some(512),
2263
+ metric: None,
2264
+ if_not_exists: false,
2265
+ },
2266
+ );
2267
+ }
2268
+
2269
+ #[test]
2270
+ fn test_oc_parse_create_vector_index_if_not_exists()
2271
+ {
2272
+ compare_parse_to_ast(
2273
+ "CREATE VECTOR INDEX doc_idx IF NOT EXISTS ON :Doc (embedding)",
2274
+ statement,
2275
+ ast::CreateVectorIndex {
2276
+ name: "doc_idx".to_string(),
2277
+ label: "Doc".to_string(),
2278
+ property: "embedding".to_string(),
2279
+ dimension: None,
2280
+ metric: None,
2281
+ if_not_exists: true,
2282
+ },
2283
+ );
2284
+ }