PostgreSQL Source Code  git master
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-2024, 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"
24 #include "rewrite/rewriteManip.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  */
116 static RowExpr *
117 make_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  */
158 static 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  */
179 static Expr *
180 make_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)
408  ereport(ERROR,
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,
477  InvalidOid,
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 
554  saoe = makeNode(ScalarArrayOpExpr);
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
#define Assert(condition)
Definition: c.h:812
int64_t int64
Definition: c.h:482
#define FLOAT8PASSBYVAL
Definition: c.h:589
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
int i
Definition: isn.c:72
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
Expr * make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop, Oid opcollid, Oid inputcollid)
Definition: makefuncs.c:651
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:389
FuncExpr * makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid funccollid, Oid inputcollid, CoercionForm fformat)
Definition: makefuncs.c:544
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:240
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:301
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:287
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
#define copyObject(obj)
Definition: nodes.h:224
@ CMD_SELECT
Definition: nodes.h:265
#define makeNode(_type_)
Definition: nodes.h:155
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
@ SETOP_UNION
Definition: parsenodes.h:2119
@ RTE_CTE
Definition: parsenodes.h:1023
@ RTE_SUBQUERY
Definition: parsenodes.h:1018
SortGroupClause * makeSortGroupClauseForSetOp(Oid rescoltype, bool require_hash)
Definition: analyze.c:2047
#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
#define linitial(l)
Definition: pg_list.h:178
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#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:36
unsigned int Oid
Definition: postgres_ext.h:31
e
Definition: preproc-init.c:82
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:736
@ COERCE_EXPLICIT_CALL
Definition: primnodes.h:734
void IncrementVarSublevelsUp(Node *node, int delta_sublevels_up, int min_sublevels_up)
Definition: rewriteManip.c:841
static Expr * make_path_cat_expr(RowExpr *rowexpr, AttrNumber path_varattno)
static RowExpr * make_path_rowexpr(const CommonTableExpr *cte, const List *col_list)
CommonTableExpr * rewriteSearchAndCycle(CommonTableExpr *cte)
static Expr * make_path_initial_array(RowExpr *rowexpr)
ParseLoc location
Definition: primnodes.h:1384
List * elements
Definition: primnodes.h:1380
ParseLoc location
Definition: primnodes.h:1316
Expr * defresult
Definition: primnodes.h:1315
List * args
Definition: primnodes.h:1314
Expr * result
Definition: primnodes.h:1326
Expr * expr
Definition: primnodes.h:1325
ParseLoc location
Definition: primnodes.h:1327
AttrNumber fieldnum
Definition: primnodes.h:1129
Expr * arg
Definition: primnodes.h:1128
Definition: pg_list.h:54
Definition: nodes.h:129
FromExpr * jointree
Definition: parsenodes.h:177
Node * setOperations
Definition: parsenodes.h:221
List * rtable
Definition: parsenodes.h:170
CmdType commandType
Definition: parsenodes.h:121
List * targetList
Definition: parsenodes.h:193
Query * subquery
Definition: parsenodes.h:1104
RTEKind rtekind
Definition: parsenodes.h:1047
List * args
Definition: primnodes.h:1411
ParseLoc location
Definition: primnodes.h:1435
ParseLoc location
Definition: primnodes.h:919
SetOperation op
Definition: parsenodes.h:2198
Definition: primnodes.h:248
String * makeString(char *str)
Definition: value.c:63
#define strVal(v)
Definition: value.h:82