@@ -164,6 +164,8 @@ static void deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context);
164
164
static void deparseNullTest (NullTest * node , deparse_expr_cxt * context );
165
165
static void deparseCaseExpr (CaseExpr * node , deparse_expr_cxt * context );
166
166
static void deparseArrayExpr (ArrayExpr * node , deparse_expr_cxt * context );
167
+ static void deparseConvertRowtypeExpr (ConvertRowtypeExpr * cre ,
168
+ deparse_expr_cxt * context );
167
169
static void printRemoteParam (int paramindex , Oid paramtype , int32 paramtypmod ,
168
170
deparse_expr_cxt * context );
169
171
static void printRemotePlaceholder (Oid paramtype , int32 paramtypmod ,
@@ -200,9 +202,9 @@ static Node *deparseSortGroupClause(Index ref, List *tlist, bool force_colno,
200
202
/*
201
203
* Helper functions
202
204
*/
203
- static bool is_subquery_var (Var * node , RelOptInfo * foreignrel ,
205
+ static bool is_subquery_var (Node * node , Index varno , int varattno , RelOptInfo * foreignrel ,
204
206
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 ,
206
208
int * relno , int * colno );
207
209
208
210
@@ -1186,18 +1188,25 @@ build_tlist_to_deparse(RelOptInfo *foreignrel)
1186
1188
1187
1189
/*
1188
1190
* 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.
1190
1197
*/
1191
1198
tlist = add_to_flat_tlist (tlist ,
1192
1199
pull_var_clause ((Node * ) foreignrel -> reltarget -> exprs ,
1193
- PVC_RECURSE_PLACEHOLDERS ));
1200
+ PVC_RECURSE_PLACEHOLDERS |
1201
+ PVC_INCLUDE_CONVERTROWTYPES ));
1194
1202
foreach (lc , fpinfo -> local_conds )
1195
1203
{
1196
1204
RestrictInfo * rinfo = lfirst_node (RestrictInfo , lc );
1197
1205
1198
1206
tlist = add_to_flat_tlist (tlist ,
1199
1207
pull_var_clause ((Node * ) rinfo -> clause ,
1200
- PVC_RECURSE_PLACEHOLDERS ));
1208
+ PVC_RECURSE_PLACEHOLDERS |
1209
+ PVC_INCLUDE_CONVERTROWTYPES ));
1201
1210
}
1202
1211
1203
1212
return tlist ;
@@ -2298,7 +2307,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
2298
2307
2299
2308
appendStringInfoString (buf , "UPDATE " );
2300
2309
deparseRelation (buf , rel );
2301
- if (foreignrel -> reloptkind == RELOPT_JOINREL )
2310
+ if (IS_JOIN_REL ( foreignrel ) )
2302
2311
appendStringInfo (buf , " %s%d" , REL_ALIAS_PREFIX , rtindex );
2303
2312
appendStringInfoString (buf , " SET " );
2304
2313
@@ -2325,7 +2334,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
2325
2334
2326
2335
reset_transmission_modes (nestlevel );
2327
2336
2328
- if (foreignrel -> reloptkind == RELOPT_JOINREL )
2337
+ if (IS_JOIN_REL ( foreignrel ) )
2329
2338
{
2330
2339
List * ignore_conds = NIL ;
2331
2340
@@ -2341,7 +2350,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
2341
2350
if (additional_conds != NIL )
2342
2351
list_free_deep (additional_conds );
2343
2352
2344
- if (foreignrel -> reloptkind == RELOPT_JOINREL )
2353
+ if (IS_JOIN_REL ( foreignrel ) )
2345
2354
deparseExplicitTargetList (returningList , true, retrieved_attrs ,
2346
2355
& context );
2347
2356
else
@@ -2406,10 +2415,10 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
2406
2415
2407
2416
appendStringInfoString (buf , "DELETE FROM " );
2408
2417
deparseRelation (buf , rel );
2409
- if (foreignrel -> reloptkind == RELOPT_JOINREL )
2418
+ if (IS_JOIN_REL ( foreignrel ) )
2410
2419
appendStringInfo (buf , " %s%d" , REL_ALIAS_PREFIX , rtindex );
2411
2420
2412
- if (foreignrel -> reloptkind == RELOPT_JOINREL )
2421
+ if (IS_JOIN_REL ( foreignrel ) )
2413
2422
{
2414
2423
List * ignore_conds = NIL ;
2415
2424
@@ -2424,7 +2433,7 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
2424
2433
if (additional_conds != NIL )
2425
2434
list_free_deep (additional_conds );
2426
2435
2427
- if (foreignrel -> reloptkind == RELOPT_JOINREL )
2436
+ if (IS_JOIN_REL ( foreignrel ) )
2428
2437
deparseExplicitTargetList (returningList , true, retrieved_attrs ,
2429
2438
& context );
2430
2439
else
@@ -2928,6 +2937,10 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
2928
2937
case T_Aggref :
2929
2938
deparseAggref ((Aggref * ) node , context );
2930
2939
break ;
2940
+ case T_ConvertRowtypeExpr :
2941
+ deparseConvertRowtypeExpr (castNode (ConvertRowtypeExpr , node ),
2942
+ context );
2943
+ break ;
2931
2944
default :
2932
2945
elog (ERROR , "unsupported expression type for deparse: %d" ,
2933
2946
(int ) nodeTag (node ));
@@ -2958,7 +2971,8 @@ deparseVar(Var *node, deparse_expr_cxt *context)
2958
2971
* subquery, use the relation and column alias to the Var provided by the
2959
2972
* subquery, instead of the remote name.
2960
2973
*/
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 ))
2962
2976
{
2963
2977
appendStringInfo (context -> buf , "%s%d.%s%d" ,
2964
2978
SUBQUERY_REL_ALIAS_PREFIX , relno ,
@@ -3740,6 +3754,115 @@ deparseAggref(Aggref *node, deparse_expr_cxt *context)
3740
3754
appendStringInfoChar (buf , ')' );
3741
3755
}
3742
3756
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
+
3743
3866
/*
3744
3867
* Append ORDER BY within aggregate function.
3745
3868
*/
@@ -4102,12 +4225,18 @@ deparseSortGroupClause(Index ref, List *tlist, bool force_colno,
4102
4225
4103
4226
4104
4227
/*
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
4106
4229
* 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.
4108
4236
*/
4109
4237
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 )
4111
4240
{
4112
4241
PgFdwRelationInfo * fpinfo = (PgFdwRelationInfo * ) foreignrel -> fdw_private ;
4113
4242
RelOptInfo * outerrel = fpinfo -> outerrel ;
@@ -4116,6 +4245,8 @@ is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
4116
4245
/* Should only be called in these cases. */
4117
4246
Assert (IS_SIMPLE_REL (foreignrel ) || IS_JOIN_REL (foreignrel ));
4118
4247
4248
+ Assert (IsA (node , ConvertRowtypeExpr ) || IsA (node , Var ));
4249
+
4119
4250
/*
4120
4251
* If the given relation isn't a join relation, it doesn't have any lower
4121
4252
* 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)
4127
4258
* If the Var doesn't belong to any lower subqueries, it isn't a subquery
4128
4259
* output column.
4129
4260
*/
4130
- if (!bms_is_member (node -> varno , fpinfo -> lower_subquery_rels ))
4261
+ if (!bms_is_member (varno , fpinfo -> lower_subquery_rels ))
4131
4262
return false;
4132
4263
4133
- if (bms_is_member (node -> varno , outerrel -> relids ))
4264
+ if (bms_is_member (varno , outerrel -> relids ))
4134
4265
{
4135
4266
/*
4136
4267
* If outer relation is deparsed as a subquery, the Var is an output
4137
4268
* column of the subquery; get the IDs for the relation/column alias.
4138
4269
*/
4139
4270
if (fpinfo -> make_outerrel_subquery )
4140
4271
{
4141
- get_relation_column_alias_ids (node , outerrel , relno , colno );
4272
+ get_relation_column_alias_ids (node , varno , varattno , outerrel , relno , colno );
4142
4273
return true;
4143
4274
}
4144
4275
4145
4276
/* 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 );
4147
4278
}
4148
4279
else
4149
4280
{
4150
- Assert (bms_is_member (node -> varno , innerrel -> relids ));
4281
+ Assert (bms_is_member (varno , innerrel -> relids ));
4151
4282
4152
4283
/*
4153
4284
* If inner relation is deparsed as a subquery, the Var is an output
4154
4285
* column of the subquery; get the IDs for the relation/column alias.
4155
4286
*/
4156
4287
if (fpinfo -> make_innerrel_subquery )
4157
4288
{
4158
- get_relation_column_alias_ids (node , innerrel , relno , colno );
4289
+ get_relation_column_alias_ids (node , varno , varattno , innerrel , relno , colno );
4159
4290
return true;
4160
4291
}
4161
4292
4162
4293
/* 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 );
4164
4295
}
4165
4296
}
4166
4297
@@ -4169,7 +4300,7 @@ is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
4169
4300
* given relation, which are returned into *relno and *colno.
4170
4301
*/
4171
4302
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 ,
4173
4304
int * relno , int * colno )
4174
4305
{
4175
4306
PgFdwRelationInfo * fpinfo = (PgFdwRelationInfo * ) foreignrel -> fdw_private ;
@@ -4189,11 +4320,12 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
4189
4320
* Match reltarget entries only on varno/varattno. Ideally there
4190
4321
* would be some cross-check on varnullingrels, but it's unclear what
4191
4322
* 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.
4193
4324
*/
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 ))
4197
4329
{
4198
4330
* colno = i ;
4199
4331
return ;
0 commit comments