llkv_plan/
subquery_correlation.rs1use crate::plans::CorrelatedColumn;
10use rustc_hash::FxHashMap;
11use std::collections::hash_map::Entry;
12
13pub const SUBQUERY_CORRELATED_PLACEHOLDER_PREFIX: &str = "__llkv_outer$";
15
16#[derive(Clone, Debug, Eq, PartialEq, Hash)]
17struct OuterColumnKey {
18 column: String,
19 field_path: Vec<String>,
20}
21
22impl OuterColumnKey {
23 fn new(column: &str, field_path: &[String]) -> Self {
24 Self {
25 column: column.to_ascii_lowercase(),
26 field_path: field_path
27 .iter()
28 .map(|segment| segment.to_ascii_lowercase())
29 .collect(),
30 }
31 }
32}
33#[derive(Default)]
38pub struct SubqueryCorrelatedColumnTracker {
39 placeholders: FxHashMap<OuterColumnKey, usize>,
40 columns: Vec<CorrelatedColumn>,
41}
42
43impl SubqueryCorrelatedColumnTracker {
44 #[inline]
46 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn placeholder_for_column_path(&mut self, column: &str, field_path: &[String]) -> String {
52 let key = OuterColumnKey::new(column, field_path);
53 match self.placeholders.entry(key) {
54 Entry::Occupied(existing) => subquery_correlated_placeholder(*existing.get()),
55 Entry::Vacant(slot) => {
56 let index = self.columns.len();
57 let placeholder = subquery_correlated_placeholder(index);
58 self.columns.push(CorrelatedColumn {
59 placeholder: placeholder.clone(),
60 column: column.to_string(),
61 field_path: field_path.to_vec(),
62 });
63 slot.insert(index);
64 placeholder
65 }
66 }
67 }
68
69 #[inline]
71 pub fn into_columns(self) -> Vec<CorrelatedColumn> {
72 self.columns
73 }
74}
75
76#[inline]
78pub fn subquery_correlated_placeholder(index: usize) -> String {
79 format!("{SUBQUERY_CORRELATED_PLACEHOLDER_PREFIX}{index}")
80}
81
82pub enum SubqueryCorrelatedTracker<'a> {
84 Active(&'a mut SubqueryCorrelatedColumnTracker),
85 Inactive,
86}
87
88impl<'a> SubqueryCorrelatedTracker<'a> {
89 #[inline]
91 pub fn from_option(tracker: Option<&'a mut SubqueryCorrelatedColumnTracker>) -> Self {
92 match tracker {
93 Some(inner) => SubqueryCorrelatedTracker::Active(inner),
94 None => SubqueryCorrelatedTracker::Inactive,
95 }
96 }
97
98 #[inline]
100 pub fn placeholder_for_column_path(
101 &mut self,
102 column: &str,
103 field_path: &[String],
104 ) -> Option<String> {
105 match self {
106 SubqueryCorrelatedTracker::Active(tracker) => {
107 Some(tracker.placeholder_for_column_path(column, field_path))
108 }
109 SubqueryCorrelatedTracker::Inactive => None,
110 }
111 }
112
113 #[inline]
115 pub fn reborrow(&mut self) -> SubqueryCorrelatedTracker<'_> {
116 match self {
117 SubqueryCorrelatedTracker::Active(tracker) => {
118 SubqueryCorrelatedTracker::Active(tracker)
119 }
120 SubqueryCorrelatedTracker::Inactive => SubqueryCorrelatedTracker::Inactive,
121 }
122 }
123
124 #[inline]
126 pub fn is_active(&self) -> bool {
127 matches!(self, SubqueryCorrelatedTracker::Active(_))
128 }
129}