113 lines
2.9 KiB
C
113 lines
2.9 KiB
C
|
|
/*-------------------------------------------------------------------------
|
||
|
|
*
|
||
|
|
* subselect.c
|
||
|
|
* incremental view maintenance extension
|
||
|
|
* Routines for CTE support.
|
||
|
|
*
|
||
|
|
* Portions Copyright (c) 2023, IVM Development Group
|
||
|
|
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
|
||
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||
|
|
*
|
||
|
|
*-------------------------------------------------------------------------
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "postgres.h"
|
||
|
|
|
||
|
|
#include "nodes/nodeFuncs.h"
|
||
|
|
#include "rewrite/rewriteManip.h"
|
||
|
|
|
||
|
|
#include "pg_ivm.h"
|
||
|
|
|
||
|
|
typedef struct inline_cte_walker_context
|
||
|
|
{
|
||
|
|
const char *ctename; /* name and relative level of target CTE */
|
||
|
|
int levelsup;
|
||
|
|
Query *ctequery; /* query to substitute */
|
||
|
|
} inline_cte_walker_context;
|
||
|
|
|
||
|
|
static bool inline_cte_walker(Node *node, inline_cte_walker_context *context);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* inline_cte: convert RTE_CTE references to given CTE into RTE_SUBQUERYs
|
||
|
|
*/
|
||
|
|
void
|
||
|
|
inline_cte(PlannerInfo *root, CommonTableExpr *cte)
|
||
|
|
{
|
||
|
|
struct inline_cte_walker_context context;
|
||
|
|
|
||
|
|
context.ctename = cte->ctename;
|
||
|
|
/* Start at levelsup = -1 because we'll immediately increment it */
|
||
|
|
context.levelsup = -1;
|
||
|
|
context.ctequery = castNode(Query, cte->ctequery);
|
||
|
|
|
||
|
|
(void) inline_cte_walker((Node *) root->parse, &context);
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool
|
||
|
|
inline_cte_walker(Node *node, inline_cte_walker_context *context)
|
||
|
|
{
|
||
|
|
if (node == NULL)
|
||
|
|
return false;
|
||
|
|
if (IsA(node, Query))
|
||
|
|
{
|
||
|
|
Query *query = (Query *) node;
|
||
|
|
|
||
|
|
context->levelsup++;
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Visit the query's RTE nodes after their contents; otherwise
|
||
|
|
* query_tree_walker would descend into the newly inlined CTE query,
|
||
|
|
* which we don't want.
|
||
|
|
*/
|
||
|
|
(void) query_tree_walker(query, inline_cte_walker, context,
|
||
|
|
QTW_EXAMINE_RTES_AFTER);
|
||
|
|
|
||
|
|
context->levelsup--;
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else if (IsA(node, RangeTblEntry))
|
||
|
|
{
|
||
|
|
RangeTblEntry *rte = (RangeTblEntry *) node;
|
||
|
|
|
||
|
|
if (rte->rtekind == RTE_CTE &&
|
||
|
|
strcmp(rte->ctename, context->ctename) == 0 &&
|
||
|
|
rte->ctelevelsup == context->levelsup)
|
||
|
|
{
|
||
|
|
/*
|
||
|
|
* Found a reference to replace. Generate a copy of the CTE query
|
||
|
|
* with appropriate level adjustment for outer references (e.g.,
|
||
|
|
* to other CTEs).
|
||
|
|
*/
|
||
|
|
Query *newquery = copyObject(context->ctequery);
|
||
|
|
|
||
|
|
if (context->levelsup > 0)
|
||
|
|
IncrementVarSublevelsUp((Node *) newquery, context->levelsup, 1);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Convert the RTE_CTE RTE into a RTE_SUBQUERY.
|
||
|
|
*
|
||
|
|
* Historically, a FOR UPDATE clause has been treated as extending
|
||
|
|
* into views and subqueries, but not into CTEs. We preserve this
|
||
|
|
* distinction by not trying to push rowmarks into the new
|
||
|
|
* subquery.
|
||
|
|
*/
|
||
|
|
rte->rtekind = RTE_SUBQUERY;
|
||
|
|
rte->subquery = newquery;
|
||
|
|
rte->security_barrier = false;
|
||
|
|
|
||
|
|
/* Zero out CTE-specific fields */
|
||
|
|
rte->ctename = NULL;
|
||
|
|
rte->ctelevelsup = 0;
|
||
|
|
rte->self_reference = false;
|
||
|
|
rte->coltypes = NIL;
|
||
|
|
rte->coltypmods = NIL;
|
||
|
|
rte->colcollations = NIL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return expression_tree_walker(node, inline_cte_walker, context);
|
||
|
|
}
|