1use std::collections::{HashMap, HashSet};
2
3use sql_fun_core::IVec;
4
5use crate::{
6 sem::{
7 AnalysisError, AnalysisProblem, FromClause, ParseContext, SemScalarExpr, TypeReference,
8 WithClause, analyze_scaler_expr,
9 scalar_expr::{AnalyzeScalarExpr, SemScalarExprNode},
10 },
11 syn::{ListOpt as _, Opt, ScanToken},
12};
13
14pub struct CaseExprAnalysisContext<'a> {
15 with_clause: &'a WithClause,
16 from_clause: &'a FromClause,
17 case_arg: Option<SemScalarExpr>,
18 nullabilities: Vec<Option<bool>>,
19 when_exprs: Vec<CaseWhen>,
20 result_types: HashSet<TypeReference>,
21 default_expr: Option<SemScalarExpr>,
22}
23
24impl<'a> CaseExprAnalysisContext<'a> {
25 fn new(with_clause: &'a WithClause, from_clause: &'a FromClause) -> Self {
26 Self {
27 with_clause,
28 from_clause,
29 case_arg: None,
30 nullabilities: Default::default(),
31 when_exprs: Default::default(),
32 result_types: Default::default(),
33 default_expr: None,
34 }
35 }
36}
37
38#[cfg(test)]
39mod test_case_expr_analysis_context_new {
40 use crate::sem::{FromClause, WithClause};
41
42 #[test]
43 pub fn test_case_expr_analysis_context_new() {
44 let with_clause = WithClause::default();
45 let from_clause = FromClause::default();
46 let _ = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
47 }
48}
49
50impl CaseExprAnalysisContext<'_> {
51 fn analyze_case_arg<TParseContext>(
52 &mut self,
53 context: TParseContext,
54 syn: &crate::syn::CaseExpr,
55 tokens: &IVec<ScanToken>,
56 ) -> Result<TParseContext, AnalysisError>
57 where
58 TParseContext: ParseContext,
59 {
60 if let Some(arg) = syn.get_arg().as_inner() {
61 let (arg_expr, new_context) =
62 analyze_scaler_expr(context, self.with_clause, self.from_clause, arg, tokens)?;
63 self.case_arg = Some(arg_expr);
64 Ok(new_context)
65 } else {
66 Ok(context)
67 }
68 }
69}
70
71#[cfg(test)]
72mod test_case_expr_analysis_context_analyze_case_arg {
73 use sql_fun_core::IVec;
74 use testresult::TestResult;
75
76 use crate::{
77 sem::{FromClause, WithClause},
78 syn::{Node, NodeInner, NodeList},
79 test_helpers::{SynBuilder, TestParseContext, test_context},
80 };
81
82 #[rstest::rstest]
83 fn test_analyze_case_arg_none(test_context: TestParseContext) -> TestResult {
84 let with_clause = WithClause::default();
85 let from_clause = FromClause::default();
86 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
87
88 let builder = SynBuilder::new();
89 let case_expr = builder.case_expr(Node::none(), NodeList::default(), Node::none());
90 let tokens = IVec::default();
91 let _new_context = acontext.analyze_case_arg(test_context, &case_expr, &tokens)?;
92 assert!(acontext.case_arg.is_none());
93 Ok(())
94 }
95
96 #[rstest::rstest]
97 fn test_analyze_case_arg_some(test_context: TestParseContext) -> TestResult {
98 let with_clause = WithClause::default();
99 let from_clause = FromClause::default();
100 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
101
102 let builder = SynBuilder::new();
103 let arg_expr = NodeInner::AConst(builder.const_int4(10));
104 let case_expr = builder.case_expr(arg_expr.into(), NodeList::default(), Node::none());
105 let tokens = IVec::default();
106 let _new_context = acontext.analyze_case_arg(test_context, &case_expr, &tokens)?;
107 assert!(acontext.case_arg.is_some());
108 Ok(())
109 }
110}
111
112impl CaseExprAnalysisContext<'_> {
113 fn analyze_case_when<TParseContext>(
114 &self,
115 mut context: TParseContext,
116 syn: crate::syn::CaseWhen,
117 tokens: &IVec<ScanToken>,
118 ) -> Result<(CaseWhen, TParseContext), AnalysisError>
119 where
120 TParseContext: ParseContext,
121 {
122 let Some(when_value) = syn.get_expr().as_inner() else {
123 AnalysisError::raise_unexpected_none("casewhen.expr")?
124 };
125 let Some(result) = syn.get_result().as_inner() else {
126 AnalysisError::raise_unexpected_none("casewhen.result")?
127 };
128 let (when_value, new_context) = analyze_scaler_expr(
129 context,
130 self.with_clause,
131 self.from_clause,
132 when_value,
133 tokens,
134 )?;
135 context = new_context;
136 let (result, new_context) =
137 analyze_scaler_expr(context, self.with_clause, self.from_clause, result, tokens)?;
138 context = new_context;
139 Ok((CaseWhen::new(when_value, result), context))
140 }
141}
142
143#[cfg(test)]
144mod test_case_expr_analysis_context_analyze_case_when {
145 use sql_fun_core::IVec;
146 use testresult::TestResult;
147
148 use crate::{
149 sem::{FromClause, ScalarConstExpr, SemScalarExpr, WithClause},
150 syn::NodeInner,
151 test_helpers::{SynBuilder, TestParseContext, test_context},
152 };
153
154 #[rstest::rstest]
155 fn test_analyze_case_when(test_context: TestParseContext) -> TestResult {
156 let with_clause = WithClause::default();
157 let from_clause = FromClause::default();
158 let acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
159
160 let builder = SynBuilder::new();
161 let expr = NodeInner::AConst(builder.const_int4(10));
162 let result = NodeInner::AConst(builder.const_int4(20));
163 let case_when = builder.case_when(expr.into(), result.into());
164 let tokens = IVec::default();
165 let (sem_case_when, _) = acontext.analyze_case_when(test_context, case_when, &tokens)?;
166 let SemScalarExpr::Const(ScalarConstExpr::Integer(expr_value)) = sem_case_when.when_value
167 else {
168 panic!("unexpected when_value {sem_case_when:?}")
169 };
170 assert_eq!(10, expr_value);
171 let SemScalarExpr::Const(ScalarConstExpr::Integer(result_value)) = sem_case_when.result
172 else {
173 panic!("unexpected result {sem_case_when:?}")
174 };
175 assert_eq!(20, result_value);
176 Ok(())
177 }
178}
179
180impl CaseExprAnalysisContext<'_> {
181 fn analyze_case_when_list<TParseContext>(
182 &mut self,
183 mut context: TParseContext,
184 args: Vec<crate::syn::CaseWhen>,
185 tokens: &IVec<ScanToken>,
186 ) -> Result<TParseContext, AnalysisError>
187 where
188 TParseContext: ParseContext,
189 {
190 for arg in args {
191 let (when_expr, new_context) = self.analyze_case_when(context, arg, tokens)?;
192 context = new_context;
193
194 if let Some(result_type) = when_expr.result.get_type() {
195 if result_type.is_dynamic() {
196 self.result_types.insert(result_type);
197 } else if let Some(result_type) = context.canonicalize_type_ref(&result_type) {
198 self.result_types.insert(result_type);
199 } else {
200 context.report_problem(AnalysisProblem::expression_type_not_known(
201 &when_expr.result,
202 ))?;
203 }
204 } else {
205 context.report_problem(AnalysisProblem::case_when_type_unknown(
206 when_expr.result.clone(),
207 ))?;
208 }
209
210 self.nullabilities.push(when_expr.result.is_not_null());
211 self.when_exprs.push(when_expr);
212 }
213 Ok(context)
214 }
215}
216
217#[cfg(test)]
218mod test_case_expr_analysis_context_analyze_case_when_list {
219 use crate::{
220 sem::{FromClause, PgBuiltInType, WithClause},
221 syn::NodeInner,
222 test_helpers::{SynBuilder, TestParseContext, test_context},
223 };
224 use sql_fun_core::IVec;
225 use testresult::TestResult;
226
227 #[rstest::rstest]
228 fn test_analyze_case_when_list(mut test_context: TestParseContext) -> TestResult {
229 test_context.setup_type(PgBuiltInType::int4());
230 test_context.setup_type(PgBuiltInType::text());
231
232 let with_clause = WithClause::default();
233 let from_clause = FromClause::default();
234 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
235
236 let builder = SynBuilder::new();
237
238 let expr = NodeInner::AConst(builder.const_int4(10));
239 let result = NodeInner::AConst(builder.const_int4(20));
240 let case_when_int = builder.case_when(expr.into(), result.into());
241
242 let expr = NodeInner::AConst(builder.const_sval("a"));
243 let result = NodeInner::AConst(builder.const_sval("b"));
244 let case_when_sval = builder.case_when(expr.into(), result.into());
245
246 let when_values = vec![case_when_int, case_when_sval];
247 let tokens = IVec::default();
248
249 let _ = acontext.analyze_case_when_list(test_context, when_values, &tokens)?;
250 assert_eq!(acontext.result_types.len(), 2);
251 assert_eq!(acontext.nullabilities.len(), 2);
252 assert_eq!(acontext.when_exprs.len(), 2);
253 Ok(())
254 }
255}
256
257impl CaseExprAnalysisContext<'_> {
258 fn analyze_default_expr<TParseContext>(
259 &mut self,
260 mut context: TParseContext,
261 syn: crate::syn::CaseExpr,
262 tokens: &IVec<ScanToken>,
263 ) -> Result<TParseContext, AnalysisError>
264 where
265 TParseContext: ParseContext,
266 {
267 let Some(def_result) = syn.get_defresult().as_inner() else {
268 return Ok(context);
269 };
270
271 let (default_expr, new_context) = analyze_scaler_expr(
272 context,
273 self.with_clause,
274 self.from_clause,
275 def_result,
276 tokens,
277 )?;
278 context = new_context;
279
280 if let Some(default_result_type) = default_expr.get_type() {
281 if default_result_type.is_dynamic() {
282 self.result_types.insert(default_result_type);
283 } else if let Some(default_result_type) =
284 context.canonicalize_type_ref(&default_result_type)
285 {
286 self.result_types.insert(default_result_type);
287 } else {
288 context
289 .report_problem(AnalysisProblem::expression_type_not_known(&default_expr))?;
290 }
291 } else {
292 context.report_problem(AnalysisProblem::case_when_type_unknown(
293 default_expr.clone(),
294 ))?;
295 }
296 self.nullabilities.push(default_expr.is_not_null());
297 self.default_expr = Some(default_expr);
298 Ok(context)
299 }
300}
301
302#[cfg(test)]
303mod test_case_expr_analysis_context_analyze_default_expr {
304 use crate::{
305 sem::{FromClause, PgBuiltInType, WithClause},
306 syn::{Node, NodeInner, NodeList},
307 test_helpers::{SynBuilder, TestParseContext, test_context},
308 };
309 use sql_fun_core::IVec;
310 use testresult::TestResult;
311
312 #[rstest::rstest]
313 fn test_analyze_default_expr_none(test_context: TestParseContext) -> TestResult {
314 let with_clause = WithClause::default();
315 let from_clause = FromClause::default();
316 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
317
318 let builder = SynBuilder::new();
319 let case_expr = builder.case_expr(Node::none(), NodeList::default(), Node::none());
320 let tokens = IVec::default();
321 let _new_context = acontext.analyze_default_expr(test_context, case_expr, &tokens)?;
322 assert!(acontext.default_expr.is_none());
323 Ok(())
324 }
325
326 #[rstest::rstest]
327 fn test_analyze_default_expr_some(mut test_context: TestParseContext) -> TestResult {
328 test_context.setup_type(PgBuiltInType::int4());
329 let with_clause = WithClause::default();
330 let from_clause = FromClause::default();
331 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
332 let builder = SynBuilder::new();
333 let expr = NodeInner::AConst(builder.const_int4(10));
334 let case_expr = builder.case_expr(Node::none(), NodeList::default(), expr.into());
335 let tokens = IVec::default();
336 let _new_context = acontext.analyze_default_expr(test_context, case_expr, &tokens)?;
337
338 assert!(acontext.default_expr.is_some());
339 Ok(())
340 }
341}
342
343impl CaseExprAnalysisContext<'_> {
344 fn cast_args<TParseContext>(
345 &mut self,
346 context: &mut TParseContext,
347 result_type: &TypeReference,
348 ) -> Result<Option<TypeReference>, AnalysisError>
349 where
350 TParseContext: ParseContext,
351 {
352 for when_expr in &mut self.when_exprs {
353 let Some(_ty) = when_expr.result.get_type() else {
354 continue;
355 };
356 SemScalarExpr::implicit_cast_if_require(context, result_type, &mut when_expr.result)?;
357 }
358 if let Some(default) = self.default_expr.as_mut() {
359 SemScalarExpr::implicit_cast_if_require(context, result_type, default)?;
360 }
361
362 Ok(Some(result_type.clone()))
363 }
364}
365
366impl CaseExprAnalysisContext<'_> {
367 fn type_fixup<TParseContext>(
368 &mut self,
369 context: &mut TParseContext,
370 ) -> Result<Option<TypeReference>, AnalysisError>
371 where
372 TParseContext: ParseContext,
373 {
374 if self.result_types.len() == 1 {
375 return Ok(self.result_types.iter().next().cloned());
376 }
377
378 let cast_candiates = self.enumerate_cast_candiates(context);
379 if cast_candiates.is_empty() {
380 context.report_problem(AnalysisProblem::incompatible_case_values(
381 self.result_types.clone(),
382 ))?;
383 Ok(None)
384 } else if cast_candiates.len() == 1 {
385 let Some((result_type, _casts)) = cast_candiates.into_iter().next() else {
386 AnalysisError::raise_unexpected_none("cast candiates")?
387 };
388 Ok(self.cast_args(context, &result_type)?)
389 } else {
390 let mut implicit_castable = Vec::new();
391 for (ty, _casts) in cast_candiates {
392 if self
393 .result_types
394 .iter()
395 .all(|t| context.get_implicit_cast(t, &ty).is_some())
396 {
397 implicit_castable.push(ty);
398 }
399 }
400
401 if implicit_castable.len() == 1 {
402 Ok(implicit_castable.into_iter().next())
403 } else {
404 todo!("{implicit_castable:#?}");
405 }
406 }
407 }
408}
409
410#[cfg(test)]
411mod test_case_expr_analysis_context_type_fixup {
412 use sql_fun_core::IVec;
413 use testresult::TestResult;
414
415 use super::SemScalarExprNode;
416 use crate::{
417 sem::{CastContext, CastDefinition, FromClause, PgBuiltInType, SemScalarExpr, WithClause},
418 syn::NodeInner,
419 test_helpers::{SynBuilder, TestParseContext, test_context},
420 };
421
422 #[rstest::rstest]
423 fn test_type_fixup_with_implicit_cast(mut test_context: TestParseContext) -> TestResult {
424 test_context.setup_type(PgBuiltInType::int4());
425 test_context.setup_type(PgBuiltInType::text());
426 let int4 = PgBuiltInType::int4();
427 let text = PgBuiltInType::text();
428 test_context.set_get_implicit_cast_result(
429 &int4,
430 &text,
431 Some(CastDefinition::new(CastContext::Implicit)),
432 );
433
434 let with_clause = WithClause::default();
435 let from_clause = FromClause::default();
436 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
437
438 let builder = SynBuilder::new();
439 let expr = NodeInner::AConst(builder.const_int4(1));
440 let result = NodeInner::AConst(builder.const_int4(10));
441 let when_int = builder.case_when(expr.into(), result.into());
442
443 let expr = NodeInner::AConst(builder.const_sval("a"));
444 let result = NodeInner::AConst(builder.const_sval("b"));
445 let when_text = builder.case_when(expr.into(), result.into());
446
447 let tokens = IVec::default();
448 let mut context =
449 acontext.analyze_case_when_list(test_context, vec![when_int, when_text], &tokens)?;
450 let result_type = acontext.type_fixup(&mut context)?;
451 assert_eq!(Some(text.clone()), result_type);
452
453 match &acontext.when_exprs[0].result {
454 SemScalarExpr::ImplicitCast(cast_expr) => {
455 assert_eq!(Some(text.clone()), cast_expr.get_type());
456 }
457 other => panic!("expected implicit cast, got {other:?}"),
458 }
459
460 match &acontext.when_exprs[1].result {
461 SemScalarExpr::Const(_) => {}
462 other => panic!("expected const, got {other:?}"),
463 }
464 Ok(())
465 }
466
467 #[rstest::rstest]
468 fn test_type_fixup_no_cast_candidates(mut test_context: TestParseContext) -> TestResult {
469 let int4 = PgBuiltInType::int4();
470 let text = PgBuiltInType::text();
471 test_context.setup_type(int4.clone());
472 test_context.setup_type(text.clone());
473
474 let with_clause = WithClause::default();
475 let from_clause = FromClause::default();
476 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
477 acontext.result_types.insert(int4);
478 acontext.result_types.insert(text);
479
480 let result = acontext.type_fixup(&mut test_context)?;
481 assert!(result.is_none());
482 assert_eq!(1, test_context.reported_problem_count());
483 Ok(())
484 }
485}
486
487impl CaseExprAnalysisContext<'_> {
488 fn enumerate_cast_candiates<TParseContext>(
489 &mut self,
490 context: &mut TParseContext,
491 ) -> HashMap<TypeReference, Vec<crate::sem::CastDefinition>>
492 where
493 TParseContext: ParseContext,
494 {
495 let mut cast_candiates = HashMap::new();
496 for ty1 in self.result_types.clone() {
497 for ty2 in self.result_types.clone() {
498 if ty1 == ty2 {
499 continue;
500 }
501 let Some(cast) = context.get_implicit_cast(&ty1, &ty2) else {
502 continue;
503 };
504 cast_candiates
505 .entry(ty2)
506 .and_modify(|v: &mut Vec<_>| v.push(cast.clone()))
507 .or_insert(vec![cast.clone()]);
508 }
509 }
510 cast_candiates
511 }
512}
513
514#[cfg(test)]
515mod test_case_expr_analysis_context_enumerate_cast_candiates {
516 use testresult::TestResult;
517
518 use crate::{
519 sem::{CastContext, CastDefinition, FromClause, PgBuiltInType, WithClause},
520 test_helpers::{TestParseContext, test_context},
521 };
522
523 #[rstest::rstest]
524 fn test_enumerate_cast_candiates_single(mut test_context: TestParseContext) -> TestResult {
525 let int4 = PgBuiltInType::int4();
526 let text = PgBuiltInType::text();
527 test_context.setup_type(int4.clone());
528 test_context.setup_type(text.clone());
529 test_context.set_get_implicit_cast_result(
530 &int4,
531 &text,
532 Some(CastDefinition::new(CastContext::Implicit)),
533 );
534
535 let with_clause = WithClause::default();
536 let from_clause = FromClause::default();
537 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
538 acontext.result_types.insert(int4);
539 acontext.result_types.insert(text.clone());
540
541 let cast_candidates = acontext.enumerate_cast_candiates(&mut test_context);
542 let Some(casts) = cast_candidates.get(&text) else {
543 panic!("expected cast candidates for text");
544 };
545 assert_eq!(1, casts.len());
546 Ok(())
547 }
548}
549
550impl CaseExprAnalysisContext<'_> {
551 fn is_not_null(&self) -> Option<bool> {
552 if self.nullabilities.iter().any(|n| n == &Some(false)) {
553 Some(false)
554 } else if self.nullabilities.iter().all(std::option::Option::is_some) {
555 Some(true)
556 } else {
557 None
558 }
559 }
560}
561
562#[cfg(test)]
563mod test_case_expr_analysis_context_is_not_null {
564 use crate::sem::{FromClause, WithClause};
565
566 #[test]
567 fn test_is_not_null_any_false() {
568 let with_clause = WithClause::default();
569 let from_clause = FromClause::default();
570 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
571
572 acontext.nullabilities = vec![Some(true), Some(false)];
573 assert_eq!(Some(false), acontext.is_not_null());
574 }
575
576 #[test]
577 fn test_is_not_null_all_some() {
578 let with_clause = WithClause::default();
579 let from_clause = FromClause::default();
580 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
581
582 acontext.nullabilities = vec![Some(true), Some(true)];
583 assert_eq!(Some(true), acontext.is_not_null());
584 }
585
586 #[test]
587 fn test_is_not_null_with_unknown() {
588 let with_clause = WithClause::default();
589 let from_clause = FromClause::default();
590 let mut acontext = super::CaseExprAnalysisContext::new(&with_clause, &from_clause);
591
592 acontext.nullabilities = vec![Some(true), None];
593 assert_eq!(None, acontext.is_not_null());
594 }
595}
596
597impl CaseExprAnalysisContext<'_> {
598 fn into_case_expr<TParseContext>(
599 mut self,
600 context: &mut TParseContext,
601 ) -> Result<CaseExpr, AnalysisError>
602 where
603 TParseContext: ParseContext,
604 {
605 let case_expr_type = self.type_fixup(context)?;
606 let is_not_null = self.is_not_null();
607 Ok(CaseExpr {
608 arg_expr: Box::new(self.case_arg),
609 default_expr: Box::new(self.default_expr),
610 when_exprs: self.when_exprs,
611 case_expr_type,
612 is_not_null,
613 })
614 }
615}
616
617#[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
618pub struct CaseWhen {
619 when_value: SemScalarExpr,
620 result: SemScalarExpr,
621}
622
623impl CaseWhen {
624 pub fn new(when_value: SemScalarExpr, result: SemScalarExpr) -> Self {
625 Self { when_value, result }
626 }
627}
628
629#[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
631pub struct CaseExpr {
632 arg_expr: Box<Option<SemScalarExpr>>,
633 when_exprs: Vec<CaseWhen>,
634 default_expr: Box<Option<SemScalarExpr>>,
635 case_expr_type: Option<TypeReference>,
636 is_not_null: Option<bool>,
637}
638
639impl CaseExpr {
640 #[must_use]
642 pub fn new(
643 arg_expr: Box<Option<SemScalarExpr>>,
644 when_exprs: Vec<CaseWhen>,
645 default_expr: Box<Option<SemScalarExpr>>,
646 case_expr_type: Option<TypeReference>,
647 is_not_null: Option<bool>,
648 ) -> Self {
649 Self {
650 arg_expr,
651 when_exprs,
652 default_expr,
653 case_expr_type,
654 is_not_null,
655 }
656 }
657}
658
659impl SemScalarExprNode for CaseExpr {
660 fn get_type(&self) -> Option<TypeReference> {
661 self.case_expr_type.clone()
662 }
663
664 fn is_not_null(&self) -> Option<bool> {
665 self.is_not_null
666 }
667}
668
669impl<TParseContext> AnalyzeScalarExpr<TParseContext, crate::syn::CaseExpr> for CaseExpr
670where
671 TParseContext: ParseContext,
672{
673 fn analyze_scalar_expr(
674 mut context: TParseContext,
675 with_clause: &WithClause,
676 from_clause: &FromClause,
677 syn: crate::syn::CaseExpr,
678 tokens: &IVec<ScanToken>,
679 ) -> Result<(SemScalarExpr, TParseContext), AnalysisError> {
680 let mut analisys_context = CaseExprAnalysisContext::new(with_clause, from_clause);
681
682 context = analisys_context.analyze_case_arg(context, &syn, tokens)?;
683 let Some(args) = syn.get_args().map(|v| v.as_case_when()) else {
684 AnalysisError::raise_unexpected_none("caseexpr.args")?
685 };
686 context = analisys_context.analyze_case_when_list(context, args, tokens)?;
687 context = analisys_context.analyze_default_expr(context, syn, tokens)?;
688 let case = analisys_context.into_case_expr(&mut context)?;
689 Ok((SemScalarExpr::Case(case), context))
690 }
691}