1use itertools::Itertools;
17use mz_ore::id_gen::IdGen;
18use mz_ore::stack::{CheckedRecursion, RecursionGuard};
19use mz_repr::namespaces::{MZ_CATALOG_SCHEMA, MZ_UNSAFE_SCHEMA, PG_CATALOG_SCHEMA};
20use mz_sql_parser::ast::visit_mut::{self, VisitMut, VisitMutNode};
21use mz_sql_parser::ast::{
22 Expr, Function, FunctionArgs, HomogenizingFunction, Ident, IsExprConstruct, Op, OrderByExpr,
23 Query, Select, SelectItem, TableAlias, TableFactor, TableWithJoins, Value, WindowSpec,
24};
25use mz_sql_parser::ident;
26
27use crate::names::{Aug, PartialItemName, ResolvedDataType, ResolvedItemName};
28use crate::plan::{PlanError, StatementContext};
29use crate::session::vars::ENABLE_ANY_ALL_NULL_ARRAY_SEMANTICS;
30use crate::{ORDINALITY_COL_NAME, normalize};
31
32pub(crate) fn transform<N>(scx: &StatementContext, node: &mut N) -> Result<(), PlanError>
33where
34 N: for<'a> VisitMutNode<'a, Aug>,
35{
36 let mut func_rewriter = FuncRewriter::new(scx);
37 node.visit_mut(&mut func_rewriter);
38 func_rewriter.status?;
39
40 let mut desugarer = Desugarer::new(scx);
41 node.visit_mut(&mut desugarer);
42 desugarer.status
43}
44
45struct FuncRewriter<'a> {
66 scx: &'a StatementContext<'a>,
67 status: Result<(), PlanError>,
68 rewriting_table_factor: bool,
69}
70
71impl<'a> FuncRewriter<'a> {
72 fn new(scx: &'a StatementContext<'a>) -> FuncRewriter<'a> {
73 FuncRewriter {
74 scx,
75 status: Ok(()),
76 rewriting_table_factor: false,
77 }
78 }
79
80 fn resolve_known_valid_data_type(&self, name: &PartialItemName) -> ResolvedDataType {
81 let item = self
82 .scx
83 .catalog
84 .resolve_type(name)
85 .expect("data type known to be valid");
86 let full_name = self.scx.catalog.resolve_full_name(item.name());
87 ResolvedDataType::Named {
88 id: item.id(),
89 qualifiers: item.name().qualifiers.clone(),
90 full_name,
91 modifiers: vec![],
92 print_id: true,
93 }
94 }
95
96 fn int32_data_type(&self) -> ResolvedDataType {
97 self.resolve_known_valid_data_type(&PartialItemName {
98 database: None,
99 schema: Some(PG_CATALOG_SCHEMA.into()),
100 item: "int4".into(),
101 })
102 }
103
104 fn plan_divide(lhs: Expr<Aug>, rhs: Expr<Aug>) -> Expr<Aug> {
107 lhs.divide(Expr::Case {
108 operand: None,
109 conditions: vec![rhs.clone().equals(Expr::number("0"))],
110 results: vec![Expr::null()],
111 else_result: Some(Box::new(rhs)),
112 })
113 }
114
115 fn plan_agg(
116 &mut self,
117 name: ResolvedItemName,
118 expr: Expr<Aug>,
119 order_by: Vec<OrderByExpr<Aug>>,
120 filter: Option<Box<Expr<Aug>>>,
121 distinct: bool,
122 over: Option<WindowSpec<Aug>>,
123 ) -> Expr<Aug> {
124 if self.rewriting_table_factor && self.status.is_ok() {
125 self.status = Err(PlanError::Unstructured(
126 "aggregate functions are not supported in functions in FROM".to_string(),
127 ))
128 }
129 Expr::Function(Function {
130 name,
131 args: FunctionArgs::Args {
132 args: vec![expr],
133 order_by,
134 },
135 filter,
136 over,
137 distinct,
138 })
139 }
140
141 fn plan_avg(
142 &mut self,
143 expr: Expr<Aug>,
144 filter: Option<Box<Expr<Aug>>>,
145 distinct: bool,
146 over: Option<WindowSpec<Aug>>,
147 ) -> Expr<Aug> {
148 let sum = self
149 .plan_agg(
150 self.scx
151 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "sum"]),
152 expr.clone(),
153 vec![],
154 filter.clone(),
155 distinct,
156 over.clone(),
157 )
158 .call_unary(
159 self.scx
160 .dangerous_resolve_name(vec![MZ_UNSAFE_SCHEMA, "mz_avg_promotion"]),
161 );
162 let count = self.plan_agg(
163 self.scx
164 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "count"]),
165 expr,
166 vec![],
167 filter,
168 distinct,
169 over,
170 );
171 Self::plan_divide(sum, count)
172 }
173
174 fn plan_avg_internal_v1(
176 &mut self,
177 expr: Expr<Aug>,
178 filter: Option<Box<Expr<Aug>>>,
179 distinct: bool,
180 over: Option<WindowSpec<Aug>>,
181 ) -> Expr<Aug> {
182 let sum = self
183 .plan_agg(
184 self.scx
185 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "sum"]),
186 expr.clone(),
187 vec![],
188 filter.clone(),
189 distinct,
190 over.clone(),
191 )
192 .call_unary(
193 self.scx
194 .dangerous_resolve_name(vec![MZ_UNSAFE_SCHEMA, "mz_avg_promotion_internal_v1"]),
195 );
196 let count = self.plan_agg(
197 self.scx
198 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "count"]),
199 expr,
200 vec![],
201 filter,
202 distinct,
203 over,
204 );
205 Self::plan_divide(sum, count)
206 }
207
208 fn plan_variance(
209 &mut self,
210 expr: Expr<Aug>,
211 filter: Option<Box<Expr<Aug>>>,
212 distinct: bool,
213 sample: bool,
214 over: Option<WindowSpec<Aug>>,
215 ) -> Expr<Aug> {
216 let expr = expr.call_unary(
231 self.scx
232 .dangerous_resolve_name(vec![MZ_UNSAFE_SCHEMA, "mz_avg_promotion"]),
233 );
234 let expr_squared = expr.clone().multiply(expr.clone());
235 let sum_squares = if distinct {
236 let case_squared = |condition| Expr::Case {
253 operand: None,
254 conditions: vec![condition],
255 results: vec![expr_squared.clone()],
256 else_result: None,
257 };
258 let sum_squares_nonneg = self.plan_agg(
259 self.scx
260 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "sum"]),
261 case_squared(expr.clone().gt_eq(Expr::number("0"))),
262 vec![],
263 filter.clone(),
264 distinct,
265 over.clone(),
266 );
267 let sum_squares_neg = self.plan_agg(
268 self.scx
269 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "sum"]),
270 case_squared(expr.clone().lt(Expr::number("0"))),
271 vec![],
272 filter.clone(),
273 distinct,
274 over.clone(),
275 );
276 let coalesce_zero = |sum| Expr::HomogenizingFunction {
277 function: HomogenizingFunction::Coalesce,
278 exprs: vec![sum, Expr::number("0")],
279 };
280 coalesce_zero(sum_squares_nonneg).binop(Op::bare("+"), coalesce_zero(sum_squares_neg))
281 } else {
282 self.plan_agg(
283 self.scx
284 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "sum"]),
285 expr_squared,
286 vec![],
287 filter.clone(),
288 distinct,
289 over.clone(),
290 )
291 };
292 let sum = self.plan_agg(
293 self.scx
294 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "sum"]),
295 expr.clone(),
296 vec![],
297 filter.clone(),
298 distinct,
299 over.clone(),
300 );
301 let sum_squared = sum.clone().multiply(sum);
302 let count = self.plan_agg(
303 self.scx
304 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "count"]),
305 expr,
306 vec![],
307 filter,
308 distinct,
309 over,
310 );
311 let result = Self::plan_divide(
312 sum_squares.minus(Self::plan_divide(sum_squared, count.clone())),
313 if sample {
314 count.minus(Expr::number("1"))
315 } else {
316 count
317 },
318 );
319 let result_is_null = Expr::IsExpr {
334 expr: Box::new(result.clone()),
335 construct: IsExprConstruct::Null,
336 negated: false,
337 };
338 Expr::Case {
339 operand: None,
340 conditions: vec![result_is_null],
341 results: vec![Expr::Value(Value::Null)],
342 else_result: Some(Box::new(Expr::HomogenizingFunction {
343 function: HomogenizingFunction::Greatest,
344 exprs: vec![result, Expr::number("0")],
345 })),
346 }
347 }
348
349 fn plan_stddev(
350 &mut self,
351 expr: Expr<Aug>,
352 filter: Option<Box<Expr<Aug>>>,
353 distinct: bool,
354 sample: bool,
355 over: Option<WindowSpec<Aug>>,
356 ) -> Expr<Aug> {
357 self.plan_variance(expr, filter, distinct, sample, over)
358 .call_unary(
359 self.scx
360 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "sqrt"]),
361 )
362 }
363
364 fn plan_bool_and(
365 &mut self,
366 expr: Expr<Aug>,
367 filter: Option<Box<Expr<Aug>>>,
368 distinct: bool,
369 over: Option<WindowSpec<Aug>>,
370 ) -> Expr<Aug> {
371 let sum = self.plan_agg(
383 self.scx
384 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "sum"]),
385 expr.negate().cast(self.int32_data_type()),
386 vec![],
387 filter,
388 distinct,
389 over,
390 );
391 sum.equals(Expr::Value(Value::Number(0.to_string())))
392 }
393
394 fn plan_bool_or(
395 &mut self,
396 expr: Expr<Aug>,
397 filter: Option<Box<Expr<Aug>>>,
398 distinct: bool,
399 over: Option<WindowSpec<Aug>>,
400 ) -> Expr<Aug> {
401 let sum = self.plan_agg(
413 self.scx
414 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "sum"]),
415 expr.or(Expr::Value(Value::Boolean(false)))
416 .cast(self.int32_data_type()),
417 vec![],
418 filter,
419 distinct,
420 over,
421 );
422 sum.gt(Expr::Value(Value::Number(0.to_string())))
423 }
424
425 fn rewrite_function(&mut self, func: &Function<Aug>) -> Option<(Ident, Expr<Aug>)> {
426 if let Function {
427 name,
428 args: FunctionArgs::Args { args, order_by: _ },
429 filter,
430 distinct,
431 over,
432 } = func
433 {
434 let pg_catalog_id = self
435 .scx
436 .catalog
437 .resolve_schema(None, PG_CATALOG_SCHEMA)
438 .expect("pg_catalog schema exists")
439 .id();
440 let mz_catalog_id = self
441 .scx
442 .catalog
443 .resolve_schema(None, MZ_CATALOG_SCHEMA)
444 .expect("mz_catalog schema exists")
445 .id();
446 let name = match name {
447 ResolvedItemName::Item {
448 qualifiers,
449 full_name,
450 ..
451 } => {
452 if ![*pg_catalog_id, *mz_catalog_id].contains(&qualifiers.schema_spec) {
453 return None;
454 }
455 full_name.item.clone()
456 }
457 _ => unreachable!(),
458 };
459
460 let filter = filter.clone();
461 let distinct = *distinct;
462 let over = over.clone();
463 let expr = if args.len() == 1 {
464 let arg = args[0].clone();
465 match name.as_str() {
466 "avg_internal_v1" => self.plan_avg_internal_v1(arg, filter, distinct, over),
467 "avg" => self.plan_avg(arg, filter, distinct, over),
468 "variance" | "var_samp" => {
469 self.plan_variance(arg, filter, distinct, true, over)
470 }
471 "var_pop" => self.plan_variance(arg, filter, distinct, false, over),
472 "stddev" | "stddev_samp" => self.plan_stddev(arg, filter, distinct, true, over),
473 "stddev_pop" => self.plan_stddev(arg, filter, distinct, false, over),
474 "bool_and" => self.plan_bool_and(arg, filter, distinct, over),
475 "bool_or" => self.plan_bool_or(arg, filter, distinct, over),
476 _ => return None,
477 }
478 } else if args.len() == 2 {
479 let (lhs, rhs) = (args[0].clone(), args[1].clone());
480 match name.as_str() {
481 "mod" => lhs.modulo(rhs),
482 "pow" => Expr::call(
483 self.scx
484 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, "power"]),
485 vec![lhs, rhs],
486 ),
487 _ => return None,
488 }
489 } else {
490 return None;
491 };
492 Some((Ident::new_unchecked(name), expr))
493 } else {
494 None
495 }
496 }
497
498 fn rewrite_expr(&mut self, expr: &Expr<Aug>) -> Option<(Ident, Expr<Aug>)> {
499 match expr {
500 Expr::Function(function) => self.rewrite_function(function),
501 Expr::Identifier(ident) if ident.len() == 1 => {
505 let ident = normalize::ident(ident[0].clone());
506 let fn_ident = match ident.as_str() {
507 "current_role" => Some("current_user"),
508 "current_schema" | "current_timestamp" | "current_user" | "session_user"
509 | "current_catalog" => Some(ident.as_str()),
510 _ => None,
511 };
512 match fn_ident {
513 None => None,
514 Some(fn_ident) => {
515 let expr = Expr::call_nullary(
516 self.scx
517 .dangerous_resolve_name(vec![PG_CATALOG_SCHEMA, fn_ident]),
518 );
519 Some((Ident::new_unchecked(ident), expr))
520 }
521 }
522 }
523 _ => None,
524 }
525 }
526}
527
528impl<'ast> VisitMut<'ast, Aug> for FuncRewriter<'_> {
529 fn visit_select_item_mut(&mut self, item: &'ast mut SelectItem<Aug>) {
530 if let SelectItem::Expr { expr, alias: None } = item {
531 visit_mut::visit_expr_mut(self, expr);
532 if let Some((alias, expr)) = self.rewrite_expr(expr) {
533 *item = SelectItem::Expr {
534 expr,
535 alias: Some(alias),
536 };
537 }
538 } else {
539 visit_mut::visit_select_item_mut(self, item);
540 }
541 }
542
543 fn visit_table_with_joins_mut(&mut self, item: &'ast mut TableWithJoins<Aug>) {
544 visit_mut::visit_table_with_joins_mut(self, item);
545 match &mut item.relation {
546 TableFactor::Function {
547 function,
548 alias,
549 with_ordinality,
550 } => {
551 self.rewriting_table_factor = true;
552 if let Some((ident, expr)) = self.rewrite_function(function) {
555 let mut select = Select::default().project(SelectItem::Expr {
556 expr,
557 alias: Some(match &alias {
558 Some(TableAlias { name, columns, .. }) => {
559 columns.get(0).unwrap_or(name).clone()
560 }
561 None => ident,
562 }),
563 });
564
565 if *with_ordinality {
566 select = select.project(SelectItem::Expr {
567 expr: Expr::Value(Value::Number("1".into())),
568 alias: Some(ident!(ORDINALITY_COL_NAME)),
569 });
570 }
571
572 item.relation = TableFactor::Derived {
573 lateral: false,
574 subquery: Box::new(Query {
575 ctes: mz_sql_parser::ast::CteBlock::Simple(vec![]),
576 body: mz_sql_parser::ast::SetExpr::Select(Box::new(select)),
577 order_by: vec![],
578 limit: None,
579 offset: None,
580 }),
581 alias: alias.clone(),
582 }
583 }
584 self.rewriting_table_factor = false;
585 }
586 _ => {}
587 }
588 }
589
590 fn visit_expr_mut(&mut self, expr: &'ast mut Expr<Aug>) {
591 visit_mut::visit_expr_mut(self, expr);
592 if let Some((_name, new_expr)) = self.rewrite_expr(expr) {
593 *expr = new_expr;
594 }
595 }
596}
597
598struct Desugarer<'a> {
603 scx: &'a StatementContext<'a>,
604 status: Result<(), PlanError>,
605 id_gen: IdGen,
606 recursion_guard: RecursionGuard,
607}
608
609impl<'a> CheckedRecursion for Desugarer<'a> {
610 fn recursion_guard(&self) -> &RecursionGuard {
611 &self.recursion_guard
612 }
613}
614
615impl<'a, 'ast> VisitMut<'ast, Aug> for Desugarer<'a> {
616 fn visit_expr_mut(&mut self, expr: &'ast mut Expr<Aug>) {
617 self.visit_internal(Self::visit_expr_mut_internal, expr);
618 }
619}
620
621impl<'a> Desugarer<'a> {
622 fn visit_internal<F, X>(&mut self, f: F, x: X)
623 where
624 F: Fn(&mut Self, X) -> Result<(), PlanError>,
625 {
626 if self.status.is_ok() {
627 let status = self.checked_recur_mut(|d| f(d, x));
630 if self.status.is_ok() {
631 self.status = status;
632 }
633 }
634 }
635
636 fn new(scx: &'a StatementContext) -> Desugarer<'a> {
637 Desugarer {
638 scx,
639 status: Ok(()),
640 id_gen: Default::default(),
641 recursion_guard: RecursionGuard::with_limit(1024), }
643 }
644
645 fn visit_expr_mut_internal(&mut self, expr: &mut Expr<Aug>) -> Result<(), PlanError> {
646 while let Expr::Nested(e) = expr {
648 *expr = e.take();
649 }
650
651 if let Expr::Between {
654 expr: e,
655 low,
656 high,
657 negated,
658 } = expr
659 {
660 if *negated {
661 *expr = Expr::lt(*e.clone(), low.take()).or(e.take().gt(high.take()));
662 } else {
663 *expr = e.clone().gt_eq(low.take()).and(e.take().lt_eq(high.take()));
664 }
665 }
666
667 if let Expr::InList {
677 expr: e,
678 list,
679 negated,
680 } = expr
681 {
682 if let Expr::Row { .. } = &**e {
683 if *negated {
684 *expr = list
685 .drain(..)
686 .map(|r| e.clone().not_equals(r))
687 .reduce(|e1, e2| e1.and(e2))
688 .expect("list known to contain at least one element");
689 } else {
690 *expr = list
691 .drain(..)
692 .map(|r| e.clone().equals(r))
693 .reduce(|e1, e2| e1.or(e2))
694 .expect("list known to contain at least one element");
695 }
696 }
697 }
698
699 if let Expr::InSubquery {
702 expr: e,
703 subquery,
704 negated,
705 } = expr
706 {
707 if *negated {
708 *expr = Expr::AllSubquery {
709 left: Box::new(e.take()),
710 op: Op::bare("<>"),
711 right: Box::new(subquery.take()),
712 };
713 } else {
714 *expr = Expr::AnySubquery {
715 left: Box::new(e.take()),
716 op: Op::bare("="),
717 right: Box::new(subquery.take()),
718 };
719 }
720 }
721
722 if let Expr::AnyExpr { left, op, right } | Expr::AllExpr { left, op, right } = expr {
741 let binding = ident!("elem");
742
743 let array_is_null = self
745 .scx
746 .is_feature_flag_enabled(&ENABLE_ANY_ALL_NULL_ARRAY_SEMANTICS)
747 .then(|| Expr::IsExpr {
748 expr: Box::new((**right).clone()),
749 construct: IsExprConstruct::Null,
750 negated: false,
751 });
752
753 let subquery = Query::select(
754 Select::default()
755 .from(TableWithJoins {
756 relation: TableFactor::Function {
757 function: Function {
758 name: self
759 .scx
760 .dangerous_resolve_name(vec![MZ_CATALOG_SCHEMA, "unnest"]),
761 args: FunctionArgs::args(vec![right.take()]),
762 filter: None,
763 over: None,
764 distinct: false,
765 },
766 alias: Some(TableAlias {
767 name: ident!("_"),
768 columns: vec![binding.clone()],
769 strict: true,
770 }),
771 with_ordinality: false,
772 },
773 joins: vec![],
774 })
775 .project(SelectItem::Expr {
776 expr: Expr::Identifier(vec![binding]),
777 alias: None,
778 }),
779 );
780
781 let left = Box::new(left.take());
782
783 let op = op.clone();
784
785 let any_all = match expr {
786 Expr::AnyExpr { .. } => Expr::AnySubquery {
787 left,
788 op,
789 right: Box::new(subquery),
790 },
791 Expr::AllExpr { .. } => Expr::AllSubquery {
792 left,
793 op,
794 right: Box::new(subquery),
795 },
796 _ => unreachable!(),
797 };
798
799 *expr = match array_is_null {
800 Some(array_is_null) => Expr::Case {
801 operand: None,
802 conditions: vec![array_is_null],
803 results: vec![Expr::null()],
804 else_result: Some(Box::new(any_all)),
805 },
806 None => any_all,
807 };
808 }
809
810 if let Expr::AnySubquery { left, op, right } | Expr::AllSubquery { left, op, right } = expr
816 {
817 let left = match &mut **left {
818 Expr::Row { .. } => left.take(),
819 _ => Expr::Row {
820 exprs: vec![left.take()],
821 },
822 };
823
824 let arity = match &left {
825 Expr::Row { exprs } => exprs.len(),
826 _ => unreachable!(),
827 };
828
829 let bindings: Vec<_> = (0..arity)
830 .map(|col| {
833 let unique_id = self.id_gen.allocate_id();
834 Ident::new_unchecked(format!("right_col{col}_{unique_id}"))
835 })
836 .collect();
837
838 let subquery_unique_id = self.id_gen.allocate_id();
839 let subquery_name = Ident::new_unchecked(format!("subquery{subquery_unique_id}"));
841 let select = Select::default()
842 .from(TableWithJoins::subquery(
843 right.take(),
844 TableAlias {
845 name: subquery_name,
846 columns: bindings.clone(),
847 strict: true,
848 },
849 ))
850 .project(SelectItem::Expr {
851 expr: left
852 .binop(
853 op.clone(),
854 Expr::Row {
855 exprs: bindings
856 .into_iter()
857 .map(|b| Expr::Identifier(vec![b]))
858 .collect(),
859 },
860 )
861 .call_unary(self.scx.dangerous_resolve_name(match expr {
862 Expr::AnySubquery { .. } => vec![MZ_UNSAFE_SCHEMA, "mz_any"],
863 Expr::AllSubquery { .. } => vec![MZ_UNSAFE_SCHEMA, "mz_all"],
864 _ => unreachable!(),
865 })),
866 alias: None,
867 });
868
869 *expr = Expr::Subquery(Box::new(Query::select(select)));
870 }
871
872 if let Expr::Op {
888 op,
889 expr1: left,
890 expr2: Some(right),
891 } = expr
892 {
893 if let (Expr::Row { exprs: left }, Expr::Row { exprs: right }) =
894 (&mut **left, &mut **right)
895 {
896 if matches!(normalize::op(op)?, "=" | "<>" | "<" | "<=" | ">" | ">=") {
897 if left.len() != right.len() {
898 sql_bail!("unequal number of entries in row expressions");
899 }
900 if left.is_empty() {
901 assert!(right.is_empty());
902 sql_bail!("cannot compare rows of zero length");
903 }
904 }
905 match normalize::op(op)? {
906 "=" | "<>" => {
907 let mut pairs = left.iter_mut().zip_eq(right);
908 let mut new = pairs
909 .next()
910 .map(|(l, r)| l.take().equals(r.take()))
911 .expect("cannot compare rows of zero length");
912 for (l, r) in pairs {
913 new = l.take().equals(r.take()).and(new);
914 }
915 if normalize::op(op)? == "<>" {
916 new = new.negate();
917 }
918 *expr = new;
919 }
920 "<" | "<=" | ">" | ">=" => {
921 let strict_op = match normalize::op(op)? {
922 "<" | "<=" => "<",
923 ">" | ">=" => ">",
924 _ => unreachable!(),
925 };
926 let (l, r) = (left.last_mut().unwrap(), right.last_mut().unwrap());
927 let mut new = l.take().binop(op.clone(), r.take());
928 for (l, r) in left
929 .iter_mut()
930 .rev()
931 .zip_eq(right.into_iter().rev())
932 .skip(1)
933 {
934 new = l
935 .clone()
936 .binop(Op::bare(strict_op), r.clone())
937 .or(l.take().equals(r.take()).and(new));
938 }
939 *expr = new;
940 }
941 _ if left.len() == 1 && right.len() == 1 => {
942 let left = left.remove(0);
943 let right = right.remove(0);
944 *expr = left.binop(op.clone(), right);
945 }
946 _ => (),
947 }
948 }
949 }
950
951 visit_mut::visit_expr_mut(self, expr);
952 Ok(())
953 }
954}