Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5839052

Browse files
committed
Add CREATE RECURSIVE VIEW syntax
This is specified in the SQL standard. The CREATE RECURSIVE VIEW specification is transformed into a normal CREATE VIEW statement with a WITH RECURSIVE clause. reviewed by Abhijit Menon-Sen and Stephen Frost
1 parent b1980f6 commit 5839052

File tree

4 files changed

+158
-1
lines changed

4 files changed

+158
-1
lines changed

doc/src/sgml/ref/create_view.sgml

+28-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ PostgreSQL documentation
2121

2222
<refsynopsisdiv>
2323
<synopsis>
24-
CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
24+
CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] [ RECURSIVE ] VIEW <replaceable class="PARAMETER">name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
2525
[ WITH ( <replaceable class="PARAMETER">view_option_name</replaceable> [= <replaceable class="PARAMETER">view_option_value</replaceable>] [, ... ] ) ]
2626
AS <replaceable class="PARAMETER">query</replaceable>
2727
</synopsis>
@@ -80,6 +80,23 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
8080
</listitem>
8181
</varlistentry>
8282

83+
<varlistentry>
84+
<term><literal>RECURSIVE</></term>
85+
<listitem>
86+
<para>
87+
Creates a recursive view. The syntax
88+
<synopsis>
89+
CREATE RECURSIVE VIEW <replaceable>name</> (<replaceable>columns</>) AS SELECT <replaceable>...</>;
90+
</synopsis>
91+
is equivalent to
92+
<synopsis>
93+
CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replaceable>columns</>) AS (SELECT <replaceable>...</>) SELECT <replaceable>columns</> FROM <replaceable>name</>;
94+
</synopsis>
95+
A view column list must be specified for a recursive view.
96+
</para>
97+
</listitem>
98+
</varlistentry>
99+
83100
<varlistentry>
84101
<term><replaceable class="parameter">name</replaceable></term>
85102
<listitem>
@@ -282,6 +299,16 @@ CREATE VIEW comedies AS
282299
<literal>*</> was used to create the view, columns added later to
283300
the table will not be part of the view.
284301
</para>
302+
303+
<para>
304+
Create a recursive view consisting of the numbers from 1 to 100:
305+
<programlisting>
306+
CREATE RECURSIVE VIEW nums_1_100 (n) AS
307+
VALUES (1)
308+
UNION ALL
309+
SELECT n+1 FROM nums_1_100 WHERE n < 100;
310+
</programlisting>
311+
</para>
285312
</refsect1>
286313

287314
<refsect1>

src/backend/parser/gram.y

+85
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ static void SplitColQualList(List *qualList,
164164
static void processCASbits(int cas_bits, int location, const char *constrType,
165165
bool *deferrable, bool *initdeferred, bool *not_valid,
166166
bool *no_inherit, core_yyscan_t yyscanner);
167+
static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
167168

168169
%}
169170

@@ -7839,6 +7840,30 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list opt_reloptions
78397840
n->options = $8;
78407841
$$ = (Node *) n;
78417842
}
7843+
| CREATE OptTemp RECURSIVE VIEW qualified_name '(' columnList ')' opt_reloptions
7844+
AS SelectStmt
7845+
{
7846+
ViewStmt *n = makeNode(ViewStmt);
7847+
n->view = $5;
7848+
n->view->relpersistence = $2;
7849+
n->aliases = $7;
7850+
n->query = makeRecursiveViewSelect(n->view->relname, n->aliases, $11);
7851+
n->replace = false;
7852+
n->options = $9;
7853+
$$ = (Node *) n;
7854+
}
7855+
| CREATE OR REPLACE OptTemp RECURSIVE VIEW qualified_name '(' columnList ')' opt_reloptions
7856+
AS SelectStmt
7857+
{
7858+
ViewStmt *n = makeNode(ViewStmt);
7859+
n->view = $7;
7860+
n->view->relpersistence = $4;
7861+
n->aliases = $9;
7862+
n->query = makeRecursiveViewSelect(n->view->relname, n->aliases, $13);
7863+
n->replace = true;
7864+
n->options = $11;
7865+
$$ = (Node *) n;
7866+
}
78427867
;
78437868

78447869
opt_check_option:
@@ -13570,6 +13595,66 @@ processCASbits(int cas_bits, int location, const char *constrType,
1357013595
}
1357113596
}
1357213597

13598+
/*----------
13599+
* Recursive view transformation
13600+
*
13601+
* Convert
13602+
*
13603+
* CREATE RECURSIVE VIEW relname (aliases) AS query
13604+
*
13605+
* to
13606+
*
13607+
* CREATE VIEW relname (aliases) AS
13608+
* WITH RECURSIVE relname (aliases) AS (query)
13609+
* SELECT aliases FROM relname
13610+
*
13611+
* Actually, just the WITH ... part, which is then inserted into the original
13612+
* view definition as the query.
13613+
* ----------
13614+
*/
13615+
static Node *
13616+
makeRecursiveViewSelect(char *relname, List *aliases, Node *query)
13617+
{
13618+
SelectStmt *s = makeNode(SelectStmt);
13619+
WithClause *w = makeNode(WithClause);
13620+
CommonTableExpr *cte = makeNode(CommonTableExpr);
13621+
List *tl = NIL;
13622+
ListCell *lc;
13623+
13624+
/* create common table expression */
13625+
cte->ctename = relname;
13626+
cte->aliascolnames = aliases;
13627+
cte->ctequery = query;
13628+
cte->location = -1;
13629+
13630+
/* create WITH clause and attach CTE */
13631+
w->recursive = true;
13632+
w->ctes = list_make1(cte);
13633+
w->location = -1;
13634+
13635+
/* create target list for the new SELECT from the alias list of the
13636+
* recursive view specification */
13637+
foreach (lc, aliases)
13638+
{
13639+
ResTarget *rt = makeNode(ResTarget);
13640+
13641+
rt->name = NULL;
13642+
rt->indirection = NIL;
13643+
rt->val = makeColumnRef(strVal(lfirst(lc)), NIL, -1, 0);
13644+
rt->location = -1;
13645+
13646+
tl = lappend(tl, rt);
13647+
}
13648+
13649+
/* create new SELECT combining WITH clause, target list, and fake FROM
13650+
* clause */
13651+
s->withClause = w;
13652+
s->targetList = tl;
13653+
s->fromClause = list_make1(makeRangeVar(NULL, relname, -1));
13654+
13655+
return (Node *) s;
13656+
}
13657+
1357313658
/* parser_init()
1357413659
* Initialize to parse one query string
1357513660
*/

src/test/regress/expected/with.out

+30
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,36 @@ SELECT * FROM t;
4949
5
5050
(5 rows)
5151

52+
-- recursive view
53+
CREATE RECURSIVE VIEW nums (n) AS
54+
VALUES (1)
55+
UNION ALL
56+
SELECT n+1 FROM nums WHERE n < 5;
57+
SELECT * FROM nums;
58+
n
59+
---
60+
1
61+
2
62+
3
63+
4
64+
5
65+
(5 rows)
66+
67+
CREATE OR REPLACE RECURSIVE VIEW nums (n) AS
68+
VALUES (1)
69+
UNION ALL
70+
SELECT n+1 FROM nums WHERE n < 6;
71+
SELECT * FROM nums;
72+
n
73+
---
74+
1
75+
2
76+
3
77+
4
78+
5
79+
6
80+
(6 rows)
81+
5282
-- This is an infinite loop with UNION ALL, but not with UNION
5383
WITH RECURSIVE t(n) AS (
5484
SELECT 1

src/test/regress/sql/with.sql

+15
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,21 @@ UNION ALL
3131
)
3232
SELECT * FROM t;
3333

34+
-- recursive view
35+
CREATE RECURSIVE VIEW nums (n) AS
36+
VALUES (1)
37+
UNION ALL
38+
SELECT n+1 FROM nums WHERE n < 5;
39+
40+
SELECT * FROM nums;
41+
42+
CREATE OR REPLACE RECURSIVE VIEW nums (n) AS
43+
VALUES (1)
44+
UNION ALL
45+
SELECT n+1 FROM nums WHERE n < 6;
46+
47+
SELECT * FROM nums;
48+
3449
-- This is an infinite loop with UNION ALL, but not with UNION
3550
WITH RECURSIVE t(n) AS (
3651
SELECT 1

0 commit comments

Comments
 (0)