aboutsummaryrefslogtreecommitdiff
path: root/contrib/postgres_fdw/deparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/postgres_fdw/deparse.c')
-rw-r--r--contrib/postgres_fdw/deparse.c212
1 files changed, 196 insertions, 16 deletions
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 31919fda8c6..2b8f00abe78 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -116,7 +116,8 @@ typedef struct deparse_expr_cxt
*/
static bool foreign_expr_walker(Node *node,
foreign_glob_cxt *glob_cxt,
- foreign_loc_cxt *outer_cxt);
+ foreign_loc_cxt *outer_cxt,
+ foreign_loc_cxt *case_arg_cxt);
static char *deparse_type_name(Oid type_oid, int32 typemod);
/*
@@ -158,6 +159,7 @@ static void deparseScalarArrayOpExpr(ScalarArrayOpExpr *node,
static void deparseRelabelType(RelabelType *node, deparse_expr_cxt *context);
static void deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context);
static void deparseNullTest(NullTest *node, deparse_expr_cxt *context);
+static void deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context);
static void deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context);
static void printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod,
deparse_expr_cxt *context);
@@ -254,7 +256,7 @@ is_foreign_expr(PlannerInfo *root,
glob_cxt.relids = baserel->relids;
loc_cxt.collation = InvalidOid;
loc_cxt.state = FDW_COLLATE_NONE;
- if (!foreign_expr_walker((Node *) expr, &glob_cxt, &loc_cxt))
+ if (!foreign_expr_walker((Node *) expr, &glob_cxt, &loc_cxt, NULL))
return false;
/*
@@ -283,6 +285,10 @@ is_foreign_expr(PlannerInfo *root,
*
* In addition, *outer_cxt is updated with collation information.
*
+ * case_arg_cxt is NULL if this subexpression is not inside a CASE-with-arg.
+ * Otherwise, it points to the collation info derived from the arg expression,
+ * which must be consulted by any CaseTestExpr.
+ *
* We must check that the expression contains only node types we can deparse,
* that all types/functions/operators are safe to send (they are "shippable"),
* and that all collations used in the expression derive from Vars of the
@@ -294,7 +300,8 @@ is_foreign_expr(PlannerInfo *root,
static bool
foreign_expr_walker(Node *node,
foreign_glob_cxt *glob_cxt,
- foreign_loc_cxt *outer_cxt)
+ foreign_loc_cxt *outer_cxt,
+ foreign_loc_cxt *case_arg_cxt)
{
bool check_type = true;
PgFdwRelationInfo *fpinfo;
@@ -432,17 +439,17 @@ foreign_expr_walker(Node *node,
* result, so do those first and reset inner_cxt afterwards.
*/
if (!foreign_expr_walker((Node *) sr->refupperindexpr,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
inner_cxt.collation = InvalidOid;
inner_cxt.state = FDW_COLLATE_NONE;
if (!foreign_expr_walker((Node *) sr->reflowerindexpr,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
inner_cxt.collation = InvalidOid;
inner_cxt.state = FDW_COLLATE_NONE;
if (!foreign_expr_walker((Node *) sr->refexpr,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
/*
@@ -478,7 +485,7 @@ foreign_expr_walker(Node *node,
* Recurse to input subexpressions.
*/
if (!foreign_expr_walker((Node *) fe->args,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
/*
@@ -526,7 +533,7 @@ foreign_expr_walker(Node *node,
* Recurse to input subexpressions.
*/
if (!foreign_expr_walker((Node *) oe->args,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
/*
@@ -566,7 +573,7 @@ foreign_expr_walker(Node *node,
* Recurse to input subexpressions.
*/
if (!foreign_expr_walker((Node *) oe->args,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
/*
@@ -592,7 +599,7 @@ foreign_expr_walker(Node *node,
* Recurse to input subexpression.
*/
if (!foreign_expr_walker((Node *) r->arg,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
/*
@@ -619,7 +626,7 @@ foreign_expr_walker(Node *node,
* Recurse to input subexpressions.
*/
if (!foreign_expr_walker((Node *) b->args,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
/* Output is always boolean and so noncollatable. */
@@ -635,7 +642,7 @@ foreign_expr_walker(Node *node,
* Recurse to input subexpressions.
*/
if (!foreign_expr_walker((Node *) nt->arg,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
/* Output is always boolean and so noncollatable. */
@@ -643,6 +650,125 @@ foreign_expr_walker(Node *node,
state = FDW_COLLATE_NONE;
}
break;
+ case T_CaseExpr:
+ {
+ CaseExpr *ce = (CaseExpr *) node;
+ foreign_loc_cxt arg_cxt;
+ foreign_loc_cxt tmp_cxt;
+ ListCell *lc;
+
+ /*
+ * Recurse to CASE's arg expression, if any. Its collation
+ * has to be saved aside for use while examining CaseTestExprs
+ * within the WHEN expressions.
+ */
+ arg_cxt.collation = InvalidOid;
+ arg_cxt.state = FDW_COLLATE_NONE;
+ if (ce->arg)
+ {
+ if (!foreign_expr_walker((Node *) ce->arg,
+ glob_cxt, &arg_cxt, case_arg_cxt))
+ return false;
+ }
+
+ /* Examine the CaseWhen subexpressions. */
+ foreach(lc, ce->args)
+ {
+ CaseWhen *cw = lfirst_node(CaseWhen, lc);
+
+ if (ce->arg)
+ {
+ /*
+ * In a CASE-with-arg, the parser should have produced
+ * WHEN clauses of the form "CaseTestExpr = RHS",
+ * possibly with an implicit coercion inserted above
+ * the CaseTestExpr. However in an expression that's
+ * been through the optimizer, the WHEN clause could
+ * be almost anything (since the equality operator
+ * could have been expanded into an inline function).
+ * In such cases forbid pushdown, because
+ * deparseCaseExpr can't handle it.
+ */
+ Node *whenExpr = (Node *) cw->expr;
+ List *opArgs;
+
+ if (!IsA(whenExpr, OpExpr))
+ return false;
+
+ opArgs = ((OpExpr *) whenExpr)->args;
+ if (list_length(opArgs) != 2 ||
+ !IsA(strip_implicit_coercions(linitial(opArgs)),
+ CaseTestExpr))
+ return false;
+ }
+
+ /*
+ * Recurse to WHEN expression, passing down the arg info.
+ * Its collation doesn't affect the result (really, it
+ * should be boolean and thus not have a collation).
+ */
+ tmp_cxt.collation = InvalidOid;
+ tmp_cxt.state = FDW_COLLATE_NONE;
+ if (!foreign_expr_walker((Node *) cw->expr,
+ glob_cxt, &tmp_cxt, &arg_cxt))
+ return false;
+
+ /* Recurse to THEN expression. */
+ if (!foreign_expr_walker((Node *) cw->result,
+ glob_cxt, &inner_cxt, case_arg_cxt))
+ return false;
+ }
+
+ /* Recurse to ELSE expression. */
+ if (!foreign_expr_walker((Node *) ce->defresult,
+ glob_cxt, &inner_cxt, case_arg_cxt))
+ return false;
+
+ /*
+ * Detect whether node is introducing a collation not derived
+ * from a foreign Var. (If so, we just mark it unsafe for now
+ * rather than immediately returning false, since the parent
+ * node might not care.) This is the same as for function
+ * nodes, except that the input collation is derived from only
+ * the THEN and ELSE subexpressions.
+ */
+ collation = ce->casecollid;
+ if (collation == InvalidOid)
+ state = FDW_COLLATE_NONE;
+ else if (inner_cxt.state == FDW_COLLATE_SAFE &&
+ collation == inner_cxt.collation)
+ state = FDW_COLLATE_SAFE;
+ else if (collation == DEFAULT_COLLATION_OID)
+ state = FDW_COLLATE_NONE;
+ else
+ state = FDW_COLLATE_UNSAFE;
+ }
+ break;
+ case T_CaseTestExpr:
+ {
+ CaseTestExpr *c = (CaseTestExpr *) node;
+
+ /* Punt if we seem not to be inside a CASE arg WHEN. */
+ if (!case_arg_cxt)
+ return false;
+
+ /*
+ * Otherwise, any nondefault collation attached to the
+ * CaseTestExpr node must be derived from foreign Var(s) in
+ * the CASE arg.
+ */
+ collation = c->collation;
+ if (collation == InvalidOid)
+ state = FDW_COLLATE_NONE;
+ else if (case_arg_cxt->state == FDW_COLLATE_SAFE &&
+ collation == case_arg_cxt->collation)
+ state = FDW_COLLATE_SAFE;
+ else if (collation == DEFAULT_COLLATION_OID)
+ state = FDW_COLLATE_NONE;
+ else
+ state = FDW_COLLATE_UNSAFE;
+ }
+ break;
case T_ArrayExpr:
{
ArrayExpr *a = (ArrayExpr *) node;
@@ -651,7 +777,7 @@ foreign_expr_walker(Node *node,
* Recurse to input subexpressions.
*/
if (!foreign_expr_walker((Node *) a->elements,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
/*
@@ -681,7 +807,7 @@ foreign_expr_walker(Node *node,
foreach(lc, l)
{
if (!foreign_expr_walker((Node *) lfirst(lc),
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
}
@@ -730,7 +856,8 @@ foreign_expr_walker(Node *node,
n = (Node *) tle->expr;
}
- if (!foreign_expr_walker(n, glob_cxt, &inner_cxt))
+ if (!foreign_expr_walker(n,
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
}
@@ -765,7 +892,7 @@ foreign_expr_walker(Node *node,
/* Check aggregate filter */
if (!foreign_expr_walker((Node *) agg->aggfilter,
- glob_cxt, &inner_cxt))
+ glob_cxt, &inner_cxt, case_arg_cxt))
return false;
/*
@@ -2456,6 +2583,9 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
case T_NullTest:
deparseNullTest((NullTest *) node, context);
break;
+ case T_CaseExpr:
+ deparseCaseExpr((CaseExpr *) node, context);
+ break;
case T_ArrayExpr:
deparseArrayExpr((ArrayExpr *) node, context);
break;
@@ -3008,6 +3138,56 @@ deparseNullTest(NullTest *node, deparse_expr_cxt *context)
}
/*
+ * Deparse CASE expression
+ */
+static void
+deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context)
+{
+ StringInfo buf = context->buf;
+ ListCell *lc;
+
+ appendStringInfoString(buf, "(CASE");
+
+ /* If this is a CASE arg WHEN then emit the arg expression */
+ if (node->arg != NULL)
+ {
+ appendStringInfoChar(buf, ' ');
+ deparseExpr(node->arg, context);
+ }
+
+ /* Add each condition/result of the CASE clause */
+ foreach(lc, node->args)
+ {
+ CaseWhen *whenclause = (CaseWhen *) lfirst(lc);
+
+ /* WHEN */
+ appendStringInfoString(buf, " WHEN ");
+ if (node->arg == NULL) /* CASE WHEN */
+ deparseExpr(whenclause->expr, context);
+ else /* CASE arg WHEN */
+ {
+ /* Ignore the CaseTestExpr and equality operator. */
+ deparseExpr(lsecond(castNode(OpExpr, whenclause->expr)->args),
+ context);
+ }
+
+ /* THEN */
+ appendStringInfoString(buf, " THEN ");
+ deparseExpr(whenclause->result, context);
+ }
+
+ /* add ELSE if present */
+ if (node->defresult != NULL)
+ {
+ appendStringInfoString(buf, " ELSE ");
+ deparseExpr(node->defresult, context);
+ }
+
+ /* append END */
+ appendStringInfoString(buf, " END)");
+}
+
+/*
* Deparse ARRAY[...] construct.
*/
static void