use crate::planning::plan::execution_plan::PartitionSpec;
use std::hash::Hasher;
#[derive(Debug, Clone)]
pub struct ParamPosition {
pub index: usize,
pub name: Option<String>,
pub position: usize,
pub expected_type: Option<linkrs_core::types::DataType>,
}
#[derive(Debug, Clone, Default)]
pub struct PlanCachePutContext {
pub dependent_tables: Vec<String>,
pub space_name: Option<String>,
pub schema_version: Option<u64>,
pub index_version: Option<u64>,
pub is_dml: bool,
pub is_transaction: bool,
pub optimizer_version: u64,
pub planning_config_hash: u64,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PlanCacheContext {
pub space_name: Option<String>,
pub schema_version: Option<u64>,
pub index_version: Option<u64>,
pub param_type_signature: Option<u64>,
pub optimizer_version: u64,
pub planning_config_hash: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PlanCacheKey {
pub hash: u64,
pub(crate) query_text: String,
pub(crate) partition_fingerprint: Option<u64>,
pub(crate) space_name: Option<String>,
pub(crate) schema_version: Option<u64>,
pub(crate) param_type_signature: Option<u64>,
pub(crate) index_version: Option<u64>,
pub(crate) optimizer_version: u64,
pub(crate) planning_config_hash: u64,
}
impl PlanCacheKey {
pub fn from_query(query: &str) -> Self {
let hash = Self::hash_query(query);
Self {
hash,
query_text: query.to_string(),
partition_fingerprint: None,
space_name: None,
schema_version: None,
param_type_signature: None,
index_version: None,
optimizer_version: 0,
planning_config_hash: 0,
}
}
pub fn from_query_with_space(
query: &str,
space_name: Option<String>,
schema_version: Option<u64>,
) -> Self {
Self::from_query_with_full_context(query, space_name, schema_version, None, None)
}
pub fn from_query_with_full_context(
query: &str,
space_name: Option<String>,
schema_version: Option<u64>,
param_type_signature: Option<u64>,
index_version: Option<u64>,
) -> Self {
Self::from_query_with_context(
query,
PlanCacheContext {
space_name,
schema_version,
index_version,
param_type_signature,
..Default::default()
},
)
}
pub fn from_query_with_context(query: &str, context: PlanCacheContext) -> Self {
use std::hash::Hash;
let mut hasher = Self::hasher();
Self::normalize_query(query).hash(&mut hasher);
if let Some(ref name) = context.space_name {
name.hash(&mut hasher);
}
context.schema_version.hash(&mut hasher);
context.param_type_signature.hash(&mut hasher);
context.index_version.hash(&mut hasher);
context.optimizer_version.hash(&mut hasher);
context.planning_config_hash.hash(&mut hasher);
let hash = hasher.finish();
Self {
hash,
query_text: query.to_string(),
partition_fingerprint: None,
space_name: context.space_name,
schema_version: context.schema_version,
param_type_signature: context.param_type_signature,
index_version: context.index_version,
optimizer_version: context.optimizer_version,
planning_config_hash: context.planning_config_hash,
}
}
pub fn from_query_with_partition(query: &str, spec: &PartitionSpec) -> Self {
Self::from_query_with_partition_and_context(query, spec, PlanCacheContext::default())
}
pub fn from_query_with_partition_and_context(
query: &str,
spec: &PartitionSpec,
context: PlanCacheContext,
) -> Self {
use std::hash::Hash;
let fp = Self::compute_fingerprint(spec);
let mut hasher = Self::hasher();
Self::normalize_query(query).hash(&mut hasher);
fp.hash(&mut hasher);
if let Some(ref name) = context.space_name {
name.hash(&mut hasher);
}
context.schema_version.hash(&mut hasher);
context.param_type_signature.hash(&mut hasher);
context.index_version.hash(&mut hasher);
context.optimizer_version.hash(&mut hasher);
context.planning_config_hash.hash(&mut hasher);
let hash = hasher.finish();
Self {
hash,
query_text: query.to_string(),
partition_fingerprint: Some(fp),
space_name: context.space_name,
schema_version: context.schema_version,
param_type_signature: context.param_type_signature,
index_version: context.index_version,
optimizer_version: context.optimizer_version,
planning_config_hash: context.planning_config_hash,
}
}
fn hash_query(query: &str) -> u64 {
use std::hash::Hash;
let mut hasher = Self::hasher();
Self::normalize_query(query).hash(&mut hasher);
hasher.finish()
}
fn normalize_query(query: &str) -> String {
query.split_whitespace().collect::<Vec<_>>().join(" ")
}
fn hasher() -> std::collections::hash_map::DefaultHasher {
std::collections::hash_map::DefaultHasher::new()
}
fn compute_fingerprint(spec: &PartitionSpec) -> u64 {
use std::hash::Hash;
let mut hasher = Self::hasher();
for range in spec.ranges() {
range.start.hash(&mut hasher);
range.end.hash(&mut hasher);
}
spec.source().to_string().hash(&mut hasher);
spec.layout_version().hash(&mut hasher);
hasher.finish()
}
pub fn verify_query(&self, query: &str) -> bool {
self.query_text == query
}
pub fn query_text(&self) -> &str {
&self.query_text
}
pub fn has_partition_fingerprint(&self) -> bool {
self.partition_fingerprint.is_some()
}
pub fn space_name(&self) -> Option<&str> {
self.space_name.as_deref()
}
pub fn schema_version(&self) -> Option<u64> {
self.schema_version
}
}