Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * appendinfo.c
4 : : * Routines for mapping between append parent(s) and children
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/optimizer/util/appendinfo.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/htup_details.h"
18 : : #include "access/table.h"
19 : : #include "foreign/fdwapi.h"
20 : : #include "nodes/makefuncs.h"
21 : : #include "nodes/nodeFuncs.h"
22 : : #include "optimizer/appendinfo.h"
23 : : #include "optimizer/pathnode.h"
24 : : #include "optimizer/planmain.h"
25 : : #include "parser/parsetree.h"
26 : : #include "utils/lsyscache.h"
27 : : #include "utils/rel.h"
28 : : #include "utils/syscache.h"
29 : :
30 : :
31 : : typedef struct
32 : : {
33 : : PlannerInfo *root;
34 : : int nappinfos;
35 : : AppendRelInfo **appinfos;
36 : : } adjust_appendrel_attrs_context;
37 : :
38 : : static void make_inh_translation_list(Relation oldrelation,
39 : : Relation newrelation,
40 : : Index newvarno,
41 : : AppendRelInfo *appinfo);
42 : : static Node *adjust_appendrel_attrs_mutator(Node *node,
43 : : adjust_appendrel_attrs_context *context);
44 : :
45 : :
46 : : /*
47 : : * make_append_rel_info
48 : : * Build an AppendRelInfo for the parent-child pair
49 : : */
50 : : AppendRelInfo *
1921 alvherre@alvh.no-ip. 51 :CBC 19299 : make_append_rel_info(Relation parentrel, Relation childrel,
52 : : Index parentRTindex, Index childRTindex)
53 : : {
54 : 19299 : AppendRelInfo *appinfo = makeNode(AppendRelInfo);
55 : :
56 : 19299 : appinfo->parent_relid = parentRTindex;
57 : 19299 : appinfo->child_relid = childRTindex;
58 : 19299 : appinfo->parent_reltype = parentrel->rd_rel->reltype;
59 : 19299 : appinfo->child_reltype = childrel->rd_rel->reltype;
1595 tgl@sss.pgh.pa.us 60 : 19299 : make_inh_translation_list(parentrel, childrel, childRTindex, appinfo);
1921 alvherre@alvh.no-ip. 61 : 19298 : appinfo->parent_reloid = RelationGetRelid(parentrel);
62 : :
63 : 19298 : return appinfo;
64 : : }
65 : :
66 : : /*
67 : : * make_inh_translation_list
68 : : * Build the list of translations from parent Vars to child Vars for
69 : : * an inheritance child, as well as a reverse-translation array.
70 : : *
71 : : * The reverse-translation array has an entry for each child relation
72 : : * column, which is either the 1-based index of the corresponding parent
73 : : * column, or 0 if there's no match (that happens for dropped child columns,
74 : : * as well as child columns beyond those of the parent, which are allowed in
75 : : * traditional inheritance though not partitioning).
76 : : *
77 : : * For paranoia's sake, we match type/collation as well as attribute name.
78 : : */
79 : : static void
80 : 19299 : make_inh_translation_list(Relation oldrelation, Relation newrelation,
81 : : Index newvarno,
82 : : AppendRelInfo *appinfo)
83 : : {
84 : 19299 : List *vars = NIL;
85 : : AttrNumber *pcolnos;
86 : 19299 : TupleDesc old_tupdesc = RelationGetDescr(oldrelation);
87 : 19299 : TupleDesc new_tupdesc = RelationGetDescr(newrelation);
88 : 19299 : Oid new_relid = RelationGetRelid(newrelation);
89 : 19299 : int oldnatts = old_tupdesc->natts;
90 : 19299 : int newnatts = new_tupdesc->natts;
91 : : int old_attno;
92 : 19299 : int new_attno = 0;
93 : :
94 : : /* Initialize reverse-translation array with all entries zero */
1595 tgl@sss.pgh.pa.us 95 : 19299 : appinfo->num_child_cols = newnatts;
96 : 19299 : appinfo->parent_colnos = pcolnos =
97 : 19299 : (AttrNumber *) palloc0(newnatts * sizeof(AttrNumber));
98 : :
1921 alvherre@alvh.no-ip. 99 [ + + ]: 72409 : for (old_attno = 0; old_attno < oldnatts; old_attno++)
100 : : {
101 : : Form_pg_attribute att;
102 : : char *attname;
103 : : Oid atttypid;
104 : : int32 atttypmod;
105 : : Oid attcollation;
106 : :
107 : 53111 : att = TupleDescAttr(old_tupdesc, old_attno);
108 [ + + ]: 53111 : if (att->attisdropped)
109 : : {
110 : : /* Just put NULL into this list entry */
111 : 1550 : vars = lappend(vars, NULL);
112 : 1550 : continue;
113 : : }
114 : 51561 : attname = NameStr(att->attname);
115 : 51561 : atttypid = att->atttypid;
116 : 51561 : atttypmod = att->atttypmod;
117 : 51561 : attcollation = att->attcollation;
118 : :
119 : : /*
120 : : * When we are generating the "translation list" for the parent table
121 : : * of an inheritance set, no need to search for matches.
122 : : */
123 [ + + ]: 51561 : if (oldrelation == newrelation)
124 : : {
125 : 2888 : vars = lappend(vars, makeVar(newvarno,
126 : 2888 : (AttrNumber) (old_attno + 1),
127 : : atttypid,
128 : : atttypmod,
129 : : attcollation,
130 : : 0));
1595 tgl@sss.pgh.pa.us 131 : 2888 : pcolnos[old_attno] = old_attno + 1;
1921 alvherre@alvh.no-ip. 132 : 2888 : continue;
133 : : }
134 : :
135 : : /*
136 : : * Otherwise we have to search for the matching column by name.
137 : : * There's no guarantee it'll have the same column position, because
138 : : * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.
139 : : * However, in simple cases, the relative order of columns is mostly
140 : : * the same in both relations, so try the column of newrelation that
141 : : * follows immediately after the one that we just found, and if that
142 : : * fails, let syscache handle it.
143 : : */
144 [ + + ]: 48673 : if (new_attno >= newnatts ||
145 [ + + ]: 47622 : (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped ||
146 [ + + ]: 47189 : strcmp(attname, NameStr(att->attname)) != 0)
147 : : {
148 : : HeapTuple newtup;
149 : :
150 : 3802 : newtup = SearchSysCacheAttName(new_relid, attname);
1806 tgl@sss.pgh.pa.us 151 [ - + ]: 3802 : if (!HeapTupleIsValid(newtup))
1921 alvherre@alvh.no-ip. 152 [ # # ]:UBC 0 : elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
153 : : attname, RelationGetRelationName(newrelation));
1921 alvherre@alvh.no-ip. 154 :CBC 3802 : new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
1595 tgl@sss.pgh.pa.us 155 [ + - - + ]: 3802 : Assert(new_attno >= 0 && new_attno < newnatts);
1921 alvherre@alvh.no-ip. 156 : 3802 : ReleaseSysCache(newtup);
157 : :
158 : 3802 : att = TupleDescAttr(new_tupdesc, new_attno);
159 : : }
160 : :
161 : : /* Found it, check type and collation match */
162 [ + + - + ]: 48673 : if (atttypid != att->atttypid || atttypmod != att->atttypmod)
163 [ + - ]: 1 : elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
164 : : attname, RelationGetRelationName(newrelation));
165 [ - + ]: 48672 : if (attcollation != att->attcollation)
1921 alvherre@alvh.no-ip. 166 [ # # ]:UBC 0 : elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
167 : : attname, RelationGetRelationName(newrelation));
168 : :
1921 alvherre@alvh.no-ip. 169 :CBC 48672 : vars = lappend(vars, makeVar(newvarno,
170 : 48672 : (AttrNumber) (new_attno + 1),
171 : : atttypid,
172 : : atttypmod,
173 : : attcollation,
174 : : 0));
1595 tgl@sss.pgh.pa.us 175 : 48672 : pcolnos[new_attno] = old_attno + 1;
1921 alvherre@alvh.no-ip. 176 : 48672 : new_attno++;
177 : : }
178 : :
1595 tgl@sss.pgh.pa.us 179 : 19298 : appinfo->translated_vars = vars;
1921 alvherre@alvh.no-ip. 180 : 19298 : }
181 : :
182 : : /*
183 : : * adjust_appendrel_attrs
184 : : * Copy the specified query or expression and translate Vars referring to a
185 : : * parent rel to refer to the corresponding child rel instead. We also
186 : : * update rtindexes appearing outside Vars, such as resultRelation and
187 : : * jointree relids.
188 : : *
189 : : * Note: this is only applied after conversion of sublinks to subplans,
190 : : * so we don't need to cope with recursion into sub-queries.
191 : : *
192 : : * Note: this is not hugely different from what pullup_replace_vars() does;
193 : : * maybe we should try to fold the two routines together.
194 : : */
195 : : Node *
196 : 92283 : adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,
197 : : AppendRelInfo **appinfos)
198 : : {
199 : : adjust_appendrel_attrs_context context;
200 : :
201 : 92283 : context.root = root;
202 : 92283 : context.nappinfos = nappinfos;
203 : 92283 : context.appinfos = appinfos;
204 : :
205 : : /* If there's nothing to adjust, don't call this function. */
206 [ + - - + ]: 92283 : Assert(nappinfos >= 1 && appinfos != NULL);
207 : :
208 : : /* Should never be translating a Query tree. */
1110 tgl@sss.pgh.pa.us 209 [ + + - + ]: 92283 : Assert(node == NULL || !IsA(node, Query));
210 : :
211 : 92283 : return adjust_appendrel_attrs_mutator(node, &context);
212 : : }
213 : :
214 : : static Node *
1921 alvherre@alvh.no-ip. 215 : 385752 : adjust_appendrel_attrs_mutator(Node *node,
216 : : adjust_appendrel_attrs_context *context)
217 : : {
218 : 385752 : AppendRelInfo **appinfos = context->appinfos;
219 : 385752 : int nappinfos = context->nappinfos;
220 : : int cnt;
221 : :
222 [ + + ]: 385752 : if (node == NULL)
223 : 37408 : return NULL;
224 [ + + ]: 348344 : if (IsA(node, Var))
225 : : {
226 : 161915 : Var *var = (Var *) copyObject(node);
227 : 161915 : AppendRelInfo *appinfo = NULL;
228 : :
1557 tgl@sss.pgh.pa.us 229 [ - + ]: 161915 : if (var->varlevelsup != 0)
1557 tgl@sss.pgh.pa.us 230 :UBC 0 : return (Node *) var; /* no changes needed */
231 : :
232 : : /*
233 : : * You might think we need to adjust var->varnullingrels, but that
234 : : * shouldn't need any changes. It will contain outer-join relids,
235 : : * while the transformation we are making affects only baserels.
236 : : * Below, we just propagate var->varnullingrels into the translated
237 : : * Var.
238 : : *
239 : : * If var->varnullingrels isn't empty, and the translation wouldn't be
240 : : * a Var, we have to fail. One could imagine wrapping the translated
241 : : * expression in a PlaceHolderVar, but that won't work because this is
242 : : * typically used after freezing placeholders. Fortunately, the case
243 : : * appears unreachable at the moment. We can see nonempty
244 : : * var->varnullingrels here, but only in cases involving partitionwise
245 : : * joining, and in such cases the translations will always be Vars.
246 : : * (Non-Var translations occur only for appendrels made by flattening
247 : : * UNION ALL subqueries.) Should we need to make this work in future,
248 : : * a possible fix is to mandate that prepjointree.c create PHVs for
249 : : * all non-Var outputs of such subqueries, and then we could look up
250 : : * the pre-existing PHV here. Or perhaps just wrap the translations
251 : : * that way to begin with?
252 : : */
253 : :
1921 alvherre@alvh.no-ip. 254 [ + + ]:CBC 198652 : for (cnt = 0; cnt < nappinfos; cnt++)
255 : : {
256 [ + + ]: 179475 : if (var->varno == appinfos[cnt]->parent_relid)
257 : : {
258 : 142738 : appinfo = appinfos[cnt];
259 : 142738 : break;
260 : : }
261 : : }
262 : :
1557 tgl@sss.pgh.pa.us 263 [ + + ]: 161915 : if (appinfo)
264 : : {
1921 alvherre@alvh.no-ip. 265 : 142738 : var->varno = appinfo->child_relid;
266 : : /* it's now a generated Var, so drop any syntactic labeling */
1557 tgl@sss.pgh.pa.us 267 : 142738 : var->varnosyn = 0;
268 : 142738 : var->varattnosyn = 0;
1921 alvherre@alvh.no-ip. 269 [ + + ]: 142738 : if (var->varattno > 0)
270 : : {
271 : : Node *newnode;
272 : :
273 [ - + ]: 134479 : if (var->varattno > list_length(appinfo->translated_vars))
1921 alvherre@alvh.no-ip. 274 [ # # ]:UBC 0 : elog(ERROR, "attribute %d of relation \"%s\" does not exist",
275 : : var->varattno, get_rel_name(appinfo->parent_reloid));
1921 alvherre@alvh.no-ip. 276 :CBC 134479 : newnode = copyObject(list_nth(appinfo->translated_vars,
277 : : var->varattno - 1));
278 [ - + ]: 134479 : if (newnode == NULL)
1921 alvherre@alvh.no-ip. 279 [ # # ]:UBC 0 : elog(ERROR, "attribute %d of relation \"%s\" does not exist",
280 : : var->varattno, get_rel_name(appinfo->parent_reloid));
440 tgl@sss.pgh.pa.us 281 [ + + ]:CBC 134479 : if (IsA(newnode, Var))
282 : 132709 : ((Var *) newnode)->varnullingrels = var->varnullingrels;
283 [ - + ]: 1770 : else if (var->varnullingrels != NULL)
440 tgl@sss.pgh.pa.us 284 [ # # ]:UBC 0 : elog(ERROR, "failed to apply nullingrels to a non-Var");
1921 alvherre@alvh.no-ip. 285 :CBC 134479 : return newnode;
286 : : }
287 [ + + ]: 8259 : else if (var->varattno == 0)
288 : : {
289 : : /*
290 : : * Whole-row Var: if we are dealing with named rowtypes, we
291 : : * can use a whole-row Var for the child table plus a coercion
292 : : * step to convert the tuple layout to the parent's rowtype.
293 : : * Otherwise we have to generate a RowExpr.
294 : : */
295 [ + + ]: 443 : if (OidIsValid(appinfo->child_reltype))
296 : : {
297 [ - + ]: 415 : Assert(var->vartype == appinfo->parent_reltype);
298 [ + + ]: 415 : if (appinfo->parent_reltype != appinfo->child_reltype)
299 : : {
300 : 343 : ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
301 : :
302 : 343 : r->arg = (Expr *) var;
303 : 343 : r->resulttype = appinfo->parent_reltype;
304 : 343 : r->convertformat = COERCE_IMPLICIT_CAST;
305 : 343 : r->location = -1;
306 : : /* Make sure the Var node has the right type ID, too */
307 : 343 : var->vartype = appinfo->child_reltype;
308 : 343 : return (Node *) r;
309 : : }
310 : : }
311 : : else
312 : : {
313 : : /*
314 : : * Build a RowExpr containing the translated variables.
315 : : *
316 : : * In practice var->vartype will always be RECORDOID here,
317 : : * so we need to come up with some suitable column names.
318 : : * We use the parent RTE's column names.
319 : : *
320 : : * Note: we can't get here for inheritance cases, so there
321 : : * is no need to worry that translated_vars might contain
322 : : * some dummy NULLs.
323 : : */
324 : : RowExpr *rowexpr;
325 : : List *fields;
326 : : RangeTblEntry *rte;
327 : :
328 : 28 : rte = rt_fetch(appinfo->parent_relid,
329 : : context->root->parse->rtable);
330 : 28 : fields = copyObject(appinfo->translated_vars);
331 : 28 : rowexpr = makeNode(RowExpr);
332 : 28 : rowexpr->args = fields;
333 : 28 : rowexpr->row_typeid = var->vartype;
334 : 28 : rowexpr->row_format = COERCE_IMPLICIT_CAST;
335 : 28 : rowexpr->colnames = copyObject(rte->eref->colnames);
336 : 28 : rowexpr->location = -1;
337 : :
440 tgl@sss.pgh.pa.us 338 [ - + ]: 28 : if (var->varnullingrels != NULL)
440 tgl@sss.pgh.pa.us 339 [ # # ]:UBC 0 : elog(ERROR, "failed to apply nullingrels to a non-Var");
340 : :
1921 alvherre@alvh.no-ip. 341 :CBC 28 : return (Node *) rowexpr;
342 : : }
343 : : }
344 : : /* system attributes don't need any other translation */
345 : : }
1110 tgl@sss.pgh.pa.us 346 [ + + ]: 19177 : else if (var->varno == ROWID_VAR)
347 : : {
348 : : /*
349 : : * If it's a ROWID_VAR placeholder, see if we've reached a leaf
350 : : * target rel, for which we can translate the Var to a specific
351 : : * instantiation. We should never be asked to translate to a set
352 : : * of relids containing more than one leaf target rel, so the
353 : : * answer will be unique. If we're still considering non-leaf
354 : : * inheritance levels, return the ROWID_VAR Var as-is.
355 : : */
356 : 8823 : Relids leaf_result_relids = context->root->leaf_result_relids;
357 : 8823 : Index leaf_relid = 0;
358 : :
359 [ + + ]: 17646 : for (cnt = 0; cnt < nappinfos; cnt++)
360 : : {
361 [ + + ]: 8823 : if (bms_is_member(appinfos[cnt]->child_relid,
362 : : leaf_result_relids))
363 : : {
364 [ - + ]: 7781 : if (leaf_relid)
1110 tgl@sss.pgh.pa.us 365 [ # # ]:UBC 0 : elog(ERROR, "cannot translate to multiple leaf relids");
1110 tgl@sss.pgh.pa.us 366 :CBC 7781 : leaf_relid = appinfos[cnt]->child_relid;
367 : : }
368 : : }
369 : :
370 [ + + ]: 8823 : if (leaf_relid)
371 : : {
372 : : RowIdentityVarInfo *ridinfo = (RowIdentityVarInfo *)
331 373 : 7781 : list_nth(context->root->row_identity_vars, var->varattno - 1);
374 : :
1110 375 [ + + ]: 7781 : if (bms_is_member(leaf_relid, ridinfo->rowidrels))
376 : : {
377 : : /* Substitute the Var given in the RowIdentityVarInfo */
378 : 7737 : var = copyObject(ridinfo->rowidvar);
379 : : /* ... but use the correct relid */
380 : 7737 : var->varno = leaf_relid;
381 : : /* identity vars shouldn't have nulling rels */
440 382 [ - + ]: 7737 : Assert(var->varnullingrels == NULL);
383 : : /* varnosyn in the RowIdentityVarInfo is probably wrong */
1110 384 : 7737 : var->varnosyn = 0;
385 : 7737 : var->varattnosyn = 0;
386 : : }
387 : : else
388 : : {
389 : : /*
390 : : * This leaf rel can't return the desired value, so
391 : : * substitute a NULL of the correct type.
392 : : */
393 : 44 : return (Node *) makeNullConst(var->vartype,
394 : : var->vartypmod,
395 : : var->varcollid);
396 : : }
397 : : }
398 : : }
1921 alvherre@alvh.no-ip. 399 : 27021 : return (Node *) var;
400 : : }
401 [ + + ]: 186429 : if (IsA(node, CurrentOfExpr))
402 : : {
403 : 92 : CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
404 : :
405 [ + - ]: 92 : for (cnt = 0; cnt < nappinfos; cnt++)
406 : : {
407 : 92 : AppendRelInfo *appinfo = appinfos[cnt];
408 : :
409 [ + - ]: 92 : if (cexpr->cvarno == appinfo->parent_relid)
410 : : {
411 : 92 : cexpr->cvarno = appinfo->child_relid;
412 : 92 : break;
413 : : }
414 : : }
415 : 92 : return (Node *) cexpr;
416 : : }
417 [ + + ]: 186337 : if (IsA(node, PlaceHolderVar))
418 : : {
419 : : /* Copy the PlaceHolderVar node with correct mutation of subnodes */
420 : : PlaceHolderVar *phv;
421 : :
422 : 1359 : phv = (PlaceHolderVar *) expression_tree_mutator(node,
423 : : adjust_appendrel_attrs_mutator,
424 : : (void *) context);
425 : : /* now fix PlaceHolderVar's relid sets */
426 [ + - ]: 1359 : if (phv->phlevelsup == 0)
427 : : {
440 tgl@sss.pgh.pa.us 428 : 1359 : phv->phrels = adjust_child_relids(phv->phrels,
429 : : nappinfos, appinfos);
430 : : /* as above, we needn't touch phnullingrels */
431 : : }
1921 alvherre@alvh.no-ip. 432 : 1359 : return (Node *) phv;
433 : : }
434 : : /* Shouldn't need to handle planner auxiliary nodes here */
435 [ - + ]: 184978 : Assert(!IsA(node, SpecialJoinInfo));
436 [ - + ]: 184978 : Assert(!IsA(node, AppendRelInfo));
437 [ - + ]: 184978 : Assert(!IsA(node, PlaceHolderInfo));
438 [ - + ]: 184978 : Assert(!IsA(node, MinMaxAggInfo));
439 : :
440 : : /*
441 : : * We have to process RestrictInfo nodes specially. (Note: although
442 : : * set_append_rel_pathlist will hide RestrictInfos in the parent's
443 : : * baserestrictinfo list from us, it doesn't hide those in joininfo.)
444 : : */
445 [ + + ]: 184978 : if (IsA(node, RestrictInfo))
446 : : {
447 : 12187 : RestrictInfo *oldinfo = (RestrictInfo *) node;
448 : 12187 : RestrictInfo *newinfo = makeNode(RestrictInfo);
449 : :
450 : : /* Copy all flat-copiable fields, notably including rinfo_serial */
451 : 12187 : memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
452 : :
453 : : /* Recursively fix the clause itself */
454 : 12187 : newinfo->clause = (Expr *)
455 : 12187 : adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);
456 : :
457 : : /* and the modified version, if an OR clause */
458 : 12187 : newinfo->orclause = (Expr *)
459 : 12187 : adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
460 : :
461 : : /* adjust relid sets too */
462 : 12187 : newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,
463 : : context->nappinfos,
464 : : context->appinfos);
465 : 12187 : newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,
466 : : context->nappinfos,
467 : : context->appinfos);
468 : 12187 : newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,
469 : : context->nappinfos,
470 : : context->appinfos);
471 : 12187 : newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,
472 : : context->nappinfos,
473 : : context->appinfos);
474 : 12187 : newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,
475 : : context->nappinfos,
476 : : context->appinfos);
477 : :
478 : : /*
479 : : * Reset cached derivative fields, since these might need to have
480 : : * different values when considering the child relation. Note we
481 : : * don't reset left_ec/right_ec: each child variable is implicitly
482 : : * equivalent to its parent, so still a member of the same EC if any.
483 : : */
484 : 12187 : newinfo->eval_cost.startup = -1;
485 : 12187 : newinfo->norm_selec = -1;
486 : 12187 : newinfo->outer_selec = -1;
487 : 12187 : newinfo->left_em = NULL;
488 : 12187 : newinfo->right_em = NULL;
489 : 12187 : newinfo->scansel_cache = NIL;
490 : 12187 : newinfo->left_bucketsize = -1;
491 : 12187 : newinfo->right_bucketsize = -1;
492 : 12187 : newinfo->left_mcvfreq = -1;
493 : 12187 : newinfo->right_mcvfreq = -1;
494 : :
495 : 12187 : return (Node *) newinfo;
496 : : }
497 : :
498 : : /*
499 : : * NOTE: we do not need to recurse into sublinks, because they should
500 : : * already have been converted to subplans before we see them.
501 : : */
502 [ - + ]: 172791 : Assert(!IsA(node, SubLink));
503 [ - + ]: 172791 : Assert(!IsA(node, Query));
504 : : /* We should never see these Query substructures, either. */
1110 tgl@sss.pgh.pa.us 505 [ - + ]: 172791 : Assert(!IsA(node, RangeTblRef));
506 [ - + ]: 172791 : Assert(!IsA(node, JoinExpr));
507 : :
1921 alvherre@alvh.no-ip. 508 : 172791 : return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
509 : : (void *) context);
510 : : }
511 : :
512 : : /*
513 : : * adjust_appendrel_attrs_multilevel
514 : : * Apply Var translations from an appendrel parent down to a child.
515 : : *
516 : : * Replace Vars in the "node" expression that reference "parentrel" with
517 : : * the appropriate Vars for "childrel". childrel can be more than one
518 : : * inheritance level removed from parentrel.
519 : : */
520 : : Node *
1915 521 : 12555 : adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
522 : : RelOptInfo *childrel,
523 : : RelOptInfo *parentrel)
524 : : {
525 : : AppendRelInfo **appinfos;
526 : : int nappinfos;
527 : :
528 : : /* Recurse if immediate parent is not the top parent. */
605 tgl@sss.pgh.pa.us 529 [ + + ]: 12555 : if (childrel->parent != parentrel)
530 : : {
531 [ + - ]: 4958 : if (childrel->parent)
532 : 4958 : node = adjust_appendrel_attrs_multilevel(root, node,
533 : 4958 : childrel->parent,
534 : : parentrel);
535 : : else
605 tgl@sss.pgh.pa.us 536 [ # # ]:UBC 0 : elog(ERROR, "childrel is not a child of parentrel");
537 : : }
538 : :
539 : : /* Now translate for this child. */
605 tgl@sss.pgh.pa.us 540 :CBC 12555 : appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
541 : :
1915 alvherre@alvh.no-ip. 542 : 12555 : node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);
543 : :
544 : 12555 : pfree(appinfos);
545 : :
546 : 12555 : return node;
547 : : }
548 : :
549 : : /*
550 : : * Substitute child relids for parent relids in a Relid set. The array of
551 : : * appinfos specifies the substitutions to be performed.
552 : : */
553 : : Relids
1921 554 : 73778 : adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)
555 : : {
556 : 73778 : Bitmapset *result = NULL;
557 : : int cnt;
558 : :
559 [ + + ]: 188726 : for (cnt = 0; cnt < nappinfos; cnt++)
560 : : {
561 : 114948 : AppendRelInfo *appinfo = appinfos[cnt];
562 : :
563 : : /* Remove parent, add child */
564 [ + + ]: 114948 : if (bms_is_member(appinfo->parent_relid, relids))
565 : : {
566 : : /* Make a copy if we are changing the set. */
567 [ + + ]: 74749 : if (!result)
568 : 56790 : result = bms_copy(relids);
569 : :
570 : 74749 : result = bms_del_member(result, appinfo->parent_relid);
571 : 74749 : result = bms_add_member(result, appinfo->child_relid);
572 : : }
573 : : }
574 : :
575 : : /* If we made any changes, return the modified copy. */
576 [ + + ]: 73778 : if (result)
577 : 56790 : return result;
578 : :
579 : : /* Otherwise, return the original set without modification. */
580 : 16988 : return relids;
581 : : }
582 : :
583 : : /*
584 : : * Substitute child's relids for parent's relids in a Relid set.
585 : : * The childrel can be multiple inheritance levels below the parent.
586 : : */
587 : : Relids
588 : 516 : adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
589 : : RelOptInfo *childrel,
590 : : RelOptInfo *parentrel)
591 : : {
592 : : AppendRelInfo **appinfos;
593 : : int nappinfos;
594 : :
595 : : /*
596 : : * If the given relids set doesn't contain any of the parent relids, it
597 : : * will remain unchanged.
598 : : */
605 tgl@sss.pgh.pa.us 599 [ - + ]: 516 : if (!bms_overlap(relids, parentrel->relids))
1921 alvherre@alvh.no-ip. 600 :UBC 0 : return relids;
601 : :
602 : : /* Recurse if immediate parent is not the top parent. */
605 tgl@sss.pgh.pa.us 603 [ + + ]:CBC 516 : if (childrel->parent != parentrel)
604 : : {
605 [ + - ]: 72 : if (childrel->parent)
606 : 72 : relids = adjust_child_relids_multilevel(root, relids,
607 : 72 : childrel->parent,
608 : : parentrel);
609 : : else
605 tgl@sss.pgh.pa.us 610 [ # # ]:UBC 0 : elog(ERROR, "childrel is not a child of parentrel");
611 : : }
612 : :
613 : : /* Now translate for this child. */
605 tgl@sss.pgh.pa.us 614 :CBC 516 : appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
615 : :
616 : 516 : relids = adjust_child_relids(relids, nappinfos, appinfos);
617 : :
1921 alvherre@alvh.no-ip. 618 : 516 : pfree(appinfos);
619 : :
605 tgl@sss.pgh.pa.us 620 : 516 : return relids;
621 : : }
622 : :
623 : : /*
624 : : * adjust_inherited_attnums
625 : : * Translate an integer list of attribute numbers from parent to child.
626 : : */
627 : : List *
1110 628 : 2435 : adjust_inherited_attnums(List *attnums, AppendRelInfo *context)
629 : : {
630 : 2435 : List *result = NIL;
631 : : ListCell *lc;
632 : :
633 : : /* This should only happen for an inheritance case, not UNION ALL */
1921 alvherre@alvh.no-ip. 634 [ - + ]: 2435 : Assert(OidIsValid(context->parent_reloid));
635 : :
636 : : /* Look up each attribute in the AppendRelInfo's translated_vars list */
1110 tgl@sss.pgh.pa.us 637 [ + - + + : 5386 : foreach(lc, attnums)
+ + ]
638 : : {
639 : 2951 : AttrNumber parentattno = lfirst_int(lc);
640 : : Var *childvar;
641 : :
642 : : /* Look up the translation of this column: it must be a Var */
643 [ + - - + ]: 5902 : if (parentattno <= 0 ||
644 : 2951 : parentattno > list_length(context->translated_vars))
1921 alvherre@alvh.no-ip. 645 [ # # ]:UBC 0 : elog(ERROR, "attribute %d of relation \"%s\" does not exist",
646 : : parentattno, get_rel_name(context->parent_reloid));
1110 tgl@sss.pgh.pa.us 647 :CBC 2951 : childvar = (Var *) list_nth(context->translated_vars, parentattno - 1);
1921 alvherre@alvh.no-ip. 648 [ + - - + ]: 2951 : if (childvar == NULL || !IsA(childvar, Var))
1921 alvherre@alvh.no-ip. 649 [ # # ]:UBC 0 : elog(ERROR, "attribute %d of relation \"%s\" does not exist",
650 : : parentattno, get_rel_name(context->parent_reloid));
651 : :
1110 tgl@sss.pgh.pa.us 652 :CBC 2951 : result = lappend_int(result, childvar->varattno);
653 : : }
654 : 2435 : return result;
655 : : }
656 : :
657 : : /*
658 : : * adjust_inherited_attnums_multilevel
659 : : * As above, but traverse multiple inheritance levels as needed.
660 : : */
661 : : List *
662 : 2435 : adjust_inherited_attnums_multilevel(PlannerInfo *root, List *attnums,
663 : : Index child_relid, Index top_parent_relid)
664 : : {
665 : 2435 : AppendRelInfo *appinfo = root->append_rel_array[child_relid];
666 : :
667 [ - + ]: 2435 : if (!appinfo)
1110 tgl@sss.pgh.pa.us 668 [ # # ]:UBC 0 : elog(ERROR, "child rel %d not found in append_rel_array", child_relid);
669 : :
670 : : /* Recurse if immediate parent is not the top parent. */
1110 tgl@sss.pgh.pa.us 671 [ + + ]:CBC 2435 : if (appinfo->parent_relid != top_parent_relid)
672 : 404 : attnums = adjust_inherited_attnums_multilevel(root, attnums,
673 : : appinfo->parent_relid,
674 : : top_parent_relid);
675 : :
676 : : /* Now translate for this child */
677 : 2435 : return adjust_inherited_attnums(attnums, appinfo);
678 : : }
679 : :
680 : : /*
681 : : * get_translated_update_targetlist
682 : : * Get the processed_tlist of an UPDATE query, translated as needed to
683 : : * match a child target relation.
684 : : *
685 : : * Optionally also return the list of target column numbers translated
686 : : * to this target relation. (The resnos in processed_tlist MUST NOT be
687 : : * relied on for this purpose.)
688 : : */
689 : : void
690 : 50 : get_translated_update_targetlist(PlannerInfo *root, Index relid,
691 : : List **processed_tlist, List **update_colnos)
692 : : {
693 : : /* This is pretty meaningless for commands other than UPDATE. */
694 [ - + ]: 50 : Assert(root->parse->commandType == CMD_UPDATE);
695 [ + + ]: 50 : if (relid == root->parse->resultRelation)
696 : : {
697 : : /*
698 : : * Non-inheritance case, so it's easy. The caller might be expecting
699 : : * a tree it can scribble on, though, so copy.
700 : : */
701 : 33 : *processed_tlist = copyObject(root->processed_tlist);
702 [ + - ]: 33 : if (update_colnos)
703 : 33 : *update_colnos = copyObject(root->update_colnos);
704 : : }
705 : : else
706 : : {
707 [ - + ]: 17 : Assert(bms_is_member(relid, root->all_result_relids));
708 : 17 : *processed_tlist = (List *)
709 : 17 : adjust_appendrel_attrs_multilevel(root,
710 : 17 : (Node *) root->processed_tlist,
711 : : find_base_rel(root, relid),
605 712 : 17 : find_base_rel(root, root->parse->resultRelation));
1110 713 [ + - ]: 17 : if (update_colnos)
714 : 17 : *update_colnos =
715 : 17 : adjust_inherited_attnums_multilevel(root, root->update_colnos,
716 : : relid,
717 : 17 : root->parse->resultRelation);
718 : : }
1921 alvherre@alvh.no-ip. 719 : 50 : }
720 : :
721 : : /*
722 : : * find_appinfos_by_relids
723 : : * Find AppendRelInfo structures for base relations listed in relids.
724 : : *
725 : : * The relids argument is typically a join relation's relids, which can
726 : : * include outer-join RT indexes in addition to baserels. We silently
727 : : * ignore the outer joins.
728 : : *
729 : : * The AppendRelInfos are returned in an array, which can be pfree'd by the
730 : : * caller. *nappinfos is set to the number of entries in the array.
731 : : */
732 : : AppendRelInfo **
733 : 32987 : find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
734 : : {
735 : : AppendRelInfo **appinfos;
736 : 32987 : int cnt = 0;
737 : : int i;
738 : :
739 : : /* Allocate an array that's certainly big enough */
740 : : appinfos = (AppendRelInfo **)
440 tgl@sss.pgh.pa.us 741 : 32987 : palloc(sizeof(AppendRelInfo *) * bms_num_members(relids));
742 : :
1921 alvherre@alvh.no-ip. 743 : 32987 : i = -1;
744 [ + + ]: 76838 : while ((i = bms_next_member(relids, i)) >= 0)
745 : : {
746 : 43851 : AppendRelInfo *appinfo = root->append_rel_array[i];
747 : :
748 [ + + ]: 43851 : if (!appinfo)
749 : : {
750 : : /* Probably i is an OJ index, but let's check */
440 tgl@sss.pgh.pa.us 751 [ + - ]: 2004 : if (find_base_rel_ignore_join(root, i) == NULL)
752 : 2004 : continue;
753 : : /* It's a base rel, but we lack an append_rel_array entry */
1921 alvherre@alvh.no-ip. 754 [ # # ]:UBC 0 : elog(ERROR, "child rel %d not found in append_rel_array", i);
755 : : }
756 : :
1921 alvherre@alvh.no-ip. 757 :CBC 41847 : appinfos[cnt++] = appinfo;
758 : : }
440 tgl@sss.pgh.pa.us 759 : 32987 : *nappinfos = cnt;
1921 alvherre@alvh.no-ip. 760 : 32987 : return appinfos;
761 : : }
762 : :
763 : :
764 : : /*****************************************************************************
765 : : *
766 : : * ROW-IDENTITY VARIABLE MANAGEMENT
767 : : *
768 : : * This code lacks a good home, perhaps. We choose to keep it here because
769 : : * adjust_appendrel_attrs_mutator() is its principal co-conspirator. That
770 : : * function does most of what is needed to expand ROWID_VAR Vars into the
771 : : * right things.
772 : : *
773 : : *****************************************************************************/
774 : :
775 : : /*
776 : : * add_row_identity_var
777 : : * Register a row-identity column to be used in UPDATE/DELETE/MERGE.
778 : : *
779 : : * The Var must be equal(), aside from varno, to any other row-identity
780 : : * column with the same rowid_name. Thus, for example, "wholerow"
781 : : * row identities had better use vartype == RECORDOID.
782 : : *
783 : : * rtindex is currently redundant with rowid_var->varno, but we specify
784 : : * it as a separate parameter in case this is ever generalized to support
785 : : * non-Var expressions. (We could reasonably handle expressions over
786 : : * Vars of the specified rtindex, but for now that seems unnecessary.)
787 : : */
788 : : void
1110 tgl@sss.pgh.pa.us 789 : 13236 : add_row_identity_var(PlannerInfo *root, Var *orig_var,
790 : : Index rtindex, const char *rowid_name)
791 : : {
792 : : TargetEntry *tle;
793 : : Var *rowid_var;
794 : : RowIdentityVarInfo *ridinfo;
795 : : ListCell *lc;
796 : :
797 : : /* For now, the argument must be just a Var of the given rtindex */
798 [ - + ]: 13236 : Assert(IsA(orig_var, Var));
799 [ - + ]: 13236 : Assert(orig_var->varno == rtindex);
800 [ - + ]: 13236 : Assert(orig_var->varlevelsup == 0);
440 801 [ - + ]: 13236 : Assert(orig_var->varnullingrels == NULL);
802 : :
803 : : /*
804 : : * If we're doing non-inherited UPDATE/DELETE/MERGE, there's little need
805 : : * for ROWID_VAR shenanigans. Just shove the presented Var into the
806 : : * processed_tlist, and we're done.
807 : : */
1110 808 [ + + ]: 13236 : if (rtindex == root->parse->resultRelation)
809 : : {
810 : 8321 : tle = makeTargetEntry((Expr *) orig_var,
811 : 8321 : list_length(root->processed_tlist) + 1,
812 : : pstrdup(rowid_name),
813 : : true);
814 : 8321 : root->processed_tlist = lappend(root->processed_tlist, tle);
815 : 8321 : return;
816 : : }
817 : :
818 : : /*
819 : : * Otherwise, rtindex should reference a leaf target relation that's being
820 : : * added to the query during expand_inherited_rtentry().
821 : : */
822 [ - + ]: 4915 : Assert(bms_is_member(rtindex, root->leaf_result_relids));
823 [ - + ]: 4915 : Assert(root->append_rel_array[rtindex] != NULL);
824 : :
825 : : /*
826 : : * We have to find a matching RowIdentityVarInfo, or make one if there is
827 : : * none. To allow using equal() to match the vars, change the varno to
828 : : * ROWID_VAR, leaving all else alone.
829 : : */
830 : 4915 : rowid_var = copyObject(orig_var);
831 : : /* This could eventually become ChangeVarNodes() */
832 : 4915 : rowid_var->varno = ROWID_VAR;
833 : :
834 : : /* Look for an existing row-id column of the same name */
835 [ + + + + : 7430 : foreach(lc, root->row_identity_vars)
+ + ]
836 : : {
837 : 4879 : ridinfo = (RowIdentityVarInfo *) lfirst(lc);
838 [ + + ]: 4879 : if (strcmp(rowid_name, ridinfo->rowidname) != 0)
839 : 2515 : continue;
840 [ + - ]: 2364 : if (equal(rowid_var, ridinfo->rowidvar))
841 : : {
842 : : /* Found a match; we need only record that rtindex needs it too */
843 : 2364 : ridinfo->rowidrels = bms_add_member(ridinfo->rowidrels, rtindex);
844 : 2364 : return;
845 : : }
846 : : else
847 : : {
848 : : /* Ooops, can't handle this */
1110 tgl@sss.pgh.pa.us 849 [ # # ]:UBC 0 : elog(ERROR, "conflicting uses of row-identity name \"%s\"",
850 : : rowid_name);
851 : : }
852 : : }
853 : :
854 : : /* No request yet, so add a new RowIdentityVarInfo */
1110 tgl@sss.pgh.pa.us 855 :CBC 2551 : ridinfo = makeNode(RowIdentityVarInfo);
856 : 2551 : ridinfo->rowidvar = copyObject(rowid_var);
857 : : /* for the moment, estimate width using just the datatype info */
858 : 2551 : ridinfo->rowidwidth = get_typavgwidth(exprType((Node *) rowid_var),
859 : : exprTypmod((Node *) rowid_var));
860 : 2551 : ridinfo->rowidname = pstrdup(rowid_name);
861 : 2551 : ridinfo->rowidrels = bms_make_singleton(rtindex);
862 : :
863 : 2551 : root->row_identity_vars = lappend(root->row_identity_vars, ridinfo);
864 : :
865 : : /* Change rowid_var into a reference to this row_identity_vars entry */
866 : 2551 : rowid_var->varattno = list_length(root->row_identity_vars);
867 : :
868 : : /* Push the ROWID_VAR reference variable into processed_tlist */
869 : 2551 : tle = makeTargetEntry((Expr *) rowid_var,
870 : 2551 : list_length(root->processed_tlist) + 1,
871 : : pstrdup(rowid_name),
872 : : true);
873 : 2551 : root->processed_tlist = lappend(root->processed_tlist, tle);
874 : : }
875 : :
876 : : /*
877 : : * add_row_identity_columns
878 : : *
879 : : * This function adds the row identity columns needed by the core code.
880 : : * FDWs might call add_row_identity_var() for themselves to add nonstandard
881 : : * columns. (Duplicate requests are fine.)
882 : : */
883 : : void
884 : 10828 : add_row_identity_columns(PlannerInfo *root, Index rtindex,
885 : : RangeTblEntry *target_rte,
886 : : Relation target_relation)
887 : : {
888 : 10828 : CmdType commandType = root->parse->commandType;
889 : 10828 : char relkind = target_relation->rd_rel->relkind;
890 : : Var *var;
891 : :
748 alvherre@alvh.no-ip. 892 [ + + + + : 10828 : Assert(commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE);
- + ]
893 : :
45 dean.a.rasheed@gmail 894 [ + + + + ]:GNC 10828 : if (relkind == RELKIND_RELATION ||
1110 tgl@sss.pgh.pa.us 895 [ + + ]:CBC 326 : relkind == RELKIND_MATVIEW ||
896 : : relkind == RELKIND_PARTITIONED_TABLE)
897 : : {
898 : : /*
899 : : * Emit CTID so that executor can find the row to merge, update or
900 : : * delete.
901 : : */
902 : 10520 : var = makeVar(rtindex,
903 : : SelfItemPointerAttributeNumber,
904 : : TIDOID,
905 : : -1,
906 : : InvalidOid,
907 : : 0);
908 : 10520 : add_row_identity_var(root, var, rtindex, "ctid");
909 : : }
910 [ + + ]: 308 : else if (relkind == RELKIND_FOREIGN_TABLE)
911 : : {
912 : : /*
913 : : * Let the foreign table's FDW add whatever junk TLEs it wants.
914 : : */
915 : : FdwRoutine *fdwroutine;
916 : :
917 : 176 : fdwroutine = GetFdwRoutineForRelation(target_relation, false);
918 : :
919 [ + + ]: 176 : if (fdwroutine->AddForeignUpdateTargets != NULL)
920 : 172 : fdwroutine->AddForeignUpdateTargets(root, rtindex,
921 : : target_rte, target_relation);
922 : :
923 : : /*
924 : : * For UPDATE, we need to make the FDW fetch unchanged columns by
925 : : * asking it to fetch a whole-row Var. That's because the top-level
926 : : * targetlist only contains entries for changed columns, but
927 : : * ExecUpdate will need to build the complete new tuple. (Actually,
928 : : * we only really need this in UPDATEs that are not pushed to the
929 : : * remote side, but it's hard to tell if that will be the case at the
930 : : * point when this function is called.)
931 : : *
932 : : * We will also need the whole row if there are any row triggers, so
933 : : * that the executor will have the "old" row to pass to the trigger.
934 : : * Alas, this misses system columns.
935 : : */
936 [ + + ]: 176 : if (commandType == CMD_UPDATE ||
937 [ + + ]: 79 : (target_relation->trigdesc &&
938 [ + + ]: 15 : (target_relation->trigdesc->trig_delete_after_row ||
939 [ + + ]: 9 : target_relation->trigdesc->trig_delete_before_row)))
940 : : {
941 : 105 : var = makeVar(rtindex,
942 : : InvalidAttrNumber,
943 : : RECORDOID,
944 : : -1,
945 : : InvalidOid,
946 : : 0);
947 : 105 : add_row_identity_var(root, var, rtindex, "wholerow");
948 : : }
949 : : }
950 : 10828 : }
951 : :
952 : : /*
953 : : * distribute_row_identity_vars
954 : : *
955 : : * After we have finished identifying all the row identity columns
956 : : * needed by an inherited UPDATE/DELETE/MERGE query, make sure that
957 : : * these columns will be generated by all the target relations.
958 : : *
959 : : * This is more or less like what build_base_rel_tlists() does,
960 : : * except that it would not understand what to do with ROWID_VAR Vars.
961 : : * Since that function runs before inheritance relations are expanded,
962 : : * it will never see any such Vars anyway.
963 : : */
964 : : void
965 : 139536 : distribute_row_identity_vars(PlannerInfo *root)
966 : : {
967 : 139536 : Query *parse = root->parse;
968 : 139536 : int result_relation = parse->resultRelation;
969 : : RangeTblEntry *target_rte;
970 : : RelOptInfo *target_rel;
971 : : ListCell *lc;
972 : :
973 : : /*
974 : : * There's nothing to do if this isn't an inherited UPDATE/DELETE/MERGE.
975 : : */
748 alvherre@alvh.no-ip. 976 [ + + + + ]: 139536 : if (parse->commandType != CMD_UPDATE && parse->commandType != CMD_DELETE &&
977 [ + + ]: 130742 : parse->commandType != CMD_MERGE)
978 : : {
1110 tgl@sss.pgh.pa.us 979 [ - + ]: 129890 : Assert(root->row_identity_vars == NIL);
980 : 129890 : return;
981 : : }
982 : 9646 : target_rte = rt_fetch(result_relation, parse->rtable);
983 [ + + ]: 9646 : if (!target_rte->inh)
984 : : {
985 [ - + ]: 8374 : Assert(root->row_identity_vars == NIL);
986 : 8374 : return;
987 : : }
988 : :
989 : : /*
990 : : * Ordinarily, we expect that leaf result relation(s) will have added some
991 : : * ROWID_VAR Vars to the query. However, it's possible that constraint
992 : : * exclusion suppressed every leaf relation. The executor will get upset
993 : : * if the plan has no row identity columns at all, even though it will
994 : : * certainly process no rows. Handle this edge case by re-opening the top
995 : : * result relation and adding the row identity columns it would have used,
996 : : * as preprocess_targetlist() would have done if it weren't marked "inh".
997 : : * Then re-run build_base_rel_tlists() to ensure that the added columns
998 : : * get propagated to the relation's reltarget. (This is a bit ugly, but
999 : : * it seems better to confine the ugliness and extra cycles to this
1000 : : * unusual corner case.)
1001 : : */
1002 [ + + ]: 1272 : if (root->row_identity_vars == NIL)
1003 : : {
1004 : : Relation target_relation;
1005 : :
1006 : 15 : target_relation = table_open(target_rte->relid, NoLock);
1007 : 15 : add_row_identity_columns(root, result_relation,
1008 : : target_rte, target_relation);
1009 : 15 : table_close(target_relation, NoLock);
383 1010 : 15 : build_base_rel_tlists(root, root->processed_tlist);
1011 : : /* There are no ROWID_VAR Vars in this case, so we're done. */
1110 1012 : 15 : return;
1013 : : }
1014 : :
1015 : : /*
1016 : : * Dig through the processed_tlist to find the ROWID_VAR reference Vars,
1017 : : * and forcibly copy them into the reltarget list of the topmost target
1018 : : * relation. That's sufficient because they'll be copied to the
1019 : : * individual leaf target rels (with appropriate translation) later,
1020 : : * during appendrel expansion --- see set_append_rel_size().
1021 : : */
1022 : 1257 : target_rel = find_base_rel(root, result_relation);
1023 : :
1024 [ + - + + : 5227 : foreach(lc, root->processed_tlist)
+ + ]
1025 : : {
1026 : 3970 : TargetEntry *tle = lfirst(lc);
1027 : 3970 : Var *var = (Var *) tle->expr;
1028 : :
1029 [ + - + + : 3970 : if (var && IsA(var, Var) && var->varno == ROWID_VAR)
+ + ]
1030 : : {
1031 : 2551 : target_rel->reltarget->exprs =
1032 : 2551 : lappend(target_rel->reltarget->exprs, copyObject(var));
1033 : : /* reltarget cost and width will be computed later */
1034 : : }
1035 : : }
1036 : : }
|