@ -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 ) ;
@ -904,6 +907,33 @@ is_foreign_param(PlannerInfo *root,
return false ;
}
/*
* 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
@ -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 = " , " ;
}