Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * preptlist.c
4 : : * Routines to preprocess the parse tree target list
5 : : *
6 : : * For an INSERT, the targetlist must contain an entry for each attribute of
7 : : * the target relation in the correct order.
8 : : *
9 : : * For an UPDATE, the targetlist just contains the expressions for the new
10 : : * column values.
11 : : *
12 : : * For UPDATE and DELETE queries, the targetlist must also contain "junk"
13 : : * tlist entries needed to allow the executor to identify the rows to be
14 : : * updated or deleted; for example, the ctid of a heap row. (The planner
15 : : * adds these; they're not in what we receive from the parser/rewriter.)
16 : : *
17 : : * For all query types, there can be additional junk tlist entries, such as
18 : : * sort keys, Vars needed for a RETURNING list, and row ID information needed
19 : : * for SELECT FOR UPDATE locking and/or EvalPlanQual checking.
20 : : *
21 : : * The query rewrite phase also does preprocessing of the targetlist (see
22 : : * rewriteTargetListIU). The division of labor between here and there is
23 : : * partially historical, but it's not entirely arbitrary. The stuff done
24 : : * here is closely connected to physical access to tables, whereas the
25 : : * rewriter's work is more concerned with SQL semantics.
26 : : *
27 : : *
28 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
29 : : * Portions Copyright (c) 1994, Regents of the University of California
30 : : *
31 : : * IDENTIFICATION
32 : : * src/backend/optimizer/prep/preptlist.c
33 : : *
34 : : *-------------------------------------------------------------------------
35 : : */
36 : :
37 : : #include "postgres.h"
38 : :
39 : : #include "access/table.h"
40 : : #include "nodes/makefuncs.h"
41 : : #include "optimizer/appendinfo.h"
42 : : #include "optimizer/optimizer.h"
43 : : #include "optimizer/prep.h"
44 : : #include "optimizer/tlist.h"
45 : : #include "parser/parse_coerce.h"
46 : : #include "parser/parsetree.h"
47 : : #include "utils/lsyscache.h"
48 : : #include "utils/rel.h"
49 : :
50 : : static List *expand_insert_targetlist(PlannerInfo *root, List *tlist,
51 : : Relation rel);
52 : :
53 : :
54 : : /*
55 : : * preprocess_targetlist
56 : : * Driver for preprocessing the parse tree targetlist.
57 : : *
58 : : * The preprocessed targetlist is returned in root->processed_tlist.
59 : : * Also, if this is an UPDATE, we return a list of target column numbers
60 : : * in root->update_colnos. (Resnos in processed_tlist will be consecutive,
61 : : * so do not look at that to find out which columns are targets!)
62 : : */
63 : : void
2840 tgl@sss.pgh.pa.us 64 :CBC 253353 : preprocess_targetlist(PlannerInfo *root)
65 : : {
7266 bruce@momjian.us 66 : 253353 : Query *parse = root->parse;
67 : 253353 : int result_relation = parse->resultRelation;
68 : 253353 : List *range_table = parse->rtable;
69 : 253353 : CmdType command_type = parse->commandType;
2840 tgl@sss.pgh.pa.us 70 : 253353 : RangeTblEntry *target_rte = NULL;
71 : 253353 : Relation target_relation = NULL;
72 : : List *tlist;
73 : : ListCell *lc;
74 : :
75 : : /*
76 : : * If there is a result relation, open it so we can look for missing
77 : : * columns and so on. We assume that previous code already acquired at
78 : : * least AccessShareLock on the relation, so we need no lock here.
79 : : */
9102 80 [ + + ]: 253353 : if (result_relation)
81 : : {
2840 82 : 43081 : target_rte = rt_fetch(result_relation, range_table);
83 : :
84 : : /*
85 : : * Sanity check: it'd better be a real relation not, say, a subquery.
86 : : * Else parser or rewriter messed up.
87 : : */
88 [ - + ]: 43081 : if (target_rte->rtekind != RTE_RELATION)
2840 tgl@sss.pgh.pa.us 89 [ # # ]:UBC 0 : elog(ERROR, "result relation must be a regular relation");
90 : :
2420 andres@anarazel.de 91 :CBC 43081 : target_relation = table_open(target_rte->relid, NoLock);
92 : : }
93 : : else
2840 tgl@sss.pgh.pa.us 94 [ - + ]: 210272 : Assert(command_type == CMD_SELECT);
95 : :
96 : : /*
97 : : * In an INSERT, the executor expects the targetlist to match the exact
98 : : * order of the target table's attributes, including entries for
99 : : * attributes not mentioned in the source query.
100 : : *
101 : : * In an UPDATE, we don't rearrange the tlist order, but we need to make a
102 : : * separate list of the target attribute numbers, in tlist order, and then
103 : : * renumber the processed_tlist entries to be consecutive.
104 : : */
105 : 253353 : tlist = parse->targetList;
1620 106 [ + + ]: 253353 : if (command_type == CMD_INSERT)
220 107 : 33072 : tlist = expand_insert_targetlist(root, tlist, target_relation);
1620 108 [ + + ]: 220281 : else if (command_type == CMD_UPDATE)
1580 109 : 6962 : root->update_colnos = extract_update_targetlist_colnos(tlist);
110 : :
111 : : /*
112 : : * For non-inherited UPDATE/DELETE/MERGE, register any junk column(s)
113 : : * needed to allow the executor to identify the rows to be updated or
114 : : * deleted. In the inheritance case, we do nothing now, leaving this to
115 : : * be dealt with when expand_inherited_rtentry() makes the leaf target
116 : : * relations. (But there might not be any leaf target relations, in which
117 : : * case we must do this in distribute_row_identity_vars().)
118 : : */
1243 alvherre@alvh.no-ip. 119 [ + + + + : 253353 : if ((command_type == CMD_UPDATE || command_type == CMD_DELETE ||
+ + ]
120 : 10009 : command_type == CMD_MERGE) &&
1620 tgl@sss.pgh.pa.us 121 [ + + ]: 10009 : !target_rte->inh)
122 : : {
123 : : /* row-identity logic expects to add stuff to processed_tlist */
124 : 8564 : root->processed_tlist = tlist;
125 : 8564 : add_row_identity_columns(root, result_relation,
126 : : target_rte, target_relation);
127 : 8564 : tlist = root->processed_tlist;
128 : : }
129 : :
130 : : /*
131 : : * For MERGE we also need to handle the target list for each INSERT and
132 : : * UPDATE action separately. In addition, we examine the qual of each
133 : : * action and add any Vars there (other than those of the target rel) to
134 : : * the subplan targetlist.
135 : : */
1258 alvherre@alvh.no-ip. 136 [ + + ]: 253353 : if (command_type == CMD_MERGE)
137 : : {
138 : : ListCell *l;
139 : : List *vars;
140 : :
141 : : /*
142 : : * For MERGE, handle targetlist of each MergeAction separately. Give
143 : : * the same treatment to MergeAction->targetList as we would have
144 : : * given to a regular INSERT. For UPDATE, collect the column numbers
145 : : * being modified.
146 : : */
147 [ + - + + : 2292 : foreach(l, parse->mergeActionList)
+ + ]
148 : : {
149 : 1398 : MergeAction *action = (MergeAction *) lfirst(l);
150 : : ListCell *l2;
151 : :
152 [ + + ]: 1398 : if (action->commandType == CMD_INSERT)
220 tgl@sss.pgh.pa.us 153 : 457 : action->targetList = expand_insert_targetlist(root,
154 : : action->targetList,
155 : : target_relation);
1258 alvherre@alvh.no-ip. 156 [ + + ]: 941 : else if (action->commandType == CMD_UPDATE)
157 : 676 : action->updateColnos =
158 : 676 : extract_update_targetlist_colnos(action->targetList);
159 : :
160 : : /*
161 : : * Add resjunk entries for any Vars and PlaceHolderVars used in
162 : : * each action's targetlist and WHEN condition that belong to
163 : : * relations other than the target. We don't expect to see any
164 : : * aggregates or window functions here.
165 : : */
1243 166 : 1398 : vars = pull_var_clause((Node *)
167 : 1398 : list_concat_copy((List *) action->qual,
168 : 1398 : action->targetList),
169 : : PVC_INCLUDE_PLACEHOLDERS);
170 [ + + + + : 3174 : foreach(l2, vars)
+ + ]
171 : : {
172 : 1776 : Var *var = (Var *) lfirst(l2);
173 : : TargetEntry *tle;
174 : :
175 [ + + + + ]: 1776 : if (IsA(var, Var) && var->varno == result_relation)
176 : 877 : continue; /* don't need it */
177 : :
178 [ + + ]: 899 : if (tlist_member((Expr *) var, tlist))
179 : 241 : continue; /* already got it */
180 : :
181 : 658 : tle = makeTargetEntry((Expr *) var,
182 : 658 : list_length(tlist) + 1,
183 : : NULL, true);
184 : 658 : tlist = lappend(tlist, tle);
185 : : }
186 : 1398 : list_free(vars);
187 : : }
188 : :
189 : : /*
190 : : * Add resjunk entries for any Vars and PlaceHolderVars used in the
191 : : * join condition that belong to relations other than the target. We
192 : : * don't expect to see any aggregates or window functions here.
193 : : */
525 dean.a.rasheed@gmail 194 : 894 : vars = pull_var_clause(parse->mergeJoinCondition,
195 : : PVC_INCLUDE_PLACEHOLDERS);
196 [ + + + + : 1089 : foreach(l, vars)
+ + ]
197 : : {
198 : 195 : Var *var = (Var *) lfirst(l);
199 : : TargetEntry *tle;
200 : :
201 [ + + + + ]: 195 : if (IsA(var, Var) && var->varno == result_relation)
202 : 66 : continue; /* don't need it */
203 : :
204 [ + + ]: 129 : if (tlist_member((Expr *) var, tlist))
205 : 51 : continue; /* already got it */
206 : :
207 : 78 : tle = makeTargetEntry((Expr *) var,
208 : 78 : list_length(tlist) + 1,
209 : : NULL, true);
210 : 78 : tlist = lappend(tlist, tle);
211 : : }
212 : : }
213 : :
214 : : /*
215 : : * Add necessary junk columns for rowmarked rels. These values are needed
216 : : * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
217 : : * rechecking. See comments for PlanRowMark in plannodes.h. If you
218 : : * change this stanza, see also expand_inherited_rtentry(), which has to
219 : : * be able to add on junk columns equivalent to these.
220 : : *
221 : : * (Someday it might be useful to fold these resjunk columns into the
222 : : * row-identity-column management used for UPDATE/DELETE. Today is not
223 : : * that day, however. One notable issue is that it seems important that
224 : : * the whole-row Vars made here use the real table rowtype, not RECORD, so
225 : : * that conversion to/from child relations' rowtypes will happen. Also,
226 : : * since these entries don't potentially bloat with more and more child
227 : : * relations, there's not really much need for column sharing.)
228 : : */
5794 tgl@sss.pgh.pa.us 229 [ + + + + : 258729 : foreach(lc, root->rowMarks)
+ + ]
230 : : {
231 : 5376 : PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
232 : : Var *var;
233 : : char resname[32];
234 : : TargetEntry *tle;
235 : :
236 : : /* child rels use the same junk attrs as their parents */
237 [ - + ]: 5376 : if (rc->rti != rc->prti)
5794 tgl@sss.pgh.pa.us 238 :UBC 0 : continue;
239 : :
3828 tgl@sss.pgh.pa.us 240 [ + + ]:CBC 5376 : if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
241 : : {
242 : : /* Need to fetch TID */
7069 243 : 5011 : var = makeVar(rc->rti,
244 : : SelfItemPointerAttributeNumber,
245 : : TIDOID,
246 : : -1,
247 : : InvalidOid,
248 : : 0);
5323 249 : 5011 : snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
7458 250 : 5011 : tle = makeTargetEntry((Expr *) var,
251 : 5011 : list_length(tlist) + 1,
252 : : pstrdup(resname),
253 : : true);
254 : 5011 : tlist = lappend(tlist, tle);
255 : : }
3828 256 [ + + ]: 5376 : if (rc->allMarkTypes & (1 << ROW_MARK_COPY))
257 : : {
258 : : /* Need the whole row as a junk var */
5436 259 : 365 : var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
260 : 365 : rc->rti,
261 : : 0,
262 : : false);
5323 263 : 365 : snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
5794 264 : 365 : tle = makeTargetEntry((Expr *) var,
265 : 365 : list_length(tlist) + 1,
266 : : pstrdup(resname),
267 : : true);
268 : 365 : tlist = lappend(tlist, tle);
269 : : }
270 : :
271 : : /* If parent of inheritance tree, always fetch the tableoid too. */
3790 sfrost@snowman.net 272 [ - + ]: 5376 : if (rc->isParent)
273 : : {
3790 sfrost@snowman.net 274 :UBC 0 : var = makeVar(rc->rti,
275 : : TableOidAttributeNumber,
276 : : OIDOID,
277 : : -1,
278 : : InvalidOid,
279 : : 0);
280 : 0 : snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
281 : 0 : tle = makeTargetEntry((Expr *) var,
282 : 0 : list_length(tlist) + 1,
283 : : pstrdup(resname),
284 : : true);
285 : 0 : tlist = lappend(tlist, tle);
286 : : }
287 : : }
288 : :
289 : : /*
290 : : * If the query has a RETURNING list, add resjunk entries for any Vars
291 : : * used in RETURNING that belong to other relations. We need to do this
292 : : * to make these Vars available for the RETURNING calculation. Vars that
293 : : * belong to the result rel don't need to be added, because they will be
294 : : * made to refer to the actual heap tuple.
295 : : */
6965 tgl@sss.pgh.pa.us 296 [ + + + + ]:CBC 253353 : if (parse->returningList && list_length(parse->rtable) > 1)
297 : : {
298 : : List *vars;
299 : : ListCell *l;
300 : :
5984 301 : 888 : vars = pull_var_clause((Node *) parse->returningList,
302 : : PVC_RECURSE_AGGREGATES |
303 : : PVC_RECURSE_WINDOWFUNCS |
304 : : PVC_INCLUDE_PLACEHOLDERS);
6965 305 [ + + + + : 4069 : foreach(l, vars)
+ + ]
306 : : {
307 : 3181 : Var *var = (Var *) lfirst(l);
308 : : TargetEntry *tle;
309 : :
6164 310 [ + - ]: 3181 : if (IsA(var, Var) &&
311 [ + + ]: 3181 : var->varno == result_relation)
6965 312 : 2869 : continue; /* don't need it */
313 : :
3103 peter_e@gmx.net 314 [ + + ]: 312 : if (tlist_member((Expr *) var, tlist))
6965 tgl@sss.pgh.pa.us 315 : 118 : continue; /* already got it */
316 : :
317 : 194 : tle = makeTargetEntry((Expr *) var,
318 : 194 : list_length(tlist) + 1,
319 : : NULL,
320 : : true);
321 : :
322 : 194 : tlist = lappend(tlist, tle);
323 : : }
324 : 888 : list_free(vars);
325 : : }
326 : :
1620 327 : 253353 : root->processed_tlist = tlist;
328 : :
2840 329 [ + + ]: 253353 : if (target_relation)
2420 andres@anarazel.de 330 : 43081 : table_close(target_relation, NoLock);
1620 tgl@sss.pgh.pa.us 331 : 253353 : }
332 : :
333 : : /*
334 : : * extract_update_targetlist_colnos
335 : : * Extract a list of the target-table column numbers that
336 : : * an UPDATE's targetlist wants to assign to, then renumber.
337 : : *
338 : : * The convention in the parser and rewriter is that the resnos in an
339 : : * UPDATE's non-resjunk TLE entries are the target column numbers
340 : : * to assign to. Here, we extract that info into a separate list, and
341 : : * then convert the tlist to the sequential-numbering convention that's
342 : : * used by all other query types.
343 : : *
344 : : * This is also applied to the tlist associated with INSERT ... ON CONFLICT
345 : : * ... UPDATE, although not till much later in planning.
346 : : */
347 : : List *
1580 348 : 8548 : extract_update_targetlist_colnos(List *tlist)
349 : : {
1620 350 : 8548 : List *update_colnos = NIL;
351 : 8548 : AttrNumber nextresno = 1;
352 : : ListCell *lc;
353 : :
354 [ + + + + : 19208 : foreach(lc, tlist)
+ + ]
355 : : {
356 : 10660 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
357 : :
358 [ + + ]: 10660 : if (!tle->resjunk)
359 : 10516 : update_colnos = lappend_int(update_colnos, tle->resno);
360 : 10660 : tle->resno = nextresno++;
361 : : }
362 : 8548 : return update_colnos;
363 : : }
364 : :
365 : :
366 : : /*****************************************************************************
367 : : *
368 : : * TARGETLIST EXPANSION
369 : : *
370 : : *****************************************************************************/
371 : :
372 : : /*
373 : : * expand_insert_targetlist
374 : : * Given a target list as generated by the parser and a result relation,
375 : : * add targetlist entries for any missing attributes, and ensure the
376 : : * non-junk attributes appear in proper field order.
377 : : *
378 : : * Once upon a time we also did more or less this with UPDATE targetlists,
379 : : * but now this code is only applied to INSERT targetlists.
380 : : */
381 : : static List *
220 382 : 33529 : expand_insert_targetlist(PlannerInfo *root, List *tlist, Relation rel)
383 : : {
9443 384 : 33529 : List *new_tlist = NIL;
385 : : ListCell *tlist_item;
386 : : int attrno,
387 : : numattrs;
388 : :
7773 neilc@samurai.com 389 : 33529 : tlist_item = list_head(tlist);
390 : :
391 : : /*
392 : : * The rewriter should have already ensured that the TLEs are in correct
393 : : * order; but we have to insert TLEs for any missing attributes.
394 : : *
395 : : * Scan the tuple description in the relation's relcache entry to make
396 : : * sure we have all the user attributes in the right order.
397 : : */
9443 tgl@sss.pgh.pa.us 398 : 33529 : numattrs = RelationGetNumberOfAttributes(rel);
399 : :
400 [ + + ]: 110056 : for (attrno = 1; attrno <= numattrs; attrno++)
401 : : {
2939 andres@anarazel.de 402 : 76527 : Form_pg_attribute att_tup = TupleDescAttr(rel->rd_att, attrno - 1);
9278 bruce@momjian.us 403 : 76527 : TargetEntry *new_tle = NULL;
404 : :
7773 neilc@samurai.com 405 [ + + ]: 76527 : if (tlist_item != NULL)
406 : : {
407 : 71356 : TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
408 : :
7458 tgl@sss.pgh.pa.us 409 [ + - + + ]: 71356 : if (!old_tle->resjunk && old_tle->resno == attrno)
410 : : {
8555 411 : 66873 : new_tle = old_tle;
2245 412 : 66873 : tlist_item = lnext(tlist, tlist_item);
413 : : }
414 : : }
415 : :
9443 416 [ + + ]: 76527 : if (new_tle == NULL)
417 : : {
418 : : /*
419 : : * Didn't find a matching tlist entry, so make one.
420 : : *
421 : : * INSERTs should insert NULL in this case. (We assume the
422 : : * rewriter would have inserted any available non-NULL default
423 : : * value.) Also, normally we must apply any domain constraints
424 : : * that might exist --- this is to catch domain NOT NULL.
425 : : *
426 : : * When generating a NULL constant for a dropped column, we label
427 : : * it INT4 (any other guaranteed-to-exist datatype would do as
428 : : * well). We can't label it with the dropped column's datatype
429 : : * since that might not exist anymore. It does not really matter
430 : : * what we claim the type is, since NULL is NULL --- its
431 : : * representation is datatype-independent. This could perhaps
432 : : * confuse code comparing the finished plan to the target
433 : : * relation, however.
434 : : *
435 : : * Another exception is that if the column is generated, the value
436 : : * we produce here will be ignored, and we don't want to risk
437 : : * throwing an error. So in that case we *don't* want to apply
438 : : * domain constraints, so we must produce a NULL of the base type.
439 : : * Again, code comparing the finished plan to the target relation
440 : : * must account for this.
441 : : */
442 : : Node *new_expr;
443 : :
144 444 [ + + ]: 9654 : if (att_tup->attisdropped)
445 : : {
446 : : /* Insert NULL for dropped column */
1578 447 : 314 : new_expr = (Node *) makeConst(INT4OID,
448 : : -1,
449 : : InvalidOid,
450 : : sizeof(int32),
451 : : (Datum) 0,
452 : : true, /* isnull */
453 : : true /* byval */ );
454 : : }
144 455 [ + + ]: 9340 : else if (att_tup->attgenerated)
456 : : {
457 : : /* Generated column, insert a NULL of the base type */
458 : 559 : Oid baseTypeId = att_tup->atttypid;
459 : 559 : int32 baseTypeMod = att_tup->atttypmod;
460 : :
461 : 559 : baseTypeId = getBaseTypeAndTypmod(baseTypeId, &baseTypeMod);
462 : 559 : new_expr = (Node *) makeConst(baseTypeId,
463 : : baseTypeMod,
464 : : att_tup->attcollation,
465 : 559 : att_tup->attlen,
466 : : (Datum) 0,
467 : : true, /* isnull */
468 : 559 : att_tup->attbyval);
469 : : }
470 : : else
471 : : {
472 : : /* Normal column, insert a NULL of the column datatype */
473 : 8781 : new_expr = coerce_null_to_domain(att_tup->atttypid,
474 : : att_tup->atttypmod,
475 : : att_tup->attcollation,
476 : 8781 : att_tup->attlen,
477 : 8781 : att_tup->attbyval);
478 : : /* Must run expression preprocessing on any non-const nodes */
479 [ + + ]: 8781 : if (!IsA(new_expr, Const))
480 : 33 : new_expr = eval_const_expressions(root, new_expr);
481 : : }
482 : :
7458 483 : 9654 : new_tle = makeTargetEntry((Expr *) new_expr,
484 : : attrno,
8403 bruce@momjian.us 485 : 9654 : pstrdup(NameStr(att_tup->attname)),
486 : : false);
487 : : }
488 : :
9443 tgl@sss.pgh.pa.us 489 : 76527 : new_tlist = lappend(new_tlist, new_tle);
490 : : }
491 : :
492 : : /*
493 : : * The remaining tlist entries should be resjunk; append them all to the
494 : : * end of the new tlist, making sure they have resnos higher than the last
495 : : * real attribute. (Note: although the rewriter already did such
496 : : * renumbering, we have to do it again here in case we added NULL entries
497 : : * above.)
498 : : */
7773 neilc@samurai.com 499 [ - + ]: 33529 : while (tlist_item)
500 : : {
7773 neilc@samurai.com 501 :UBC 0 : TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
502 : :
7458 tgl@sss.pgh.pa.us 503 [ # # ]: 0 : if (!old_tle->resjunk)
8079 504 [ # # ]: 0 : elog(ERROR, "targetlist is not sorted correctly");
505 : : /* Get the resno right, but don't copy unnecessarily */
7458 506 [ # # ]: 0 : if (old_tle->resno != attrno)
507 : : {
508 : 0 : old_tle = flatCopyTargetEntry(old_tle);
509 : 0 : old_tle->resno = attrno;
510 : : }
8555 511 : 0 : new_tlist = lappend(new_tlist, old_tle);
512 : 0 : attrno++;
2245 513 : 0 : tlist_item = lnext(tlist, tlist_item);
514 : : }
515 : :
9443 tgl@sss.pgh.pa.us 516 :CBC 33529 : return new_tlist;
517 : : }
518 : :
519 : :
520 : : /*
521 : : * Locate PlanRowMark for given RT index, or return NULL if none
522 : : *
523 : : * This probably ought to be elsewhere, but there's no very good place
524 : : */
525 : : PlanRowMark *
5794 526 : 11646 : get_plan_rowmark(List *rowmarks, Index rtindex)
527 : : {
528 : : ListCell *l;
529 : :
530 [ + + + + : 12404 : foreach(l, rowmarks)
+ + ]
531 : : {
532 : 1929 : PlanRowMark *rc = (PlanRowMark *) lfirst(l);
533 : :
534 [ + + ]: 1929 : if (rc->rti == rtindex)
535 : 1171 : return rc;
536 : : }
537 : 10475 : return NULL;
538 : : }
|