Skip to content

Commit

Permalink
[GraphQL/Type Filter] Intersection algorithm (MystenLabs#15701)
Browse files Browse the repository at this point in the history
## Description

Adds a way to intersect two module or fully-qualified name filters, to
correct the behaviour of transaction block filtering (which previously
implemented intersection of its function filter by simple equality
test).

## Test Plan

New unit tests:

```
sui-graphql-rpc$ cargo nextest run -- type_filter
```

## Stack 

- MystenLabs#15661 
- MystenLabs#15695 
- MystenLabs#15698 
- MystenLabs#15699
  • Loading branch information
amnn committed Jan 15, 2024
1 parent 2249e11 commit c037dea
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
2 changes: 1 addition & 1 deletion crates/sui-graphql-rpc/src/types/transaction_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ impl TransactionBlockFilter {
}

Some(Self {
function: merge!(function, by_eq)?,
function: merge!(function, FqNameFilter::intersect)?,
kind: merge!(kind, by_eq)?,

after_checkpoint: merge!(after_checkpoint, by_max)?,
Expand Down
59 changes: 59 additions & 0 deletions crates/sui-graphql-rpc/src/types/type_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,31 @@ impl FqNameFilter {
.filter(name.eq(n.clone())),
}
}

/// Try to create a filter whose results are the intersection of the results of the input
/// filters (`self` and `other`). This may not be possible if the resulting filter is
/// inconsistent (e.g. a filter that requires the module member's package to be at two different
/// addresses simultaneously), in which case `None` is returned.
pub(crate) fn intersect(self, other: Self) -> Option<Self> {
use FqNameFilter as F;
use ModuleFilter as M;

match (&self, &other) {
(F::ByModule(m), F::ByModule(n)) => m.clone().intersect(n.clone()).map(Self::ByModule),
(F::ByFqName(_, _, _), F::ByFqName(_, _, _)) => (self == other).then_some(self),

(F::ByFqName(p, _, _), F::ByModule(M::ByPackage(q))) => (p == q).then_some(self),
(F::ByModule(M::ByPackage(p)), F::ByFqName(q, _, _)) => (p == q).then_some(other),

(F::ByFqName(p, m, _), F::ByModule(M::ByModule(q, n))) => {
((p, m) == (q, n)).then_some(self)
}

(F::ByModule(M::ByModule(p, m)), F::ByFqName(q, n, _)) => {
((p, m) == (q, n)).then_some(other)
}
}
}
}

impl ModuleFilter {
Expand Down Expand Up @@ -160,6 +185,20 @@ impl ModuleFilter {
.filter(module.eq(m.clone())),
}
}

/// Try to create a filter whose results are the intersection of the results of the input
/// filters (`self` and `other`). This may not be possible if the resulting filter is
/// inconsistent (e.g. a filter that requires the module's package to be at two different
/// addresses simultaneously), in which case `None` is returned.
pub(crate) fn intersect(self, other: Self) -> Option<Self> {
match (&self, &other) {
(Self::ByPackage(_), Self::ByPackage(_))
| (Self::ByModule(_, _), Self::ByModule(_, _)) => (self == other).then_some(self),

(Self::ByPackage(p), Self::ByModule(q, _)) => (p == q).then_some(other),
(Self::ByModule(p, _), Self::ByPackage(q)) => (p == q).then_some(self),
}
}
}

impl_string_input!(TypeFilter);
Expand Down Expand Up @@ -610,4 +649,24 @@ mod tests {
assert!(ModuleFilter::from_str(invalid_module_filter).is_err());
}
}

#[test]
fn test_intersection() {
let sui = FqNameFilter::from_str("0x2").unwrap();
let coin = FqNameFilter::from_str("0x2::coin").unwrap();
let take = FqNameFilter::from_str("0x2::coin::take").unwrap();

let std = FqNameFilter::from_str("0x1").unwrap();
let string = FqNameFilter::from_str("0x1::string").unwrap();
let utf8 = FqNameFilter::from_str("0x1::string::utf8").unwrap();

assert_eq!(sui.clone().intersect(sui.clone()), Some(sui.clone()));
assert_eq!(sui.clone().intersect(coin.clone()), Some(coin.clone()));
assert_eq!(sui.clone().intersect(take.clone()), Some(take.clone()));
assert_eq!(take.clone().intersect(coin.clone()), Some(take.clone()));

assert_eq!(sui.clone().intersect(std.clone()), None);
assert_eq!(sui.clone().intersect(string.clone()), None);
assert_eq!(utf8.clone().intersect(coin.clone()), None);
}
}

0 comments on commit c037dea

Please sign in to comment.