use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use crate::planning::plan::core::nodes::{BinaryInputNode, PlanNodeEnum, SingleInputNode};
use linkrs_core::types::expr::ContextualExpression;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PlanFingerprint(pub u64);
impl PlanFingerprint {
pub fn new(value: u64) -> Self {
Self(value)
}
pub fn value(&self) -> u64 {
self.0
}
}
#[derive(Debug, Clone)]
pub struct FingerprintCalculator;
impl Default for FingerprintCalculator {
fn default() -> Self {
Self
}
}
impl FingerprintCalculator {
pub fn new() -> Self {
Self
}
pub fn calculate_fingerprint(&self, node: &PlanNodeEnum) -> PlanFingerprint {
let mut hasher = DefaultHasher::new();
std::mem::discriminant(node).hash(&mut hasher);
self.hash_children(node, &mut hasher);
self.hash_node_config(node, &mut hasher);
PlanFingerprint::new(hasher.finish())
}
fn hash_node_config(&self, node: &PlanNodeEnum, hasher: &mut DefaultHasher) {
use crate::planning::plan::core::nodes::*;
match node {
PlanNodeEnum::Filter(n) => self.hash_expression(n.condition(), hasher),
PlanNodeEnum::Project(n) => {
for col in n.columns() {
col.alias.hash(hasher);
self.hash_expression(&col.expression, hasher);
}
}
PlanNodeEnum::Sort(n) => {
for item in n.sort_items() {
item.expression.to_expression_string().hash(hasher);
item.direction.hash(hasher);
}
n.limit().hash(hasher);
}
PlanNodeEnum::TopN(n) => {
for item in n.sort_items() {
item.expression.to_expression_string().hash(hasher);
item.direction.hash(hasher);
}
n.limit().hash(hasher);
}
PlanNodeEnum::Limit(n) => {
n.offset().hash(hasher);
n.count().hash(hasher);
}
PlanNodeEnum::Sample(n) => {
n.count().hash(hasher);
}
PlanNodeEnum::Aggregate(n) => {
for key in n.group_keys() {
key.hash(hasher);
}
}
PlanNodeEnum::ScanVertices(n) => {
n.tag().hash(hasher);
n.projected_properties().hash(hasher);
if let Some(filter) = n.filter() {
self.hash_expression(filter, hasher);
}
n.limit().hash(hasher);
}
PlanNodeEnum::ScanEdges(n) => {
n.edge_type().hash(hasher);
n.projected_properties().hash(hasher);
if let Some(filter) = n.filter() {
self.hash_expression(filter, hasher);
}
n.limit().hash(hasher);
}
PlanNodeEnum::IndexScan(n) => {
n.schema_name().hash(hasher);
n.index_name().hash(hasher);
if let Some(filter) = n.filter() {
self.hash_expression(filter, hasher);
}
n.limit().hash(hasher);
}
_ => {}
}
}
fn hash_expression(&self, expr: &ContextualExpression, hasher: &mut DefaultHasher) {
match expr.expression() {
Some(meta) => meta.to_expression_string().hash(hasher),
None => expr.id().hash(hasher),
}
}
fn hash_children(&self, node: &PlanNodeEnum, hasher: &mut DefaultHasher) {
use crate::planning::plan::core::nodes::*;
match node {
PlanNodeEnum::Filter(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Project(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Sort(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Limit(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::TopN(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Sample(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Aggregate(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Dedup(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Unwind(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::DataCollect(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Traverse(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Expand(n) => {
for dep in n.inputs() {
let fp = self.calculate_fingerprint(dep);
fp.hash(hasher);
}
}
PlanNodeEnum::ExpandAll(n) => {
for dep in n.inputs() {
let fp = self.calculate_fingerprint(dep);
fp.hash(hasher);
}
}
PlanNodeEnum::AppendVertices(n) => {
for dep in n.inputs() {
let fp = self.calculate_fingerprint(dep);
fp.hash(hasher);
}
}
PlanNodeEnum::Argument(_) => {
}
PlanNodeEnum::PassThrough(_) => {
}
PlanNodeEnum::PatternApply(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::CorrelatedApply(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::RollUpApply(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Assign(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::InnerJoin(n) => {
self.hash_binary_input(n, hasher);
}
PlanNodeEnum::LeftJoin(n) => {
self.hash_binary_input(n, hasher);
}
PlanNodeEnum::CrossJoin(n) => {
self.hash_binary_input(n, hasher);
}
PlanNodeEnum::FullOuterJoin(n) => {
self.hash_binary_input(n, hasher);
}
PlanNodeEnum::Union(n) => {
self.hash_single_input(n, hasher);
}
PlanNodeEnum::Minus(n) => {
let left_fp = self.calculate_fingerprint(n.input());
let right_fp = self.calculate_fingerprint(n.minus_input());
left_fp.hash(hasher);
right_fp.hash(hasher);
}
PlanNodeEnum::Intersect(n) => {
let left_fp = self.calculate_fingerprint(n.input());
let right_fp = self.calculate_fingerprint(n.intersect_input());
left_fp.hash(hasher);
right_fp.hash(hasher);
}
PlanNodeEnum::Select(n) => {
if let Some(ref branch) = n.if_branch() {
let fp = self.calculate_fingerprint(branch);
fp.hash(hasher);
}
if let Some(ref branch) = n.else_branch() {
let fp = self.calculate_fingerprint(branch);
fp.hash(hasher);
}
}
PlanNodeEnum::Loop(n) => {
if let Some(ref body) = n.body() {
let body_fp = self.calculate_fingerprint(body);
body_fp.hash(hasher);
}
}
PlanNodeEnum::RecursiveCte(n) => {
n.cte_name().hash(hasher);
n.max_iterations().hash(hasher);
let anchor_fp = self.calculate_fingerprint(n.anchor());
anchor_fp.hash(hasher);
if let Some(step) = n.step() {
let step_fp = self.calculate_fingerprint(step);
step_fp.hash(hasher);
}
}
PlanNodeEnum::Start(_) => {
}
PlanNodeEnum::GetVertices(_) => {
}
PlanNodeEnum::GetEdges(_) => {
}
PlanNodeEnum::GetNeighbors(_) => {
}
PlanNodeEnum::ScanVertices(_) => {
}
PlanNodeEnum::ScanEdges(_) => {
}
PlanNodeEnum::IndexScan(_) => {
}
PlanNodeEnum::ShortestPath(_) => {
}
PlanNodeEnum::MultiShortestPath(_) => {
}
PlanNodeEnum::BFSShortest(_) => {
}
PlanNodeEnum::AllPaths(_) => {
}
_ => {
}
}
}
fn hash_single_input<T: SingleInputNode>(&self, node: &T, hasher: &mut DefaultHasher) {
let input_fp = self.calculate_fingerprint(node.input());
input_fp.hash(hasher);
}
fn hash_binary_input<T: BinaryInputNode>(&self, node: &T, hasher: &mut DefaultHasher) {
let left_fp = self.calculate_fingerprint(node.left_input());
let right_fp = self.calculate_fingerprint(node.right_input());
left_fp.hash(hasher);
right_fp.hash(hasher);
}
}