linkrs_query/cache/plan_cache/
key.rs1use crate::planning::plan::execution_plan::PartitionSpec;
2use std::hash::Hasher;
3
4#[derive(Debug, Clone)]
6pub struct ParamPosition {
7 pub index: usize,
9 pub name: Option<String>,
11 pub position: usize,
13 pub expected_type: Option<linkrs_core::types::DataType>,
15}
16
17#[derive(Debug, Clone, Default)]
19pub struct PlanCachePutContext {
20 pub dependent_tables: Vec<String>,
21 pub space_name: Option<String>,
22 pub schema_version: Option<u64>,
23 pub index_version: Option<u64>,
24 pub is_dml: bool,
25 pub is_transaction: bool,
26 pub optimizer_version: u64,
27 pub planning_config_hash: u64,
28}
29
30#[derive(Debug, Clone, Default, PartialEq, Eq)]
32pub struct PlanCacheContext {
33 pub space_name: Option<String>,
34 pub schema_version: Option<u64>,
35 pub index_version: Option<u64>,
36 pub param_type_signature: Option<u64>,
37 pub optimizer_version: u64,
38 pub planning_config_hash: u64,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash)]
63pub struct PlanCacheKey {
64 pub hash: u64,
66 pub(crate) query_text: String,
68 pub(crate) partition_fingerprint: Option<u64>,
71 pub(crate) space_name: Option<String>,
73 pub(crate) schema_version: Option<u64>,
75 pub(crate) param_type_signature: Option<u64>,
78 pub(crate) index_version: Option<u64>,
80 pub(crate) optimizer_version: u64,
81 pub(crate) planning_config_hash: u64,
82}
83
84impl PlanCacheKey {
85 pub fn from_query(query: &str) -> Self {
87 let hash = Self::hash_query(query);
88 Self {
89 hash,
90 query_text: query.to_string(),
91 partition_fingerprint: None,
92 space_name: None,
93 schema_version: None,
94 param_type_signature: None,
95 index_version: None,
96 optimizer_version: 0,
97 planning_config_hash: 0,
98 }
99 }
100
101 pub fn from_query_with_space(
107 query: &str,
108 space_name: Option<String>,
109 schema_version: Option<u64>,
110 ) -> Self {
111 Self::from_query_with_full_context(query, space_name, schema_version, None, None)
112 }
113
114 pub fn from_query_with_full_context(
116 query: &str,
117 space_name: Option<String>,
118 schema_version: Option<u64>,
119 param_type_signature: Option<u64>,
120 index_version: Option<u64>,
121 ) -> Self {
122 Self::from_query_with_context(
123 query,
124 PlanCacheContext {
125 space_name,
126 schema_version,
127 index_version,
128 param_type_signature,
129 ..Default::default()
130 },
131 )
132 }
133
134 pub fn from_query_with_context(query: &str, context: PlanCacheContext) -> Self {
135 use std::hash::Hash;
136
137 let mut hasher = Self::hasher();
138 Self::normalize_query(query).hash(&mut hasher);
139 if let Some(ref name) = context.space_name {
140 name.hash(&mut hasher);
141 }
142 context.schema_version.hash(&mut hasher);
143 context.param_type_signature.hash(&mut hasher);
144 context.index_version.hash(&mut hasher);
145 context.optimizer_version.hash(&mut hasher);
146 context.planning_config_hash.hash(&mut hasher);
147 let hash = hasher.finish();
148
149 Self {
150 hash,
151 query_text: query.to_string(),
152 partition_fingerprint: None,
153 space_name: context.space_name,
154 schema_version: context.schema_version,
155 param_type_signature: context.param_type_signature,
156 index_version: context.index_version,
157 optimizer_version: context.optimizer_version,
158 planning_config_hash: context.planning_config_hash,
159 }
160 }
161
162 pub fn from_query_with_partition(query: &str, spec: &PartitionSpec) -> Self {
165 Self::from_query_with_partition_and_context(query, spec, PlanCacheContext::default())
166 }
167
168 pub fn from_query_with_partition_and_context(
173 query: &str,
174 spec: &PartitionSpec,
175 context: PlanCacheContext,
176 ) -> Self {
177 use std::hash::Hash;
178
179 let fp = Self::compute_fingerprint(spec);
180
181 let mut hasher = Self::hasher();
182 Self::normalize_query(query).hash(&mut hasher);
183 fp.hash(&mut hasher);
184 if let Some(ref name) = context.space_name {
185 name.hash(&mut hasher);
186 }
187 context.schema_version.hash(&mut hasher);
188 context.param_type_signature.hash(&mut hasher);
189 context.index_version.hash(&mut hasher);
190 context.optimizer_version.hash(&mut hasher);
191 context.planning_config_hash.hash(&mut hasher);
192 let hash = hasher.finish();
193
194 Self {
195 hash,
196 query_text: query.to_string(),
197 partition_fingerprint: Some(fp),
198 space_name: context.space_name,
199 schema_version: context.schema_version,
200 param_type_signature: context.param_type_signature,
201 index_version: context.index_version,
202 optimizer_version: context.optimizer_version,
203 planning_config_hash: context.planning_config_hash,
204 }
205 }
206
207 fn hash_query(query: &str) -> u64 {
208 use std::hash::Hash;
209 let mut hasher = Self::hasher();
210 Self::normalize_query(query).hash(&mut hasher);
211 hasher.finish()
212 }
213
214 fn normalize_query(query: &str) -> String {
220 query.split_whitespace().collect::<Vec<_>>().join(" ")
221 }
222
223 fn hasher() -> std::collections::hash_map::DefaultHasher {
224 std::collections::hash_map::DefaultHasher::new()
225 }
226
227 fn compute_fingerprint(spec: &PartitionSpec) -> u64 {
231 use std::hash::Hash;
232 let mut hasher = Self::hasher();
233 for range in spec.ranges() {
234 range.start.hash(&mut hasher);
235 range.end.hash(&mut hasher);
236 }
237 spec.source().to_string().hash(&mut hasher);
238 spec.layout_version().hash(&mut hasher);
239 hasher.finish()
240 }
241
242 pub fn verify_query(&self, query: &str) -> bool {
244 self.query_text == query
245 }
246
247 pub fn query_text(&self) -> &str {
249 &self.query_text
250 }
251
252 pub fn has_partition_fingerprint(&self) -> bool {
254 self.partition_fingerprint.is_some()
255 }
256
257 pub fn space_name(&self) -> Option<&str> {
259 self.space_name.as_deref()
260 }
261
262 pub fn schema_version(&self) -> Option<u64> {
264 self.schema_version
265 }
266}