@@ -116,7 +116,8 @@ typedef struct deparse_expr_cxt
116
116
*/
117
117
static bool foreign_expr_walker (Node * node ,
118
118
foreign_glob_cxt * glob_cxt ,
119
- foreign_loc_cxt * outer_cxt );
119
+ foreign_loc_cxt * outer_cxt ,
120
+ foreign_loc_cxt * case_arg_cxt );
120
121
static char * deparse_type_name (Oid type_oid , int32 typemod );
121
122
122
123
/*
@@ -158,6 +159,7 @@ static void deparseScalarArrayOpExpr(ScalarArrayOpExpr *node,
158
159
static void deparseRelabelType (RelabelType * node , deparse_expr_cxt * context );
159
160
static void deparseBoolExpr (BoolExpr * node , deparse_expr_cxt * context );
160
161
static void deparseNullTest (NullTest * node , deparse_expr_cxt * context );
162
+ static void deparseCaseExpr (CaseExpr * node , deparse_expr_cxt * context );
161
163
static void deparseArrayExpr (ArrayExpr * node , deparse_expr_cxt * context );
162
164
static void printRemoteParam (int paramindex , Oid paramtype , int32 paramtypmod ,
163
165
deparse_expr_cxt * context );
@@ -254,7 +256,7 @@ is_foreign_expr(PlannerInfo *root,
254
256
glob_cxt .relids = baserel -> relids ;
255
257
loc_cxt .collation = InvalidOid ;
256
258
loc_cxt .state = FDW_COLLATE_NONE ;
257
- if (!foreign_expr_walker ((Node * ) expr , & glob_cxt , & loc_cxt ))
259
+ if (!foreign_expr_walker ((Node * ) expr , & glob_cxt , & loc_cxt , NULL ))
258
260
return false;
259
261
260
262
/*
@@ -283,6 +285,10 @@ is_foreign_expr(PlannerInfo *root,
283
285
*
284
286
* In addition, *outer_cxt is updated with collation information.
285
287
*
288
+ * case_arg_cxt is NULL if this subexpression is not inside a CASE-with-arg.
289
+ * Otherwise, it points to the collation info derived from the arg expression,
290
+ * which must be consulted by any CaseTestExpr.
291
+ *
286
292
* We must check that the expression contains only node types we can deparse,
287
293
* that all types/functions/operators are safe to send (they are "shippable"),
288
294
* and that all collations used in the expression derive from Vars of the
@@ -294,7 +300,8 @@ is_foreign_expr(PlannerInfo *root,
294
300
static bool
295
301
foreign_expr_walker (Node * node ,
296
302
foreign_glob_cxt * glob_cxt ,
297
- foreign_loc_cxt * outer_cxt )
303
+ foreign_loc_cxt * outer_cxt ,
304
+ foreign_loc_cxt * case_arg_cxt )
298
305
{
299
306
bool check_type = true;
300
307
PgFdwRelationInfo * fpinfo ;
@@ -432,17 +439,17 @@ foreign_expr_walker(Node *node,
432
439
* result, so do those first and reset inner_cxt afterwards.
433
440
*/
434
441
if (!foreign_expr_walker ((Node * ) sr -> refupperindexpr ,
435
- glob_cxt , & inner_cxt ))
442
+ glob_cxt , & inner_cxt , case_arg_cxt ))
436
443
return false;
437
444
inner_cxt .collation = InvalidOid ;
438
445
inner_cxt .state = FDW_COLLATE_NONE ;
439
446
if (!foreign_expr_walker ((Node * ) sr -> reflowerindexpr ,
440
- glob_cxt , & inner_cxt ))
447
+ glob_cxt , & inner_cxt , case_arg_cxt ))
441
448
return false;
442
449
inner_cxt .collation = InvalidOid ;
443
450
inner_cxt .state = FDW_COLLATE_NONE ;
444
451
if (!foreign_expr_walker ((Node * ) sr -> refexpr ,
445
- glob_cxt , & inner_cxt ))
452
+ glob_cxt , & inner_cxt , case_arg_cxt ))
446
453
return false;
447
454
448
455
/*
@@ -478,7 +485,7 @@ foreign_expr_walker(Node *node,
478
485
* Recurse to input subexpressions.
479
486
*/
480
487
if (!foreign_expr_walker ((Node * ) fe -> args ,
481
- glob_cxt , & inner_cxt ))
488
+ glob_cxt , & inner_cxt , case_arg_cxt ))
482
489
return false;
483
490
484
491
/*
@@ -526,7 +533,7 @@ foreign_expr_walker(Node *node,
526
533
* Recurse to input subexpressions.
527
534
*/
528
535
if (!foreign_expr_walker ((Node * ) oe -> args ,
529
- glob_cxt , & inner_cxt ))
536
+ glob_cxt , & inner_cxt , case_arg_cxt ))
530
537
return false;
531
538
532
539
/*
@@ -566,7 +573,7 @@ foreign_expr_walker(Node *node,
566
573
* Recurse to input subexpressions.
567
574
*/
568
575
if (!foreign_expr_walker ((Node * ) oe -> args ,
569
- glob_cxt , & inner_cxt ))
576
+ glob_cxt , & inner_cxt , case_arg_cxt ))
570
577
return false;
571
578
572
579
/*
@@ -592,7 +599,7 @@ foreign_expr_walker(Node *node,
592
599
* Recurse to input subexpression.
593
600
*/
594
601
if (!foreign_expr_walker ((Node * ) r -> arg ,
595
- glob_cxt , & inner_cxt ))
602
+ glob_cxt , & inner_cxt , case_arg_cxt ))
596
603
return false;
597
604
598
605
/*
@@ -619,7 +626,7 @@ foreign_expr_walker(Node *node,
619
626
* Recurse to input subexpressions.
620
627
*/
621
628
if (!foreign_expr_walker ((Node * ) b -> args ,
622
- glob_cxt , & inner_cxt ))
629
+ glob_cxt , & inner_cxt , case_arg_cxt ))
623
630
return false;
624
631
625
632
/* Output is always boolean and so noncollatable. */
@@ -635,14 +642,133 @@ foreign_expr_walker(Node *node,
635
642
* Recurse to input subexpressions.
636
643
*/
637
644
if (!foreign_expr_walker ((Node * ) nt -> arg ,
638
- glob_cxt , & inner_cxt ))
645
+ glob_cxt , & inner_cxt , case_arg_cxt ))
639
646
return false;
640
647
641
648
/* Output is always boolean and so noncollatable. */
642
649
collation = InvalidOid ;
643
650
state = FDW_COLLATE_NONE ;
644
651
}
645
652
break ;
653
+ case T_CaseExpr :
654
+ {
655
+ CaseExpr * ce = (CaseExpr * ) node ;
656
+ foreign_loc_cxt arg_cxt ;
657
+ foreign_loc_cxt tmp_cxt ;
658
+ ListCell * lc ;
659
+
660
+ /*
661
+ * Recurse to CASE's arg expression, if any. Its collation
662
+ * has to be saved aside for use while examining CaseTestExprs
663
+ * within the WHEN expressions.
664
+ */
665
+ arg_cxt .collation = InvalidOid ;
666
+ arg_cxt .state = FDW_COLLATE_NONE ;
667
+ if (ce -> arg )
668
+ {
669
+ if (!foreign_expr_walker ((Node * ) ce -> arg ,
670
+ glob_cxt , & arg_cxt , case_arg_cxt ))
671
+ return false;
672
+ }
673
+
674
+ /* Examine the CaseWhen subexpressions. */
675
+ foreach (lc , ce -> args )
676
+ {
677
+ CaseWhen * cw = lfirst_node (CaseWhen , lc );
678
+
679
+ if (ce -> arg )
680
+ {
681
+ /*
682
+ * In a CASE-with-arg, the parser should have produced
683
+ * WHEN clauses of the form "CaseTestExpr = RHS",
684
+ * possibly with an implicit coercion inserted above
685
+ * the CaseTestExpr. However in an expression that's
686
+ * been through the optimizer, the WHEN clause could
687
+ * be almost anything (since the equality operator
688
+ * could have been expanded into an inline function).
689
+ * In such cases forbid pushdown, because
690
+ * deparseCaseExpr can't handle it.
691
+ */
692
+ Node * whenExpr = (Node * ) cw -> expr ;
693
+ List * opArgs ;
694
+
695
+ if (!IsA (whenExpr , OpExpr ))
696
+ return false;
697
+
698
+ opArgs = ((OpExpr * ) whenExpr )-> args ;
699
+ if (list_length (opArgs ) != 2 ||
700
+ !IsA (strip_implicit_coercions (linitial (opArgs )),
701
+ CaseTestExpr ))
702
+ return false;
703
+ }
704
+
705
+ /*
706
+ * Recurse to WHEN expression, passing down the arg info.
707
+ * Its collation doesn't affect the result (really, it
708
+ * should be boolean and thus not have a collation).
709
+ */
710
+ tmp_cxt .collation = InvalidOid ;
711
+ tmp_cxt .state = FDW_COLLATE_NONE ;
712
+ if (!foreign_expr_walker ((Node * ) cw -> expr ,
713
+ glob_cxt , & tmp_cxt , & arg_cxt ))
714
+ return false;
715
+
716
+ /* Recurse to THEN expression. */
717
+ if (!foreign_expr_walker ((Node * ) cw -> result ,
718
+ glob_cxt , & inner_cxt , case_arg_cxt ))
719
+ return false;
720
+ }
721
+
722
+ /* Recurse to ELSE expression. */
723
+ if (!foreign_expr_walker ((Node * ) ce -> defresult ,
724
+ glob_cxt , & inner_cxt , case_arg_cxt ))
725
+ return false;
726
+
727
+ /*
728
+ * Detect whether node is introducing a collation not derived
729
+ * from a foreign Var. (If so, we just mark it unsafe for now
730
+ * rather than immediately returning false, since the parent
731
+ * node might not care.) This is the same as for function
732
+ * nodes, except that the input collation is derived from only
733
+ * the THEN and ELSE subexpressions.
734
+ */
735
+ collation = ce -> casecollid ;
736
+ if (collation == InvalidOid )
737
+ state = FDW_COLLATE_NONE ;
738
+ else if (inner_cxt .state == FDW_COLLATE_SAFE &&
739
+ collation == inner_cxt .collation )
740
+ state = FDW_COLLATE_SAFE ;
741
+ else if (collation == DEFAULT_COLLATION_OID )
742
+ state = FDW_COLLATE_NONE ;
743
+ else
744
+ state = FDW_COLLATE_UNSAFE ;
745
+ }
746
+ break ;
747
+ case T_CaseTestExpr :
748
+ {
749
+ CaseTestExpr * c = (CaseTestExpr * ) node ;
750
+
751
+ /* Punt if we seem not to be inside a CASE arg WHEN. */
752
+ if (!case_arg_cxt )
753
+ return false;
754
+
755
+ /*
756
+ * Otherwise, any nondefault collation attached to the
757
+ * CaseTestExpr node must be derived from foreign Var(s) in
758
+ * the CASE arg.
759
+ */
760
+ collation = c -> collation ;
761
+ if (collation == InvalidOid )
762
+ state = FDW_COLLATE_NONE ;
763
+ else if (case_arg_cxt -> state == FDW_COLLATE_SAFE &&
764
+ collation == case_arg_cxt -> collation )
765
+ state = FDW_COLLATE_SAFE ;
766
+ else if (collation == DEFAULT_COLLATION_OID )
767
+ state = FDW_COLLATE_NONE ;
768
+ else
769
+ state = FDW_COLLATE_UNSAFE ;
770
+ }
771
+ break ;
646
772
case T_ArrayExpr :
647
773
{
648
774
ArrayExpr * a = (ArrayExpr * ) node ;
@@ -651,7 +777,7 @@ foreign_expr_walker(Node *node,
651
777
* Recurse to input subexpressions.
652
778
*/
653
779
if (!foreign_expr_walker ((Node * ) a -> elements ,
654
- glob_cxt , & inner_cxt ))
780
+ glob_cxt , & inner_cxt , case_arg_cxt ))
655
781
return false;
656
782
657
783
/*
@@ -681,7 +807,7 @@ foreign_expr_walker(Node *node,
681
807
foreach (lc , l )
682
808
{
683
809
if (!foreign_expr_walker ((Node * ) lfirst (lc ),
684
- glob_cxt , & inner_cxt ))
810
+ glob_cxt , & inner_cxt , case_arg_cxt ))
685
811
return false;
686
812
}
687
813
@@ -730,7 +856,8 @@ foreign_expr_walker(Node *node,
730
856
n = (Node * ) tle -> expr ;
731
857
}
732
858
733
- if (!foreign_expr_walker (n , glob_cxt , & inner_cxt ))
859
+ if (!foreign_expr_walker (n ,
860
+ glob_cxt , & inner_cxt , case_arg_cxt ))
734
861
return false;
735
862
}
736
863
@@ -765,7 +892,7 @@ foreign_expr_walker(Node *node,
765
892
766
893
/* Check aggregate filter */
767
894
if (!foreign_expr_walker ((Node * ) agg -> aggfilter ,
768
- glob_cxt , & inner_cxt ))
895
+ glob_cxt , & inner_cxt , case_arg_cxt ))
769
896
return false;
770
897
771
898
/*
@@ -2456,6 +2583,9 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
2456
2583
case T_NullTest :
2457
2584
deparseNullTest ((NullTest * ) node , context );
2458
2585
break ;
2586
+ case T_CaseExpr :
2587
+ deparseCaseExpr ((CaseExpr * ) node , context );
2588
+ break ;
2459
2589
case T_ArrayExpr :
2460
2590
deparseArrayExpr ((ArrayExpr * ) node , context );
2461
2591
break ;
@@ -3007,6 +3137,56 @@ deparseNullTest(NullTest *node, deparse_expr_cxt *context)
3007
3137
}
3008
3138
}
3009
3139
3140
+ /*
3141
+ * Deparse CASE expression
3142
+ */
3143
+ static void
3144
+ deparseCaseExpr (CaseExpr * node , deparse_expr_cxt * context )
3145
+ {
3146
+ StringInfo buf = context -> buf ;
3147
+ ListCell * lc ;
3148
+
3149
+ appendStringInfoString (buf , "(CASE" );
3150
+
3151
+ /* If this is a CASE arg WHEN then emit the arg expression */
3152
+ if (node -> arg != NULL )
3153
+ {
3154
+ appendStringInfoChar (buf , ' ' );
3155
+ deparseExpr (node -> arg , context );
3156
+ }
3157
+
3158
+ /* Add each condition/result of the CASE clause */
3159
+ foreach (lc , node -> args )
3160
+ {
3161
+ CaseWhen * whenclause = (CaseWhen * ) lfirst (lc );
3162
+
3163
+ /* WHEN */
3164
+ appendStringInfoString (buf , " WHEN " );
3165
+ if (node -> arg == NULL ) /* CASE WHEN */
3166
+ deparseExpr (whenclause -> expr , context );
3167
+ else /* CASE arg WHEN */
3168
+ {
3169
+ /* Ignore the CaseTestExpr and equality operator. */
3170
+ deparseExpr (lsecond (castNode (OpExpr , whenclause -> expr )-> args ),
3171
+ context );
3172
+ }
3173
+
3174
+ /* THEN */
3175
+ appendStringInfoString (buf , " THEN " );
3176
+ deparseExpr (whenclause -> result , context );
3177
+ }
3178
+
3179
+ /* add ELSE if present */
3180
+ if (node -> defresult != NULL )
3181
+ {
3182
+ appendStringInfoString (buf , " ELSE " );
3183
+ deparseExpr (node -> defresult , context );
3184
+ }
3185
+
3186
+ /* append END */
3187
+ appendStringInfoString (buf , " END)" );
3188
+ }
3189
+
3010
3190
/*
3011
3191
* Deparse ARRAY[...] construct.
3012
3192
*/
0 commit comments