v17-0004-pushdown-aggregation-foreign-table.patch
application/octet-stream
Filename: v17-0004-pushdown-aggregation-foreign-table.patch
Type: application/octet-stream
Part: 0
Message:
RE: WIP: Aggregation push-down
Patch
Format: unified
Series: patch v17-0004
| File | + | − |
|---|---|---|
| contrib/postgres_fdw/deparse.c | 213 | 7 |
| contrib/postgres_fdw/postgres_fdw.c | 144 | 23 |
| contrib/postgres_fdw/postgres_fdw.h | 5 | 2 |
| src/backend/optimizer/path/allpaths.c | 29 | 1 |
| src/backend/optimizer/plan/planner.c | 1 | 0 |
| src/backend/optimizer/util/relnode.c | 51 | 3 |
| src/backend/utils/adt/numeric.c | 0 | 1 |
| src/include/nodes/pathnodes.h | 1 | 0 |
| src/include/optimizer/pathnode.h | 2 | 0 |
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index ad37a74221..ef4d8ab120 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -100,8 +100,21 @@ typedef struct deparse_expr_cxt
* a base relation. */
StringInfo buf; /* output buffer to append to */
List **params_list; /* exprs that will become remote Params */
+ RelAggInfo *agg_info;
+ List **avg_attrs;
+ List **sum_attrs;
+ AggSplit aggsplit;
} deparse_expr_cxt;
+typedef enum {
+ AGGFUNC_SUM,
+ AGGFUNC_AVG,
+ AGGFUNC_COUNT,
+ AGGFUNC_MAX,
+ AGGFUNC_MIN,
+ AGGFUNC_OTHER
+} AggFuncType;
+
#define REL_ALIAS_PREFIX "r"
/* Handy macro to add relation name qualification */
#define ADD_REL_QUALIFIER(buf, varno) \
@@ -184,6 +197,7 @@ static void appendAggOrderBy(List *orderList, List *targetList,
static void appendFunctionName(Oid funcid, deparse_expr_cxt *context);
static Node *deparseSortGroupClause(Index ref, List *tlist, bool force_colno,
deparse_expr_cxt *context);
+static AggFuncType getAggFunc(Aggref* aggref);
/*
* Helper functions
@@ -693,6 +707,7 @@ foreign_expr_walker(Node *node,
case T_Aggref:
{
Aggref *agg = (Aggref *) node;
+ AggFuncType aggfunc_type = AGGFUNC_OTHER;
ListCell *lc;
/* Not safe to pushdown when not in grouping context */
@@ -700,8 +715,15 @@ foreign_expr_walker(Node *node,
return false;
/* Only non-split aggregates are pushable. */
- if (agg->aggsplit != AGGSPLIT_SIMPLE)
+ if (agg->aggsplit == AGGSPLIT_INITIAL_SERIAL) {
+ fpinfo->aggsplit = AGGSPLIT_INITIAL_SERIAL;
+ aggfunc_type = getAggFunc(agg);
+ if (aggfunc_type == AGGFUNC_OTHER) {
+ return false;
+ }
+ } else if (agg->aggsplit != AGGSPLIT_SIMPLE) {
return false;
+ }
/* As usual, it must be shippable. */
if (!is_shippable(agg->aggfnoid, ProcedureRelationId, fpinfo))
@@ -723,6 +745,18 @@ foreign_expr_walker(Node *node,
n = (Node *) tle->expr;
}
+ if (fpinfo->aggsplit == AGGSPLIT_INITIAL_SERIAL) {
+ bool pushdown = false;
+ if (nodeTag(n) == T_Var) {
+ Var *var = (Var*)n;
+ if (var->vartype == INT8OID) {
+ pushdown = true;
+ }
+ }
+ if (pushdown == false) {
+ return false;
+ }
+ }
if (!foreign_expr_walker(n, glob_cxt, &inner_cxt))
return false;
@@ -995,7 +1029,8 @@ void
deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel,
List *tlist, List *remote_conds, List *pathkeys,
bool has_final_sort, bool has_limit, bool is_subquery,
- List **retrieved_attrs, List **params_list)
+ List **retrieved_attrs, List **params_list, List **avg_attrs,
+ List **sum_attrs)
{
deparse_expr_cxt context;
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
@@ -1013,6 +1048,10 @@ deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel,
context.foreignrel = rel;
context.scanrel = IS_UPPER_REL(rel) ? fpinfo->outerrel : rel;
context.params_list = params_list;
+ context.agg_info = fpinfo->agg_info;
+ context.avg_attrs = avg_attrs;
+ context.sum_attrs = sum_attrs;
+ context.aggsplit = fpinfo->aggsplit;
/* Construct SELECT clause */
deparseSelectSql(tlist, is_subquery, retrieved_attrs, &context);
@@ -1075,7 +1114,7 @@ deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel,
*/
static void
deparseSelectSql(List *tlist, bool is_subquery, List **retrieved_attrs,
- deparse_expr_cxt *context)
+ deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
RelOptInfo *foreignrel = context->foreignrel;
@@ -1391,6 +1430,142 @@ get_jointype_name(JoinType jointype)
return NULL;
}
+/* Get function name */
+static StringInfo getFuncName(Oid funcid) {
+ StringInfo buf;
+ HeapTuple proctup;
+ Form_pg_proc procform;
+ const char* proname;
+
+ buf = makeStringInfo();
+ initStringInfo(buf);
+
+ proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+ procform = (Form_pg_proc)GETSTRUCT(proctup);
+
+ /* Print schema name only if it's not pg_catalog */
+ if (procform->pronamespace != PG_CATALOG_NAMESPACE)
+ {
+ const char* schemaname;
+
+ schemaname = get_namespace_name(procform->pronamespace);
+ appendStringInfo(buf, "%s.", quote_identifier(schemaname));
+ }
+
+ /* Always print the function name */
+ proname = NameStr(procform->proname);
+ appendStringInfoString(buf, quote_identifier(proname));
+
+ ReleaseSysCache(proctup);
+
+ return buf;
+}
+
+/* Get name of aggregation function */
+static AggFuncType getAggFunc(Aggref *aggref) {
+ AggFuncType type = AGGFUNC_OTHER;
+ StringInfo buf = getFuncName(aggref->aggfnoid);
+ if (strcmp(buf->data, "avg") == 0) {
+ type = AGGFUNC_AVG;
+ } else if (strcmp(buf->data, "sum") == 0) {
+ type = AGGFUNC_SUM;
+ } else if (strcmp(buf->data, "count") == 0) {
+ type = AGGFUNC_COUNT;
+ } else if (strcmp(buf->data, "min") == 0) {
+ type = AGGFUNC_MIN;
+ } else if (strcmp(buf->data, "max") == 0) {
+ type = AGGFUNC_MAX;
+ } else {
+ type = AGGFUNC_OTHER;
+ }
+ return type;
+}
+
+/* Deparse arg of aggref */
+static void deparseAggrefArg(Aggref * node, deparse_expr_cxt *context){
+ StringInfo buf = context->buf;
+ bool use_variadic;
+
+ use_variadic = node->aggvariadic;
+
+ appendStringInfoChar(buf, '(');
+
+ /* Add DISTINCT */
+ appendStringInfoString(buf, (node->aggdistinct != NIL) ? "DISTINCT " : "");
+
+ if (AGGKIND_IS_ORDERED_SET(node->aggkind))
+ {
+ /* Add WITHIN GROUP (ORDER BY ..) */
+ ListCell* arg;
+ bool first = true;
+
+ Assert(!node->aggvariadic);
+ Assert(node->aggorder != NIL);
+
+ foreach(arg, node->aggdirectargs)
+ {
+ if (!first)
+ appendStringInfoString(buf, ", ");
+ first = false;
+
+ deparseExpr((Expr*)lfirst(arg), context);
+ }
+
+ appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
+ appendAggOrderBy(node->aggorder, node->args, context);
+ }
+ else
+ {
+ /* aggstar can be set only in zero-argument aggregates */
+ if (node->aggstar)
+ appendStringInfoChar(buf, '*');
+ else
+ {
+ ListCell* arg;
+ bool first = true;
+
+ /* Add all the arguments */
+ foreach(arg, node->args)
+ {
+ TargetEntry* tle = (TargetEntry*)lfirst(arg);
+ Node* n = (Node*)tle->expr;
+
+ if (tle->resjunk)
+ continue;
+
+ if (!first)
+ appendStringInfoString(buf, ", ");
+ first = false;
+
+ /* Add VARIADIC */
+ if (use_variadic && lnext(node->args, arg) == NULL)
+ appendStringInfoString(buf, "VARIADIC ");
+
+ deparseExpr((Expr*)n, context);
+ }
+ }
+
+ /* Add ORDER BY */
+ if (node->aggorder != NIL)
+ {
+ appendStringInfoString(buf, " ORDER BY ");
+ appendAggOrderBy(node->aggorder, node->args, context);
+ }
+ }
+
+ /* Add FILTER (WHERE ..) */
+ if (node->aggfilter != NULL)
+ {
+ appendStringInfoString(buf, ") FILTER (WHERE ");
+ deparseExpr((Expr*)node->aggfilter, context);
+ }
+
+ appendStringInfoChar(buf, ')');
+}
+
+
/*
* Deparse given targetlist and append it to context->buf.
*
@@ -1417,13 +1592,33 @@ deparseExplicitTargetList(List *tlist,
foreach(lc, tlist)
{
TargetEntry *tle = lfirst_node(TargetEntry, lc);
+ AggFuncType type;
if (i > 0)
appendStringInfoString(buf, ", ");
else if (is_returning)
appendStringInfoString(buf, " RETURNING ");
- deparseExpr((Expr *) tle->expr, context);
+ if ((nodeTag((Expr*)tle->expr) == T_Aggref)
+ && (context->aggsplit == AGGSPLIT_INITIAL_SERIAL)) {
+ type = getAggFunc((Aggref*)tle->expr);
+ if (type == AGGFUNC_AVG) {
+ *(context->avg_attrs) = lappend_int(*(context->avg_attrs), i + 1);
+ appendStringInfoString(buf, "count");
+ deparseAggrefArg((Aggref*)tle->expr, context);
+
+ appendStringInfoString(buf, ", ");
+ appendStringInfoString(buf, "sum");
+ deparseAggrefArg((Aggref*)tle->expr, context);
+ } else if (type == AGGFUNC_SUM) {
+ *(context->sum_attrs) = lappend_int(*(context->sum_attrs), i + 1);
+ deparseExpr((Expr*)tle->expr, context);
+ } else {
+ deparseExpr((Expr*)tle->expr, context);
+ }
+ } else {
+ deparseExpr((Expr*)tle->expr, context);
+ }
*retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
i++;
@@ -1646,6 +1841,8 @@ deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
if (make_subquery)
{
List *retrieved_attrs;
+ List *avg_attrs = NIL;
+ List *sum_attrs = NIL;
int ncols;
/*
@@ -1661,7 +1858,8 @@ deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
deparseSelectStmtForRel(buf, root, foreignrel, NIL,
fpinfo->remote_conds, NIL,
false, false, true,
- &retrieved_attrs, params_list);
+ &retrieved_attrs, params_list, &avg_attrs,
+ &sum_attrs);
appendStringInfoChar(buf, ')');
/* Append the relation alias. */
@@ -3148,9 +3346,17 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
Query *query = context->root->parse;
ListCell *lc;
bool first = true;
+ List *groupClause;
+
+ if (context->agg_info) {
+ groupClause = context->agg_info->group_clauses;
+ }
+ else {
+ groupClause = query->groupClause;
+ }
/* Nothing to be done, if there's no GROUP BY clause in the query. */
- if (!query->groupClause)
+ if (!groupClause)
return;
appendStringInfoString(buf, " GROUP BY ");
@@ -3161,7 +3367,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
*/
Assert(!query->groupingSets);
- foreach(lc, query->groupClause)
+ foreach(lc, groupClause)
{
SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index a46834a377..f99a0e2b7f 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -44,6 +44,8 @@
#include "utils/rel.h"
#include "utils/sampling.h"
#include "utils/selfuncs.h"
+#include "utils/numeric.h"
+#include "libpq/pqformat.h"
PG_MODULE_MAGIC;
@@ -72,6 +74,10 @@ enum FdwScanPrivateIndex
/* Integer representing the desired fetch_size */
FdwScanPrivateFetchSize,
+ FdwScanPrivateAvgAttrs,
+ FdwScanPrivateSumAttrs,
+ FdwScanPrivateAggSplit,
+
/*
* String describing join i.e. names of relations being joined and types
* of join, added when the scan is join
@@ -159,6 +165,10 @@ typedef struct PgFdwScanState
MemoryContext temp_cxt; /* context for per-tuple temporary data */
int fetch_size; /* number of tuples per fetch */
+
+ List *avg_attrs;
+ List *sum_attrs;
+ AggSplit aggsplit;
} PgFdwScanState;
/*
@@ -481,7 +491,7 @@ static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel,
JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel,
JoinPathExtraData *extra);
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
- Node *havingQual);
+ Node *havingQual, RelAggInfo *agg_info);
static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
RelOptInfo *rel);
static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -1190,6 +1200,8 @@ postgresGetForeignPlan(PlannerInfo *root,
bool has_final_sort = false;
bool has_limit = false;
ListCell *lc;
+ List *avg_attrs = NIL;
+ List *sum_attrs = NIL;
/*
* Get FDW private data created by postgresGetForeignUpperPaths(), if any.
@@ -1351,7 +1363,8 @@ postgresGetForeignPlan(PlannerInfo *root,
deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
remote_exprs, best_path->path.pathkeys,
has_final_sort, has_limit, false,
- &retrieved_attrs, ¶ms_list);
+ &retrieved_attrs, ¶ms_list, &avg_attrs,
+ &sum_attrs);
/* Remember remote_exprs for possible use by postgresPlanDirectModify */
fpinfo->final_remote_exprs = remote_exprs;
@@ -1360,9 +1373,12 @@ postgresGetForeignPlan(PlannerInfo *root,
* Build the fdw_private list that will be available to the executor.
* Items in the list must match order in enum FdwScanPrivateIndex.
*/
- fdw_private = list_make3(makeString(sql.data),
+ fdw_private = list_make4(makeString(sql.data),
retrieved_attrs,
- makeInteger(fpinfo->fetch_size));
+ makeInteger(fpinfo->fetch_size),
+ avg_attrs);
+ fdw_private = lappend(fdw_private, sum_attrs);
+ fdw_private = lappend(fdw_private, makeInteger(fpinfo->aggsplit));
if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
fdw_private = lappend(fdw_private,
makeString(fpinfo->relation_name));
@@ -1448,6 +1464,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
fsstate->fetch_size = intVal(list_nth(fsplan->fdw_private,
FdwScanPrivateFetchSize));
+ fsstate->avg_attrs = (List*)(list_nth(fsplan->fdw_private,
+ FdwScanPrivateAvgAttrs));
+
+ fsstate->sum_attrs = (List*)(list_nth(fsplan->fdw_private,
+ FdwScanPrivateSumAttrs));
+
+ fsstate->aggsplit = intVal(list_nth(fsplan->fdw_private,
+ FdwScanPrivateAggSplit));
+
/* Create contexts for batches of tuples and per-tuple temp workspace. */
fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
"postgres_fdw tuple data",
@@ -2710,6 +2735,8 @@ estimate_path_cost_size(PlannerInfo *root,
/* Required only to be passed to deparseSelectStmtForRel */
List *retrieved_attrs;
+ List *avg_attrs;
+ List *sum_attrs;
/*
* param_join_conds might contain both clauses that are safe to send
@@ -2743,7 +2770,8 @@ estimate_path_cost_size(PlannerInfo *root,
remote_conds, pathkeys,
fpextra ? fpextra->has_final_sort : false,
fpextra ? fpextra->has_limit : false,
- false, &retrieved_attrs, NULL);
+ false, &retrieved_attrs, NULL, &avg_attrs,
+ &sum_attrs);
/* Get the remote estimate */
conn = GetConnection(fpinfo->user, false);
@@ -2920,6 +2948,7 @@ estimate_path_cost_size(PlannerInfo *root,
double input_rows;
int numGroupCols;
double numGroups = 1;
+ List *group_exprs;
/* The upper relation should have its outer relation set */
Assert(outerrel);
@@ -2957,9 +2986,16 @@ estimate_path_cost_size(PlannerInfo *root,
/* Get number of grouping columns and possible number of groups */
numGroupCols = list_length(root->parse->groupClause);
+ numGroups = 100;
+ if (fpinfo->agg_info) {
+ group_exprs = fpinfo->agg_info->group_exprs;
+ }
+ else {
+ group_exprs = get_sortgrouplist_exprs(root->parse->groupClause,
+ fpinfo->grouped_tlist);
+ }
numGroups = estimate_num_groups(root,
- get_sortgrouplist_exprs(root->parse->groupClause,
- fpinfo->grouped_tlist),
+ group_exprs,
input_rows, NULL);
/*
@@ -5568,7 +5604,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
*/
static bool
foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
- Node *havingQual)
+ Node *havingQual, RelAggInfo *agg_info)
{
Query *query = root->parse;
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -5615,9 +5651,10 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
Expr *expr = (Expr *) lfirst(lc);
Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
ListCell *l;
+ List *groupClause = (agg_info != NULL) ? agg_info->group_clauses : query->groupClause;
/* Check whether this expression is part of GROUP BY clause */
- if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+ if (sgref && get_sortgroupref_clause_noerr(sgref, groupClause))
{
TargetEntry *tle;
@@ -5888,6 +5925,7 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
fpinfo->table = ifpinfo->table;
fpinfo->server = ifpinfo->server;
fpinfo->user = ifpinfo->user;
+ fpinfo->agg_info = extra->agg_info;
merge_fdw_options(fpinfo, ifpinfo, NULL);
/*
@@ -5896,7 +5934,8 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
* Use HAVING qual from extra. In case of child partition, it will have
* translated Vars.
*/
- if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
+ if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual,
+ extra->agg_info))
return;
/*
@@ -6329,6 +6368,7 @@ make_tuple_from_result_row(PGresult *res,
MemoryContext oldcontext;
ListCell *lc;
int j;
+ PgFdwScanState *festate = (fsstate == NULL) ? NULL : (PgFdwScanState*)fsstate->fdw_state;
Assert(row < PQntuples(res));
@@ -6370,13 +6410,43 @@ make_tuple_from_result_row(PGresult *res,
foreach(lc, retrieved_attrs)
{
int i = lfirst_int(lc);
- char *valstr;
-
- /* fetch next column's textual value */
- if (PQgetisnull(res, row, j))
- valstr = NULL;
- else
- valstr = PQgetvalue(res, row, j);
+ char *valstr = NULL;
+ char *countstr = NULL;
+ char *sumstr = NULL;
+ bool split_serial = false;
+
+ if (festate == NULL) {
+ split_serial = false;
+ } else if (festate->aggsplit == AGGSPLIT_INITIAL_SERIAL) {
+ split_serial = true;
+ } else {
+ split_serial = false;
+ }
+ if (split_serial == true) {
+ if (list_member_int(festate->avg_attrs, i)) {
+ /* fetch next column's textual value */
+ if (PQgetisnull(res, row, j)) {
+ countstr = NULL;
+ sumstr = NULL;
+ } else {
+ countstr = PQgetvalue(res, row, j);
+ j++;
+ sumstr = PQgetvalue(res, row, j);
+ }
+ } else {
+ /* fetch next column's textual value */
+ if (PQgetisnull(res, row, j))
+ valstr = NULL;
+ else
+ valstr = PQgetvalue(res, row, j);
+ }
+ } else {
+ /* fetch next column's textual value */
+ if (PQgetisnull(res, row, j))
+ valstr = NULL;
+ else
+ valstr = PQgetvalue(res, row, j);
+ }
/*
* convert value to internal representation
@@ -6388,12 +6458,63 @@ make_tuple_from_result_row(PGresult *res,
{
/* ordinary column */
Assert(i <= tupdesc->natts);
- nulls[i - 1] = (valstr == NULL);
- /* Apply the input function even to nulls, to support domains */
- values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
- valstr,
- attinmeta->attioparams[i - 1],
- attinmeta->atttypmods[i - 1]);
+
+ if (split_serial == false) {
+ nulls[i - 1] = (valstr == NULL);
+ /* Apply the input function even to nulls, to support domains */
+ values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
+ valstr,
+ attinmeta->attioparams[i - 1],
+ attinmeta->atttypmods[i - 1]);
+ } else {
+ if (list_member_int(festate->avg_attrs, i)) {
+ StringInfoData buf;
+ int64 count;
+ Numeric sumN;
+ bytea* sumX;
+ Datum temp;
+ bytea* result;
+
+ nulls[i - 1] = (sumstr == NULL);
+
+ count = DatumGetInt32(DirectFunctionCall1(int8in, PointerGetDatum(countstr)));
+
+ sumN = DatumGetNumeric(DirectFunctionCall3(numeric_in, PointerGetDatum(sumstr), 0,
+ Int32GetDatum(attinmeta->atttypmods[i - 1])));
+ temp = DirectFunctionCall1(numeric_send, NumericGetDatum(sumN));
+ sumX = DatumGetByteaPP(temp);
+ pq_begintypsend(&buf);
+ pq_sendint64(&buf, count);
+ pq_sendbytes(&buf, VARDATA_ANY(sumX), VARSIZE_ANY_EXHDR(sumX));
+ result = pq_endtypsend(&buf);
+ values[i - 1] = PointerGetDatum(result);
+ } else if (list_member_int(festate->sum_attrs, i)) {
+ StringInfoData buf;
+ Numeric sumN;
+ bytea* sumX;
+ Datum temp;
+ bytea* result;
+
+ nulls[i - 1] = (valstr == NULL);
+
+ sumN = DatumGetNumeric(DirectFunctionCall3(numeric_in, PointerGetDatum(valstr), 0,
+ Int32GetDatum(attinmeta->atttypmods[i - 1])));
+ temp = DirectFunctionCall1(numeric_send, NumericGetDatum(sumN));
+ sumX = DatumGetByteaPP(temp);
+ pq_begintypsend(&buf);
+ pq_sendint64(&buf, 1);
+ pq_sendbytes(&buf, VARDATA_ANY(sumX), VARSIZE_ANY_EXHDR(sumX));
+ result = pq_endtypsend(&buf);
+ values[i - 1] = PointerGetDatum(result);
+ } else {
+ nulls[i - 1] = (valstr == NULL);
+ /* Apply the input function even to nulls, to support domains */
+ values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
+ valstr,
+ attinmeta->attioparams[i - 1],
+ attinmeta->atttypmods[i - 1]);
+ }
+ }
}
else if (i == SelfItemPointerAttributeNumber)
{
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index eef410db39..40c564abef 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -122,6 +122,8 @@ typedef struct PgFdwRelationInfo
* representing the relation.
*/
int relation_index;
+ RelAggInfo *agg_info;
+ AggSplit aggsplit;
} PgFdwRelationInfo;
/* in postgres_fdw.c */
@@ -200,8 +202,9 @@ extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root,
RelOptInfo *foreignrel, List *tlist,
List *remote_conds, List *pathkeys,
bool has_final_sort, bool has_limit,
- bool is_subquery,
- List **retrieved_attrs, List **params_list);
+ bool is_subquery, List **retrieved_attrs,
+ List **params_list, List **avg_attrs,
+ List **sum_attrs);
extern const char *get_jointype_name(JoinType jointype);
/* in shippable.c */
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index e0b5418de4..0fd00ef2bd 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -49,7 +49,6 @@
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
-
/* results of subquery_is_pushdown_safe */
typedef struct pushdown_safety_info
{
@@ -540,8 +539,37 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
case RTE_RELATION:
if (rte->relkind == RELKIND_FOREIGN_TABLE)
{
+ RelOptInfo* rel_grouped;
+ RelAggInfo* agg_info;
+ GroupPathExtraData extra;
+
/* Foreign table */
set_foreign_pathlist(root, rel, rte);
+
+ /* Add paths to the grouped relation if one exists. */
+ rel_grouped = find_grouped_rel(root, rel->relids,
+ &agg_info);
+ if (rel_grouped) {
+ extra.target_parallel_safe = false;
+ extra.targetList = root->parse->targetList;
+ extra.havingQual = root->parse->havingQual;
+ extra.partial_costs_set = false;
+ extra.agg_info = agg_info;
+
+ if (enable_partitionwise_aggregate && !root->parse->groupingSets)
+ extra.patype = PARTITIONWISE_AGGREGATE_FULL;
+ else
+ extra.patype = PARTITIONWISE_AGGREGATE_NONE;
+
+ rel_grouped->fdwroutine->GetForeignUpperPaths(root, UPPERREL_GROUP_AGG,
+ rel, rel_grouped, (void*)&extra);
+ if (rel_grouped->pathlist != NIL) {
+ set_cheapest(rel_grouped);
+ }
+ else {
+ del_grouped_rel(root, rel_grouped, agg_info);
+ }
+ }
}
else if (rte->tablesample != NULL)
{
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 6fe12d08ce..0f6b787a91 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -3882,6 +3882,7 @@ create_grouping_paths(PlannerInfo *root,
extra.havingQual = parse->havingQual;
extra.targetList = parse->targetList;
extra.partial_costs_set = false;
+ extra.agg_info = NULL;
/*
* Determine whether partitionwise aggregation is in theory possible.
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 4164f4b0e4..0854d426f9 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -412,9 +412,7 @@ build_simple_grouped_rel(PlannerInfo *root, int relid,
* TODO Consider relaxing some of these restrictions.
*/
rte = root->simple_rte_array[rel_plain->relid];
- if (rte->rtekind != RTE_RELATION ||
- rte->relkind == RELKIND_FOREIGN_TABLE ||
- rte->tablesample != NULL)
+ if (rte->rtekind != RTE_RELATION || rte->tablesample != NULL)
return NULL;
/*
@@ -443,6 +441,10 @@ build_simple_grouped_rel(PlannerInfo *root, int relid,
*/
rel_grouped = makeNode(RelOptInfo);
memcpy(rel_grouped, rel_plain, sizeof(RelOptInfo));
+ if (rte->relkind == RELKIND_FOREIGN_TABLE) {
+ rel_grouped->fdw_private = NULL;
+ rel_grouped->reloptkind = RELOPT_UPPER_REL;
+ }
/*
* Note on consider_startup: while the AGG_HASHED strategy needs the whole
@@ -658,6 +660,39 @@ add_rel_info(RelInfoList *list, void *data)
}
}
+/*
+ * del_rel_info
+ * Delete relation specific info to a list, and also add it to the auxiliary
+ * hashtable if there is one.
+ */
+static void
+del_rel_info(RelInfoList* list, void* data)
+{
+ Assert(IsA(data, RelOptInfo) || IsA(data, RelAggInfo));
+
+ /* GEQO requires us to append the new joinrel to the end of the list! */
+ list->items = list_delete(list->items, data);
+
+ /* store it into the auxiliary hashtable if there is one. */
+ if (list->hash)
+ {
+ Relids relids;
+ bool found;
+
+ if (IsA(data, RelOptInfo))
+ relids = ((RelOptInfo*)data)->relids;
+ else if (IsA(data, RelAggInfo))
+ relids = ((RelAggInfo*)data)->relids;
+
+ hash_search(list->hash,
+ &relids,
+ HASH_REMOVE,
+ &found);
+ Assert(!found);
+ }
+}
+
+
/*
* add_join_rel
* Add given join relation to the list of join relations in the given
@@ -682,6 +717,19 @@ add_grouped_rel(PlannerInfo *root, RelOptInfo *rel, RelAggInfo *agg_info)
add_rel_info(root->agg_info_list, agg_info);
}
+/*
+ * del_grouped_rel
+ * Del grouped base or join relation to the list of grouped relations in
+ * the given PlannerInfo. Also add the corresponding RelAggInfo to
+ * agg_info_list.
+ */
+void
+del_grouped_rel(PlannerInfo* root, RelOptInfo* rel, RelAggInfo* agg_info)
+{
+ del_rel_info(&root->upper_rels[UPPERREL_PARTIAL_GROUP_AGG], rel);
+ del_rel_info(root->agg_info_list, agg_info);
+}
+
/*
* find_grouped_rel
* Returns grouped relation entry (base or join relation) corresponding to
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 1773fa292e..4cf757c676 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -4874,7 +4874,6 @@ int8_avg_serialize(PG_FUNCTION_ARGS)
{
Datum temp;
NumericVar num;
-
init_var(&num);
#ifdef HAVE_INT128
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 98be8b7de2..1b35d94035 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -2593,6 +2593,7 @@ typedef struct
Node *havingQual;
List *targetList;
PartitionwiseAggregateType patype;
+ RelAggInfo* agg_info;
} GroupPathExtraData;
/*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 47afa7ed1a..e902f11da0 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -301,6 +301,8 @@ extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
extern void add_grouped_rel(PlannerInfo *root, RelOptInfo *rel,
RelAggInfo *agg_info);
+extern void del_grouped_rel(PlannerInfo* root, RelOptInfo* rel,
+ RelAggInfo* agg_info);
extern RelOptInfo *find_grouped_rel(PlannerInfo *root, Relids relids,
RelAggInfo **agg_info_p);
extern RelOptInfo *build_join_rel(PlannerInfo *root,