Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3e641ab

Browse files
author
Commitfest Bot
committed
[CF 51/5128] Allow partition-wise join when whole row var is needed
This commit was automatically generated by a robot at cfbot.cputube.org. It is based on patches submitted to the PostgreSQL mailing lists and registered in the PostgreSQL Commitfest application. This branch will be overwritten each time a new patch version is posted to the email thread, and also periodically to check for bitrot caused by changes on the master branch. Commitfest entry: https://commitfest.postgresql.org/51/5128 Patch(es): https://www.postgresql.org/message-id/dfe2031412085e376c95b6887a63e4ac@postgrespro.ru Author(s): Ashutosh Bapat, Alexander Pyhalov
2 parents 53a4936 + 4b565fe commit 3e641ab

File tree

19 files changed

+795
-125
lines changed

19 files changed

+795
-125
lines changed

contrib/postgres_fdw/deparse.c

Lines changed: 159 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ static void deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context);
164164
static void deparseNullTest(NullTest *node, deparse_expr_cxt *context);
165165
static void deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context);
166166
static void deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context);
167+
static void deparseConvertRowtypeExpr(ConvertRowtypeExpr *cre,
168+
deparse_expr_cxt *context);
167169
static void printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod,
168170
deparse_expr_cxt *context);
169171
static void printRemotePlaceholder(Oid paramtype, int32 paramtypmod,
@@ -200,9 +202,9 @@ static Node *deparseSortGroupClause(Index ref, List *tlist, bool force_colno,
200202
/*
201203
* Helper functions
202204
*/
203-
static bool is_subquery_var(Var *node, RelOptInfo *foreignrel,
205+
static bool is_subquery_var(Node *node, Index varno, int varattno, RelOptInfo *foreignrel,
204206
int *relno, int *colno);
205-
static void get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
207+
static void get_relation_column_alias_ids(Node *node, Index varno, int varattno, RelOptInfo *foreignrel,
206208
int *relno, int *colno);
207209

208210

@@ -1186,18 +1188,25 @@ build_tlist_to_deparse(RelOptInfo *foreignrel)
11861188

11871189
/*
11881190
* We require columns specified in foreignrel->reltarget->exprs and those
1189-
* required for evaluating the local conditions.
1191+
* required for evaluating the local conditions. Child relation's
1192+
* targetlist or local conditions may have ConvertRowtypeExpr when parent
1193+
* whole-row Vars were translated. We need to include those in targetlist
1194+
* to be pushed down to match the targetlists produced for the joining
1195+
* relations (in case we are using subqueries in the deparsed query) and
1196+
* also to match the targetlist of outer plan if foreign scan has one.
11901197
*/
11911198
tlist = add_to_flat_tlist(tlist,
11921199
pull_var_clause((Node *) foreignrel->reltarget->exprs,
1193-
PVC_RECURSE_PLACEHOLDERS));
1200+
PVC_RECURSE_PLACEHOLDERS |
1201+
PVC_INCLUDE_CONVERTROWTYPES));
11941202
foreach(lc, fpinfo->local_conds)
11951203
{
11961204
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
11971205

11981206
tlist = add_to_flat_tlist(tlist,
11991207
pull_var_clause((Node *) rinfo->clause,
1200-
PVC_RECURSE_PLACEHOLDERS));
1208+
PVC_RECURSE_PLACEHOLDERS |
1209+
PVC_INCLUDE_CONVERTROWTYPES));
12011210
}
12021211

12031212
return tlist;
@@ -2298,7 +2307,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
22982307

22992308
appendStringInfoString(buf, "UPDATE ");
23002309
deparseRelation(buf, rel);
2301-
if (foreignrel->reloptkind == RELOPT_JOINREL)
2310+
if (IS_JOIN_REL(foreignrel))
23022311
appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
23032312
appendStringInfoString(buf, " SET ");
23042313

@@ -2325,7 +2334,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
23252334

23262335
reset_transmission_modes(nestlevel);
23272336

2328-
if (foreignrel->reloptkind == RELOPT_JOINREL)
2337+
if (IS_JOIN_REL(foreignrel))
23292338
{
23302339
List *ignore_conds = NIL;
23312340

@@ -2341,7 +2350,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
23412350
if (additional_conds != NIL)
23422351
list_free_deep(additional_conds);
23432352

2344-
if (foreignrel->reloptkind == RELOPT_JOINREL)
2353+
if (IS_JOIN_REL(foreignrel))
23452354
deparseExplicitTargetList(returningList, true, retrieved_attrs,
23462355
&context);
23472356
else
@@ -2406,10 +2415,10 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
24062415

24072416
appendStringInfoString(buf, "DELETE FROM ");
24082417
deparseRelation(buf, rel);
2409-
if (foreignrel->reloptkind == RELOPT_JOINREL)
2418+
if (IS_JOIN_REL(foreignrel))
24102419
appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
24112420

2412-
if (foreignrel->reloptkind == RELOPT_JOINREL)
2421+
if (IS_JOIN_REL(foreignrel))
24132422
{
24142423
List *ignore_conds = NIL;
24152424

@@ -2424,7 +2433,7 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
24242433
if (additional_conds != NIL)
24252434
list_free_deep(additional_conds);
24262435

2427-
if (foreignrel->reloptkind == RELOPT_JOINREL)
2436+
if (IS_JOIN_REL(foreignrel))
24282437
deparseExplicitTargetList(returningList, true, retrieved_attrs,
24292438
&context);
24302439
else
@@ -2928,6 +2937,10 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
29282937
case T_Aggref:
29292938
deparseAggref((Aggref *) node, context);
29302939
break;
2940+
case T_ConvertRowtypeExpr:
2941+
deparseConvertRowtypeExpr(castNode(ConvertRowtypeExpr, node),
2942+
context);
2943+
break;
29312944
default:
29322945
elog(ERROR, "unsupported expression type for deparse: %d",
29332946
(int) nodeTag(node));
@@ -2958,7 +2971,8 @@ deparseVar(Var *node, deparse_expr_cxt *context)
29582971
* subquery, use the relation and column alias to the Var provided by the
29592972
* subquery, instead of the remote name.
29602973
*/
2961-
if (is_subquery_var(node, context->scanrel, &relno, &colno))
2974+
if (is_subquery_var((Node *) node, node->varno, node->varattno, context->scanrel, &relno,
2975+
&colno))
29622976
{
29632977
appendStringInfo(context->buf, "%s%d.%s%d",
29642978
SUBQUERY_REL_ALIAS_PREFIX, relno,
@@ -3740,6 +3754,115 @@ deparseAggref(Aggref *node, deparse_expr_cxt *context)
37403754
appendStringInfoChar(buf, ')');
37413755
}
37423756

3757+
/*
3758+
* Deparse a ConvertRowtypeExpr node.
3759+
*
3760+
* The function handles ConvertRowtypeExpr nodes constructed to convert a child
3761+
* tuple to parent tuple. The function deparses a ConvertRowtypeExpr as a ROW
3762+
* expression child column refrences arranged according to the tuple conversion
3763+
* map for converting child tuple to that of the parent. The ROW expression is
3764+
* decorated with CASE similar to deparseColumnRef() to take care of
3765+
* ConvertRowtypeExpr nodes on nullable side of the join.
3766+
*/
3767+
static void
3768+
deparseConvertRowtypeExpr(ConvertRowtypeExpr *cre,
3769+
deparse_expr_cxt *context)
3770+
{
3771+
ConvertRowtypeExpr *expr = cre;
3772+
Var *child_var;
3773+
TupleDesc parent_desc;
3774+
TupleDesc child_desc;
3775+
TupleConversionMap *conv_map;
3776+
AttrMap *attrMap;
3777+
int cnt;
3778+
bool qualify_col = (bms_num_members(context->scanrel->relids) > 1);
3779+
StringInfo buf = context->buf;
3780+
PlannerInfo *root = context->root;
3781+
int relno;
3782+
int colno;
3783+
bool first_col;
3784+
3785+
/*
3786+
* Multi-level partitioned hierarchies produce nested ConvertRowtypeExprs,
3787+
* where the top ConvertRowtypeExpr gives the parent relation and the leaf
3788+
* Var node gives the child relation. Fetch the leaf Var node
3789+
* corresponding to the lowest child.
3790+
*/
3791+
while (IsA(expr->arg, ConvertRowtypeExpr))
3792+
expr = castNode(ConvertRowtypeExpr, expr->arg);
3793+
child_var = castNode(Var, expr->arg);
3794+
Assert(child_var->varattno == 0);
3795+
3796+
/*
3797+
* If the child Var belongs to the foreign relation that is deparsed as a
3798+
* subquery, use the relation and column alias to the child Var provided
3799+
* by the subquery, instead of the remote name.
3800+
*/
3801+
if (is_subquery_var((Node *) cre, child_var->varno, child_var->varattno, context->scanrel,
3802+
&relno, &colno))
3803+
{
3804+
appendStringInfo(context->buf, "%s%d.%s%d",
3805+
SUBQUERY_REL_ALIAS_PREFIX, relno,
3806+
SUBQUERY_COL_ALIAS_PREFIX, colno);
3807+
return;
3808+
}
3809+
3810+
/* Construct the conversion map. */
3811+
parent_desc = lookup_rowtype_tupdesc(cre->resulttype, -1);
3812+
child_desc = lookup_rowtype_tupdesc(child_var->vartype,
3813+
child_var->vartypmod);
3814+
conv_map = convert_tuples_by_name(child_desc, parent_desc);
3815+
3816+
ReleaseTupleDesc(parent_desc);
3817+
ReleaseTupleDesc(child_desc);
3818+
3819+
/* If no conversion is needed, deparse the child Var as is. */
3820+
if (conv_map == NULL)
3821+
{
3822+
deparseVar(child_var, context);
3823+
return;
3824+
}
3825+
3826+
attrMap = conv_map->attrMap;
3827+
3828+
/*
3829+
* In case the whole-row reference is under an outer join then it has to
3830+
* go NULL whenever the rest of the row goes NULL. Deparsing a join query
3831+
* would always involve multiple relations, thus qualify_col would be
3832+
* true.
3833+
*/
3834+
if (qualify_col)
3835+
{
3836+
appendStringInfoString(buf, "CASE WHEN (");
3837+
ADD_REL_QUALIFIER(buf, child_var->varno);
3838+
appendStringInfoString(buf, "*)::text IS NOT NULL THEN ");
3839+
}
3840+
3841+
/* Construct ROW expression according to the conversion map. */
3842+
appendStringInfoString(buf, "ROW(");
3843+
first_col = true;
3844+
for (cnt = 0; cnt < parent_desc->natts; cnt++)
3845+
{
3846+
/* Ignore dropped columns. */
3847+
if (attrMap->attnums[cnt] == 0)
3848+
continue;
3849+
3850+
if (!first_col)
3851+
appendStringInfoString(buf, ", ");
3852+
deparseColumnRef(buf, child_var->varno, attrMap->attnums[cnt],
3853+
planner_rt_fetch(child_var->varno, root),
3854+
qualify_col);
3855+
first_col = false;
3856+
}
3857+
appendStringInfoChar(buf, ')');
3858+
3859+
/* Complete the CASE WHEN statement started above. */
3860+
if (qualify_col)
3861+
appendStringInfoString(buf, " END");
3862+
3863+
free_conversion_map(conv_map);
3864+
}
3865+
37433866
/*
37443867
* Append ORDER BY within aggregate function.
37453868
*/
@@ -4102,12 +4225,18 @@ deparseSortGroupClause(Index ref, List *tlist, bool force_colno,
41024225

41034226

41044227
/*
4105-
* Returns true if given Var is deparsed as a subquery output column, in
4228+
* Returns true if given Node is deparsed as a subquery output column, in
41064229
* which case, *relno and *colno are set to the IDs for the relation and
4107-
* column alias to the Var provided by the subquery.
4230+
* column alias to the Node provided by the subquery.
4231+
*
4232+
* We do not allow relations with PlaceHolderVars to be pushed down. We call
4233+
* this function only for simple or join relations, whose targetlists can
4234+
* contain only Var and ConvertRowtypeExpr (in case of child-joins) nodes
4235+
* (apart from PHVs). So support only Var and ConvertRowtypeExpr nodes.
41084236
*/
41094237
static bool
4110-
is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
4238+
is_subquery_var(Node *node, Index varno, int varattno, RelOptInfo *foreignrel,
4239+
int *relno, int *colno)
41114240
{
41124241
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
41134242
RelOptInfo *outerrel = fpinfo->outerrel;
@@ -4116,6 +4245,8 @@ is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
41164245
/* Should only be called in these cases. */
41174246
Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
41184247

4248+
Assert(IsA(node, ConvertRowtypeExpr) || IsA(node, Var));
4249+
41194250
/*
41204251
* If the given relation isn't a join relation, it doesn't have any lower
41214252
* subqueries, so the Var isn't a subquery output column.
@@ -4127,40 +4258,40 @@ is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
41274258
* If the Var doesn't belong to any lower subqueries, it isn't a subquery
41284259
* output column.
41294260
*/
4130-
if (!bms_is_member(node->varno, fpinfo->lower_subquery_rels))
4261+
if (!bms_is_member(varno, fpinfo->lower_subquery_rels))
41314262
return false;
41324263

4133-
if (bms_is_member(node->varno, outerrel->relids))
4264+
if (bms_is_member(varno, outerrel->relids))
41344265
{
41354266
/*
41364267
* If outer relation is deparsed as a subquery, the Var is an output
41374268
* column of the subquery; get the IDs for the relation/column alias.
41384269
*/
41394270
if (fpinfo->make_outerrel_subquery)
41404271
{
4141-
get_relation_column_alias_ids(node, outerrel, relno, colno);
4272+
get_relation_column_alias_ids(node, varno, varattno, outerrel, relno, colno);
41424273
return true;
41434274
}
41444275

41454276
/* Otherwise, recurse into the outer relation. */
4146-
return is_subquery_var(node, outerrel, relno, colno);
4277+
return is_subquery_var(node, varno, varattno, outerrel, relno, colno);
41474278
}
41484279
else
41494280
{
4150-
Assert(bms_is_member(node->varno, innerrel->relids));
4281+
Assert(bms_is_member(varno, innerrel->relids));
41514282

41524283
/*
41534284
* If inner relation is deparsed as a subquery, the Var is an output
41544285
* column of the subquery; get the IDs for the relation/column alias.
41554286
*/
41564287
if (fpinfo->make_innerrel_subquery)
41574288
{
4158-
get_relation_column_alias_ids(node, innerrel, relno, colno);
4289+
get_relation_column_alias_ids(node, varno, varattno, innerrel, relno, colno);
41594290
return true;
41604291
}
41614292

41624293
/* Otherwise, recurse into the inner relation. */
4163-
return is_subquery_var(node, innerrel, relno, colno);
4294+
return is_subquery_var(node, varno, varattno, innerrel, relno, colno);
41644295
}
41654296
}
41664297

@@ -4169,7 +4300,7 @@ is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
41694300
* given relation, which are returned into *relno and *colno.
41704301
*/
41714302
static void
4172-
get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
4303+
get_relation_column_alias_ids(Node *node, Index varno, int varattno, RelOptInfo *foreignrel,
41734304
int *relno, int *colno)
41744305
{
41754306
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
@@ -4189,11 +4320,12 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
41894320
* Match reltarget entries only on varno/varattno. Ideally there
41904321
* would be some cross-check on varnullingrels, but it's unclear what
41914322
* to do exactly; we don't have enough context to know what that value
4192-
* should be.
4323+
* should be. Also match posible converted whole-row references.
41934324
*/
4194-
if (IsA(tlvar, Var) &&
4195-
tlvar->varno == node->varno &&
4196-
tlvar->varattno == node->varattno)
4325+
if ((IsA(tlvar, Var) &&
4326+
tlvar->varno == varno &&
4327+
tlvar->varattno == varattno)
4328+
|| is_equal_converted_whole_row_references((Node *) tlvar, node))
41974329
{
41984330
*colno = i;
41994331
return;

0 commit comments

Comments
 (0)