gqlite 1.8.0 → 1.8.2
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 +8 -8
- data/ext/db-index/Cargo.toml +2 -2
- data/ext/gqlitedb/Cargo.toml +0 -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 +111 -11
- data/ext/gqlitedb/src/compiler.rs +506 -48
- data/ext/gqlitedb/src/connection.rs +14 -2
- data/ext/gqlitedb/src/consts.rs +1 -1
- data/ext/gqlitedb/src/error.rs +31 -0
- data/ext/gqlitedb/src/interpreter/evaluators.rs +153 -331
- data/ext/gqlitedb/src/interpreter/instructions.rs +77 -0
- data/ext/gqlitedb/src/interpreter/mod.rs +1 -0
- data/ext/gqlitedb/src/interpreter/path/bfs.rs +494 -0
- data/ext/gqlitedb/src/interpreter/path/dfs.rs +267 -0
- data/ext/gqlitedb/src/interpreter/path/group.rs +418 -0
- data/ext/gqlitedb/src/interpreter/path.rs +542 -0
- data/ext/gqlitedb/src/planner.rs +231 -6
- data/ext/gqlitedb/src/query_result.rs +19 -4
- data/ext/gqlitedb/src/store/redb.rs +1 -1
- data/ext/gqlitedb/src/tests/compiler.rs +2454 -1
- data/ext/gqlitedb/src/tests/connection/postgres.rs +22 -0
- data/ext/gqlitedb/src/tests/connection/redb.rs +24 -0
- data/ext/gqlitedb/src/tests/connection/sqlite.rs +16 -0
- data/ext/gqlitedb/src/tests/connection.rs +101 -0
- data/ext/gqlitedb/src/tests/evaluators.rs +2206 -1
- data/ext/gqlitedb/src/tests/parser.rs +1225 -0
- data/ext/gqlitedb/src/tests/planner.rs +433 -2
- data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +5 -5
- data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +3 -1
- data/ext/gqlitedb/src/tests/templates/ast.rs +294 -1
- data/ext/gqlitedb/src/tests/templates/programs.rs +318 -0
- data/ext/gqliterb/Cargo.toml +0 -1
- data/ext/gqlparser/src/oc/ast.rs +72 -3
- data/ext/gqlparser/src/oc/error.rs +18 -0
- data/ext/gqlparser/src/oc/lexer.rs +157 -2
- data/ext/gqlparser/src/oc/parser/tests.rs +735 -29
- data/ext/gqlparser/src/oc/parser.rs +1312 -182
- 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 +13 -1
- metadata +6 -2
- data/ext/gqlitedb/src/parser/gql.pest +0 -198
|
@@ -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.
|
|
@@ -122,6 +122,18 @@ where
|
|
|
122
122
|
text: query.substring(e.span().start, e.span().end).to_string(),
|
|
123
123
|
span: e.span().clone(),
|
|
124
124
|
},
|
|
125
|
+
ErrorKind::CreatingWithPredicate => error::CompileTimeError::CreatingWithPredicate {
|
|
126
|
+
text: query.substring(e.span().start, e.span().end).to_string(),
|
|
127
|
+
span: e.span().clone(),
|
|
128
|
+
},
|
|
129
|
+
ErrorKind::ConflictingQuantifiers => error::CompileTimeError::ConflictingQuantifiers {
|
|
130
|
+
text: query.substring(e.span().start, e.span().end).to_string(),
|
|
131
|
+
span: e.span().clone(),
|
|
132
|
+
},
|
|
133
|
+
ErrorKind::EmptyQuantifiedGroup => error::CompileTimeError::EmptyQuantifiedGroup {
|
|
134
|
+
text: query.substring(e.span().start, e.span().end).to_string(),
|
|
135
|
+
span: e.span().clone(),
|
|
136
|
+
},
|
|
125
137
|
_ => error::CompileTimeError::ParseError {
|
|
126
138
|
error_kind: e.error_kind().clone(),
|
|
127
139
|
text: query.substring(e.span().start, e.span().end).to_string(),
|
data/ext/gqlitedb/src/consts.rs
CHANGED
data/ext/gqlitedb/src/error.rs
CHANGED
|
@@ -71,6 +71,30 @@ pub enum CompileTimeError
|
|
|
71
71
|
text: String,
|
|
72
72
|
span: std::ops::Range<usize>,
|
|
73
73
|
},
|
|
74
|
+
#[error(
|
|
75
|
+
"CreatingWithPredicate: CREATE patterns cannot use an inline WHERE predicate at: '{text}' at {span:?}."
|
|
76
|
+
)]
|
|
77
|
+
CreatingWithPredicate
|
|
78
|
+
{
|
|
79
|
+
text: String,
|
|
80
|
+
span: std::ops::Range<usize>,
|
|
81
|
+
},
|
|
82
|
+
#[error(
|
|
83
|
+
"ConflictingQuantifiers: cannot specify both a legacy `[*min..max]` range and a `{{min,max}}`/`*`/`+` quantifier on the same relationship at: '{text}' at {span:?}."
|
|
84
|
+
)]
|
|
85
|
+
ConflictingQuantifiers
|
|
86
|
+
{
|
|
87
|
+
text: String,
|
|
88
|
+
span: std::ops::Range<usize>,
|
|
89
|
+
},
|
|
90
|
+
#[error(
|
|
91
|
+
"EmptyQuantifiedGroup: a quantified group must contain at least one relationship at: '{text}' at {span:?}."
|
|
92
|
+
)]
|
|
93
|
+
EmptyQuantifiedGroup
|
|
94
|
+
{
|
|
95
|
+
text: String,
|
|
96
|
+
span: std::ops::Range<usize>,
|
|
97
|
+
},
|
|
74
98
|
#[error("NotComparable: values are not comparable.")]
|
|
75
99
|
NotComparable,
|
|
76
100
|
#[error("UnknownFunction: {name}.")]
|
|
@@ -78,6 +102,13 @@ pub enum CompileTimeError
|
|
|
78
102
|
{
|
|
79
103
|
name: String
|
|
80
104
|
},
|
|
105
|
+
#[error(
|
|
106
|
+
"DistinctOnNonAggregateFunction: DISTINCT can only be used with aggregate functions, got '{name}'."
|
|
107
|
+
)]
|
|
108
|
+
DistinctOnNonAggregateFunction
|
|
109
|
+
{
|
|
110
|
+
name: String
|
|
111
|
+
},
|
|
81
112
|
#[error("InvalidAggregation: aggregation is not accepted in this expression.")]
|
|
82
113
|
InvalidAggregation,
|
|
83
114
|
#[error("ColumnNameConflict: Column '{name}' is duplicated.")]
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
2
|
-
|
|
3
|
-
use rustc_hash::FxHashSet;
|
|
1
|
+
use std::collections::{HashMap, HashSet};
|
|
4
2
|
|
|
5
3
|
use crate::{
|
|
6
|
-
interpreter::
|
|
4
|
+
interpreter::{
|
|
5
|
+
instructions::{Block, VariablesSizes},
|
|
6
|
+
path,
|
|
7
|
+
},
|
|
7
8
|
prelude::*,
|
|
8
9
|
store::{SelectNodeQuery, TransactionBoxable},
|
|
9
10
|
value_table::{MutableRowInterface, Row, RowInterface},
|
|
@@ -12,7 +13,7 @@ use interpreter::instructions;
|
|
|
12
13
|
|
|
13
14
|
#[derive(Debug, Clone)]
|
|
14
15
|
#[allow(clippy::enum_variant_names)]
|
|
15
|
-
enum Value
|
|
16
|
+
pub(crate) enum Value
|
|
16
17
|
{
|
|
17
18
|
GraphValue(value::Value),
|
|
18
19
|
NodeQuery(store::SelectNodeQuery),
|
|
@@ -157,18 +158,18 @@ macro_rules! contain_to_value {
|
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
#[derive(Default, Debug)]
|
|
160
|
-
struct Stack
|
|
161
|
+
pub(crate) struct Stack
|
|
161
162
|
{
|
|
162
163
|
stack: Vec<Value>,
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
impl Stack
|
|
166
167
|
{
|
|
167
|
-
fn push(&mut self, value: impl Into<Value>)
|
|
168
|
+
pub(crate) fn push(&mut self, value: impl Into<Value>)
|
|
168
169
|
{
|
|
169
170
|
self.stack.push(value.into());
|
|
170
171
|
}
|
|
171
|
-
fn try_pop(&mut self) -> Result<Value>
|
|
172
|
+
pub(crate) fn try_pop(&mut self) -> Result<Value>
|
|
172
173
|
{
|
|
173
174
|
self.stack.pop().ok_or(InternalError::EmptyStack.into())
|
|
174
175
|
}
|
|
@@ -191,7 +192,7 @@ impl Stack
|
|
|
191
192
|
{
|
|
192
193
|
self.stack
|
|
193
194
|
}
|
|
194
|
-
fn try_pop_as_boolean(&mut self) -> Result<bool>
|
|
195
|
+
pub(crate) fn try_pop_as_boolean(&mut self) -> Result<bool>
|
|
195
196
|
{
|
|
196
197
|
let v = self.try_pop()?;
|
|
197
198
|
match v
|
|
@@ -205,7 +206,7 @@ impl Stack
|
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
trait TryPopInto<T>
|
|
209
|
+
pub(crate) trait TryPopInto<T>
|
|
209
210
|
{
|
|
210
211
|
fn try_pop_into(&mut self) -> Result<T>;
|
|
211
212
|
fn try_drain_into(&mut self, n: usize) -> Result<Vec<T>>;
|
|
@@ -333,7 +334,28 @@ fn execute_binary_operator<T: Into<crate::value::Value>>(
|
|
|
333
334
|
Ok(())
|
|
334
335
|
}
|
|
335
336
|
|
|
336
|
-
fn
|
|
337
|
+
fn execute_string_operator(
|
|
338
|
+
stack: &mut Stack,
|
|
339
|
+
operand: impl FnOnce(String, String) -> bool,
|
|
340
|
+
) -> Result<()>
|
|
341
|
+
{
|
|
342
|
+
let a: Value = stack.try_pop()?;
|
|
343
|
+
let b: Value = stack.try_pop()?;
|
|
344
|
+
|
|
345
|
+
let a: graphcore::Value = a.try_into()?;
|
|
346
|
+
let b: graphcore::Value = b.try_into()?;
|
|
347
|
+
|
|
348
|
+
let a = a.try_into();
|
|
349
|
+
let b = b.try_into();
|
|
350
|
+
match (a, b)
|
|
351
|
+
{
|
|
352
|
+
(Ok(a), Ok(b)) => stack.push(operand(a, b)),
|
|
353
|
+
_ => stack.push(graphcore::Value::Null),
|
|
354
|
+
}
|
|
355
|
+
Ok(())
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
pub(crate) fn eval_instructions(
|
|
337
359
|
stack: &mut Stack,
|
|
338
360
|
row: &impl value_table::RowInterface,
|
|
339
361
|
instructions: &instructions::Instructions,
|
|
@@ -807,6 +829,18 @@ fn eval_instructions(
|
|
|
807
829
|
{
|
|
808
830
|
execute_binary_operator(stack, |a, b| a.pow(b))?;
|
|
809
831
|
}
|
|
832
|
+
&instructions::Instruction::StringContainsOperator =>
|
|
833
|
+
{
|
|
834
|
+
execute_string_operator(stack, |a, b| a.contains(&b))?;
|
|
835
|
+
}
|
|
836
|
+
&instructions::Instruction::StringStartsWithOperator =>
|
|
837
|
+
{
|
|
838
|
+
execute_string_operator(stack, |a, b| a.starts_with(&b))?;
|
|
839
|
+
}
|
|
840
|
+
&instructions::Instruction::StringEndsWithOperator =>
|
|
841
|
+
{
|
|
842
|
+
execute_string_operator(stack, |a, b| a.ends_with(&b))?;
|
|
843
|
+
}
|
|
810
844
|
}
|
|
811
845
|
}
|
|
812
846
|
Ok(())
|
|
@@ -921,13 +955,20 @@ fn create_aggregations_states(
|
|
|
921
955
|
&agg.init_instructions,
|
|
922
956
|
parameters,
|
|
923
957
|
)?;
|
|
924
|
-
let state = agg.aggregator.create(
|
|
958
|
+
let mut state = agg.aggregator.create(
|
|
925
959
|
stack
|
|
926
960
|
.into_vec()
|
|
927
961
|
.into_iter()
|
|
928
962
|
.map(|v| v.try_into())
|
|
929
963
|
.collect::<Result<_>>()?,
|
|
930
964
|
)?;
|
|
965
|
+
if agg.distinct
|
|
966
|
+
{
|
|
967
|
+
state = Box::new(aggregators::DistinctAggregatorState {
|
|
968
|
+
inner: state,
|
|
969
|
+
seen: Vec::new(),
|
|
970
|
+
});
|
|
971
|
+
}
|
|
931
972
|
|
|
932
973
|
Ok((name.to_owned(), state))
|
|
933
974
|
})
|
|
@@ -1059,7 +1100,6 @@ fn compute_return_with_table(
|
|
|
1059
1100
|
{
|
|
1060
1101
|
output_table = filter_rows(output_table.into_row_iter(), filter, parameters)?.try_into()?;
|
|
1061
1102
|
}
|
|
1062
|
-
// Apply modifiers
|
|
1063
1103
|
// Sort the table according to order_by
|
|
1064
1104
|
if !modifiers.order_by.is_empty()
|
|
1065
1105
|
{
|
|
@@ -1125,7 +1165,7 @@ fn compute_return_with_table(
|
|
|
1125
1165
|
}
|
|
1126
1166
|
}
|
|
1127
1167
|
|
|
1128
|
-
output_table
|
|
1168
|
+
let output_table_iter = output_table
|
|
1129
1169
|
.into_row_iter()
|
|
1130
1170
|
.map(|mut row| {
|
|
1131
1171
|
Ok(Row::new(
|
|
@@ -1136,8 +1176,25 @@ fn compute_return_with_table(
|
|
|
1136
1176
|
0,
|
|
1137
1177
|
))
|
|
1138
1178
|
})
|
|
1139
|
-
.map(value_table::RowResult)
|
|
1140
|
-
|
|
1179
|
+
.map(value_table::RowResult);
|
|
1180
|
+
|
|
1181
|
+
// Apply modifiers
|
|
1182
|
+
if modifiers.distinct
|
|
1183
|
+
{
|
|
1184
|
+
let mut seen = HashSet::new();
|
|
1185
|
+
|
|
1186
|
+
output_table_iter
|
|
1187
|
+
.filter(|row| match &row.0
|
|
1188
|
+
{
|
|
1189
|
+
Ok(row) => seen.insert(RowKey(row.clone())),
|
|
1190
|
+
Err(_) => true,
|
|
1191
|
+
})
|
|
1192
|
+
.collect()
|
|
1193
|
+
}
|
|
1194
|
+
else
|
|
1195
|
+
{
|
|
1196
|
+
output_table_iter.collect()
|
|
1197
|
+
}
|
|
1141
1198
|
}
|
|
1142
1199
|
|
|
1143
1200
|
fn filter_rows(
|
|
@@ -1172,6 +1229,39 @@ fn filter_rows(
|
|
|
1172
1229
|
.collect()
|
|
1173
1230
|
}
|
|
1174
1231
|
|
|
1232
|
+
/// Executes a chain of `BlockMatch` blocks sequentially against `current_rows`.
|
|
1233
|
+
/// Each block transforms the rows for the next block in the sequence.
|
|
1234
|
+
///
|
|
1235
|
+
/// This is the reusable helper for executing a `Vec<BlockMatch>` chain, used by
|
|
1236
|
+
/// `Block::Match` handlers, group-repetition steps (WI4), and similar contexts.
|
|
1237
|
+
#[allow(clippy::too_many_arguments)]
|
|
1238
|
+
pub(crate) fn execute_block_matches<TStore: store::Store>(
|
|
1239
|
+
blocks: &[instructions::BlockMatch],
|
|
1240
|
+
store: &TStore,
|
|
1241
|
+
tx: &mut TStore::TransactionBox,
|
|
1242
|
+
graph_name: &str,
|
|
1243
|
+
stack: &mut Stack,
|
|
1244
|
+
parameters: &crate::value::ValueMap,
|
|
1245
|
+
mut current_rows: Vec<value_table::Row>,
|
|
1246
|
+
allow_recursive: bool,
|
|
1247
|
+
) -> Result<Vec<value_table::Row>>
|
|
1248
|
+
{
|
|
1249
|
+
for block in blocks
|
|
1250
|
+
{
|
|
1251
|
+
current_rows = execute_block_match(
|
|
1252
|
+
block,
|
|
1253
|
+
store,
|
|
1254
|
+
tx,
|
|
1255
|
+
graph_name,
|
|
1256
|
+
stack,
|
|
1257
|
+
parameters,
|
|
1258
|
+
current_rows,
|
|
1259
|
+
allow_recursive,
|
|
1260
|
+
)?;
|
|
1261
|
+
}
|
|
1262
|
+
Ok(current_rows)
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1175
1265
|
/// Executes a single `BlockMatch` (node or edge) against every row in `current_rows`,
|
|
1176
1266
|
/// appending matching rows into `new_rows`.
|
|
1177
1267
|
///
|
|
@@ -1234,309 +1324,34 @@ fn execute_block_match<TStore: store::Store>(
|
|
|
1234
1324
|
}
|
|
1235
1325
|
}
|
|
1236
1326
|
|
|
1237
|
-
instructions::BlockMatch::MatchEdge {
|
|
1238
|
-
instructions,
|
|
1239
|
-
left_variable,
|
|
1240
|
-
edge_variable,
|
|
1241
|
-
right_variable,
|
|
1242
|
-
path_variable,
|
|
1243
|
-
filter,
|
|
1244
|
-
directivity,
|
|
1245
|
-
recursive_range,
|
|
1246
|
-
} =>
|
|
1327
|
+
instructions::BlockMatch::MatchEdge { .. } =>
|
|
1247
1328
|
{
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
let (left, right) = if edge.reversed
|
|
1261
|
-
{
|
|
1262
|
-
(edge.path.destination(), edge.path.source())
|
|
1263
|
-
}
|
|
1264
|
-
else
|
|
1265
|
-
{
|
|
1266
|
-
(edge.path.source(), edge.path.destination())
|
|
1267
|
-
};
|
|
1268
|
-
|
|
1269
|
-
if let Some(left_variable) = left_variable
|
|
1270
|
-
{
|
|
1271
|
-
new_row.set(*left_variable, left.to_owned().into())?;
|
|
1272
|
-
}
|
|
1273
|
-
if let Some(right_variable) = right_variable
|
|
1274
|
-
{
|
|
1275
|
-
new_row.set(*right_variable, right.to_owned().into())?;
|
|
1276
|
-
}
|
|
1277
|
-
if let Some(edge_variable) = edge_variable
|
|
1278
|
-
{
|
|
1279
|
-
new_row.set(*edge_variable, edge.path.to_edge().into())?;
|
|
1280
|
-
}
|
|
1281
|
-
if let Some(path_variable) = path_variable
|
|
1282
|
-
{
|
|
1283
|
-
new_row.set(*path_variable, edge.path.to_owned().into())?;
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
let should_add_row = if filter.is_empty()
|
|
1287
|
-
{
|
|
1288
|
-
true
|
|
1289
|
-
}
|
|
1290
|
-
else
|
|
1291
|
-
{
|
|
1292
|
-
let mut stack = Stack::default();
|
|
1293
|
-
stack.push(true);
|
|
1294
|
-
stack.push(edge.path.to_edge());
|
|
1295
|
-
eval_instructions(&mut stack, &new_row, filter, parameters)?;
|
|
1296
|
-
stack.try_pop()?;
|
|
1297
|
-
stack.try_pop_into()?
|
|
1298
|
-
};
|
|
1299
|
-
|
|
1300
|
-
if should_add_row
|
|
1301
|
-
{
|
|
1302
|
-
new_rows.push(new_row);
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
Some(recursive_range) =>
|
|
1308
|
-
{
|
|
1309
|
-
if !allow_recursive
|
|
1310
|
-
{
|
|
1311
|
-
return Err(InternalError::UnsupportedRecursivePatternInMerge.into());
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
// Recursive DFS
|
|
1315
|
-
let min_hops = recursive_range.start;
|
|
1316
|
-
let max_hops = recursive_range.end;
|
|
1317
|
-
let capacity = max_hops.min(100);
|
|
1318
|
-
|
|
1319
|
-
let bound_dest_key: Option<graph::Key> = if let Some(right_col) = right_variable
|
|
1320
|
-
{
|
|
1321
|
-
match row.get(*right_col)?
|
|
1322
|
-
{
|
|
1323
|
-
value::Value::Node(node) => Some(node.key()),
|
|
1324
|
-
_ => None,
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
else
|
|
1328
|
-
{
|
|
1329
|
-
None
|
|
1330
|
-
};
|
|
1331
|
-
|
|
1332
|
-
let start_nodes: Vec<graph::Node> = {
|
|
1333
|
-
let source_query = query.source_query();
|
|
1334
|
-
store.select_nodes(tx, graph_name, source_query)?
|
|
1335
|
-
};
|
|
1336
|
-
|
|
1337
|
-
struct Frame
|
|
1338
|
-
{
|
|
1339
|
-
node: graph::Node,
|
|
1340
|
-
edges: Vec<store::EdgeResult>,
|
|
1341
|
-
edge_index: usize,
|
|
1342
|
-
depth: usize,
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
for start_node in start_nodes
|
|
1346
|
-
{
|
|
1347
|
-
let mut path_edges: Vec<graph::Edge> = Vec::with_capacity(capacity);
|
|
1348
|
-
let mut used_edges: FxHashSet<graph::Key> =
|
|
1349
|
-
FxHashSet::with_capacity_and_hasher(capacity, Default::default());
|
|
1350
|
-
let mut dedup_set: FxHashSet<(Option<graph::Key>, Option<graph::Key>, usize)> =
|
|
1351
|
-
FxHashSet::with_capacity_and_hasher(capacity, Default::default());
|
|
1352
|
-
|
|
1353
|
-
let initial_edges = {
|
|
1354
|
-
let q = store::SelectEdgeQuery::select_source_keys(
|
|
1355
|
-
store::SelectNodeQuery::select_keys(vec![start_node.key()]),
|
|
1356
|
-
);
|
|
1357
|
-
store.select_edges(tx, graph_name, q, *directivity)?
|
|
1358
|
-
};
|
|
1359
|
-
|
|
1360
|
-
let mut dfs_stack: Vec<Frame> = vec![Frame {
|
|
1361
|
-
node: start_node.clone(),
|
|
1362
|
-
edges: initial_edges,
|
|
1363
|
-
edge_index: 0,
|
|
1364
|
-
depth: 0,
|
|
1365
|
-
}];
|
|
1366
|
-
|
|
1367
|
-
while !dfs_stack.is_empty()
|
|
1368
|
-
{
|
|
1369
|
-
let mut push_next: Option<Frame> = None;
|
|
1370
|
-
let mut should_pop = false;
|
|
1371
|
-
|
|
1372
|
-
let dfs_intermediate_nodes: Vec<graph::Node> = if dfs_stack.len() >= 2
|
|
1373
|
-
{
|
|
1374
|
-
dfs_stack[1..dfs_stack.len() - 1]
|
|
1375
|
-
.iter()
|
|
1376
|
-
.map(|f| f.node.clone())
|
|
1377
|
-
.collect()
|
|
1378
|
-
}
|
|
1379
|
-
else
|
|
1380
|
-
{
|
|
1381
|
-
Vec::new()
|
|
1382
|
-
};
|
|
1383
|
-
|
|
1384
|
-
{
|
|
1385
|
-
let frame = dfs_stack.last_mut().unwrap();
|
|
1386
|
-
|
|
1387
|
-
// Emit row
|
|
1388
|
-
if frame.edge_index == 0 && frame.depth >= min_hops
|
|
1389
|
-
{
|
|
1390
|
-
let current_node = &frame.node;
|
|
1391
|
-
|
|
1392
|
-
let mut new_row = row.clone();
|
|
1393
|
-
|
|
1394
|
-
if let Some(left_var) = left_variable
|
|
1395
|
-
{
|
|
1396
|
-
new_row.set(*left_var, start_node.clone().into())?;
|
|
1397
|
-
}
|
|
1398
|
-
if let Some(right_var) = right_variable
|
|
1399
|
-
{
|
|
1400
|
-
new_row.set(*right_var, current_node.clone().into())?;
|
|
1401
|
-
}
|
|
1402
|
-
if let Some(edge_var) = edge_variable
|
|
1403
|
-
&& let Some(last_edge) = path_edges.last()
|
|
1404
|
-
{
|
|
1405
|
-
new_row.set(*edge_var, last_edge.clone().into())?;
|
|
1406
|
-
}
|
|
1407
|
-
if let Some(path_var) = path_variable
|
|
1408
|
-
{
|
|
1409
|
-
let path = graph::Path::new(
|
|
1410
|
-
start_node.clone(),
|
|
1411
|
-
path_edges
|
|
1412
|
-
.clone()
|
|
1413
|
-
.into_iter()
|
|
1414
|
-
.zip(
|
|
1415
|
-
dfs_intermediate_nodes
|
|
1416
|
-
.clone()
|
|
1417
|
-
.into_iter()
|
|
1418
|
-
.chain(std::iter::once(current_node.clone())),
|
|
1419
|
-
)
|
|
1420
|
-
.collect(),
|
|
1421
|
-
);
|
|
1422
|
-
new_row.set(*path_var, path.into())?;
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
let passes_filter = if filter.is_empty()
|
|
1426
|
-
{
|
|
1427
|
-
true
|
|
1428
|
-
}
|
|
1429
|
-
else
|
|
1430
|
-
{
|
|
1431
|
-
let mut eval_stack = Stack::default();
|
|
1432
|
-
eval_stack.push(true);
|
|
1433
|
-
if let Some(last_edge) = path_edges.last()
|
|
1434
|
-
{
|
|
1435
|
-
eval_stack.push(last_edge.clone());
|
|
1436
|
-
}
|
|
1437
|
-
eval_instructions(&mut eval_stack, &new_row, filter, parameters)?;
|
|
1438
|
-
eval_stack.try_pop()?;
|
|
1439
|
-
eval_stack.try_pop_into()?
|
|
1440
|
-
};
|
|
1441
|
-
|
|
1442
|
-
let matches_dest = if let Some(dest_key) = bound_dest_key
|
|
1443
|
-
{
|
|
1444
|
-
current_node.key() == dest_key
|
|
1445
|
-
}
|
|
1446
|
-
else
|
|
1447
|
-
{
|
|
1448
|
-
true
|
|
1449
|
-
};
|
|
1450
|
-
|
|
1451
|
-
if passes_filter && matches_dest
|
|
1452
|
-
{
|
|
1453
|
-
let left_key_opt: Option<graph::Key> = if left_variable.is_some()
|
|
1454
|
-
{
|
|
1455
|
-
Some(start_node.key())
|
|
1456
|
-
}
|
|
1457
|
-
else
|
|
1458
|
-
{
|
|
1459
|
-
None
|
|
1460
|
-
};
|
|
1461
|
-
let right_key_opt: Option<graph::Key> = Some(current_node.key());
|
|
1462
|
-
let path_len = path_edges.len();
|
|
1463
|
-
let sig = (left_key_opt, right_key_opt, path_len);
|
|
1464
|
-
|
|
1465
|
-
if !dedup_set.contains(&sig)
|
|
1466
|
-
{
|
|
1467
|
-
dedup_set.insert(sig);
|
|
1468
|
-
new_rows.push(new_row);
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
// Stop for max depth
|
|
1474
|
-
if frame.depth == max_hops || frame.edge_index >= frame.edges.len()
|
|
1475
|
-
{
|
|
1476
|
-
should_pop = true;
|
|
1477
|
-
}
|
|
1478
|
-
else
|
|
1479
|
-
{
|
|
1480
|
-
// Next hop
|
|
1481
|
-
let edge_result = &frame.edges[frame.edge_index];
|
|
1482
|
-
frame.edge_index += 1;
|
|
1483
|
-
|
|
1484
|
-
let edge = edge_result.path.to_edge();
|
|
1485
|
-
let edge_key = edge.key();
|
|
1486
|
-
|
|
1487
|
-
if used_edges.contains(&edge_key)
|
|
1488
|
-
{
|
|
1489
|
-
continue;
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
let next_node = if edge_result.reversed
|
|
1493
|
-
{
|
|
1494
|
-
edge_result.path.source().clone()
|
|
1495
|
-
}
|
|
1496
|
-
else
|
|
1497
|
-
{
|
|
1498
|
-
edge_result.path.destination().clone()
|
|
1499
|
-
};
|
|
1500
|
-
|
|
1501
|
-
used_edges.insert(edge_key);
|
|
1502
|
-
path_edges.push(edge.clone());
|
|
1503
|
-
|
|
1504
|
-
let next_edges = {
|
|
1505
|
-
let q = store::SelectEdgeQuery::select_source_keys(
|
|
1506
|
-
store::SelectNodeQuery::select_keys(vec![next_node.key()]),
|
|
1507
|
-
);
|
|
1508
|
-
store.select_edges(tx, graph_name, q, *directivity)?
|
|
1509
|
-
};
|
|
1510
|
-
|
|
1511
|
-
push_next = Some(Frame {
|
|
1512
|
-
node: next_node,
|
|
1513
|
-
edges: next_edges,
|
|
1514
|
-
edge_index: 0,
|
|
1515
|
-
depth: frame.depth + 1,
|
|
1516
|
-
});
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
// Mutate stack
|
|
1521
|
-
if should_pop
|
|
1522
|
-
{
|
|
1523
|
-
dfs_stack.pop();
|
|
1524
|
-
if let Some(last_edge) = path_edges.pop()
|
|
1525
|
-
{
|
|
1526
|
-
used_edges.remove(&last_edge.key());
|
|
1527
|
-
}
|
|
1528
|
-
continue;
|
|
1529
|
-
}
|
|
1329
|
+
super::path::execute_match_edge(
|
|
1330
|
+
block,
|
|
1331
|
+
store,
|
|
1332
|
+
tx,
|
|
1333
|
+
graph_name,
|
|
1334
|
+
stack,
|
|
1335
|
+
parameters,
|
|
1336
|
+
&row,
|
|
1337
|
+
allow_recursive,
|
|
1338
|
+
&mut new_rows,
|
|
1339
|
+
)?;
|
|
1340
|
+
}
|
|
1530
1341
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1342
|
+
instructions::BlockMatch::MatchQuantifiedPath { .. } =>
|
|
1343
|
+
{
|
|
1344
|
+
path::execute_match_quantified_path(
|
|
1345
|
+
block,
|
|
1346
|
+
store,
|
|
1347
|
+
tx,
|
|
1348
|
+
graph_name,
|
|
1349
|
+
stack,
|
|
1350
|
+
parameters,
|
|
1351
|
+
&row,
|
|
1352
|
+
allow_recursive,
|
|
1353
|
+
&mut new_rows,
|
|
1354
|
+
)?;
|
|
1540
1355
|
}
|
|
1541
1356
|
}
|
|
1542
1357
|
}
|
|
@@ -1714,20 +1529,17 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1714
1529
|
let mut output_table = value_table::ValueTable::new(variables_size.persistent_variables);
|
|
1715
1530
|
for row in input_table.into_row_iter()
|
|
1716
1531
|
{
|
|
1717
|
-
let
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
true, // recursive patterns allowed in MATCH
|
|
1729
|
-
)?;
|
|
1730
|
-
}
|
|
1532
|
+
let current_rows: Vec<_> = vec![row.clone().extended(variables_size.total_size())?];
|
|
1533
|
+
let mut current_rows = execute_block_matches(
|
|
1534
|
+
blocks,
|
|
1535
|
+
store,
|
|
1536
|
+
&mut tx,
|
|
1537
|
+
&graph_name,
|
|
1538
|
+
&mut stack,
|
|
1539
|
+
parameters,
|
|
1540
|
+
current_rows,
|
|
1541
|
+
true, // recursive patterns allowed in MATCH
|
|
1542
|
+
)?;
|
|
1731
1543
|
if !filter.is_empty()
|
|
1732
1544
|
{
|
|
1733
1545
|
current_rows = filter_rows(current_rows.into_iter(), filter, parameters)?;
|
|
@@ -2384,6 +2196,16 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
2384
2196
|
);
|
|
2385
2197
|
return Ok(crate::value::Value::Map(res).into());
|
|
2386
2198
|
}
|
|
2199
|
+
else if name == "gqlite.db.list_graphs"
|
|
2200
|
+
{
|
|
2201
|
+
let mut graphs = store.graphs_list(&mut tx)?;
|
|
2202
|
+
graphs.sort();
|
|
2203
|
+
let list: Vec<crate::value::Value> = graphs
|
|
2204
|
+
.into_iter()
|
|
2205
|
+
.map(crate::value::Value::String)
|
|
2206
|
+
.collect();
|
|
2207
|
+
return Ok(crate::value::Value::Array(list).into());
|
|
2208
|
+
}
|
|
2387
2209
|
else
|
|
2388
2210
|
{
|
|
2389
2211
|
return Err(InternalError::Unimplemented("call for any other function").into());
|