gqlite 1.8.1 → 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/gqlitedb/Cargo.toml +0 -2
- data/ext/gqlitedb/src/compiler/variables_manager.rs +109 -10
- data/ext/gqlitedb/src/compiler.rs +481 -47
- data/ext/gqlitedb/src/connection.rs +12 -0
- data/ext/gqlitedb/src/consts.rs +1 -1
- data/ext/gqlitedb/src/error.rs +24 -0
- data/ext/gqlitedb/src/interpreter/evaluators.rs +91 -325
- data/ext/gqlitedb/src/interpreter/instructions.rs +72 -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 +218 -6
- data/ext/gqlitedb/src/query_result.rs +19 -4
- data/ext/gqlitedb/src/tests/compiler.rs +2453 -1
- data/ext/gqlitedb/src/tests/connection/redb.rs +8 -0
- data/ext/gqlitedb/src/tests/connection.rs +37 -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 +432 -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 +284 -1
- data/ext/gqlitedb/src/tests/templates/programs.rs +306 -0
- data/ext/gqliterb/Cargo.toml +0 -1
- data/ext/gqlparser/src/oc/ast.rs +52 -3
- data/ext/gqlparser/src/oc/error.rs +18 -0
- data/ext/gqlparser/src/oc/lexer.rs +146 -0
- data/ext/gqlparser/src/oc/parser/tests.rs +365 -20
- data/ext/gqlparser/src/oc/parser.rs +1196 -143
- data/ext/graphcore/src/value.rs +12 -1
- metadata +5 -2
- data/ext/gqlitedb/src/parser/gql.pest +0 -198
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
use std::collections::{HashMap, HashSet};
|
|
2
2
|
|
|
3
|
-
use rustc_hash::FxHashSet;
|
|
4
|
-
|
|
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>>;
|
|
@@ -354,7 +355,7 @@ fn execute_string_operator(
|
|
|
354
355
|
Ok(())
|
|
355
356
|
}
|
|
356
357
|
|
|
357
|
-
fn eval_instructions(
|
|
358
|
+
pub(crate) fn eval_instructions(
|
|
358
359
|
stack: &mut Stack,
|
|
359
360
|
row: &impl value_table::RowInterface,
|
|
360
361
|
instructions: &instructions::Instructions,
|
|
@@ -1228,6 +1229,39 @@ fn filter_rows(
|
|
|
1228
1229
|
.collect()
|
|
1229
1230
|
}
|
|
1230
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
|
+
|
|
1231
1265
|
/// Executes a single `BlockMatch` (node or edge) against every row in `current_rows`,
|
|
1232
1266
|
/// appending matching rows into `new_rows`.
|
|
1233
1267
|
///
|
|
@@ -1290,309 +1324,34 @@ fn execute_block_match<TStore: store::Store>(
|
|
|
1290
1324
|
}
|
|
1291
1325
|
}
|
|
1292
1326
|
|
|
1293
|
-
instructions::BlockMatch::MatchEdge {
|
|
1294
|
-
instructions,
|
|
1295
|
-
left_variable,
|
|
1296
|
-
edge_variable,
|
|
1297
|
-
right_variable,
|
|
1298
|
-
path_variable,
|
|
1299
|
-
filter,
|
|
1300
|
-
directivity,
|
|
1301
|
-
recursive_range,
|
|
1302
|
-
} =>
|
|
1327
|
+
instructions::BlockMatch::MatchEdge { .. } =>
|
|
1303
1328
|
{
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
let (left, right) = if edge.reversed
|
|
1317
|
-
{
|
|
1318
|
-
(edge.path.destination(), edge.path.source())
|
|
1319
|
-
}
|
|
1320
|
-
else
|
|
1321
|
-
{
|
|
1322
|
-
(edge.path.source(), edge.path.destination())
|
|
1323
|
-
};
|
|
1324
|
-
|
|
1325
|
-
if let Some(left_variable) = left_variable
|
|
1326
|
-
{
|
|
1327
|
-
new_row.set(*left_variable, left.to_owned().into())?;
|
|
1328
|
-
}
|
|
1329
|
-
if let Some(right_variable) = right_variable
|
|
1330
|
-
{
|
|
1331
|
-
new_row.set(*right_variable, right.to_owned().into())?;
|
|
1332
|
-
}
|
|
1333
|
-
if let Some(edge_variable) = edge_variable
|
|
1334
|
-
{
|
|
1335
|
-
new_row.set(*edge_variable, edge.path.to_edge().into())?;
|
|
1336
|
-
}
|
|
1337
|
-
if let Some(path_variable) = path_variable
|
|
1338
|
-
{
|
|
1339
|
-
new_row.set(*path_variable, edge.path.to_owned().into())?;
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
let should_add_row = if filter.is_empty()
|
|
1343
|
-
{
|
|
1344
|
-
true
|
|
1345
|
-
}
|
|
1346
|
-
else
|
|
1347
|
-
{
|
|
1348
|
-
let mut stack = Stack::default();
|
|
1349
|
-
stack.push(true);
|
|
1350
|
-
stack.push(edge.path.to_edge());
|
|
1351
|
-
eval_instructions(&mut stack, &new_row, filter, parameters)?;
|
|
1352
|
-
stack.try_pop()?;
|
|
1353
|
-
stack.try_pop_into()?
|
|
1354
|
-
};
|
|
1355
|
-
|
|
1356
|
-
if should_add_row
|
|
1357
|
-
{
|
|
1358
|
-
new_rows.push(new_row);
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
Some(recursive_range) =>
|
|
1364
|
-
{
|
|
1365
|
-
if !allow_recursive
|
|
1366
|
-
{
|
|
1367
|
-
return Err(InternalError::UnsupportedRecursivePatternInMerge.into());
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
// Recursive DFS
|
|
1371
|
-
let min_hops = recursive_range.start;
|
|
1372
|
-
let max_hops = recursive_range.end;
|
|
1373
|
-
let capacity = max_hops.min(100);
|
|
1374
|
-
|
|
1375
|
-
let bound_dest_key: Option<graph::Key> = if let Some(right_col) = right_variable
|
|
1376
|
-
{
|
|
1377
|
-
match row.get(*right_col)?
|
|
1378
|
-
{
|
|
1379
|
-
value::Value::Node(node) => Some(node.key()),
|
|
1380
|
-
_ => None,
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
else
|
|
1384
|
-
{
|
|
1385
|
-
None
|
|
1386
|
-
};
|
|
1387
|
-
|
|
1388
|
-
let start_nodes: Vec<graph::Node> = {
|
|
1389
|
-
let source_query = query.source_query();
|
|
1390
|
-
store.select_nodes(tx, graph_name, source_query)?
|
|
1391
|
-
};
|
|
1392
|
-
|
|
1393
|
-
struct Frame
|
|
1394
|
-
{
|
|
1395
|
-
node: graph::Node,
|
|
1396
|
-
edges: Vec<store::EdgeResult>,
|
|
1397
|
-
edge_index: usize,
|
|
1398
|
-
depth: usize,
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
for start_node in start_nodes
|
|
1402
|
-
{
|
|
1403
|
-
let mut path_edges: Vec<graph::Edge> = Vec::with_capacity(capacity);
|
|
1404
|
-
let mut used_edges: FxHashSet<graph::Key> =
|
|
1405
|
-
FxHashSet::with_capacity_and_hasher(capacity, Default::default());
|
|
1406
|
-
let mut dedup_set: FxHashSet<(Option<graph::Key>, Option<graph::Key>, usize)> =
|
|
1407
|
-
FxHashSet::with_capacity_and_hasher(capacity, Default::default());
|
|
1408
|
-
|
|
1409
|
-
let initial_edges = {
|
|
1410
|
-
let q = store::SelectEdgeQuery::select_source_keys(
|
|
1411
|
-
store::SelectNodeQuery::select_keys(vec![start_node.key()]),
|
|
1412
|
-
);
|
|
1413
|
-
store.select_edges(tx, graph_name, q, *directivity)?
|
|
1414
|
-
};
|
|
1415
|
-
|
|
1416
|
-
let mut dfs_stack: Vec<Frame> = vec![Frame {
|
|
1417
|
-
node: start_node.clone(),
|
|
1418
|
-
edges: initial_edges,
|
|
1419
|
-
edge_index: 0,
|
|
1420
|
-
depth: 0,
|
|
1421
|
-
}];
|
|
1422
|
-
|
|
1423
|
-
while !dfs_stack.is_empty()
|
|
1424
|
-
{
|
|
1425
|
-
let mut push_next: Option<Frame> = None;
|
|
1426
|
-
let mut should_pop = false;
|
|
1427
|
-
|
|
1428
|
-
let dfs_intermediate_nodes: Vec<graph::Node> = if dfs_stack.len() >= 2
|
|
1429
|
-
{
|
|
1430
|
-
dfs_stack[1..dfs_stack.len() - 1]
|
|
1431
|
-
.iter()
|
|
1432
|
-
.map(|f| f.node.clone())
|
|
1433
|
-
.collect()
|
|
1434
|
-
}
|
|
1435
|
-
else
|
|
1436
|
-
{
|
|
1437
|
-
Vec::new()
|
|
1438
|
-
};
|
|
1439
|
-
|
|
1440
|
-
{
|
|
1441
|
-
let frame = dfs_stack.last_mut().unwrap();
|
|
1442
|
-
|
|
1443
|
-
// Emit row
|
|
1444
|
-
if frame.edge_index == 0 && frame.depth >= min_hops
|
|
1445
|
-
{
|
|
1446
|
-
let current_node = &frame.node;
|
|
1447
|
-
|
|
1448
|
-
let mut new_row = row.clone();
|
|
1449
|
-
|
|
1450
|
-
if let Some(left_var) = left_variable
|
|
1451
|
-
{
|
|
1452
|
-
new_row.set(*left_var, start_node.clone().into())?;
|
|
1453
|
-
}
|
|
1454
|
-
if let Some(right_var) = right_variable
|
|
1455
|
-
{
|
|
1456
|
-
new_row.set(*right_var, current_node.clone().into())?;
|
|
1457
|
-
}
|
|
1458
|
-
if let Some(edge_var) = edge_variable
|
|
1459
|
-
&& let Some(last_edge) = path_edges.last()
|
|
1460
|
-
{
|
|
1461
|
-
new_row.set(*edge_var, last_edge.clone().into())?;
|
|
1462
|
-
}
|
|
1463
|
-
if let Some(path_var) = path_variable
|
|
1464
|
-
{
|
|
1465
|
-
let path = graph::Path::new(
|
|
1466
|
-
start_node.clone(),
|
|
1467
|
-
path_edges
|
|
1468
|
-
.clone()
|
|
1469
|
-
.into_iter()
|
|
1470
|
-
.zip(
|
|
1471
|
-
dfs_intermediate_nodes
|
|
1472
|
-
.clone()
|
|
1473
|
-
.into_iter()
|
|
1474
|
-
.chain(std::iter::once(current_node.clone())),
|
|
1475
|
-
)
|
|
1476
|
-
.collect(),
|
|
1477
|
-
);
|
|
1478
|
-
new_row.set(*path_var, path.into())?;
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
let passes_filter = if filter.is_empty()
|
|
1482
|
-
{
|
|
1483
|
-
true
|
|
1484
|
-
}
|
|
1485
|
-
else
|
|
1486
|
-
{
|
|
1487
|
-
let mut eval_stack = Stack::default();
|
|
1488
|
-
eval_stack.push(true);
|
|
1489
|
-
if let Some(last_edge) = path_edges.last()
|
|
1490
|
-
{
|
|
1491
|
-
eval_stack.push(last_edge.clone());
|
|
1492
|
-
}
|
|
1493
|
-
eval_instructions(&mut eval_stack, &new_row, filter, parameters)?;
|
|
1494
|
-
eval_stack.try_pop()?;
|
|
1495
|
-
eval_stack.try_pop_into()?
|
|
1496
|
-
};
|
|
1497
|
-
|
|
1498
|
-
let matches_dest = if let Some(dest_key) = bound_dest_key
|
|
1499
|
-
{
|
|
1500
|
-
current_node.key() == dest_key
|
|
1501
|
-
}
|
|
1502
|
-
else
|
|
1503
|
-
{
|
|
1504
|
-
true
|
|
1505
|
-
};
|
|
1506
|
-
|
|
1507
|
-
if passes_filter && matches_dest
|
|
1508
|
-
{
|
|
1509
|
-
let left_key_opt: Option<graph::Key> = if left_variable.is_some()
|
|
1510
|
-
{
|
|
1511
|
-
Some(start_node.key())
|
|
1512
|
-
}
|
|
1513
|
-
else
|
|
1514
|
-
{
|
|
1515
|
-
None
|
|
1516
|
-
};
|
|
1517
|
-
let right_key_opt: Option<graph::Key> = Some(current_node.key());
|
|
1518
|
-
let path_len = path_edges.len();
|
|
1519
|
-
let sig = (left_key_opt, right_key_opt, path_len);
|
|
1520
|
-
|
|
1521
|
-
if !dedup_set.contains(&sig)
|
|
1522
|
-
{
|
|
1523
|
-
dedup_set.insert(sig);
|
|
1524
|
-
new_rows.push(new_row);
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
// Stop for max depth
|
|
1530
|
-
if frame.depth == max_hops || frame.edge_index >= frame.edges.len()
|
|
1531
|
-
{
|
|
1532
|
-
should_pop = true;
|
|
1533
|
-
}
|
|
1534
|
-
else
|
|
1535
|
-
{
|
|
1536
|
-
// Next hop
|
|
1537
|
-
let edge_result = &frame.edges[frame.edge_index];
|
|
1538
|
-
frame.edge_index += 1;
|
|
1539
|
-
|
|
1540
|
-
let edge = edge_result.path.to_edge();
|
|
1541
|
-
let edge_key = edge.key();
|
|
1542
|
-
|
|
1543
|
-
if used_edges.contains(&edge_key)
|
|
1544
|
-
{
|
|
1545
|
-
continue;
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
let next_node = if edge_result.reversed
|
|
1549
|
-
{
|
|
1550
|
-
edge_result.path.source().clone()
|
|
1551
|
-
}
|
|
1552
|
-
else
|
|
1553
|
-
{
|
|
1554
|
-
edge_result.path.destination().clone()
|
|
1555
|
-
};
|
|
1556
|
-
|
|
1557
|
-
used_edges.insert(edge_key);
|
|
1558
|
-
path_edges.push(edge.clone());
|
|
1559
|
-
|
|
1560
|
-
let next_edges = {
|
|
1561
|
-
let q = store::SelectEdgeQuery::select_source_keys(
|
|
1562
|
-
store::SelectNodeQuery::select_keys(vec![next_node.key()]),
|
|
1563
|
-
);
|
|
1564
|
-
store.select_edges(tx, graph_name, q, *directivity)?
|
|
1565
|
-
};
|
|
1566
|
-
|
|
1567
|
-
push_next = Some(Frame {
|
|
1568
|
-
node: next_node,
|
|
1569
|
-
edges: next_edges,
|
|
1570
|
-
edge_index: 0,
|
|
1571
|
-
depth: frame.depth + 1,
|
|
1572
|
-
});
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
// Mutate stack
|
|
1577
|
-
if should_pop
|
|
1578
|
-
{
|
|
1579
|
-
dfs_stack.pop();
|
|
1580
|
-
if let Some(last_edge) = path_edges.pop()
|
|
1581
|
-
{
|
|
1582
|
-
used_edges.remove(&last_edge.key());
|
|
1583
|
-
}
|
|
1584
|
-
continue;
|
|
1585
|
-
}
|
|
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
|
+
}
|
|
1586
1341
|
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
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
|
+
)?;
|
|
1596
1355
|
}
|
|
1597
1356
|
}
|
|
1598
1357
|
}
|
|
@@ -1770,20 +1529,17 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1770
1529
|
let mut output_table = value_table::ValueTable::new(variables_size.persistent_variables);
|
|
1771
1530
|
for row in input_table.into_row_iter()
|
|
1772
1531
|
{
|
|
1773
|
-
let
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
true, // recursive patterns allowed in MATCH
|
|
1785
|
-
)?;
|
|
1786
|
-
}
|
|
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
|
+
)?;
|
|
1787
1543
|
if !filter.is_empty()
|
|
1788
1544
|
{
|
|
1789
1545
|
current_rows = filter_rows(current_rows.into_iter(), filter, parameters)?;
|
|
@@ -2440,6 +2196,16 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
2440
2196
|
);
|
|
2441
2197
|
return Ok(crate::value::Value::Map(res).into());
|
|
2442
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
|
+
}
|
|
2443
2209
|
else
|
|
2444
2210
|
{
|
|
2445
2211
|
return Err(InternalError::Unimplemented("call for any other function").into());
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
use crate::{prelude::*, value_table::ColId};
|
|
2
2
|
|
|
3
|
+
#[derive(Debug)]
|
|
4
|
+
pub(crate) enum StopCondition
|
|
5
|
+
{
|
|
6
|
+
Shortest(Instructions),
|
|
7
|
+
ShortestGroups(Instructions),
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
#[derive(Debug)]
|
|
11
|
+
pub(crate) enum PathStrategy
|
|
12
|
+
{
|
|
13
|
+
Dfs,
|
|
14
|
+
Bfs
|
|
15
|
+
{
|
|
16
|
+
stop_condition: StopCondition,
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
3
20
|
#[derive(Debug)]
|
|
4
21
|
#[allow(clippy::large_enum_variant)]
|
|
5
22
|
pub(crate) enum Instruction
|
|
@@ -103,6 +120,7 @@ pub(crate) struct CreateAction
|
|
|
103
120
|
}
|
|
104
121
|
|
|
105
122
|
#[derive(Debug)]
|
|
123
|
+
#[allow(clippy::enum_variant_names)]
|
|
106
124
|
pub(crate) enum BlockMatch
|
|
107
125
|
{
|
|
108
126
|
MatchNode
|
|
@@ -122,6 +140,60 @@ pub(crate) enum BlockMatch
|
|
|
122
140
|
directivity: graphcore::EdgeDirectivity,
|
|
123
141
|
recursive_range: Option<std::ops::Range<usize>>,
|
|
124
142
|
},
|
|
143
|
+
MatchQuantifiedPath
|
|
144
|
+
{
|
|
145
|
+
/// Compiled query for the group's fixed anchor before it (`qp.source`).
|
|
146
|
+
/// Pops one `SelectNodeQuery`, same as an ordinary `MatchNode`'s
|
|
147
|
+
/// `instructions` field — reuses `compile_match_node` directly (WI2).
|
|
148
|
+
source_instructions: Instructions,
|
|
149
|
+
source_variable: Option<ColId>,
|
|
150
|
+
source_filter: Instructions,
|
|
151
|
+
/// The destination node is validated only at emission time via
|
|
152
|
+
/// `destination_filter`, not queried from the store.
|
|
153
|
+
destination_variable: Option<ColId>,
|
|
154
|
+
destination_filter: Instructions,
|
|
155
|
+
/// The `p = ...` binding, if present.
|
|
156
|
+
path_variable: Option<ColId>,
|
|
157
|
+
/// Resolved `min..max` from `qp.quantifier` — always `Some` in
|
|
158
|
+
/// practice by the time this instruction exists (WI3 only ever
|
|
159
|
+
/// constructs one from a `QuantifiedPathPattern` with `group.len() > 1`,
|
|
160
|
+
/// and Milestone 2/4's `None`-quantifier scoping-wrapper use case,
|
|
161
|
+
/// design §1d, isn't reachable from real query text until Milestone 5).
|
|
162
|
+
quantifier: std::ops::Range<usize>,
|
|
163
|
+
/// The group's inner pattern sequence, compiled the same way an
|
|
164
|
+
/// ordinary top-level pattern chain is (WI2) — run once per repetition.
|
|
165
|
+
group_instructions: Vec<BlockMatch>,
|
|
166
|
+
/// Every variable declared inside the group (`n`, `r`, `m` for
|
|
167
|
+
/// `(a) ((n)-[r]->(m)){1,5} (b)`) — however many there are. Bound as a
|
|
168
|
+
/// list at execution (Epic 8); this field only declares *which*
|
|
169
|
+
/// columns need that treatment.
|
|
170
|
+
group_variables: Vec<ColId>,
|
|
171
|
+
/// Which entry in `group_variables` is the group's own terminal node
|
|
172
|
+
/// (the exit node of its last inner edge) - guaranteed present by the
|
|
173
|
+
/// parser-level fix. Used for chaining repetitions and for
|
|
174
|
+
/// checking `destination_filter`/binding `destination_variable`,
|
|
175
|
+
/// replacing the "first Node found" heuristic.
|
|
176
|
+
group_terminal_variable: ColId,
|
|
177
|
+
/// Which entry in `group_variables` is the group's own entry node
|
|
178
|
+
/// (the source node of its first inner edge) - guaranteed present by the
|
|
179
|
+
/// parser-level fix. Used to wire the previous repetition's terminal
|
|
180
|
+
/// to the next repetition's entry, so pattern search is constrained
|
|
181
|
+
/// from the current position rather than all nodes.
|
|
182
|
+
group_entry_variable: ColId,
|
|
183
|
+
/// Compiled group `WHERE`, if present (design §1, `qp.group_where`) —
|
|
184
|
+
/// evaluated once per repetition once Epic 8 wires up execution.
|
|
185
|
+
/// Genuine groups only; never `Some` on wrapper-promoted patterns.
|
|
186
|
+
group_where_filter: Option<Instructions>,
|
|
187
|
+
/// Whole-completed-path predicate, from the selector-scoped wrapper
|
|
188
|
+
/// (`qp.path_where`). Evaluated once a candidate reaches `destination`,
|
|
189
|
+
/// before it's admitted to shortest-path selection. Distinct from
|
|
190
|
+
/// `group_where_filter`'s per-repetition timing; never both `Some` on
|
|
191
|
+
/// the same instruction in practice (a wrapper-promoted pattern has no
|
|
192
|
+
/// genuine group `WHERE` of its own).
|
|
193
|
+
path_filter: Option<Instructions>,
|
|
194
|
+
/// The path strategy (Dfs or Bfs with stop condition) from the planner.
|
|
195
|
+
strategy: PathStrategy,
|
|
196
|
+
},
|
|
125
197
|
}
|
|
126
198
|
|
|
127
199
|
#[derive(Debug)]
|