gqlite 1.8.0 → 1.8.1
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/db-index/Cargo.toml +2 -2
- data/ext/gqlitedb/src/aggregators.rs +28 -0
- data/ext/gqlitedb/src/compiler/expression_analyser.rs +33 -0
- data/ext/gqlitedb/src/compiler/variables_manager.rs +2 -1
- data/ext/gqlitedb/src/compiler.rs +25 -1
- data/ext/gqlitedb/src/connection.rs +2 -2
- data/ext/gqlitedb/src/consts.rs +1 -1
- data/ext/gqlitedb/src/error.rs +7 -0
- data/ext/gqlitedb/src/interpreter/evaluators.rs +62 -6
- data/ext/gqlitedb/src/interpreter/instructions.rs +5 -0
- data/ext/gqlitedb/src/planner.rs +13 -0
- data/ext/gqlitedb/src/store/redb.rs +1 -1
- data/ext/gqlitedb/src/tests/compiler.rs +1 -0
- data/ext/gqlitedb/src/tests/connection/postgres.rs +22 -0
- data/ext/gqlitedb/src/tests/connection/redb.rs +16 -0
- data/ext/gqlitedb/src/tests/connection/sqlite.rs +16 -0
- data/ext/gqlitedb/src/tests/connection.rs +64 -0
- data/ext/gqlitedb/src/tests/planner.rs +1 -0
- data/ext/gqlitedb/src/tests/templates/ast.rs +10 -0
- data/ext/gqlitedb/src/tests/templates/programs.rs +12 -0
- data/ext/gqlparser/src/oc/ast.rs +20 -0
- data/ext/gqlparser/src/oc/lexer.rs +11 -2
- data/ext/gqlparser/src/oc/parser/tests.rs +370 -9
- data/ext/gqlparser/src/oc/parser.rs +116 -39
- data/ext/gqlparser/src/oc/string.rs +172 -0
- data/ext/gqlparser/src/oc.rs +1 -0
- data/ext/graphcore/src/value/value_map.rs +2 -2
- data/ext/graphcore/src/value.rs +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4af74e4cc44046fee49a1e9573a876066a80ddc6eaaf2def11f5af0002b89ff2
|
|
4
|
+
data.tar.gz: c105b94002e5c43a74e8f7b418ba45d8d17c47bab1a68dc32c914e18c3a3e1cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 885b3a13b70473a45cdad51df3fe04f1aebd98828a6211445bf1907c44f88ff543e78ffbf86b5afa971f76482e0b190ed76a7212cbf8b520a20b6e5505a58081
|
|
7
|
+
data.tar.gz: d784d78413e35a708b204a557ef0e1d54236adae193679205b522d788529277c2f183824c87e57946733dd68e36013aea2e0cca6f42fe774d92ce6eccb8d4aed
|
data/ext/db-index/Cargo.toml
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
name = "db-index"
|
|
3
3
|
description = "Reusable indexing algorithms for key-value storage engines"
|
|
4
4
|
readme = "README.md"
|
|
5
|
-
license =
|
|
5
|
+
license.workspace = true
|
|
6
6
|
homepage.workspace = true
|
|
7
7
|
repository.workspace = true
|
|
8
|
-
version =
|
|
8
|
+
version.workspace = true
|
|
9
9
|
edition = "2021"
|
|
10
10
|
|
|
11
11
|
[dependencies]
|
|
@@ -22,6 +22,34 @@ pub(crate) trait AggregatorTrait: Debug + Sync + Send
|
|
|
22
22
|
|
|
23
23
|
pub(crate) type Aggregator = Arc<Box<dyn AggregatorTrait>>;
|
|
24
24
|
|
|
25
|
+
#[derive(Debug)]
|
|
26
|
+
pub(crate) struct DistinctAggregatorState
|
|
27
|
+
{
|
|
28
|
+
pub(crate) inner: Box<dyn AggregatorState>,
|
|
29
|
+
pub(crate) seen: Vec<value::Value>,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl AggregatorState for DistinctAggregatorState
|
|
33
|
+
{
|
|
34
|
+
fn next(&mut self, expression: value::Value) -> Result<()>
|
|
35
|
+
{
|
|
36
|
+
if self.seen.iter().any(|v| v == &expression)
|
|
37
|
+
{
|
|
38
|
+
Ok(())
|
|
39
|
+
}
|
|
40
|
+
else
|
|
41
|
+
{
|
|
42
|
+
self.seen.push(expression.clone());
|
|
43
|
+
self.inner.next(expression)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fn finalise(self: Box<Self>) -> Result<value::Value>
|
|
48
|
+
{
|
|
49
|
+
self.inner.finalise()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
25
53
|
macro_rules! declare_aggregator {
|
|
26
54
|
($function_name: ident, $type_name: ident, $state_type_name: tt, ( $( $arg_type: ty $(,)? )* ) -> $ret_type: ty) => {
|
|
27
55
|
#[derive(Debug)]
|
|
@@ -256,6 +256,15 @@ impl<'b> Analyser<'b>
|
|
|
256
256
|
)),
|
|
257
257
|
ast::Expression::FunctionCall(call) =>
|
|
258
258
|
{
|
|
259
|
+
if call.distinct && !self.functions_manager.is_aggregate(&call.name)?
|
|
260
|
+
{
|
|
261
|
+
return Err(
|
|
262
|
+
error::CompileTimeError::DistinctOnNonAggregateFunction {
|
|
263
|
+
name: call.name.clone(),
|
|
264
|
+
}
|
|
265
|
+
.into(),
|
|
266
|
+
);
|
|
267
|
+
}
|
|
259
268
|
let arguments = (call.arguments.iter(), validators::any).analyse(self)?;
|
|
260
269
|
Ok(ExpressionInfo::new(
|
|
261
270
|
self.functions_manager.validate_arguments(
|
|
@@ -438,6 +447,30 @@ impl<'b> Analyser<'b>
|
|
|
438
447
|
false,
|
|
439
448
|
false,
|
|
440
449
|
)),
|
|
450
|
+
ast::Expression::Contains(contains) => Ok(ExpressionInfo::new_type(
|
|
451
|
+
ExpressionType::Boolean,
|
|
452
|
+
(
|
|
453
|
+
[&contains.left, &contains.right].into_iter(),
|
|
454
|
+
validators::string_or_null,
|
|
455
|
+
)
|
|
456
|
+
.analyse(self)?,
|
|
457
|
+
)),
|
|
458
|
+
ast::Expression::StartsWith(starts_with) => Ok(ExpressionInfo::new_type(
|
|
459
|
+
ExpressionType::Boolean,
|
|
460
|
+
(
|
|
461
|
+
[&starts_with.left, &starts_with.right].into_iter(),
|
|
462
|
+
validators::string_or_null,
|
|
463
|
+
)
|
|
464
|
+
.analyse(self)?,
|
|
465
|
+
)),
|
|
466
|
+
ast::Expression::EndsWith(ends_with) => Ok(ExpressionInfo::new_type(
|
|
467
|
+
ExpressionType::Boolean,
|
|
468
|
+
(
|
|
469
|
+
[&ends_with.left, &ends_with.right].into_iter(),
|
|
470
|
+
validators::string_or_null,
|
|
471
|
+
)
|
|
472
|
+
.analyse(self)?,
|
|
473
|
+
)),
|
|
441
474
|
}
|
|
442
475
|
}
|
|
443
476
|
}
|
|
@@ -596,7 +596,8 @@ impl VariablesManager
|
|
|
596
596
|
LogicalPlan::Filter { source, .. }
|
|
597
597
|
| LogicalPlan::Sort { source, .. }
|
|
598
598
|
| LogicalPlan::Skip { source, .. }
|
|
599
|
-
| LogicalPlan::Limit { source, .. }
|
|
599
|
+
| LogicalPlan::Limit { source, .. }
|
|
600
|
+
| LogicalPlan::Distinct { source } => self.analyse(source.as_ref())?,
|
|
600
601
|
LogicalPlan::Projection { .. } =>
|
|
601
602
|
{}
|
|
602
603
|
LogicalPlan::Call { .. } =>
|
|
@@ -132,6 +132,7 @@ impl Compiler
|
|
|
132
132
|
init_instructions,
|
|
133
133
|
aggregator,
|
|
134
134
|
argument_instructions,
|
|
135
|
+
distinct: function_call.distinct,
|
|
135
136
|
},
|
|
136
137
|
));
|
|
137
138
|
Instruction::GetVariable { col_id: var_col_id }
|
|
@@ -317,6 +318,21 @@ impl Compiler
|
|
|
317
318
|
instructions.push(Instruction::IsNullUnaryOperator);
|
|
318
319
|
Instruction::NotUnaryOperator
|
|
319
320
|
}
|
|
321
|
+
ast::Expression::Contains(contains) =>
|
|
322
|
+
{
|
|
323
|
+
compile_binary_op!(self, contains, instructions, aggregations);
|
|
324
|
+
Instruction::StringContainsOperator
|
|
325
|
+
}
|
|
326
|
+
ast::Expression::StartsWith(starts_with) =>
|
|
327
|
+
{
|
|
328
|
+
compile_binary_op!(self, starts_with, instructions, aggregations);
|
|
329
|
+
Instruction::StringStartsWithOperator
|
|
330
|
+
}
|
|
331
|
+
ast::Expression::EndsWith(ends_with) =>
|
|
332
|
+
{
|
|
333
|
+
compile_binary_op!(self, ends_with, instructions, aggregations);
|
|
334
|
+
Instruction::StringEndsWithOperator
|
|
335
|
+
}
|
|
320
336
|
};
|
|
321
337
|
instructions.push(expr);
|
|
322
338
|
Ok(())
|
|
@@ -980,6 +996,7 @@ impl Compiler
|
|
|
980
996
|
},
|
|
981
997
|
)?;
|
|
982
998
|
Ok(instructions::Modifiers {
|
|
999
|
+
distinct: modifiers.distinct,
|
|
983
1000
|
limit,
|
|
984
1001
|
skip,
|
|
985
1002
|
order_by,
|
|
@@ -1100,6 +1117,12 @@ impl Compiler
|
|
|
1100
1117
|
projection.modifiers.limit = Some(expression);
|
|
1101
1118
|
Ok(projection)
|
|
1102
1119
|
}
|
|
1120
|
+
LogicalPlan::Distinct { source } =>
|
|
1121
|
+
{
|
|
1122
|
+
let mut projection = Self::unpack_projection_plan(*source)?;
|
|
1123
|
+
projection.modifiers.distinct = true;
|
|
1124
|
+
Ok(projection)
|
|
1125
|
+
}
|
|
1103
1126
|
_ => Err(
|
|
1104
1127
|
InternalError::Unreachable {
|
|
1105
1128
|
context: "compile/unpack_projection_plan",
|
|
@@ -1239,7 +1262,8 @@ pub(crate) fn compile(
|
|
|
1239
1262
|
| LogicalPlan::Sort { .. }
|
|
1240
1263
|
| LogicalPlan::Skip { .. }
|
|
1241
1264
|
| LogicalPlan::Limit { .. }
|
|
1242
|
-
| LogicalPlan::Filter { .. }
|
|
1265
|
+
| LogicalPlan::Filter { .. }
|
|
1266
|
+
| LogicalPlan::Distinct { .. } => compiler.compile_projection_plan(logical_plan),
|
|
1243
1267
|
LogicalPlan::Call { name, arguments } =>
|
|
1244
1268
|
{
|
|
1245
1269
|
let mut instructions = Instructions::new();
|
|
@@ -32,8 +32,8 @@ impl ConnectionBuilder
|
|
|
32
32
|
/// Merge options. This might overwrite value from the builder
|
|
33
33
|
pub fn options(mut self, options: impl Into<value::ValueMap>) -> Self
|
|
34
34
|
{
|
|
35
|
-
let
|
|
36
|
-
self.map.extend(options
|
|
35
|
+
let options = options.into();
|
|
36
|
+
self.map.extend(options);
|
|
37
37
|
self
|
|
38
38
|
}
|
|
39
39
|
/// Set the option value for the given key.
|
data/ext/gqlitedb/src/consts.rs
CHANGED
data/ext/gqlitedb/src/error.rs
CHANGED
|
@@ -78,6 +78,13 @@ pub enum CompileTimeError
|
|
|
78
78
|
{
|
|
79
79
|
name: String
|
|
80
80
|
},
|
|
81
|
+
#[error(
|
|
82
|
+
"DistinctOnNonAggregateFunction: DISTINCT can only be used with aggregate functions, got '{name}'."
|
|
83
|
+
)]
|
|
84
|
+
DistinctOnNonAggregateFunction
|
|
85
|
+
{
|
|
86
|
+
name: String
|
|
87
|
+
},
|
|
81
88
|
#[error("InvalidAggregation: aggregation is not accepted in this expression.")]
|
|
82
89
|
InvalidAggregation,
|
|
83
90
|
#[error("ColumnNameConflict: Column '{name}' is duplicated.")]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
1
|
+
use std::collections::{HashMap, HashSet};
|
|
2
2
|
|
|
3
3
|
use rustc_hash::FxHashSet;
|
|
4
4
|
|
|
@@ -333,6 +333,27 @@ fn execute_binary_operator<T: Into<crate::value::Value>>(
|
|
|
333
333
|
Ok(())
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
+
fn execute_string_operator(
|
|
337
|
+
stack: &mut Stack,
|
|
338
|
+
operand: impl FnOnce(String, String) -> bool,
|
|
339
|
+
) -> Result<()>
|
|
340
|
+
{
|
|
341
|
+
let a: Value = stack.try_pop()?;
|
|
342
|
+
let b: Value = stack.try_pop()?;
|
|
343
|
+
|
|
344
|
+
let a: graphcore::Value = a.try_into()?;
|
|
345
|
+
let b: graphcore::Value = b.try_into()?;
|
|
346
|
+
|
|
347
|
+
let a = a.try_into();
|
|
348
|
+
let b = b.try_into();
|
|
349
|
+
match (a, b)
|
|
350
|
+
{
|
|
351
|
+
(Ok(a), Ok(b)) => stack.push(operand(a, b)),
|
|
352
|
+
_ => stack.push(graphcore::Value::Null),
|
|
353
|
+
}
|
|
354
|
+
Ok(())
|
|
355
|
+
}
|
|
356
|
+
|
|
336
357
|
fn eval_instructions(
|
|
337
358
|
stack: &mut Stack,
|
|
338
359
|
row: &impl value_table::RowInterface,
|
|
@@ -807,6 +828,18 @@ fn eval_instructions(
|
|
|
807
828
|
{
|
|
808
829
|
execute_binary_operator(stack, |a, b| a.pow(b))?;
|
|
809
830
|
}
|
|
831
|
+
&instructions::Instruction::StringContainsOperator =>
|
|
832
|
+
{
|
|
833
|
+
execute_string_operator(stack, |a, b| a.contains(&b))?;
|
|
834
|
+
}
|
|
835
|
+
&instructions::Instruction::StringStartsWithOperator =>
|
|
836
|
+
{
|
|
837
|
+
execute_string_operator(stack, |a, b| a.starts_with(&b))?;
|
|
838
|
+
}
|
|
839
|
+
&instructions::Instruction::StringEndsWithOperator =>
|
|
840
|
+
{
|
|
841
|
+
execute_string_operator(stack, |a, b| a.ends_with(&b))?;
|
|
842
|
+
}
|
|
810
843
|
}
|
|
811
844
|
}
|
|
812
845
|
Ok(())
|
|
@@ -921,13 +954,20 @@ fn create_aggregations_states(
|
|
|
921
954
|
&agg.init_instructions,
|
|
922
955
|
parameters,
|
|
923
956
|
)?;
|
|
924
|
-
let state = agg.aggregator.create(
|
|
957
|
+
let mut state = agg.aggregator.create(
|
|
925
958
|
stack
|
|
926
959
|
.into_vec()
|
|
927
960
|
.into_iter()
|
|
928
961
|
.map(|v| v.try_into())
|
|
929
962
|
.collect::<Result<_>>()?,
|
|
930
963
|
)?;
|
|
964
|
+
if agg.distinct
|
|
965
|
+
{
|
|
966
|
+
state = Box::new(aggregators::DistinctAggregatorState {
|
|
967
|
+
inner: state,
|
|
968
|
+
seen: Vec::new(),
|
|
969
|
+
});
|
|
970
|
+
}
|
|
931
971
|
|
|
932
972
|
Ok((name.to_owned(), state))
|
|
933
973
|
})
|
|
@@ -1059,7 +1099,6 @@ fn compute_return_with_table(
|
|
|
1059
1099
|
{
|
|
1060
1100
|
output_table = filter_rows(output_table.into_row_iter(), filter, parameters)?.try_into()?;
|
|
1061
1101
|
}
|
|
1062
|
-
// Apply modifiers
|
|
1063
1102
|
// Sort the table according to order_by
|
|
1064
1103
|
if !modifiers.order_by.is_empty()
|
|
1065
1104
|
{
|
|
@@ -1125,7 +1164,7 @@ fn compute_return_with_table(
|
|
|
1125
1164
|
}
|
|
1126
1165
|
}
|
|
1127
1166
|
|
|
1128
|
-
output_table
|
|
1167
|
+
let output_table_iter = output_table
|
|
1129
1168
|
.into_row_iter()
|
|
1130
1169
|
.map(|mut row| {
|
|
1131
1170
|
Ok(Row::new(
|
|
@@ -1136,8 +1175,25 @@ fn compute_return_with_table(
|
|
|
1136
1175
|
0,
|
|
1137
1176
|
))
|
|
1138
1177
|
})
|
|
1139
|
-
.map(value_table::RowResult)
|
|
1140
|
-
|
|
1178
|
+
.map(value_table::RowResult);
|
|
1179
|
+
|
|
1180
|
+
// Apply modifiers
|
|
1181
|
+
if modifiers.distinct
|
|
1182
|
+
{
|
|
1183
|
+
let mut seen = HashSet::new();
|
|
1184
|
+
|
|
1185
|
+
output_table_iter
|
|
1186
|
+
.filter(|row| match &row.0
|
|
1187
|
+
{
|
|
1188
|
+
Ok(row) => seen.insert(RowKey(row.clone())),
|
|
1189
|
+
Err(_) => true,
|
|
1190
|
+
})
|
|
1191
|
+
.collect()
|
|
1192
|
+
}
|
|
1193
|
+
else
|
|
1194
|
+
{
|
|
1195
|
+
output_table_iter.collect()
|
|
1196
|
+
}
|
|
1141
1197
|
}
|
|
1142
1198
|
|
|
1143
1199
|
fn filter_rows(
|
|
@@ -79,6 +79,9 @@ pub(crate) enum Instruction
|
|
|
79
79
|
DivisionBinaryOperator,
|
|
80
80
|
ModuloBinaryOperator,
|
|
81
81
|
ExponentBinaryOperator,
|
|
82
|
+
StringContainsOperator,
|
|
83
|
+
StringStartsWithOperator,
|
|
84
|
+
StringEndsWithOperator,
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
pub(crate) type Instructions = Vec<Instruction>;
|
|
@@ -127,6 +130,7 @@ pub(crate) struct RWAggregation
|
|
|
127
130
|
pub(crate) init_instructions: Instructions,
|
|
128
131
|
pub(crate) argument_instructions: Instructions,
|
|
129
132
|
pub(crate) aggregator: aggregators::Aggregator,
|
|
133
|
+
pub(crate) distinct: bool,
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
/// R(eturn)W(ith)Expression are expressions computed in Return or With blocks.
|
|
@@ -189,6 +193,7 @@ pub(crate) struct OrderBy
|
|
|
189
193
|
#[derive(Debug)]
|
|
190
194
|
pub(crate) struct Modifiers
|
|
191
195
|
{
|
|
196
|
+
pub distinct: bool,
|
|
192
197
|
pub limit: Option<Instructions>,
|
|
193
198
|
pub skip: Option<Instructions>,
|
|
194
199
|
pub order_by: Vec<OrderBy>,
|
data/ext/gqlitedb/src/planner.rs
CHANGED
|
@@ -89,6 +89,12 @@ pub enum LogicalPlan
|
|
|
89
89
|
/// Limit expression.
|
|
90
90
|
expression: ast::Expression,
|
|
91
91
|
},
|
|
92
|
+
/// Make sure values only appear onces.
|
|
93
|
+
Distinct
|
|
94
|
+
{
|
|
95
|
+
/// Input operator.
|
|
96
|
+
source: Box<LogicalPlan>,
|
|
97
|
+
},
|
|
92
98
|
/// Invoke a callable.
|
|
93
99
|
Call
|
|
94
100
|
{
|
|
@@ -211,6 +217,13 @@ fn wrap_modifiers(plan: LogicalPlan, modifiers: ast::Modifiers) -> LogicalPlan
|
|
|
211
217
|
};
|
|
212
218
|
}
|
|
213
219
|
|
|
220
|
+
if modifiers.distinct
|
|
221
|
+
{
|
|
222
|
+
plan = LogicalPlan::Distinct {
|
|
223
|
+
source: Box::new(plan),
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
214
227
|
plan
|
|
215
228
|
}
|
|
216
229
|
|
|
@@ -570,7 +570,7 @@ impl Store
|
|
|
570
570
|
/// database forward exactly one step; the function recurses until `from` matches current.
|
|
571
571
|
fn upgrade_database(&self, from: utils::Version, tx: &mut TransactionBox) -> Result<()>
|
|
572
572
|
{
|
|
573
|
-
if from == consts::GQLITE_VERSION
|
|
573
|
+
if from.major == consts::GQLITE_VERSION.major && from.minor == consts::GQLITE_VERSION.minor
|
|
574
574
|
{
|
|
575
575
|
return Ok(());
|
|
576
576
|
}
|
|
@@ -41,3 +41,25 @@ fn test_vector_search_dimension_mismatch()
|
|
|
41
41
|
let connection = builder.create().unwrap();
|
|
42
42
|
super::test_vector_search_dimension_mismatch(connection);
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
#[test]
|
|
46
|
+
fn test_not_wraps_comparison_at_runtime()
|
|
47
|
+
{
|
|
48
|
+
let db = crate::tests::postgres::create_tmp_db(14);
|
|
49
|
+
let builder = crate::Connection::builder()
|
|
50
|
+
.set_option("url", db.connection_uri())
|
|
51
|
+
.backend(crate::Backend::Postgres);
|
|
52
|
+
let connection = builder.create().unwrap();
|
|
53
|
+
super::test_not_wraps_comparison_at_runtime(connection);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[test]
|
|
57
|
+
fn test_count_distinct_end_to_end()
|
|
58
|
+
{
|
|
59
|
+
let db = crate::tests::postgres::create_tmp_db(15);
|
|
60
|
+
let builder = crate::Connection::builder()
|
|
61
|
+
.set_option("url", db.connection_uri())
|
|
62
|
+
.backend(crate::Backend::Postgres);
|
|
63
|
+
let connection = builder.create().unwrap();
|
|
64
|
+
super::test_count_distinct_end_to_end(connection);
|
|
65
|
+
}
|
|
@@ -21,3 +21,19 @@ fn test_create_edge_return()
|
|
|
21
21
|
let connection = builder.create().unwrap();
|
|
22
22
|
super::test_create_edge_return(connection);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
#[test]
|
|
26
|
+
fn test_not_wraps_comparison_at_runtime()
|
|
27
|
+
{
|
|
28
|
+
let builder = crate::Connection::builder().backend(crate::Backend::Redb);
|
|
29
|
+
let connection = builder.create().unwrap();
|
|
30
|
+
super::test_not_wraps_comparison_at_runtime(connection);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[test]
|
|
34
|
+
fn test_count_distinct_end_to_end()
|
|
35
|
+
{
|
|
36
|
+
let builder = crate::Connection::builder().backend(crate::Backend::Redb);
|
|
37
|
+
let connection = builder.create().unwrap();
|
|
38
|
+
super::test_count_distinct_end_to_end(connection);
|
|
39
|
+
}
|
|
@@ -29,3 +29,19 @@ fn test_vector_search_dimension_mismatch()
|
|
|
29
29
|
let connection = builder.create().unwrap();
|
|
30
30
|
super::test_vector_search_dimension_mismatch(connection);
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
#[test]
|
|
34
|
+
fn test_not_wraps_comparison_at_runtime()
|
|
35
|
+
{
|
|
36
|
+
let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
|
|
37
|
+
let connection = builder.create().unwrap();
|
|
38
|
+
super::test_not_wraps_comparison_at_runtime(connection);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#[test]
|
|
42
|
+
fn test_count_distinct_end_to_end()
|
|
43
|
+
{
|
|
44
|
+
let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
|
|
45
|
+
let connection = builder.create().unwrap();
|
|
46
|
+
super::test_count_distinct_end_to_end(connection);
|
|
47
|
+
}
|
|
@@ -77,3 +77,67 @@ fn test_vector_search_dimension_mismatch(connection: crate::Connection)
|
|
|
77
77
|
);
|
|
78
78
|
assert!(result.is_err(), "dimension mismatch should raise an error");
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
fn test_not_wraps_comparison_at_runtime(connection: crate::Connection)
|
|
82
|
+
{
|
|
83
|
+
connection
|
|
84
|
+
.execute_oc_query("CREATE (:Person {name: \"Alice\"})", Default::default())
|
|
85
|
+
.unwrap();
|
|
86
|
+
connection
|
|
87
|
+
.execute_oc_query("CREATE (:Person {name: \"Bob\"})", Default::default())
|
|
88
|
+
.unwrap();
|
|
89
|
+
|
|
90
|
+
let result = connection
|
|
91
|
+
.execute_oc_query(
|
|
92
|
+
r#"MATCH (p:Person) WHERE NOT p.name = "Alice" RETURN count(p) AS n"#,
|
|
93
|
+
Default::default(),
|
|
94
|
+
)
|
|
95
|
+
.unwrap();
|
|
96
|
+
let table = result.try_into_table().unwrap();
|
|
97
|
+
assert_eq!(table.rows(), 1);
|
|
98
|
+
let count: i64 = table.value(0, 0).unwrap().try_into().unwrap();
|
|
99
|
+
assert_eq!(count, 1, "only Bob should match (NOT Alice)");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fn test_count_distinct_end_to_end(connection: crate::Connection)
|
|
103
|
+
{
|
|
104
|
+
connection
|
|
105
|
+
.execute_oc_query("CREATE (:Person {id: 1})", Default::default())
|
|
106
|
+
.unwrap();
|
|
107
|
+
connection
|
|
108
|
+
.execute_oc_query("CREATE (:Person {id: 2})", Default::default())
|
|
109
|
+
.unwrap();
|
|
110
|
+
connection
|
|
111
|
+
.execute_oc_query("CREATE (:Person {id: 3})", Default::default())
|
|
112
|
+
.unwrap();
|
|
113
|
+
|
|
114
|
+
connection
|
|
115
|
+
.execute_oc_query(
|
|
116
|
+
"MATCH (p:Person {id: 1}), (f:Person) CREATE (p)-[:KNOWS]->(f)",
|
|
117
|
+
Default::default(),
|
|
118
|
+
)
|
|
119
|
+
.unwrap();
|
|
120
|
+
connection
|
|
121
|
+
.execute_oc_query(
|
|
122
|
+
"MATCH (p:Person {id: 1}), (f:Person {id: 2}) CREATE (p)-[:KNOWS]->(f)",
|
|
123
|
+
Default::default(),
|
|
124
|
+
)
|
|
125
|
+
.unwrap();
|
|
126
|
+
connection
|
|
127
|
+
.execute_oc_query(
|
|
128
|
+
"MATCH (p:Person {id: 1}), (f:Person {id: 3}) CREATE (p)-[:KNOWS]->(f)",
|
|
129
|
+
Default::default(),
|
|
130
|
+
)
|
|
131
|
+
.unwrap();
|
|
132
|
+
|
|
133
|
+
let result = connection
|
|
134
|
+
.execute_oc_query(
|
|
135
|
+
"MATCH (p:Person {id: 1})-[:KNOWS]->(friend:Person) RETURN count(DISTINCT friend) AS n",
|
|
136
|
+
Default::default(),
|
|
137
|
+
)
|
|
138
|
+
.unwrap();
|
|
139
|
+
let table = result.try_into_table().unwrap();
|
|
140
|
+
assert_eq!(table.rows(), 1);
|
|
141
|
+
let count: i64 = table.value(0, 0).unwrap().try_into().unwrap();
|
|
142
|
+
assert_eq!(count, 3, "should count distinct friends");
|
|
143
|
+
}
|
|
@@ -9,6 +9,7 @@ fn return_statement(var_id: VariableIdentifier) -> Statement
|
|
|
9
9
|
expression: Variable { identifier: var_id }.into(),
|
|
10
10
|
}],
|
|
11
11
|
modifiers: Modifiers {
|
|
12
|
+
distinct: false,
|
|
12
13
|
skip: None,
|
|
13
14
|
limit: None,
|
|
14
15
|
order_by: None,
|
|
@@ -70,6 +71,7 @@ pub(crate) fn create_named_node() -> Statements
|
|
|
70
71
|
.into(),
|
|
71
72
|
}],
|
|
72
73
|
modifiers: Modifiers {
|
|
74
|
+
distinct: false,
|
|
73
75
|
skip: None,
|
|
74
76
|
limit: None,
|
|
75
77
|
order_by: None,
|
|
@@ -134,6 +136,7 @@ pub(crate) fn create_named_node_double_return() -> Statements
|
|
|
134
136
|
},
|
|
135
137
|
],
|
|
136
138
|
modifiers: Modifiers {
|
|
139
|
+
distinct: false,
|
|
137
140
|
skip: None,
|
|
138
141
|
limit: None,
|
|
139
142
|
order_by: None,
|
|
@@ -162,6 +165,7 @@ pub(crate) fn double_with_return() -> Statements
|
|
|
162
165
|
},
|
|
163
166
|
],
|
|
164
167
|
modifiers: Modifiers {
|
|
168
|
+
distinct: false,
|
|
165
169
|
skip: None,
|
|
166
170
|
limit: None,
|
|
167
171
|
order_by: None,
|
|
@@ -188,6 +192,7 @@ pub(crate) fn double_with_return() -> Statements
|
|
|
188
192
|
},
|
|
189
193
|
],
|
|
190
194
|
modifiers: Modifiers {
|
|
195
|
+
distinct: false,
|
|
191
196
|
skip: None,
|
|
192
197
|
limit: None,
|
|
193
198
|
order_by: None,
|
|
@@ -291,10 +296,12 @@ pub(crate) fn match_count() -> Statements
|
|
|
291
296
|
expression: FunctionCall {
|
|
292
297
|
name: "count".into(),
|
|
293
298
|
arguments: vec![Value { value: 0.into() }.into()],
|
|
299
|
+
distinct: false,
|
|
294
300
|
}
|
|
295
301
|
.into(),
|
|
296
302
|
}],
|
|
297
303
|
modifiers: Modifiers {
|
|
304
|
+
distinct: false,
|
|
298
305
|
skip: None,
|
|
299
306
|
limit: None,
|
|
300
307
|
order_by: None,
|
|
@@ -349,11 +356,13 @@ pub(crate) fn aggregation() -> Statements
|
|
|
349
356
|
}
|
|
350
357
|
.into(),
|
|
351
358
|
],
|
|
359
|
+
distinct: false,
|
|
352
360
|
}
|
|
353
361
|
.into(),
|
|
354
362
|
},
|
|
355
363
|
],
|
|
356
364
|
modifiers: Modifiers {
|
|
365
|
+
distinct: false,
|
|
357
366
|
skip: None,
|
|
358
367
|
limit: None,
|
|
359
368
|
order_by: None,
|
|
@@ -421,6 +430,7 @@ pub(crate) fn match_with_vector_search(index_name: &str) -> Statements
|
|
|
421
430
|
.into(),
|
|
422
431
|
}],
|
|
423
432
|
modifiers: Modifiers {
|
|
433
|
+
distinct: false,
|
|
424
434
|
skip: None,
|
|
425
435
|
limit: None,
|
|
426
436
|
order_by: None,
|