PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
rewriteSearchCycle.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * rewriteSearchCycle.c
4 * Support for rewriting SEARCH and CYCLE clauses.
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * IDENTIFICATION
10 * src/backend/rewrite/rewriteSearchCycle.c
11 *
12 *-------------------------------------------------------------------------
13 */
14#include "postgres.h"
15
16#include "catalog/pg_operator_d.h"
17#include "catalog/pg_type_d.h"
18#include "nodes/makefuncs.h"
19#include "nodes/parsenodes.h"
20#include "nodes/pg_list.h"
21#include "nodes/primnodes.h"
22#include "parser/analyze.h"
23#include "parser/parsetree.h"
26#include "utils/fmgroids.h"
27
28
29/*----------
30 * Rewrite a CTE with SEARCH or CYCLE clause
31 *
32 * Consider a CTE like
33 *
34 * WITH RECURSIVE ctename (col1, col2, col3) AS (
35 * query1
36 * UNION [ALL]
37 * SELECT trosl FROM ctename
38 * )
39 *
40 * With a search clause
41 *
42 * SEARCH BREADTH FIRST BY col1, col2 SET sqc
43 *
44 * the CTE is rewritten to
45 *
46 * WITH RECURSIVE ctename (col1, col2, col3, sqc) AS (
47 * SELECT col1, col2, col3, -- original WITH column list
48 * ROW(0, col1, col2) -- initial row of search columns
49 * FROM (query1) "*TLOCRN*" (col1, col2, col3)
50 * UNION [ALL]
51 * SELECT col1, col2, col3, -- same as above
52 * ROW(sqc.depth + 1, col1, col2) -- count depth
53 * FROM (SELECT trosl, ctename.sqc FROM ctename) "*TROCRN*" (col1, col2, col3, sqc)
54 * )
55 *
56 * (This isn't quite legal SQL: sqc.depth is meant to refer to the first
57 * column of sqc, which has a row type, but the field names are not defined
58 * here. Representing this properly in SQL would be more complicated (and the
59 * SQL standard actually does it in that more complicated way), but the
60 * internal representation allows us to construct it this way.)
61 *
62 * With a search clause
63 *
64 * SEARCH DEPTH FIRST BY col1, col2 SET sqc
65 *
66 * the CTE is rewritten to
67 *
68 * WITH RECURSIVE ctename (col1, col2, col3, sqc) AS (
69 * SELECT col1, col2, col3, -- original WITH column list
70 * ARRAY[ROW(col1, col2)] -- initial row of search columns
71 * FROM (query1) "*TLOCRN*" (col1, col2, col3)
72 * UNION [ALL]
73 * SELECT col1, col2, col3, -- same as above
74 * sqc || ARRAY[ROW(col1, col2)] -- record rows seen
75 * FROM (SELECT trosl, ctename.sqc FROM ctename) "*TROCRN*" (col1, col2, col3, sqc)
76 * )
77 *
78 * With a cycle clause
79 *
80 * CYCLE col1, col2 SET cmc TO 'Y' DEFAULT 'N' USING cpa
81 *
82 * (cmc = cycle mark column, cpa = cycle path) the CTE is rewritten to
83 *
84 * WITH RECURSIVE ctename (col1, col2, col3, cmc, cpa) AS (
85 * SELECT col1, col2, col3, -- original WITH column list
86 * 'N', -- cycle mark default
87 * ARRAY[ROW(col1, col2)] -- initial row of cycle columns
88 * FROM (query1) "*TLOCRN*" (col1, col2, col3)
89 * UNION [ALL]
90 * SELECT col1, col2, col3, -- same as above
91 * CASE WHEN ROW(col1, col2) = ANY (ARRAY[cpa]) THEN 'Y' ELSE 'N' END, -- compute cycle mark column
92 * cpa || ARRAY[ROW(col1, col2)] -- record rows seen
93 * FROM (SELECT trosl, ctename.cmc, ctename.cpa FROM ctename) "*TROCRN*" (col1, col2, col3, cmc, cpa)
94 * WHERE cmc <> 'Y'
95 * )
96 *
97 * The expression to compute the cycle mark column in the right-hand query is
98 * written as
99 *
100 * CASE WHEN ROW(col1, col2) IN (SELECT p.* FROM TABLE(cpa) p) THEN cmv ELSE cmd END
101 *
102 * in the SQL standard, but in PostgreSQL we can use the scalar-array operator
103 * expression shown above.
104 *
105 * Also, in some of the cases where operators are shown above we actually
106 * directly produce the underlying function call.
107 *
108 * If both a search clause and a cycle clause is specified, then the search
109 * clause column is added before the cycle clause columns.
110 */
111
112/*
113 * Make a RowExpr from the specified column names, which have to be among the
114 * output columns of the CTE.
115 */
116static RowExpr *
117make_path_rowexpr(const CommonTableExpr *cte, const List *col_list)
118{
119 RowExpr *rowexpr;
120 ListCell *lc;
121
122 rowexpr = makeNode(RowExpr);
123 rowexpr->row_typeid = RECORDOID;
124 rowexpr->row_format = COERCE_IMPLICIT_CAST;
125 rowexpr->location = -1;
126
127 foreach(lc, col_list)
128 {
129 char *colname = strVal(lfirst(lc));
130
131 for (int i = 0; i < list_length(cte->ctecolnames); i++)
132 {
133 char *colname2 = strVal(list_nth(cte->ctecolnames, i));
134
135 if (strcmp(colname, colname2) == 0)
136 {
137 Var *var;
138
139 var = makeVar(1, i + 1,
140 list_nth_oid(cte->ctecoltypes, i),
141 list_nth_int(cte->ctecoltypmods, i),
142 list_nth_oid(cte->ctecolcollations, i),
143 0);
144 rowexpr->args = lappend(rowexpr->args, var);
145 rowexpr->colnames = lappend(rowexpr->colnames, makeString(colname));
146 break;
147 }
148 }
149 }
150
151 return rowexpr;
152}
153
154/*
155 * Wrap a RowExpr in an ArrayExpr, for the initial search depth first or cycle
156 * row.
157 */
158static Expr *
160{
161 ArrayExpr *arr;
162
163 arr = makeNode(ArrayExpr);
164 arr->array_typeid = RECORDARRAYOID;
165 arr->element_typeid = RECORDOID;
166 arr->location = -1;
167 arr->elements = list_make1(rowexpr);
168
169 return (Expr *) arr;
170}
171
172/*
173 * Make an array catenation expression like
174 *
175 * cpa || ARRAY[ROW(cols)]
176 *
177 * where the varattno of cpa is provided as path_varattno.
178 */
179static Expr *
180make_path_cat_expr(RowExpr *rowexpr, AttrNumber path_varattno)
181{
182 ArrayExpr *arr;
183 FuncExpr *fexpr;
184
185 arr = makeNode(ArrayExpr);
186 arr->array_typeid = RECORDARRAYOID;
187 arr->element_typeid = RECORDOID;
188 arr->location = -1;
189 arr->elements = list_make1(rowexpr);
190
191 fexpr = makeFuncExpr(F_ARRAY_CAT, RECORDARRAYOID,
192 list_make2(makeVar(1, path_varattno, RECORDARRAYOID, -1, 0, 0),
193 arr),
195
196 return (Expr *) fexpr;
197}
198
199/*
200 * The real work happens here.
201 */
204{
205 Query *ctequery;
206 SetOperationStmt *sos;
207 int rti1,
208 rti2;
209 RangeTblEntry *rte1,
210 *rte2,
211 *newrte;
212 Query *newq1,
213 *newq2;
214 Query *newsubquery;
215 RangeTblRef *rtr;
216 Oid search_seq_type = InvalidOid;
217 AttrNumber sqc_attno = InvalidAttrNumber;
218 AttrNumber cmc_attno = InvalidAttrNumber;
219 AttrNumber cpa_attno = InvalidAttrNumber;
220 TargetEntry *tle;
221 RowExpr *cycle_col_rowexpr = NULL;
222 RowExpr *search_col_rowexpr = NULL;
223 List *ewcl;
224 int cte_rtindex = -1;
225
226 Assert(cte->search_clause || cte->cycle_clause);
227
228 cte = copyObject(cte);
229
230 ctequery = castNode(Query, cte->ctequery);
231
232 /*
233 * The top level of the CTE's query should be a UNION. Find the two
234 * subqueries.
235 */
236 Assert(ctequery->setOperations);
237 sos = castNode(SetOperationStmt, ctequery->setOperations);
238 Assert(sos->op == SETOP_UNION);
239
240 rti1 = castNode(RangeTblRef, sos->larg)->rtindex;
241 rti2 = castNode(RangeTblRef, sos->rarg)->rtindex;
242
243 rte1 = rt_fetch(rti1, ctequery->rtable);
244 rte2 = rt_fetch(rti2, ctequery->rtable);
245
246 Assert(rte1->rtekind == RTE_SUBQUERY);
247 Assert(rte2->rtekind == RTE_SUBQUERY);
248
249 /*
250 * We'll need this a few times later.
251 */
252 if (cte->search_clause)
253 {
254 if (cte->search_clause->search_breadth_first)
255 search_seq_type = RECORDOID;
256 else
257 search_seq_type = RECORDARRAYOID;
258 }
259
260 /*
261 * Attribute numbers of the added columns in the CTE's column list
262 */
263 if (cte->search_clause)
264 sqc_attno = list_length(cte->ctecolnames) + 1;
265 if (cte->cycle_clause)
266 {
267 cmc_attno = list_length(cte->ctecolnames) + 1;
268 cpa_attno = list_length(cte->ctecolnames) + 2;
269 if (cte->search_clause)
270 {
271 cmc_attno++;
272 cpa_attno++;
273 }
274 }
275
276 /*
277 * Make new left subquery
278 */
279 newq1 = makeNode(Query);
280 newq1->commandType = CMD_SELECT;
281 newq1->canSetTag = true;
282
283 newrte = makeNode(RangeTblEntry);
284 newrte->rtekind = RTE_SUBQUERY;
285 newrte->alias = makeAlias("*TLOCRN*", cte->ctecolnames);
286 newrte->eref = newrte->alias;
287 newsubquery = copyObject(rte1->subquery);
288 IncrementVarSublevelsUp((Node *) newsubquery, 1, 1);
289 newrte->subquery = newsubquery;
290 newrte->inFromCl = true;
291 newq1->rtable = list_make1(newrte);
292
293 rtr = makeNode(RangeTblRef);
294 rtr->rtindex = 1;
295 newq1->jointree = makeFromExpr(list_make1(rtr), NULL);
296
297 /*
298 * Make target list
299 */
300 for (int i = 0; i < list_length(cte->ctecolnames); i++)
301 {
302 Var *var;
303
304 var = makeVar(1, i + 1,
305 list_nth_oid(cte->ctecoltypes, i),
306 list_nth_int(cte->ctecoltypmods, i),
307 list_nth_oid(cte->ctecolcollations, i),
308 0);
309 tle = makeTargetEntry((Expr *) var, i + 1, strVal(list_nth(cte->ctecolnames, i)), false);
310 tle->resorigtbl = list_nth_node(TargetEntry, rte1->subquery->targetList, i)->resorigtbl;
311 tle->resorigcol = list_nth_node(TargetEntry, rte1->subquery->targetList, i)->resorigcol;
312 newq1->targetList = lappend(newq1->targetList, tle);
313 }
314
315 if (cte->search_clause)
316 {
317 Expr *texpr;
318
319 search_col_rowexpr = make_path_rowexpr(cte, cte->search_clause->search_col_list);
320 if (cte->search_clause->search_breadth_first)
321 {
322 search_col_rowexpr->args = lcons(makeConst(INT8OID, -1, InvalidOid, sizeof(int64),
323 Int64GetDatum(0), false, FLOAT8PASSBYVAL),
324 search_col_rowexpr->args);
325 search_col_rowexpr->colnames = lcons(makeString("*DEPTH*"), search_col_rowexpr->colnames);
326 texpr = (Expr *) search_col_rowexpr;
327 }
328 else
329 texpr = make_path_initial_array(search_col_rowexpr);
330 tle = makeTargetEntry(texpr,
331 list_length(newq1->targetList) + 1,
332 cte->search_clause->search_seq_column,
333 false);
334 newq1->targetList = lappend(newq1->targetList, tle);
335 }
336 if (cte->cycle_clause)
337 {
338 tle = makeTargetEntry((Expr *) cte->cycle_clause->cycle_mark_default,
339 list_length(newq1->targetList) + 1,
340 cte->cycle_clause->cycle_mark_column,
341 false);
342 newq1->targetList = lappend(newq1->targetList, tle);
343 cycle_col_rowexpr = make_path_rowexpr(cte, cte->cycle_clause->cycle_col_list);
344 tle = makeTargetEntry(make_path_initial_array(cycle_col_rowexpr),
345 list_length(newq1->targetList) + 1,
346 cte->cycle_clause->cycle_path_column,
347 false);
348 newq1->targetList = lappend(newq1->targetList, tle);
349 }
350
351 rte1->subquery = newq1;
352
353 if (cte->search_clause)
354 {
355 rte1->eref->colnames = lappend(rte1->eref->colnames, makeString(cte->search_clause->search_seq_column));
356 }
357 if (cte->cycle_clause)
358 {
359 rte1->eref->colnames = lappend(rte1->eref->colnames, makeString(cte->cycle_clause->cycle_mark_column));
360 rte1->eref->colnames = lappend(rte1->eref->colnames, makeString(cte->cycle_clause->cycle_path_column));
361 }
362
363 /*
364 * Make new right subquery
365 */
366 newq2 = makeNode(Query);
367 newq2->commandType = CMD_SELECT;
368 newq2->canSetTag = true;
369
370 newrte = makeNode(RangeTblEntry);
371 newrte->rtekind = RTE_SUBQUERY;
372 ewcl = copyObject(cte->ctecolnames);
373 if (cte->search_clause)
374 {
375 ewcl = lappend(ewcl, makeString(cte->search_clause->search_seq_column));
376 }
377 if (cte->cycle_clause)
378 {
379 ewcl = lappend(ewcl, makeString(cte->cycle_clause->cycle_mark_column));
380 ewcl = lappend(ewcl, makeString(cte->cycle_clause->cycle_path_column));
381 }
382 newrte->alias = makeAlias("*TROCRN*", ewcl);
383 newrte->eref = newrte->alias;
384
385 /*
386 * Find the reference to the recursive CTE in the right UNION subquery's
387 * range table. We expect it to be two levels up from the UNION subquery
388 * (and must check that to avoid being fooled by sub-WITHs with the same
389 * CTE name). There will not be more than one such reference, because the
390 * parser would have rejected that (see checkWellFormedRecursion() in
391 * parse_cte.c). However, the parser doesn't insist that the reference
392 * appear in the UNION subquery's topmost range table, so we might fail to
393 * find it at all. That's an unimplemented case for the moment.
394 */
395 for (int rti = 1; rti <= list_length(rte2->subquery->rtable); rti++)
396 {
397 RangeTblEntry *e = rt_fetch(rti, rte2->subquery->rtable);
398
399 if (e->rtekind == RTE_CTE &&
400 strcmp(cte->ctename, e->ctename) == 0 &&
401 e->ctelevelsup == 2)
402 {
403 cte_rtindex = rti;
404 break;
405 }
406 }
407 if (cte_rtindex <= 0)
409 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
410 errmsg("with a SEARCH or CYCLE clause, the recursive reference to WITH query \"%s\" must be at the top level of its right-hand SELECT",
411 cte->ctename)));
412
413 newsubquery = copyObject(rte2->subquery);
414 IncrementVarSublevelsUp((Node *) newsubquery, 1, 1);
415
416 /*
417 * Add extra columns to target list of subquery of right subquery
418 */
419 if (cte->search_clause)
420 {
421 Var *var;
422
423 /* ctename.sqc */
424 var = makeVar(cte_rtindex, sqc_attno,
425 search_seq_type, -1, InvalidOid, 0);
426 tle = makeTargetEntry((Expr *) var,
427 list_length(newsubquery->targetList) + 1,
428 cte->search_clause->search_seq_column,
429 false);
430 newsubquery->targetList = lappend(newsubquery->targetList, tle);
431 }
432 if (cte->cycle_clause)
433 {
434 Var *var;
435
436 /* ctename.cmc */
437 var = makeVar(cte_rtindex, cmc_attno,
438 cte->cycle_clause->cycle_mark_type,
439 cte->cycle_clause->cycle_mark_typmod,
440 cte->cycle_clause->cycle_mark_collation, 0);
441 tle = makeTargetEntry((Expr *) var,
442 list_length(newsubquery->targetList) + 1,
443 cte->cycle_clause->cycle_mark_column,
444 false);
445 newsubquery->targetList = lappend(newsubquery->targetList, tle);
446
447 /* ctename.cpa */
448 var = makeVar(cte_rtindex, cpa_attno,
449 RECORDARRAYOID, -1, InvalidOid, 0);
450 tle = makeTargetEntry((Expr *) var,
451 list_length(newsubquery->targetList) + 1,
452 cte->cycle_clause->cycle_path_column,
453 false);
454 newsubquery->targetList = lappend(newsubquery->targetList, tle);
455 }
456
457 newrte->subquery = newsubquery;
458 newrte->inFromCl = true;
459 newq2->rtable = list_make1(newrte);
460
461 rtr = makeNode(RangeTblRef);
462 rtr->rtindex = 1;
463
464 if (cte->cycle_clause)
465 {
466 Expr *expr;
467
468 /*
469 * Add cmc <> cmv condition
470 */
471 expr = make_opclause(cte->cycle_clause->cycle_mark_neop, BOOLOID, false,
472 (Expr *) makeVar(1, cmc_attno,
473 cte->cycle_clause->cycle_mark_type,
474 cte->cycle_clause->cycle_mark_typmod,
475 cte->cycle_clause->cycle_mark_collation, 0),
476 (Expr *) cte->cycle_clause->cycle_mark_value,
478 cte->cycle_clause->cycle_mark_collation);
479
480 newq2->jointree = makeFromExpr(list_make1(rtr), (Node *) expr);
481 }
482 else
483 newq2->jointree = makeFromExpr(list_make1(rtr), NULL);
484
485 /*
486 * Make target list
487 */
488 for (int i = 0; i < list_length(cte->ctecolnames); i++)
489 {
490 Var *var;
491
492 var = makeVar(1, i + 1,
493 list_nth_oid(cte->ctecoltypes, i),
494 list_nth_int(cte->ctecoltypmods, i),
495 list_nth_oid(cte->ctecolcollations, i),
496 0);
497 tle = makeTargetEntry((Expr *) var, i + 1, strVal(list_nth(cte->ctecolnames, i)), false);
498 tle->resorigtbl = list_nth_node(TargetEntry, rte2->subquery->targetList, i)->resorigtbl;
499 tle->resorigcol = list_nth_node(TargetEntry, rte2->subquery->targetList, i)->resorigcol;
500 newq2->targetList = lappend(newq2->targetList, tle);
501 }
502
503 if (cte->search_clause)
504 {
505 Expr *texpr;
506
507 if (cte->search_clause->search_breadth_first)
508 {
509 FieldSelect *fs;
510 FuncExpr *fexpr;
511
512 /*
513 * ROW(sqc.depth + 1, cols)
514 */
515
516 search_col_rowexpr = copyObject(search_col_rowexpr);
517
518 fs = makeNode(FieldSelect);
519 fs->arg = (Expr *) makeVar(1, sqc_attno, RECORDOID, -1, 0, 0);
520 fs->fieldnum = 1;
521 fs->resulttype = INT8OID;
522 fs->resulttypmod = -1;
523
524 fexpr = makeFuncExpr(F_INT8INC, INT8OID, list_make1(fs), InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
525
526 linitial(search_col_rowexpr->args) = fexpr;
527
528 texpr = (Expr *) search_col_rowexpr;
529 }
530 else
531 {
532 /*
533 * sqc || ARRAY[ROW(cols)]
534 */
535 texpr = make_path_cat_expr(search_col_rowexpr, sqc_attno);
536 }
537 tle = makeTargetEntry(texpr,
538 list_length(newq2->targetList) + 1,
539 cte->search_clause->search_seq_column,
540 false);
541 newq2->targetList = lappend(newq2->targetList, tle);
542 }
543
544 if (cte->cycle_clause)
545 {
546 ScalarArrayOpExpr *saoe;
547 CaseExpr *caseexpr;
548 CaseWhen *casewhen;
549
550 /*
551 * CASE WHEN ROW(cols) = ANY (ARRAY[cpa]) THEN cmv ELSE cmd END
552 */
553
555 saoe->location = -1;
556 saoe->opno = RECORD_EQ_OP;
557 saoe->useOr = true;
558 saoe->args = list_make2(cycle_col_rowexpr,
559 makeVar(1, cpa_attno, RECORDARRAYOID, -1, 0, 0));
560
561 caseexpr = makeNode(CaseExpr);
562 caseexpr->location = -1;
563 caseexpr->casetype = cte->cycle_clause->cycle_mark_type;
564 caseexpr->casecollid = cte->cycle_clause->cycle_mark_collation;
565 casewhen = makeNode(CaseWhen);
566 casewhen->location = -1;
567 casewhen->expr = (Expr *) saoe;
568 casewhen->result = (Expr *) cte->cycle_clause->cycle_mark_value;
569 caseexpr->args = list_make1(casewhen);
570 caseexpr->defresult = (Expr *) cte->cycle_clause->cycle_mark_default;
571
572 tle = makeTargetEntry((Expr *) caseexpr,
573 list_length(newq2->targetList) + 1,
574 cte->cycle_clause->cycle_mark_column,
575 false);
576 newq2->targetList = lappend(newq2->targetList, tle);
577
578 /*
579 * cpa || ARRAY[ROW(cols)]
580 */
581 tle = makeTargetEntry(make_path_cat_expr(cycle_col_rowexpr, cpa_attno),
582 list_length(newq2->targetList) + 1,
583 cte->cycle_clause->cycle_path_column,
584 false);
585 newq2->targetList = lappend(newq2->targetList, tle);
586 }
587
588 rte2->subquery = newq2;
589
590 if (cte->search_clause)
591 {
592 rte2->eref->colnames = lappend(rte2->eref->colnames, makeString(cte->search_clause->search_seq_column));
593 }
594 if (cte->cycle_clause)
595 {
596 rte2->eref->colnames = lappend(rte2->eref->colnames, makeString(cte->cycle_clause->cycle_mark_column));
597 rte2->eref->colnames = lappend(rte2->eref->colnames, makeString(cte->cycle_clause->cycle_path_column));
598 }
599
600 /*
601 * Add the additional columns to the SetOperationStmt
602 */
603 if (cte->search_clause)
604 {
605 sos->colTypes = lappend_oid(sos->colTypes, search_seq_type);
606 sos->colTypmods = lappend_int(sos->colTypmods, -1);
607 sos->colCollations = lappend_oid(sos->colCollations, InvalidOid);
608 if (!sos->all)
609 sos->groupClauses = lappend(sos->groupClauses,
610 makeSortGroupClauseForSetOp(search_seq_type, true));
611 }
612 if (cte->cycle_clause)
613 {
614 sos->colTypes = lappend_oid(sos->colTypes, cte->cycle_clause->cycle_mark_type);
615 sos->colTypmods = lappend_int(sos->colTypmods, cte->cycle_clause->cycle_mark_typmod);
616 sos->colCollations = lappend_oid(sos->colCollations, cte->cycle_clause->cycle_mark_collation);
617 if (!sos->all)
618 sos->groupClauses = lappend(sos->groupClauses,
619 makeSortGroupClauseForSetOp(cte->cycle_clause->cycle_mark_type, true));
620
621 sos->colTypes = lappend_oid(sos->colTypes, RECORDARRAYOID);
622 sos->colTypmods = lappend_int(sos->colTypmods, -1);
623 sos->colCollations = lappend_oid(sos->colCollations, InvalidOid);
624 if (!sos->all)
625 sos->groupClauses = lappend(sos->groupClauses,
626 makeSortGroupClauseForSetOp(RECORDARRAYOID, true));
627 }
628
629 /*
630 * Add the additional columns to the CTE query's target list
631 */
632 if (cte->search_clause)
633 {
634 ctequery->targetList = lappend(ctequery->targetList,
635 makeTargetEntry((Expr *) makeVar(1, sqc_attno,
636 search_seq_type, -1, InvalidOid, 0),
637 list_length(ctequery->targetList) + 1,
638 cte->search_clause->search_seq_column,
639 false));
640 }
641 if (cte->cycle_clause)
642 {
643 ctequery->targetList = lappend(ctequery->targetList,
644 makeTargetEntry((Expr *) makeVar(1, cmc_attno,
645 cte->cycle_clause->cycle_mark_type,
646 cte->cycle_clause->cycle_mark_typmod,
647 cte->cycle_clause->cycle_mark_collation, 0),
648 list_length(ctequery->targetList) + 1,
649 cte->cycle_clause->cycle_mark_column,
650 false));
651 ctequery->targetList = lappend(ctequery->targetList,
652 makeTargetEntry((Expr *) makeVar(1, cpa_attno,
653 RECORDARRAYOID, -1, InvalidOid, 0),
654 list_length(ctequery->targetList) + 1,
655 cte->cycle_clause->cycle_path_column,
656 false));
657 }
658
659 /*
660 * Add the additional columns to the CTE's output columns
661 */
662 cte->ctecolnames = ewcl;
663 if (cte->search_clause)
664 {
665 cte->ctecoltypes = lappend_oid(cte->ctecoltypes, search_seq_type);
666 cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, -1);
667 cte->ctecolcollations = lappend_oid(cte->ctecolcollations, InvalidOid);
668 }
669 if (cte->cycle_clause)
670 {
671 cte->ctecoltypes = lappend_oid(cte->ctecoltypes, cte->cycle_clause->cycle_mark_type);
672 cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, cte->cycle_clause->cycle_mark_typmod);
673 cte->ctecolcollations = lappend_oid(cte->ctecolcollations, cte->cycle_clause->cycle_mark_collation);
674
675 cte->ctecoltypes = lappend_oid(cte->ctecoltypes, RECORDARRAYOID);
676 cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, -1);
677 cte->ctecolcollations = lappend_oid(cte->ctecolcollations, InvalidOid);
678 }
679
680 return cte;
681}
int16 AttrNumber
Definition: attnum.h:21
#define InvalidAttrNumber
Definition: attnum.h:23
int64_t int64
Definition: c.h:499
#define FLOAT8PASSBYVAL
Definition: c.h:606
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
Assert(PointerIsAligned(start, uint64))
int i
Definition: isn.c:77
List * lappend(List *list, void *datum)
Definition: list.c:339
List * lappend_int(List *list, int datum)
Definition: list.c:357
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
List * lcons(void *datum, List *list)
Definition: list.c:495
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:438
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:336
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:289
FuncExpr * makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid funccollid, Oid inputcollid, CoercionForm fformat)
Definition: makefuncs.c:594
Expr * make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop, Oid opcollid, Oid inputcollid)
Definition: makefuncs.c:701
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:350
#define copyObject(obj)
Definition: nodes.h:230
@ CMD_SELECT
Definition: nodes.h:271
#define makeNode(_type_)
Definition: nodes.h:161
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
@ SETOP_UNION
Definition: parsenodes.h:2168
@ RTE_CTE
Definition: parsenodes.h:1032
@ RTE_SUBQUERY
Definition: parsenodes.h:1027
SortGroupClause * makeSortGroupClauseForSetOp(Oid rescoltype, bool require_hash)
Definition: analyze.c:2085
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
static Oid list_nth_oid(const List *list, int n)
Definition: pg_list.h:321
#define list_make1(x1)
Definition: pg_list.h:212
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define linitial(l)
Definition: pg_list.h:178
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
#define list_make2(x1, x2)
Definition: pg_list.h:214
static int list_nth_int(const List *list, int n)
Definition: pg_list.h:310
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
e
Definition: preproc-init.c:82
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:753
@ COERCE_EXPLICIT_CALL
Definition: primnodes.h:751
void IncrementVarSublevelsUp(Node *node, int delta_sublevels_up, int min_sublevels_up)
Definition: rewriteManip.c:928
static RowExpr * make_path_rowexpr(const CommonTableExpr *cte, const List *col_list)
static Expr * make_path_initial_array(RowExpr *rowexpr)
static Expr * make_path_cat_expr(RowExpr *rowexpr, AttrNumber path_varattno)
CommonTableExpr * rewriteSearchAndCycle(CommonTableExpr *cte)
ParseLoc location
Definition: primnodes.h:1401
ParseLoc location
Definition: primnodes.h:1333
Expr * defresult
Definition: primnodes.h:1332
List * args
Definition: primnodes.h:1331
Expr * result
Definition: primnodes.h:1343
Expr * expr
Definition: primnodes.h:1342
ParseLoc location
Definition: primnodes.h:1344
AttrNumber fieldnum
Definition: primnodes.h:1146
Expr * arg
Definition: primnodes.h:1145
Definition: pg_list.h:54
Definition: nodes.h:135
FromExpr * jointree
Definition: parsenodes.h:177
Node * setOperations
Definition: parsenodes.h:230
List * rtable
Definition: parsenodes.h:170
CmdType commandType
Definition: parsenodes.h:121
List * targetList
Definition: parsenodes.h:193
Query * subquery
Definition: parsenodes.h:1118
RTEKind rtekind
Definition: parsenodes.h:1061
List * args
Definition: primnodes.h:1428
ParseLoc location
Definition: primnodes.h:1452
ParseLoc location
Definition: primnodes.h:936
SetOperation op
Definition: parsenodes.h:2247
Definition: primnodes.h:262
String * makeString(char *str)
Definition: value.c:63
#define strVal(v)
Definition: value.h:82