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.c116
1 files changed, 108 insertions, 8 deletions
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index d98bd666818..b27689d0864 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -152,6 +152,7 @@ static void deparseParam(Param *node, deparse_expr_cxt *context);
static void deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context);
static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
+static bool isPlainForeignVar(Expr *node, deparse_expr_cxt *context);
static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
static void deparseDistinctExpr(DistinctExpr *node, deparse_expr_cxt *context);
static void deparseScalarArrayOpExpr(ScalarArrayOpExpr *node,
@@ -2695,9 +2696,14 @@ deparseVar(Var *node, deparse_expr_cxt *context)
* Deparse given constant value into context->buf.
*
* This function has to be kept in sync with ruleutils.c's get_const_expr.
- * As for that function, showtype can be -1 to never show "::typename" decoration,
- * or +1 to always show it, or 0 to show it only if the constant wouldn't be assumed
- * to be the right type by default.
+ *
+ * As in that function, showtype can be -1 to never show "::typename"
+ * decoration, +1 to always show it, or 0 to show it only if the constant
+ * wouldn't be assumed to be the right type by default.
+ *
+ * In addition, this code allows showtype to be -2 to indicate that we should
+ * not show "::typename" decoration if the constant is printed as an untyped
+ * literal or NULL (while in other cases, behaving as for showtype == 0).
*/
static void
deparseConst(Const *node, deparse_expr_cxt *context, int showtype)
@@ -2707,6 +2713,7 @@ deparseConst(Const *node, deparse_expr_cxt *context, int showtype)
bool typIsVarlena;
char *extval;
bool isfloat = false;
+ bool isstring = false;
bool needlabel;
if (node->constisnull)
@@ -2762,13 +2769,14 @@ deparseConst(Const *node, deparse_expr_cxt *context, int showtype)
break;
default:
deparseStringLiteral(buf, extval);
+ isstring = true;
break;
}
pfree(extval);
- if (showtype < 0)
- return;
+ if (showtype == -1)
+ return; /* never print type label */
/*
* For showtype == 0, append ::typename unless the constant will be
@@ -2788,7 +2796,13 @@ deparseConst(Const *node, deparse_expr_cxt *context, int showtype)
needlabel = !isfloat || (node->consttypmod >= 0);
break;
default:
- needlabel = true;
+ if (showtype == -2)
+ {
+ /* label unless we printed it as an untyped string */
+ needlabel = !isstring;
+ }
+ else
+ needlabel = true;
break;
}
if (needlabel || showtype > 0)
@@ -2953,6 +2967,8 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
StringInfo buf = context->buf;
HeapTuple tuple;
Form_pg_operator form;
+ Expr *right;
+ bool canSuppressRightConstCast = false;
char oprkind;
/* Retrieve information about the operator from system catalog. */
@@ -2966,13 +2982,58 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
Assert((oprkind == 'l' && list_length(node->args) == 1) ||
(oprkind == 'b' && list_length(node->args) == 2));
+ right = llast(node->args);
+
/* Always parenthesize the expression. */
appendStringInfoChar(buf, '(');
/* Deparse left operand, if any. */
if (oprkind == 'b')
{
- deparseExpr(linitial(node->args), context);
+ Expr *left = linitial(node->args);
+ Oid leftType = exprType((Node *) left);
+ Oid rightType = exprType((Node *) right);
+ bool canSuppressLeftConstCast = false;
+
+ /*
+ * When considering a binary operator, if one operand is a Const that
+ * can be printed as a bare string literal or NULL (i.e., it will look
+ * like type UNKNOWN to the remote parser), the Const normally
+ * receives an explicit cast to the operator's input type. However,
+ * in Const-to-Var comparisons where both operands are of the same
+ * type, we prefer to suppress the explicit cast, leaving the Const's
+ * type resolution up to the remote parser. The remote's resolution
+ * heuristic will assume that an unknown input type being compared to
+ * a known input type is of that known type as well.
+ *
+ * This hack allows some cases to succeed where a remote column is
+ * declared with a different type in the local (foreign) table. By
+ * emitting "foreigncol = 'foo'" not "foreigncol = 'foo'::text" or the
+ * like, we allow the remote parser to pick an "=" operator that's
+ * compatible with whatever type the remote column really is, such as
+ * an enum.
+ *
+ * We allow cast suppression to happen only when the other operand is
+ * a plain foreign Var. Although the remote's unknown-type heuristic
+ * would apply to other cases just as well, we would be taking a
+ * bigger risk that the inferred type is something unexpected. With
+ * this restriction, if anything goes wrong it's the user's fault for
+ * not declaring the local column with the same type as the remote
+ * column.
+ */
+ if (leftType == rightType)
+ {
+ if (IsA(left, Const))
+ canSuppressLeftConstCast = isPlainForeignVar(right, context);
+ else if (IsA(right, Const))
+ canSuppressRightConstCast = isPlainForeignVar(left, context);
+ }
+
+ if (canSuppressLeftConstCast)
+ deparseConst((Const *) left, context, -2);
+ else
+ deparseExpr(left, context);
+
appendStringInfoChar(buf, ' ');
}
@@ -2981,7 +3042,11 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
/* Deparse right operand. */
appendStringInfoChar(buf, ' ');
- deparseExpr(llast(node->args), context);
+
+ if (canSuppressRightConstCast)
+ deparseConst((Const *) right, context, -2);
+ else
+ deparseExpr(right, context);
appendStringInfoChar(buf, ')');
@@ -2989,6 +3054,41 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
}
/*
+ * Will "node" deparse as a plain foreign Var?
+ */
+static bool
+isPlainForeignVar(Expr *node, deparse_expr_cxt *context)
+{
+ /*
+ * We allow the foreign Var to have an implicit RelabelType, mainly so
+ * that this'll work with varchar columns. Note that deparseRelabelType
+ * will not print such a cast, so we're not breaking the restriction that
+ * the expression print as a plain Var. We won't risk it for an implicit
+ * cast that requires a function, nor for non-implicit RelabelType; such
+ * cases seem too likely to involve semantics changes compared to what
+ * would happen on the remote side.
+ */
+ if (IsA(node, RelabelType) &&
+ ((RelabelType *) node)->relabelformat == COERCE_IMPLICIT_CAST)
+ node = ((RelabelType *) node)->arg;
+
+ if (IsA(node, Var))
+ {
+ /*
+ * The Var must be one that'll deparse as a foreign column reference
+ * (cf. deparseVar).
+ */
+ Var *var = (Var *) node;
+ Relids relids = context->scanrel->relids;
+
+ if (bms_is_member(var->varno, relids) && var->varlevelsup == 0)
+ return true;
+ }
+
+ return false;
+}
+
+/*
* Print the name of an operator.
*/
static void