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,