use crate::plans::CorrelatedColumn;
use rustc_hash::FxHashMap;
use std::collections::hash_map::Entry;
pub const SUBQUERY_CORRELATED_PLACEHOLDER_PREFIX: &str = "__llkv_outer$";
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
struct OuterColumnKey {
column: String,
field_path: Vec<String>,
}
impl OuterColumnKey {
fn new(column: &str, field_path: &[String]) -> Self {
Self {
column: column.to_ascii_lowercase(),
field_path: field_path
.iter()
.map(|segment| segment.to_ascii_lowercase())
.collect(),
}
}
}
#[derive(Default)]
pub struct SubqueryCorrelatedColumnTracker {
placeholders: FxHashMap<OuterColumnKey, usize>,
columns: Vec<CorrelatedColumn>,
}
impl SubqueryCorrelatedColumnTracker {
#[inline]
pub fn new() -> Self {
Self::default()
}
pub fn placeholder_for_column_path(&mut self, column: &str, field_path: &[String]) -> String {
let key = OuterColumnKey::new(column, field_path);
match self.placeholders.entry(key) {
Entry::Occupied(existing) => subquery_correlated_placeholder(*existing.get()),
Entry::Vacant(slot) => {
let index = self.columns.len();
let placeholder = subquery_correlated_placeholder(index);
self.columns.push(CorrelatedColumn {
placeholder: placeholder.clone(),
column: column.to_string(),
field_path: field_path.to_vec(),
});
slot.insert(index);
placeholder
}
}
}
#[inline]
pub fn into_columns(self) -> Vec<CorrelatedColumn> {
self.columns
}
}
#[inline]
pub fn subquery_correlated_placeholder(index: usize) -> String {
format!("{SUBQUERY_CORRELATED_PLACEHOLDER_PREFIX}{index}")
}
pub enum SubqueryCorrelatedTracker<'a> {
Active(&'a mut SubqueryCorrelatedColumnTracker),
Inactive,
}
impl<'a> SubqueryCorrelatedTracker<'a> {
#[inline]
pub fn from_option(tracker: Option<&'a mut SubqueryCorrelatedColumnTracker>) -> Self {
match tracker {
Some(inner) => SubqueryCorrelatedTracker::Active(inner),
None => SubqueryCorrelatedTracker::Inactive,
}
}
#[inline]
pub fn placeholder_for_column_path(
&mut self,
column: &str,
field_path: &[String],
) -> Option<String> {
match self {
SubqueryCorrelatedTracker::Active(tracker) => {
Some(tracker.placeholder_for_column_path(column, field_path))
}
SubqueryCorrelatedTracker::Inactive => None,
}
}
#[inline]
pub fn reborrow(&mut self) -> SubqueryCorrelatedTracker<'_> {
match self {
SubqueryCorrelatedTracker::Active(tracker) => {
SubqueryCorrelatedTracker::Active(tracker)
}
SubqueryCorrelatedTracker::Inactive => SubqueryCorrelatedTracker::Inactive,
}
}
#[inline]
pub fn is_active(&self) -> bool {
matches!(self, SubqueryCorrelatedTracker::Active(_))
}
}