diff options
Diffstat (limited to 'contrib/postgres_fdw/deparse.c')
-rw-r--r-- | contrib/postgres_fdw/deparse.c | 166 |
1 files changed, 119 insertions, 47 deletions
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 2029036c62a..80c6f0f1b1a 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -40,6 +40,7 @@ #include "catalog/pg_collation.h" #include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" +#include "catalog/pg_opfamily.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "commands/defrem.h" @@ -179,6 +180,8 @@ static void deparseRangeTblRef(StringInfo buf, PlannerInfo *root, Index ignore_rel, List **ignore_conds, List **params_list); static void deparseAggref(Aggref *node, deparse_expr_cxt *context); static void appendGroupByClause(List *tlist, deparse_expr_cxt *context); +static void appendOrderBySuffix(Oid sortop, Oid sortcoltype, bool nulls_first, + deparse_expr_cxt *context); static void appendAggOrderBy(List *orderList, List *targetList, deparse_expr_cxt *context); static void appendFunctionName(Oid funcid, deparse_expr_cxt *context); @@ -905,6 +908,33 @@ is_foreign_param(PlannerInfo *root, } /* + * Returns true if it's safe to push down the sort expression described by + * 'pathkey' to the foreign server. + */ +bool +is_foreign_pathkey(PlannerInfo *root, + RelOptInfo *baserel, + PathKey *pathkey) +{ + EquivalenceClass *pathkey_ec = pathkey->pk_eclass; + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) baserel->fdw_private; + + /* + * is_foreign_expr would detect volatile expressions as well, but checking + * ec_has_volatile here saves some cycles. + */ + if (pathkey_ec->ec_has_volatile) + return false; + + /* can't push down the sort if the pathkey's opfamily is not shippable */ + if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId, fpinfo)) + return false; + + /* can push if a suitable EC member exists */ + return (find_em_for_rel(root, pathkey_ec, baserel) != NULL); +} + +/* * Convert type OID + typmod info into a type name we can ship to the remote * server. Someplace else had better have verified that this type name is * expected to be known on the remote end. @@ -3070,44 +3100,59 @@ appendAggOrderBy(List *orderList, List *targetList, deparse_expr_cxt *context) { SortGroupClause *srt = (SortGroupClause *) lfirst(lc); Node *sortexpr; - Oid sortcoltype; - TypeCacheEntry *typentry; if (!first) appendStringInfoString(buf, ", "); first = false; + /* Deparse the sort expression proper. */ sortexpr = deparseSortGroupClause(srt->tleSortGroupRef, targetList, false, context); - sortcoltype = exprType(sortexpr); - /* See whether operator is default < or > for datatype */ - typentry = lookup_type_cache(sortcoltype, - TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); - if (srt->sortop == typentry->lt_opr) - appendStringInfoString(buf, " ASC"); - else if (srt->sortop == typentry->gt_opr) - appendStringInfoString(buf, " DESC"); - else - { - HeapTuple opertup; - Form_pg_operator operform; - - appendStringInfoString(buf, " USING "); - - /* Append operator name. */ - opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(srt->sortop)); - if (!HeapTupleIsValid(opertup)) - elog(ERROR, "cache lookup failed for operator %u", srt->sortop); - operform = (Form_pg_operator) GETSTRUCT(opertup); - deparseOperatorName(buf, operform); - ReleaseSysCache(opertup); - } + /* Add decoration as needed. */ + appendOrderBySuffix(srt->sortop, exprType(sortexpr), srt->nulls_first, + context); + } +} - if (srt->nulls_first) - appendStringInfoString(buf, " NULLS FIRST"); - else - appendStringInfoString(buf, " NULLS LAST"); +/* + * Append the ASC, DESC, USING <OPERATOR> and NULLS FIRST / NULLS LAST parts + * of an ORDER BY clause. + */ +static void +appendOrderBySuffix(Oid sortop, Oid sortcoltype, bool nulls_first, + deparse_expr_cxt *context) +{ + StringInfo buf = context->buf; + TypeCacheEntry *typentry; + + /* See whether operator is default < or > for sort expr's datatype. */ + typentry = lookup_type_cache(sortcoltype, + TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); + + if (sortop == typentry->lt_opr) + appendStringInfoString(buf, " ASC"); + else if (sortop == typentry->gt_opr) + appendStringInfoString(buf, " DESC"); + else + { + HeapTuple opertup; + Form_pg_operator operform; + + appendStringInfoString(buf, " USING "); + + /* Append operator name. */ + opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(sortop)); + if (!HeapTupleIsValid(opertup)) + elog(ERROR, "cache lookup failed for operator %u", sortop); + operform = (Form_pg_operator) GETSTRUCT(opertup); + deparseOperatorName(buf, operform); + ReleaseSysCache(opertup); } + + if (nulls_first) + appendStringInfoString(buf, " NULLS FIRST"); + else + appendStringInfoString(buf, " NULLS LAST"); } /* @@ -3190,9 +3235,13 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context) } /* - * Deparse ORDER BY clause according to the given pathkeys for given base - * relation. From given pathkeys expressions belonging entirely to the given - * base relation are obtained and deparsed. + * Deparse ORDER BY clause defined by the given pathkeys. + * + * The clause should use Vars from context->scanrel if !has_final_sort, + * or from context->foreignrel's targetlist if has_final_sort. + * + * We find a suitable pathkey expression (some earlier step + * should have verified that there is one) and deparse it. */ static void appendOrderByClause(List *pathkeys, bool has_final_sort, @@ -3200,8 +3249,7 @@ appendOrderByClause(List *pathkeys, bool has_final_sort, { ListCell *lcell; int nestlevel; - char *delim = " "; - RelOptInfo *baserel = context->scanrel; + const char *delim = " "; StringInfo buf = context->buf; /* Make sure any constants in the exprs are printed portably */ @@ -3211,7 +3259,9 @@ appendOrderByClause(List *pathkeys, bool has_final_sort, foreach(lcell, pathkeys) { PathKey *pathkey = lfirst(lcell); + EquivalenceMember *em; Expr *em_expr; + Oid oprid; if (has_final_sort) { @@ -3219,26 +3269,48 @@ appendOrderByClause(List *pathkeys, bool has_final_sort, * By construction, context->foreignrel is the input relation to * the final sort. */ - em_expr = find_em_expr_for_input_target(context->root, - pathkey->pk_eclass, - context->foreignrel->reltarget); + em = find_em_for_rel_target(context->root, + pathkey->pk_eclass, + context->foreignrel); } else - em_expr = find_em_expr_for_rel(pathkey->pk_eclass, baserel); + em = find_em_for_rel(context->root, + pathkey->pk_eclass, + context->scanrel); + + /* + * We don't expect any error here; it would mean that shippability + * wasn't verified earlier. For the same reason, we don't recheck + * shippability of the sort operator. + */ + if (em == NULL) + elog(ERROR, "could not find pathkey item to sort"); + + em_expr = em->em_expr; - Assert(em_expr != NULL); + /* + * Lookup the operator corresponding to the strategy in the opclass. + * The datatype used by the opfamily is not necessarily the same as + * the expression type (for array types for example). + */ + oprid = get_opfamily_member(pathkey->pk_opfamily, + em->em_datatype, + em->em_datatype, + pathkey->pk_strategy); + if (!OidIsValid(oprid)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + pathkey->pk_strategy, em->em_datatype, em->em_datatype, + pathkey->pk_opfamily); appendStringInfoString(buf, delim); deparseExpr(em_expr, context); - if (pathkey->pk_strategy == BTLessStrategyNumber) - appendStringInfoString(buf, " ASC"); - else - appendStringInfoString(buf, " DESC"); - if (pathkey->pk_nulls_first) - appendStringInfoString(buf, " NULLS FIRST"); - else - appendStringInfoString(buf, " NULLS LAST"); + /* + * Here we need to use the expression's actual type to discover + * whether the desired operator will be the default or not. + */ + appendOrderBySuffix(oprid, exprType((Node *) em_expr), + pathkey->pk_nulls_first, context); delim = ", "; } |