@@ -116,7 +116,8 @@ typedef struct deparse_expr_cxt
116116 */
117117static bool foreign_expr_walker (Node * node ,
118118 foreign_glob_cxt * glob_cxt ,
119- foreign_loc_cxt * outer_cxt );
119+ foreign_loc_cxt * outer_cxt ,
120+ foreign_loc_cxt * case_arg_cxt );
120121static char * deparse_type_name (Oid type_oid , int32 typemod );
121122
122123/*
@@ -158,6 +159,7 @@ static void deparseScalarArrayOpExpr(ScalarArrayOpExpr *node,
158159static void deparseRelabelType (RelabelType * node , deparse_expr_cxt * context );
159160static void deparseBoolExpr (BoolExpr * node , deparse_expr_cxt * context );
160161static void deparseNullTest (NullTest * node , deparse_expr_cxt * context );
162+ static void deparseCaseExpr (CaseExpr * node , deparse_expr_cxt * context );
161163static void deparseArrayExpr (ArrayExpr * node , deparse_expr_cxt * context );
162164static void printRemoteParam (int paramindex , Oid paramtype , int32 paramtypmod ,
163165 deparse_expr_cxt * context );
@@ -254,7 +256,7 @@ is_foreign_expr(PlannerInfo *root,
254256 glob_cxt .relids = baserel -> relids ;
255257 loc_cxt .collation = InvalidOid ;
256258 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 ))
258260 return false;
259261
260262 /*
@@ -283,6 +285,10 @@ is_foreign_expr(PlannerInfo *root,
283285 *
284286 * In addition, *outer_cxt is updated with collation information.
285287 *
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+ *
286292 * We must check that the expression contains only node types we can deparse,
287293 * that all types/functions/operators are safe to send (they are "shippable"),
288294 * and that all collations used in the expression derive from Vars of the
@@ -294,7 +300,8 @@ is_foreign_expr(PlannerInfo *root,
294300static bool
295301foreign_expr_walker (Node * node ,
296302 foreign_glob_cxt * glob_cxt ,
297- foreign_loc_cxt * outer_cxt )
303+ foreign_loc_cxt * outer_cxt ,
304+ foreign_loc_cxt * case_arg_cxt )
298305{
299306 bool check_type = true;
300307 PgFdwRelationInfo * fpinfo ;
@@ -432,17 +439,17 @@ foreign_expr_walker(Node *node,
432439 * result, so do those first and reset inner_cxt afterwards.
433440 */
434441 if (!foreign_expr_walker ((Node * ) sr -> refupperindexpr ,
435- glob_cxt , & inner_cxt ))
442+ glob_cxt , & inner_cxt , case_arg_cxt ))
436443 return false;
437444 inner_cxt .collation = InvalidOid ;
438445 inner_cxt .state = FDW_COLLATE_NONE ;
439446 if (!foreign_expr_walker ((Node * ) sr -> reflowerindexpr ,
440- glob_cxt , & inner_cxt ))
447+ glob_cxt , & inner_cxt , case_arg_cxt ))
441448 return false;
442449 inner_cxt .collation = InvalidOid ;
443450 inner_cxt .state = FDW_COLLATE_NONE ;
444451 if (!foreign_expr_walker ((Node * ) sr -> refexpr ,
445- glob_cxt , & inner_cxt ))
452+ glob_cxt , & inner_cxt , case_arg_cxt ))
446453 return false;
447454
448455 /*
@@ -478,7 +485,7 @@ foreign_expr_walker(Node *node,
478485 * Recurse to input subexpressions.
479486 */
480487 if (!foreign_expr_walker ((Node * ) fe -> args ,
481- glob_cxt , & inner_cxt ))
488+ glob_cxt , & inner_cxt , case_arg_cxt ))
482489 return false;
483490
484491 /*
@@ -526,7 +533,7 @@ foreign_expr_walker(Node *node,
526533 * Recurse to input subexpressions.
527534 */
528535 if (!foreign_expr_walker ((Node * ) oe -> args ,
529- glob_cxt , & inner_cxt ))
536+ glob_cxt , & inner_cxt , case_arg_cxt ))
530537 return false;
531538
532539 /*
@@ -566,7 +573,7 @@ foreign_expr_walker(Node *node,
566573 * Recurse to input subexpressions.
567574 */
568575 if (!foreign_expr_walker ((Node * ) oe -> args ,
569- glob_cxt , & inner_cxt ))
576+ glob_cxt , & inner_cxt , case_arg_cxt ))
570577 return false;
571578
572579 /*
@@ -592,7 +599,7 @@ foreign_expr_walker(Node *node,
592599 * Recurse to input subexpression.
593600 */
594601 if (!foreign_expr_walker ((Node * ) r -> arg ,
595- glob_cxt , & inner_cxt ))
602+ glob_cxt , & inner_cxt , case_arg_cxt ))
596603 return false;
597604
598605 /*
@@ -619,7 +626,7 @@ foreign_expr_walker(Node *node,
619626 * Recurse to input subexpressions.
620627 */
621628 if (!foreign_expr_walker ((Node * ) b -> args ,
622- glob_cxt , & inner_cxt ))
629+ glob_cxt , & inner_cxt , case_arg_cxt ))
623630 return false;
624631
625632 /* Output is always boolean and so noncollatable. */
@@ -635,14 +642,133 @@ foreign_expr_walker(Node *node,
635642 * Recurse to input subexpressions.
636643 */
637644 if (!foreign_expr_walker ((Node * ) nt -> arg ,
638- glob_cxt , & inner_cxt ))
645+ glob_cxt , & inner_cxt , case_arg_cxt ))
639646 return false;
640647
641648 /* Output is always boolean and so noncollatable. */
642649 collation = InvalidOid ;
643650 state = FDW_COLLATE_NONE ;
644651 }
645652 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 ;
646772 case T_ArrayExpr :
647773 {
648774 ArrayExpr * a = (ArrayExpr * ) node ;
@@ -651,7 +777,7 @@ foreign_expr_walker(Node *node,
651777 * Recurse to input subexpressions.
652778 */
653779 if (!foreign_expr_walker ((Node * ) a -> elements ,
654- glob_cxt , & inner_cxt ))
780+ glob_cxt , & inner_cxt , case_arg_cxt ))
655781 return false;
656782
657783 /*
@@ -681,7 +807,7 @@ foreign_expr_walker(Node *node,
681807 foreach (lc , l )
682808 {
683809 if (!foreign_expr_walker ((Node * ) lfirst (lc ),
684- glob_cxt , & inner_cxt ))
810+ glob_cxt , & inner_cxt , case_arg_cxt ))
685811 return false;
686812 }
687813
@@ -730,7 +856,8 @@ foreign_expr_walker(Node *node,
730856 n = (Node * ) tle -> expr ;
731857 }
732858
733- if (!foreign_expr_walker (n , glob_cxt , & inner_cxt ))
859+ if (!foreign_expr_walker (n ,
860+ glob_cxt , & inner_cxt , case_arg_cxt ))
734861 return false;
735862 }
736863
@@ -765,7 +892,7 @@ foreign_expr_walker(Node *node,
765892
766893 /* Check aggregate filter */
767894 if (!foreign_expr_walker ((Node * ) agg -> aggfilter ,
768- glob_cxt , & inner_cxt ))
895+ glob_cxt , & inner_cxt , case_arg_cxt ))
769896 return false;
770897
771898 /*
@@ -2456,6 +2583,9 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
24562583 case T_NullTest :
24572584 deparseNullTest ((NullTest * ) node , context );
24582585 break ;
2586+ case T_CaseExpr :
2587+ deparseCaseExpr ((CaseExpr * ) node , context );
2588+ break ;
24592589 case T_ArrayExpr :
24602590 deparseArrayExpr ((ArrayExpr * ) node , context );
24612591 break ;
@@ -3007,6 +3137,56 @@ deparseNullTest(NullTest *node, deparse_expr_cxt *context)
30073137 }
30083138}
30093139
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+
30103190/*
30113191 * Deparse ARRAY[...] construct.
30123192 */
0 commit comments