delta_funnel/report/sql_server/
write_all.rs1use std::fmt;
2
3use crate::{
4 DeltaSourceReport, MssqlOutputWriteStatus, MssqlWorkflowWriteReport, PhaseTimingReport,
5 QueryExecutionProfile, QueryExecutionScope, support::sanitize_text_for_display,
6};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct WriteAllReport {
16 workflow: MssqlWorkflowWriteReport,
17 cache: WriteAllCacheReport,
18 sources: Vec<DeltaSourceReport>,
19 phase_timings: Vec<PhaseTimingReport>,
20}
21
22impl WriteAllReport {
23 pub(crate) fn new(
24 workflow: MssqlWorkflowWriteReport,
25 cache: WriteAllCacheReport,
26 sources: Vec<DeltaSourceReport>,
27 ) -> Self {
28 Self {
29 workflow,
30 cache,
31 sources,
32 phase_timings: Vec::new(),
33 }
34 }
35
36 pub(crate) fn with_phase_timings(mut self, phase_timings: Vec<PhaseTimingReport>) -> Self {
37 self.phase_timings = phase_timings;
38 self
39 }
40
41 #[must_use]
43 pub const fn workflow(&self) -> &MssqlWorkflowWriteReport {
44 &self.workflow
45 }
46
47 #[must_use]
49 pub const fn cache(&self) -> &WriteAllCacheReport {
50 &self.cache
51 }
52
53 #[must_use]
55 pub fn sources(&self) -> &[DeltaSourceReport] {
56 &self.sources
57 }
58
59 #[must_use]
61 pub fn phase_timings(&self) -> &[PhaseTimingReport] {
62 &self.phase_timings
63 }
64
65 #[must_use]
67 pub fn len(&self) -> usize {
68 self.workflow.len()
69 }
70
71 #[must_use]
73 pub fn is_empty(&self) -> bool {
74 self.workflow.is_empty()
75 }
76
77 #[must_use]
79 pub fn outputs(&self) -> &[MssqlOutputWriteStatus] {
80 self.workflow.outputs()
81 }
82
83 #[must_use]
85 pub fn all_succeeded(&self) -> bool {
86 self.workflow.all_succeeded()
87 }
88
89 #[must_use]
91 pub fn succeeded_count(&self) -> usize {
92 self.workflow.succeeded_count()
93 }
94
95 #[must_use]
97 pub fn failed_count(&self) -> usize {
98 self.workflow.failed_count()
99 }
100
101 #[must_use]
103 pub fn skipped_count(&self) -> usize {
104 self.workflow.skipped_count()
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
115pub enum WriteAllCacheReport {
116 Disabled,
118 NoCache {
120 reason: WriteAllNoCacheReason,
122 skipped_candidates: Vec<WriteAllCacheCandidateSkip>,
124 },
125 CacheAliases {
127 aliases: Vec<WriteAllCacheAliasReport>,
129 skipped_candidates: Vec<WriteAllCacheCandidateSkip>,
131 },
132}
133
134impl WriteAllCacheReport {
135 pub(crate) fn disabled() -> Self {
136 Self::Disabled
137 }
138
139 pub(crate) fn no_cache(
140 reason: WriteAllNoCacheReason,
141 skipped_candidates: Vec<WriteAllCacheCandidateSkip>,
142 ) -> Self {
143 Self::NoCache {
144 reason,
145 skipped_candidates,
146 }
147 }
148
149 pub(crate) fn cache_aliases(
150 aliases: Vec<WriteAllCacheAliasReport>,
151 skipped_candidates: Vec<WriteAllCacheCandidateSkip>,
152 ) -> Self {
153 Self::CacheAliases {
154 aliases,
155 skipped_candidates,
156 }
157 }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq)]
162pub enum WriteAllNoCacheReason {
163 FewerThanTwoOutputs,
165 NoSharedRegisteredDerivedAlias,
167 AmbiguousSharedDerivedAlias,
169}
170
171#[derive(Clone, PartialEq, Eq)]
178pub struct WriteAllCacheAliasReport {
179 table_id: u64,
180 alias: String,
181 output_indexes: Vec<usize>,
182 status: WriteAllCacheAliasStatus,
183 phase_timings: Vec<PhaseTimingReport>,
184 failed_phase: Option<String>,
185 execution_profile: Option<QueryExecutionProfile>,
186}
187
188impl WriteAllCacheAliasReport {
189 pub(crate) fn selected(
190 table_id: u64,
191 alias: impl Into<String>,
192 output_indexes: Vec<usize>,
193 ) -> Self {
194 Self {
195 table_id,
196 alias: alias.into(),
197 output_indexes,
198 status: WriteAllCacheAliasStatus::Selected,
199 phase_timings: Vec::new(),
200 failed_phase: None,
201 execution_profile: None,
202 }
203 }
204
205 pub(crate) fn executed(
206 table_id: u64,
207 alias: impl Into<String>,
208 output_indexes: Vec<usize>,
209 status: WriteAllCacheAliasStatus,
210 phase_timings: Vec<PhaseTimingReport>,
211 failed_phase: Option<&'static str>,
212 ) -> Self {
213 debug_assert_ne!(status, WriteAllCacheAliasStatus::Selected);
214 debug_assert_eq!(
215 status == WriteAllCacheAliasStatus::Failed,
216 failed_phase.is_some()
217 );
218 Self {
219 table_id,
220 alias: alias.into(),
221 output_indexes,
222 status,
223 phase_timings,
224 failed_phase: failed_phase.map(str::to_owned),
225 execution_profile: None,
226 }
227 }
228
229 #[must_use]
231 pub const fn table_id(&self) -> u64 {
232 self.table_id
233 }
234
235 #[must_use]
237 pub fn alias(&self) -> &str {
238 &self.alias
239 }
240
241 #[must_use]
243 pub fn output_indexes(&self) -> &[usize] {
244 &self.output_indexes
245 }
246
247 #[must_use]
249 pub const fn status(&self) -> WriteAllCacheAliasStatus {
250 self.status
251 }
252
253 #[must_use]
258 pub fn phase_timings(&self) -> &[PhaseTimingReport] {
259 &self.phase_timings
260 }
261
262 #[must_use]
264 pub fn failed_phase(&self) -> Option<&str> {
265 self.failed_phase.as_deref()
266 }
267
268 #[must_use]
274 pub const fn execution_profile(&self) -> Option<&QueryExecutionProfile> {
275 self.execution_profile.as_ref()
276 }
277
278 pub(crate) fn with_execution_profile(
279 mut self,
280 execution_profile: Option<QueryExecutionProfile>,
281 ) -> Self {
282 debug_assert!(execution_profile.as_ref().is_none_or(|profile| {
283 profile.scope() == QueryExecutionScope::WriteAllCacheAlias
284 && profile.delta_funnel_row_limit().is_none()
285 }));
286 self.execution_profile = execution_profile;
287 self
288 }
289}
290
291impl fmt::Debug for WriteAllCacheAliasReport {
292 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
293 formatter
294 .debug_struct("WriteAllCacheAliasReport")
295 .field("table_id", &self.table_id)
296 .field("alias", &sanitize_text_for_display(&self.alias))
297 .field("output_indexes", &self.output_indexes)
298 .field("status", &self.status)
299 .field("phase_timings", &self.phase_timings)
300 .field("failed_phase", &self.failed_phase)
301 .field("execution_profile", &self.execution_profile)
302 .finish()
303 }
304}
305
306#[derive(Debug, Clone, Copy, PartialEq, Eq)]
308pub enum WriteAllCacheAliasStatus {
309 Selected,
316 MaterializedAndRestored,
321 Failed,
323}
324
325#[derive(Debug, Clone, PartialEq, Eq)]
327pub struct WriteAllCacheFailure {
328 aliases: Vec<WriteAllCacheAliasReport>,
329 primary_failed_alias_table_id: Option<u64>,
330 workflow: Option<MssqlWorkflowWriteReport>,
331}
332
333impl WriteAllCacheFailure {
334 pub(crate) fn new(
335 aliases: Vec<WriteAllCacheAliasReport>,
336 primary_failed_alias_table_id: Option<u64>,
337 workflow: Option<MssqlWorkflowWriteReport>,
338 ) -> Self {
339 Self {
340 aliases,
341 primary_failed_alias_table_id,
342 workflow,
343 }
344 }
345
346 #[must_use]
348 pub fn aliases(&self) -> &[WriteAllCacheAliasReport] {
349 &self.aliases
350 }
351
352 #[must_use]
354 pub const fn primary_failed_alias_table_id(&self) -> Option<u64> {
355 self.primary_failed_alias_table_id
356 }
357
358 #[must_use]
360 pub const fn workflow(&self) -> Option<&MssqlWorkflowWriteReport> {
361 self.workflow.as_ref()
362 }
363}
364
365impl WriteAllCacheAliasStatus {
366 #[must_use]
368 pub const fn as_str(self) -> &'static str {
369 match self {
370 Self::Selected => "selected",
371 Self::MaterializedAndRestored => "materialized_and_restored",
372 Self::Failed => "failed",
373 }
374 }
375}
376
377impl fmt::Display for WriteAllCacheAliasStatus {
378 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
379 formatter.write_str(self.as_str())
380 }
381}
382
383#[derive(Clone, PartialEq, Eq)]
385pub struct WriteAllCacheCandidateSkip {
386 table_id: u64,
387 alias: String,
388 reason: WriteAllCacheCandidateSkipReason,
389}
390
391impl WriteAllCacheCandidateSkip {
392 pub(crate) fn new(
393 table_id: u64,
394 alias: impl Into<String>,
395 reason: WriteAllCacheCandidateSkipReason,
396 ) -> Self {
397 Self {
398 table_id,
399 alias: alias.into(),
400 reason,
401 }
402 }
403
404 #[must_use]
406 pub const fn table_id(&self) -> u64 {
407 self.table_id
408 }
409
410 #[must_use]
412 pub fn alias(&self) -> &str {
413 &self.alias
414 }
415
416 #[must_use]
418 pub const fn reason(&self) -> &WriteAllCacheCandidateSkipReason {
419 &self.reason
420 }
421}
422
423impl fmt::Debug for WriteAllCacheCandidateSkip {
424 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
425 formatter
426 .debug_struct("WriteAllCacheCandidateSkip")
427 .field("table_id", &self.table_id)
428 .field("alias", &sanitize_text_for_display(&self.alias))
429 .field("reason", &self.reason)
430 .finish()
431 }
432}
433
434#[derive(Debug, Clone, PartialEq, Eq)]
436pub enum WriteAllCacheCandidateSkipReason {
437 NotShared {
439 output_count: usize,
441 },
442 MissingSqlText,
444 IncompleteLineage,
446 CoveredByDeeperSharedAlias {
448 selected_table_id: u64,
450 },
451 AmbiguousDepth,
453 NotExplicitlySelected,
455}