1use std::fmt::Display as _;
16use std::sync::Arc;
17
18use itertools::Itertools;
19use mz_expr::explain::{HumanizedExplain, HumanizedExpr, HumanizerMode};
20use mz_expr::{
21 BinaryFunc, Columns, Eval, EvalError, MapFilterProject, MfpPlan, MirScalarExpr,
22 OptimizableExpr, SafeMfpPlan, UnaryFunc, UnmaterializableFunc, VariadicFunc,
23};
24use mz_ore::str::separated;
25use mz_ore::treat_as_equal::TreatAsEqual;
26use mz_repr::explain::ScalarOps;
27use mz_repr::{Datum, ReprColumnType, ReprScalarType, Row, RowArena};
28use serde::{Deserialize, Serialize};
29
30#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
33pub enum LirScalarExpr {
34 Column(usize, TreatAsEqual<Option<Arc<str>>>),
36 Literal(Result<Row, EvalError>, ReprColumnType),
39 CallUnary {
41 func: UnaryFunc,
43 expr: Box<LirScalarExpr>,
45 },
46 CallBinary {
48 func: BinaryFunc,
50 expr1: Box<LirScalarExpr>,
52 expr2: Box<LirScalarExpr>,
54 },
55 CallVariadic {
57 func: VariadicFunc,
59 exprs: Vec<LirScalarExpr>,
61 },
62 If {
69 cond: Box<LirScalarExpr>,
71 then: Box<LirScalarExpr>,
73 els: Box<LirScalarExpr>,
75 },
76}
77
78impl LirScalarExpr {
79 pub fn column(c: usize) -> Self {
81 LirScalarExpr::Column(c, TreatAsEqual(None))
82 }
83
84 pub fn literal(res: Result<Datum, EvalError>, typ: ReprScalarType) -> Self {
86 let typ = ReprColumnType {
87 scalar_type: typ,
88 nullable: matches!(res, Ok(Datum::Null)),
89 };
90 let row = res.map(|datum| Row::pack_slice(&[datum]));
91 LirScalarExpr::Literal(row, typ)
92 }
93
94 pub fn literal_ok(datum: Datum, typ: ReprScalarType) -> Self {
96 LirScalarExpr::literal(Ok(datum), typ)
97 }
98
99 pub fn as_literal(&self) -> Option<Result<Datum<'_>, &EvalError>> {
102 if let LirScalarExpr::Literal(lit, _column_type) = self {
103 Some(lit.as_ref().map(|row| row.unpack_first()))
104 } else {
105 None
106 }
107 }
108
109 pub fn is_literal_true(&self) -> bool {
111 Some(Ok(Datum::True)) == self.as_literal()
112 }
113
114 pub fn as_literal_int64(&self) -> Option<i64> {
116 match self.as_literal() {
117 Some(Ok(Datum::Int64(i))) => Some(i),
118 _ => None,
119 }
120 }
121
122 pub fn call_unary<U: Into<UnaryFunc>>(self, func: U) -> Self {
124 LirScalarExpr::CallUnary {
125 func: func.into(),
126 expr: Box::new(self),
127 }
128 }
129
130 pub fn call_binary<B: Into<BinaryFunc>>(self, other: Self, func: B) -> Self {
132 LirScalarExpr::CallBinary {
133 func: func.into(),
134 expr1: Box::new(self),
135 expr2: Box::new(other),
136 }
137 }
138
139 pub fn visit_pre<F>(&self, mut f: F)
141 where
142 F: FnMut(&Self),
143 {
144 let mut worklist = vec![self];
145 while let Some(e) = worklist.pop() {
146 f(e);
147 worklist.extend(e.children().rev());
148 }
149 }
150
151 pub fn visit_pre_mut<F: FnMut(&mut Self)>(&mut self, mut f: F) {
153 let mut worklist = vec![self];
154 while let Some(expr) = worklist.pop() {
155 f(expr);
156 worklist.extend(expr.children_mut().rev());
157 }
158 }
159
160 pub fn children(&self) -> impl DoubleEndedIterator<Item = &Self> {
162 let mut first = None;
163 let mut second = None;
164 let mut third = None;
165 let mut variadic = None;
166
167 use LirScalarExpr::*;
168 match self {
169 Column(_, _) | Literal(_, _) => (),
170 CallUnary { expr, .. } => {
171 first = Some(&**expr);
172 }
173 CallBinary { expr1, expr2, .. } => {
174 first = Some(&**expr1);
175 second = Some(&**expr2);
176 }
177 CallVariadic { exprs, .. } => {
178 variadic = Some(exprs);
179 }
180 If { cond, then, els } => {
181 first = Some(&**cond);
182 second = Some(&**then);
183 third = Some(&**els);
184 }
185 }
186
187 first
188 .into_iter()
189 .chain(second)
190 .chain(third)
191 .chain(variadic.into_iter().flatten())
192 }
193
194 pub fn children_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Self> {
196 let mut first = None;
197 let mut second = None;
198 let mut third = None;
199 let mut variadic = None;
200
201 use LirScalarExpr::*;
202 match self {
203 Column(_, _) | Literal(_, _) => (),
204 CallUnary { expr, .. } => {
205 first = Some(&mut **expr);
206 }
207 CallBinary { expr1, expr2, .. } => {
208 first = Some(&mut **expr1);
209 second = Some(&mut **expr2);
210 }
211 CallVariadic { exprs, .. } => {
212 variadic = Some(exprs);
213 }
214 If { cond, then, els } => {
215 first = Some(&mut **cond);
216 second = Some(&mut **then);
217 third = Some(&mut **els);
218 }
219 }
220
221 first
222 .into_iter()
223 .chain(second)
224 .chain(third)
225 .chain(variadic.into_iter().flatten())
226 }
227}
228
229impl mz_expr::visit::VisitChildren<LirScalarExpr> for LirScalarExpr {
230 fn visit_children<F>(&self, f: F)
231 where
232 F: FnMut(&Self),
233 {
234 self.children().for_each(f);
235 }
236
237 fn visit_mut_children<F>(&mut self, f: F)
238 where
239 F: FnMut(&mut Self),
240 {
241 self.children_mut().for_each(f);
242 }
243
244 fn try_visit_children<F, E>(&self, mut f: F) -> Result<(), E>
245 where
246 F: FnMut(&Self) -> Result<(), E>,
247 {
248 for child in self.children() {
249 f(child)?;
250 }
251 Ok(())
252 }
253
254 fn try_visit_mut_children<F, E>(&mut self, mut f: F) -> Result<(), E>
255 where
256 F: FnMut(&mut Self) -> Result<(), E>,
257 {
258 use LirScalarExpr::*;
259 match self {
260 Column(_, _) | Literal(_, _) => (),
261 CallUnary { expr, .. } => f(expr)?,
262 CallBinary { expr1, expr2, .. } => {
263 f(expr1)?;
264 f(expr2)?;
265 }
266 CallVariadic { exprs, .. } => {
267 for expr in exprs {
268 f(expr)?;
269 }
270 }
271 If { cond, then, els } => {
272 f(cond)?;
273 f(then)?;
274 f(els)?;
275 }
276 }
277 Ok(())
278 }
279
280 fn children<'a>(&'a self) -> impl DoubleEndedIterator<Item = &'a LirScalarExpr>
281 where
282 LirScalarExpr: 'a,
283 {
284 LirScalarExpr::children(self)
285 }
286
287 fn children_mut<'a>(&'a mut self) -> impl DoubleEndedIterator<Item = &'a mut LirScalarExpr>
288 where
289 LirScalarExpr: 'a,
290 {
291 LirScalarExpr::children_mut(self)
292 }
293}
294
295impl Columns for LirScalarExpr {
296 fn column(c: usize) -> Self {
297 LirScalarExpr::Column(c, TreatAsEqual(None))
298 }
299
300 fn visit_columns<F>(&mut self, mut action: F)
304 where
305 F: FnMut(&mut usize),
306 {
307 self.visit_pre_mut(|e| {
308 if let LirScalarExpr::Column(col, _) = e {
309 action(col);
310 }
311 });
312 }
313
314 fn is_column(&self) -> bool {
315 matches!(self, LirScalarExpr::Column(_, _))
316 }
317
318 fn as_column(&self) -> Option<usize> {
319 if let LirScalarExpr::Column(i, _) = self {
320 Some(*i)
321 } else {
322 None
323 }
324 }
325
326 fn as_column_mut(&mut self) -> Option<&mut usize> {
327 if let LirScalarExpr::Column(i, _) = self {
328 Some(i)
329 } else {
330 None
331 }
332 }
333
334 fn support_into(&self, support: &mut std::collections::BTreeSet<usize>) {
335 self.visit_pre(|e| {
336 if let LirScalarExpr::Column(i, _) = e {
337 support.insert(*i);
338 }
339 });
340 }
341}
342
343impl Eval for LirScalarExpr {
344 fn eval<'a>(
345 &'a self,
346 datums: &[Datum<'a>],
347 temp_storage: &'a RowArena,
348 ) -> Result<Datum<'a>, EvalError> {
349 use LirScalarExpr::*;
350 match self {
351 Column(index, _name) => Ok(datums[*index]),
352 Literal(res, _column_type) => match res {
353 Ok(row) => Ok(row.unpack_first()),
354 Err(e) => Err(e.clone()),
355 },
356 CallUnary { func, expr } => func.eval(datums, temp_storage, expr.as_ref()),
357 CallBinary { func, expr1, expr2 } => {
358 func.eval(datums, temp_storage, &[expr1.as_ref(), expr2.as_ref()])
359 }
360 CallVariadic { func, exprs } => func.eval(datums, temp_storage, exprs.as_slice()),
361 If { cond, then, els } => match cond.eval(datums, temp_storage)? {
362 Datum::True => then.eval(datums, temp_storage),
363 Datum::False | Datum::Null => els.eval(datums, temp_storage),
364 d => Err(EvalError::Internal(
365 format!("if condition evaluated to non-boolean datum: {:?}", d).into(),
366 )),
367 },
368 }
369 }
370
371 fn could_error(&self) -> bool {
373 use LirScalarExpr::*;
374 match self {
375 Column(_col, _name) => false,
376 Literal(row, ..) => row.is_err(),
377 CallUnary { func, expr } => func.could_error() || expr.could_error(),
378 CallBinary { func, expr1, expr2 } => {
379 func.could_error() || expr1.could_error() || expr2.could_error()
380 }
381 CallVariadic { func, exprs } => {
382 func.could_error() || exprs.iter().any(|e| e.could_error())
383 }
384 If { cond, then, els } => cond.could_error() || then.could_error() || els.could_error(),
385 }
386 }
387}
388
389impl OptimizableExpr for LirScalarExpr {
390 fn is_literal(&self) -> bool {
391 matches!(self, LirScalarExpr::Literal(_, _))
392 }
393
394 fn is_literal_err(&self) -> bool {
395 matches!(self, LirScalarExpr::Literal(Err(_), _))
396 }
397
398 fn contains_temporal(&self) -> bool {
399 false }
401
402 fn size(&self) -> usize {
403 let mut size = 0;
404 self.visit_pre(|_| size += 1);
405 size
406 }
407
408 fn eager_children(&mut self) -> Option<Vec<&mut Self>> {
409 if let LirScalarExpr::If { cond, .. } = self {
411 return Some(vec![cond]);
412 }
413
414 if let LirScalarExpr::CallVariadic {
416 func: VariadicFunc::Coalesce(_),
417 exprs,
418 } = self
419 {
420 return Some(exprs.iter_mut().take(1).collect());
421 }
422
423 None
425 }
426
427 fn equality_column_alias(predicate: &Self, expr: &Self, threshold: usize) -> Option<Self> {
428 if let LirScalarExpr::CallBinary {
429 func: BinaryFunc::Eq(_),
430 expr1,
431 expr2,
432 } = predicate
433 {
434 if let LirScalarExpr::Column(c, name) = &**expr1 {
435 if *c < threshold && &**expr2 == expr {
436 return Some(LirScalarExpr::Column(*c, name.clone()));
437 }
438 }
439 if let LirScalarExpr::Column(c, name) = &**expr2 {
440 if *c < threshold && &**expr1 == expr {
441 return Some(LirScalarExpr::Column(*c, name.clone()));
442 }
443 }
444 }
445 None
446 }
447
448 fn extract_temporal_bounds(temporal: Vec<Self>) -> Result<(Vec<Self>, Vec<Self>), String> {
449 if temporal.is_empty() {
450 Ok((Vec::new(), Vec::new()))
451 } else {
452 Err("LIR expressions do not support temporal predicates".into())
453 }
454 }
455}
456
457impl std::fmt::Debug for LirScalarExpr {
460 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
461 match self {
462 LirScalarExpr::Column(i, TreatAsEqual(Some(name))) => {
463 write!(f, "Column({i}, {name:?})")
464 }
465 LirScalarExpr::Column(i, TreatAsEqual(None)) => write!(f, "Column({i})"),
466 LirScalarExpr::Literal(lit, typ) => write!(f, "Literal({lit:?}, {typ:?})"),
467 LirScalarExpr::CallUnary { func, expr } => {
468 write!(f, "CallUnary({func:?}, {expr:?})")
469 }
470 LirScalarExpr::CallBinary { func, expr1, expr2 } => {
471 write!(f, "CallBinary({func:?}, {expr1:?}, {expr2:?})")
472 }
473 LirScalarExpr::CallVariadic { func, exprs } => {
474 write!(f, "CallVariadic({func:?}, {exprs:?})")
475 }
476 LirScalarExpr::If { cond, then, els } => {
477 write!(f, "If({cond:?}, {then:?}, {els:?})")
478 }
479 }
480 }
481}
482
483impl ScalarOps for LirScalarExpr {
484 fn match_col_ref(&self) -> Option<usize> {
485 match self {
486 LirScalarExpr::Column(c, _name) => Some(*c),
487 _ => None,
488 }
489 }
490
491 fn references(&self, column: usize) -> bool {
492 match self {
493 LirScalarExpr::Column(c, _name) => *c == column,
494 _ => false,
495 }
496 }
497}
498
499impl mz_expr::explain::HumanizeDisplay for LirScalarExpr {
500 fn humanize<'a, M: HumanizerMode>(
501 e: &HumanizedExpr<'a, Self, M>,
502 f: &mut std::fmt::Formatter<'_>,
503 ) -> std::fmt::Result {
504 use LirScalarExpr::*;
505
506 match e.expr {
507 Column(i, TreatAsEqual(None)) => {
508 e.child(i).fmt(f)
510 }
511 Column(i, TreatAsEqual(Some(name))) => {
512 e.child(&(i, name)).fmt(f)
514 }
515 Literal(row, _) => {
516 e.child(row).fmt(f)
518 }
519 CallUnary { func, expr } => {
520 if let UnaryFunc::Not(_) = *func {
521 if let CallUnary { func, expr } = expr.as_ref() {
522 if let Some(is) = func.is() {
523 let expr = e.child::<LirScalarExpr>(&*expr);
524 return write!(f, "({}) IS NOT {}", expr, is);
525 }
526 }
527 }
528 if let Some(is) = func.is() {
529 let expr = e.child::<LirScalarExpr>(&*expr);
530 write!(f, "({}) IS {}", expr, is)
531 } else {
532 let expr = e.child::<LirScalarExpr>(&*expr);
533 write!(f, "{}({})", func, expr)
534 }
535 }
536 CallBinary { func, expr1, expr2 } => {
537 let expr1 = e.child::<LirScalarExpr>(&*expr1);
538 let expr2 = e.child::<LirScalarExpr>(&*expr2);
539 if func.is_infix_op() {
540 write!(f, "({} {} {})", expr1, func, expr2)
541 } else {
542 write!(f, "{}({}, {})", func, expr1, expr2)
543 }
544 }
545 CallVariadic { func, exprs } => {
546 use VariadicFunc::*;
547 match func {
548 CaseLiteral(cl) => {
549 let input = e.child::<LirScalarExpr>(&exprs[0]);
550 write!(f, "case_lookup {}", input)?;
551 for entry in &cl.lookup {
552 let result = e.child::<LirScalarExpr>(&exprs[entry.expr_index]);
553 write!(f, " when ")?;
554 e.mode.humanize_datum(entry.literal.unpack_first(), f)?;
555 write!(f, " then {}", result)?;
556 }
557 let els = e.child::<LirScalarExpr>(exprs.last().unwrap());
558 write!(f, " else {} end", els)
559 }
560 ArrayCreate(..) => {
561 let exprs = exprs.iter().map(|expr| e.child(expr));
562 let exprs = separated(", ", exprs);
563 write!(f, "array[{}]", exprs)
564 }
565 ListCreate(..) => {
566 let exprs = exprs.iter().map(|expr| e.child(expr));
567 let exprs = separated(", ", exprs);
568 write!(f, "list[{}]", exprs)
569 }
570 RecordCreate(..) => {
571 let exprs = exprs.iter().map(|expr| e.child(expr));
572 let exprs = separated(", ", exprs);
573 write!(f, "row({})", exprs)
574 }
575 func if func.is_infix_op() && exprs.len() > 1 => {
576 let exprs = exprs.iter().map(|expr| e.child(expr));
577 let func = format!(" {} ", func);
578 let exprs = separated(&func, exprs);
579 write!(f, "({})", exprs)
580 }
581 func => {
582 let exprs = exprs.iter().map(|expr| e.child(expr));
583 let exprs = separated(", ", exprs);
584 write!(f, "{}({})", func, exprs)
585 }
586 }
587 }
588 If { cond, then, els } => {
589 let cond = e.child::<LirScalarExpr>(&*cond);
590 let then = e.child::<LirScalarExpr>(&*then);
591 let els = e.child::<LirScalarExpr>(&*els);
592 write!(f, "case when {} then {} else {} end", cond, then, els)
593 }
594 }
595 }
596}
597
598impl std::fmt::Display for LirScalarExpr {
599 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
600 let mode = HumanizedExplain::default();
601 std::fmt::Display::fmt(&mode.expr(self, None), f)
602 }
603}
604
605impl From<&LirScalarExpr> for MirScalarExpr {
606 fn from(value: &LirScalarExpr) -> Self {
607 use LirScalarExpr::*;
608 match value {
609 Column(c, treat_as_equal) => MirScalarExpr::Column(c.clone(), treat_as_equal.clone()),
610 Literal(row, repr_column_type) => {
611 MirScalarExpr::Literal(row.clone(), repr_column_type.clone())
612 }
613 CallUnary { func, expr } => MirScalarExpr::CallUnary {
614 func: func.clone(),
615 expr: Box::new(MirScalarExpr::from(expr.as_ref())),
616 },
617 CallBinary { func, expr1, expr2 } => MirScalarExpr::CallBinary {
618 func: func.clone(),
619 expr1: Box::new(MirScalarExpr::from(expr1.as_ref())),
620 expr2: Box::new(MirScalarExpr::from(expr2.as_ref())),
621 },
622 CallVariadic { func, exprs } => MirScalarExpr::CallVariadic {
623 func: func.clone(),
624 exprs: exprs.iter().map(MirScalarExpr::from).collect(),
625 },
626 If { cond, then, els } => MirScalarExpr::If {
627 cond: Box::new(MirScalarExpr::from(cond.as_ref())),
628 then: Box::new(MirScalarExpr::from(then.as_ref())),
629 els: Box::new(MirScalarExpr::from(els.as_ref())),
630 },
631 }
632 }
633}
634
635impl TryFrom<&MirScalarExpr> for LirScalarExpr {
636 type Error = Vec<UnmaterializableFunc>;
638
639 fn try_from(value: &MirScalarExpr) -> Result<Self, Self::Error> {
640 use MirScalarExpr::*;
641 match value {
642 Column(c, treat_as_equal) => Ok(LirScalarExpr::Column(*c, treat_as_equal.clone())),
643 Literal(row, repr_column_type) => Ok(LirScalarExpr::Literal(
644 row.clone(),
645 repr_column_type.clone(),
646 )),
647 CallUnary { func, expr } => Ok(LirScalarExpr::CallUnary {
648 func: func.clone(),
649 expr: Box::new(LirScalarExpr::try_from(expr.as_ref())?),
650 }),
651 CallBinary { func, expr1, expr2 } => {
652 match (
653 LirScalarExpr::try_from(expr1.as_ref()),
654 LirScalarExpr::try_from(expr2.as_ref()),
655 ) {
656 (Ok(expr1), Ok(expr2)) => Ok(LirScalarExpr::CallBinary {
657 func: func.clone(),
658 expr1: Box::new(expr1),
659 expr2: Box::new(expr2),
660 }),
661 (Ok(_), Err(e)) | (Err(e), Ok(_)) => Err(e),
662 (Err(mut e1), Err(mut e2)) => {
663 e1.append(&mut e2);
664 Err(e1)
665 }
666 }
667 }
668 CallVariadic { func, exprs } => {
669 let (exprs, errors): (Vec<LirScalarExpr>, Vec<Vec<UnmaterializableFunc>>) = exprs
670 .into_iter()
671 .map(LirScalarExpr::try_from)
672 .partition_result();
673
674 if errors.is_empty() {
675 Ok(LirScalarExpr::CallVariadic {
676 func: func.clone(),
677 exprs,
678 })
679 } else {
680 Err(errors.concat())
681 }
682 }
683 If { cond, then, els } => {
684 let cond = LirScalarExpr::try_from(cond.as_ref());
685 let then = LirScalarExpr::try_from(then.as_ref());
686 let els = LirScalarExpr::try_from(els.as_ref());
687
688 match (cond, then, els) {
689 (Ok(cond), Ok(then), Ok(els)) => Ok(LirScalarExpr::If {
690 cond: Box::new(cond),
691 then: Box::new(then),
692 els: Box::new(els),
693 }),
694 (Err(e), Ok(_), Ok(_)) | (Ok(_), Err(e), Ok(_)) | (Ok(_), Ok(_), Err(e)) => {
695 Err(e)
696 }
697 (Err(mut e1), Err(mut e2), Ok(_))
698 | (Err(mut e1), Ok(_), Err(mut e2))
699 | (Ok(_), Err(mut e1), Err(mut e2)) => {
700 e1.append(&mut e2);
701 Err(e1)
702 }
703 (Err(mut e1), Err(mut e2), Err(mut e3)) => {
704 e1.append(&mut e2);
705 e1.append(&mut e3);
706 Err(e1)
707 }
708 }
709 }
710 CallUnmaterializable(f) => Err(vec![f.clone()]),
711 }
712 }
713}
714
715pub fn mfp_mir_to_lir(mfp: MapFilterProject<MirScalarExpr>) -> MapFilterProject<LirScalarExpr> {
719 let expressions = lses_from_mses(&mfp.expressions);
720 let predicates = mfp
721 .predicates
722 .iter()
723 .map(|(pos, pred)| {
724 (
725 *pos,
726 LirScalarExpr::try_from(pred).expect("unmaterializable in MFP predicate"),
727 )
728 })
729 .collect();
730 MapFilterProject::<LirScalarExpr> {
731 expressions,
732 predicates,
733 projection: mfp.projection,
734 input_arity: mfp.input_arity,
735 }
736}
737
738pub fn safe_mfp_mir_to_lir(plan: SafeMfpPlan<MirScalarExpr>) -> SafeMfpPlan<LirScalarExpr> {
742 SafeMfpPlan::from_mfp(mfp_mir_to_lir(plan.into_mfp()))
743}
744
745pub fn mfp_mir_to_lir_plan(mfp: MapFilterProject<MirScalarExpr>) -> MfpPlan<LirScalarExpr> {
751 let plan = mfp.into_plan().expect("MFP planning failed");
752 let (safe, lower, upper) = plan.into_parts();
753 MfpPlan::from_parts(
754 safe_mfp_mir_to_lir(safe),
755 lses_from_mses(&lower),
756 lses_from_mses(&upper),
757 )
758}
759
760pub fn mfp_plan_lir_to_mir(plan: MfpPlan<LirScalarExpr>) -> MfpPlan<MirScalarExpr> {
762 let (safe, lower, upper) = plan.into_parts();
763
764 let mfp = safe.into_mfp();
765 let expressions = mfp.expressions.iter().map(MirScalarExpr::from).collect();
766 let predicates = mfp
767 .predicates
768 .iter()
769 .map(|(pos, pred)| (*pos, MirScalarExpr::from(pred)))
770 .collect();
771 let mir_mfp = MapFilterProject::<MirScalarExpr> {
772 expressions,
773 predicates,
774 projection: mfp.projection,
775 input_arity: mfp.input_arity,
776 };
777
778 let lower = lower.iter().map(MirScalarExpr::from).collect();
779 let upper = upper.iter().map(MirScalarExpr::from).collect();
780 MfpPlan::from_parts(SafeMfpPlan::from_mfp(mir_mfp), lower, upper)
781}
782
783pub(crate) fn lses_from_mses<'a>(
790 exprs: impl IntoIterator<Item = &'a MirScalarExpr>,
791) -> Vec<LirScalarExpr> {
792 match exprs
793 .into_iter()
794 .map(LirScalarExpr::try_from)
795 .collect::<Result<Vec<LirScalarExpr>, _>>()
796 {
797 Ok(exprs) => exprs,
798 Err(funcs) => {
799 panic!(
800 "unmaterializable functions cannot be translated to LirScalarExpr: {}",
801 separated(", ", &funcs)
802 )
803 }
804 }
805}